aboutsummaryrefslogtreecommitdiff
path: root/.claude/commands/refactor.md
blob: 97ce366b20299f82bbc952ab98a33092a31377ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
---
description: Scan code for refactoring opportunities or perform a targeted refactor. Seven modes — `full` (default; complexity + duplication + dead-code + simplification scans), `quick` (high-severity findings only), `complexity` (length / nesting / cyclomatic / parameter count / boolean ops with severity bands and techniques like guard clauses, extract method/predicate, parameter object, decompose conditional), `duplication` (clones / logic / constants / patterns / error-handling with extract-function / parameterize / template-method strategies), `dead-code` (imports / exports / branches / feature flags / deps with high/medium/low confidence labels), `simplification` (over-defensive guards / needless indirection / convoluted logic / redundant state / legibility rewrites — behavior-preserving clarity and size reduction, distinct from the metric-driven complexity scan), `rename old new` (codebase-wide symbol rename with reference search, preview gate, atomic commit, post-apply verification). Findings render as `[SEVERITY] Category — File / Metric / Issue / Suggestion` blocks plus a summary table and quick-wins. Structure-only — no feature work mixed in, no auto-apply without confirmation, characterization tests first when coverage is missing, small focused commits. Use for cleanup or wide renames. Do NOT use for behavior changes (`fix:` or `feat:`, not refactor), green-field design (use `/arch-design`), or single-symbol single-file renames (just edit). Companion to `/add-tests` for the characterization-test prereq.
argument-hint: "[scope: full|quick|complexity|duplication|dead-code|rename old new]"
---

# /refactor — Code Refactoring Skill

Parse `$ARGUMENTS` to determine the operation:

| Argument | Description |
|----------|-------------|
| `full` (default) | Run all scans: complexity + duplication + dead code + simplification |
| `quick` | High-severity issues only (critical/high across all scans) |
| `complexity` | Analyze code complexity: nesting, length, parameters, boolean expressions |
| `duplication` | Detect duplicated logic, clone blocks, repeated patterns |
| `dead-code` | Find unused imports, exports, unreachable code, dead feature flags |
| `simplification` | Find over-complicated code: redundant guards, needless indirection, convoluted logic, redundant state, legibility rewrites |
| `rename old new` | Codebase-wide symbol rename with verification |

If a file or directory path is included in the arguments, scope the scan to that path. Otherwise scan the project source directories (exclude vendored code, node_modules, build output, test fixtures).

---

## General Rules (All Modes)

- Refactoring changes structure, not behavior. If behavior changes, that is a fix, not a refactoring.
- Never refactor and add features in the same step.
- Run tests after each change to confirm behavior preservation.
- If tests are missing for the code being refactored, write characterization tests first.
- Commit each refactoring step individually with a message naming the specific transformation.
- Present findings to the user before making changes. Do not auto-apply without confirmation.

---

## Mode: Complexity

Scan for code that is too complex to maintain, test, or understand.

### What to Check

**Function length:**

| Lines | Severity | Action |
|-------|----------|--------|
| 1-30 | OK | — |
| 31-50 | Medium | Consider splitting |
| 51-100 | High | Split recommended |
| 101+ | Critical | Split required |

**Nesting depth:**

| Levels | Severity | Action |
|--------|----------|--------|
| 1-2 | OK | — |
| 3 | Medium | Consider flattening |
| 4+ | High | Refactor: guard clauses, extract method |

**Cyclomatic complexity** (count decision points: if, else if, else, case, for, while, catch, &&, ||):

| Score | Severity | Action |
|-------|----------|--------|
| 1-5 | OK | — |
| 6-10 | Medium | Consider simplifying |
| 11-15 | High | Refactor recommended |
| 16+ | Critical | Refactor required |

**Parameter count:**

| Params | Severity | Action |
|--------|----------|--------|
| 0-3 | OK | — |
| 4-5 | Medium | Consider parameter object |
| 6+ | High | Use parameter object or split function |

**Complex boolean expressions** (count operators in a single condition):

