For code, this skill forces a measured per-function number, records its movement, and installs the
same check in the project's normal verification. Prose about "cleaner code" is not accepted.
Adapted from saurabhkumar8112/cyclomatic-complexity-skill (Apache-2.0). The refactoring model
never signs its own result; a verifier re-measures and runs the armed project gate.
JS/TS uses the project's installed ESLint and parser, so object-literal methods are real
functions. Lizard is the fallback for other languages. Its JS/TS fallback is NOT AST-complete:
it can attribute hundreds of lines to the preceding function and never enter methods inside a
returned object literal. The command names this fallback on stderr. Never treat it as proof for
that pattern: install/configure ESLint, or explicitly skip the file with a reason.
Project config is .ccnrc JSON:
{
"version": 1,
"default": 15,
"budgets": { "src/machine.ts:transition": 20 },
"skip": { "src/legacy-repo.ts:*": "lizard cannot enter createLegacyRepo's returned methods" }
}
Selectors are repo-relative-file:function globs. --skip 'selector=reason' is the one-run
equivalent. A skip without a reason is invalid. Budgets are declared design constraints, not a
way to bless today's number: name why the function earns the higher ceiling in the config diff.
Exit 2 means invalid config or analyzer failure. Exit 3 means no analyzer. Either blocks
verification. Never replace an unavailable parser with an unlabelled hand count.
Ladder (default, project config outranks it):
- 1 to 5: leave alone
- 6 to 10: refactor only if already touching
- 11 to 15: refactor now
- over 15: must split
ACCEPTANCE: every preservation-contract item has an equally strong delivery route; no measured function regresses; budgets hold; the project's normal verify path runs the gate
ACCEPT WHEN: item-by-item destination comparison has zero removed, weakened, newly deferred, or optional outcomes; fresh --diff says regressed=0; project verify passes; seeded over-budget fixture makes it fail; no exported signature changed
CHECK: compare the source-of-truth outcomes/features/constraints against the result item by item; ${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --diff <before.tsv> <repo>; <project verify command>; <seeded failure probe>; git diff <base-ref> -- <files> | grep -E '^[-+](def |export |func |pub fn )'
ESCALATE IF: any preservation-contract item lacks a route or became weaker/later/optional; AST-aware JS/TS parsing is unavailable for object-literal methods; any row regresses; a skip lacks a concrete parser limitation; or a one-liner replaced a branch without a name
DISCOVERY AXIS: invariant
Builder and verifier identities go on the receipt. The builder never fills in "behavior verified".
1---2name: simplify3description: Simplify code, plans, and systems without removing required outcomes or features; for code, lower cyclomatic complexity with AST-aware measurement and regression gates. Triggers: simplify, refactor, complexity, spaghetti, overengineered.4---56<skill id="simplify">78<purpose>9Simplification makes the road easier; it does not change the destination. Preserve every required10outcome, feature, behavior, and explicit constraint. Simplify only how they are delivered: fewer11branches, concepts, components, dependencies, states, handoffs, and duplicated mechanisms.1213For code, this skill forces a measured per-function number, records its movement, and installs the14same check in the project's normal verification. Prose about "cleaner code" is not accepted.1516Adapted from saurabhkumar8112/cyclomatic-complexity-skill (Apache-2.0). The refactoring model17never signs its own result; a verifier re-measures and runs the armed project gate.18</purpose>1920<on_start>21agentdb recall "simplify complexity <files/symbols>" --global22</on_start>2324<measure>25```bash26# violations, worst first (TSV: file, line, function, ccn, nloc)27${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh <repo-dir>28# only what this branch changed29${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh <repo-dir> <base-ref>30# complete snapshot + before/current diff31${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --all <repo-dir> > before.tsv32${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --diff before.tsv <repo-dir>33# CI ratchet: baseline contains only current over-budget debt34${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --check-baseline .complexity-baseline.tsv <repo-dir>35```3637JS/TS uses the project's installed ESLint and parser, so object-literal methods are real38functions. Lizard is the fallback for other languages. Its JS/TS fallback is NOT AST-complete:39it can attribute hundreds of lines to the preceding function and never enter methods inside a40returned object literal. The command names this fallback on stderr. Never treat it as proof for41that pattern: install/configure ESLint, or explicitly skip the file with a reason.4243Project config is `.ccnrc` JSON:4445```json46{47 "version": 1,48 "default": 15,49 "budgets": { "src/machine.ts:transition": 20 },50 "skip": { "src/legacy-repo.ts:*": "lizard cannot enter createLegacyRepo's returned methods" }51}52```5354Selectors are `repo-relative-file:function` globs. `--skip 'selector=reason'` is the one-run55equivalent. A skip without a reason is invalid. Budgets are declared design constraints, not a56way to bless today's number: name why the function earns the higher ceiling in the config diff.5758Exit 2 means invalid config or analyzer failure. Exit 3 means no analyzer. Either blocks59verification. Never replace an unavailable parser with an unlabelled hand count.6061Ladder (default, project config outranks it):62- 1 to 5: leave alone63- 6 to 10: refactor only if already touching64- 11 to 15: refactor now65- over 15: must split66</measure>6768<workflow>691. Freeze the destination before editing: list every required outcome, feature, behavior, explicit70 constraint, and acceptance condition from the request and current source of truth. This is the71 preservation contract. Deferred is not preserved unless the source already defers it.722. Trace each contract item to a concrete part of the proposed result. If any item has no route,73 the simplification is invalid. Never redefine product scope, success, or priority to make the74 implementation smaller.753. Measure. Print the table before touching anything. Rank by CCN descending.764. Confirm tests exist and pass. None: say so, refactor conservatively, propose one test per77 extracted function.785. Save `--all` output as the before baseline. Refactor worst first, one function at a time.796. Re-measure with `--diff before.tsv`. Any `regressed` row is unresolved.807. Wire the project-owned gate before handoff:81 - add a `complexity` script/check using the project's checked-in runner or native analyzer;82 - seed `.complexity-baseline.tsv` with current over-budget rows only; the CI ratchet83 grandfathers those exact values, rejects increases/new violations, and requires a refreshed84 baseline after reductions or removals;85 - include it in `npm run verify`, Make/just verify, or the existing pre-commit gate;86 - run that exact parent command red against a seeded over-budget fixture, then green;87 - never point CI at a developer's plugin-cache path.888. Re-check every preservation-contract item against the result. A shorter plan, backlog, or code89 path that drops, weakens, postpones, or makes optional any item is a regression, even if its90 complexity score improves.919. Hand off to the verifier. Its fresh diff, preservation check, and armed-gate run are the record.92</workflow>9394<tactics order="preference">951. Guard clauses: invert, return early, kill nesting.962. Extract function. The name says what, not how. Names are documentation.973. Lookup table or map instead of if/else or switch chains.984. Named predicates: `if is_eligible_for_refund(order)` beats a four-clause boolean.995. Polymorphism or strategy for switch-on-type, only when the switch appears in 2+ places.1006. Flatten loops: extract the body, `continue` instead of nested `if`.101</tactics>102103<hard_rules>104- Preserve the destination: every requested outcome and feature remains required and reachable.105 Simplify architecture and execution, never the user's ambition or product scope.106- Preserve behavior. Tests before and after. Same inputs, same outputs, same errors.107- Removal is allowed only for duplication or machinery whose absence cannot change any preservation-108 contract item. "Not now," "later," "manual for the pilot," and narrower audiences/products are109 scope cuts when the source of truth did not already say them.110- Do not use sequencing as deletion. Later phases must still name their delivery route and acceptance111 condition; evidence gates may reorder work, but cannot cancel it.112- Do not game the metric. A dense one-liner hiding six branches is worse than the honest113 if-chain it replaced. Complexity moves into named units; it never disappears into cleverness.114 A CCN drop with a rising token count per line is the tell.115- Do not change public APIs or exported signatures without asking.116- One responsibility per function. If the name needs "and", split again.117- Small functions with clear names beat few functions with section comments.118- No optional finish: a manual complexity command without project verify/pre-commit wiring is incomplete.119- Never introduce the gate red on the existing default branch. Snapshot current debt, declare narrow120 budgets with reasons, then tighten them as functions improve.121</hard_rules>122123<verify>124Spawn a verifier that never saw this session's reasoning. It receives: the diff, the before125table, the claimed after table, the test command, and this contract:126127```128ACCEPTANCE: every preservation-contract item has an equally strong delivery route; no measured function regresses; budgets hold; the project's normal verify path runs the gate129ACCEPT WHEN: item-by-item destination comparison has zero removed, weakened, newly deferred, or optional outcomes; fresh --diff says regressed=0; project verify passes; seeded over-budget fixture makes it fail; no exported signature changed130CHECK: compare the source-of-truth outcomes/features/constraints against the result item by item; ${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --diff <before.tsv> <repo>; <project verify command>; <seeded failure probe>; git diff <base-ref> -- <files> | grep -E '^[-+](def |export |func |pub fn )'131ESCALATE IF: any preservation-contract item lacks a route or became weaker/later/optional; AST-aware JS/TS parsing is unavailable for object-literal methods; any row regresses; a skip lacks a concrete parser limitation; or a one-liner replaced a branch without a name132DISCOVERY AXIS: invariant133```134Builder and verifier identities go on the receipt. The builder never fills in "behavior verified".135</verify>136137<output>138End with, and nothing after it:139```140## Complexity report141Reduced: N · unchanged: N · regressed: 0 · removed: N142Destination: N/N outcomes and features preserved · weakened/deferred/removed: 0143Budgets: <config path>; exceptions: <none | selectors + reasons>144Gate: <project verify command> (seeded red -> clean green)145Verified by: <verifier identity>146```147Keep prose minimal. Numbers and diffs do the talking.148</output>149150</skill>