From 2eb4251b9a489c1b25178ad7cb4777701509e802 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 16 Feb 2026 22:37:18 +0900 Subject: [PATCH] refactor: rewrite remove-deadcode command for parallel deep agent batching --- .opencode/command/remove-deadcode.md | 433 ++++++++++----------------- 1 file changed, 156 insertions(+), 277 deletions(-) diff --git a/.opencode/command/remove-deadcode.md b/.opencode/command/remove-deadcode.md index 835ad6cbf..b254ff7e3 100644 --- a/.opencode/command/remove-deadcode.md +++ b/.opencode/command/remove-deadcode.md @@ -3,337 +3,216 @@ description: Remove unused code from this project with ultrawork mode, LSP-verif --- -You are a dead code removal specialist. Execute the FULL dead code removal workflow using ultrawork mode. -Your core weapon: **LSP FindReferences**. If a symbol has ZERO external references, it's dead. Remove it. +Dead code removal via massively parallel deep agents. You are the ORCHESTRATOR — you scan, verify, batch, then delegate ALL removals to parallel agents. -## CRITICAL RULES + +- **LSP is law.** Verify with `LspFindReferences(includeDeclaration=false)` before ANY removal decision. +- **Never remove entry points.** `src/index.ts`, `src/cli/index.ts`, test files, config files, `packages/` — off-limits. +- **You do NOT remove code yourself.** You scan, verify, batch, then fire deep agents. They do the work. + -1. **LSP is law.** Never guess. Always verify with `LspFindReferences` before removing ANYTHING. -2. **One removal = one commit.** Every dead code removal gets its own atomic commit. -3. **Test after every removal.** Run `bun test` after each. If it fails, REVERT and skip. -4. **Leaf-first order.** Remove deepest unused symbols first, then work up the dependency chain. Removing a leaf may expose new dead code upstream. -5. **Never remove entry points.** `src/index.ts`, `src/cli/index.ts`, test files, config files, and files in `packages/` are off-limits unless explicitly targeted. + +NEVER mark as dead: +- Symbols in `src/index.ts` or barrel `index.ts` re-exports +- Symbols referenced in test files (tests are valid consumers) +- Symbols with `@public` / `@api` JSDoc tags +- Hook factories (`createXXXHook`), tool factories (`createXXXTool`), agent definitions in `agentSources` +- Command templates, skill definitions, MCP configs +- Symbols in `package.json` exports + --- -## STEP 0: REGISTER TODO LIST (MANDATORY FIRST ACTION) +## PHASE 1: SCAN — Find Dead Code Candidates -``` -TodoWrite([ - {"id": "scan", "content": "PHASE 1: Scan codebase for dead code candidates using LSP + explore agents", "status": "pending", "priority": "high"}, - {"id": "verify", "content": "PHASE 2: Verify each candidate with LspFindReferences - zero false positives", "status": "pending", "priority": "high"}, - {"id": "plan", "content": "PHASE 3: Plan removal order (leaf-first dependency order)", "status": "pending", "priority": "high"}, - {"id": "remove", "content": "PHASE 4: Remove dead code one-by-one (remove -> test -> commit loop)", "status": "pending", "priority": "high"}, - {"id": "final", "content": "PHASE 5: Final verification - full test suite + build + typecheck", "status": "pending", "priority": "high"} -]) -``` +Run ALL of these in parallel: ---- + -## PHASE 1: SCAN FOR DEAD CODE CANDIDATES - -**Mark scan as in_progress.** - -### 1.1: Launch Parallel Explore Agents (ALL BACKGROUND) - -Fire ALL simultaneously: - -``` -// Agent 1: Find all exported symbols -task(subagent_type="explore", run_in_background=true, - prompt="Find ALL exported functions, classes, types, interfaces, and constants across src/. - List each with: file path, line number, symbol name, export type (named/default). - EXCLUDE: src/index.ts root exports, test files. - Return as structured list.") - -// Agent 2: Find potentially unused files -task(subagent_type="explore", run_in_background=true, - prompt="Find files in src/ that are NOT imported by any other file. - Check import/require statements across the entire codebase. - EXCLUDE: index.ts files, test files, entry points, config files, .md files. - Return list of potentially orphaned files.") - -// Agent 3: Find unused imports within files -task(subagent_type="explore", run_in_background=true, - prompt="Find unused imports across src/**/*.ts files. - Look for import statements where the imported symbol is never referenced in the file body. - Return: file path, line number, imported symbol name.") - -// Agent 4: Find functions/variables only used in their own declaration -task(subagent_type="explore", run_in_background=true, - prompt="Find private/non-exported functions, variables, and types in src/**/*.ts that appear - to have zero usage beyond their declaration. Return: file path, line number, symbol name.") -``` - -### 1.2: Direct AST-Grep Scans (WHILE AGENTS RUN) - -```typescript -// Find unused imports pattern -ast_grep_search(pattern="import { $NAME } from '$PATH'", lang="typescript", paths=["src/"]) - -// Find empty export objects -ast_grep_search(pattern="export {}", lang="typescript", paths=["src/"]) -``` - -### 1.3: Collect All Results - -Collect background agent results. Compile into a master candidate list: - -``` -## DEAD CODE CANDIDATES - -| # | File | Line | Symbol | Type | Confidence | -|---|------|------|--------|------|------------| -| 1 | src/foo.ts | 42 | unusedFunc | function | HIGH | -| 2 | src/bar.ts | 10 | OldType | type | MEDIUM | -``` - -**Mark scan as completed.** - ---- - -## PHASE 2: VERIFY WITH LSP (ZERO FALSE POSITIVES) - -**Mark verify as in_progress.** - -For EVERY candidate from Phase 1, run this verification: - -### 2.1: The LSP Verification Protocol - -For each candidate symbol: - -```typescript -// Step 1: Find the symbol's exact position -LspDocumentSymbols(filePath) // Get line/character of the symbol - -// Step 2: Find ALL references across the ENTIRE workspace -LspFindReferences(filePath, line, character, includeDeclaration=false) -// includeDeclaration=false → only counts USAGES, not the definition itself - -// Step 3: Evaluate -// 0 references → CONFIRMED DEAD CODE -// 1+ references → NOT dead, remove from candidate list -``` - -### 2.2: False Positive Guards - -**NEVER mark as dead code if:** -- Symbol is in `src/index.ts` (package entry point) -- Symbol is in any `index.ts` that re-exports (barrel file check: look if it's re-exported) -- Symbol is referenced in test files (tests are valid consumers) -- Symbol has `@public` or `@api` JSDoc tags -- Symbol is in a file listed in `package.json` exports -- Symbol is a hook factory (`createXXXHook`) registered in `src/index.ts` -- Symbol is a tool factory (`createXXXTool`) registered in tool loading -- Symbol is an agent definition registered in `agentSources` -- File is a command template, skill definition, or MCP config - -### 2.3: Build Confirmed Dead Code List - -After verification, produce: - -``` -## CONFIRMED DEAD CODE (LSP-verified, 0 external references) - -| # | File | Line | Symbol | Type | Safe to Remove | -|---|------|------|--------|------|----------------| -| 1 | src/foo.ts | 42 | unusedFunc | function | YES | -``` - -**If ZERO confirmed dead code found: Report "No dead code found" and STOP.** - -**Mark verify as completed.** - ---- - -## PHASE 3: PLAN REMOVAL ORDER - -**Mark plan as in_progress.** - -### 3.1: Dependency Analysis - -For each confirmed dead symbol: -1. Check if removing it would expose other dead code -2. Check if other dead symbols depend on this one -3. Build removal dependency graph - -### 3.2: Order by Leaf-First - -``` -Removal Order: -1. [Leaf symbols - no other dead code depends on them] -2. [Intermediate symbols - depended on only by already-removed dead code] -3. [Dead files - entire files with no live exports] -``` - -### 3.3: Register Granular Todos - -Create one todo per removal: - -``` -TodoWrite([ - {"id": "remove-1", "content": "Remove unusedFunc from src/foo.ts:42", "status": "pending", "priority": "high"}, - {"id": "remove-2", "content": "Remove OldType from src/bar.ts:10", "status": "pending", "priority": "high"}, - // ... one per confirmed dead symbol -]) -``` - -**Mark plan as completed.** - ---- - -## PHASE 4: ITERATIVE REMOVAL LOOP - -**Mark remove as in_progress.** - -For EACH dead code item, execute this exact loop: - -### 4.1: Pre-Removal Check - -```typescript -// Re-verify it's still dead (previous removals may have changed things) -LspFindReferences(filePath, line, character, includeDeclaration=false) -// If references > 0 now → SKIP (previous removal exposed a new consumer) -``` - -### 4.2: Remove the Dead Code - -Use appropriate tool: - -**For unused imports:** -```typescript -Edit(filePath, oldString="import { deadSymbol } from '...';\n", newString="") -// Or if it's one of many imports, remove just the symbol from the import list -``` - -**For unused functions/classes/types:** -```typescript -// Read the full symbol extent first -Read(filePath, offset=startLine, limit=endLine-startLine+1) -// Then remove it -Edit(filePath, oldString="[full symbol text]", newString="") -``` - -**For dead files:** +**TypeScript strict mode (your primary scanner — run this FIRST):** ```bash -# Only after confirming ZERO imports point to this file -rm "path/to/dead-file.ts" +bunx tsc --noEmit --noUnusedLocals --noUnusedParameters 2>&1 +``` +This gives you the definitive list of unused locals, imports, parameters, and types with exact file:line locations. + +**Explore agents (fire ALL simultaneously as background):** + +``` +task(subagent_type="explore", run_in_background=true, load_skills=[], + description="Find orphaned files", + prompt="Find files in src/ NOT imported by any other file. Check all import statements. EXCLUDE: index.ts, *.test.ts, entry points, .md, packages/. Return: file paths.") + +task(subagent_type="explore", run_in_background=true, load_skills=[], + description="Find unused exported symbols", + prompt="Find exported functions/types/constants in src/ that are never imported by other files. Cross-reference: for each export, grep the symbol name across src/ — if it only appears in its own file, it's a candidate. EXCLUDE: src/index.ts exports, test files. Return: file path, line, symbol name, export type.") ``` -**After removal, also clean up:** -- Remove any imports that were ONLY used by the removed code -- Remove any now-empty import statements -- Fix any trailing whitespace / double blank lines left behind + -### 4.3: Post-Removal Verification +Collect all results into a master candidate list. + +--- + +## PHASE 2: VERIFY — LSP Confirmation (Zero False Positives) + +For EACH candidate from Phase 1: ```typescript -// 1. LSP diagnostics on changed file -LspDiagnostics(filePath, severity="error") -// Must be clean (or only pre-existing errors) - -// 2. Run tests -bash("bun test") -// Must pass - -// 3. Typecheck -bash("bun run typecheck") -// Must pass +LspFindReferences(filePath, line, character, includeDeclaration=false) +// 0 references → CONFIRMED dead +// 1+ references → NOT dead, drop from list ``` -### 4.4: Handle Failures +Also apply the false-positive-guards above. Produce a confirmed list: -If ANY verification fails: -1. **REVERT** the change immediately (`git checkout -- [file]`) -2. Mark this removal todo as `cancelled` with note: "Removal caused [error]. Skipped." -3. Proceed to next item - -### 4.5: Commit - -```bash -git add [changed-files] -git commit -m "refactor: remove unused [symbolType] [symbolName] from [filePath]" +``` +| # | File | Symbol | Type | Action | +|---|------|--------|------|--------| +| 1 | src/foo.ts:42 | unusedFunc | function | REMOVE | +| 2 | src/bar.ts:10 | OldType | type | REMOVE | +| 3 | src/baz.ts:7 | ctx | parameter | PREFIX _ | ``` -Mark this removal todo as `completed`. +**Action types:** +- `REMOVE` — delete the symbol/import/file entirely +- `PREFIX _` — unused function parameter required by signature → rename to `_paramName` -### 4.6: Re-scan After Removal +If ZERO confirmed: report "No dead code found" and STOP. -After removing a symbol, check if its removal exposed NEW dead code: -- Were there imports that only existed to serve the removed symbol? -- Are there other symbols in the same file now unreferenced? +--- -If new dead code is found, add it to the removal queue. +## PHASE 3: BATCH — Group by File for Conflict-Free Parallelism -**Repeat 4.1-4.6 for every item. Mark remove as completed when done.** + + +**Goal: maximize parallel agents with ZERO git conflicts.** + +1. Group confirmed dead code items by FILE PATH +2. All items in the SAME file go to the SAME batch (prevents two agents editing the same file) +3. If a dead FILE (entire file deletion) exists, it's its own batch +4. Target 5-15 batches. If fewer than 5 items total, use 1 batch per item. + +**Example batching:** +``` +Batch A: [src/hooks/foo/hook.ts — 3 unused imports] +Batch B: [src/features/bar/manager.ts — 2 unused constants, 1 dead function] +Batch C: [src/tools/baz/tool.ts — 1 unused param, src/tools/baz/types.ts — 1 unused type] +Batch D: [src/dead-file.ts — entire file deletion] +``` + +Files in the same directory CAN be batched together (they won't conflict as long as no two agents edit the same file). Maximize batch count for parallelism. + + + +--- + +## PHASE 4: EXECUTE — Fire Parallel Deep Agents + +For EACH batch, fire a deep agent: + +``` +task( + category="deep", + load_skills=["typescript-programmer", "git-master"], + run_in_background=true, + description="Remove dead code batch N: [brief description]", + prompt="[see template below]" +) +``` + + + +Every deep agent gets this prompt structure (fill in the specifics per batch): + +``` +## TASK: Remove dead code from [file list] + +## DEAD CODE TO REMOVE + +### [file path] line [N] +- Symbol: `[name]` — [type: unused import / unused constant / unused function / unused parameter / dead file] +- Action: [REMOVE entirely / REMOVE from import list / PREFIX with _] + +### [file path] line [N] +- ... + +## PROTOCOL + +1. Read each file to understand exact syntax at the target lines +2. For each symbol, run LspFindReferences to RE-VERIFY it's still dead (another agent may have changed things) +3. Apply the change: + - Unused import (only symbol in line): remove entire import line + - Unused import (one of many): remove only that symbol from the import list + - Unused constant/function/type: remove the declaration. Clean up trailing blank lines. + - Unused parameter: prefix with `_` (do NOT remove — required by signature) + - Dead file: delete with `rm` +4. After ALL edits in this batch, run: `bun run typecheck` +5. If typecheck fails: `git checkout -- [files]` and report failure +6. If typecheck passes: stage ONLY your files and commit: + `git add [your-specific-files] && git commit -m "refactor: remove dead code from [brief file list]"` +7. Report what you removed and the commit hash + +## CRITICAL +- Stage ONLY your batch's files (`git add [specific files]`). NEVER `git add -A` — other agents are working in parallel. +- If typecheck fails after your edits, REVERT all changes and report. Do not attempt to fix. +- Pre-existing test failures in other files are expected. Only typecheck matters for your batch. +``` + + + +Fire ALL batches simultaneously. Wait for all to complete. --- ## PHASE 5: FINAL VERIFICATION -**Mark final as in_progress.** +After ALL agents complete: -### 5.1: Full Test Suite ```bash -bun test +bun run typecheck # must pass +bun test # note any NEW failures vs pre-existing +bun run build # must pass ``` -### 5.2: Full Typecheck -```bash -bun run typecheck -``` - -### 5.3: Full Build -```bash -bun run build -``` - -### 5.4: Summary Report +Produce summary: ```markdown ## Dead Code Removal Complete ### Removed -| # | Symbol | File | Type | Commit | -|---|--------|------|------|--------| -| 1 | unusedFunc | src/foo.ts | function | abc1234 | +| # | Symbol | File | Type | Commit | Agent | +|---|--------|------|------|--------|-------| +| 1 | unusedFunc | src/foo.ts | function | abc1234 | Batch A | -### Skipped (caused failures) +### Skipped (agent reported failure) | # | Symbol | File | Reason | |---|--------|------|--------| -| 1 | riskyFunc | src/bar.ts | Test failure: [details] | ### Verification -- Tests: PASSED (X/Y passing) -- Typecheck: CLEAN -- Build: SUCCESS -- Total dead code removed: N symbols across M files +- Typecheck: PASS/FAIL +- Tests: X passing, Y failing (Z pre-existing) +- Build: PASS/FAIL +- Total removed: N symbols across M files - Total commits: K atomic commits +- Parallel agents used: P ``` -**Mark final as completed.** - --- ## SCOPE CONTROL -**If $ARGUMENTS is provided**, narrow the scan to the specified scope: -- File path: Only scan that file -- Directory: Only scan that directory -- Symbol name: Only check that specific symbol -- "all" or empty: Full project scan (default) +If `$ARGUMENTS` is provided, narrow the scan: +- File path → only that file +- Directory → only that directory +- Symbol name → only that symbol +- `all` or empty → full project scan (default) ## ABORT CONDITIONS -**STOP and report to user if:** -- 3 consecutive removals cause test failures +STOP and report if: +- More than 50 candidates found (ask user to narrow scope or confirm proceeding) - Build breaks and cannot be fixed by reverting -- More than 50 candidates found (ask user to narrow scope) - -## LANGUAGE - -Use English for commit messages and technical output.