Claude-starter cleanup-unused
Detect and delete unused code, exports, files, and dependencies. Runs knip/vulture/staticcheck/cargo-machete appropriate to the language, writes a critical assessment, and auto-applies HIGH-confidence deletions. Use when the user asks to remove dead code, find unused exports, clean up dependencies, or run dead-code analysis.
git clone https://github.com/raintree-technology/claude-starter
T=$(mktemp -d) && git clone --depth=1 https://github.com/raintree-technology/claude-starter "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/code-quality/cleanup-unused" ~/.claude/skills/raintree-technology-claude-starter-cleanup-unused && rm -rf "$T"
skills/code-quality/cleanup-unused/SKILL.mdDetect unused code, exports, files, and dependencies. Auto-delete only what's verifiably dead. Write a critical assessment with confidence ratings; defer ambiguous items to the human.
Preflight
- Detect language(s) by checking for:
(TS/JS),package.json
/pyproject.toml
(Py),requirements.txt
(Go),go.mod
(Rust). Skip languages not present.Cargo.toml - Check git state. If working tree is dirty, refuse to auto-apply — ask user to stash or commit first. The verify step needs a clean baseline to revert to.
- Create report dir:
. Addmkdir -p .claude/cleanup-reports/
to.claude/cleanup-reports/
if not already present..gitignore - Scan for dynamic-import patterns that defeat static analysis. Save the list — findings in these areas drop to MEDIUM confidence.
- JS/TS:
,import(
,require(
,eval(
,Function(
, dynamic route file conventions (__webpack_require__
,pages/
)app/ - Python:
,__import__
,importlib
, plugin entry-points ingetattr
/setup.pypyproject.toml - Go:
, plugin loader, build tagsreflect. - Rust:
gates, proc-macros,cfg(feature = ...)extern crate
- JS/TS:
Detect
Run the right tool per language. Install on demand only after asking.
TypeScript / JavaScript
# Knip is the gold standard for TS/JS unused-code detection. npx knip --reporter json > /tmp/knip.json
Parse
files, exports, types, dependencies, devDependencies, unlisted, binaries arrays.
Python
uvx vulture . --min-confidence 80 --json > /tmp/vulture.json 2>/dev/null || pipx run vulture . --min-confidence 80
Vulture confidence ≥80 maps roughly to our HIGH; 60-79 = MEDIUM.
Go
go install honnef.co/go/tools/cmd/staticcheck@latest 2>/dev/null staticcheck -checks=U1000 ./... > /tmp/staticcheck.txt
U1000 = unused code.
Rust
cargo install cargo-machete 2>/dev/null cargo machete --with-metadata > /tmp/machete.txt # unused deps cargo +nightly udeps 2>/dev/null || true # optional, nightly-only
Use
RUSTFLAGS="-W dead_code" and re-build for in-crate dead code if needed.
Assess
Write
.claude/cleanup-reports/cleanup-unused-{YYYY-MM-DD}.md with:
# Unused Code Assessment — YYYY-MM-DD ## Scope - Languages detected: [TS, Py, …] - Tools run: [knip, vulture, …] - Dynamic-import risk areas: [list paths] ## Summary - Unused files: N (HIGH: x, MEDIUM: y) - Unused exports: N (HIGH: x, MEDIUM: y) - Unused dependencies: N (HIGH: x, MEDIUM: y) - Total LOC removable (HIGH only): ~N ## Findings | Conf | Type | Location | Tool said | Recommendation | |------|------|----------|-----------|----------------| | HIGH | export | src/utils/foo.ts:12 `parseDate` | knip: unused export | Delete export + function | | MED | file | src/legacy/old.ts | knip: unused file | Defer — referenced from dynamic import area | | ... ## Critical Assessment [2-4 paragraphs of human-readable analysis] - What patterns emerged? (e.g., "8 of 12 unused exports are in `lib/legacy/` — consider deleting the whole directory") - Why were items downgraded from HIGH to MEDIUM? - Architectural observations. ## Out of scope - [Items the tool flagged but skill won't touch — public API surfaces, framework conventions, etc.]
Apply
Auto-apply HIGH-confidence only. Group all deletions into one commit.
Confidence rubric
HIGH (auto-apply):
- Tool flagged the item AND it's not in a dynamic-import risk area
- Not on a public API surface (
,index.ts
,__init__.py
items, packagepub
/main
)exports - Has zero references in the entire repo (verify with grep, not just the tool's word)
- For deps: not used in any script, config, or runtime require
MEDIUM (report only, don't apply):
- Tool flagged BUT one of: in dynamic-import area, on public API surface, has indirect references (re-exports), is a type-only export from a
consumed externally.d.ts - Vulture confidence 60-79
- Dependencies that appear in
or are used by build toolingpeerDependencies
LOW (note in report, no action):
- Heuristic guesses with no tool backing
- Things the user previously declined to delete (check git log for prior reverts)
Execution
- Delete files:
— but verify norm <path>
'd sibling files reference them first..gitignore - Remove exports:
to remove the export keyword + drop the symbol if internal usage is also zero.Edit - Uninstall deps using the project's package manager (detect from lockfile:
→package-lock.json
,npm uninstall
→yarn.lock
,yarn remove
→pnpm-lock.yaml
,pnpm remove
→bun.lockb
). For Python:bun remove
/pip uninstall <pkg>
/uv remove <pkg>
. For Rust:poetry remove <pkg>
.cargo remove <pkg> - Single commit:
.git add -A && git commit -m "chore(cleanup): cleanup-unused — N items removed"
Verify
Run all that apply, in order. Halt and revert on first failure.
# Typecheck — prefer the project's typecheck script if defined npm run typecheck 2>&1 || npx tsc --noEmit mypy . 2>&1 || true go build ./... 2>&1 cargo check 2>&1 # Tests (if scripts exist) npm test 2>&1 pytest 2>&1 go test ./... 2>&1 cargo test 2>&1 # Lint — prefer project's lint script npm run lint 2>&1 || npx @biomejs/biome check . 2>&1 || npx eslint . 2>&1 ruff check . 2>&1 golangci-lint run 2>&1 cargo clippy 2>&1
If any fail:
git revert HEAD --no-edit, downgrade all auto-applied items to MEDIUM in the report, append a "## Verify Failure" section with the error output.
Output
End-of-turn message (≤4 lines):
- "Removed N unused items (X files, Y exports, Z deps). M deferred for review."
- Path to the report.
- Verify status (✓ all green, ✗ reverted).
NEVER
- Delete anything in
,node_modules/
,.next/
,dist/
, or other generated dirs.build/ - Delete migration files (
,drizzle/*.sql
,alembic/
) even if they look unused — they describe history.migrations/ - Delete test fixtures based on knip flags — they're often loaded by glob.
- Auto-apply on a dirty working tree — refuse and ask.
- Trust knip blindly for files in framework convention directories (Next.js
,app/
, Remixpages/
, etc.) — those have routing-based dynamic loads.routes/ - Run
or any flag that bypasses lockfile integrity if a dep removal causes resolution issues.npm install --force