Hooks

Shell commands, prompts, or HTTP calls that fire on lifecycle events — deterministic automation, not left to the model's discretion.

Overview

A hook is something Claudin runs automatically at a defined point in the session — after a file edit, before a tool call, when a session starts — instead of relying on the model to remember to do it. Hooks live under a hooks key in a settings file, keyed by event name.

Config

SourcePathGit
Project.claudin/settings.jsonCommit
Project-local.claudin/settings.local.jsonGitignore
User~/.claudin/settings.json
ManagedOS-specific absolute path, e.g. /etc/claude-code/managed-settings.json on LinuxEnterprise

Precedence when the same event/matcher shows up in more than one source: user < project < local < the --settings flag < managed.

Full settings reference (permissions, model config, output styles, and more) lives on the Configuration page.

{
  "hooks": {
    "<EventName>": [
      { "matcher": "<pattern>", "hooks": [ { "type": "command", "command": "..." } ] }
    ]
  }
}

/hooks opens a read-only browser of everything currently configured.

Events

27 event names, grouped by what they're for:

GroupEvents
Tool callsPreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied
Session lifecycleSessionStart, SessionEnd, Setup, Stop, StopFailure, InstructionsLoaded
Subagents & tasksSubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle
Context & inputUserPromptSubmit, PreCompact, PostCompact, Elicitation, ElicitationResult
EnvironmentConfigChange, WorktreeCreate, WorktreeRemove, CwdChanged, FileChanged
MiscNotification

Matcher syntax

For tool events, matcher matches a tool name — "Write", "Write|Edit", "Bash", empty for every tool. Non-tool events use their own matcher values, e.g. PreCompact/PostCompact use "manual" or "auto". Each individual hook can also carry its own if field, using permission-rule syntax (like "Bash(git *)") to conditionally gate that one hook regardless of the matcher.

Action types

TypeFieldsNotes
commandcommand, if, shell, timeout, once, async, asyncRewakeRuns a shell command. asyncRewake runs it in the background and wakes the model if it exits with code 2.
promptprompt (uses $ARGUMENTS), timeout, modelLLM-evaluated condition — works on tool events and lifecycle events like Stop.
agentprompt, timeout (default 60s), model (default Haiku)Agentic verifier hook, runs with tools — works on tool events and lifecycle events like Stop.
httpurl, headers, allowedEnvVarsPOSTs the hook input JSON to a URL.

Input & output

Every hook gets a common base on stdin — session_id, transcript_path, cwd, hook_event_name, plus optional permission_mode/agent_id/agent_type — with extra fields layered on per event. Tool events (PreToolUse, PostToolUse, and friends) also carry tool_name, tool_input, and tool_response:

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": { "file_path": "/path/to/file.txt", "content": "..." },
  "tool_response": { "success": true }
}

To block or steer, a hook writes JSON to stdout:

{
  "decision": "block",
  "reason": "Explanation shown to the model",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "..."
  }
}

Example

Run prettier after every write or edit:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true"
      }]
    }]
  }
}

Hook chains

A separate, opt-in layer sits on top of ordinary hooks: instead of a matcher firing a command, a rules engine reacts to hook outcomes — a failed tool call, a completed task — with declarative remediation (spawn a fallback agent, notify the team, warm up remote capacity). Configured at .claudin/hook-chains.json, disabled by default. Most setups won't need this; reach for it only once plain hooks stop being enough.