跳到正文
FunCoding

搜索

搜索文档、智能体、博客、Skill 和 MCP

Hooks 参考

Hook 配置结构、matcher 与 handler 字段、输入输出与退出码、JSON 输出、各事件能否被阻止,以及安全注意事项。

这是 Hook 事件、配置结构、JSON 输入输出格式、退出码、异步 Hook、HTTP Hook 等的参考。入门和常见用例见用 Hooks 自动化。官方原文逐事件详细列出了每个事件的输入字段和决策控制,本页只整理通用规则和速查表。

配置

Hook 在 JSON 设置文件里定义,配置有三层嵌套:

  1. 选择一个要响应的Hook 事件(如 PreToolUse 或 Stop)
  2. 添加一个matcher 组来过滤何时触发(如「只对 Bash 工具」)
  3. 定义一个或多个在匹配时运行的Hook handler

「Hook 事件」指生命周期点,「matcher 组」指过滤器,「Hook handler」指实际运行的 shell 命令、HTTP 端点、MCP 工具、提示词或智能体。

Hook 位置

位置范围是否可共享
~/.claude/settings.json你的所有项目否,仅本机
.claude/settings.json单个项目是,可提交进仓库
.claude/settings.local.json单个项目否
托管策略设置整个组织是,管理员控制
插件的 hooks/hooks.json插件启用时是,随插件打包
Skill 前置信息Skill 被调用后会话的剩余时间是
子智能体前置信息该子智能体运行期间是

云端会话不读取你本地的 ~/.claude/settings.json。来自设置文件、托管策略设置和插件的 Hook 也会在子智能体内运行:子智能体调用工具时,PreToolUse、PostToolUse 等工具事件会像主对话里一样触发已配置的 Hook。管理员可以在托管设置里用 allowManagedHooksOnly 限制哪些 Hook 能运行(你的用户、项目、本地和插件 Hook 会被阻止)。Hook 条目在设置层级之间合并而不是互相替换。

matcher 模式

matcher 字段过滤 Hook 何时触发,如何评估取决于它包含的字符:

matcher 值评估为示例
"*"、"" 或省略匹配全部在该事件每次发生时触发
只含字母、数字、_、-、空格、, 和 |精确字符串,或用 | 或 , 分隔的精确字符串列表Bash 只匹配 Bash 工具;Edit|Write 和 Edit, Write 各自精确匹配任一工具
包含任何其他字符JavaScript 正则表达式,不锚定^Notebook 匹配名字以 Notebook 开头的任何工具;mcp__memory__.* 匹配 memory 服务器的每个工具

正则路径上的 matcher 用 RegExp.prototype.test 测试,在值的任何位置匹配都成功:Edit.* 同时匹配 Edit 和 NotebookEdit;需要整串匹配时用 ^ 和 $ 包起来,如 ^Edit$。

Hook handler 字段

内层 hooks 数组里的每个对象是一个 Hook handler。有五种类型:

  • 命令 Hook(type: "command"):运行 shell 命令,脚本在 stdin 收到事件的 JSON 输入,通过退出码和 stdout 返回结果
  • HTTP Hook(type: "http"):把事件的 JSON 输入作为 HTTP POST 请求发到某个 URL,端点通过响应体以与命令 Hook 相同的 JSON 输出格式返回结果
  • MCP 工具 Hook(type: "mcp_tool"):调用已配置 MCP 服务器上的工具,工具的文本输出被当作命令 Hook 的 stdout
  • 提示词 Hook(type: "prompt"):把提示词发给 Claude 模型做单轮评估,模型以 JSON 返回决定
  • 智能体 Hook(type: "agent"):派生一个能用 Read、Grep、Glob 等工具的子智能体,在返回决定之前验证条件(实验性)

所有匹配的 Hook 并行运行;同一个 handler 在多个设置文件里定义时只运行一次。

通用字段:

