Hook 不止能挡:把 agent 变成可编程流水线

· 约 4 分钟阅读

说到 hook,多数人第一反应是闸门:挡危险命令、挡敏感文件。这没错,但只用了 hook 一小半的能力。官方文档对它的定位是”确定性的、由代码驱动的自动化”:与提示词引导模型不同,hook 在生命周期特定点执行你的代码,结果有保证。[4] 提示词是概率,hook 是必然。既然每次必然执行,它能干的就不只是拦截,而是把 agent 变成一条可编程的流水线:事件是触发器,脚本是处理单元,stdin 进 JSON、stdout 出结果,整个会话就是一条由你定义的数据流。

先看清事件总线#

Claude Code 的 hook 事件有三十来个,贯穿整个会话:SessionStart 会话开始、UserPromptSubmit 收到提示词、PreToolUse 工具执行前、PostToolUse 工具成功、Stop 回复结束、Notification 需要人介入、SessionEnd 会话终止,还有 PreCompact/PostCompact 上下文压缩前后、ConfigChange 配置被改、FileChanged 监听的文件变动。[2] 每个事件都是一个挂载点,挂什么由你决定。handler 有五种:command 跑 shell 命令、http POST 给内部服务、mcp_tool 调 MCP 工具、prompt 让模型按指令判断、agent 直接派一个子代理去验证。[2] 前三种是代码,后两种把智能判断也接进了流水线。组合起来能拼出完整闭环:UserPromptSubmit 记录需求,PostToolUse 触发验证,Stop 检查收尾,SessionEnd 归档全程。[2]

四个值得抄的模式#

上下文注入。 会话长了会被压缩,压缩后模型容易忘事。官方给的解法很朴素:在 SessionStart 上挂一个 compact 匹配器,压缩结束后把关键约定重新塞回去,比如 echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'。[1] 一句话的成本,换来回想起关键约束的确定性。

环境同步。 会话里 cd 换目录后,环境变量可能过期。官方方案是把 direnv 接到 hook 上:SessionStart 和 CwdChanged 两个事件都跑 direnv export bash > "$CLAUDE_ENV_FILE",把最新的 shell 环境写进 Claude 读取的文件。[1] 目录一换,环境跟着换,不用每次手动 export。.envrc、.env 这类文件被外部改动时,还能用 FileChanged 事件配合 matcher 监听,文件一变就重新导出。[1]

通知。 长任务挂后台时,agent 卡在等你批准或回复,你不会知道。Notification 事件配合 matcher 能精确到场景:permission_prompt 表示等批准已约六秒、idle_prompt 表示你约一分钟没输入、agent_needs_input 表示后台会话在等你。[1] 挂一个 notify-send 或 osascript,人机协作的延迟立刻降下来。[1] 完整的通知类型清单在参考文档里能查到,auth_success、elicitation_dialog 这类边角场景也能覆盖。[2]

后台验证。 全量测试塞进每次编辑会拖慢节奏,官方给的答案是异步:PostToolUse 挂脚本,async: true 让它后台跑,结果通过 additionalContext 反馈给 Claude。[2] 脚本按文件类型过滤,只对源码文件触发 npm test,失败时把输出带回对话,模型能自己接着修。[2] 测试照跑,节奏不被打断。

配置本身还能被审计:ConfigChange 事件把每次设置变更按时间、来源、文件记进日志,谁改了配置一目了然。[1]

同一份配置,三个工具#

这套体系已经成了事实标准。VS Code 的 agent hooks 明确声明与 Claude Code、Copilot CLI 使用同一格式,保证兼容。[4] GitHub 的 Copilot 云 agent 和 CLI 也支持 hook,定义在仓库的 .github/hooks/*.json,个人级配置放 ~/.copilot/hooks/。[3] 也就是说,你在 Claude Code 里调通的 hook 知识,换到 VS Code 或 Copilot 时能平移大半,只是事件名大小写和工具名略有差异,VS Code 会解析 Copilot CLI 的配置并把驼峰事件名转成自己的格式。[4] 对团队来说,hook 文件提交到仓库默认分支后,云 agent 在每次会话里都会执行。[5] lint、格式、安全扫描这类质量门禁从此不用靠人自觉,平台在变,hook 的心智模型不变。

快速上手:先挂两道低风险钩子#

第一道,压缩后自动提醒,防止模型忘掉项目约定:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}

第二道,改完源码文件后台跑测试,失败时把结果喂回对话:

# .claude/hooks/run-tests-async.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi
RESULT=$(npm test 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
  MSG="Tests passed after editing $FILE_PATH"
else
  MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}

chmod +x 后先手动喂一次 stdin 验证脚本,再进会话用 /hooks 确认已加载。[1] 由于协议就是”stdin 进 JSON、退出码表态”,每段 hook 都能脱离会话单独测试:echo '{"tool_input":{"file_path":"x.ts"}}' | ./run-tests-async.sh 这样喂一次,看输出和退出码对不对,比在会话里反复试错快得多。[5]

分界线只有一条:凡是你愿意为它承担一次失误成本的规则,就该从 CLAUDE.md 搬进 hook;需要判断的事留在提示词里。hook 是唯一能把”每次都必须发生”变成事实的机制,先用好上下文注入和后台验证这两道,比急着堆拦截规则实在得多。规则从”挡住坏事”进化到”让好事必然发生”,才算把 agent 真正变成了流水线。

Sources#

[1] https://docs.anthropic.com/en/docs/claude-code/hooks-guide — Automate actions with hooks(Claude Code 官方指南) [2] https://docs.anthropic.com/en/docs/claude-code/hooks — Hooks reference(Claude Code 官方参考) [3] https://docs.github.com/en/copilot/concepts/agents/hooks — About hooks for GitHub Copilot(GitHub 官方文档) [4] https://code.visualstudio.com/docs/agent-customization/hooks — Agent hooks in Visual Studio Code(VS Code 官方文档) [5] https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks — Customize agent workflows with hooks(GitHub 官方教程)