← 所有文章

Claude Code 钩子详解:包裹智能体的确定性层

来自指南: Claude Code Comprehensive Guide

什么是 Claude Code 钩子? 钩子是用户自定义的 shell 命令(也包括 HTTP 端点、MCP 工具和模型提示词),Claude Code 会在生命周期的固定节点自动执行它们:工具调用之前、编辑之后、会话开始时,以及 Claude 回答结束时。1 CLAUDE.md 给模型的是它多半会遵守的指令,而钩子无论模型是否配合都会执行。在任意会话中输入 /hooks,即可查看全部生命周期事件以及各自挂载了什么。

多数开发者用两层控制来运行 Claude Code:权限约束智能体被允许做什么,CLAUDE.md 描述它应该做什么。钩子是第三层,也是唯一能保证任何事情的一层。下文将依次讲解:心智模型、当前文档中的每一个生命周期事件、精确的输入输出契约、配置方式、五个可用模式,以及一套取舍框架。所有 API 细节都已对照 2026年8月8日的官方钩子参考文档与指南核实过。这套机制迭代很快,因此当本文与参考文档冲突时,以参考文档为准。(初次接触 Claude Code?不妨先看 5 分钟上手或 Claude Code 入门路径。)

TL;DR: 钩子从 stdin 接收 JSON,通过退出码或 stdout 上的 JSON 作答。exit 0 表示放行,exit 2 表示拦截(限于支持拦截的事件),而 exit 1 这个 Unix 里约定俗成的失败码什么都拦不住——这是钩子最大的一个坑。2 配置写在 settings.json 中 PreToolUse、Stop 这类事件名之下,再用 matcher 过滤。凡是必须发生的事情交给钩子,凡是模型知道即可的事情交给 CLAUDE.md。

心智模型:为非确定性内核套上保证

编码智能体是一个概率系统。让它每次编辑后都跑一遍 Prettier,它多半会照做。但当改动看起来无关紧要、上下文变得很长,或者你的措辞被理解成另一个意思时,它可能就跳过这一步。CLAUDE.md、技能和提示词全都只是建议:质量很高,通常被采纳,却永远得不到保证。

钩子是包裹在这个内核外面的确定性外壳。指南开篇给了一个单行定义——“钩子是用户自定义的 shell 命令”——并把要点讲得很直白:钩子提供的是“确定性控制:某些动作总会发生,而不是依赖 LLM 自己选择去执行”。3(指南这一行低估了当前的实际能力:参考文档中更完整的定义已经加入了 HTTP 端点和 LLM 提示词,处理器还可以是 MCP 工具,详见下文的配置一节。)格式化器在每次编辑时触发。命令守卫会评估每一次 Bash 调用。完成门禁会检查每一次收尾。

这种强制是真的,不是摆设:PreToolUse 钩子在任何权限模式检查之前触发,所以一个返回 permissionDecision: "deny" 的钩子,即便在 bypassPermissions 模式下或带着 --dangerously-skip-permissions 运行,也照样能拦住工具。反过来则不成立——返回 "allow" 的钩子无法放宽设置里的 deny 规则。钩子只能把策略收得比权限更紧,绝不能把它放松。4

生命周期:全部钩子事件

截至 2026年8月8日,参考文档记录了 31 个钩子事件。1 它们分属三种节奏:每会话一次(SessionStart、SessionEnd)、每轮对话一次(UserPromptSubmit、Stop、StopFailure),以及智能体循环内每次工具调用都触发(PreToolUse、PostToolUse)。其余的则在特定条件下触发——配置变更、上下文压缩、子智能体、MCP 交互等。

