@unclebob/crap4clj
BCalculates cyclomatic complexity and CRAP scores for Clojure functions by combining complexity analysis with Cloverage test coverage data, generating sorted reports that identify high-risk under-tested code. Use when the user asks for a CRAP report, cyclomatic complexity analysis, or code quality metrics on a Clojure project.
Install
agr install @unclebob/crap4clj --target claudeWrites 6 files into .claude/skills/, pinned to git-a22525e1.
- .claude/skills/crap4clj/.gitignore
- .claude/skills/crap4clj/BUGREPORT-clj-mutate-stale-lcov-and-worker-collision.md
- .claude/skills/crap4clj/README.md
- .claude/skills/crap4clj/SKILL.md
- .claude/skills/crap4clj/bb.edn
- .claude/skills/crap4clj/deps.edn
Document
name: crap4clj description: "Calculates cyclomatic complexity and CRAP scores for Clojure functions by combining complexity analysis with Cloverage test coverage data, generating sorted reports that identify high-risk under-tested code. Use when the user asks for a CRAP report, cyclomatic complexity analysis, or code quality metrics on a Clojure project."
crap4clj — CRAP Metric for Clojure
Computes the CRAP (Change Risk Anti-Pattern) score for every defn and defn- in a Clojure project. CRAP combines cyclomatic complexity with test coverage to identify functions that are both complex and under-tested.
Setup
Add both a :cov alias (Cloverage) and a :crap alias to the project's deps.edn:
:cov {:extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}}
:main-opts ["-m" "speclj.cloverage" "--" "-p" "src" "-s" "spec"]}
:crap {:extra-deps {io.github.unclebob/crap4clj
{:git/url "https://github.com/unclebob/crap4clj"
:git/sha "<current-sha>"}}
:main-opts ["-m" "crap4clj.core"]}
The example above uses speclj.cloverage as the runner. For clojure.test projects, use cloverage.coverage instead:
:cov {:extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}}
:main-opts ["-m" "cloverage.coverage" "-p" "src" "-s" "test"]}
Adjust the -p (source path) and -s (test path) flags in :cov to match your project layout.
Usage
# First verify Cloverage works on its own
clj -M:cov
# Analyze all source files under src/
clj -M:crap
# Filter to specific modules
clj -M:crap combat movement
crap4clj automatically deletes stale coverage reports, runs clj -M:cov, and then analyzes the results.
Output
A table sorted by CRAP score (worst first):
CRAP Report
===========
Function Namespace CC Cov% CRAP
-------------------------------------------------------------------------------------
complex-fn my.namespace 12 45.0% 130.2
simple-fn my.namespace 1 100.0% 1.0
Interpreting Scores
| CRAP Score | Meaning |
|---|---|
| 1-5 | Clean — low complexity, well tested |
| 5-30 | Moderate — consider refactoring or adding tests |
| 30+ | Crappy — high complexity with poor coverage |
How It Works
- Deletes old coverage reports and runs Cloverage (
clj -M:cov) - Finds all
.cljand.cljcfiles undersrc/ - Extracts
defn/defn-functions with line ranges - Computes cyclomatic complexity (if/when/cond/condp/case/cond->/cond->>/some->/some->>/and/or/loop/catch)
- Reads Cloverage HTML for per-line form coverage
- Applies CRAP formula:
CC² × (1 - cov)³ + CC - Sorts by CRAP score descending and prints report
Troubleshooting
- Cloverage fails to run: Verify
:covalias paths match your project layout —-pshould point to your source root and-sto your test root. - All coverage shows 0%: Ensure
clj -M:covruns successfully on its own before running:crap. Check that test files are found under the specified test path. - Functions show N/A coverage: This occurs with split-file namespace patterns (
in-ns+load). Add--lcovto your:covalias main-opts for accurate per-file coverage.
Trustgrade B
- passBody integrity
Whether the stored document is plausibly the kind of file the artifact declares, rather than something fetched by mistake.
- passType matchnot applicable to this artifact type
Whether the artifact is really the kind of thing its metadata claims it is.
- passFreshness
How long since the source repository was last pushed to.
- passPrompt injection
Scans the artifact's own text for instructions aimed at your agent rather than at you.
- warnLicenseno SPDX license detected
Whether the source repository declares an SPDX license permissive enough to redistribute.
How the grade is calculated
Each check contributes 0 points when it passes, 1 when it warns, and 2 when it fails. The total maps to a letter:
- Aevery check passed
- Bone warning
- Ctwo warnings
- Dprompt injection or body integrity failed, or three warnings
- Fone of those failed, and something else is wrong
These are automated hygiene checks, not a security audit, and not a dependency or vulnerability scan. A grade of A means nothing was flagged — not that the artifact is safe.
Versions
git-a22525e1e9682026-07-31