Claude Code 从零到精通:多Agent协作开发完全教程-第七章:Hooks 事件钩子
第七章:Hooks 事件钩子
Hooks 让你在 Claude Code 的特定事件发生时自动执行操作。
7.1 Hook 的作用
Plaintext
┌──────────────────────────────────────────────┐
│ Hooks 工作原理 │
├──────────────────────────────────────────────┤
│ │
│ Claude Code 事件 → 触发 Hook → 执行 │
│ │
│ 例如: │
│ - 会话开始时 → 读取配置文件 │
│ - 工具调用前 → 检查权限 │
│ - 工具调用后 → 记录日志 │
│ - 提交代码前 → 运行 lint 检查 │
│ - 发送通知 → 同步到 Slack │
│ │
└──────────────────────────────────────────────┘
7.2 Hook 的四种类型
类型 说明 适用场景
command 执行 shell 命令 运行 lint、格式化、通知
http 发送 HTTP 请求 通知外部服务、记录日志
prompt 注入提示词到上下文 动态添加规范、提醒
agent 触发子agent 自动代码审查
7.3 配置 Hooks
在 .claude/settings.json 中配置(支持三个层级:用户全局、项目共享、项目本地):
JSON
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "cat .claude/welcome.md",
"description": "显示欢迎信息和项目概览"
}
],
"PreToolUse": [
{
"tool": "Bash",
"type": "command",
"command": "echo '准备执行命令...'",
"description": "命令执行前的提示"
}
],
"PostToolUse": [
{
"tool": "Edit",
"type": "command",
"command": "npx eslint --fix $FILE",
"description": "编辑文件后自动 lint"
}
],
"PreCompact": [
{
"type": "prompt",
"prompt": "压缩时请务必保留:1.所有修改过的文件列表 2.测试命令 3.当前进度",
"description": "压缩前注入保留指令(减少信息丢失30%)"
}
]
}
}
💡 PreToolUse hook 的特殊能力:可以返回 allow(允许)、deny(拒绝)、ask(询问用户)来控制工具执行,非常适合做安全防护。
7.4 常见 Hook 场景
Hook 事件 用途 示例
SessionStart 会话开始时注入上下文 读取项目配置、显示最近变更
PreToolUse 工具调用前检查/拦截 权限检查、危险操作拦截
PostToolUse 工具调用后处理 自动格式化、运行测试
PreCompact 压缩上下文前 指定必须保留的信息
Stop Agent 停止时 保存进度、发送通知
Notification 通知事件 推送到 Slack/微信
7.5 Hook 执行生命周期与数据流
理解 Hook 的完整执行流程,是掌握"怎么生效"的关键:
Plaintext
┌─────────────────────────────────────────────────────────────┐
│ Hook 执行生命周期 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ① 事件触发(如 SessionStart / PreToolUse) │
│ │ │
│ ▼ │
│ ② 查找匹配的 Hook 配置 │
│ - 按配置层级合并(managed > local > project > user) │
│ - 检查 matcher 是否匹配(正则匹配工具名等) │
│ │ │
│ ▼ │
│ ③ 构建 stdin JSON 输入 │
│ { │
│ "session_id": "abc-123", │
│ "cwd": "/path/to/project", │
│ "hook_event_name": "PreToolUse", │
│ "tool_name": "Bash", ← 仅工具相关事件 │
│ "tool_input": { "command": "..." } │
│ } │
│ │ │
│ ▼ │
│ ④ 执行 Hook(command / http / prompt / agent) │
│ │ │
│ ▼ │
│ ⑤ 处理输出 │
│ - command 类型:stdout 作为反馈/上下文 │
│ - command 类型:exit code 控制流程(0=继续, 2=阻止) │
│ - prompt 类型:注入文本到 Claude 对话上下文 │
│ │
└─────────────────────────────────────────────────────────────┘
关键点:Hook 的 stdin 会自动注入事件的上下文信息(JSON 格式),你的脚本可以读取并据此做出决策。
7.6 上下文注入机制详解
这是 Hooks 最强大的能力之一——在对话中动态注入信息,让 Claude 自动获取额外上下文。
方式一:prompt 类型 Hook(直接注入)
prompt 类型 Hook 会将指定文本直接注入到 Claude 的对话上下文中:
JSON
{
"hooks": {
"PreCompact": [
{
"type": "prompt",
"prompt": "压缩时请保留所有修改过的文件路径和测试命令",
"description": "压缩前注入保留指令"
}
],
"SessionStart": [
{
"type": "prompt",
"prompt": "当前项目使用 TypeScript + React,测试框架为 Vitest",
"description": "会话开始注入技术栈信息"
}
]
}
}
prompt 字段支持 $ARGUMENTS 占位符,会被替换为事件的实际参数。
方式二:command 类型 Hook 的 stdout 注入
对于 SessionStart 和 UserPromptSubmit 事件,command 类型 Hook 的标准输出(stdout)会被直接添加到 Claude 的上下文中:
JSON
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "echo '## 项目状态' && git log --oneline -5 && echo '## 待修复问题' && cat .claude/todo.md",
"description": "注入项目状态和待办事项"
}
],
"UserPromptSubmit": [
{
"type": "command",
"command": "echo '当前分支: '$(git branch --show-current) && echo '未提交变更: '$(git status --short | wc -l)' 个文件'",
"description": "每次对话时注入 git 状态"
}
]
}
}
Plaintext
┌──────────────────────────────────────────────────────────┐
│ 上下文注入方式对比 │
├──────────────┬───────────────────────────────────────────┤
│ │ prompt 类型 command 类型 │
├──────────────┼───────────────────────────────────────────┤
│ 注入内容 │ 固定文本 脚本 stdout 输出 │
│ 是否动态 │ 静态(或用 $ARGUMENTS)完全动态 │
│ 适用场景 │ 固定规范、提醒 实时状态、环境信息 │
│ 注入时机 │ 事件触发时 事件触发时 │
│ 执行开销 │ 无 有(执行脚本) │
└──────────────┴───────────────────────────────────────────┘
💡 实用技巧:SessionStart 的 stdout 注入非常适合做"项目上下文预加载"——Claude 一启动就知道项目状态、最近改动、待办事项等。
7.7 PreToolUse 权限控制详解
PreToolUse 是最强大的 Hook 事件,可以在工具执行前拦截、允许或询问用户:
控制方式:Exit Code + JSON 输出
Plaintext
┌──────────────────────────────────────────────────────────┐
│ PreToolUse 控制流程 │
├──────────────────────────────────────────────────────────┤
│ │
│ 工具调用请求 (如: Bash "rm -rf /") │
│ │ │
│ ▼ │
│ PreToolUse Hook 执行 │
│ │ │
│ ├── exit 0 → ✅ 允许执行(默认) │
│ │ │
│ ├── exit 2 → 🚫 阻止执行(静默拦截) │
│ │ │
│ └── stdout JSON → 精细控制 │
│ { │
│ "hookSpecificOutput": { │
│ "permissionDecision": "allow" | "deny" | "ask"
│ } │
│ } │
│ - "allow": 允许执行,跳过用户确认 │
│ - "deny": 阻止执行,告知 Claude │
│ - "ask": 弹出确认框,让用户决定 │
│ │
└──────────────────────────────────────────────────────────┘
实战示例:危险命令拦截器
Bash
#!/bin/bash
.claude/hooks/safety-guard.sh
读取 stdin 中的工具调用信息
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
拦截危险命令
if [[ "$TOOL_NAME" == "Bash" ]]; then
if echo "$COMMAND" | grep -qE 'rm\s+-rf|drop\s+table|--force|reset\s+--hard'; then
echo '{"hookSpecificOutput":{"permissionDecision":"deny"}}'
exit 0
fi
fi
其他情况允许
exit 0
JSON
{
"hooks": {
"PreToolUse": [
{
"matcher": { "tool": "Bash" },
"type": "command",
"command": "bash .claude/hooks/safety-guard.sh",
"description": "拦截危险 shell 命令"
}
]
}
}
7.8 Matcher 匹配器
Matcher 用于精确控制 Hook 在哪些条件下触发,避免不必要的执行:
JSON
{
"hooks": {
"PreToolUse": [
{
"matcher": {
"tool": "Bash"
},
"type": "command",
"command": "bash .claude/hooks/check-bash.sh",
"description": "仅匹配 Bash 工具调用"
}
],
"PostToolUse": [
{
"matcher": {
"tool": "Edit|Write"
},
"type": "command",
"command": "npx prettier --write $FILE",
"description": "匹配 Edit 或 Write 工具(正则语法)"
}
]
}
}
匹配字段 说明 示例
tool 匹配工具名(支持正则) "Bash", "Edit|Write", ".*"
没有设置 matcher 的 Hook 会在每次对应事件触发时执行。建议总是设置 matcher 以避免性能损耗。
7.9 Hook 可用的环境变量
Hook 脚本可以通过 stdin JSON 获取丰富的上下文信息:
字段 说明 示例值
session_id 当前会话 ID "abc-123-def"
cwd Claude Code 工作目录 "/Users/dev/myproject"
hook_event_name 触发的事件名 "PreToolUse"
tool_name 工具名(仅工具事件) "Bash", "Edit"
tool_input 工具输入参数(仅工具事件) {"command": "npm test"}
transcript_path 对话记录文件路径 "/path/to/transcript.jsonl"
读取方式示例(Bash):
Bash
#!/bin/bash
INPUT=$(cat) # 从 stdin 读取 JSON
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
CWD=$(echo "$INPUT" | jq -r '.cwd')
echo "工具: $TOOL, 目录: $CWD"
7.10 配置层级与优先级
Hook 可以在四个层级配置,高优先级的层级覆盖低优先级:
Plaintext
┌─────────────────────────────────────────────────┐
│ 配置层级(优先级从高到低) │
├─────────────────────────────────────────────────┤
│ │
│ ① managed (企业管理) 最高优先级 │
│ ~/.claude/settings.managed.json │
│ → 企业 IT 统一下发,用户无法覆盖 │
│ │
│ ② local (项目本地) │
│ .claude/settings.local.json │
│ → 个人本地配置,不提交到 Git │
│ │
│ ③ project (项目共享) │
│ .claude/settings.json │
│ → 团队共享规范,提交到 Git │
│ │
│ ④ user (用户全局) 最低优先级 │
│ ~/.claude/settings.json │
│ → 个人全局默认配置 │
│ │
├─────────────────────────────────────────────────┤
│ 合并规则:同一事件的 Hook 数组跨层级合并 │
│ (不是覆盖,而是拼接执行) │
└─────────────────────────────────────────────────┘
⚠️ 注意:不同层级的同一事件 Hook 会合并执行(数组拼接),而不是高优先级覆盖低优先级。优先级主要影响的是权限设置等其他配置项。
7.11 完整 Hook 配置示例
一个综合了多种 Hook 能力的实际项目配置:
JSON
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "echo '📋 最近 5 条提交:' && git log --oneline -5 && echo '📌 当前分支:' $(git branch --show-current)",
"description": "会话启动时注入项目上下文"
}
],
"UserPromptSubmit": [
{
"type": "command",
"command": "echo '⚡ 未暂存变更:' $(git diff --stat --shortstat 2>/dev/null || echo '无')",
"description": "每次用户发送消息时注入 git 状态"
}
],
"PreToolUse": [
{
"matcher": { "tool": "Bash" },
"type": "command",
"command": "bash .claude/hooks/safety-guard.sh",
"description": "拦截危险 shell 命令"
}
],
"PostToolUse": [
{
"matcher": { "tool": "Edit|Write" },
"type": "command",
"command": "npx prettier --write $FILE 2>/dev/null; exit 0",
"description": "文件编辑后自动格式化"
}
],
"PreCompact": [
{
"type": "prompt",
"prompt": "压缩时务必保留:1.所有修改过的文件完整路径 2.测试运行命令 3.当前任务进度 4.已知问题",
"description": "压缩前注入保留指令"
}
],
"Notification": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code 有新消息\" with title \"CC 通知\"'",
"description": "macOS 系统通知"
}
]
}
}