with one click
claude-code-hooks
Use when configuring automated lifecycle commands (hooks) in Claude Code settings.json — event types, matcher syntax, exit codes, JSON output, path placeholders.
Menu
Use when configuring automated lifecycle commands (hooks) in Claude Code settings.json — event types, matcher syntax, exit codes, JSON output, path placeholders.
Based on SOC occupation classification
Use when unsure which Claude Code capability fits a task, when about to single-thread large/cross-cutting/repetitive work, or when you or the user ask what Claude Code can do — loads the full capability map (signal → capability) and points to the deep-dive skills.
Use when building custom AI agents headlessly, embedding Claude Code tools in an app, or running scripted/cron agents—need SDK package names, setup, core API entry points, supported languages, and relation to Claude Code CLI and Messages...
Use when needing to understand or manage background agents, long-running tasks, polling, scheduling, or async work in Claude Code without blocking the session.
Use when you need to undo code edits, explore alternatives without losing a starting point, recover from a bad edit path, or restore previous conversation state — press Esc Esc or run /rewind to open checkpoints menu.
Use when a task is too big for one context — broad audits, multi-file migrations, multi-source research, or parallel multi-dimension review — and you want to fan out and orchestrate many subagents deterministically with the Workflow tool (requires ultracode / explicit opt-in).
Use when deciding how to work on a task - choosing effort level, model, or fast mode to balance reasoning depth, speed, and token cost.
| name | claude-code-hooks |
| description | Use when configuring automated lifecycle commands (hooks) in Claude Code settings.json — event types, matcher syntax, exit codes, JSON output, path placeholders. |
| user-invocable | true |
Hooks are shell commands, HTTP endpoints, or MCP tools that execute automatically at lifecycle points: before/after tool calls, on session start/end, when config changes, and on other events. The harness controls execution; Claude cannot suppress them.
Hooks fire at specific cadences:
Once per session:
SessionStart — beginning, resume, or after compactionSessionEnd — on session closeOnce per turn:
UserPromptSubmit — before Claude reads prompt (can block)UserPromptExpansion — before user command expands to prompt (can block)Stop — after Claude respondsStopFailure — if response failedPer tool call:
PreToolUse — before tool executes (can block)PermissionRequest — when permission dialog appearsPermissionDenied — when tool denied; can return {retry: true}PostToolUse — after tool succeedsPostToolUseFailure — if tool failedPostToolBatch — after parallel batch resolvesAsync/file watching:
FileChanged — watched file modified on disk (matcher specifies files)CwdChanged — working directory changedConfigChange — settings/skills file modifiedInstructionsLoaded — CLAUDE.md or rules/*.md loadedNotification — when Claude Code sends notification (matcher: notification type)MessageDisplay — while assistant message displaysSubagentStart — subagent spawned (matcher: agent type)SubagentStop — subagent finishedTaskCreated, TaskCompleted — task lifecycleSetup — on --init-only or CI setup modePreCompact, PostCompact — context compaction boundaryElicitation, ElicitationResult — MCP server user inputWorktreeCreate, WorktreeRemove — git worktree lifecycleTeammateIdle — agent team teammate idle~/.claude/settings.json — user-wide (all projects).claude/settings.json — project-wide (shareable, commitable).claude/settings.local.json — project-specific (gitignored)hooks/hooks.json — loaded at plugin install{
"hooks": {
"EventName": [
{
"matcher": "ToolName|OtherTool",
"hooks": [
{
"type": "command|http|mcp_tool|prompt|agent",
"command": "script.sh",
"timeout": 30,
"statusMessage": "Running validation...",
"async": false,
"if": "Bash(git *)"
}
]
}
]
}
}
"*" — matches all_/| — exact match or literal alternatives: Bash, Edit|Write, mcp__github__create_issue^Notebook, .envrc|.env, mcp__.*__write.*startup|resume|clear|compactpermission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_responseExec form (preferred; safe for paths with special chars):
{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/script.js", "--fix"]
}
Spawns directly without shell; ${} placeholders safe.
Shell form (full features like pipes, globs):
{
"type": "command",
"command": "grep 'error' log.txt | wc -l"
}
Runs in shell; supports &&, pipes, * globs. Avoid untrusted paths.
Common fields:
timeout — seconds before kill (default: varies by event)statusMessage — user-visible spinner textasync — run in background without blockingasyncRewake — background task, wake Claude on exit 2if — permission rule: Bash(rm **), Edit(*.ts), Bash(git *)once — run once per session, then remove (skills only)shell — bash (default) or powershell${CLAUDE_PROJECT_DIR} — project root${CLAUDE_PLUGIN_ROOT} — plugin installation directory${CLAUDE_PLUGIN_DATA} — plugin persistent data dirAlways use exec form with args when using placeholders.
Claude Code on Windows auto-prepends bash to .sh commands. Override by using extensionless filenames with a wrapper:
{
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start"
}
Wrapper (run-hook.cmd): batch block for cmd.exe, shell code for bash:
: << 'CMDBLOCK'
@echo off
if exist "C:\Program Files\Git\bin\bash.exe" (
"C:\Program Files\Git\bin\bash.exe" "%~dp0%~1" %2 %3
exit /b %ERRORLEVEL%
)
where bash >nul 2>nul && bash "%~dp0%~1" %2 %3 && exit /b %ERRORLEVEL%
exit /b 0
CMDBLOCK
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
exec bash "${SCRIPT_DIR}/$1" "${@:2}"
The : << 'CMDBLOCK' trick: cmd.exe runs batch; bash sees : (no-op) and runs Unix shell code.
| Code | Meaning |
|---|---|
| 0 | Success. Harness parses stdout for JSON output. |
| 2 | Blocking error. Stderr shown to user. Hook can deny action or block turn. |
| Other | Non-blocking error. First line of stderr shown, execution continues. |
All hooks receive:
session_id — unique identifiercwd — current working directorypermission_mode — default, plan, acceptEdits, auto, dontAsk, bypassPermissionshook_event_name — event nameeffort.level — low, medium, high, xhigh, maxtranscript_path — path to session transcript (JSONL)agent_id, agent_type — when inside subagenttool_name, tool_input for PreToolUse)Exit 0 with valid JSON stdout to control harness. Output format depends on your platform:
Claude Code (CLAUDE_PLUGIN_ROOT set):
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "Context for Claude",
"permissionDecision": "deny",
"permissionDecisionReason": "Blocked by policy"
}
}
Cursor (CURSOR_PLUGIN_ROOT set):
{
"additional_context": "Context for Claude"
}
Copilot CLI (SDK standard):
{
"additionalContext": "Context for Claude"
}
Universal fields:
continue: false — stop entire turnstopReason — message when stoppingsuppressOutput: true — hide from transcriptsystemMessage — warning shown to userterminalSequence — OSC codes (desktop notification, window title){
"type": "http",
"url": "http://localhost:8080/hook",
"headers": {"Authorization": "Bearer ${TOKEN}"},
"allowedEnvVars": ["TOKEN"]
}
POST with stdin JSON. Whitelist env vars to pass. Return 2xx with JSON body for decisions.
{
"type": "mcp_tool",
"server": "my_mcp_server",
"tool": "tool_name",
"input": {"file_path": "${tool_input.file_path}"}
}
Call tool on connected MCP server. Fields can interpolate from stdin.
{
"type": "prompt",
"prompt": "Should this be allowed? $ARGUMENTS",
"model": "fast-model"
}
Sends yes/no question to Claude. Rarely needed; prefer command hooks for determinism.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"if": "Bash(rm -rf *)",
"hooks": [{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.js"]
}]
}
]
}
}
Exit code 2 to deny; stderr message shown.
{
"disableAllHooks": true
}
Top-level key to skip all hooks (useful for debugging).
Don't use hooks for:
/hooks command: browse configured hooks, view matchers/handlersjq before deployingchmod +x script.shtimeout to kill runaway scriptssuppressOutput: true)Hooks execute with your user permissions. Always:
.claude/ or plugin dirs (not world-writable)