A Claude Code plugin · repo health automation

A repo that audits, fixes, and hardens itself.

The heal suite is a family of /heal:* slash commands: eighteen specialist auditors — each owning one health axis — plus eight meta commands: an orchestrator, a fixer, a preventer, a report renderer, a run lister, a retrospective that learns from the defects the suite missed, an installer that stands it up in your project, and an overnight pipeline that runs the whole loop unattended. Install it once, run it on a loop, and the codebase converges toward green instead of quietly rotting.

18specialist healers
8meta commands
10axes auto-fix
1number to trend

01What is it?

Every codebase drifts: docs go stale, deps age, invariants erode, migrations grow footguns, the same bug class returns. Traditionally a human notices — eventually. The heal suite makes drift detectable, persistent, fixable, and preventable as a repeatable, mostly automated process.

One healer, one axis

Each healer ships as one skill — skills/<axis>/SKILL.md — and is a self-contained audit spec: the surface it owns, the checks it runs, its fix policy, and the .heal/backlog.md heading it converges findings to. /heal:security knows nothing about bundle budgets; /heal:perf never touches auth. Narrow scope is what keeps each audit deep and each verdict trustworthy.

An orchestrator, not a monolith

/heal:sweep resolves the enabled axes at runtime (never a hardcoded list), fans the independent read-only ones out as parallel agents, and fuses their reports into one dashboard. Its real value is cross-axis correlation: an IDOR flagged by security, an ownership-scoping gap flagged by arch, and a missing abuse-case test flagged by tests are one root cause seen through three lenses — and get ranked as one high-leverage cluster.

Idempotent by contract

Every healer is written to run on a schedule: a healthy repo yields a clean report and zero edits. Findings that can't be fixed now are persisted as checklist items so the next run builds on the last instead of re-reporting the same thing forever.

What it can achieve

  • A single health number + backlog count a loop or schedule can watch trend over time.
  • Findings as data — fingerprinted findings.json sidecars enable new / fixed / persisting diffs between runs.
  • Fixes as reviewable PRs — mechanical fixes land as focused branches off the project's integration branch, never silent edits to a protected one.
  • Fewer repeat findings — recurring issues get promoted into durable guardrails (rules, hooks, scripts, tests).
  • A shareable HTML dashboard per run — zero token cost, pure render of on-disk data.
Not the same as an adversarial sweep (code-review / a bug hunt). A healer is a standing, axis-scoped drift audit: it re-checks a fixed roster of known-bad patterns, deprecations, and invariant violations, tuned for repeatability — stable fingerprints, low false positives, a trend you can watch converge. An adversarial sweep is the opposite shape: a one-shot, recall-oriented attempt to break specific code paths (an IDOR on one unchecked id, a lost edit on unmount, a mis-parsed external shape). That reasoning is expensive and non-deterministic — it finds a different subset each run and can't be fingerprinted into a trend — so it belongs on a diff (what /code-review is for), not run whole-repo every cycle. The two are complementary: when a hunt finds a real bug, the suite absorbs it by distilling the lesson into a deterministic check inside the owning healer (via /heal:prevent and /heal:self) — so the hard reasoning happens once and becomes cheap to re-verify forever.

02The loop: detect → persist → apply → prevent

The suite is a closed feedback loop. Detection is cheap to re-run; applying and preventing are deliberate, user-triggered steps — so the system proposes aggressively but never guesses on decisions that are yours to make. /heal:night chains the whole loop unattended — a diff-scoped sweep, the mechanical fixes as capped PRs, a morning report — while everything decision-shaped stays queued for a human.

/heal:sweep
Detect
self runs first, then the pack fans out in parallel — the full roster, or a diff-scoped subset picked from the committed axis map. A gate preflight + cost estimate print before any token is spent; deterministic gates run alongside the LLM audits.
.heal/runs/<run-id>/
Persist
Every report + fingerprinted findings hit disk. Runs are resumable — an interrupt skips already-recorded healers; a partial sweep carries the skipped axes' findings forward so the burden number stays whole-repo-comparable.
.heal/backlog.md
Converge
Unfixed findings land under per-axis headings, idempotently. The backlog is the durable state between runs.
/heal:fix
Apply
Drives the backlog to green: mechanical items as focused PRs, each built in an isolated worktree — big sweeps fan out to parallel fixer agents. Judgment items surface as explicit questions.
/heal:prevent
Prevent
Proposes the cheapest durable guardrail per finding; escalates when a finding recurs despite one.
⟲  the next /heal:sweep verifies the fixes held and the guardrails are holding — the trend report proves it with data, not memory
Detection never mutates; mutation never audits. Healers in report mode are read-only. /heal:fix re-processes what the healers already found (no re-audit cost), and /heal:prevent --apply writes only low-risk artifacts. Contract-touching, visual, and security-posture changes always stop for a human call.

