DOCUMENTATION
The Handbook
Every hook. Every event. Every adapter. Every command.
§1 Overview
Agent_hook is the hook layer of a three-repo system:
- Agent_skills — methodology: how the agent thinks (16 skills)
- Agent_hook — enforcement: what must happen (9 hooks)
- Agent_mcp — context: what the agent can touch (10 MCPs)
Each hook is a Python script following the Claude Code stdin/stdout/exit-2 protocol. Adapters translate it to opencode / cursor / kimi (codex skipped — no hook concept yet).
§2 Hook Events
Following the 8 Claude Code hook events:
| Event | When it fires | Return semantics |
|---|---|---|
PreToolUse | Before any tool call (Write/Edit/Bash/...) | exit 2 = block · exit 0 + JSON = annotate |
PostToolUse | After a tool call succeeded | exit 0 + JSON = side-effect log |
UserPromptSubmit | When the user sends a message | JSON.prepend = prepend to prompt |
SessionStart | Each new conversation | JSON.context = inject context |
Stop | Agent says "done" | JSON = summary surfaced to user |
SubagentStop | A subagent returns | JSON.acceptance = ok | needs-review |
Notification | Long task waiting for input | side-effect (e.g. desktop notify) |
PreCompact | Just before context compaction | side-effect (e.g. snapshot to disk) |
§3 The 9 Hooks
protect-sensitive-files
What. Blocks writes to .env, .env.*, credentials, service-account.json, *.pem, *.key, .aws/credentials, .kube/config, SSH keys, applied migrations.
Why. One bad write = leaked secrets in git log forever. Prompt-only rules fail when the LLM "knows" it's safe.
Override. AGENT_HOOK_ALLOW_SENSITIVE=/intentional/.env:/other/path.
guard-bash
What. 17 dangerous patterns: rm -rf, git reset --hard, git push --force, curl | sh, mkfs.*, dd if=...of=/dev/sd*, chmod -R 777, DROP TABLE, fork bombs, raw-device redirects.
Conservative bias. Even echo 'rm -rf is in a string' blocks — LLM might bypass via eval.
Override. AGENT_HOOK_BASH_YOLO=1.
post-edit-format
What. Picks formatter by suffix: .ts/.tsx/.js/.jsx → prettier/biome; .py → ruff/black; .go → gofmt; .rs → rustfmt; .sh → shfmt.
Graceful. Skips silently if no formatter installed. AGENT_HOOK_SKIP_FORMAT=1 to disable.
session-context-injector
Injects git branch, last commit, dirty count, AGENTS.md / CLAUDE.md / README.md presence, active .sisyphus/plans/*.md, python version, platform.
final-verify
Reports changed files (from git status), open TODO/FIXME/XXX/HACK, optional test run (AGENT_HOOK_FINAL_RUN_TESTS=1). Auto-detects pyproject.toml / package.json / Cargo.toml / go.mod.
prompt-context-enricher
Prepends a <project-context> block: branch, last commit, working-tree stats, active plans. Stops the agent from asking what it could just inspect.
subagent-acceptance
Scans the subagent's report for shallow-completion claims ("should work", "did not run", "not fully tested") and incomplete markers (TODO, placeholder, stub). Returns acceptance=needs-review if found.
notify-on-idle
macOS only (uses osascript). Fires display notification when the agent waits for input or finishes a long task.
pre-compact-save
Before context compaction, snapshots {branch, last_commit, working_tree, plans, timestamp} to .sisyphus/snapshots/pre-compact-{timestamp}.json. Recovery-friendly.
§4 Evidence: real blocking
Real node loading the generated opencode plugin, real spawnSync of Python, real exit-2:
$ node --input-type=module -e '
const mod = await import("/Users/lute/.config/opencode/plugins/agent-hook-protect-sensitive-files.js");
const r1 = await mod.default.onPreToolUse({
tool: "Write", parameters: { filePath: "/proj/.env" }
});
console.log(r1);
'
{
block: true,
reason: "protect-sensitive-files: blocked write to '/proj/.env'
— matches exact name '.env'.
Override via AGENT_HOOK_ALLOW_SENSITIVE=path1:path2 if intentional."
}
Recorded in repo at docs/demo/E2E-DEMO.md (in Agent_skills for the cross-repo demo).
§5 CLI Commands
| Command | Does |
|---|---|
agent-hook list | Tabular view of all 9 hooks × 4 client install states. |
agent-hook install <name> --client all|opencode|cursor|kimi | Generate adapter artifact, write client config, backup, anchor. |
agent-hook uninstall <name> --client all|... | Remove by anchor. Refuse non-managed entries. |
agent-hook doctor | Validate manifests, check binaries on PATH, audit client config consistency. |
agent-hook show <name> | Print manifest as JSON. |
§6 Manifest Schema
Identical schema across all 3 source repos (Agent_skills/Agent_hook/Agent_mcp). Validated by agent/lib/manifest.py on every load:
kind: hook # required: skill | hook | mcp
name: protect-sensitive-files # required: [a-z][a-z0-9-]{1,63}
version: 0.1.0 # required: semver
description: | # required: >= 20 chars (WHAT + WHEN)
Block writes to sensitive files (.env, secrets.*, credentials, *.pem,
applied migrations) on PreToolUse(Write|Edit|MultiEdit). Provides
AGENT_HOOK_ALLOW_SENSITIVE escape hatch for intentional cases.
domain: code-quality # required
priority: P0 # required: P0|P1|P2
compatibility: # required: all 4 clients
opencode: native
codex: unsupported
cursor: adapter
kimi: adapter
source:
type: local # local | external | git | npm | pypi
path: registry/protect-sensitive-files/source/
hook_events: # required when kind=hook
- PreToolUse
matchers:
- "Write"
- "Edit"
- "MultiEdit"
requires:
binaries: [python3]
triggers: [sensitive file, .env, secret protection]
Reject conditions: literal token strings (ghp_*, sk-*, AIza*), unknown hook_events, missing client, wrong source type.