字段必填说明
type是"command"、"http"、"mcp_tool"、"prompt" 或 "agent"
if否用权限规则语法过滤此 Hook 何时运行,如 "Bash(git *)" 或 "Edit(*.ts)";只有工具调用匹配该模式时才运行 Hook 命令。if 字段只放一条权限规则,没有 &&、|| 或列表语法,要应用多个条件就为每个条件定义单独的 handler
timeout否取消前的秒数。默认值:command、http、mcp_tool 为 600;prompt 为 30;agent 为 60
statusMessage否Hook 运行时显示的自定义旋转指示器消息
once否为 true 时,Hook 在第一次成功运行后被移除;只对 Skill 前置信息里声明的 Hook 有效

命令 Hook 字段:command(必填,要执行的 shell 命令;带 args 时是直接启动的可执行文件)、args(参数列表;存在时 command 被当作可执行文件直接启动,不经 shell)、async(为 true 时在后台运行而不阻塞)、asyncRewake(后台运行,并在退出码 2 时唤醒 Claude)、shell("bash" 或 "powershell")。

执行形式与 shell 形式:设置了 args 就是 exec 形式:Claude Code 把 command 解析为 PATH 上的可执行文件并以 args 为参数向量直接启动,没有 shell,每个 args 元素就是原样的一个参数,${CLAUDE_PROJECT_DIR} 这样的路径占位符会在每个 args 元素里被替换。省略 args 就是 shell 形式:command 字符串传给 shell(macOS 和 Linux 上是 sh -c,Windows 上是 Git Bash,没有 Git Bash 时是 PowerShell)。引用路径占位符时设置 args;需要 shell 特性(管道、重定向、变量展开)时省略。

Hook 输入与输出

命令 Hook 通过 stdin 接收 JSON 数据,通过退出码、stdout 和 stderr 通信。HTTP Hook 把同样的 JSON 作为 POST 请求体接收,通过 HTTP 响应体通信。在 macOS 和 Linux 上,命令 Hook 在自己的会话里运行,没有控制终端,Hook 进程无法打开 /dev/tty;想向用户展示一条消息,在 JSON 输出里返回 systemMessage。

通用输入字段

字段说明
session_id当前会话标识符
prompt_id标识当前正在处理的用户提示的 UUID,与 OpenTelemetry 事件上的 prompt.id 属性匹配
transcript_path对话 JSON 的路径(转录文件是异步写入的,Hook 触发时可能还没包含当前回合最新的消息)
cwdHook 被调用时的当前工作目录
permission_mode当前权限模式:"default"、"plan"、"acceptEdits"、"auto"、"dontAsk" 或 "bypassPermissions"。标为 Manual 的模式以 "default" 到达,从不是 "manual"
effort带 level 字段的对象,值是 Hook 运行时生效的努力等级
hook_event_name触发的事件名
agent_id、agent_type在 --agent 下运行或在子智能体内部时额外包含

比如 Bash 命令的 PreToolUse Hook 在 stdin 收到:

{
  "session_id": "abc123",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite",
    "timeout": 120000,
    "run_in_background": false
  },
  "tool_use_id": "toolu_01ABC123..."
}

对文件工具 Write、Edit 和 Read,tool_input.file_path 始终是绝对路径:Claude Code 在 Hook 运行前展开 ~ 和相对路径,所以按路径匹配的 Hook 无法通过 ~ 或同一路径的相对写法被绕过。在 Windows 上,路径以反斜杠分隔符到达,比较前要规范化分隔符(Bash 里 FILE_PATH="${FILE_PATH//\\//}",Python 里 file_path.replace("\\", "/"))。