| Operators | Severity | Action |
|-----------|----------|--------|
| 1-2 | OK | — |
| 3-4 | Medium | Extract to named predicate |
| 5+ | High | Extract required |

### Detection Heuristics

Use Grep to find candidates:

- Long functions: Search for function/method definitions, then count lines to closing brace/end
- Deep nesting: Search for lines with 4+ levels of indentation inside control structures
- Complex conditions: Search for lines with 3+ `&&` or `||` operators
- Long parameter lists: Search for function signatures spanning 60+ characters in the parameter area

### Refactoring Strategies

Apply the simplest effective technique:

1. **Guard clauses** — Flatten nested if/else by returning early
2. **Extract method** — Pull a named function from a block that has a comment explaining it or that does a distinct sub-task
3. **Extract predicate** — Replace complex boolean expression with a named function returning bool
4. **Parameter object** — Group related parameters into a struct/type/alist
5. **Decompose conditional** — Replace if/else branches with named functions for each branch

---

## Mode: Duplication

Scan for duplicated code that violates DRY.

### What to Check

**Clone blocks** (nearly identical code in multiple places):

| Lines | Severity | Action |
|-------|----------|--------|
| 5-10 | Low | Consider extraction |
| 11-25 | Medium | Should extract |
| 26-50 | High | Must extract |
| 51+ | Critical | Extract immediately |

**Logic duplication** — Same algorithm with minor variations (different variable names, different field access, different types).

**Constant duplication** — Same magic number or string literal in 3+ places.

**Pattern repetition** — Same structural pattern (setup-execute-teardown, validate-process-respond) repeated across files.

**Error handling duplication** — Same try/catch or condition-case pattern repeated in many functions.

### Detection Heuristics

- Search for identical or near-identical multi-line blocks across files
- Search for the same string literal appearing in 3+ locations
- Search for functions with very similar names that suggest copy-paste origin
- Search for the same error message string in multiple places

### Refactoring Strategies

1. **Extract function** — Pull the duplicated block into a shared function with parameters for the varying parts
2. **Extract constant** — Replace magic numbers/strings with named constants
3. **Parameterize** — If two functions differ only in one value, merge into one function with a parameter
4. **Template method / higher-order function** — If the structure is the same but the operations differ, extract the structure and accept operations as arguments

---

## Mode: Dead Code

Find code that is never executed or referenced.

### Step-by-step

1. **Unused imports** — Use available tooling or grep for imports, then search for usage of each imported symbol. Language hints:
   - TypeScript/JS: `tsc --noUnusedLocals --noEmit`, or ESLint `no-unused-vars`
   - Python: `ruff check --select F401` or `pyflakes`
   - Go: compiler catches these automatically
   - Emacs Lisp: search for `(require 'X)` then grep for symbols from X
   - Rust: compiler warns on dead code

2. **Unused exports / public functions** — For each exported or public symbol, search the codebase for references. If zero references and not a public API entry point, flag it.

3. **Unreachable code** — Look for:
   - Code after unconditional return/throw/break/signal
   - Branches with conditions that are always true or always false
   - Functions defined but never called
   - Commented-out code blocks (these should be deleted; version control has the history)

4. **Dead feature flags** — Search for feature flag checks or environment variable guards. If the flag is always true/false in all environments, remove the dead branch.

5. **Unused dependencies** — Compare declared dependencies against actual imports. Flag packages with zero import references.

### Presentation

Group findings by category with confidence levels:
- **High confidence** — Definitely unused, safe to remove
- **Medium confidence** — Likely unused, needs manual review
- **Low confidence** — Possibly unused, dynamic references may exist

### Rules

- Never remove code used via dynamic imports, reflection, metaprogramming, or string-based references
- Preserve public API exports in libraries
- Skip test utilities, fixtures, and dev-only code unless explicitly asked
- Remove code in small, focused commits — one category per commit
- Never remove error handling or fallback code just because it hasn't triggered yet
- Run the full test suite after each removal batch

---

## Mode: Simplification

Scan for code that's more complicated than it needs to be — a plainer, smaller, more direct expression of the same behavior. Behavior-preserving like every other mode; this targets clarity and size, not metrics (complexity mode) or repetition (duplication mode).

