RSS

第 8 章 / 共 10 章

钩子的完整生命周期:事件、决策与守门

约 6 分钟 更新于

8.1 一个回合里会触发哪些事件

钩子不止 PostToolUse 一个。从你按下回车到模型答完,一个回合会流经一连串生命周期事件,每个都能挂钩子:

  • UserPromptSubmit:你提交提示、模型开始处理之前。适合注入上下文或拦下不该发的请求。
  • PreToolUse:某个工具调用执行之前。可以拦截——守门的关键。
  • PostToolUse:工具成功之后。上一章的自动格式化就在这里。
  • Stop:模型答完这一轮。适合收尾检查。
  • SessionStart:会话开始或恢复时。适合加载环境信息。
第8章:一个回合的事件轴,以及 PreToolUse 的放行/拦截决策分支
图 8.1:把一个回合摊平成一条时间轴,你就知道该把纪律挂在哪一刻。注意 PreToolUse 这个岔口——它是回合里唯一能在动作发生前把它拦下来的位置,守门只能在这里做。

8.2 只掌握核心几个事件就够起步

诚实地说,钩子事件如今已经有三十多个——还有子代理、模型切换、上下文压缩、工作树等各种时机。但你不需要把它们当字典背下来。

起步阶段,上面那五个事件覆盖了绝大多数真实需求。等你遇到”想在压缩上下文前保存点东西""想在子代理结束时做检查”这类具体需求,再去查那个具体事件的字段即可。工具类内容变化快,写钩子前用 /hooks 或当前文档核对一遍事件名和字段,是个好习惯。

8.3 用 permissionDecision 拦下危险操作

现在做守门。我们要的第二条纪律:绝不允许 git push --force。这得挂在 PreToolUse 上,因为只有它能在命令真正执行前拦下来。

配置 .claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-push.sh"
          }
        ]
      }
    ]
  }
}

${CLAUDE_PROJECT_DIR} 会被替换成项目根目录,这样不管从哪个子目录启动,脚本路径都对得上。脚本 .claude/hooks/guard-push.sh

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

if echo "$COMMAND" | grep -qE 'git push .*(--force|-f)\b'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "禁止强制推送:请改用 --force-with-lease,或走 PR 流程。"
    }
  }'
else
  exit 0
fi

这段的现代写法值得记住:脚本正常退出(0),但往标准输出打印一段 JSON 来表达决策hookSpecificOutput 里的 permissionDecision 设为 deny 就拦下这次操作,permissionDecisionReason 会作为理由反馈给模型,让它知道为什么被拦、该怎么改。除了 deny,还可以是 allow(直接放行、跳过常规询问)。没匹配到危险模式时,脚本退出 0、不打印任何决策,操作就走正常权限流程。

这比单纯用”退出 2 + 标准错误”更精细:你能给出结构化的理由,甚至选择直接放行某些可信操作。

8.4 钩子会以你的权限跑任意代码

到这里必须停下来说一件严肃的事:钩子会在你的机器上,以你的权限,执行任意代码,而且不经过模型判断。这既是它的全部能力来源,也是它最大的风险。

含义有两层。第一,你自己写的钩子要小心——一条挂在 PreToolUse 上的错误命令,可能拦掉所有工具让会话瘫痪,或者反过来放行了本不该放行的东西。第二,也是更要警惕的:别人仓库里的钩子同样会以你的权限跑。当你 clone 一个陌生项目并在里面启动 Claude Code,它 .claude/settings.json 里的钩子、.claude/skills/ 里技能的 allowed-tools,都可能在你没细看的情况下生效。第 10 章会把这条信任边界讲透。

你现在的成果:三件产物齐了——一个技能、一个子代理、两个钩子。但它们现在散落在你个人目录和项目目录里,同事拿不到。最后两章,我们把它们收拢成一个能一键分发的插件,再把整套东西组合起来跑一遍。

广告位 · Multiplex 关联广告