Install
$ agentstack add skill-jerseycheese-agent-skills-dead-code-cleanup ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Dead Code Cleanup
Overview
Systematically identifies dead, orphaned, and trivial code across the codebase including components, tests, and Storybook stories. Validates that code is truly unused before removal to prevent breaking changes. Also removes stories that provide no documentation value or only demonstrate rare edge cases.
What Qualifies as Dead/Trivial Code
Dead/Orphaned Code
- Components never imported - React components with no imports anywhere
- Unused utilities - Functions/modules with no consumers
- Orphaned tests - Tests for deleted/moved components
- Orphaned stories - Storybook stories for deleted/moved components
- Commented-out code - Code blocks that have been commented out
- Debug code - Console logs, debug flags, temporary test utilities
Trivial Tests
- Tests that always pass - Tests with no real assertions or mocked to always succeed
- Empty render tests - Tests that only verify component renders without crashing
- Pointless existence tests - Tests that check if a component is defined
- Snapshot-only tests - Tests with only snapshots and no behavior validation
- Duplicate tests - Multiple tests checking the same exact behavior
Trivial Storybook Stories
- No-value stories - Stories that show the exact same thing as another story
- Default-only stories - Stories that only render component with no props (when default story exists)
- Edge case stories - Stories demonstrating extremely rare scenarios unlikely to occur
- Duplicate states - Multiple stories showing identical visual states
- Over-specification stories - Stories for every tiny prop variation that adds no documentation value
- Obvious behavior stories - Stories showing behavior that's self-evident from the component
Examples of trivial stories to remove:
// ❌ Trivial - just default with no props (when Default story exists)
export const Empty: Story = {
args: {},
};
// ❌ Trivial - rare edge case nobody will encounter
export const WithExactly47Characters: Story = {
args: {
text: "This string has exactly forty-seven chars!",
},
};
// ❌ Trivial - duplicate of existing state
export const LoadingState: Story = {
args: { loading: true },
};
export const IsLoading: Story = { // Duplicate!
args: { loading: true },
};
// ❌ Trivial - no documentation value
export const WithPadding5: Story = {
args: { padding: 5 },
};
export const WithPadding6: Story = {
args: { padding: 6 },
};
export const WithPadding7: Story = {
args: { padding: 7 },
};
// ✅ Keep - demonstrates important state
export const ErrorState: Story = {
args: {
error: "Failed to load data",
onRetry: fn(),
},
};
// ✅ Keep - shows important variant
export const WithLongContent: Story = {
args: {
children: "Very long content that tests text wrapping...",
},
};
Trivial Other Code
- Empty functions - Functions that do nothing or only return undefined
- Duplicate implementations - Multiple versions of the same functionality
- Unused types/interfaces - TypeScript definitions with no references
Detection Strategy
Phase 1: Identify Candidates
For Components:
# Find all component files
find src/components -name "*.tsx" -o -name "*.ts"
# For each component, check for imports
grep -r "import.*ComponentName" src/
grep -r "from.*path/to/component" src/
For Tests:
# Find all test files
find . -name "*.test.ts" -o -name "*.test.tsx"
# For each test, verify the tested file exists
# Check if test is testing actual behavior (not just rendering)
For Storybook Stories:
# Find all story files
find . -name "*.stories.tsx" -o -name "*.stories.ts"
# For each story:
# 1. Verify the component exists (orphaned check)
# 2. Check if story provides meaningful documentation
# 3. Look for duplicate states
# 4. Identify edge case stories
Phase 2: Validation
Before marking as dead, verify:
- No direct imports in src/
- No dynamic imports (e.g., React.lazy, import())
- Not exported from index files
- Not used in type definitions
- Not referenced in configuration files
- Not used in Storybook stories
- Not referenced in documentation
For tests, verify they're trivial:
// Trivial test patterns
❌ test('renders without crashing', () => { render() })
❌ test('exists', () => { expect(Component).toBeDefined() })
❌ test('has correct snapshot', () => { expect(tree).toMatchSnapshot() })
❌ test('renders', () => { render(); expect(screen.getByRole('button')).toBeInTheDocument() })
// Non-trivial tests
✅ test('displays error when validation fails', () => { ... })
✅ test('submits form with correct data', () => { ... })
✅ test('calls callback when button clicked', () => { ... })
For stories, verify they're trivial:
// Read the story file and check:
// 1. Does it duplicate another story's state?
// 2. Is it an edge case (e.g., exactly N characters, rare prop combination)?
// 3. Does it show obvious behavior?
// 4. Is it just testing every prop value without value?
// Compare stories:
const stories = extractStoriesFromFile(file);
const duplicates = findDuplicateStates(stories);
const edgeCases = findEdgeCaseStories(stories);
const noValue = findNoValueStories(stories);
Phase 3: Categorization
Organize findings by confidence level:
High confidence (safe to remove):
- No imports found anywhere
- File created but never used
- Test for deleted component
- Story for deleted component
- Commented-out code blocks
- Stories that exactly duplicate existing stories
- Tests that only check rendering with no assertions
Medium confidence (needs review):
- Only imported in other dead code
- Only used in development/test environments
- Trivial tests that could be replaced
- Duplicate implementations
- Stories showing edge cases or minor variations
- Stories with questionable documentation value
Low confidence (keep or investigate):
- Used in generated code
- Dynamic imports possible
- Part of public API
- Referenced in documentation
- Stories that might demonstrate important variants
Story Value Assessment
Questions to Ask
For each Storybook story, evaluate:
- Does it show a distinct visual state?
- ✅ Keep: Shows error state vs success state vs loading state
- ❌ Remove: Shows padding: 5 vs padding: 6 vs padding: 7
- Would a developer need to see this?
- ✅ Keep: Interactive states, error handling, edge cases that matter
- ❌ Remove: Every possible prop combination, trivial variations
- Does it document important behavior?
- ✅ Keep: Form validation, disabled states, responsive behavior
- ❌ Remove: Component with exactly 47 characters of text
- Is it already covered by another story?
- ✅ Keep: First story showing this state
- ❌ Remove: Duplicate stories showing identical states
- Is this a realistic use case?
- ✅ Keep: Common scenarios developers will encounter
- ❌ Remove: Contrived edge cases unlikely to happen
Story Cleanup Patterns
Common trivial story patterns:
// Pattern 1: Duplicate states
export const Loading = { args: { isLoading: true } };
export const IsLoading = { args: { isLoading: true } }; // ❌ Remove
// Pattern 2: Micro-variations
export const Small = { args: { size: 'sm' } };
export const SmallWithPadding = { args: { size: 'sm', padding: 2 } }; // ❌ Remove if not meaningful
// Pattern 3: Edge cases
export const With99Items = { args: { items: Array(99).fill({}) } }; // ❌ Remove (why 99?)
// Pattern 4: Every prop value
export const RedButton = { args: { color: 'red' } };
export const BlueButton = { args: { color: 'blue' } };
export const GreenButton = { args: { color: 'green' } };
// ❌ Keep one or two examples, remove the rest
// Pattern 5: No-value empty states
export const Empty = { args: {} }; // ❌ Remove if Default exists
Keep these valuable stories:
// ✅ Shows important interactive state
export const WithValidationError: Story = {
args: {
error: "Email is required",
touched: true,
},
};
// ✅ Demonstrates realistic use case
export const LongFormWithManyFields: Story = {
args: {
fields: realisticFormFields,
},
};
// ✅ Shows responsive behavior
export const MobileLayout: Story = {
parameters: {
viewport: { defaultViewport: 'mobile1' },
},
};
// ✅ Documents complex interaction
export const WithAsyncValidation: Story = {
args: {
onValidate: async (value) => { /* realistic validation */ },
},
play: async ({ canvasElement }) => {
// Demonstrates the interaction
},
};
Removal Process
Step 1: Create Removal Plan
Document all findings:
## Dead Code Findings
### High Confidence (Safe to Remove)
- [ ] `src/components/OldButton.tsx` - No imports, replaced by shadcn Button
- [ ] `src/components/__tests__/OldButton.test.tsx` - Test for removed component
- [ ] `src/stories/OldButton.stories.tsx` - Story for removed component
### Trivial Tests to Remove
- [ ] `Component.test.tsx:15-18` - "renders without crashing" test
- [ ] `Component.test.tsx:20-23` - Empty snapshot test
- [ ] `Form.test.tsx:45-48` - Duplicate of existing test
### Trivial Stories to Remove
- [ ] `Button.stories.tsx` - "Empty" story (duplicates Default)
- [ ] `Button.stories.tsx` - "WithPadding5/6/7" stories (no value)
- [ ] `Input.stories.tsx` - "With47Characters" story (contrived edge case)
- [ ] `Card.stories.tsx` - "Loading" and "IsLoading" (duplicates)
### Medium Confidence (Review First)
- [ ] `src/lib/oldDateFormatter.ts` - Only used in dead code
- [ ] `Dialog.stories.tsx` - "WithCustomZIndex" (edge case?)
Step 2: Validate with User
Present findings for approval:
Found the following dead/trivial code:
High Confidence (3 files, 245 lines):
- OldButton component and tests (never imported)
- Legacy date formatter (only used in removed code)
Trivial Tests (12 tests, 156 lines):
- 8 "renders without crashing" tests
- 4 duplicate tests
Trivial Stories (15 stories, 203 lines):
- 6 stories showing micro-variations (padding values)
- 4 duplicate state stories
- 3 contrived edge case stories
- 2 empty/default duplicates
Total potential cleanup: 5 files + 27 test/story removals, 604 lines
Shall I proceed with removal?
Step 3: Safe Removal
Remove in order:
- Trivial tests first - Remove pointless tests
- Trivial stories second - Remove no-value stories
- Orphaned tests third - Remove tests for deleted code
- Orphaned stories fourth - Remove stories for deleted code
- Implementation last - Remove actual dead code
For each removal:
# 1. For code: Double-check no imports
grep -r "ComponentName" src/
# 2. For stories: Verify not referenced in docs
grep -r "story-id" docs/
# 3. Check git history to understand why it existed
git log --all --full-history -- path/to/file.tsx
# 4. Remove file or specific exports
# Option A: Remove entire file
rm path/to/file.tsx
# Option B: Remove specific story/test from file
# Use Edit tool to remove the export
# 5. Verify build still works
npm run build
# 6. Verify tests still pass
npm run test
# 7. Verify Storybook builds
npm run storybook
Step 4: Commit Strategy
Create focused commits:
# Separate commits for different types
git commit -m "test: Remove trivial 'renders without crashing' tests"
git commit -m "test: Remove duplicate test cases"
git commit -m "docs: Remove trivial Storybook stories with no documentation value"
git commit -m "docs: Remove edge case stories (WithExactly47Chars, etc)"
git commit -m "test: Remove orphaned tests for deleted components"
git commit -m "docs: Remove orphaned Storybook stories"
git commit -m "refactor: Remove dead OldButton component"
git commit -m "refactor: Remove debug utilities and commented code"
Detection Tools
Custom Grep Patterns
Find components with no imports:
# For each .tsx component file
for file in $(find src/components -name "*.tsx" ! -name "*.test.tsx" ! -name "*.stories.tsx"); do
name=$(basename "$file" .tsx)
imports=$(grep -r "import.*$name" src/ | wc -l)
if [ "$imports" -eq 0 ]; then
echo "No imports: $file"
fi
done
Find trivial tests:
# Search for common trivial test patterns
grep -r "renders without crashing" src/
grep -r "toBeDefined()" src/ | grep -i component
grep -r "toMatchSnapshot()" src/ | grep -v "// meaningful snapshot"
grep -r "test('renders'" src/
grep -r "it('renders'" src/
Find potentially trivial stories:
# Search for common trivial story patterns
grep -r "export const Empty" **/*.stories.tsx
grep -r "args: {}" **/*.stories.tsx
grep -r "WithExactly" **/*.stories.tsx
grep -r "With[0-9]+" **/*.stories.tsx
# Find story files with many exports (likely over-specified)
for file in $(find . -name "*.stories.tsx"); do
count=$(grep -c "^export const.*Story" "$file")
if [ "$count" -gt 10 ]; then
echo "Many stories ($count): $file"
fi
done
Find orphaned stories:
# For each story file, check if component exists
for story in $(find . -name "*.stories.tsx"); do
# Extract component import
component=$(grep "import.*from" "$story" | grep -v "@" | head -1)
# Check if imported file exists
# ...validation logic...
done
Safety Checks
Before Removing Anything
Required validations:
- [ ] Run full test suite:
npm run test - [ ] Run build:
npm run build - [ ] Check Storybook builds:
npm run storybook - [ ] Search for dynamic imports:
grep -r "import(" src/ - [ ] Search for string references:
grep -r "ComponentName" src/ - [ ] Check configuration files: package.json, next.config.js, etc.
- [ ] Review story file to ensure not removing valuable documentation
After Removal
Verification steps:
- [ ] All tests pass:
npm run test - [ ] Build succeeds:
npm run build - [ ] Storybook builds:
npm run storybook - [ ] No TypeScript errors:
npm run type-check(if available) - [ ] No ESLint errors:
npm run lint - [ ] Spot-check Storybook to ensure important stories remain
Common Scenarios
Scenario 1: Removed Component with Orphaned Files
Problem: Deleted a component but forgot to remove tests/stories
Solution:
# Find the component in git history
git log --all --full-history --diff-filter=D -- "**/ComponentName.tsx"
# Find orphaned test
find . -name "*ComponentName.test.tsx"
# Find orphaned story
find . -name "*ComponentName.stories.tsx"
# Remove both
rm path/to/ComponentName.test.tsx
rm path/to/ComponentName.stories.tsx
Scenario 2: Story File with Mix of Good and Trivial Stories
Problem: Some stories are valuable, others are trivial variations
Detection:
// src/stories/Button.stories.tsx
// ✅ Keep - shows important states
export const Default: Story = { args: { children: "Click me" } };
export const Disabled: Story = { args: { disabled: true } };
export const Loading: Story = { args: { loading: true } };
// ❌ Remove - trivial variations
export const WithPadding5: Story = { args: { padding: 5 } };
export const WithPadding6: Story = { args: { padding: 6 } };
export const WithPadding7: Story = { args: { padding: 7 } };
// ❌ Remove - duplicate
export const IsLoading: Story = { args: { loading: true } }; // Same as Loading!
// ❌ Remove - contrived edge case
export const WithExactly42Characters: Story = {
args: { children: "This button has exactly 42 characters!" },
};
Action: Use Edit tool to remove only the trivial exports, keep the valuable ones
Scenario 3: Trivial Tests Adding No Value
Problem: Tests that only check component renders
Detection:
// src/components/__tests__/MyComponent.test.tsx
❌ test('renders without crashing', () => {
render();
// No assertions!
});
❌ test('component exists', () => {
expect(MyComp
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [jerseycheese](https://github.com/jerseycheese)
- **Source:** [jerseycheese/agent-skills](https://github.com/jerseycheese/agent-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.