03The roster

Eighteen healers, in /heal:sweep's run order — cheap deterministic gates first, the heavy LLM reads last. Badges show each healer's mode support: --fix can auto-apply low-risk fixes, --branch can scope to the current branch's diff, scored produces a 0–100 rating, suggest-only never auto-fixes.

self
Meta-audit of the suite's project side — stale cited paths, asserted facts that no longer match the project, coverage holes. Runs first so a drifted healer is caught before its output is trusted.
runs first--fix
arch
Architecture drift against the invariants the project has committed to — gate-first, one explicit verdict per invariant, plus a parallel-layer parity report.
--fix--branch
api
Contract audit across the HTTP boundary — server routes ↔ client fetch wrappers ↔ request-validation schemas ↔ the error-code union ↔ cache-key invalidation.
suggest-only--branch
db
ORM schema ↔ migration drift, migration-ledger ordering, PK-change footguns, FK index coverage, transactional + backfill-safe writes, soft-delete key reconciliation.
--fix
ci
CI/CD workflow currency, hermeticity, least privilege, and quiet logs — no tolerated deprecation noise.
--fix
packages
Dependency currency (what can bump safely, respecting the pins) + a trust pass flagging deps not worth depending on.
--fix
ai
LLM / image providers & models still active and non-deprecated; call-site misuse; newly released models worth adopting.
--fix--branch
security
Secrets, auth/session, IDOR & privilege escalation, mass assignment, injection, XSS, CSRF, prompt injection, headers, dependency advisories.
--fix--branch
privacy
Privacy/GDPR drift — erasure + export completeness as the schema grows, log-retention coverage, the server-enforced Art. 9 consent gate, PII hygiene in event payloads & log sinks, admin-surface minimisation, policy-doc vs code.
suggest-only--branch
content-ip
Licence & attribution drift — imported third-party media never re-hosted without a right to reproduce it, and every attribution/share-alike source on the default resolution path credited on a user-reachable surface.
suggest-only--branch
web-egress
Every third-party origin the frontend contacts at page load is either self-hosted away or disclosed as a subprocessor on the project's legal surface — and the deterministic egress allowlist stays derived from that disclosure, never the reverse.
suggest-only--branch
tests
Tests that don't earn their keep (no real assertion, implementation-coupled, tautological, redundant, flaky), an order-shuffling probe across seeds, and coverage gaps on the behaviour the product hangs on.
--fix
quality
SOLID, accidental duplication, complexity hotspots, type-safety erosion (any / assertion / ts-ignore counts) — a deterministic size-watch + dead-export triage ranks the read; scored so the trend is trackable.
suggest-only--branchscored
perf
Server-side N+1 / query-shape hotspots in the data-access layer, client bundle-size budgets, client render & asset cost — scored.
suggest-only--branchscored
docs
Prose sources of truth vs reality: documented commands exist, env vars match code, version pins match what's installed, cited paths resolve, doc-lifecycle statuses honest.
--fix
i18n
Every user-facing string reaches the reader in their own language, every number/date follows the active locale. Owns what the deterministic i18n gates deliberately can't decide — scanner escape hatches, locale-pinned formatting, untranslated stub values, orphaned keys.
suggest-only--branch
a11y
Semantic HTML, accessible names / ARIA, keyboard operability, focus management, and contrast against the project's theme policy.
--fix--branch
design
UI consistency & design-system adherence: reuse over raw markup, styling-token drift, responsive integrity at the project's viewport floor, component-gallery completeness — a deterministic duplicate-cluster precompute seeds the read; scored.
suggest-only--branchscored

The eight meta commands (not healers — they have no axis of their own)

