Codumentor logo Codumentor

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):

SourceWhen to use
args.hooks (inline)Small, project-specific rule sets
args.hooks_file (YAML/JSON)Sharing hook sets across configs
.claude/settings.json filesOpt-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.

ParameterTypeDefaultDescription
hooksobjectunsetInline hook rules, Claude Code settings.json shape.
hooks_filestringunsetExternal YAML or JSON file of the same shape.
discover_claude_settingsboolfalseMerge hooks from Claude Code settings files.
claude_settings_pathslistthe three paths aboveFiles to read when discovery is on.
priorityint5Plugin priority (lower runs earlier).

Events

Claude Code parity

EventWhen it firesIf a hook returns block
SessionStartFirst moment of a sessionLogged only (no effect)
SessionEndSession endsLogged only (no effect)
UserPromptSubmitUser submits a promptThe reason becomes the assistant's reply; the model is not called
PreToolUseBefore a tool runsThe tool is skipped; the model sees blocked: <reason>
PostToolUseAfter a tool finishesA "Hook blocked: <reason>" notice is added for the next turn
StopFinal assistant text is about to be sentThe reason replaces that text

Codumentor-only

Subscribe to these the same way as the Claude Code events. Blocking is ignored (the reason is logged):

EventTypical use
PreLLMCallInspect model or parameters before the call
PostLLMCallLog completions and token usage
LLMErrorAlert on model failure
PromptAssembleObserve or adjust assembled messages and tools
TurnInterruptedCleanup when the user cancels the turn
ToolProgressStream long-running tool output to an external system
ResponsePersistAudit 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

CodeMeaning
0allow (JSON on stdout is still honoured when present)
2block (stderr becomes the reason if no JSON is supplied)
otherwarn-only; stderr is logged

Environment

$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.