Claude Code Hooks Plugin
The Claude Code Hooks plugin runs external shell commands or HTTP endpoints on agent lifecycle events. It is wire-compatible with Anthropic's Claude Code hooks — the same JSON-in/JSON-out protocol, the same event names, the same settings.json schema — so existing hook scripts can be reused without modification.
Alongside the six Claude Code events, seven Codumentor-only events cover LLM calls, prompt assembly, tool progress, and persistence.
Enable
plugins:
- module: codumentor.plugins.claude_code_hooks
class: ClaudeCodeHooksPlugin
priority: 5
args:
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "$CODUMENTOR_PROJECT_DIR/.codumentor/hooks/block-rm.sh"
timeout: 10
UserPromptSubmit:
- hooks:
- type: command
command: ".codumentor/hooks/audit.sh"
The hooks: block is a drop-in Claude Code settings.json snippet. YAML parses JSON as a subset, so you can paste existing Claude Code rules unchanged.
Default priority is 5 (early), so PreToolUse and UserPromptSubmit see the prompt and tool call before other plugins rewrite them. Raise priority if you want last-word observation instead.
Configuration sources
Three inputs are merged in this order (later entries are appended):
| Source | When to use |
|---|---|
args.hooks (inline) | Small, project-specific rule sets |
args.hooks_file (YAML/JSON) | Sharing hook sets across configs |
.claude/settings.json files | Opt-in reuse of existing Claude Code setups |
args:
hooks_file: "hooks.yaml" # resolved vs. the config file's directory
discover_claude_settings: true # default false
claude_settings_paths:
- ".claude/settings.json"
- ".claude/settings.local.json"
- "~/.claude/settings.json"
Paths support ~ expansion and ${VAR} interpolation. Invalid rules are logged and dropped rather than crashing plugin load.
| Parameter | Type | Default | Description |
|---|---|---|---|
hooks | object | unset | Inline hook rules, Claude Code settings.json shape. |
hooks_file | string | unset | External YAML or JSON file of the same shape. |
discover_claude_settings | bool | false | Merge hooks from Claude Code settings files. |
claude_settings_paths | list | the three paths above | Files to read when discovery is on. |
priority | int | 5 | Plugin priority (lower runs earlier). |
Events
Claude Code parity
| Event | When it fires | If a hook returns block |
|---|---|---|
SessionStart | First moment of a session | Logged only (no effect) |
SessionEnd | Session ends | Logged only (no effect) |
UserPromptSubmit | User submits a prompt | The reason becomes the assistant's reply; the model is not called |
PreToolUse | Before a tool runs | The tool is skipped; the model sees blocked: <reason> |
PostToolUse | After a tool finishes | A "Hook blocked: <reason>" notice is added for the next turn |
Stop | Final assistant text is about to be sent | The reason replaces that text |
Codumentor-only
Subscribe to these the same way as the Claude Code events. Blocking is ignored (the reason is logged):
| Event | Typical use |
|---|---|
PreLLMCall | Inspect model or parameters before the call |
PostLLMCall | Log completions and token usage |
LLMError | Alert on model failure |
PromptAssemble | Observe or adjust assembled messages and tools |
TurnInterrupted | Cleanup when the user cancels the turn |
ToolProgress | Stream long-running tool output to an external system |
ResponsePersist | Audit or analytics on each saved assistant turn |
Command hooks (type: command)
stdin — a single JSON object:
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {"command": "rm -rf /"},
"cwd": "/home/user/project",
"payload": { "...": "event-specific fields" }
}
stdout — optional JSON; plain text is treated as reason:
{
"decision": "block",
"reason": "Destructive rm blocked by policy",
"hookSpecificOutput": {"hookEventName": "PreToolUse"}
}
Exit codes
| Code | Meaning |
|---|---|
0 | allow (JSON on stdout is still honoured when present) |
2 | block (stderr becomes the reason if no JSON is supplied) |
| other | warn-only; stderr is logged |
Environment
CLAUDE_PROJECT_DIR,CODUMENTOR_PROJECT_DIR— workspace rootCLAUDE_HOOK_EVENT,CODUMENTOR_HOOK_EVENT— event nameCLAUDE_SESSION_ID— session identifier
$CLAUDE_PROJECT_DIR / ${CLAUDE_PROJECT_DIR} and the Codumentor alias are expanded inside the command string before the process starts.
Timeouts — per-hook timeout: <seconds> (default 30). Timeouts are warn-only (no block) and logged.
Concurrency — all matching hooks for one event run at the same time. The first block wins; other outputs are still captured in the logs.
HTTP hooks (type: http)
PreToolUse:
- matcher: "Bash"
hooks:
- type: http
url: "https://audit.internal/codumentor-hook"
method: POST
headers: {Authorization: "Bearer ${AUDIT_TOKEN}"}
timeout: 5
Request body matches the command-hook stdin. A JSON response uses the same decision / reason contract. Non-2xx responses behave like a non-zero exit code — warn-only unless decision is set explicitly.
Matchers and if:
matcher: is a regex applied to the tool name. It applies on per-tool events only (PreToolUse, PostToolUse, ToolProgress). Omit it to run for every tool.
A small if: filter is accepted for compatibility: ToolName(arg-glob) matches the tool name and a glob against the string form of the tool arguments. Anything richer is logged and ignored.