你已经会为你的 agent 写一份好合同:你在 AGENTS.md 里告诉它你希望它怎么工作。问题是 LLM 读了那份合同后,每一轮都重新决定——所以那条关键规则几乎总被遵守,而那个\"几乎\"恰恰就是咬你一口的那次。hooks 是下一级台阶:你把规则从模型的脑子里拿出来,塞进基础设施里,好让它 100% 的时候都发生,不依赖它的判断。我给你讲你挂钩的那些生命周期事件、JSON 的解剖、每个 hook 住在哪里以及为什么重要,并留给你三个真实、拿来即贴的 hook(自动格式化、阻止危险命令、桌面通知),取自 Anthropic 的官方文档。一个主 prompt,把安全装好来搭起你的第一个——因为一个 hook 用你的权限跑 shell。这是从\"写合同\"到\"让合同自己执行\"的那一跃。
每个用 AI 的项目都会走到一个节点,你不再指望模型的一片好心。你在 AGENTS.md 里写了让它格式化代码、别碰敏感文件、跑测试。90% 的时候它都做到了。但剩下那 10% 恰恰就是咬你一口的那一次:它改了生产环境 .env 的那一次,它跳过格式化、把提交搞脏的那一次,它对着错误的文件夹执行 rm -rf 只因为"看起来像临时的"那一次。
hooks 的瞬间就是你想到:"要是这件事不取决于模型自己决定做对呢?要是它就是会发生、总是发生、不取决于它的判断呢?"。这正是 Claude Code 官方文档在 hooks 页面正中央摆出的承诺:它们给了对 agent 行为的 "确定性控制","保证某些动作总是发生,而不是取决于 LLM 是否选择执行它们"(忠实翻译自英文原文)。这句话就是整个资源的核心论点。
痛点不是一场末日,而是一种侵蚀。你让 AI 去做某件"总是"要做的事,可它有时不做,因为 LLM 不是确定性的:每一轮它都重新决定,而在一个塞满上下文的轮次里,你那句"记得格式化"的提醒可能被上千个别的 token 埋掉。不是 AI 不听话——而是你在向一个概率系统索要一致性。这就像求一个聪明但心不在焉的人永远别忘了关煤气阀:大多数晚上他都关了,而这恰恰是你放松警惕的原因。
.env、某个 package-lock.json、.git/ 里的某样东西。这里就是让这个资源进阶的角度:如果你已经读过关于为 agent 写一份好合同(AGENTS.md)的内容,那么 hooks 就是下一级台阶。你从"写合同"(声明式:告诉它你要什么)跨到"让合同自己执行"(确定性:机器替你落实它)。这是一部写在纸上的法律,和一个站在街角的警察之间的区别。
一个 hook 是 Claude Code 在其生命周期某个具体节点自动执行的一条 shell 命令。你在一个 settings.json 文件里声明它一次;从那以后,每当到达生命周期的那个节点,你的命令就会运行。它不是由模型凭判断触发的:它由事件触发。魔法就在这里——它是确定的,不容商量。
官方文档列出了许多生命周期事件——几乎 agent 工作的每个时刻都有一个。别去背数量或整个列表:理解这个思路,记住你 95% 的时候真正会用到的那几个。以下就是它们,连同官方文档里原样出现的真实名称:
rm -rf,或对受保护文件的编辑。compact matcher 的 SessionStart)— 围绕上下文压缩,好在记忆被压缩时不丢掉重要的东西。Pre。如果你想对已经发生的事做出反应(格式化、提醒、记录),挂到 Post。每个 hook 都住在你 settings.json 里的一个 "hooks" 块中。结构永远是同一个套娃:事件名 → 一个 matcher(应用到什么上)→ 一个 hooks 列表,里面是 type: "command" 和要运行的 command。看一遍你就再也忘不掉:
{
"hooks": {
"PostToolUse": [ // 1. 生命周期的 EVENTO(事件)
{
"matcher": "Edit|Write", // 2. 应用到 QUÉ(哪些工具)
"hooks": [
{
"type": "command", // 3. 这是一条 shell 命令
"command": "...tu comando..." // 4. 发生时 QUÉ(运行什么)
}
]
}
]
}
}PreToolUse、PostToolUse)它按工具名过滤:"Edit|Write" 意思是"只在用 Edit 或 Write 时","Bash" 只针对 shell 命令。一个空的 matcher("")总是触发,对一切生效。竖线 | 分隔备选项(在较新版本的 Claude Code 里逗号也可以:"Edit, Write" 是等价的)。你放 hook 的位置定义了它的作用范围。这很关键:一个项目级 hook 会跟你的团队共享(它被提交进仓库);一个全局的只属于你。官方表格总结了每样东西该放哪:
.claude/settings.json 并被提交。这样合同就不再住在某一个人的脑子里,而是跟着代码一起走。这才是真正的力量:纪律成了仓库的一部分,而不是某人得记住的便利贴。理论够了。这里有三个真实的 hook,取自官方文档,拿来即贴。从今天最让你痛的那个开始。
经典款。每当 Claude 编辑或写入一个文件,自动过一遍 Prettier。再也不会有格式不一致的提交了。放进你项目的 .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}jq -r '.tool_input.file_path' 从那个 JSON 里提取被编辑文件的路径,然后 xargs npx prettier --write 把它传给 Prettier。jq 是一个命令行 JSON 阅读器——在 macOS 上用 brew install jq 安装,在 Debian/Ubuntu 上用 apt-get install jq。这个能帮你摆脱恐惧。一个 PreToolUse hook,在每条 Bash 命令执行之前审查它,如果含有破坏性的东西就否决它。这里有一个文档讲得极清楚的技术细节很重要:通过以退出码 2 结束来阻止,而你写到 stderr 的内容会作为解释传给 Claude,让它自我纠正。先看脚本:
#!/bin/bash # .claude/hooks/block-rm-rf.sh INPUT=$(cat) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command') if echo "$COMMAND" | grep -q "rm -rf"; then echo "Bloqueado: 'rm -rf' no está permitido. Borra rutas concretas, no recursivo-forzado." >&2 exit 2 # exit 2 = bloquea la accion; stderr le llega a Claude como feedback fi exit 0 # exit 0 = sin objecion; sigue el flujo normal de permisos
让它可执行,并在你的 settings 里指向这个脚本来注册它:
chmod +x .claude/hooks/block-rm-rf.sh
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
}
]
}
]
}
}grep -q "rm -rf" 能抓住那个显而易见的情况,但它会漏掉 rm -fr、带奇怪空格的 rm -rf,或者 rm --recursive --force。它当一张看得见的网、当给 AI 的教育性反馈是有用的,但别把它当成盔甲。文档本身直言不讳:hooks 的过滤是尽力而为(best-effort)且"失败即放行"(如果它无法解析命令,就让它过),所以对于一条谁都不能绕过的硬禁令,用权限系统(permissions.deny),而不是一个 hook。正确的模式是:hook 用来反应和提醒;permission rules 用来做真正的锁。PreToolUse 会否决该工具,即使在 `bypassPermissions` 下或带着 `--dangerously-skip-permissions`。也就是说:哪怕你(或 AI)已经把所有权限护栏都放下了,你的阻止 hook 依然屹立。这条规则是不对称的,而且是故意这么设计的:hooks 可以收紧策略,永远无法把它放松到超出权限规则允许的程度。一个阻止 hook 是一条连你自己都不会意外绕过的规则。这样你就不用一直盯着终端了。当 Claude 完成或需要你的权限时,弹出一个桌面通知,你就可以去做别的事。放进你全局的 ~/.claude/settings.json(macOS 用 osascript 的示例):
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code necesita tu atención\" with title \"Claude Code\"'"
}
]
}
]
}
}notify-send 'Claude Code' 'Claude Code necesita tu atención';在 Windows(PowerShell) 上用 System.Windows.Forms 的一个 MessageBox。JSON 的结构完全一样——只有 command 那一行变。指南本身记录的一个 macOS 小技巧:如果通知没出现,先跑一次 osascript -e 'display notification "test"',然后在"系统设置 → 通知"里给 Script Editor 授予权限。hooks 不是万能的。心里的规则很简单:如果某件事必须总是发生、机械地、无需判断,那就是一个 hook。如果某件事需要逐案的判断,别用一个确定性的 hook 去硬来——让模型来决定(或用文档同样支持的 prompt/agent 类型 hook,不过那是另一个层级的东西)。
.env、递归删除、写入 .git/):那是一个阻止型 PreToolUse——由一条权限规则做硬锁托底。PostToolUse。PostToolUse:它无法撤销已经发生的事——它只反应。要阻止,永远用 PreToolUse。往一个 hook 里塞太多复杂逻辑会让它变脆;让它们保持简短、单一目的。这里有个实用细节:文档本身建议你把 hook 描述给 Claude,让它替你写出来。但要写得好——而且安全——最好给它一个结构化的委托,而不是一句"给我弄个 hook"。这就是本资源的那个主 prompt:唯一一个、强大的、把安全刹车都装好的、来设计你第一个 hook 的 prompt。
我想搭起我第一个 Claude Code hook,来自动化我工作流里的一条规则。我想要永远保证的那条规则是:[DESCRIBE LA REGLA,例如:"用 prettier 格式化你编辑的每个文件" / "阻止任何以递归方式删除文件的命令" / "禁止编辑 .env 或 .git/ 里的任何东西" / "完成时用通知提醒我"]。 在写任何东西之前,用清晰的语言(我不会编程)回答这些,帮我把它设计好: 1. 这条规则对应生命周期里正确的 EVENTO(事件)。如果规则是要 IMPEDIR(阻止)某事,它必须是 PreToolUse(发生在之前且能阻止)。如果是对已经做完的事做出 REACCIONAR(反应)(格式化、提醒、记录),它必须是 PostToolUse 或 Notification。告诉我你选哪个以及为什么。 2. 精确的 MATCHER(应用到哪些工具:Edit|Write、Bash,或空表示全部)以及为什么是这个而不是别的。让它尽可能地窄:一个过宽的 matcher 会在不该触发的地方触发 hook。 3. settings.json 该住的 LUGAR(位置): - 如果这条规则必须由这个仓库里整个团队遵守 → .claude/settings.json(会被提交)。 - 如果只针对我的机器和我所有的项目 → ~/.claude/settings.json。 - 如果是本地且私有的 → .claude/settings.local.json。 告诉我哪个以及为什么。 4. 完整的 JSON 块,拿来即贴,hook 已经装好。如果它需要一个单独的脚本(带 PreToolUse 的阻止里很典型),也给我完整的 .sh 脚本,并提醒我用 chmod +x 让它可执行。 5. 如果它是一个阻止型 hook:用 exit 2 加一条清晰的 stderr 消息,向我解释为什么被阻止,好让你自己(Claude)收到理由并自我纠正。别让它静默。而且要 AVÍSAME(提醒我)这条规则是否还值得配一条权限规则(permissions.deny)当硬锁,因为一个 hook 里的 grep 是尽力而为、可以被绕过的。 6. 安全,必填:用一句话向我解释这个 hook 会跑 QUÉ(什么)shell 命令,并向我确认它不做任何破坏性的事、也不把数据发到任何地方。提醒我一个 hook 是用我的权限跑 shell 的,我只该保存我理解的命令。 7. 我怎么测试它:告诉我怎么验证 hook 已注册(Claude Code 里的 /hooks 命令),以及一种在不破坏任何真实东西的前提下测试它确实会触发的方法。 先只给我看 SOLO(仅)设计(事件、matcher、位置),然后是 JSON,如果需要再给脚本,最后是安全解释和测试。别现在就改我的 settings.json:我想在自己贴之前先审查这个块。
注意这个模式:先设计,再审查安全,然后由你来贴块。你绝不让一个 hook 在你还没理解它之前就出现在你的配置里。这跟保护任何强大自动化的两步纪律是一样的:机器提议,你批准。
为了别把你绕晕,这就是你可以通过聊天委托给 AI 的,以及依然归你自己管的:
.sh 脚本,带上它的 jq、它的 exit 2 和它给 stderr 的消息。settings.json 里。chmod +x,缺 jq 的话 brew install jq。settings.json——在读过并理解之后。安全网不能是你没审查过的代码。PreToolUse 用于阻止,PostToolUse/Notification 用于反应。PreToolUse。这是一个 hook 的同一种哲学,只不过应用到了一整个团队而不是一个终端上:让质量不是某人记得的一个承诺,而是传送带上一个绕不过去的传感器。你把规则放一次;系统永远替你落实它。Join 4,200+ builders. No credit card. Build your first app with AI in minutes.