第 09 章

钩子 Hooks

在工具调用前后插入自动检查,让流程自己守门

把"每次都要记得做的事"交给钩子,它永远不忘

AI 能连外部系统了。这一章给它装上『自动守门』 —— 在工具调用前后自动执行检查,把团队规范沉淀为系统行为。

总在补救

很多问题是"如果当时检查一下就好了"

提交了密钥

AI 帮你改了配置文件,顺手把 API Key 写进去了,你点了确认,提交了——事后才发现泄露。

改完忘了跑测试

AI 改完代码你以为没事了,结果破坏了别处。如果每次改完自动跑测试,当时就能发现。

不规范代码进库

AI 写的代码没过 lint,混进了主分支。写入前自动 lint 就能拦住。

钩子(Hook)就是为这种"自动守门"设计的:在工具调用前后的特定时机,自动执行你预先写好的脚本——检查、拦截、提醒、补救,无需你每次记得。

什么是 Hook

定义、白话、和普通脚本的区别

Hook 是 Qoder CLI 在特定事件发生时自动触发的 shell 命令。你预先配置好"当 X 事件发生时,执行 Y 脚本",之后每次 X 发生,Y 自动跑。

说白了,Hook 就像家里的烟雾报警器——它平时不动,但一旦检测到烟雾(事件),就自动响(执行脚本)。你不用每次做饭都盯着,它替你守着。

普通脚本

  • 你主动执行
  • 执行与否靠你记得
  • 和 AI 工作流无关

Hook 脚本

  • 事件触发自动执行
  • 永远不漏,永远不忘
  • 嵌入 AI 的工具调用流程

四类事件

Hook 能挂载到哪些时机

类别典型事件用途
工具事件 PreToolUse(工具调用前)、PostToolUse(工具调用后) 拦截/检查文件操作、命令执行
会话事件 SessionStart(会话开始)、SessionEnd(会话结束) 初始化环境、清理、通知
任务事件 任务开始、任务完成 进度通知、状态记录
生命周期 停止、中断等 收尾、保存状态
最常用的是工具事件——PreToolUse 在 AI 真正执行操作之前拦截,PostToolUse 在之后补救。本章重点讲这两个。

三种策略

Hook 脚本的退出码,决定接下来发生什么

Hook 脚本通过退出码(exit code)告诉 CLI 检查结果。三个码对应三种行为:

退出码含义CLI 行为
0 通过 检查通过,继续执行原操作
1 提醒 给 AI/用户一个提示信息,但仍继续
2 阻断 阻止原操作,把错误反馈给 AI
这是 Hook 最强大的能力:退出码 2 能阻止 AI 的危险操作。比如 PreToolUse 钩子检测到文件含密钥,返回 2,AI 的写入就被拦下——比事后补救强得多。

环境变量

Hook 脚本怎么知道"当前在操作什么"

CLI 触发 Hook 时,会通过环境变量把当前操作的上下文传给脚本。常用变量:

变量含义
$CLAUDE_TOOL_NAME当前调用的工具名(如 Write、Bash)
$CLAUDE_FILE_PATH操作的文件路径
其他上下文命令内容、会话信息等
#!/bin/bash
# 一个 PreToolUse 钩子示例:写入前检查是否含密钥
if grep -E "(api_key|secret|password)" "$CLAUDE_FILE_PATH"; then
  echo "检测到疑似密钥,已阻止写入"
  exit 2    # 阻断
fi
exit 0      # 通过
脚本逻辑:读取环境变量拿到要写的文件 → 用 grep 扫敏感词 → 命中就 exit 2 阻断,否则 exit 0 放行。这就是一个完整的"密钥守门员"。

实战:防密钥

PreToolUse 钩子——AI 写文件前扫描敏感信息

场景:防止 AI 把 API Key、密码写进代码。

# scripts/security-scan.sh
#!/bin/bash
# 检查即将写入的文件是否含敏感信息

PATTERNS="(api_key|apikey|secret|password|token)\s*[:=]"
if grep -iE "$PATTERNS" "$CLAUDE_FILE_PATH" 2>/dev/null; then
  echo "⚠️ 检测到疑似敏感信息,写入已阻止"
  echo "请确认是否真的需要写入这些内容"
  exit 2
fi
exit 0

把这个脚本注册为 PreToolUse 钩子后,每次 AI 要写文件,都会先跑这个检查。命中敏感模式就阻断,AI 收到反馈会自己调整。

模式要按项目调整:上面的正则可能误报(如变量名含 token)。根据项目实际情况细化模式,或维护一份白名单。钩子太严会让 AI 寸步难行。

实战:跑测试

PostToolUse 钩子——AI 改完代码自动跑测试

场景:AI 每次修改源码后,自动跑相关测试,立即发现破坏。

# scripts/auto-test.sh
#!/bin/bash
# 只在修改的是源码文件时触发
case "$CLAUDE_FILE_PATH" in
  *.ts|*.tsx|*.js|*.jsx)
    echo "检测到代码变更,运行测试..."
    if npm test 2>&1; then
      echo "✅ 测试通过"
      exit 0
    else
      echo "❌ 测试失败,请修复"
      exit 2     # 反馈给 AI,让它修
    fi
    ;;
  *)
    exit 0       # 非代码文件,跳过
    ;;