退出码输出

  • 退出 0:成功,是打印 JSON 做结构化控制时的预期退出码。对多数事件,Claude Code 把 stdout 写入调试日志而不在转录里显示;例外是 UserPromptSubmit、UserPromptExpansion、SessionStart 和 PostModelSwitch,其纯文本 stdout 会被作为 Claude 能看到并据此行动的上下文加入。stdout 以 { 开头且以 } 结尾时按 JSON 解析,否则当作纯文本
  • 退出 2:阻塞错误。在能阻塞的事件上,退出 2 不管是否打印 JSON 都会阻塞,连 JSON 里 permissionDecision 为 "allow" 也无法覆盖它。阻塞消息是你 JSON 里阻塞决定的 reason,否则是你的 stderr 文本
  • 其他退出码:对多数 Hook 事件本身不阻塞。取决于 stdout:能通过校验的 JSON 对象,退出码被忽略,只由 JSON 决定结果;校验失败或解析失败是非阻塞错误;纯文本或空输出,动作继续,转录里显示 <hook name> hook error 通知

对多数 Hook 事件,只有退出码 2 能靠退出码本身阻塞。没有有效 JSON 时,Claude Code 把退出码 1 当作非阻塞错误并继续动作,尽管 1 是 Unix 的常规失败码。

超时:除了你用 async: true 运行的命令 Hook,Claude Code 会取消到达 timeout 的 command、http 或 mcp_tool Hook 并丢弃其输出,所以在多数事件上超时的 Hook 不产生决定。在 PreToolUse 上,超时的命令 Hook 不阻止工具调用,调用继续走正常的权限流程,所以不要指望一个卡住的 Hook 充当关卡。

各事件上退出码 2 的行为

Hook 事件能阻塞?退出 2 时发生什么
PreToolUse是阻止工具调用
PermissionRequest否不被采纳,权限流程原样继续;改用 decision 对象拒绝
UserPromptSubmit是阻止该提示,它永远到不了 Claude
UserPromptExpansion是阻止展开
Stop是阻止 Claude 停止,对话继续
SubagentStop是阻止子智能体停止
TeammateIdle是阻止队友进入空闲,让它继续工作
TaskCreated是回滚任务创建
TaskCompleted是阻止任务被标记为完成
ConfigChange是阻止配置变更生效(policy_settings 除外)
PostToolBatch是在下一次模型调用之前停止智能体循环
PreCompact是阻止压缩
PreModelSwitch是阻止模型切换并向用户显示 stderr
Elicitation / ElicitationResult是拒绝 elicitation / 阻止响应(动作变为拒绝)
WorktreeCreate / WorktreeRemove是任何非零退出码都会让 worktree 创建 / 移除失败
PostToolUse / PostToolUseFailure否向 Claude 显示 stderr;工具已经运行或已经失败
StopFailure、Notification、Setup、InstructionsLoaded、PermissionDenied否输出和退出码被忽略
SessionStart、SubagentStart、SessionEnd、CwdChanged、FileChanged、PostCompact、PostModelSwitch否只向用户显示 stderr

JSON 输出

退出码只能让你阻止或保持沉默,JSON 输出给你更细的控制:退出 0 并向 stdout 打印 JSON 对象。每个 Hook 选一种方式:只用退出码,或退出 0 并打印 JSON。如果混用,退出 2 保持其阻塞效果,Claude Code 仍读取 JSON 字段。你的 Hook 的 stdout 必须只包含 JSON 对象,shell 配置文件在启动时打印文字会干扰 JSON 解析。Hook 的 additionalContext、systemMessage、initialUserMessage 字符串及其纯 stdout 上限为 10,000 个字符,超出的内容会被保存到会话目录的文件里,并被替换为文件路径和最多前 2000 个字符的预览。

JSON 对象支持三类字段:

  • 通用字段(每个事件都接受):
字段默认值说明
continuetrue为 false 时,Hook 运行后 Claude 完全停止处理,优先于任何事件特定的决策字段
stopReason无continue 为 false 时向用户显示的消息
systemMessage无向用户显示的警告消息
terminalSequence无让 Claude Code 代你发出的终端转义序列,如桌面通知、窗口标题或响铃;限于 OSC 0/1/2/9/99/777 和 BEL,其他序列会被拒绝
  • 顶层 decision 和 reason:一些事件用来阻止或给出反馈
  • hookSpecificOutput:给需要更丰富控制的事件用的嵌套对象,需要一个设为事件名的 hookEventName 字段

要完全停止 Claude:

