/clean-code
Apply the superskills quality standard — KISS, DRY, SOLID, YAGNI — to the
code that changed on this branch, and prove each cleanup with tests. This is the
in-tree counterpart to Claude Code's built-in /simplify: it can be invoked from
inside any session or pipeline (/qa-full Step 3 runs it), and it works the same
in every tool ./setup targets.
The principles are defined once, in ENGINEERING_STANDARDS.md in the
superskills install (a plugin install exposes it at
${CLAUDE_PLUGIN_ROOT}/ENGINEERING_STANDARDS.md). Read its DRY, SOLID,
and YAGNI sections before auditing — this file does not restate them, it
only lists the diff-level signals to look for. Do not expect the file in the
project under review, and do not flag it as missing. KISS is not a separate
section there: it is the standard's "smallest correct change" + YAGNI rules
applied to structure — the simplest design that passes the tests.
Hard rules
- Quality only. This skill does not look for correctness bugs, security
issues, or performance problems. If you notice one, record it as an
out-of-scope note for
/review / /defense and move on.
- Diff-scoped. Audit the files changed on the branch. Refactoring untouched
modules is itself a YAGNI violation. The one exception: when the diff
duplicates a helper that already exists elsewhere, the fix is to use the
existing helper, which may touch its file.
- Clean tree before any fix. Reverting a failed refactor uses
git checkout -- <files>, which discards everything uncommitted in those
files. So no fix is attempted while unrelated uncommitted work exists
(Step 1.4). Never use git stash as the revert mechanism — it stashes the
whole tree, not one change.
- Behavior-preserving. A cleanup changes structure, not behavior. Every fix
runs under the existing tests; if the code you're restructuring has no test,
write a characterization test first (
/tdd), then refactor under green.
- Smallest safe refactor. Extract one helper, not a framework. Inline one
needless indirection, not the whole module. Stop when the finding is gone.
- Ground every finding in evidence: file:line for each duplicate, the two
responsibilities a unit mixes, the abstraction with one implementation, the
config nobody reads. No "this feels complex."
- One atomic commit per fix, message naming the principle and the finding
(e.g.
clean-code: DRY — extract parseRange() used by 3 call sites). Never
push.
- Bounded. One audit pass, one fix round, one verify pass. Report what's
left as warnings — don't polish indefinitely.
Step 1: Scope and preconditions
- Base ref: detect the base the same way
/qa-full Step 1 does (gh repo view default branch → origin/HEAD → main/master), unless
$ARGUMENTS names one, then diff against origin/<base> when that ref
exists (fetch it first), else local <base>. When invoked from /qa-full,
use the diff ref and scope it resolved — do not re-detect.
- Changed files:
git diff --name-only <diff-ref>...HEAD -- <scope paths>
(omit the -- part when no --scope was given) plus any uncommitted
changes to those paths. Keep only source files: skip lockfiles, generated
code, vendored dirs, and pure docs unless docs are the deliverable.
- Tests: find the test command (
CLAUDE.md, package.json scripts,
Makefile, pyproject.toml) and run it once. If the suite is red,
stop — refactoring can't be verified on a red suite. Report and exit.
- Clean tree: if
git status --porcelain is non-empty, ask the user (via
AskUserQuestion) whether to commit the in-progress work now
(recommended) or stash it before continuing. Do not audit-and-fix on a
dirty tree; a revert in Step 3 would take the user's work with it. (Inside
/qa-full this was already handled in its Step 1 — the tree is clean.)
Step 2: Audit (report-only discovery)
Read every changed file in full. For each principle, apply the definition from
ENGINEERING_STANDARDS.md and hunt for these diff-level signals. Record
findings as PRINCIPLE — file:line — evidence — smallest fix, with severity
HIGH (duplicated logic, a unit doing two jobs that will diverge, dead code
on a live path), MEDIUM (wrong altitude, needless abstraction, speculative
config), or LOW (naming, ordering, minor simplification).
- DRY signals: before calling any new function "new", Grep the repo for its
distinctive identifiers, string literals, and regexes — a hit is a duplicate.
Two near-identical branches or helpers within the diff differing by one
argument. A rule or constant pasted where a reference would do (docs and
governance text count). Accepted duplication with no inline reason.
- SOLID signals: a new
if type == X branch bolted onto a stable core; an
implementation that throws or no-ops where its interface's contract returns;
a parameter object passed to read one field; an interface where callers use
two of eight methods; a unit reaching straight for $HOME, the network, the
clock, a global singleton, or new Concrete() where a seam would make it
testable in isolation. Name both responsibilities when flagging a unit that
has two reasons to change.
- KISS signals: an indirection with a single call site and no second
implementation; low-level detail inline in a high-level orchestrator, or a
business rule buried in a utility; nested ternaries, reduce-as-control-flow,
or a regex doing a parser's job; a function whose branches exceed what a
reader holds in one pass (split by responsibility, not line count).
- YAGNI signals: hooks, options, flags, config keys, abstract bases with one
subclass, or toggles nothing consumes (Grep for each consumer — a flag
advertised but never read is a finding); unreachable branches, unused
exports/params/imports, commented-out blocks, TODOs for unplanned work;
restructuring in the diff of code that had no reason to change.
Also note (not as findings) any existing helper you found that the diff should
reuse — that becomes the DRY fix.
Step 3: Fix
For each HIGH and MEDIUM finding, and any LOW whose fix is a one-liner:
- If the code being restructured has no test that would catch a behavior
change, write a characterization test first and watch it pass.
- Apply the smallest safe refactor:
- DRY → extract once, call from every site (or switch to the existing helper
and delete the duplicate).
- SOLID → split by responsibility, introduce the seam, narrow the
interface; keep public signatures stable unless the diff introduced them.
- KISS → inline the single-use indirection, flatten the clever construct,
move code to its right altitude.
- YAGNI → delete the speculative hook/config/dead code; if the user might
want it back, say so in the commit message rather than leaving it in.
- Run the test suite. Green ⇒ commit atomically. Red ⇒ revert only the
files this fix touched with
git checkout -- <those files> (safe because
the tree was clean before the fix), and record the finding as UNFIXED
with the failing test name.
Do not touch findings outside the diff scope, and do not "improve" the tests
themselves beyond what a refactor needs.
Step 4: Verify + report
- Re-run the audit signals on the changed files (now including your fix
commits). Each fixed finding must no longer appear; the fixes must not have
introduced a new duplicate or a new one-use abstraction.
- Run the full test suite one final time on HEAD.
- Print:
# clean-code — <branch> @ <YYYY-MM-DD>
Base: <base> Files audited: N Suite: <command> → green/red
## Fixed (principle — finding — commit)
1. DRY — src/x.ts:40 duplicated parseRange() from src/util/range.ts — <sha>
- (or) None needed.
## Unfixed / deferred (with reason)
- SOLID — src/y.ts:12 mixes validation + persistence — needs interface change beyond the diff
## Warnings (LOW, left as-is)
- …
## Out of scope (hand to /review or /defense)
- …
Every fix lists a commit SHA; every unfixed finding lists why. No claim without
the re-audit and the fresh suite run behind it.
Anti-patterns (do not do)
- Hunting bugs, security issues, or perf regressions — wrong skill.
- Refactoring code the diff didn't touch "while you're here."
- Extracting a helper for two lines used twice, or a base class for one
subclass — that's trading a DRY smell for a YAGNI violation.
- Refactoring without a test that would catch a behavior change.
- Running a fix round on a dirty tree, or reverting with
git stash.
- Leaving a commit that mixes a refactor with any functional change.
- Pushing, opening PRs, or force-anything.
- Reporting "looks clean" without reading every changed file in full.
Related commands
/review — correctness / production-readiness review of the same diff; run
it (or /code-review) for bugs, run this for quality.
/code-review / /simplify — Claude Code built-ins with overlapping scope;
use them when typing commands yourself. Pipelines and other tools use this
skill because it's invocable everywhere.
/tdd — the characterization-test-first discipline this skill's fixes follow.
/test-coverage — writes the missing tests for logic this skill leaves
untested.
/qa-full — runs this skill in Step 3 as the quality half of the correctness
pass, passing it the base and scope it resolved.
/write-plan — applies the same principles at plan time so there's less to
clean up here.
1---2name: clean-code3description: Audit → fix → verify the branch diff against KISS, DRY, SOLID, and YAGNI. Finds duplicated logic, units with more than one reason to change, needless abstraction or speculative extensibility, wrong-altitude code, and dead code in the changed files — then applies the smallest safe refactor for each, one atomic commit per fix, under a green test suite. Quality only: it does not hunt for bugs (use /review or /code-review for that). Use when asked to "clean up the diff", "simplify this", "DRY this up", "refactor for SOLID", "remove duplication", "KISS", "YAGNI check", or "tidy before ship".4---56# /clean-code78Apply the superskills quality standard — **KISS, DRY, SOLID, YAGNI** — to the9code that changed on this branch, and prove each cleanup with tests. This is the10in-tree counterpart to Claude Code's built-in `/simplify`: it can be invoked from11inside any session or pipeline (`/qa-full` Step 3 runs it), and it works the same12in every tool `./setup` targets.1314The principles are **defined once**, in `ENGINEERING_STANDARDS.md` in the15superskills install (a plugin install exposes it at16`${CLAUDE_PLUGIN_ROOT}/ENGINEERING_STANDARDS.md`). Read its **DRY**, **SOLID**,17and **YAGNI** sections before auditing — this file does not restate them, it18only lists the diff-level signals to look for. Do not expect the file in the19project under review, and do not flag it as missing. KISS is not a separate20section there: it is the standard's "smallest correct change" + YAGNI rules21applied to structure — the simplest design that passes the tests.2223## Hard rules2425- **Quality only.** This skill does not look for correctness bugs, security26 issues, or performance problems. If you notice one, record it as an27 out-of-scope note for `/review` / `/defense` and move on.28- **Diff-scoped.** Audit the files changed on the branch. Refactoring untouched29 modules is itself a YAGNI violation. The one exception: when the diff30 duplicates a helper that already exists elsewhere, the fix is to *use* the31 existing helper, which may touch its file.32- **Clean tree before any fix.** Reverting a failed refactor uses33 `git checkout -- <files>`, which discards *everything* uncommitted in those34 files. So no fix is attempted while unrelated uncommitted work exists35 (Step 1.4). Never use `git stash` as the revert mechanism — it stashes the36 whole tree, not one change.37- **Behavior-preserving.** A cleanup changes structure, not behavior. Every fix38 runs under the existing tests; if the code you're restructuring has no test,39 write a characterization test first (`/tdd`), then refactor under green.40- **Smallest safe refactor.** Extract one helper, not a framework. Inline one41 needless indirection, not the whole module. Stop when the finding is gone.42- **Ground every finding in evidence:** file:line for each duplicate, the two43 responsibilities a unit mixes, the abstraction with one implementation, the44 config nobody reads. No "this feels complex."45- **One atomic commit per fix**, message naming the principle and the finding46 (e.g. `clean-code: DRY — extract parseRange() used by 3 call sites`). Never47 push.48- **Bounded.** One audit pass, one fix round, one verify pass. Report what's49 left as warnings — don't polish indefinitely.5051## Step 1: Scope and preconditions52531. **Base ref:** detect the base the same way `/qa-full` Step 1 does (`gh repo54 view` default branch → `origin/HEAD` → `main`/`master`), unless55 `$ARGUMENTS` names one, then diff against `origin/<base>` when that ref56 exists (fetch it first), else local `<base>`. When invoked from `/qa-full`,57 use the diff ref and scope it resolved — do not re-detect.582. **Changed files:** `git diff --name-only <diff-ref>...HEAD -- <scope paths>`59 (omit the `--` part when no `--scope` was given) plus any uncommitted60 changes to those paths. Keep only source files: skip lockfiles, generated61 code, vendored dirs, and pure docs unless docs are the deliverable.623. **Tests:** find the test command (`CLAUDE.md`, `package.json` scripts,63 `Makefile`, `pyproject.toml`) and run it once. **If the suite is red,64 stop** — refactoring can't be verified on a red suite. Report and exit.654. **Clean tree:** if `git status --porcelain` is non-empty, ask the user (via66 AskUserQuestion) whether to **commit the in-progress work now**67 (recommended) or **stash it** before continuing. Do not audit-and-fix on a68 dirty tree; a revert in Step 3 would take the user's work with it. (Inside69 `/qa-full` this was already handled in its Step 1 — the tree is clean.)7071## Step 2: Audit (report-only discovery)7273Read every changed file in full. For each principle, apply the definition from74`ENGINEERING_STANDARDS.md` and hunt for these diff-level signals. Record75findings as `PRINCIPLE — file:line — evidence — smallest fix`, with severity76**HIGH** (duplicated logic, a unit doing two jobs that will diverge, dead code77on a live path), **MEDIUM** (wrong altitude, needless abstraction, speculative78config), or **LOW** (naming, ordering, minor simplification).7980- **DRY signals:** before calling any new function "new", Grep the repo for its81 distinctive identifiers, string literals, and regexes — a hit is a duplicate.82 Two near-identical branches or helpers within the diff differing by one83 argument. A rule or constant pasted where a reference would do (docs and84 governance text count). Accepted duplication with no inline reason.85- **SOLID signals:** a new `if type == X` branch bolted onto a stable core; an86 implementation that throws or no-ops where its interface's contract returns;87 a parameter object passed to read one field; an interface where callers use88 two of eight methods; a unit reaching straight for `$HOME`, the network, the89 clock, a global singleton, or `new Concrete()` where a seam would make it90 testable in isolation. Name both responsibilities when flagging a unit that91 has two reasons to change.92- **KISS signals:** an indirection with a single call site and no second93 implementation; low-level detail inline in a high-level orchestrator, or a94 business rule buried in a utility; nested ternaries, reduce-as-control-flow,95 or a regex doing a parser's job; a function whose branches exceed what a96 reader holds in one pass (split by responsibility, not line count).97- **YAGNI signals:** hooks, options, flags, config keys, abstract bases with one98 subclass, or toggles nothing consumes (Grep for each consumer — a flag99 advertised but never read is a finding); unreachable branches, unused100 exports/params/imports, commented-out blocks, TODOs for unplanned work;101 restructuring in the diff of code that had no reason to change.102103Also note (not as findings) any *existing* helper you found that the diff should104reuse — that becomes the DRY fix.105106## Step 3: Fix107108For each HIGH and MEDIUM finding, and any LOW whose fix is a one-liner:1091101. If the code being restructured has no test that would catch a behavior111 change, write a characterization test first and watch it pass.1122. Apply the smallest safe refactor:113 - DRY → extract once, call from every site (or switch to the existing helper114 and delete the duplicate).115 - SOLID → split by responsibility, introduce the seam, narrow the116 interface; keep public signatures stable unless the diff introduced them.117 - KISS → inline the single-use indirection, flatten the clever construct,118 move code to its right altitude.119 - YAGNI → delete the speculative hook/config/dead code; if the user might120 want it back, say so in the commit message rather than leaving it in.1213. Run the test suite. Green ⇒ commit atomically. Red ⇒ revert **only the122 files this fix touched** with `git checkout -- <those files>` (safe because123 the tree was clean before the fix), and record the finding as **UNFIXED**124 with the failing test name.125126Do not touch findings outside the diff scope, and do not "improve" the tests127themselves beyond what a refactor needs.128129## Step 4: Verify + report1301311. Re-run the audit signals on the changed files (now including your fix132 commits). Each fixed finding must no longer appear; the fixes must not have133 introduced a new duplicate or a new one-use abstraction.1342. Run the full test suite one final time on HEAD.1353. Print:136137```markdown138# clean-code — <branch> @ <YYYY-MM-DD>139Base: <base> Files audited: N Suite: <command> → green/red140141## Fixed (principle — finding — commit)1421. DRY — src/x.ts:40 duplicated parseRange() from src/util/range.ts — <sha>143- (or) None needed.144145## Unfixed / deferred (with reason)146- SOLID — src/y.ts:12 mixes validation + persistence — needs interface change beyond the diff147148## Warnings (LOW, left as-is)149- …150151## Out of scope (hand to /review or /defense)152- …153```154155Every fix lists a commit SHA; every unfixed finding lists why. No claim without156the re-audit and the fresh suite run behind it.157158## Anti-patterns (do not do)159160- Hunting bugs, security issues, or perf regressions — wrong skill.161- Refactoring code the diff didn't touch "while you're here."162- Extracting a helper for two lines used twice, or a base class for one163 subclass — that's trading a DRY smell for a YAGNI violation.164- Refactoring without a test that would catch a behavior change.165- Running a fix round on a dirty tree, or reverting with `git stash`.166- Leaving a commit that mixes a refactor with any functional change.167- Pushing, opening PRs, or force-anything.168- Reporting "looks clean" without reading every changed file in full.169170## Related commands171172- `/review` — correctness / production-readiness review of the same diff; run173 it (or `/code-review`) for bugs, run this for quality.174- `/code-review` / `/simplify` — Claude Code built-ins with overlapping scope;175 use them when typing commands yourself. Pipelines and other tools use this176 skill because it's invocable everywhere.177- `/tdd` — the characterization-test-first discipline this skill's fixes follow.178- `/test-coverage` — writes the missing tests for logic this skill leaves179 untested.180- `/qa-full` — runs this skill in Step 3 as the quality half of the correctness181 pass, passing it the base and scope it resolved.182- `/write-plan` — applies the same principles at plan time so there's less to183 clean up here.