Claude Code Hooks 完整参考指南
Hooks 是在 Claude Code 生命周期特定时机自动执行的用户定义处理器,提供对 Claude Code 行为的确定性控制(deterministic control),而不是依赖 LLM 去决定是否运行某些操作。可用于执行项目规则、自动化重复任务、与外部工具集成。
文档来源:
- Hooks reference: https://code.claude.com/docs/en/hooks.md
- Hooks guide: https://code.claude.com/docs/en/hooks-guide.md
1. Hook 处理器类型
| 类型 | 说明 |
|---|---|
command | Shell 命令(最常见) |
http | POST 到 HTTP endpoint |
prompt | 单轮 LLM 评估(默认 Haiku) |
agent | 多轮带工具的 subagent 评估 |
2. Hook 生命周期节奏
| 节奏 | 事件 |
|---|---|
| 每个会话一次 | SessionStart, SessionEnd |
| 每个回合一次 | UserPromptSubmit, Stop, StopFailure |
| 每次工具调用 | PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied, SubagentStart, SubagentStop, TaskCreated, TaskCompleted |
| 异步事件 | FileChanged, CwdChanged, ConfigChange, InstructionsLoaded, Notification, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact, Elicitation, ElicitationResult, TeammateIdle |
3. 完整事件列表
| Event | 何时触发 | 可否阻塞 |
|---|---|---|
SessionStart | 会话开始或恢复 | 否(stdout 作为 context 注入) |
SessionEnd | 会话终止 | 否 |
UserPromptSubmit | 用户提交 prompt,Claude 处理前 | 是 |
PreToolUse | 工具调用执行前 | 是 |
PostToolUse | 工具调用成功后 | 否(工具已执行),但可 decision: block 给反馈 |
PostToolUseFailure | 工具调用失败后 | 否 |
PermissionRequest | 权限对话框即将显示 | 是(可改为 allow/deny) |
PermissionDenied | auto mode classifier 拒绝工具调用 | 否(可 retry: true) |
Notification | Claude Code 发送通知 | 否 |
SubagentStart | 通过 Agent tool 生成 subagent | 否 |
SubagentStop | subagent 完成回答 | 是 |
Stop | Claude 完成回答 | 是 |
StopFailure | 由于 API 错误结束回合 | 否 |
TaskCreated | 通过 TaskCreate 创建任务 | 是 |
TaskCompleted | 任务被标记为完成 | 是 |
TeammateIdle | agent team 中 teammate 即将 idle | 是 |
InstructionsLoaded | CLAUDE.md 或 .claude/rules/*.md 被加载 | 否(仅审计) |
ConfigChange | 配置文件在会话中变更 | 是(policy_settings 除外) |
CwdChanged | 工作目录变更(如 cd) | 否 |
FileChanged | 被 watch 的文件在磁盘变化 | 否 |
WorktreeCreate | 创建 worktree | 是(非零退出失败) |
WorktreeRemove | worktree 被移除 | 否 |
PreCompact | 上下文压缩前 | 是 |
PostCompact | 上下文压缩完成后 | 否 |
Elicitation | MCP server 请求用户输入 | 是 |
ElicitationResult | 用户响应 MCP elicitation 后 | 是 |
4. 配置格式(settings.json)
Hooks 使用 3 层 JSON 嵌套结构:
{
"hooks": {
"EVENT_NAME": [
{
"matcher": "ToolName|OtherTool",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "/path/to/script.sh",
"timeout": 600,
"statusMessage": "Validating...",
"async": false
}
]
}
]
}
}
4.1 通用字段
| 字段 | 必需 | 说明 |
|---|---|---|
type | 是 | "command" / "http" / "prompt" / "agent" |
if | 否 | 权限规则语法过滤,如 "Bash(git *)"、"Edit(*.ts)"(v2.1.85+) |
timeout | 否 | 超时秒数(默认:command 600,prompt 30,agent 60) |
statusMessage | 否 | 自定义 spinner 消息 |
once | 否 | 会话中运行一次后移除(仅 skills) |
4.2 Command hook
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/script.sh",
"async": false,
"asyncRewake": false,
"shell": "bash"
}
async: 后台运行,不阻塞asyncRewake: 后台运行,exit code 2 时唤醒 Claudeshell:"bash"(默认)或"powershell"
环境变量:
$CLAUDE_PROJECT_DIR— 项目根目录${CLAUDE_PLUGIN_ROOT}— 插件安装目录${CLAUDE_PLUGIN_DATA}— 插件持久化数据目录CLAUDE_ENV_FILE— 写入export VAR=value行以持久化环境变量
4.3 HTTP hook
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": { "Authorization": "Bearer $MY_TOKEN" },
"allowedEnvVars": ["MY_TOKEN"]
}
- POST 发送 JSON body(与 command hook 的 stdin 格式相同)
- 仅
allowedEnvVars中列出的环境变量会被插值
4.4 Prompt hook
{
"type": "prompt",
"prompt": "Review this and decide if it's safe: $ARGUMENTS",
"model": "fast-model",
"timeout": 30
}
响应格式:{"ok": true} 允许,或 {"ok": false, "reason": "..."} 阻止。
4.5 Agent hook
{
"type": "agent",
"prompt": "Verify these changes don't break tests: $ARGUMENTS",
"model": "sonnet",
"timeout": 120
}
生成可用工具(Read/Grep/Glob)的 subagent,默认最多 50 次 tool-use turns。
4.6 Matcher 模式
| 模式 | 求值方式 | 例子 |
|---|---|---|
"*"、""、省略 | 匹配全部 | 每次都触发 |
字母、数字、_、| | 精确匹配或管道列表 | Bash、Edit|Write |
| 其他字符 | JavaScript 正则 | ^Notebook、mcp__memory__.* |
事件的 matcher 含义:
| Event | Matcher 过滤字段 | 示例 |
|---|---|---|
PreToolUse, PostToolUse, PermissionRequest 等 | 工具名 | Bash, Edit|Write, mcp__.* |
SessionStart | 启动源 | startup, resume, clear, compact |
SessionEnd | 结束原因 | clear, resume, logout, prompt_input_exit 等 |
Notification | 通知类型 | permission_prompt, idle_prompt, auth_success |
SubagentStart, SubagentStop | agent 类型 | Bash, Explore, Plan |
PreCompact, PostCompact | 触发源 | manual, auto |
ConfigChange | 配置源 | user_settings, project_settings, local_settings, policy_settings, skills |
StopFailure | 错误类型 | rate_limit, authentication_failed, server_error 等 |
FileChanged | 字面量文件名(| 分隔) | .envrc|.env |
UserPromptSubmit, Stop, TaskCreated 等 | 不支持 matcher | 每次都触发 |
MCP 工具匹配: 命名约定为 mcp__<server>__<tool>,例如 mcp__memory__.*。
5. 输入输出约定
5.1 通用输入字段
所有 hook 收到(command hook 通过 stdin):
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default|plan|acceptEdits|auto|dontAsk|bypassPermissions",
"hook_event_name": "PreToolUse",
"agent_id": "agent-abc123",
"agent_type": "Explore"
}
5.2 Exit Codes
| Exit Code | 含义 |
|---|---|
0 | 成功。UserPromptSubmit 和 SessionStart 的 stdout 作为 context 注入 |
2 | 阻止(blocking)。stderr 送给 Claude 或用户 |
| 其他 | 非阻止错误。transcript 显示第一行 stderr |
Exit code 2 在各事件的效果:
| Event | 可阻塞 | 效果 |
|---|---|---|
PreToolUse | 是 | 阻止 tool call |
PermissionRequest | 是 | 拒绝权限 |
UserPromptSubmit | 是 | 阻止 prompt,擦除它 |
Stop / SubagentStop | 是 | 阻止停止 |
TaskCreated / TaskCompleted | 是 | 阻止任务操作 |
ConfigChange | 是 | 阻止配置变更 |
PreCompact | 是 | 阻止压缩 |
Elicitation, ElicitationResult | 是 | 拒绝响应 |
PostToolUse | 否 | stderr 送给 Claude(工具已执行) |
WorktreeCreate | 是 | 非零 exit 导致失败 |
Notification, SessionStart/End | 否 | stderr 仅显示给用户 |
5.3 JSON Output 结构化控制
exit 0 且 stdout 为 JSON 时支持细粒度控制:
{
"continue": true,
"stopReason": "Message shown to user",
"suppressOutput": false,
"systemMessage": "Warning to user",
"decision": "block",
"reason": "Why blocked",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask|defer",
"permissionDecisionReason": "Reason",
"updatedInput": {},
"additionalContext": "Context for Claude"
}
}
| 字段 | 默认 | 说明 |
|---|---|---|
continue | true | false 完全停止 Claude |
stopReason | 无 | continue: false 时显示 |
suppressOutput | false | 从 debug log 省略 stdout |
systemMessage | 无 | 显示给用户的警告 |
decision | 无 | "block" 用于支持的事件 |
hookSpecificOutput | 无 | 事件特定的嵌套对象 |
重要: 不要混用 exit 2 和 JSON 输出 — exit 2 时 JSON 会被忽略。
6. 关键事件详细说明
6.1 PreToolUse
Input:
{
"tool_name": "Bash",
"tool_input": { "command": "npm test" },
"tool_use_id": "toolu_01ABC123..."
}
控制:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask|defer",
"permissionDecisionReason": "Reason",
"updatedInput": { "modified_field": "new_value" },
"additionalContext": "Context for Claude"
}
}
| Decision | 效果 |
|---|---|
allow | 跳过权限 prompt(但 deny/ask 规则仍生效) |
deny | 阻止 tool call |
ask | 提示用户确认 |
defer | 优雅退出用于 resume |
多 hook 决策优先级:deny > defer > ask > allow。
6.2 PostToolUse
Input: {tool_name, tool_input, tool_response, tool_use_id}
可通过 hookSpecificOutput.additionalContext 给模型反馈,或 updatedMCPToolOutput 替换 MCP 工具输出。
6.3 PermissionRequest
提供 permission_suggestions 数组,hook 可返回:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow|deny",
"updatedInput": {},
"updatedPermissions": []
}
}
}
updatedPermissions 条目类型: addRules, replaceRules, removeRules, setMode, addDirectories, removeDirectories
Destinations: session, localSettings, projectSettings, userSettings
⚠️
PermissionRequesthook 在非交互模式(-p)下不触发 — 使用PreToolUse替代。
6.4 Stop
Input: {stop_reason, message, stop_hook_active}
⚠️ Stop hook 必须检查
stop_hook_active字段防止无限循环。
6.5 SessionStart
Matcher: startup, resume, clear, compact
可通过 stdout 注入额外 context,支持 JSON additionalContext、sessionTitle,以及 CLAUDE_ENV_FILE 持久化环境变量。
6.6 FileChanged / CwdChanged
支持 CLAUDE_ENV_FILE — 写入 export VAR=value 行即可持久化到后续 Bash 命令,常用于 direnv 集成。
7. Hook 作用域与位置
| 位置 | 作用域 | 可共享 |
|---|---|---|
~/.claude/settings.json | 用户级,所有项目 | 否(本机) |
.claude/settings.json | 单个项目 | 是,commit 到仓库 |
.claude/settings.local.json | 单个项目 | 否,gitignored |
| Managed policy settings | 组织级 | 是,管理员控制 |
Plugin hooks/hooks.json | 插件启用时 | 是 |
| Skill / agent frontmatter | 组件激活时 | 是 |
用户级 vs 项目级 关键区别
-
多层合并: 多个 hook 匹配同一事件并行运行,决策合并采用最严格结果(
deny>ask>allow)。所有additionalContext拼接传给 Claude。 -
安全边界: Managed policy settings 来自管理员无法绕过;
PreToolUsehook 在任何 permission mode 检查之前触发 — 即便bypassPermissions也能阻止工具。但 hook 的"allow"不能绕过 deny 规则 — hook 只能收紧,不能放宽。 -
批量禁用: 在任意 settings 设置
"disableAllHooks": true可一次性禁用该作用域内所有 hook。 -
查看当前 hook:
/hooks命令(只读)显示所有已配置 hook,按事件分组。
8. 安全考虑
⚠️ 免责声明: Hooks 拥有用户权限,恶意 hook 可盗取凭据、外泄数据、入侵系统。只使用可信来源的 hook。
8.1 最佳实践
-
验证所有输入。
tool_input是用户(通过 Claude)控制的。使用jq安全提取 JSON,shell 变量必须加引号。 -
避免 shell 注入:
bash# 错误 COMMAND=$(jq -r '.tool_input.command') if echo $COMMAND | grep rm; then ... # 正确 COMMAND=$(jq -r '.tool_input.command') if echo "$COMMAND" | grep -q rm; then ...不要用
eval。 -
保护敏感文件。
transcript_path含完整会话(可能含 API key、密码)。 -
限制环境变量插值。 HTTP hook 的
headers只解析allowedEnvVars白名单内的变量。 -
不要硬编码密钥。
/hooks菜单显示完整命令/URL — 用allowedEnvVars或~/.netrc管理凭据。 -
路径遍历防护:
bashFILE_PATH=$(jq -r '.tool_input.file_path') REAL_PATH=$(realpath "$FILE_PATH" 2>/dev/null) PROJECT_ROOT=$(realpath "$CLAUDE_PROJECT_DIR") if *"$REAL_PATH" != "$PROJECT_ROOT"**; then echo "Access denied: path outside project" >&2 exit 2 fi
9. 常见使用场景
| 场景 | 使用事件 |
|---|---|
| Claude 需要输入时桌面通知 | Notification |
| 编辑后自动格式化代码(prettier) | PostToolUse matcher Edit|Write |
| 阻止修改受保护文件(.env, .git/) | PreToolUse matcher Edit|Write, exit 2 |
| 压缩后重新注入上下文 | SessionStart matcher compact,stdout → context |
| 审计配置变更 | ConfigChange |
| 目录变更时重载环境(direnv) | CwdChanged / FileChanged,写 CLAUDE_ENV_FILE |
| 自动批准特定权限提示 | PermissionRequest 返回 behavior: allow |
| Bash 命令安全验证 | PreToolUse matcher Bash,或 if: "Bash(git *)" |
| Stop 前验证测试通过 | Stop + type: agent 运行 test suite |
| 跨团队共享审计服务 | type: http 发送到 logging service |
10. 限制与故障排除
10.1 主要限制
- Command hook 仅通过 stdout/stderr/exit code 通信,不能触发
/命令或工具调用 - Hook 超时默认 10 分钟(command)
PostToolUsehook 无法撤销操作(tool 已执行)PermissionRequesthook 在-p非交互模式不触发Stophook 在 Claude 每次完成回答时触发(不只任务完成),不会在用户打断时触发- 多个
PreToolUsehook 并行修改同一工具参数时,最后完成的胜出(非确定性)
10.2 Stop hook 无限循环防护
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fi
10.3 "JSON validation failed"
原因:shell profile (~/.zshrc / ~/.bashrc) 有无条件 echo 把文字拼接到 hook 的 JSON stdout 前。
修复:
# 只在交互 shell 中 echo
if *$- == *i**; then
echo "Shell ready"
fi
10.4 Debug 技巧
Ctrl+O— 打开 transcript view,查看每个 hook 的摘要claude --debug-file /tmp/claude.log— 写 debug log/debug— 运行中启用 logging/hooks— 只读菜单验证 hook 配置
参考链接
- Hooks reference: https://code.claude.com/docs/en/hooks.md
- Hooks guide with examples: https://code.claude.com/docs/en/hooks-guide.md
- Permission rules syntax: https://code.claude.com/docs/en/permissions
- Settings reference: https://code.claude.com/docs/en/settings
- Bash validator example: https://github.com/anthropics/claude-code/blob/main/examples/hooks/bash_command_validator_example.py