esac
这个钩子的妙处:测试失败时 exit 2,错误信息会反馈给 AI,AI 会主动去修——形成"改代码→跑测试→失败就修"的闭环,无需你盯着。

实战:会话通知

SessionStart 钩子——会话开始时自动初始化

场景:每次开会话,自动拉取最新代码、提醒当前分支、检查环境。

# scripts/session-init.sh
#!/bin/bash
# 会话开始时的环境检查
echo "=== 会话环境 ==="
echo "当前分支: $(git branch --show-current 2>/dev/null)"
echo "未提交改动: $(git status --short | wc -l) 个文件"
echo "Node: $(node --version 2>/dev/null || echo '未安装')"
echo "================"
exit 0

更多 SessionStart 用途

  • 自动 git pull 拉最新
  • 检查依赖是否安装
  • 加载项目所需环境变量
  • 播放提示音(macOS: say "ready")

任务完成通知

长任务(如 Quest)跑完时,用任务事件钩子发通知:

# macOS 语音提醒
say "任务完成了"

# 或发桌面通知
osascript -e 'display notification \
  "Qoder 任务完成" with title "Qoder"'

配置方式

Hook 怎么注册到 CLI

Hook 配置写在 settings 文件中,关联"事件 → 脚本":

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "bash scripts/security-scan.sh"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "bash scripts/auto-test.sh"
      }
    ],
    "SessionStart": [
      { "command": "bash scripts/session-init.sh" }
    ]
  }
}
matcher 字段:指定该钩子匹配哪些工具。如 "Write|Edit" 只在写文件时触发;"Bash" 只在执行命令时触发。省略 matcher 则对所有工具触发。
脚本要有执行权限:注册前确保 chmod +x scripts/*.sh,否则钩子会因权限报错。Windows 用户用 .bat 或 .ps1 脚本。

排错指南

Hook 相关的高频问题

钩子没触发

原因:配置路径不对、matcher 不匹配、脚本无执行权限。修复:确认 settings 路径;matcher 写对工具名;chmod +x 脚本。

钩子报错影响正常使用

原因:脚本本身有 bug,exit 2 误阻断。修复:先在终端单独跑脚本验证;调试期临时把 exit 2 改成 exit 1(只提醒不阻断)。

钩子太慢拖慢工作流

原因:脚本做太多事(如全量测试)。修复:用 matcher 缩小触发范围;只跑相关测试;耗时操作放 PostToolUse 不阻塞。

误报太多

原因:检查模式太宽。修复:细化正则;维护白名单;逐步收紧而非一步到位。

排错指南

Hook 配置中的高频问题

Hook 没有触发

原因:事件类型不匹配、matcher 写错、或脚本路径不对。修复:检查事件名拼写(PreToolUse 不是 PreTool);用通配符 * 做 matcher 测试;用绝对路径。

Hook 脚本执行失败

原因:脚本无执行权限、语法错误、或依赖缺失。修复:chmod +x 脚本;在终端手动运行验证;检查 shebang 行(#!/bin/bash)。

exit 2 但没有反馈给 AI

原因:stderr 输出未包含可读的错误信息。修复:在脚本中用 echo "错误原因" >&2 输出到 stderr;CLI 会把 stderr 作为拒绝原因反馈给 AI。

环境变量在 Hook 中读不到

原因:CLI 注入的变量名拼写错误,或 shell 环境不同。修复:确认变量名( 而非 );在脚本开头 env | grep CLAUDE 调试。

动手练习

给你的项目装上两个守门员

练习 1:防密钥钩子

  • 写一个 security-scan.sh,扫描敏感词
  • 注册为 PreToolUse 钩子,matcher 为 Write|Edit
  • 让 AI 试着写一个含 "password" 的文件,验证被阻断

练习 2:会话初始化钩子

  • 写一个 session-init.sh 显示分支和状态
  • 注册为 SessionStart 钩子
  • 新开会话,观察自动输出的环境信息

练习 3(进阶):自动测试

  • 写 auto-test.sh,改完代码自动跑测试
  • 测试失败时 exit 2 反馈给 AI
  • 观察 AI 是否主动修复

本章回顾

带走的三个核心认知

Hook 是自动守门员

事件触发自动执行脚本,永远不漏、永远不忘。

退出码决定行为

0 通过、1 提醒、2 阻断——exit 2 能拦下 AI 的危险操作。

环境变量传上下文

$CLAUDE_TOOL_NAME、$CLAUDE_FILE_PATH 让脚本知道当前操作。

核心结论:钩子让你把团队的检查规范"代码化"——不再依赖人的记忆和纪律,而是由系统强制执行。这是从"手动质量"到"自动化质量"的关键一步。

下一章,学习把这些零散的能力(技能、钩子、MCP)打包成可分发的插件。

延伸阅读

继续本课程