Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Configure default Claude Code enforcement hooks and opt-in guard recipes. Triggers: "cc-hooks", "configure Claude Code hooks", "install hooks".
practices
["pragmatic-programmer"]
output_contract
a hooks block in ~/.claude or project settings.json (matcher + command entries), or a hook script signalling allow/deny/ask via exit codes and hookSpecificOutput JSON; installs write hook config and guard fires append hashed telemetry
Claude Code Hooks
Shell commands that fire at specific points in Claude Code's lifecycle.
Hooks enforce mechanically what prose cannot: a model can reason its way past
an instruction, but it cannot reason its way past an exit 2 — which is exactly
why every hook must be narrow, silent, and reversible.
Named failure mode — chatty happy path: a hook that emits stdout on exit 0
corrupts the tool call it was guarding; silence on success is part of the
contract, not a style preference.
Constraints
Enforcement hooks (the PreToolUse policy dispatcher) ship by DEFAULT: plugin installs auto-wire hooks/hooks.json; skill copies and checkouts wire with one command (scripts/install-hooks.sh). Operators can disable per host (/plugin disable, or remove the settings matchers).
Injection hooks (SessionStart/UserPromptSubmit context stuffing) stay dead — the #511 teardown proved delta=0 at 10.35M resident tokens. Never ship one; the hookless-cold-start gate still enforces this.
Keep the happy path silent and block only with the event's documented exit/JSON contract because stray stdout can corrupt a tool call.
Bound Stop hooks with stop_hook_active and scope matchers narrowly to prevent recursion and unrelated-command interception.
Quick Start
Add to ~/.claude/settings.json (user) or .claude/settings.json (project):
A copy-paste PreToolUse recipe that nudges agents to load the coordination
skill before hand-rolling the am/atm/ntm/tmux send-keys CLI. This
recipe auto-installs nothing; you opt in per host (unlike the policy
dispatcher, which ships by default).
Context-budget doctrine for hooks: hooks are the most powerful enforcement
(mechanical, can't be reasoned past) but they pollute context — use sparingly. A
hook must be SILENT on the happy path (exit 0, no stdout/stderr), fire ONLY on a
real violation (ideally once per session, sentinel-gated), prefer PreToolUse
violation-guards over UserPromptSubmit/SessionStart per-turn injectors, and
NEVER emit stray stdout on an exit-0 PreToolUse path (it is parsed as JSON and
breaks the tool call). Block via exit 2 + stderr.
The recipe ships both scripts verbatim, a precise head-only matcher (so a
br create --body "...am/atm/ntm..." never false-fires), the two-matcher
opt-in settings.json snippet, and a bats test proving every fire/silent case.
A PreToolUse Edit|Write guard that routes an edit of an installed skill copy
(*/.claude/skills/**, .codex, .gemini) back to the repo source of truth
skills/<name>/. This is a TRUE mistake-token — editing an installed/symlinked
copy has no legitimate form (overwritten on install, or symlinks through to the
factory checkout). Zero false-positive surface: it matches tool_input.file_path
only, so a doc that merely mentions claude/skills in its body never fires.
Reversible → it ROUTES (exit 2 + one-line redirect), not hard-blocks. Silent on
every other path; fires once per session. Ships INERT — opt-in installer:
scripts/install-installed-skill-edit-guard.sh # user scope; --project for project
Value-proof (why this guard survives the hookless teardown)
The keystone guard ships gate-blind per-fire telemetry: on each fire it
appends exactly one JSONL line — {ts, session, token_class, path_sha256} — to
${AGENTOPS_HOME:-~/.agents/ao}/guardrail-telemetry.jsonl (override with
AGENTOPS_GUARDRAIL_TELEMETRY). The path is SHA-256 hashed, never raw
(privacy); nothing is written on the happy path; the sensor is inert until the
guard is installed and fires. The pre-registered methodology — metric =
declining fire-ATTEMPT rate over time (a signal the redirect cannot fake, NOT the
circular hand-roll rate), minimum N, noise floor, and null-at-small-N is an
acceptable outcome — satisfies ADR-0002 l.58 ("test or eval evidence showing
positive value"), the criterion whose absence killed 2.x hooks (#511).
The admission-control layer (epic age-4qw1): one PreToolUse dispatcher —
hooks/policy-dispatch.sh — evaluating a
policies-as-data registry
(policies/policies.json, contract
schemas/hooks-manifest.v2.schema.json) instead of N hand-wired settings
entries. This is the membrane at tool-call altitude: same vocabulary, lower
altitude than the pawl/gate at push time.
Per policy: dcg-style id (domain.object:token), mode: deny | route | audit,
matchers (tool + command/file_path regex), a route_message that names THE
correct tool, a rationale, and a pre-registered value_proof (the ADR-0002
lease-on-life: no proof accruing → retire the policy).
Predicate discipline, schema-enforced (the #511 anti-lesson): only
predicate_class: pure — syntactic mistake-tokens over the command or file
path — may deny/route. Lookup/stateful predicates ship audit-only until
promoted with reviewed fires.
scripts/lint-policies.sh enforces this mechanically
(jq-only; runs in bats and CI).
Accepted false-positive surface: because a pure predicate matches its token
anywhere in the raw command string, a protected token quoted as data (a commit
message body, a dcg test "..." probe, a here-doc payload) can still fire even
though nothing harmful would run. This is the deliberate cost of the
pure-only-may-deny rule — the alternative (repo/context lookups) is exactly the
stateful predicate the discipline bars from deny. Every fire is reversible: a
one-shot AOP_WAIVE=<policy-id> or a policy-waivers line clears it.
Semantics: happy path = exit 0, zero output. deny = exit 2 + one stderr
route line (full message once per session, short line after — every attempt
still blocks). route = exit 0 + permissionDecision:"ask" JSON. audit =
allow + record. Every fire appends one hashed guardrail-telemetry line
(token_class = policy id, plus mode/decision). Waive once with
AOP_WAIVE=<policy-id>, or a policy-waivers file line
<policy-id> <expiry-epoch>. Missing registry or jq fails OPEN.
Day-1 enforce cohort (age-wnyt, all pure-regex, high-pain):
Policy
Blocks
Routes to
core.git:add-beads-ledger
git add naming _beads/ (private ledger leak is one-way)
push the ledger repo itself — never git add _beads in the public tree
The skill package carries its own installer: ~/.claude/skills/cc-hooks/scripts/install-hooks.sh (one command; file copies cannot self-wire)
git clone / brew checkout
scripts/install-policy-dispatch.sh (delegates to the same skill-embedded installer)
The installer lints the registry before wiring, backs up settings, and is
idempotent. Disable per host with /plugin disable agentops or by removing the
two PreToolUse matchers from settings.
{"decision":"block","reason":"Tests failing. Fix before stopping."}
Critical: Check stop_hook_active to prevent infinite loops.
Anti-Patterns
Don't
Do
Old object format
Array format with matcher
Unquoted $VAR
"$VAR"
Exit 2 with JSON
Exit 2 uses stderr only
Skip stop_hook_active check
Always check in Stop hooks
Debugging
claude --debug # Hook execution details
/hooks # View/edit in REPL
Output Specification
Path: user ~/.claude/settings.json or project .claude/settings.json, plus explicitly named hook scripts. The PreToolUse policy dispatcher ships by default (every install path wires it — see "Policy Dispatch Engine"); the additional guard recipes (skill-first coordination, standalone installed-skill-edit) stay inert until opted in.
Filename: preserve settings.json; give scripts descriptive executable filenames rather than embedding large shell programs in JSON.
Format: valid Claude hook JSON using event arrays, matchers, and command objects; hook stdout/stderr and exit codes follow the selected event schema.
Exit code: validate with jq -e '.hooks | type=="object"' <settings.json> and a representative silent/fire test for each matcher; any parse error, noisy happy path, or recursion risk blocks activation.
Downstream handoff: consumed by the operator only after the exact scope, reversal command, test evidence, and opt-in location are reported.
Quality Checklist
The matcher fires on the intended event/input and stays silent on representative near misses.
Blocking and allow paths use the documented exit code and output channel without leaking context.
The hook is reversible, narrowly scoped, recursion-safe, and clearly labeled as opt-in host policy.