什么是 Hook
Hook 是在 Claude Code 生命周期特定点自动执行的脚本。和 CLAUDE.md(“建议性”指令)不同,Hook 是确定性的——它保证每次都执行,不依赖 LLM 的判断。
类比:Hook 就像 Express 的 Middleware 或 Git Hook。Express 的 app.use() 在请求处理的特定点注入逻辑;Git 的 pre-commit 在提交前自动执行脚本。Claude Code 的 Hook 有 SessionStart、PreToolUse、PostToolUse、Stop。你在这些节点注入自定义行为,而不需要修改主流程。
关键区别:
- CLAUDE.md:Claude 会”尽量”遵守(像代码注释)
- Hook:必定执行(像编译器插件)
Hook 三层配置
{
"hooks": {
"PreToolUse": [ // 1. 事件名
{
"matcher": "Bash", // 2. 匹配器(过滤条件)
"hooks": [
{
"type": "command", // 3. 处理器(执行什么)
"command": ".claude/hooks/check.sh"
}
]
}
]
}
}
核心事件一览
会话级别
| 事件 | 时机 | 能做什么 |
|---|---|---|
SessionStart | 会话开始/恢复 | 加载环境变量、初始化 |
SessionEnd | 会话结束 | 清理资源 |
工具级别(最常用)
| 事件 | 时机 | 能做什么 |
|---|---|---|
PreToolUse | 工具执行前 | 拦截、修改、放行 |
PostToolUse | 工具成功后 | 验证结果、追加信息 |
PostToolUseFailure | 工具失败后 | 错误处理 |
PermissionRequest | 权限弹窗时 | 自动允许/拒绝 |
代理级别
| 事件 | 时机 | 能做什么 |
|---|---|---|
SubagentStart | SubAgent 开始 | 初始化环境 |
SubagentStop | SubAgent 结束 | 清理 |
Stop | Claude 完成回复 | 阻止结束、追加任务 |
其他
| 事件 | 时机 |
|---|---|
UserPromptSubmit | 用户 prompt 提交前 |
FileChanged | 监控的文件变更 |
CwdChanged | 工作目录变更 |
PreCompact / PostCompact | 上下文压缩前后 |
四种处理器类型
| 类型 | 说明 | 适用 |
|---|---|---|
command | 执行 shell 命令 | 最常用,脚本验证 |
http | 发 POST 请求 | 集成外部服务 |
prompt | 单轮 LLM 评估 | 需要语义判断的验证 |
agent | 启动 SubAgent 验证 | 复杂验证逻辑 |
退出码的含义
| 退出码 | 效果 |
|---|---|
| 0 | 成功。如果有 JSON 输出则处理 |
| 2 | 阻断。操作被拒绝,stderr 显示给用户 |
| 其他 | 非阻断错误。仅在 verbose 模式显示 |
这是 Hook 最强大的能力:PreToolUse + 退出码 2 = 安全门控。
实际应用示例
阻止危险命令
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)
if echo "$COMMAND" | grep -q 'rm -rf'; then
echo "Blocked dangerous command" >&2
exit 2
fi
exit 0
编辑后自动格式化
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "npx prettier --write \"$TOOL_INPUT_FILE\""
}]
}
]
}
}
桌面通知(Claude 完成任务时)
{
"hooks": {
"Notification": [
{
"matcher": "idle_prompt",
"hooks": [{
"type": "command",
"command": "osascript -e 'display notification \"Claude 完成了\" with title \"Claude Code\"'"
}]
}
]
}
}
配置位置
| 位置 | 作用域 |
|---|---|
~/.claude/settings.json | 所有项目 |
.claude/settings.json | 当前项目(可分享) |
.claude/settings.local.json | 当前项目(不提交) |
Plugin hooks/hooks.json | 插件范围 |
| Skill/Agent frontmatter | 特定 Skill/Agent 活跃期间 |
知识检测
概念理解题
-
Hook 和 CLAUDE.md 指令的根本区别是什么?为什么说 Hook 是”确定性的”?
-
PreToolUse 的退出码 2 有什么特殊含义?为什么说这是 Hook 最强大的能力?
-
Hook 的四种处理器类型(command/http/prompt/agent)各自适合什么场景?
prompt类型和 CLAUDE.md 指令有什么区别?
应用题
-
你想确保 Claude 永远不会修改
package-lock.json和pnpm-lock.yaml文件。写出对应的 Hook 配置(JSON 格式)和验证脚本。 -
你想让 Claude 每次编辑
.ts文件后自动跑eslint --fix,每次编辑.py文件后自动跑ruff check --fix。怎么配置?
迁移思考题
-
Claude Code 的 Hook 和 Git Hook 的设计思路有什么相似和不同?Claude Code 的 Hook 增加了什么 Git Hook 没有的能力?(提示:参考 SlipBox 中的 Hook 卡片)
-
OpenClaw 的 Hook 分为 Internal Hook(响应 Agent 事件)和 Webhook(接收外部 HTTP)。Claude Code 如何覆盖这两个场景?
参见
- 03-扩展体系总览 — Hook 在整体架构中的位置
- 01-AI-Agent-架构原理 — Hook 和 Agentic Loop 的关系
- SlipBox 相关:Hook、Claude Code
参考答案
1. Hook vs CLAUDE.md 的根本区别
CLAUDE.md 是”建议”——Claude 看到后”尽量”遵守,但可能因为上下文拥挤、指令冲突或模型推理而忽略。Hook 是”程序”——系统在事件触发时无条件执行脚本,不依赖 LLM 判断。“确定性”意味着:给定相同的输入,Hook 的输出 100% 一致;而 CLAUDE.md 的遵守率是概率性的。
2. 退出码 2 的意义
退出码 2 是 Hook 的否决权——它能阻止 Claude 的操作生效。这让你可以实现硬性安全策略:比如”绝对禁止删除 production 目录的文件”,无论 Claude 的推理多么”合理”都不行。这是 CLAUDE.md 做不到的(CLAUDE.md 写”不要删除 production 文件”,Claude 可能在某些情况下仍然这么做)。
3. 四种处理器的适用场景
command(最常用):快速的确定性检查(lint、格式验证、文件权限检查)。http:需要集成外部服务(通知 Slack、记录到监控系统)。prompt:需要语义判断但仍想由 LLM 做决定的场景(“这个命令看起来危险吗?”),比手写规则更灵活但有延迟。agent:需要多步验证的复杂场景(运行测试套件并分析结果)。prompt 和 CLAUDE.md 的区别:prompt 是独立的单轮评估,不受主对话上下文影响。
4. 保护 lock 文件的 Hook
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "bash -c 'FILE=$(jq -r \".tool_input.file_path // .tool_input.path\" < /dev/stdin); case \"$FILE\" in */package-lock.json|*/pnpm-lock.yaml) echo \"Blocked: lock files are read-only\" >&2; exit 2;; esac; exit 0'"
}]
}]
}
}
5. 按文件类型自动 lint
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "bash -c 'FILE=$(jq -r \".tool_input.file_path // .tool_input.path\" < /dev/stdin); case \"$FILE\" in *.ts|*.tsx) npx eslint --fix \"$FILE\";; *.py) ruff check --fix \"$FILE\";; esac; exit 0'"
}]
}
]
}
}
6. Claude Code Hook vs Git Hook
相似:都在流程”关节处”执行(pre-commit / PreToolUse),都支持否决(非零退出码阻止操作)。不同:(1) Claude Code Hook 有 4 种处理器(Git Hook 只有 shell)(2) Claude Code Hook 可以修改工具输入/输出(Git Hook 不能修改 commit 内容)(3) Claude Code Hook 有语义判断能力(prompt 类型)(4) Claude Code Hook 粒度更细(20+ 事件 vs Git 的 ~10 个)。
7. OpenClaw 的两个 Hook 场景
OpenClaw Internal Hook(TypeScript 函数 + 事件订阅)覆盖了 Claude Code 的 command 类型 Hook 的场景。OpenClaw Plugin Hook(before_tool_call / tool_result_persist 等)提供了 Claude Code 没有的”修改 Agent Loop 内部状态”的能力——如 tool_result_persist 可以在工具输出写入记录前修改它,Claude Code 的 PostToolUse Hook 只能观察和反馈,不能直接修改。