From ywc-agent-toolkit
Removes dead code (unused functions, exports, files, dependencies, imports) using detection tools (knip, ts-prune) and grep, with tiered deletion safety. Use for scheduled hygiene passes or consolidating near-duplicate utilities.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ywc-agent-toolkit:ywc-refactor-cleanThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Announce at start:** "I'm using the ywc-refactor-clean skill to remove dead code under a SAFE/CAUTION/DANGER tier with per-batch verification."
Announce at start: "I'm using the ywc-refactor-clean skill to remove dead code under a SAFE/CAUTION/DANGER tier with per-batch verification."
This skill is the canonical dead-code-removal discipline for ywc. It exists because hand-rolled cleanup (grep + delete + hope) routinely breaks dynamic imports, public package exports, and CI-only code paths. Adapted from ECC/refactor-cleaner (Sonnet agent) and ECC/refactor-clean (slash command), tightened to delegate completion claims to ywc-verify-done and to forbid bundled behavior changes (those route to ywc-tdd-ritual + ywc-code-gen).
NEVER DELETE WITHOUT (1) DETECTION TOOL CONFIRMS + (2) GREP CONFIRMS NO REFERENCES + (3) TESTS PASS AFTER EACH BATCH
A symbol that knip says is unused but grep finds in a require() string is not dead — that is a dynamic import. A function that ts-prune says is unused but is the package's public export is not dead — that is an external consumer. Single-source confirmation is not confirmation; the discipline requires two independent witnesses (tool + grep) plus a behavioral witness (tests).
If a deletion is committed and a downstream test breaks, revert that commit first, then re-classify the item to CAUTION or DANGER. Do not chase the failure forward with a follow-up "fix" commit — the original commit is the regression.
When tempted to bypass a rule, check this table first:
| Excuse | Reality |
|---|---|
| "knip / ts-prune already says it is unused — skipping the grep step" | The detection tool runs static analysis over the module graph. It cannot see: (a) require(${name}) dynamic imports, (b) string references in CI configs / IaC / docker / docs, (c) plugin auto-discovery (e.g., *.test.ts collected by a test runner via glob), (d) public package exports consumed by an external repo. The grep is the second witness — without it, ~5% of "unused" deletions break something. |
| "I'll batch 30 deletions into one commit — faster to review" | Each deletion is one commit. Bisectability is the ENTIRE point — when test X starts failing two weeks later, git bisect must land on the exact deletion that caused it. A 30-deletion commit forces a human to re-classify all 30 by hand. |
| "Tests passed locally, no need to re-run after each batch" | "After each batch" means after each tier (SAFE all done → run, CAUTION all done → run). Skipping intra-batch test runs means you lose the cheap rollback signal — a broken test now requires manual diff inspection to find which deletion broke it, instead of git revert <last-commit>. |
| "The duplicate helpers look identical — I'll merge them in this PR" | Consolidating duplicates is a behavior change (call sites now resolve to a different implementation). Behavior changes belong to ywc-tdd-ritual + ywc-code-gen, not this skill. This skill removes dead code; it does not refactor live code. |
| "Removing this dependency would shave 200kb — even though some tests still import it" | Tests are code. If a test imports the dependency, the dependency is not unused. Either the test is dead (delete it under the same tier discipline), or the dependency is live (skip it). "Remove the dep and rewrite the test" is a feature-PR sized scope creep — out of scope here. |
"knip flagged src/legacy/ as unused — deleting the whole directory" | Directory-scale deletion crosses the SAFE boundary into DANGER regardless of what the tool says. Each file in the directory needs its own tier classification + per-file delete + test run. Bulk-deleting a directory and then "fixing the fallout" is the inverse of the discipline. |
| "I'm in a hurry — I'll skip the verify-done block at the end" | The per-batch test run is the discipline. The ywc-verify-done block at the end is the claim that the cleanup is complete and safe — without it, the PR description says "removed N items" without evidence the suite passed. The reviewer cannot tell whether you ran the suite or hoped. |
| "I'll run this during the active feature branch — kill two birds" | The feature branch's diff already mixes new behavior + new tests. Adding 30 deletions on top makes the review impossible (reviewer cannot distinguish "this delete was safe" from "this delete broke the new feature"). Always cleanup in its own branch. |
Violating the letter of this discipline is violating the spirit. Cleanup that introduces a regression is more expensive than the cleanup ever saved — the regression debugging cost typically exceeds the bundle-size win by 10×.
| Parameter | Format | Example | Description |
|---|---|---|---|
--scope | --scope <dir> | --scope src/ | Restrict detection + deletion to a path. Default: repository root. |
--tier | --tier safe|safe+caution|all | --tier safe | Stop after the named tier. Default: safe (most conservative). |
--dry-run | flag | --dry-run | Run detection + classification + grep verification, but emit a report instead of mutating files. |
--skip-verify-done | flag | --skip-verify-done | Skip the final ywc-verify-done handoff. Only valid when the caller (e.g., ywc-finish-branch) will run it. |
Run the language-appropriate detection tools in parallel. The matrix below is the canonical mapping — extend references/detection-tools.md when adding a new language / tool, not this body.
| Ecosystem | Primary tool | What it finds | One-line invocation |
|---|---|---|---|
| JS / TS | knip | Unused files, exports, dependencies, types | npx knip |
| JS / TS | depcheck | Unused npm dependencies | npx depcheck |
| TS only | ts-prune | Unused TypeScript exports | npx ts-prune |
| Python | vulture | Unused functions, classes, imports | vulture src/ |
| Go | deadcode | Unused functions, types | deadcode ./... |
| Rust | cargo-udeps | Unused crate dependencies | cargo +nightly udeps |
| Universal fallback | grep | Symbols defined but never referenced | see references/detection-tools.md |
For an unsupported ecosystem, use the grep-based fallback in references/detection-tools.md — do not invent a new tool.
Sort every finding into exactly one tier. Items that match multiple tiers escalate to the highest tier.
| Tier | Pattern | Action |
|---|---|---|
| SAFE | Internal helper, test fixture, private function with no export keyword, internal type alias | Delete with per-item verification (Step 3) |
| CAUTION | Component, route handler, middleware, public-but-internal export, any symbol with import() or require() patterns matching its name anywhere in the repo | Manual verification (Step 4) before deletion |
| DANGER | Public package export (in exports / main / module field of package.json), config file, entry point, type definition consumed by an external API, anything git log shows touched in the last 7 days | Investigate (Step 5) — do not delete in this skill's scope |
Full classification rules with concrete examples live in references/safety-tiers.md.
The 5-substep loop below is the work the deletion worker performs per item. When the Claude Code runtime is in use and the named-agent catalog at claude-code/agents/ is installed, dispatch Task(subagent_type: ywc-refactor-cleaner) with the SAFE worklist so the worker carries the dedicated persona, Mission, and Boundaries (claude-code/agents/ywc-refactor-cleaner.md). When the runtime lacks named-agent dispatch, run the loop inline with the same discipline.
For each SAFE item, in order:
git grep -nE '<symbol>' (or pattern-matched variant for dynamic patterns). Zero hits = proceed. Any hit = re-classify to CAUTION and skip.Edit tool — surgical removal, no adjacent re-formatting.git revert <commit> (after the commit lands) and re-classify to CAUTION. Use git checkout -- <file> only for the pre-commit state, never after committing — git revert is the atomic rollback for multi-file deletions.chore(cleanup): remove unused <symbol> (knip) — name the tool that flagged it. One deletion per commit.Do not batch multiple deletions into one commit. Bisectability is the entire reason for per-item commits — when a regression surfaces in week 3, git bisect lands on the exact deletion.
Before deleting any CAUTION item, run all three checks:
git grep -nE "(import|require)\\([\\\"'\\\\\]..[\"'\\`]\)"— catchesimport('./modules/' + name)` patterns where the symbol appears as a string fragment.git grep -nE "[\\\"'\\\\\][\"'\\`]"` — catches route names, plugin slugs, config keys, doc references.cat package.json | jq '.exports, .main, .module, .bin' (or language equivalent) — if the symbol appears in any of these, it is reachable from outside the repo. Re-classify to DANGER.Zero hits across all three → proceed with Step 3 deletion loop. Any hit → escalate to DANGER (do not delete in this skill).
For each DANGER item, emit a report entry (Step 7) but do not delete. DANGER deletions belong to a separate intentional change (a major version bump, an API deprecation cycle, an external-consumer migration) — not this skill.
This step is opt-in via --tier all and is reserved for git diff-quality consolidations. Skip if --tier is safe or safe+caution.
When choosing the canonical implementation and judging what "simpler" means, apply the shared readable-code rubric and respect its anti-dogma guardrails (do not over-simplify or drop a helpful abstraction). See ../references/readable-code.md.
For near-duplicate functions (>80% AST or text similarity by inspection):
If the consolidation requires any behavior reconciliation (the two implementations differ in edge-case handling, error shape, type signatures), it is no longer cleanup — it is a behavior change. Route to ywc-tdd-ritual + ywc-code-gen and skip the consolidation here.
Hand off to ywc-verify-done with the cleanup report (Output Format below). The handoff is mandatory unless --skip-verify-done was passed by an upstream caller that will perform its own verify.
The cleanup commit series is closed by a single Verification Report following the Output Format below. The report is what the PR description quotes when delegating to ywc-create-pr.
Dead Code Cleanup Report
─────────────────────────────────────────────────
Scope: src/ (or --scope value)
Tier reached: safe (or safe+caution / all)
Detection tools: knip 5.x, depcheck 1.4, ts-prune 0.10
Deleted (SAFE):
- 12 unused functions (12 commits)
- 3 unused files ( 3 commits)
- 5 unused dependencies ( 5 commits)
Deleted (CAUTION): N/A — --tier was safe
Skipped (re-classified up):
- 2 SAFE items moved to CAUTION (grep found dynamic import)
- 1 CAUTION item moved to DANGER (public package export)
DANGER items reported (not deleted):
- src/legacy/v1-api.ts (public package export)
- src/plugins/registry.ts (plugin auto-discovery target)
Verification (per ywc-verify-done):
$ npm test
exit 0 (PASS — 482 / 482)
$ npm run build
exit 0 (PASS — bundle reduced from 2.41 MB → 2.38 MB)
$ npm run lint
exit 0 (PASS)
─────────────────────────────────────────────────
Bundle delta: -30 KB gzipped (-1.2 %)
Lines removed: ~450
The verification block at the bottom MUST follow ywc-verify-done's command → exit code → claim shape — see ../ywc-verify-done/SKILL.md. The "PASS" / "FAIL" wording is required; vocabulary like "should pass" / "probably green" is forbidden by ywc-verify-done.
ywc-onboard-repo (when entering a repo where prior dead-code accumulation blocks comprehension).ywc-verify-done (mandatory final claim); ywc-create-pr (cleanup branches ship as their own PRs); ywc-impl-review (reviewer audit of the per-commit deletions, especially CAUTION reclassifications).ywc-confidence-gate (when a deletion is ambiguous between CAUTION and DANGER, the gate's PROCEED / REVIEW / STOP bands map directly: ≥90 = CAUTION delete, 70-89 = REVIEW with reviewer, <70 = re-classify to DANGER).ywc-code-gen in the same branch (cleanup and feature must ship separately — see Rationalization Defense row 8).Before declaring the cleanup pass complete, verify:
chore(cleanup): remove ...)ywc-code-gen)ywc-verify-done block uses the canonical wording (PASS / FAIL — never "should pass")(Procedural failure modes specific to dead-code removal. Behavioral rationalizations are in the table above — do not duplicate here.)
git checkout -- <file> as the rollback for a multi-file deletion. If the deletion spanned files (e.g., removed a function and its sole caller's import), the checkout reverts only one file and leaves the codebase in a half-deleted state. Use git revert <commit> instead — atomic rollback of the entire commit.--dry-run finding without re-running the tool live. --dry-run output is a snapshot; if another commit landed between the snapshot and the live run, the finding may be stale. Always re-run live before deletion.| Reference | Use when |
|---|---|
| references/detection-tools.md | Picking a tool for a new ecosystem; writing the grep-based fallback for an unsupported language |
| references/safety-tiers.md | Classifying a borderline item (SAFE vs CAUTION vs DANGER) — full rules with concrete examples |
| ../ywc-verify-done/SKILL.md | Per-batch verification block shape (command + exit code + claim) — mandatory at Step 7 |
| ../ywc-code-gen/SKILL.md | When a "duplicate consolidation" turns out to require behavior reconciliation, route there |
| ../ywc-confidence-gate/SKILL.md | Borderline CAUTION ↔ DANGER classifications — use the 5-dimension rubric to decide |
npx claudepluginhub yongwoon/ywc-agent-toolkitGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.