Orchestrator
sweep
Runs the healer set as one system: parallel fan-out, run persistence, the fused dashboard, TODO convergence, trend, and the tail preventer.
Applier
fix
The backlog → green edge. Applies mechanical items as focused PRs off the project's integration branch — each in an isolated worktree, never the shared tree; ≥ 3 groups fan out to parallel fixer agents, each with its own throwaway test environment. Ticks items [x], lists judgment items as explicit questions.
Preventer
prevent
The detect → prevent edge. Proposes the cheapest durable guardrail per finding; runs automatically at the tail of every sweep.
Renderer
report
Pure render of a run's on-disk data into a self-contained index.html dashboard. No model, no token cost, regenerable anytime.
Installer
init
Stands the suite up in the current project: opens with a baseline preflight — lockfile, formatter, linter, type check, test runner, pre-commit hook, CI, each with the file that proves it and whether anything actually runs it — then scans the repo, proposes the axes that fit it with evidence, writes .claude/heal.json, scaffolds .heal/ and one facts file per enabled axis, vendors the structural gate, prints the permissions block to paste. Config and state only — the skills stay in the plugin, so updates arrive by updating the plugin. Idempotent, advisory about the floor it finds, and nothing is ever enabled or installed for you.
Lister
list
Lists a run's open findings severity-sorted — each with the id /heal:fix takes, plus suggested one-swoop batches that share a fix. Accepted tradeoffs sit in their own section; resolved items stay hidden unless --resolved. Reads the persisted run; runs nothing.
Retrospective
retro
The outer feedback edge — the only one that learns from what the suite missed. Walks the fix/ + hotfix/ merges since the last retro and asks, per escaped defect, which healer should have caught it and whether that healer's current prompt actually would have. --noise is the complement: findings raised but rejected, re-raised without action, or hand-resolved with no code change. Proposals-only by default; --fix applies the two mechanical project-side classes. Project-side proposals land in .heal/tuning.md; plugin-side ones stay in the report.
Overnight pipeline
night
One unattended trigger for the whole loop: a diff-scoped plan picks the healers worth re-auditing, the sweep runs report-only, mechanical fixes land as capped worktree-isolated PRs (guarded against still-open heal PRs), and a morning report queues the judgment calls. Never merges, never asks — fired by a scheduler or by hand.

04Watch one full cycle

The same loop runs two ways. In the day you drive it command by command. At night it drives itself and leaves you a report. Both end in the same place: reviewable PRs, decisions queued for a human, and a number that proves whether the repo got better.

Choose a flow

acme/storefront — dev

Neither flow ever merges anything. The night pipeline is trusted with the mechanical half of the work and nothing else: it opens PRs, caps itself at three a night, and refuses to guess at a decision when there's nobody awake to ask. Convergence happens at the speed of your PR reviews — and the next night's trend is what confirms the fixes actually held.

05How it's used

Everything is a slash command in Claude Code. The common flows, from broadest to most surgical:

