点击上方卡片关注我

设置星标 学习更多AI出海知识

上周在开发者圈子里刷到一个内容,一家做车辆租赁管理系统的公司 PocketOS,创始人让 Cursor 处理一个 staging 环境的凭证问题。

结果 AI 自作主张,找到一个不相关文件里的 API token,直接调了 Railway 的 volumeDelete 接口,9 秒,生产数据库连带所有备份全没了。

更魔幻的是,事后问 AI 为什么这么干,它写了一段"忏悔",一条一条列出自己违反了哪些安全规则,没有验证就猜测、没有被要求就执行了不可逆操作、没读文档就动手。

所有人都在问同一个问题:你为啥不给它装护栏?

护栏在哪?就在 Claude Code 的 Hooks 系统里。

我把官方文档、release notes、GitHub Issues 翻了一遍,越看越觉得,Hooks 是 Claude Code 里最被低估的功能

绝大多数人连 /hooks 这个命令都没打开过,但真正的重度用户早就在用它做护栏、做自动格式化、做上下文注入。

这篇就聊三件事:Hooks 到底是个啥机制、我现在自己在用的三个配方、踩过的那些坑。

先把最贵的一课讲清楚

Anthropic 官方给 Hooks 的定义是这样的:「user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific points in Claude Code's lifecycle」。

翻译一下,就是在 Claude Code 跑代码的各个关键节点,你可以插进去一段自己的脚本。这段脚本能做两件事:,或者

"拦"的意思是:LLM 想干这件事,但你的脚本说不行,它就真干不了。

"改"的意思是:LLM 干完之后,你的脚本顺手把活收个尾,比如格式化代码、写日志、推消息。

关键差别在确定性

以前我们靠 prompt 告诉 Claude「别删我 .env 文件」,它今天听话明天忘,这事没法赌。Hooks 不一样,Hooks 是代码层面的硬拦截,跟模型听不听话半毛钱关系都没有。只要 hook 脚本 exit 2,这个操作就是走不出去。

这就是为什么越重度的用户越离不开它。

Hooks 的生命周期有多长?

Hooks 事件目前有 28 个。我数了一下官方文档的表格,确实是 28 个,不是 10 个也不是 15 个。

按触发频率分三档:

每次 session 一次:SessionStart、SessionEnd。

每个回合一次:UserPromptSubmit、UserPromptExpansion、Stop、StopFailure。

每次工具调用都会触发:PreToolUse、PostToolUse、PostToolUseFailure、PostToolBatch、PermissionRequest、PermissionDenied。

然后还有一堆特别的:SubagentStart/Stop、TaskCreated/TaskCompleted、WorktreeCreate/Remove、PreCompact/PostCompact、FileChanged、CwdChanged、ConfigChange、InstructionsLoaded、Notification、TeammateIdle、Elicitation、ElicitationResult。

数字看着吓人,但其实 95% 的场景只需要盯住 5 个事件就够用:

事件

啥时候触发

能做啥

PreToolUse

工具要跑之前

拦命令、改参数

PostToolUse

工具跑完之后

格式化、日志、通知

SessionStart

会话开始/恢复

注入上下文、加环境变量

PreCompact

 / SessionStart(compact)

压缩上下文前后

救回要被压没的关键信息

Notification

Claude 需要你确认的时候

发桌面通知,让你别傻等

其他 23 个事件,用到再查文档。

配方一:给 rm -rf 装一道死闸

开头说的那个删库事件,说白了就是没装 PreToolUse,官方文档第一个示例就是干这个的,我直接抄过来用,一行没改:

在 ~/.claude/settings.json 加:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

然后在 .claude/hooks/block-rm.sh 里写:

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0
fi

有两个细节值得说一下:

一个是 if: "Bash(rm *)" 这里。这是 v2.1 加的新特性,matcher 匹配工具名,if 再匹配子命令,这样只有 rm 开头的命令才会触发脚本,普通 npm test 根本不会进来,省去每次都要 spawn 进程的开销。

另一个是 exit code 2 才是真正的拦截。很多人写 hook 习惯性 exit 1,或者 return false,那些都不会拦命令,官方文档写得很明确:exit 2 是唯一的"拒绝"信号,其他非零退出码只算"警告"。

还顺手扩展了同样的机制去拦 .envpackage-lock.json 和 .git/ 下的所有文件,官方叫这个 pattern「Block edits to protected files」,思路是一样的:PreToolUse 匹配 Edit|Write,脚本里检查 file_path。

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo"$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED=(".env""package-lock.json"".git/""prod.db")

for p in"${PROTECTED[@]}"; do
if [[ "$FILE_PATH" == *"$p"* ]]; then
    echo"Blocked: $FILE_PATH matches '$p'" >&2
    exit 2
fi
done
exit 0

装了这两段之后,用 auto mode 心里踏实多了。

配方二:编辑完自动跑 Prettier,再也不用自己格式化

这个是我心目中性价比最高的 hook,配一次,后面一直爽。

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

matcher 写成 Edit|Write,意思是只要 Claude 动了文件,prettier 立刻跟上跑一遍。

