Code health
A Claude Code and Codex plugin that scaffolds AI-native development practices into new projects. jig adds a repeatable spec, implementation, review, and memory workflow to AI-assisted software projects.
npx -y skills add ramboz/jig --skill code-healthAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Run a static-analysis pass on a project — detect the ecosystem (Python or Node), drive its linter (ruff / eslint, plus advisory pyright/complexity/ prettier and a cross-ecosystem duplication signal) via the `health.py` helper, and act on the normalized exit code (0 clean / 1 findings / 2 no-linter). Auto-triggers when you say lint this, check code health, run the linter, ask is this code clean, ask any lint issues, or want a static analysis pass. Tools are resolved on PATH or run ephemerally via uvx / pipx / npx — it installs nothing. Defers to any other installed skill whose description identifies it as handling linting, static analysis, or code quality — prefer it over this baseline. Do not use for running tests (use `/jig:tdd-loop`), for security review (use `/jig:security-review`), for spec-compliance review of a finished slice (use `/jig:independent-review`), or for general PR craft review (use `/jig:pr-review`).
SKILL.md
13.1 KB, as published. Nobody here has run it
Spec 060 introduced
code-healthas the static-analysis sibling oftdd-loop, under ADR-0017's "detect the language → drive its blessed tools → normalize → degrade gracefully" framing. Liketdd.py, the deterministic detection + subprocess invocation live inhealth.py; this SKILL.md drives the judgment layer. If another installed skill's description identifies it as handling linting / static analysis / code quality, the Claude Code skill router prefers it — the deferral is category-based.
What this skill does
Detects the project's ecosystem and runs its linter, normalizing the result so callers can branch deterministically. Ecosystem detection is table-driven — each ecosystem (Python, Node) is a data-structure entry, so adding a language is an entry, not a control-flow fork. Current scope: Python (ruff) + Node (eslint), each with advisory secondary signals.
- A
.jig/lint-commandoverride always wins and bypasses ecosystem detection entirely (honored verbatim — same semantics astdd.py's.jig/test-command). - Otherwise detects the ecosystem by marker files (
pyproject.toml/*.pyfor Python;package.jsonfor Node) and resolves its primary linter:- Python —
ruffonPATH→uvx ruff→pipx run ruff(ephemeral), invoked asruff check --output-format=json <dir>. - Node —
eslintonPATH→npx eslint(ephemeral), invoked aseslint --format json <dir>.
- Python —
- Parses the result into a tight summary — a findings count + the top rule codes, not the raw dump (per spec 057's "tight envelope, not a transcript").
- Adds an advisory dimension that is reported, not gating (it never
changes the exit code):
- Python — complexity: an advisory ruff probe with
--select C901,PLR0911,PLR0912,PLR0913,PLR0915surfaces a per-function complexity signal ("complexity: N function(s) over threshold; top: …"). - Python — type checking: an advisory
pyright --outputjsonprobe resolvespyrightonPATH, thenuvx pyright, thenpipx run pyright. Type diagnostics are summarized as a count + representative rules ("pyright: N type diagnostic(s); top: …"). If no type-checker resolves, it emitspyright: skipped (no type-checker) …. Like every advisory signal, it is reported, never gating. - Node — formatting: an advisory
prettier --checkprobe surfaces files that need formatting ("prettier: N file(s) need formatting"). - Cross-ecosystem — duplication: an advisory probe (run for BOTH
Python and Node) reports copy/paste duplication. It is native-first
(an explicit extension point for a future per-ecosystem native
duplication tool — currently empty, since no jig ecosystem ships a
distinct native detector), falls back to an ephemeral
npx jscpdwhennpxis onPATH(the Node analogue ofpipx run, works on any language, installs nothing), and otherwise emitsduplication: skipped (no detector) — install a duplication tool or Node (npx jscpd) to enable. When it runs, the summary is a tight percentage + the top clones asfile:line("duplication: 4.2% (12 clones); top: foo.py:10, bar.py:88") — never the raw jscpd log. Like the other advisory signals it is reported, never gating (it cannot change the exit code).
- Python — complexity: an advisory ruff probe with
- Normalizes the primary linter's exit code:
0— clean (no findings)1— findings exist (the linter ran and reported issues)2— no linter resolvable, no recognized ecosystem, OR the resolved tool failed to start
- Degrades gracefully (AC4), never a stack trace:
- no markers → exit
2+ "no recognized ecosystem (Python/Node) found — set .jig/lint-command to run your linter". - one ecosystem, no resolvable linter → exit
2+ an ecosystem-specific recommendation (ruff/pipx for Python; eslint/npx for Node). - mixed (2+ ecosystems) → exit
2+ a recommendation naming the detected ecosystems and pointing at.jig/lint-commandto disambiguate.
- no markers → exit
It installs nothing — uvx / pipx / npx run the tools ephemerally
only if those launchers are already on PATH.
Helper invocations
Two subcommands mirror tdd.py: detect reports which linter resolves, and
check runs it.
Detect the linter
python3 "${CLAUDE_PLUGIN_ROOT}/skills/code-health/health.py" detect [target]
targetdefaults to.when omitted.- Stdout: the resolved primary linter name across ecosystems (
ruff,uvx ruff,pipx run ruff,eslint, ornpx eslint). - Exit
2with a recommendation on stderr if nothing resolves (no recognized ecosystem, no resolvable linter, or a mixed project needing disambiguation).
Run the lint pass
python3 "${CLAUDE_PLUGIN_ROOT}/skills/code-health/health.py" check [target]
- Auto-resolves the ecosystem's linter via the same logic as
detect. - Prints a tight summary (count + top rule codes) to stdout, plus any advisory lines (Python complexity / Python pyright / Node prettier / cross-ecosystem duplication) — advisory lines are reported but never change the exit code.
- Exit code is normalized off the primary linter (
0clean /1findings /2no-linter) per the table above. Branch on it deterministically — exit1means inspect the summarized findings; exit2means the tool couldn't even start, no ecosystem was recognized, or a mixed project needs.jig/lint-commanddisambiguation — not "the code is clean".
Override the auto-detection
Create <target>/.jig/lint-command with the first non-blank, non-comment
line being the exact command to run. It is honored verbatim, takes
priority over all auto-detection, and bypasses ecosystem detection
entirely — the same semantics as tdd.py's .jig/test-command. Useful for
a project whose linter isn't ruff/eslint (e.g. flake8 src or pylint mypkg), or to disambiguate a mixed Python+Node repo. (This is how jig's own
CI is unaffected — jig commits a .jig/lint-command.)
When NOT to use
- Running tests — that's
/jig:tdd-loop(tdd.py). Static analysis and the test loop are different cadences. - Security review — that's
/jig:security-review; this skill is about lint / style / correctness signals, not vulnerabilities. - Spec-compliance review of a finished slice —
/jig:independent-review. - General PR craft review —
/jig:pr-review. - Pure-documentation edits that touch no code.
Relationship to other skills
health.py is the static-analysis sibling of tdd-loop's tdd.py — same
detect → drive → normalize → degrade shape, same .jig/*-command override
idiom, same 0 / 1 / 2 exit contract. Per ADR-0002
the shared idioms (_read_text_safe / _custom_command_file /
_parse_custom_command) are inline-mirrored, not extracted into a
_common module — this is only the second helper of its kind, and the two
have independent lifecycles. The deliberate duplication is noted in
health.py's module docstring (exactly as tdd.py documents its own
duplication of scaffold.py).
The code-health review pass (slice 060-05)
Beyond the health.py runner, jig wires a distinct code-health review
pass into the post-implementation flow (alongside compliance / craft /
arch). The layering (ADR-0017):
the spine runs the tool (health.py), and a read-only reviewer
subagent judges its tight summary — rendering the judgment a static tool
can't: is reported duplication within the ADR-0002
inline-mirror budget (two callers may mirror; a third triggers an extract)?
is a flagged complex function inherent or fixable? are the lint findings
worth blocking on?
- The reviewer never runs
health.py. It is read-only (Read/Glob/Grep, no Bash). The orchestrator / CI runshealth.py, captures the tight summary, and feeds it into the prompt viareview.py code-health … --summary-file <path>(or--summary-file -to pipe it in — stdin is never read implicitly, bug 017). The reviewer judges the summary, never raw logs. - The pass is GATED, not always-on. It runs only when a slice's
frontmatter declares
code_health_review: true— exactly mirroring howarch_review: truegates the arch pass. Why gated: ADR-0017 flags the per-slice review cost (the spec 055/057 context-cost discipline — every pass adds orchestrator turns + a subagent), and recommends gating it like arch-review rather than spending it on every slice. The flag defaults off, so existing slices are unaffected; a slice author opts in when a change is duplication-/complexity-heavy enough to warrant the judgment. - Evidence + block rule. The verdict is recorded as
docs/specs/NNN-slug/reviews/slice-NN-code-health.md(ADR-0014 evidence model).[blocker]-tagged findings block theREVIEWEDtransition;[nit]-tagged findings become reconciliation-log items — the same rule as the craft/arch passes.workflow.py transitionrequires thecode-healthverdict forREVIEWED/DONEiff the flag is set. Query the flag withworkflow.py code-health-review-needed <spec.md> <slice>.
See skills/spec-workflow/SKILL.md § "After implementation" for the full
four-pass orchestration recipe.
Gotchas
- Scope is Python (ruff, + advisory complexity and pyright) and Node
(eslint, + advisory prettier --check), plus a cross-ecosystem advisory
duplication signal (
npx jscpd). The dedicated code-health reviewer pass (slice 060-05) is now live — see "The code-health review pass" above; the Tier-2 scaffold-the-floor work (slice 060-06) is DEFERRED. An unrecognized ecosystem with no.jig/lint-commandoverride degrades to a recommendation. - Advisory ≠ gating. The complexity and pyright (Python), prettier
(Node), and duplication (cross-ecosystem) signals are reported in the
summary but never change the exit code — the exit code is driven solely by the
primary linter (ruff / eslint). A clean ruff run with complexity or
type findings still exits
0. - Duplication is honest about being unavailable. Unlike complexity /
prettier (which stay silent when their tool isn't present), the duplication
probe emits
duplication: skipped (no detector) …when neither a native tool nornpxis available — so a reader knows the dimension was not measured rather than measured clean. It writes jscpd's JSON report to a temp dir outside the project (read back, then removed) so it never pollutes your tree, and runs jscpd without--thresholdso jscpd itself never exits non-zero (advisory, not gating). - The override path runs no advisory probes (including duplication) — it
honors
.jig/lint-commandverbatim without ecosystem detection, so jig's own dogfood CI (which sets an override) is unaffected. - Mixed repos degrade, they don't guess. If both
pyproject.toml(or*.py) andpackage.jsonare present,checkexits2and asks you to set.jig/lint-commandto disambiguate — it never picks one for you. - Exit
1vs2. Exit1means the linter ran and found issues — inspect the summary. Exit2means no linter was resolvable, no ecosystem was recognized, a mixed project needs disambiguation, or the resolved tool failed to start (an environment issue) — don't conflate any of those with clean. - Ephemeral runs need a network/cache.
uvx ruff/pipx run ruff/uvx pyright/pipx run pyright/npx eslint/npx prettier/npx jscpdfetch the tool on first use. If neither the binary nor a launcher is present, the skill recommends (for the primary linter) or reportsskipped(for pyright/duplication) rather than failing opaquely. - Tight summary, not the raw dump.
checkparses the linter's JSON into a count + top codes; it does not echo the full tool output. Re-run the linter directly when you need every finding's location.