agentleFS
DanaResearchGroupDanaResearchGroup/agent-skills/crap/SKILL.md

crap

Skill1 starsChanged 3 months agoMITView on GitHub

Score Python functions by CRAP (cyclomatic complexity combined with test coverage) and gate on a threshold, scoped to the functions a diff touches. Use when cleaning up agent-written code before a PR, when a contract Verifier needs a complexity/coverage gate, when asked which functions are too complex or too untested, or when running a "cleaner" stage.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run it. For each FAIL row, read the cc and cov columns:

    line 54
  2. Very specific:

    cov low: write tests for the untested paths.

    line 55
  3. Distinctive:

    Never pass the gate by:

    line 60

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

CRAP

CRAP (Change Risk Anti-Patterns) scores one function as cc² · (1 − cov)³ + cc, where cc is its cyclomatic complexity and cov is the fraction of it the tests exercise. Two properties make it a good gate for agent code:

  • At full coverage the score equals cc. Tests cannot rescue a function with too many paths; only splitting it can.
  • An untested function scores cc² + cc. Even a modest one fails, so the fix is a test.

The default threshold is 6. Uncle Bob keeps human code at 4 or below and lets agents go to 6; raise it per repo only with a reason written down, never to get a red run green.

Run it

  1. Produce a coverage.py JSON report, with branch coverage, from the repo root, in the repo's own activated environment:

    pytest <tests> --cov=<package> --cov-branch --cov-report=
    coverage json -o "$TMPDIR/cov.json"
  2. Score the functions the change touched:

    ~/.claude/skills/crap/bin/crap --coverage "$TMPDIR/cov.json" --diff <base>

    --diff <base> compares the working tree against <base>, usually the branch's merge-base with main; untracked new files count as changed in full. Without --diff it scores every function in every .py file under the root (git-tracked and unignored ones in a repo), including files the report lacks. That is useful for a survey, but never gate a legacy repo on it: on ARC one file has 35 of 81 functions above 6.

ExitMeaning
0every scored function ≤ threshold
1at least one above it: the rows marked FAIL
2bad input: unreadable report, bad diff base, unparsable file

Other flags: --threshold N, --json, --top N, --exclude GLOB (test files are excluded by default), and trailing paths to limit the scope.

The cleaner loop

As a contract Verifier, the command plus exits 0 is the whole check. The loop:

  1. Run it. For each FAIL row, read the cc and cov columns:
    • cov low: write tests for the untested paths.
    • cc above the threshold: split the function, or move its branching into a lookup or into smaller named helpers. More tests will not move the score.
  2. Regenerate coverage and rerun until it exits 0.

Never pass the gate by:

  • deleting or weakening tests,
  • adding # pragma: no cover,
  • excluding the file,
  • raising the threshold.

Each of those makes the gate report clean while the code stays the same. If a function genuinely cannot be split, stop and report it with its score rather than pass by any of those routes.

Because a deterministic check decides acceptance, the loop does not need a strong model: a free-tier one can run it.

Limits

  • Python only.
  • Coverage is statement plus branch coverage from coverage.py, not the basis-path coverage of the original CRAP paper. Functions missing from the report are scored as uncovered and counted in the summary line.
  • cc is McCabe complexity from the AST: one point for each if/elif, loop, except, ternary, extra boolean operand, comprehension for/if, and refutable case.
  • Nested functions are scored separately from their parent. A lambda counts toward the function that contains it; one at module or class level is scored on its own as <lambda>, always as uncovered: its line runs when it is defined, so the report cannot tell whether the body ever did. Above cc 2 that fails the gate whatever the tests do; make it a def.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.