开始之前
你需要有第 1 课装好的 Claude Code,以及一个能编辑的设置文件。第一个 hook 发桌面通知,所以适合有桌面的机器。第二个 hook 是调用 jq 的 Bash 脚本,Anthropic 的 hooks 指南给的装法是:macOS 用 brew install jq,Debian 和 Ubuntu 用 apt-get install jq。
写任何 hook 都要记住一条规则。Anthropic 的参考文档提醒了它们能碰到什么。
命令型 hook 会以你的完整用户权限执行 shell 命令。它们可以修改、删除或读取你的用户账号能访问的任何文件。把 hook 命令加进配置之前,请逐条审查并测试。
写出第一个 hook:Claude 需要你时发通知
Anthropic 自己的演示就从通知开始,这样 Claude 在等你时,你不必一直盯着终端。下面这段是 macOS 版本。它用 Notification 事件,这个事件在 Claude 等你输入或批准权限时触发。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}- 打开
~/.claude/settings.json,没有就新建,把上面这段加进去。Linux 上把command换成notify-send 'Claude Code' 'Claude Code needs your attention'。Windows 上,从 hooks 指南的 Windows 标签页复制 PowerShell 命令。 - 如果文件里已经有
hooks这个键,就把Notification加在现有事件名旁边。每个事件都是同一个hooks对象里的一个键,不要把整个对象换掉。 - 保存,然后在 Claude Code 提示符下输入
/hooks。你的 hook 会出现在列表里的Notification下面。 - 按
Esc回到提示符。按Shift+Tab直到状态栏显示⏸ manual mode on,让 Claude 做一件需要权限的事,然后切到别的窗口。应该会收到一条桌面通知。这里要用手动模式,因为自动模式下由分类器审查操作,不再问你(第 2 课讲过)。
事件、匹配器,以及 hook 放在哪里
按参考文档的说法,hook 有三层。先是事件,比如 PreToolUse 或 Stop。再是决定何时触发的匹配器组,比如“只对 Bash 工具”。最后是处理程序,也就是那条 shell 命令。你的第一个 hook 用的是空匹配器 "",对所有通知类型都触发。把它改成 permission_prompt,就只在 Claude 需要批准、并且提示已经等了大约六秒时才触发。
对工具类事件,匹配器就是工具名。Bash 匹配 Bash 工具,Edit|Write 匹配两个工具中的任意一个。指南说匹配器区分大小写,必须和工具名完全一致。
这段配置存在哪里,决定谁能用到这个 hook。
600 s
是 command、http 和 mcp_tool 类 hook 在大多数事件上的默认超时
来源:Claude Code docs: Hooks reference (2026年11月5日 查阅)
~/.claude/settings.json:你所有的项目,不和任何人共享。.claude/settings.json:单个项目,可以提交到仓库,团队也就有了。.claude/settings.local.json:单个项目,不提交。- 插件的
hooks/hooks.json,或者技能的 frontmatter:hook 跟着那个插件或技能走。
| 事件 | 何时触发 | 匹配器过滤什么 |
|---|---|---|
SessionStart | 会话开始或恢复时 | 启动方式,比如 startup 或 resume |
UserPromptSubmit | 提交提示词时,在 Claude 处理之前 | 不过滤,总会触发 |
PreToolUse | 工具调用执行之前,并且可以拦下这次调用 | 工具名 |
PostToolUse | 工具调用成功之后 | 工具名 |
Notification | Claude Code 发出通知时 | 通知类型 |
Stop | Claude 回答完毕时 | 不过滤,总会触发 |
PreCompact | 上下文压缩之前 | manual 或 auto |
SessionEnd | 会话结束时 | 结束的原因 |
会说“不”的 hook:保护文件不被修改
通知只负责报告,PreToolUse hook 却能拦下动作。下面这个脚本来自指南,它读出 Claude 将要修改的文件路径,拒绝 .env、package-lock.json 和 .git/ 下的任何文件。它写到 stderr 的消息会交还给 Claude,让 Claude 调整做法。
它用 jq 解析 JSON,用退出码 2 来拦截。把它存成 .claude/hooks/protect-files.sh。
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0- 在 macOS 和 Linux 上用
chmod +x .claude/hooks/protect-files.sh让脚本可执行。不可执行的 hook 脚本,Claude Code 运行不了。 - 在
.claude/settings.json里把它登记成PreToolUsehook,匹配器写Edit|Write。command是"$CLAUDE_PROJECT_DIR"/.claude/hooks/protect-files.sh,写在 JSON 字符串里时,引号要加转义。 - 让 Claude 往你的
.env文件里加一行注释。Claude Code 会在修改执行之前拦下它,并把Blocked:那条消息交给 Claude。
hook 怎么回答:退出码和输入
Claude Code 把事件以 JSON 的形式,从标准输入送给 hook。每个事件都带有 session_id 和 cwd 这样的字段,各类事件再加上自己的字段。针对 Bash 的 PreToolUse hook 能看到工具名和命令,就像下面的例子。这也是上面的脚本为什么去读修改操作的 .tool_input.file_path。
hook 用退出码回答。PreToolUse hook 退出码为 0 并不是批准,工具调用仍然要走正常的权限流程。如果 hook 要做更细的决定,退出码为 0 时,Claude Code 还会读取 stdout 里的一个 JSON 对象。每个 hook 选一种方式,要么退出码 2,要么 JSON,不要混用。
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}| 退出码 | Claude Code 的做法 | 你的消息去哪里 |
|---|---|---|
| 0 | 成功,动作继续执行 | 纯文本 stdout 只在 UserPromptSubmit、UserPromptExpansion、SessionStart 和 PostModelSwitch 上才会交给 Claude |
| 2 | 阻断性错误,在能拦截的事件上,动作会被停下 | 你的 stderr 成为理由,大多数事件上 Claude 会把它当作反馈收到 |
| 其他 | 非阻断性错误,动作继续执行 | 会出现一条 hook 错误提示,显示 stderr 的第一行,前面带 “Failed with non-blocking status code:” |
Claude Code 的作者怎么用格式化 hook 和通知
Boris Cherny 在 Anthropic 做出了 Claude Code。他 2026 年 1 月在 X 上的配置串里说,团队用一个 PostToolUse hook 来格式化 Claude 写的代码。Claude 写出的代码通常已经很规整,剩下的由 hook 补齐,免得 CI 检查到时才报错。下面“动手试试”里的自动格式化 hook 是同一个思路:PostToolUse、匹配器 Edit|Write,再加 Prettier。
那串帖子的第一条讲的是等待。他在编了号的终端标签页里同时跑五个 Claude 会话,靠系统通知知道哪个在等他输入。这一课的第一个 hook 在任何终端里都能给你这个提醒,把匹配器改成 permission_prompt,就只在需要批准时提醒。
Claude 写出的代码通常开箱就格式规整,剩下最后那 10% 由 hook 处理,免得以后在 CI 里出现格式错误。
X 官方嵌入卡片的截图,拍摄于 2026 年 11 月 5 日,时间为 UTC。点击截图可到 X 上打开原帖。
常见错误
- 匹配器对不上。匹配器区分大小写,比对的是工具名,不是你提示词里的字。检查拼写,再用
/hooks看这个 hook 是不是出现在你想要的事件下面。 - 把
hooks对象整个换掉,而不是往里添加。每个事件名都是同一个hooks对象里并列的键。 - 设置文件里的 JSON 不合法。结尾多余的逗号和注释都不允许,文件一坏,
/hooks里就什么都没有。改动通常会自动读取。过几秒还没出现,就重启会话。 - 脚本不可执行,或者找不到。给它加
chmod +x,路径用$CLAUDE_PROJECT_DIR来指。如果记录里写着jq: command not found,就装上jq。 - 指望成功的 hook 会说点什么。它什么都不显示,所以看它的效果,比如文件被重新格式化了,或者按
Ctrl+O打开记录视图。 - 指望
PostToolUse能撤销什么。工具已经运行完了。匹配器为Edit|Write的格式化 hook,也看不到被 Bash 命令改写的文件。
动手试试
- 把通知 hook 的匹配器改成
permission_prompt,再测一遍。然后改回"",对比两者各在什么时候触发。 - 在信任保护脚本之前,先手动测试:
echo '{"tool_input":{"file_path":".env"}}' | .claude/hooks/protect-files.sh,然后echo $?。你应该看到 Blocked 那一行,以及一个 2。 - 打开
/hooks,选列表末尾的 All events,浏览所有事件,包括还没有 hook 的。 - 把指南里的自动格式化 hook 加进
.claude/settings.json。它是匹配器为Edit|Write的PostToolUsehook,对改过的文件运行 Prettier,需要jq和 Prettier。让 Claude 往一个 JavaScript 文件里加几个单引号字符串,然后打开那个文件看看。
下一课:Agent Skills
Claude Code 这门课到这里就讲完了:安装、计划模式、斜杠命令和 hooks。下一门课 Agent Skills 会深入讲第 3 课里你写的那些命令背后的 SKILL.md 格式。
hooks 也能装进插件里一起带走,Mindket 的插件指南讲了做法。项目的常驻说明,仍然应该放进 CLAUDE.md 文件,因为 hook 负责强制执行,CLAUDE.md 负责解释。
自测一下
PreToolUse钩子的脚本要拦住一次修改,应该用哪个退出码?查看答案
C. 退出码 2 退出码 2 会拦下这次工具调用,并把你写到 stderr 的信息交给 Claude,而退出码 1 只是非阻塞错误,操作照常进行。
想让一个钩子只对当前项目生效,并且你提交之后团队也能拿到,该把它保存在哪里?
查看答案
A.
.claude/settings.json这个文件只属于一个项目,可以提交,而~/.claude/settings.json管你所有的项目,也不会共享。哪个 matcher 能让
PreToolUse钩子对 Edit 工具或 Write 工具都生效?查看答案
D.
Edit|WriteEdit|Write能匹配两个工具中的任意一个,而且 matcher 区分大小写,必须和工具名完全一致。设置文件里已经有
hooks键,你想再加一个事件,该怎么做?查看答案
B. 在同一个
hooks对象里,把新事件名加在原有事件旁边 每个事件名都是同一个hooks对象里的并列键,所以只能往里添加,不能整个替换。
常见问题
Claude Code hooks 是拿来做什么的?
它们在会话的某些节点运行 shell 命令,比如编辑之后格式化文件,命令执行之前拦截它,Claude 需要输入时发通知,或者在会话开始时补充上下文。Anthropic 说它的好处是确定性的控制:有些动作一定会发生,不用指望模型自己选择去运行。
怎么查看当前有哪些 hook 在生效?
输入 /hooks。它会打开一个只读的浏览器,列出你配置的 hook,每个都标明来源,比如用户设置、项目设置或插件。选中某个 hook,就能看到完整命令和定义它的文件。
hook 能拦住 Claude 不让它做某件事吗?
能。PreToolUse hook 以退出码 2 结束,或者返回拒绝决定,就会取消这次工具调用。这个拦截在所有权限模式下都有效,包括 bypassPermissions,因为 PreToolUse hook 在权限模式检查之前就触发了。
怎么关掉 hooks?
在设置文件里设 "disableAllHooks": true,或者只想关这一次,就传 --settings '{"disableAllHooks": true}'。没办法只停用某一个 hook 而把它留在配置里。托管设置里的 hook 照常运行,除非在托管层面也开了这个设置。
网上抄来的 hook 安全吗?
读过之后才算。命令不管做什么,都是以你的身份、对你的账号够得着的任何文件去做。在交互式会话里,Claude Code 还会扣住所有设置文件里的 hook,直到你接受该文件夹的工作区信任对话框。
改了 hook 之后要重启 Claude Code 吗?
通常不用。Claude Code 运行时你编辑设置文件,文件监视器一般会自动读到 hook 的改动。如果几秒后 /hooks 里还是没有你的改动,就重启会话,强制重新加载。
技能或插件能带自己的 hook 吗?
能。插件把 hook 放在 hooks/hooks.json 里,技能可以在 frontmatter 里声明 hook,技能被调用之后,这些 hook 在会话剩下的时间里一直运行。子智能体的 frontmatter 也可以,那些 hook 在这个子智能体运行期间生效。
资料来源
- Claude Code docs: Automate actions with hooks https://code.claude.com/docs/en/hooks-guide
- Claude Code docs: Hooks reference https://code.claude.com/docs/en/hooks
- Boris Cherny on X: tip 9, a PostToolUse format hook, 2 January 2026 https://x.com/bcherny/status/2007179852047335529
- Boris Cherny on X: tip 1, five sessions and notifications, 2 January 2026 https://x.com/bcherny/status/2007179833990885678