我自己用的是扩展版:TS/TSX 走 prettier,Python 走 ruff format,Go 走 gofmt。一段脚本判断扩展名分发就行:

#!/bin/bash
FILE=$(jq -r '.tool_input.file_path')
case "$FILE" in
  *.ts|*.tsx|*.js|*.jsx) npx prettier --write "$FILE" ;;
  *.py) ruff format "$FILE" ;;
  *.go) gofmt -w "$FILE" ;;
esac

效果就是:Claude Code 写出来的代码永远是格式化好的,代码 review 时不会再因为 tab 和空格吵架。

顺便说一句,v2.1.119 加了个实用的东西——PostToolUse 和 PostToolUseFailure 的输入里现在带 duration_ms,也就是工具执行时间,想做性能监控的话,比如看哪些工具调用特别慢,直接把这个字段 log 出来就行,不用自己计时。

配方三:上下文被压缩后,重新灌回去

这个是老用户才会懂的痛

长 session 跑到一半,上下文塞满了,Claude Code 会自动 compact,也就是把之前的对话压缩成摘要腾地方,压完之后的 Claude 经常"失忆":项目规范忘了、用哪个包管理器忘了、当前在搞哪个 sprint 也忘了。

解法是 SessionStart + compact matcher:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use pnpm, not npm. Run pnpm test before commit. Current sprint: billing refactor.'"
          }
        ]
      }
    ]
  }
}

hook 脚本 stdout 写出来的任何东西,都会被塞回 Claude 的上下文里。塞静态提醒行,塞动态的也行:

echo "# Recent commits:"
git log --oneline -10
echo ""
echo "# Current branch: $(git branch --show-current)"
echo ""
echo "# Open TODOs:"
rg "TODO" --type py -c | head -20

这段 hook 上了之后,session 跑到 6 小时以上,Claude 也不会突然忘了在搞什么。

顺便说一下,v2.1.119 还修了一个 bug:「skills invoked before auto-compaction being re-executed against the next user message」,意思是之前 auto-compact 会让 skill 被重复执行,现在修了,所以 skill + hook 的组合比之前稳很多。

进阶玩法:让 AI 评判 AI

v2.1 其实还开了两种新的 hook 类型:prompt-based 和 agent-based

以前 hook 只能是 shell 脚本,现在可以是一段 prompt,让另一个 LLM 来判断。举个例子,想做一个「代码提交前让另一个模型 review 一下」的 hook:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "prompt",
            "if": "Bash(git commit*)",
            "prompt": "Check if the pending changes include any hardcoded secrets, API keys, or passwords. If yes, respond with 'BLOCK: <reason>'. If clean, respond with 'ALLOW'."
          }
        ]
      }
    ]
  }
}

这就不是硬规则了,是 LLM 在判断,适合那种"说不清楚但大概能感觉出来"的场景,机密信息检测、commit message 写得够不够清楚、变量命名合不合理,都可以。

更进一步,hook 还能直接调 MCP tool:

{ "type": "mcp_tool", "tool": "linear:create_ticket" }

这个是 v2.1.118 加的(Hooks can now invoke MCP tools directly via type: "mcp_tool"),

意思是某个事件一触发,直接去调一个 MCP server 上的工具。举个例子,Stop 事件触发时,自动创建一条 Linear ticket 记录这次 session 完成了啥。

这种玩法还在摸索,能做的事实在太多了。

写在最后

说到底就一句话:AI 编程工具的护栏不是 prompt,是代码

你在 CLAUDE.md 里写一万遍「不要删我的生产库」,模型都可能在某个边缘情况下忘掉,但 hook 脚本一旦装上,它就永远杵在那里,不会累、不会忘、也不会因为 context 太长被压没。

Hooks 系统目前有 28 个事件、prompt-based / agent-based / MCP tool 三种新 hook 类型,最近几个版本还修了一堆边缘 bug,这大概是 Claude Code 里除了模型本身之外,迭代最快的子系统了。

如果你用 Claude Code 超过一个月,还没写过第一个 hook,现在就可以试试了。

延伸阅读:

  • Hooks 官方参考文档:https://code.claude.com/docs/en/hooks

  • Hooks 快速上手指南:https://code.claude.com/docs/en/hooks-guide

我们出海社区终于有自己的网站了!

欢迎关注,这个账号还会持续分享更多AI编程、出海工具、实战经验、踩坑记录。

想了解更多可以加我 vx: 257735 聊。

图片

出海赚钱案例:一个人做了个开源UI库,不融资不投广告,45天30万美元

出海建站必备:一小时搞定自建邮件,免费!

OpenClaw 真香!我让它每天帮我干这些活

出海赚钱案例:一个人用 PHP 做到月入 17 万美金,利润率 99%!

(2026年最新)Codex CLI 国内使用全攻略:终端 + VSCode + Cursor + Opencode 四种姿势全搞定

从海外公司注册到 Stripe 收款,跑通了出海收付款全流程(实操分享)

玩转 Claude Code Hooks:让自动化渗透到每个环节

更多推荐