Files
zernie__vigiles/docs/rules/prefer-compiled-hooks.md
zernie ff542b7c4b refactor!: vigiles audit — Lighthouse-style harness report (rename scan→audit)
Rename the read-only scan verb to `vigiles audit` — a local, zero-config Lighthouse-style report scoring a harness across four deterministic rings (Truthfulness/Triggering/Structure/Tested) with each finding's fix inline + a shareable HTML report and a versioned AuditReport JSON.

audit is a local report, not a CI step (CI uses `vigiles lint`). The executing checks (live MCP resolution + trigger-rate + the adoption preview) run only behind one interactive consent; there is no execution flag. The safety battery is not an audit ring — it lives in the vigiles/testing API.

Adds the adoption-gateway preview ("what would vigiles catch in your repo?"): the model drafts a spec from the instruction file, the cross-reference engine verifies every drafted ref deterministically.

BREAKING CHANGE: the `scan` verb is renamed to `audit` (no alias); the `--check-hooks`/`--verify-mcp`/`--trigger`/`--fix-plan`/`--explain`/`--deep`/`--measure`/`--fast`/`--no-measure` flags are removed.
2026-06-28 02:34:50 +05:00

3.2 KiB
Raw Permalink Blame History

prefer-compiled-hooks

A single repo-level recommendation (one finding regardless of how many hooks you have): when a plugin/repo ships hand-written hook commands that aren't compiled vigiles/hook artifacts, nudge toward compiling them. Same detector vigiles audit uses (manualHookCount in src/scan.ts); one detector, two callers.

This is a discovery nudge, not a defect. Hand-written shell hooks are a first-class, supported lane — so the rule fires once, never per-hook, and is opt-out. The message links the compiled-hooks guide.

Why compiled hooks

A hook authored as a pure typed function against the closed vigiles/hook vocabulary makes whole classes of hook bugs unrepresentable at authoring time:

  • you never write the exit code / JSON decision field (the #1 false-confidence bug — exit 1 where 2 is needed, the wrong field — a guard that looks like it blocks and silently doesn't);
  • the matcher is AST-backed (catches cd x && git push -f, the compound bypass a grep/substring matcher misses);
  • capability = API surface (an import outside vigiles/hook doesn't compile);
  • the artifact is stamped (a later hand-edit is refused at runtime).

A widely-copied OSS safety hook blocks 2/7 of the disaster battery; the compiled rewrite blocks 7/7. For hooks you already have, guardrail-check proves whether one actually blocks — regardless of how it was authored.

What it flags

One nudge when the repo has ≥1 hand-written hook command — a shell script (bash ${CLAUDE_PLUGIN_ROOT}/hooks/guard.sh) or an inline one-liner — that isn't a vigiles-managed (hook-runtime run-program) compiled hook. Silent when there are no hooks, or every hook is already compiled.

Honest caveat

This is form-based (compiled vs not), so it stays a recommendation: it can't tell a correct hand-written hook from a broken one. The point is discovery and the authoring-time win, not a quality verdict — keep it opt-out, and use untested-hook + guardrail-check for the "is this hook actually sound?" question.

Configuration

{ "rules": { "prefer-compiled-hooks": "warn" } }

Severity

Value Behavior
"error" vigiles lint exits non-zero (2) — enforce compiled hooks
"warn" Prints one ℹ recommendation, exits 0
false (default) Skip the nudge (the hand-written lane stays first-class)

Off by default. This is a recommendation, not a defect — the hand-written shell hook lane is fully supported — so it shouldn't fire unasked. Opt in with "warn" (or "error") when you want the nudge toward compiled hooks.

Scope

Hook commands in .claude/settings.json / .codex/config.toml [hooks] and the plugin manifest, across both shipping harnesses.

See also

  • Compiled hooks — author a hook as a typed function and compile it (the thing this rule points you to).
  • untested-hook — a hook that ships without a test/eval.