Skip to content
Back to skills

Code Health

ASecurity

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 ...

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 4, 2026
ai-agentspythongobashnodesecuritydocumentation

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 4, 2026

npx -y skills add ramboz/jig --skill code-health --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Code Health?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Code Health
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ramboz-code-health-jig/badge)](https://www.skillsdirectory.com/skills/ramboz-code-health-jig)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: code-health
description: >
  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`).
user-invocable: true
---

> 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 Codex 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-command` override 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** — `ruff` on `PATH` → `uvx ruff` → `pipx run ruff` (ephemeral),
    invoked as `ruff check --output-format=json <dir>`.
  - **Node** — `eslint` on `PATH` → `npx eslint` (ephemeral), invoked as
    `eslint --format json <dir>`.
- 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 --outputjson` probe
    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 --check` probe 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

```bash
python3 "${PLUGIN_ROOT}/skills/code-health/health.py" detect [target]
```

- `target` defaults to `.` when omitted.
- Stdout: the resolved primary linter name across ecosystems (`ruff`,
  `uvx ruff`, `pipx run ruff`, `eslint`, or `npx eslint`).
- Exit `2` with a recommendation on stderr if nothing resolves (no recognized
  ecosystem, no resolvable linter, or a mixed project needing disambiguation).

### Run the lint pass

```bash
python3 "${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 (`0` clean / `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 `<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](../../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 <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 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 <spec.md> <slice>`.

See `skills/spec-workflow/SKILL.md` § "After implementation" for the full
four-pass orchestration recipe.

## Gotchas

- **A configured `review.code_health_skill` (scaffold.json) is honored only in
  the orchestrated code-health pass, not on interactive `/jig:code-health`.**
  Spec 096-01 / ADR-0040 D1 makes `code_health` one of the three extensible
  categories, so a project can name a richer code-health reviewer in
  `scaffold.json`; that key is read by `review.py` when it builds the
  **code-health pass** prompt for the spec-workflow reviewer subagent (before
  096-01 that builder had no richer dispatch at all). It is **not** consulted on
  this interactive invocation. Config honoring on orchestrator-invoked surfaces
  is a tracked follow-up (ADR-0040 OQ1).
- **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 `1` vs `2`.** Exit `1` means 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.** `check` parses 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.

Files in this skill

  • SKILL.md13.8 KB
  • health.py32 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.