Install
$ agentstack add skill-ramboz-jig-code-health ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
> Spec 060 introduced code-health as the static-analysis sibling of > tdd-loop, under [ADR-0017](../../docs/decisions/adr-0017-scaffolded-code-health.md)'s > "detect the language → drive its blessed tools → normalize → degrade > gracefully" framing. Like tdd.py, the deterministic detection + > subprocess invocation live in health.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 as tdd.py's .jig/test-command).
- Otherwise detects the ecosystem by marker files (
pyproject.toml/*.py
for Python; package.json for Node) and resolves its primary linter:
- Python —
ruffonPATH→uvx ruff→pipx run ruff(ephemeral),
invoked as ruff check --output-format=json .
- Node —
eslintonPATH→npx eslint(ephemeral), invoked as
eslint --format json .
- 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,PLR0915 surfaces a per-function complexity signal ("complexity: N function(s) over threshold; top: …").
- Python — type checking: an advisory
pyright --outputjsonprobe
resolves pyright on PATH, then uvx pyright, then pipx run pyright. Type diagnostics are summarized as a count + representative rules ("pyright: N type diagnostic(s); top: …"). If no type-checker resolves, it emits pyright: 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 jscpd when npx is on PATH (the Node analogue of pipx run, works on any language, installs nothing), and otherwise emits duplication: 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 as file: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).
- 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-command to disambiguate.
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, or npx 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 /1
findings / 2 no-linter) per the table above. Branch on it deterministically — exit 1 means inspect the summarized findings; exit 2 means the tool couldn't even start, no ecosystem was recognized, or a mixed project needs .jig/lint-command disambiguation — not "the code is clean".
Override the auto-detection
Create /.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](../../docs/decisions/adr-0002-contracts-stays-deferred.md) 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](../../docs/decisions/adr-0017-scaffolded-code-health.md)): 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](../../docs/decisions/adr-0002-extract-helper-on-third-caller.md) 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 runs health.py, captures the tight summary, and feeds it into the prompt via review.py code-health … --summary-file (or stdin). 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 how arch_review: true gates 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](../../docs/decisions/adr-0014-review-evidence-model.md) evidence model). [blocker]-tagged findings block the REVIEWED transition; [nit]-tagged findings become reconciliation-log items — the same rule as the craft/arch passes. workflow.py transition requires the code-health verdict for REVIEWED/DONE iff the flag is set. Query the flag with workflow.py code-health-review-needed .
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-command override 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 nor npx is 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 --threshold so jscpd itself never exits non-zero (advisory, not gating).
- The override path runs no advisory probes (including duplication) — it
honors .jig/lint-command verbatim 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) and package.json are present, check exits 2 and asks you to set .jig/lint-command to disambiguate — it never picks one for you.
- Exit
1vs2. Exit1means the linter ran and found issues —
inspect the summary. Exit 2 means 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 jscpd fetch the tool on first use. If neither the binary nor a launcher is present, the skill recommends (for the primary linter) or reports skipped (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.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ramboz
- Source: ramboz/jig
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.