DOCUMENTATION

The Handbook

Every hook. Every event. Every adapter. Every command.

Contents

§1 Overview

Agent_hook is the hook layer of a three-repo system:

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:

EventWhen it firesReturn semantics
PreToolUseBefore any tool call (Write/Edit/Bash/...)exit 2 = block · exit 0 + JSON = annotate
PostToolUseAfter a tool call succeededexit 0 + JSON = side-effect log
UserPromptSubmitWhen the user sends a messageJSON.prepend = prepend to prompt
SessionStartEach new conversationJSON.context = inject context
StopAgent says "done"JSON = summary surfaced to user
SubagentStopA subagent returnsJSON.acceptance = ok | needs-review
NotificationLong task waiting for inputside-effect (e.g. desktop notify)
PreCompactJust before context compactionside-effect (e.g. snapshot to disk)

§3 The 9 Hooks

protect-sensitive-files

P0 PreToolUse

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

P0 PreToolUse · 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

P0 PostToolUse · Write/Edit

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

P0 SessionStart

Injects git branch, last commit, dirty count, AGENTS.md / CLAUDE.md / README.md presence, active .sisyphus/plans/*.md, python version, platform.

final-verify

P0 Stop

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

P1 UserPromptSubmit

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

P1 SubagentStop

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

P1 Notification

macOS only (uses osascript). Fires display notification when the agent waits for input or finishes a long task.

pre-compact-save

P1 PreCompact

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

CommandDoes
agent-hook listTabular view of all 9 hooks × 4 client install states.
agent-hook install <name> --client all|opencode|cursor|kimiGenerate adapter artifact, write client config, backup, anchor.
agent-hook uninstall <name> --client all|...Remove by anchor. Refuse non-managed entries.
agent-hook doctorValidate 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.