CommandWhat happens
/heal:sweep The full sweep, report-only. Prints a gate preflight + per-healer cost estimate up front, then self first, then the pack in parallel. Ends with the dashboard, the trend vs the previous run, and the preventer's proposals.
/heal:night The unattended pipeline: plans a diff-scoped sweep (full roster on a cadence floor), runs it report-only, applies the mechanical subset as capped worktree-isolated PRs, and ends with a morning report. Judgment items are queued, never attempted; nothing merges without you.
/heal:sweep --branch Pre-merge mode: branch-scoped healers audit only the current branch's changes; findings belong in the PR, not .heal/backlog.md.
/heal:sweep arch,security,db A subset sweep — just the named healers.
/heal:security (any healer) One healer standalone. It opens its own single-healer run so the artifacts and trend data are identical to a sweep's.
/heal:fix Reads the .heal/backlog.md backlog (plus the newest run's detail), applies the mechanical items as focused PRs — each in an isolated worktree — and stops with questions for the rest. Pass finding ids to fix just those (several ids = one batch, one PR). --all also attempts judgment items; --dry-run plans only.
/heal:prevent --apply Writes the low-risk guardrail artifacts the last run proposed (a path-scoped rule file, a line in the project's agent-instruction doc, an inert draft script) — never live hook wiring.
/heal:report Re-renders the newest run's HTML dashboard on demand (or for a given run-id).
/heal:list Lists the newest run's open findings severity-sorted, each with a ready /heal:fix id, plus one-swoop batch suggestions (or a given run-id); pass a finding id for its full detail block, --resolved to reveal resolved items. Reads the persisted run; runs nothing.
/heal:retro The escaped-defect retrospective: walks the fix/ + hotfix/ merges since the last retro and asks, per defect, which healer should have caught it and whether that healer's current prompt actually would have. --noise is the complement — findings the suite raised that were rejected, re-raised without action, or hand-resolved with no code change, traced back to the wording that produced them. Proposals-only by default; --fix applies a missing finding-class slug or an axis path-glob gap, nothing else. Project-side proposals land in .heal/tuning.md; plugin-side ones stay in the report.
The intended rhythm Run /heal:sweep on a loop or schedule and watch two things: the overall health number (mean of the scored axes) and the findings burden (a deterministic severity-weighted sum over every axis). When the backlog has substance, run /heal:fix, review the PRs, answer the "needs your call" questions. Occasionally apply the prevention proposals. Each cycle should leave fewer findings than the last — and the trend report proves whether it did.

06Anatomy of a run

One /heal:sweep invocation is a run, materialised on disk (local, gitignored). The manifest makes runs resumable; the sidecars make findings diffable.

# .heal/runs/<run-id>/ — everything a run produces .heal/runs/2026-07-02T08-14-31Z-ab3x/ ├── run.json # manifest: healer set, mode, status, stats, provenance — makes the run resumable ├── self.mddesign.md # one full report per healer + its duration/token statistics ├── self.findings.json# fingerprinted findings — the data behind trend & recurrence ├── prevent.md # the tail preventer's guardrail proposals for this run ├── summary.md # roll-up: totals, cost split (healers vs orchestration) └── index.html # self-contained dashboard — severity chart, findings, tokens

Durable state lives in .heal/backlog.md

Run dirs are ephemeral; the backlog is not. Each axis owns a heading (## Security health, ## Quality debt, ## Healer drift, …) and converges to it idempotently. /heal:fix ticks items [x] with a dated resolution note and moves them to .heal/archive/backlog.md in the same commit as the fix — the archive holds the checked audit trail, the active file only open work. .heal/backlog.md is convergence-only; a human-shipped tradeoff gets its own .heal/tradeoffs/<slug>.md shard (one file per entry, so two PRs never collide on a shared insertion anchor), ticked [x] in place.

Trend as data, not memory

heal-run.mjs trend joins a run's findings against the previous run by fingerprint id and prints new / fixed / persisting / accepted counts plus the burden delta. Accepted risks live in .heal/baseline.json — the single source of acceptance truth — so they never re-count as new. No LLM recollection involved — it's a deterministic join.

Publishable numbers, not vibes. Every run's provenance — suite version, frozen metrics version, the exact model, the audited git sha — rides in run.json and is mirrored onto a long-horizon runs/history.jsonl, so a score is never separated from the conditions that produced it. Closing a finding records a verdict (fixed / wontfix / false-positive / duplicate / obsolete), from which the suite reports its own precision with an honest denominator: auto-resolutions and still-open findings sit in their own buckets, and dogfood runs never pool with external-repo runs.

07How it's maintained — and how it evolves

The suite's biggest risk is auditing against yesterday's repo: a healer citing a moved file or a stale pin reports phantom drift, or worse, silently stops covering real drift. So the suite audits itself, on three reinforcing layers.

scripts/heal-gate.mjs
Layer 1 · Deterministic gate
Vendored into the project by /heal:init, wired as a pre-commit or Stop hook. Mechanically enforces the suite's project-side structure: facts-file frontmatter, backlog headings, cited paths exist, rule globs still match files.
/heal:self
Layer 2 · Meta-audit
The judgment half: asserted facts vs the project's own instruction docs and config, coverage holes, cross-axis drift & duplication. Runs first in every sweep.
--self-test drills
Layer 3 · Test the testers
Mutation drill: inject known drift classes into a fixture copy and assert the gate catches each. A missed mutation is a high finding — the gate has a blind spot.

Evolution is proposal-driven, never self-rewriting

  • The axis set is resolved, never hardcoded. /heal:sweep reads the axes enabled in .claude/heal.json, so turning one on adds it to every future sweep — and the gate immediately enforces its structure. Updating the plugin updates every project at once; the config and the findings stay yours.
  • Coverage holes become drafts. When /heal:self finds an invariant no healer checks, it drafts a ready-to-apply check — evidence, owning healer, exact placement, copy-pasteable text — but a human applies it. This prevents command bloat, overfitting, and structural corruption in the very files that drive the loop.
  • Recurrence escalates. /heal:prevent proposes a guardrail on a finding's first appearance and escalates emphasis when it recurs — and flags guardrails that exist but aren't holding, so they get promoted to something stronger.
  • Guardrails graduate into code. A check that keeps being proposed gets written: the trajectory is always LLM finding → persistent backlog → durable script / rule / test, at which point the expensive reasoning is spent once and re-verified for free.
This page is held to the same standard. It carries its own contract in the file header — the section spine, and which of its facts are derived from the shipped skills, which belong to the frozen demo cycle above, and which are prose a person owns. The derived half is enforced in CI: add, remove, or re-flag a healer without updating the roster here and the build fails. The skills are the source of truth — this page renders them, and never states a fact they don't.