### What to Check

- **Over-defensive / redundant guards** — existence checks and fallbacks for conditions that can't occur given the actual call sites.
- **Needless indirection / unearned abstraction** — single-use closures, helpers, or wrappers; a single-use local that could inline (or a repeated expression that should be a local); a parameter always passed the same value; an option or branch never exercised.
- **Convoluted logic expressible more directly** — a manual loop that's a map/filter/comprehension; a verbose if-chain that's a lookup table; an if/else assigning a value that's a ternary; boolean expressions that simplify; redundant intermediate computations.
- **Redundant state** — a cache rebuilt every call anyway; two variables tracking the same thing; write-only "dead storage" (assigned, never read); dead flags.
- **Legibility rewrites** — named locals to expose a decision matrix obscured by indexing or chaining. No structural change, large readability win.

For identical-twin branches and plain deletion of unreachable code, see Mode: Dead Code; for repeated literals → named constant, see Mode: Duplication. Those modes already own that detection.

### Detection Heuristics

- Search for guard clauses and fallbacks (null checks, default branches), then check every call site to see whether the guarded condition can actually occur
- Search for helpers, closures, and locals referenced exactly once
- Search for manual index/accumulator loops that build or filter a collection
- Search for variables assigned but never read, and for values recomputed on every call that never change

### Rules

- Behavior-preserving only.
- Verify against **all** call sites before deleting a guard, dropping an option, or removing "never read" state — "can't occur" and "never read" are only true relative to every caller. Never remove a guard or fallback that's genuinely reachable.
- Run the test suite after each change.
- Present findings before applying.

### Boundary with /simplify

`/refactor simplification` sweeps the whole tree (or a scoped path), presents findings, and applies on confirmation. The built-in `/simplify` works on the current diff and applies its lenses directly. Reach for `/simplify` on a change in flight; reach for `/refactor simplification` to sweep existing code.

---

## Mode: Rename

Perform a codebase-wide symbol rename.

Parse `$ARGUMENTS` for: `rename <old-name> <new-name>`

### Step-by-step

1. **Determine symbol type** — Variable, function, class, type, file, directory, CSS class, config key.

2. **Find all references:**
   - Source code: imports, exports, usages, type references
   - Tests: descriptions, assertions, mocks, fixtures
   - Configuration: env vars, config files, build scripts
   - Documentation: comments, README, API docs, org files

3. **If renaming a file:**
   - Update all import/require paths referencing the old filename
   - Update dynamic imports or autoloads
   - Update config references (tsconfig paths, Makefile targets, load-path entries)

4. **Preview changes** — Show every file that will be modified with the specific line changes. Highlight ambiguous matches that might be false positives.

5. **Get confirmation** — Do not apply until the user approves.

6. **Apply changes** across all files in a single atomic commit.

7. **Verify** — Run tests and type checker / byte-compiler to confirm nothing broke.

### Rules

- Always preview before applying
- Handle case sensitivity: distinguish `myFunc`, `MyFunc`, `MY_FUNC`, `my-func`
- Do not rename inside vendored/dependency directories
- Preserve casing conventions (camelCase, PascalCase, snake_case, kebab-case)
- Check string literals that may reference the symbol (routes, error messages, logging)
- Update related names when appropriate (renaming `User` should prompt about `UserProps`, `UserSchema`, etc.)

---

## Output Format

For every finding in any scan mode, use this format:

```
[SEVERITY] Category — Title
File: path/to/file:line
Metric: specific measurement (e.g., "cyclomatic complexity: 18", "47 duplicated lines")
Issue: what the problem is (1-2 sentences)
Suggestion: recommended refactoring technique
```

Severity levels: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`

### After All Findings

Present a summary:

```
Refactoring Scan Summary
========================
Critical: N
High:     N
Medium:   N
Low:      N

Quick wins (low effort, high impact):
1. ...
2. ...
3. ...

Shall I apply any of these? (specify by number, or "all quick wins")
```

Do not generate a separate report file unless the user asks for one. Present findings inline in the conversation.