cleanup
Scan the repo for dead code, debug artifacts, and other cruft. Reports findings with file:line citations, then applies fixes category by category with user confirmation. Use when the user says "cleanu
SKILL.mdskills/engineering/cleanup/SKILL.md
GitHubCleanup
Announce at start: Running cleanup scan...
Scan the repo for cruft across 9 categories, write the full report to a markdown artifact, then apply fixes one category at a time after the user confirms each.
The value of this skill is its false-positive filtering, not its pattern matching. A noisy report the user has to re-verify is worse than a short one they can trust - prefer fewer, high-confidence findings, and verify every candidate against Step 2 before it reaches the report.
Args: --branch (default, scans files changed vs the base branch) or --full (whole working tree).
Delegation
Steps 1-4 are the delegable half: they read broadly, verify candidates, and end at a saved report
path. Hand them to a subagent when a delegation mechanism exists and the session holds context worth
protecting - especially under --full, where the scan reads the whole tree and almost none of it
belongs in the main thread. Brief it with the scope args, the Step 2 verification rules, the category
list, the report format, and the save path; take back only the report path and the summary counts.
In Claude Code, launch the critic agent for this (see the Claude Code binding in references/delegation.md).
Step 5 never delegates. Confirming categories with the user and applying edits belongs to the main agent, which reads the saved report first and owns every file it changes.
1. Scope
BASE=$(git merge-base HEAD main 2>/dev/null \
|| git merge-base HEAD master 2>/dev/null \
|| git rev-parse origin/HEAD 2>/dev/null)
git diff "$BASE"...HEAD --name-only
Always skip *.generated.*, *.min.*, *.lock, *.snap, binaries, and dist/ build/ vendor/ node_modules/ .next/ __pycache__/. If --branch finds no files, say "No changed files found vs
base. Try --full to scan the whole repo." and stop.
Identify the stack (JS/TS, Vue/Nuxt, Next.js, Python, Go) and note whether the repo has tests, Husky or commitlint config, and framework config files - Step 2 needs all of these.
2. Verification rules
Apply these to every candidate before reporting it.
Docs are not code. A match appearing only in context/, docs/, README*, CHANGELOG*, other
markdown, generated files, lockfiles, or binaries is not a cleanup finding.
Framework and config awareness. Before calling anything unused, check whether it is referenced
from framework config (nuxt.config.*, next.config.*, vite.config.*, vitest.config.*), tool
config (eslint.config.*, .eslintrc*, commitlint.config.*, prettier.config.*), git hooks
(.husky/**), or package.json scripts. If it is, it stays.
Test awareness. Search tests (**/*.test.*, **/*.spec.*, **/__tests__/**) as well as runtime
code. Something used only by tests is reported as, not as dead:
file:line - test-only export: <name> (keep if intentional test surface)
Same-file exports. An exported symbol used only inside its own file is a lower-severity hint, not a dead export:
file:line - export can likely be de-exported: <name> (used only within source file)
Framework packages. depcheck is incomplete on framework repos. Before reporting a package,
verify all of: module registration in nuxt.config.*/next.config.*, use via auto-components or
auto-imports, CSS entrypoint usage (e.g. tailwindcss), and CLI or hook usage through
package.json scripts or .husky/**.
3. Scan
Read references/scan-categories.md and run every category, collecting findings before modifying
anything:
A - Unused Imports 🔧 · B - Commented-Out Code 🔧 · C - Debug Artifacts 🔧/⚠️ · D - Dead Exports ⚠️ · E - Stale Suppression Directives ⚠️ · F - Empty Files 🔧 · G - Unused Packages ⚠️ · H - Stale Feature Flags ⚠️ · I - Unreferenced Pages/Routes ⚠️ (Nuxt/Next only)
4. Write the report
Render the full report to ~/.agents/cleanup-plans/<repo>/<slug>-cleanup-plan.md - slug preference:
ticket key → branch name → repo name, kebab-cased - with frontmatter date, repo, branch, mode.
## Cleanup Scan - <branch name or "full repo">
**Scope:** <N> files scanned · <date>
**Stack:** <detected stack>
### A · Unused Imports 🔧
`file:line` - unused import: `<name>`
_(none)_ if clean
### B · Commented-Out Code 🔧
`file:line` - commented-out code block (N lines)
### C · Debug Artifacts
🔧 `file:line` - debug: `console.log(...)`
⚠️ `file:line` - TODO: <text> ← manual action only
### D · Dead Exports ⚠️
`file:line` - ⚠️ dead export: `<name>` (verify before removing)
`file:line` - export can likely be de-exported: `<name>` (used only within source file)
`file:line` - test-only export: `<name>` (keep if intentional test surface)
### E · Stale Suppression Directives ⚠️
`file:line` - ⚠️ `@ts-ignore` (verify still needed)
### F · Empty Files 🔧
`path/to/file` - empty file
### G · Unused Packages ⚠️
`package.json` - ⚠️ verified unused package: `<name>`
### H · Stale Feature Flags ⚠️
`file:line` - ⚠️ `FLAG_X = true` (hardcoded)
### I · Unreferenced Pages ⚠️
`file` - ⚠️ route `/path` has no references (verify)
### Verification Notes
- Suppressed as docs-only false positives: <count or _(none)_>
- Suppressed as framework/config usage: <count or _(none)_>
- Suppressed as test-only usage: <count or _(none)_>
- Reclassified from dead export to de-export hint: <count or _(none)_>
---
**Summary:** <N> auto-fixable 🔧 · <M> manual review ⚠️
The terminal gets the file path and the confirmation prompts - nothing else. The report body stays in the file.
[cleanup-plan.md](<absolute path>)
Apply fixes for Category A? (yes / no / show-me-first)
5. Confirm and apply
Ask once per category holding at least one 🔧 finding. show-me-first prints the exact lines or diff
and re-asks. "Apply all fixable" confirms every 🔧 category at once. For categories with only ⚠️
findings, say they need manual review and move on.
Apply only confirmed categories, using the repo's own tooling where it exists:
- A - Unused imports: the repo's linter with its fix flag (
eslint --fix,ruff check --fix,goimports -w). - B - Commented-out code: Edit out each flagged block in full, not just its first line.
- C - Debug artifacts: Edit out flagged
console.log/print/debuggerlines. TODO, FIXME, and HACK lines stay. - F - Empty files: confirm each individually, then
rm. - G - Unused packages: print the uninstall command for the user to run rather than running it.
Then git diff --stat and report:
## Cleanup Complete
**Applied:** <N> categories fixed · <M> files modified
**Skipped:** <list>
**Manual review needed:** <count of ⚠️ findings - list categories>
Run your test suite to verify nothing broke.
Close by naming what you filtered out - docs-only, config-driven, framework-convention, and test-only false positives - and reminding the user that the remaining ⚠️ findings are surfaced but untouched.