事件 触发时机 一个真实用途
SessionStart 会话开始或恢复时 把 git 分支和未关闭的 issue 注入为上下文
Setup --init-only,或 -p 模式下的 --init/--maintenance 在智能体运行前于 CI 中安装依赖
UserPromptSubmit 你提交提示词后、Claude 处理之前 追加当前日期;拒绝包含密钥的提示词
UserPromptExpansion 输入的命令展开成提示词时 审计或否决技能与命令的展开结果
PreToolUse 工具调用执行之前 拦截破坏性的 shell 命令
PermissionRequest 弹出权限对话框时 自动批准可信命令,免去逐次确认
PermissionDenied 自动模式分类器拒绝了某次工具调用 返回 retry: true,允许模型重试
PostToolUse 工具调用成功之后 自动格式化每个被编辑的文件
PostToolUseFailure 工具调用失败之后 记录失败命令以便排查
PostToolBatch 一批并行工具调用之后、下一次模型调用之前 为智能体循环设检查点或直接中止
Notification Claude Code 发送通知时 Claude 需要输入时弹出桌面提醒
MessageDisplay 助手消息正文显示期间 在屏幕上做脱敏(仅影响显示,转录记录不变)
SubagentStart 子智能体被创建时 按智能体类型注入专属上下文
SubagentStop 子智能体结束时 在结果返回前校验子智能体的输出
TaskCreated 通过 TaskCreate 创建任务时 强制执行任务命名或范围规则
TaskCompleted 任务被标记为完成时 在完成生效前核验验收标准
Stop Claude 回答结束时 完成门禁:测试不通过就不许收尾
StopFailure 本轮因 API 错误而结束 对 rate_limit 或 billing_error 告警(仅记录,输出会被忽略)
TeammateIdle 智能体团队中的队友即将进入空闲 用队列让队友持续有活可干
InstructionsLoaded CLAUDE.md 或 .claude/rules/*.md 文件被载入上下文时 记录哪些指令进入了本次会话
ConfigChange 会话中途配置文件发生变化 拦截未经许可的设置改动
CwdChanged 工作目录发生变化 重新加载 direnv 式的环境
DirectoryAdded 会话中途通过 /add-dir 或 SDK 的 register_repo_root 注册工作目录时(v2.1.219 及以上) 该仓库一加入就立刻载入它的上下文
FileChanged 被监视的文件在磁盘上发生变化 .env 变更时刷新环境变量
WorktreeCreate 通过 --worktree 或 isolation: "worktree" 创建 worktree 时 替换默认的 git worktree 准备流程
WorktreeRemove worktree 被移除时 在会话或子智能体退出时执行自定义清理
PreCompact 上下文压缩之前 保存那些丢不起的状态
PostCompact 压缩完成之后 重新注入关键上下文
Elicitation MCP 服务器请求用户输入时 在无头运行中自动填表
ElicitationResult 你回答了 MCP 的 elicitation 之后 在响应返回前校验或覆盖它
SessionEnd 会话终止时 归档日志、释放资源

其中绝大多数你都用不上。几乎每一套投入生产的配置都建立在五个事件之上:PreToolUse、PostToolUse、UserPromptSubmit、SessionStart 和 Stop。其余的存在,是为了你真正需要它们的那一天。

契约:输入 JSON,输出退出码或 JSON

命令型钩子从 stdin 接收 JSON,并通过退出码、stdout 和 stderr 作答。(HTTP 钩子以 POST 请求体接收同样的 JSON,并通过响应体作答。)2

每个事件都会送来一个公共信封——session_id、transcript_path、cwd 和 hook_event_name,多数事件还带 permission_mode——再加上各事件特有的字段。一个针对 Bash 命令的 PreToolUse 钩子收到的是:

{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" }
}

其他事件只是换掉后半段:UserPromptSubmit 带 prompt,SessionStart 带 source(startup/resume/clear/compact/fork——第五个值随 v2.1.214 的分叉会话一起到来,从旧的四值列表里抄来的 source 匹配钩子会悄无声息地漏掉分叉),Stop 带 stop_hook_active 和 last_assistant_message。在子智能体内部触发的钩子还会额外收到 agent_id 和 agent_type。2

退出码

结果只有三种:2

  • Exit 0 —— 成功。Claude Code 会解析 stdout 中的 JSON 输出字段。对多数事件而言,stdout 只进调试日志;但对 UserPromptSubmit、UserPromptExpansion 和 SessionStart,纯文本 stdout 会作为 Claude 可见的上下文加入。
  • Exit 2 —— 拦截性错误。stdout(连同其中的任何 JSON)被忽略,stderr 作为错误信息回传给 Claude。“拦截”具体意味着什么,取决于事件本身。
  • 其他任何退出码 —— 非拦截性错误。转录记录中会出现一条 <hook name> hook error 提示,执行继续。

最后这条值得加粗:exit 1 什么都拦不住。 文档对此有直接警告——尽管 1 是 Unix 中约定俗成的失败码,Claude Code 仍把 exit 1 当作非拦截性错误并继续往下走。执行策略的钩子必须用 exit 2。2

exit 2 在各事件上的效果如下:2

事件 exit 2 的效果
PreToolUse 拦截该次工具调用
PermissionRequest 拒绝该项权限
UserPromptSubmit 阻止处理并清除提示词
UserPromptExpansion 阻止本次展开
Stop / SubagentStop 阻止停止,对话继续
TeammateIdle 阻止队友进入空闲
TaskCreated / TaskCompleted 回滚创建 / 阻止完成
ConfigChange 拦截该次配置变更(policy_settings 除外)
PreCompact 阻止上下文压缩
PostToolBatch 在下一次模型调用前停住智能体循环
Elicitation / ElicitationResult 拒绝该次 elicitation / 把响应变成拒答
WorktreeCreate 任何非零退出码都会中止 worktree 的创建

其余事件都无法拦截。PostToolUse 和 PostToolUseFailure 会把 stderr 展示给 Claude(工具已经跑完了);SessionStart、Notification、SessionEnd、CwdChanged、FileChanged、PostCompact、SubagentStart 和 Setup 只把 stderr 展示给用户,而 DirectoryAdded 只把 stderr 送进调试日志;StopFailure、InstructionsLoaded、MessageDisplay 和 PermissionDenied 则完全忽略退出码——对 PermissionDenied 来说,唯一的抓手是 JSON 里的 retry: true。2

JSON 输出

如果你需要比“拦截或沉默”更细的控制,就以 exit 0 结束并向 stdout 打印一个 JSON 对象。先说一条铁律:要么退出码,要么 JSON,不能两者兼用——JSON 只在 exit 0 时被处理,exit 2 会把它丢弃。5

通用字段在所有事件上都生效:continue: false 会彻底停下 Claude(并向用户展示 stopReason),suppressOutput 把 stdout 从转录记录中隐藏,systemMessage 向用户显示一条警告,terminalSequence 则发出允许列表内的终端转义序列(桌面通知、窗口标题、响铃)。决策字段则因事件而异:5

事件 决策形态 关键字段
UserPromptSubmit、UserPromptExpansion、PostToolUse、PostToolUseFailure、PostToolBatch、Stop、SubagentStop、ConfigChange、PreCompact 顶层 decision decision: "block" 加 reason(展示给 Claude)。想放行就省略 decision
PreToolUse hookSpecificOutput permissionDecision:"allow" | "deny" | "ask" | "defer",另有 permissionDecisionReason,以及在执行前改写工具参数的 updatedInput
PermissionRequest hookSpecificOutput decision.behavior:"allow" | "deny",可选 decision.updatedInput
PermissionDenied hookSpecificOutput retry: true 告诉模型可以重试
PostToolUse hookSpecificOutput updatedToolOutput 替换工具的返回结果
Stop / SubagentStop hookSpecificOutput additionalContext:非错误性的反馈,让对话继续而不计为钩子错误
SessionStart、Setup、SubagentStart 仅上下文 additionalContext,以及 SessionStart 专有的 initialUserMessage、sessionTitle、watchPaths、reloadSkills。无法拦截
MessageDisplay hookSpecificOutput displayContent 只替换屏幕上的文字
Elicitation / ElicitationResult hookSpecificOutput action:"accept" | "decline" | "cancel",以及 content
TeammateIdle、TaskCreated、TaskCompleted 通用 continue continue: false 加 stopReason 会彻底终止队友或任务的流程(按事件的拦截仍是 exit 2)
WorktreeCreate 返回路径 命令型钩子把 worktree 路径打印到 stdout;HTTP 钩子返回 hookSpecificOutput.worktreePath;失败或缺少路径都会导致创建失败
WorktreeRemove、Notification、SessionEnd、PostCompact、InstructionsLoaded、StopFailure、CwdChanged、DirectoryAdded、FileChanged 无 只有副作用

有两个细节最容易让人栽跟头。其一,PreToolUse 是顶层 decision 模式的例外:它历史上确实用顶层的 decision/reason,但这两个字段在该事件上已废弃("approve"/"block" 分别对应 "allow"/"deny"),请改用 hookSpecificOutput.permissionDecision。5 其二,当多个 PreToolUse 钩子给出不同判断时,优先级是 deny > defer > ask > allow——最严格的答案胜出。即便如此,也别指望这套平局规则,最好让每个决策都只有一个负责的钩子。5

配置:settings.json、matcher 与作用域

钩子配置分三层嵌套:选一个事件,加一个matcher 组来过滤触发时机,再定义一个或多个要运行的钩子处理器。6

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "/path/to/lint-check.sh" }
        ]
      }
    ]
  }
}

把它放在哪里决定了作用域:~/.claude/settings.json 对你所有项目生效,.claude/settings.json 属于项目级且可以提交,.claude/settings.local.json 属于项目级但被 gitignore 排除;优先级沿用标准的设置层级——托管策略高于本地,本地高于项目,项目高于用户。9 钩子还可以随插件(hooks/hooks.json)以及技能或智能体的 frontmatter 一起分发,企业管理员也可以下发用户无法覆盖的托管钩子。6

matcher 按其字符构成来判定:"*"、"" 或省略 matcher 表示匹配一切;只包含字母、数字、_、-、空格、逗号和 | 的值是精确字符串或列表(Bash、Edit|Write);其他写法都会被当作不带锚点的 JavaScript 正则,因此 Edit.* 会同时匹配 Edit 和 NotebookEdit——只想指一个工具时,请用 ^Edit$ 加上锚点。matcher 区分大小写,而且每个事件匹配的是各自的字段:工具事件看工具名,SessionStart 看 source,SubagentStart 看智能体类型,Notification 看通知类型。6 若想对工具事件做更精细的过滤,处理器级别的 if 字段可以接受一条权限规则,例如 "Bash(git *)"——但它是尽力而为的(遇到无法解析的命令会放行),所以真正需要硬性保证时,请用权限规则而不是 if。6 还有一处语义变化值得知晓:自 v2.1.214 起,if 中的单段路径模式(例如 Edit(src/**))只匹配工作目录下顶层的 src;在该版本之前写下的 if 会悄悄不再匹配 packages/app/src/ 这类嵌套路径——想要过去那种任意深度的行为,请写成 Edit(**/src/**)。6

处理器共有五种类型:command(shell)、http(POST 端点)、mcp_tool、prompt(单轮模型评估)和 agent(一个拥有 Read/Grep/Glob 权限的子智能体,实验性)。默认超时:command/http/mcp_tool 为 600 秒(UserPromptSubmit 降为 30 秒,MessageDisplay 降为 10 秒),prompt 为 30 秒,agent 为 60 秒——可用 timeout 逐个覆盖。6 所有匹配上的钩子会并行运行,完全相同的处理器会被去重,而 $CLAUDE_PROJECT_DIR 让脚本指向你的项目根目录。

用 /hooks 来核验:这是一个只读浏览界面,会列出每个事件、其上配置的钩子,以及每条配置各自来自哪个设置文件。要改动就去编辑 JSON(或者让 Claude 代劳)。想临时全部关掉,设置 "disableAllHooks": true。6

五个模式

都做了通用化和最小化处理。钩子教程把其中几个做成了更完整的生产版本,面向 Apple 开发的钩子则把它们应用到 iOS 工具链上。

1. 编辑后自动格式化(PostToolUse)

直接取自官方指南——Claude 碰过的每个文件都会被格式化,没有例外:3

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
        ]
      }
    ]
  }
}

按你的技术栈把命令换成 ruff format、gofmt 或 swiftformat 即可。

2. 拦截危险命令(PreToolUse,exit 2)

#!/bin/bash
# .claude/hooks/guard-bash.sh — register on PreToolUse, matcher "Bash"
command=$(jq -r '.tool_input.command // empty')
case "$command" in
  *"rm -rf"* | *"git push --force"* | *"DROP TABLE"*)
    echo "Blocked: matches a destructive pattern. Propose a safer alternative." >&2
    exit 2 ;;
esac
exit 0

exit 2 会拦下这次调用,并把 stderr 回传给 Claude,于是它会调整思路,而不是盲目重试。用 JSON 实现同样的效果就是带上原因的 permissionDecision: "deny",而且这条路还有向 "ask"(升级给人来定)或 updatedInput(改写命令)演进的余地。5

3. 会话开始时注入上下文(SessionStart)

SessionStart 钩子的纯文本 stdout 会直接成为 Claude 可见的上下文——不需要 JSON:1

#!/bin/bash
# .claude/hooks/session-context.sh — register on SessionStart
echo "Current branch: $(git branch --show-current)"
echo "Recent commits:"
git log --oneline -5
echo "Uncommitted files: $(git status --porcelain | wc -l | tr -d ' ')"
exit 0

这一招用于动态状态。静态约定应当写进 CLAUDE.md,文档本身也建议:不需要脚本的上下文,就交给 CLAUDE.md。1

4. 挂在 Stop 上的完成门禁

Stop 在 Claude 回答结束时触发。拦住它,就能逼着智能体一直干到条件满足为止:

#!/bin/bash
# .claude/hooks/stop-gate.sh — register on Stop
input=$(cat)
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # already continuing because of this hook; don't loop forever
fi
if ! npm test --silent > /tmp/stop-gate.log 2>&1; then
  jq -n '{decision: "block", reason: "Tests are failing. Fix them before finishing. Log: /tmp/stop-gate.log"}'
fi
exit 0

stop_hook_active 这项检查很关键:Claude Code 默认把 Stop 钩子的连续拦截限制在 8 次(可用 CLAUDE_CODE_STOP_HOOK_BLOCK_CAP 调高),而一个从不检查自己是否已经触发过续跑的门禁,会一口气把额度烧光。7 想要更柔和的引导,就返回 hookSpecificOutput.additionalContext 而不是 decision: "block"——同样让对话继续,但性质是带标签的反馈,而非钩子错误。至于一次性的条件,内置的 /goal 命令本身就是一个零配置、会话级、基于提示词的 Stop 钩子。1

5. 分发器:一个入口,许多小钩子

注册十个钩子,意味着十条会在不同机器和项目间逐渐走样的 settings.json 配置。另一种做法是:每个事件只注册一个分发器,然后按约定路由。

#!/bin/bash
# .claude/hooks/dispatch.sh — register once per event you care about
input=$(cat)
event=$(echo "$input" | jq -r '.hook_event_name')
dir="$CLAUDE_PROJECT_DIR/.claude/hooks/$event"
[ -d "$dir" ] || exit 0
for hook in "$dir"/*.sh; do
  [ -x "$hook" ] || continue
  echo "$input" | "$hook" || exit $?
done
exit 0

现在,加一个守卫只需要在 .claude/hooks/PreToolUse/ 里放一个新文件并 chmod +x——settings.json 岿然不动,每个脚本都小到可以单独测试,而且第一个 exit 2 会照常向上传递。有一个注意事项:分发器把 Claude Code 本会并行执行的东西串行化了,而且它最适合退出码型的钩子——会输出 JSON 的钩子应当单独注册,因为 stdout 里必须恰好只有一个 JSON 对象。5

钩子、CLAUDE.md、技能与记忆的分工

四种机制,四种职责:

机制 职责 选择标准
钩子 强制 如果“跳过”必须是不可能的(格式化、安全、门禁),那就用钩子
CLAUDE.md 引导 如果是模型每次会话都该知道的约定(技术栈、风格、命令),那就写进 CLAUDE.md
技能 能力 如果是一套自带说明和脚本、在相关场景被调用的流程,那就是技能
记忆 回忆 如果是某次会话学到、往后会话还要用到的事实,那就是记忆

失败会朝两个方向发生。把约定写成钩子,换来的是一堆脆弱脚本,去强制那些一句话引导就能搞定的事。把策略写成 CLAUDE.md 里的散文,换来的是一个偏偏在关键那天向 main 强推的智能体。判断标准是:模型哪怕只忽略这一次,代价是什么?只是添堵,就归 CLAUDE.md;会变成事故,就归钩子。

钩子做不到的事

以下都是官方文档给出的诚实边界:7

  • 钩子不能调用工具或斜杠命令。 命令型钩子只会说三种话:stdout、stderr 和退出码。经由 additionalContext 返回的上下文会以纯文本注入。
  • PostToolUse 无法撤销。 工具已经跑过了。防患于未然要靠 PreToolUse。
  • Stop 在每次回答结束时都会触发,而不只是在“任务完成”时;用户手动中断时它不会触发(API 错误触发的是 StopFailure)。门禁逻辑必须容得下任务中途的停止。
  • PermissionRequest 在纯无头(-p)运行中不会触发。 但当 Agent SDK 的 canUseTool 回调提供了提示时,它在 -p 下确实会触发,后台子智能体的工具调用也会触发;其余自动化场景请用 PreToolUse。
  • PreToolUse 看不到用 @ 引用的文件。 提示词里通过 @ 拉进来的文件并不涉及工具调用;要挡住这条路径,请用 Read 的 deny 规则保护相应路径。1
  • 并行场景下的 updatedInput 在设计上就不可靠。 当多个 PreToolUse 钩子改写同一个工具的参数时,只有一次改写能活下来,而且你无法决定是哪一次。让每次改写只归属一个钩子。
  • 超时会取消钩子。 命令型钩子默认 600 秒(UserPromptSubmit 为 30 秒,MessageDisplay 为 10 秒);一个会超时的慢门禁,等于一个没跑过的门禁。
  • 输出上限为 10,000 个字符——超出的部分会写入文件,并以预览内容替代。
  • 钩子以你的完整用户权限运行。 参考文档自己的警告是:它们“可以修改、删除或访问你的用户账户能访问的任何文件。在把钩子命令加入配置之前,请审阅并测试它们”。8 给变量加引号,用绝对路径,避开敏感文件。
  • 一个坏掉的钩子会拖垮之后的每一次会话,直到你修好为止。可以用转录视图(Ctrl+O)、claude --debug-file /tmp/claude.log,或会话中途的 /debug 来排查;一个经典的坑是:shell 配置文件在启动时打印了内容,把钩子的 JSON 输出弄坏了。7

常见问题

什么是 Claude Code 钩子?

钩子是用户自定义的命令——shell 脚本、HTTP 端点、MCP 工具或模型提示词——由 Claude Code 在特定生命周期节点自动执行。3 它们从 stdin 接收事件 JSON,并用退出码或 JSON 作答:拦截一次工具调用、注入上下文、改写参数、让智能体继续干活。与 CLAUDE.md 里的指令不同,它们每次都会执行,与模型的行为无关。

PreToolUse 钩子和权限有什么区别?

权限规则是声明式的:由 Claude Code 自己评估的静态 allow/deny/ask 模式。PreToolUse 钩子是可编程的:由你的代码检视完整的工具输入并作出判断。钩子在权限模式检查之前触发,因此钩子给出的 "deny" 即便在 bypassPermissions 模式下也依然有效——但钩子给出的 "allow" 无法推翻设置中的 deny 规则。4 凡是一条模式就能表达的,交给权限规则;当判断需要逻辑、外部状态或改写输入时,再动用钩子。

无头(-p)模式下钩子能用吗?

能用,只有一处细微差别:PermissionRequest 钩子会跳过纯 -p 运行(那里没有东西提供权限提示),不过当 Agent SDK 的 canUseTool 回调提供了提示时它会触发,后台子智能体的工具调用也会触发。纯无头运行中的自动权限判断,应当放进 PreToolUse。7 无头模式还解锁了一个交互式会话会忽略的选项:permissionDecision: "defer",它会暂停一次工具调用,好让外层进程(一个 Agent SDK 应用、一个自定义界面)收集输入,稍后再恢复会话。5

为什么我的钩子跑了却什么都没拦住?

几乎总是违反了契约。exit 1 不拦截——只有 exit 2 才拦,而且仅限支持拦截的事件。2 JSON 决策只在 exit 0 时被解析——一个先打印 {"decision": "block"} 再以 exit 2 结束的脚本,它的 JSON 会被丢弃。另外 matcher 区分大小写——bash 永远匹配不上 Bash。先用 /hooks 确认注册无误,再把示例 JSON 通过管道喂给脚本并检查 echo $?。7

参考来源

已对照 2026年8月8日的官方文档核实。钩子 API 在 Claude Code v2.1.x 的各次发布中发生过实质变化(新事件、新字段、matcher 语义),因此请把与版本相关的细节视为“截至该日期”的情况。

本站相关内容:想看包含提示词钩子与智能体钩子在内的完整系统视角,请读 Claude Code 指南的钩子章节;想要五个附完整配置的生产级实现,请读钩子教程;想看落到 iOS 上的应用模式,请读面向 Apple 开发的钩子;如果你还没装 Claude Code,就从快速上手开始。


  1. Anthropic,“Hooks reference — Hook lifecycle and hook events”。code.claude.com/docs/en/hooks#hook-events ↩↩↩↩↩↩

  2. Anthropic,“Hooks reference — Hook input and output; exit code output; exit code 2 behavior per event”。code.claude.com/docs/en/hooks#exit-code-output ↩↩↩↩↩↩↩↩

  3. Anthropic,“Automate actions with hooks”。code.claude.com/docs/en/hooks-guide ↩↩↩

  4. Anthropic,“Hooks guide — Hooks and permission modes”。code.claude.com/docs/en/hooks-guide#hooks-and-permission-modes ↩↩

  5. Anthropic,“Hooks reference — JSON output and decision control”。code.claude.com/docs/en/hooks#json-output ↩↩↩↩↩↩↩

  6. Anthropic,“Hooks reference — Configuration: hook locations, matcher patterns, hook handler fields, the /hooks menu”。code.claude.com/docs/en/hooks#configuration ↩↩↩↩↩↩↩

  7. Anthropic,“Hooks guide — Limitations and troubleshooting”。code.claude.com/docs/en/hooks-guide#limitations-and-troubleshooting ↩↩↩↩↩

  8. Anthropic,“Hooks reference — Security considerations”。code.claude.com/docs/en/hooks#security-considerations ↩

  9. Anthropic,“Claude Code settings”。code.claude.com/docs/en/settings ↩

相关文章

Codex CLI 与 Claude Code 2026:架构、定价与中国访问

2026 年 Codex CLI 与 Claude Code 对比:内核沙箱、hook 治理、模型上下文、定价、中国云访问,以及各自的适用场景。

27 分钟阅读

Claude Code Hooks:我的95个钩子,每一个都有存在的理由

我为Claude Code构建了95个钩子。每一个的诞生,都源于某次出错的经历。本文讲述它们的起源故事,以及由此演化出的架构。

7 分钟阅读