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."4user-invocable: true5allowed-tools: Agent, Bash, Read, Edit, Grep, Glob6kernel:7 kind: workflow8 version: 19 side_effects: writes_source10 confirmation: none11---1213<skill id="simplify">1415<purpose>16Simplification makes the road easier; it does not change the destination. Preserve every required17outcome, feature, behavior, and explicit constraint. Simplify only how they are delivered: fewer18branches, concepts, components, dependencies, states, handoffs, and duplicated mechanisms.1920For code, this skill forces a measured per-function number, records its movement, and installs the21same check in the project's normal verification. Prose about "cleaner code" is not accepted.2223Adapted from saurabhkumar8112/cyclomatic-complexity-skill (Apache-2.0). The refactoring model24never signs its own result; a verifier re-measures and runs the armed project gate.25</purpose>2627<on_start>28agentdb recall "simplify complexity <files/symbols>" --global29</on_start>3031<measure>32```bash33# violations, worst first (TSV: file, line, function, ccn, nloc)34${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh <repo-dir>35# only what this branch changed36${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh <repo-dir> <base-ref>37# complete snapshot + before/current diff38${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --all <repo-dir> > before.tsv39${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --diff before.tsv <repo-dir>40# CI ratchet: baseline contains only current over-budget debt41${CLAUDE_PLUGIN_ROOT}/scripts/complexity.sh --check-baseline .complexity-baseline.tsv <repo-dir>42```4344JS/TS uses the project's installed ESLint and parser, so object-literal methods are real45functions. Lizard is the fallback for other languages. Its JS/TS fallback is NOT AST-complete:46it can attribute hundreds of lines to the preceding function and never enter methods inside a47returned object literal. The command names this fallback on stderr. Never treat it as proof for48that pattern: install/configure ESLint, or explicitly skip the file with a reason.4950Project config is `.ccnrc` JSON:5152```json53{54 "version": 1,55 "default": 15,56 "budgets": { "src/machine.ts:transition": 20 },57 "skip": { "src/legacy-repo.ts:*": "lizard cannot enter createLegacyRepo's returned methods" }58}59```6061Selectors are `repo-relative-file:function` globs. `--skip 'selector=reason'` is the one-run62equivalent. A skip without a reason is invalid. Budgets are declared design constraints, not a63way to bless today's number: name why the function earns the higher ceiling in the config diff.6465Exit 2 means invalid config or analyzer failure. Exit 3 means no analyzer. Either blocks66verification. Never replace an unavailable parser with an unlabelled hand count.6768Ladder (default, project config outranks it):69- 1 to 5: leave alone70- 6 to 10: refactor only if already touching71- 11 to 15: refactor now72- over 15: must split73</measure>7475<workflow>761. Freeze the destination before editing: list every required outcome, feature, behavior, explicit77 constraint, and acceptance condition from the request and current source of truth. This is the78 preservation contract. Deferred is not preserved unless the source already defers it.792. Trace each contract item to a concrete part of the proposed result. If any item has no route,80 the simplification is invalid. Never redefine product scope, success, or priority to make the81 implementation smaller.823. Measure. Print the table before touching anything. Rank by CCN descending.834. Confirm tests exist and pass. None: say so, refactor conservatively, propose one test per84 extracted function.855. Save `--all` output as the before baseline. Refactor worst first, one function at a time.866. Re-measure with `--diff before.tsv`. Any `regressed` row is unresolved.877. Wire the project-owned gate before handoff:88 - add a `complexity` script/check using the project's checked-in runner or native analyzer;89 - seed `.complexity-baseline.tsv` with current over-budget rows only; the CI ratchet90 grandfathers those exact values, rejects increases/new violations, and requires a refreshed91 baseline after reductions or removals;92 - include it in `npm run verify`, Make/just verify, or the existing pre-commit gate;93 - run that exact parent command red against a seeded over-budget fixture, then green;94 - never point CI at a developer's plugin-cache path.958. Re-check every preservation-contract item against the result. A shorter plan, backlog, or code96 path that drops, weakens, postpones, or makes optional any item is a regression, even if its97 complexity score improves.989. Hand off to the verifier. Its fresh diff, preservation check, and armed-gate run are the record.99</workflow>100101<tactics order="preference">1021. Guard clauses: invert, return early, kill nesting.1032. Extract function. The name says what, not how. Names are documentation.1043. Lookup table or map instead of if/else or switch chains.1054. Named predicates: `if is_eligible_for_refund(order)` beats a four-clause boolean.1065. Polymorphism or strategy for switch-on-type, only when the switch appears in 2+ places.1076. Flatten loops: extract the body, `continue` instead of nested `if`.108</tactics>109110<hard_rules>111- Preserve the destination: every requested outcome and feature remains required and reachable.112 Simplify architecture and execution, never the user's ambition or product scope.113- Preserve behavior. Tests before and after. Same inputs, same outputs, same errors.114- Removal is allowed only for duplication or machinery whose absence cannot change any preservation-115 contract item. "Not now," "later," "manual for the pilot," and narrower audiences/products are116 scope cuts when the source of truth did not already say them.117- Do not use sequencing as deletion. Later phases must still name their delivery route and acceptance118 condition; evidence gates may reorder work, but cannot cancel it.119- Do not game the metric. A dense one-liner hiding six branches is worse than the honest120 if-chain it replaced. Complexity moves into named units; it never disappears into cleverness.121 A CCN drop with a rising token count per line is the tell.122- Do not change public APIs or exported signatures without asking.123- One responsibility per function. If the name needs "and", split again.124- Small functions with clear names beat few functions with section comments.125- No optional finish: a manual complexity command without project verify/pre-commit wiring is incomplete.126- Never introduce the gate red on the existing default branch. Snapshot current debt, declare narrow127 budgets with reasons, then tighten them as functions improve.128</hard_rules>129130<verify>131Spawn a verifier that never saw this session's reasoning. It receives: the diff, the before132table, the claimed after table, the test command, and this contract:133134```135ACCEPTANCE: every preservation-contract item has an equally strong delivery route; no measured function regresses; budgets hold; the project's normal verify path runs the gate136ACCEPT 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 changed137CHECK: 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 )'138ESCALATE 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 name139DISCOVERY AXIS: invariant140```141Builder and verifier identities go on the receipt. The builder never fills in "behavior verified".142</verify>143144<output>145End with, and nothing after it:146```147## Complexity report148Reduced: N · unchanged: N · regressed: 0 · removed: N149Destination: N/N outcomes and features preserved · weakened/deferred/removed: 0150Budgets: <config path>; exceptions: <none | selectors + reasons>151Gate: <project verify command> (seeded red -> clean green)152Verified by: <verifier identity>153```154Keep prose minimal. Numbers and diffs do the talking.155</output>156157</skill>