Claude Code Hooks 完整参考指南

Hooks 是在 Claude Code 生命周期特定时机自动执行的用户定义处理器,提供对 Claude Code 行为的确定性控制(deterministic control),而不是依赖 LLM 去决定是否运行某些操作。可用于执行项目规则、自动化重复任务、与外部工具集成。

文档来源:


1. Hook 处理器类型

类型说明
commandShell 命令(最常见)
httpPOST 到 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)
PermissionDeniedauto mode classifier 拒绝工具调用否(可 retry: true)
NotificationClaude Code 发送通知否
SubagentStart通过 Agent tool 生成 subagent否
SubagentStopsubagent 完成回答是
StopClaude 完成回答是
StopFailure由于 API 错误结束回合否
TaskCreated通过 TaskCreate 创建任务是
TaskCompleted任务被标记为完成是
TeammateIdleagent team 中 teammate 即将 idle是
InstructionsLoadedCLAUDE.md 或 .claude/rules/*.md 被加载否(仅审计)
ConfigChange配置文件在会话中变更是(policy_settings 除外)
CwdChanged工作目录变更(如 cd)否
FileChanged被 watch 的文件在磁盘变化否
WorktreeCreate创建 worktree是(非零退出失败)
WorktreeRemoveworktree 被移除否
PreCompact上下文压缩前是
PostCompact上下文压缩完成后否
ElicitationMCP server 请求用户输入是
ElicitationResult用户响应 MCP elicitation 后是

4. 配置格式(settings.json)

Hooks 使用 3 层 JSON 嵌套结构:

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

json
{
  "type": "command",
  "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/script.sh",
  "async": false,
  "asyncRewake": false,
  "shell": "bash"
}
  • async: 后台运行,不阻塞
  • asyncRewake: 后台运行,exit code 2 时唤醒 Claude
  • shell: "bash"(默认)或 "powershell"

环境变量:

  • $CLAUDE_PROJECT_DIR — 项目根目录
  • ${CLAUDE_PLUGIN_ROOT} — 插件安装目录
  • ${CLAUDE_PLUGIN_DATA} — 插件持久化数据目录
  • CLAUDE_ENV_FILE — 写入 export VAR=value 行以持久化环境变量

4.3 HTTP hook

json
{
  "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

json
{
  "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

json
{
  "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 含义:

EventMatcher 过滤字段示例
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, SubagentStopagent 类型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):

json
{
  "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 时支持细粒度控制:

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"
  }
}
字段默认说明
continuetruefalse 完全停止 Claude
stopReason无continue: false 时显示
suppressOutputfalse从 debug log 省略 stdout
systemMessage无显示给用户的警告
decision无"block" 用于支持的事件
hookSpecificOutput无事件特定的嵌套对象

重要: 不要混用 exit 2 和 JSON 输出 — exit 2 时 JSON 会被忽略。


6. 关键事件详细说明

6.1 PreToolUse

Input:

json
{
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" },
  "tool_use_id": "toolu_01ABC123..."
}

控制:

json
{
  "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 可返回:

json
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow|deny",
      "updatedInput": {},
      "updatedPermissions": []
    }
  }
}

updatedPermissions 条目类型: addRules, replaceRules, removeRules, setMode, addDirectories, removeDirectories

Destinations: session, localSettings, projectSettings, userSettings

⚠️ PermissionRequest hook 在非交互模式(-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 项目级 关键区别

  1. 多层合并: 多个 hook 匹配同一事件并行运行,决策合并采用最严格结果(deny > ask > allow)。所有 additionalContext 拼接传给 Claude。

  2. 安全边界: Managed policy settings 来自管理员无法绕过;PreToolUse hook 在任何 permission mode 检查之前触发 — 即便 bypassPermissions 也能阻止工具。但 hook 的 "allow" 不能绕过 deny 规则 — hook 只能收紧,不能放宽。

  3. 批量禁用: 在任意 settings 设置 "disableAllHooks": true 可一次性禁用该作用域内所有 hook。

  4. 查看当前 hook: /hooks 命令(只读)显示所有已配置 hook,按事件分组。


8. 安全考虑

⚠️ 免责声明: Hooks 拥有用户权限,恶意 hook 可盗取凭据、外泄数据、入侵系统。只使用可信来源的 hook。

8.1 最佳实践

  1. 验证所有输入。 tool_input 是用户(通过 Claude)控制的。使用 jq 安全提取 JSON,shell 变量必须加引号。

  2. 避免 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。

  3. 保护敏感文件。 transcript_path 含完整会话(可能含 API key、密码)。

  4. 限制环境变量插值。 HTTP hook 的 headers 只解析 allowedEnvVars 白名单内的变量。

  5. 不要硬编码密钥。 /hooks 菜单显示完整命令/URL — 用 allowedEnvVars 或 ~/.netrc 管理凭据。

  6. 路径遍历防护:

    bash
    FILE_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)
  • PostToolUse hook 无法撤销操作(tool 已执行)
  • PermissionRequest hook 在 -p 非交互模式不触发
  • Stop hook 在 Claude 每次完成回答时触发(不只任务完成),不会在用户打断时触发
  • 多个 PreToolUse hook 并行修改同一工具参数时,最后完成的胜出(非确定性)

10.2 Stop hook 无限循环防护

bash
#!/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 前。

修复:

bash
# 只在交互 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 配置

参考链接

Comments (0)

No comments yet

imfht
April 15, 2026