/code-health
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
Claude Code
$ npx -y skills add ramboz/jig --skill code-health --agent claude-code
Codex
$ npx skills add ramboz/jig --skill code-health -a codex -y
Cursor
$ npx skills add ramboz/jig --skill code-health -a cursor -y
Gemini CLI
$ npx skills add ramboz/jig --skill code-health -a gemini-cli -y
GitHub Copilot
$ npx skills add ramboz/jig --skill code-health -a github-copilot -y
OpenCode
$ npx skills add ramboz/jig --skill code-health -a opencode -y
Windsurf
$ npx skills add ramboz/jig --skill code-health -a windsurf -y
SKILL.md
code-health.SKILL.mdname: 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 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-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
python3 "${CLAUDE_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
python3 "${CLAUDE_PLUGIN_ROOT}/skills/code-health/health.py" check [target]- Auto-resolves the ecosystem's linter
Read more
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 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-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
python3 "${CLAUDE_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
python3 "${CLAUDE_PLUGIN_ROOT}/skills/code-health/health.py" check [target]- Auto-resolves the ecosystem's linter
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.
Other skills on jig.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about code-health.
- Which coding agents can use code-health?
- code-health installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