{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

发出终端通知:Hook 运行时没有控制终端,直接把转义序列写到 /dev/tty 会失败。改为在 terminalSequence 字段里返回转义序列,Claude Code 通过它自己的终端写入路径替你发出,这对 Notification 和 StopFailure 这类丢弃 systemMessage 和 continue 的事件也有效。

Hook 事件

事件按生命周期顺序分成四页,每页写明触发时机、matcher、输入字段和如何用输出控制行为:

  • 会话与提示事件:SessionStart、Setup、InstructionsLoaded、UserPromptSubmit、UserPromptExpansion、MessageDisplay
  • 工具事件:PreToolUse(含各工具的 tool_input)、PermissionRequest、PostToolUse、PostToolUseFailure、PostToolBatch、PermissionDenied
  • 智能体、任务与通知事件:Notification、SubagentStart、SubagentStop、TaskCreated、TaskCompleted、Stop、StopFailure、TeammateIdle
  • 环境、压缩与模型事件:ConfigChange、CwdChanged、DirectoryAdded、FileChanged、WorktreeCreate/WorktreeRemove、PreCompact/PostCompact、PreModelSwitch/PostModelSwitch、SessionEnd、Elicitation/ElicitationResult

在后台运行 Hook

默认情况下,Hook 会阻塞 Claude 的执行直到完成。对部署、测试套件或外部 API 调用这类长时间运行的任务,设置 "async": true 让 Hook 在后台运行,同时 Claude 继续工作。异步 Hook 不能阻塞或控制 Claude 的行为;这个字段只对 type: "command" 的 Hook 可用。下面的 Hook 在每次 Write 工具调用后运行一个测试脚本,Claude 立即继续工作,脚本完成时,它的输出在下一个对话回合送达:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

安全注意事项

命令 Hook 以你的完整用户权限执行 shell 命令,它们可以修改、删除或访问你的用户账号能访问的任何文件。添加到配置之前,请审查并测试所有 Hook 命令。

工作区信任:Claude Code 在运行任何设置文件里的 Hook 之前会检查工作区信任。交互式会话里,Claude Code 会保留每个设置文件(包括你自己的 ~/.claude/settings.json)里的 Hook,直到你接受该文件夹的工作区信任对话框;-p 或 SDK 会话从不显示对话框,并把文件夹视为受信任,所以提交在仓库 .claude/settings.json 里的 Hook 会在你从未信任过的文件夹里运行。在你对不是自己写的仓库编写 claude -p 脚本之前,审查它的 .claude/ 设置文件,以 --bare 开始,或用 --settings '{"disableAllHooks": true}' 为这次运行关闭 Hook。

安全最佳实践:

  • 验证和清理输入:永远不要盲目信任输入数据
  • 始终给 shell 变量加引号:用 "$VAR" 而不是 $VAR
  • 阻止路径遍历:检查文件路径里的 ..
  • 使用绝对路径:为脚本指定完整路径。在 exec 形式里用 ${CLAUDE_PROJECT_DIR},路径不需要加引号;在 shell 形式里用双引号包起来
  • 跳过敏感文件:避免 .env、.git/、密钥等

Windows 上的 PowerShell

在 Windows 上,可以给命令 Hook 设置 "shell": "powershell" 让单个 Hook 在 PowerShell 里运行。Claude Code 自动检测 pwsh.exe(PowerShell 7 及更新版本),并回退到 Windows PowerShell 5.1 的 powershell.exe。引用项目根时,在 PowerShell 的 shell 形式命令里写 ${CLAUDE_PROJECT_DIR} 或 $env:CLAUDE_PROJECT_DIR;不要在 PowerShell Hook 里写不带花括号的 $CLAUDE_PROJECT_DIR,PowerShell 会把它解析为未定义的局部变量,得到 $null。

调试 Hook

Hook 执行细节会被写入调试日志文件。用 claude --debug-file <path> 启动,把日志写到已知位置,或运行 claude --debug 并在 ~/.claude/debug/<session-id>.txt 读取日志。

本节页面