Claude Code Hooks — 10 种事件钩子与高级特性全解
简介
Claude Code 的 Hooks(事件钩子)系统是整个平台自动化能力的“中枢神经系统”。通过在 AI Agent 运行的关键生命周期节点上注册自定义处理程序,你可以实现危险操作拦截、审计日志记录、自动通知推送、上下文压缩管理、会话初始化等高级自动化场景。v2.1.154+ 版本还新增了 HTTP Hooks、异步 Hooks 和 LLM Prompt Hooks 三种高级处理程序类型,极大地扩展了 Hooks 的能力边界。
如果把 Claude Code 比作一台全自动工厂,Hooks 就是安装在每个工位的传感器和控制阀门:你可以在工具执行前检查参数是否安全(PreToolUse),在工具执行后自动记录结果(PostToolUse),在上下文即将溢出前触发压缩(PreCompact),在会话启动时自动加载项目配置(SessionStart)。这种"在关键时刻插入自定义逻辑"的能力,是 Claude Code 从"好用的 AI 工具"跃升为"可编程的自动化平台"的核心要素。
本文将深度解析 10 种事件钩子 的配置方法、触发时机、使用场景,并介绍 HTTP、异步、LLM Prompt 三种高级处理程序类型,最后提供生产级别的安全钩子配置方案。无论你是想保护生产环境不被误操作破坏,还是想打造一套完整的开发审计系统,本文都能为你提供实用指南。
目录
- 一、Hooks 系统概述
- 二、配置文件格式与环境变量
- 三、PreToolUse 钩子
- 四、PostToolUse 钩子
- 五、Notification 钩子
- 六、Stop 钩子
- 七、SubagentStop 钩子
- 八、PreCompact 钩子
- 九、SessionStart 钩子
- 十、UserPromptSubmit 钩子
- 十一、SessionEnd 钩子
- 十二、MessageDisplay 钩子
- 十三、HTTP Hooks
- 十四、异步 Hooks
- 十五、LLM Prompt Hooks
- 十六、安全钩子配置方案
- 十七、最佳实践
- 十八、真实经验与踩坑
- 十九、落地检查清单
- 二十、总结
- 二十一、下篇预告
一、Hooks 系统概述
1.1 事件生命周期总览
Claude Code 运行过程中会触发一系列事件,每种事件对应一个可注册的钩子:
用户启动 Claude Code
│
▼
┌────────────────────┐
│ SessionStart │ ← 会话初始化:加载配置、检查环境
└────────┬───────────┘
│
▼
用户输入提示词
│
▼
┌────────────────────┐
│ UserPromptSubmit │ ← 用户消息提交前:过滤、增强、校验
└────────┬───────────┘
│
▼
Claude Code 推理并选择工具
│
▼
┌────────────────────┐
│ PreToolUse │ ← 工具执行前:安全检查、参数修改、拒绝
└────────┬───────────┘
│
▼
工具实际执行(Bash/Edit/Write/Read...)
│
▼
┌────────────────────┐
│ PostToolUse │ ← 工具执行后:审计、触发后续操作
└────────┬───────────┘
│
▼
[可选] 上下文即将达到上限
│
▼
┌────────────────────┐
│ PreCompact │ ← 压缩前:保留关键信息、清理冗余
└────────┬───────────┘
│
▼
[可选] 收到系统通知
│
▼
┌────────────────────┐
│ Notification │ ← 通知处理:转发、过滤、自动响应
└────────┬───────────┘
│
▼
[可选] 用户停止 / Ctrl+C
│
▼
┌────────────────────┐
│ Stop │ ← 会话停止:清理、保存状态
└────────┬───────────┘
│
▼
[可选] 子 Agent 完成
│
▼
┌────────────────────┐
│ SubagentStop │ ← 子 Agent 停止:结果收集、日志记录
└────────────────────┘1.2 10 种钩子速查表
| 钩子名称 | 触发时机 | 核心用途 | 可阻断 |
|---|---|---|---|
PreToolUse |
工具执行前 | 安全检查、参数验证、危险拦截 | ✅ |
PostToolUse |
工具执行后 | 审计日志、自动测试、结果处理 | ❌ |
Notification |
收到通知时 | 通知转发、自动响应、过滤 | ❌ |
Stop |
会话停止时 | 清理资源、保存状态、统计报告 | ❌ |
SubagentStop |
子 Agent 停止时 | 结果收集、状态合并、日志记录 | ❌ |
SessionEnd |
会话完全结束时 | 最终清理、数据持久化、上报 | ❌ |
PreCompact |
上下文压缩前 | 保留关键信息、标记重要上下文 | ✅ |
SessionStart |
会话启动时 | 环境初始化、配置加载、欢迎消息 | ❌ |
UserPromptSubmit |
用户提交前 | 输入过滤、指令预处理、上下文增强 | ✅ |
MessageDisplay |
消息展示时 | 拦截/修改 Claude 输出内容 | ✅ |
二、配置文件格式与环境变量
2.1 全局 vs 项目级配置
Hooks 可以在两个层级配置:
# 全局配置 — 对所有项目生效
~/.claude/hooks.json
# 项目级配置 — 仅对当前项目生效
.claude/settings.json # 在 "hooks" 字段中项目级配置会覆盖全局配置中的同名钩子。推荐的实践是:在全局配置中定义安全相关的通用规则(如危险命令拦截),在项目级配置中定义业务相关的自动化规则(如自动测试、自动格式化)。
2.2 Hook 配置结构
每个钩子条目由三部分组成:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "echo hello" }]
}
]
}
}格式说明:Claude Code v2.1+ 统一使用上述“事件类型为键”的对象格式。早期版本和部分旧文档中使用的是数组格式(
"hooks": [{ "hook": "PreToolUse", ... }]),建议统一迁移到新格式以避免兼容性问题。
2.3 可用环境变量
不同钩子可访问的环境变量不同:
| 变量名 | 可用钩子 | 说明 |
|---|---|---|
CLAUDE_TOOL_NAME |
PreToolUse, PostToolUse | 工具名称(Bash/Edit/Write 等) |
CLAUDE_TOOL_INPUT |
PreToolUse, PostToolUse | 工具的输入内容 |
CLAUDE_FILE_PATH |
PreToolUse, PostToolUse | 被操作的文件路径 |
CLAUDE_USER_MESSAGE |
UserPromptSubmit | 用户输入的原始消息 |
CLAUDE_SESSION_ID |
所有钩子 | 当前会话的唯一标识 |
CLAUDE_PROJECT_DIR |
所有钩子 | 项目根目录路径 |
CLAUDE_SUBAGENT_NAME |
SubagentStop | 子 Agent 的名称 |
CLAUDE_COMPACT_REASON |
PreCompact | 触发压缩的原因 |
CLAUDE_STOP_REASON |
Stop | 会话停止的原因 |
三、PreToolUse 钩子
3.1 概述
PreToolUse 是 Hooks 系统中最重要、最常用的钩子类型。它在 Claude Code 调用任何工具(Bash、Edit、Write、Read、Glob 等)之前被触发,允许你:
- 检查工具调用的参数是否安全
- 修改工具调用的参数
- 拒绝危险的调用(脚本退出码非 0 时拒绝执行)
3.2 示例 1:拦截危险命令
{
"hooks": [
{
"hook": "PreToolUse",
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"DANGEROUS_PATTERNS='rm -rf /|rm -rf ~|mkfs|dd if=/dev/zero|chmod -R 777 /|:>/etc/passwd'\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qiE \"$DANGEROUS_PATTERNS\"; then\n echo \"🚫 DENY: 检测到危险命令: $CLAUDE_TOOL_INPUT\"\n exit 1\nfi\necho \"✅ 命令检查通过\"\nexit 0"
],
"env": {
"CLAUDE_TOOL_INPUT": "{{tool_input}}"
}
}
]
}
]
}工作原理:脚本通过 grep -qiE 匹配一组已知的危险命令模式。如果匹配成功,输出拒绝信息并以 exit 1 终止,Claude Code 收到非零退出码后会阻止该工具的执行。
3.3 示例 2:保护关键文件
{
"hooks": [
{
"hook": "PreToolUse",
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 禁止覆盖以下类型的关键文件\nPROTECTED='\\.env$|\\.pem$|\\.key$|id_rsa|secrets\\.json$|credentials\\.yaml$'\nif echo \"$CLAUDE_FILE_PATH\" | grep -qiE \"$PROTECTED\"; then\n echo \"🚫 DENY: 禁止写入受保护文件: $CLAUDE_FILE_PATH\"\n exit 1\nfi\nexit 0"
],
"env": {
"CLAUDE_FILE_PATH": "{{file_path}}"
}
}
]
}
]
}3.4 示例 3:限制 Git 操作范围
{
"hooks": [
{
"hook": "PreToolUse",
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 禁止 force push 到 main/master\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'git push.*--force.*(main|master)'; then\n echo \"🚫 DENY: 禁止强制推送到主分支\"\n exit 1\nfi\n# 禁止删除远程分支\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'git push.*--delete'; then\n echo \"🚫 DENY: 禁止删除远程分支\"\n exit 1\nfi\nexit 0"
],
"env": {
"CLAUDE_TOOL_INPUT": "{{tool_input}}"
}
}
]
}
]
}3.5 匹配器模式说明
matcher 字段支持三种匹配模式:
| 匹配类型 | 示例 | 说明 |
|---|---|---|
| 精确匹配 | "matcher": "Bash" |
仅匹配 Bash 工具 |
| 模式匹配 | "matcher": "rm -rf" |
匹配包含该模式的 Bash 命令 |
| 正则匹配 | "matcher": ".*" |
匹配所有工具调用 |
四、PostToolUse 钩子
4.1 概述
PostToolUse 在工具成功执行后触发,用于事后处理场景。与 PreToolUse 不同,PostToolUse 无法阻止工具执行(因为工具已经执行完毕),但可以做以下事情:
- 记录审计日志
- 自动运行测试
- 触发后续操作(如 Git 暂存、代码格式化)
- 发送通知
4.2 示例 1:自动审计日志
{
"hooks": [
{
"hook": "PostToolUse",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"LOG_DIR=\"$CLAUDE_PROJECT_DIR/.claude/audit\"\nmkdir -p \"$LOG_DIR\"\necho \"[$(date '+%Y-%m-%d %H:%M:%S')] Session=$CLAUDE_SESSION_ID Tool=$CLAUDE_TOOL_NAME Path=${CLAUDE_FILE_PATH:-N/A}\" >> \"$LOG_DIR/tools.log\""
],
"env": {
"CLAUDE_TOOL_NAME": "{{tool_name}}",
"CLAUDE_FILE_PATH": "{{file_path}}",
"CLAUDE_PROJECT_DIR": "{{project_dir}}"
}
}
]
}
]
}4.3 示例 2:编辑后自动运行测试
{
"hooks": [
{
"hook": "PostToolUse",
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"FILE=\"$CLAUDE_FILE_PATH\"\n# 根据文件类型自动运行对应测试\nif echo \"$FILE\" | grep -q '\\.py$'; then\n TEST_FILE=$(echo \"$FILE\" | sed 's|src/|tests/|;s|/test_|/|;s|\\.py$|_test\\.py|')\n [ -f \"$TEST_FILE\" ] && pytest \"$TEST_FILE\" -q --tb=short || true\nelif echo \"$FILE\" | grep -q '\\.ts$\\|\\.tsx$'; then\n npx jest --findRelatedTests \"$FILE\" --passWithNoTests 2>/dev/null || true\nfi"
],
"env": {
"CLAUDE_FILE_PATH": "{{file_path}}"
}
}
]
}
]
}4.4 示例 3:文件写入后自动 Git 暂存
{
"hooks": [
{
"hook": "PostToolUse",
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"cd \"$CLAUDE_PROJECT_DIR\"\ngit add \"$CLAUDE_FILE_PATH\" 2>/dev/null\necho \"📝 已自动暂存: $CLAUDE_FILE_PATH\""
],
"env": {
"CLAUDE_FILE_PATH": "{{file_path}}",
"CLAUDE_PROJECT_DIR": "{{project_dir}}"
}
}
]
}
]
}五、Notification 钩子
5.1 概述
Notification 钩子在 Claude Code 收到系统通知时触发。这些通知可能来自:
- 长时间运行的后台任务完成
- 外部系统集成事件(如 CI/CD 结果)
- 用户通过通知面板发送的消息
5.2 示例 1:桌面通知推送
{
"hooks": [
{
"hook": "Notification",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# macOS 桌面通知\nif command -v osascript &>/dev/null; then\n osascript -e \"display notification \\\"$CLAUDE_NOTIFICATION_BODY\\\" with title \\\"Claude Code 通知\\\"\"\nfi\n# Linux notify-desktop\nif command -v notify-send &>/dev/null; then\n notify-send \"Claude Code\" \"$CLAUDE_NOTIFICATION_BODY\"\nfi"
],
"env": {
"CLAUDE_NOTIFICATION_BODY": "{{notification_body}}"
}
}
]
}
]
}5.3 示例 2:Slack 通知转发
{
"hooks": [
{
"hook": "Notification",
"matcher": "build.*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"curl -s -X POST \"$SLACK_WEBHOOK_URL\" \\\n -H 'Content-type: application/json' \\\n --data \"{\\\"text\\\": \\\"🔔 Claude Code 通知: $CLAUDE_NOTIFICATION_BODY\\\"}\""
],
"env": {
"CLAUDE_NOTIFICATION_BODY": "{{notification_body}}",
"SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
}
}
]
}
]
}六、Stop 钩子
6.1 概述
Stop 钩子在用户主动停止 Claude Code 会话时触发(按 Ctrl+C 或发送停止信号)。适用于:
- 保存中间状态
- 清理临时文件
- 生成会话统计报告
- 发送会话结束通知
6.2 示例 1:会话统计报告
{
"hooks": [
{
"hook": "Stop",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"REPORT=\"$CLAUDE_PROJECT_DIR/.claude/sessions/report-$(date '+%Y%m%d-%H%M%S').md\"\ncat > \"$REPORT\" << EOF\n# 会话终止报告\n\n- **会话 ID**: $CLAUDE_SESSION_ID\n- **终止原因**: $CLAUDE_STOP_REASON\n- **终止时间**: $(date '+%Y-%m-%d %H:%M:%S')\n- **项目目录**: $CLAUDE_PROJECT_DIR\n\n## 本次会话变更\n\n\\`\\`\\`bash\ngit diff --stat 2>/dev/null || echo '无 Git 变更'\n\\`\\`\\`\nEOF\necho \"📋 会话报告已保存: $REPORT\""
],
"env": {
"CLAUDE_STOP_REASON": "{{stop_reason}}",
"CLAUDE_PROJECT_DIR": "{{project_dir}}"
}
}
]
}
]
}6.3 示例 2:清理临时文件
{
"hooks": [
{
"hook": "Stop",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 清理 Claude Code 运行时产生的临时文件\nTMP_DIR=\"/tmp/claude-session-$CLAUDE_SESSION_ID\"\nif [ -d \"$TMP_DIR\" ]; then\n echo \"🧹 清理临时目录: $TMP_DIR\"\n rm -rf \"$TMP_DIR\"\nfi\n# 清理临时创建的分支(如果是在 review 会话中)\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\ngit branch --list 'claude-review-*' | xargs -r git branch -D 2>/dev/null\necho \"✅ 清理完成\""
]
}
]
}
]
}七、SubagentStop 钩子
7.1 概述
当 Claude Code 派生的子 Agent(通过 --agent 模式或 bash 工具中的子进程)完成工作并停止时,SubagentStop 钩子被触发。这对于:
- 收集子 Agent 的输出结果
- 将子 Agent 的工作产物合并到主会话
- 记录子 Agent 的执行统计
7.2 示例 1:子 Agent 结果收集
{
"hooks": [
{
"hook": "SubagentStop",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"RESULT_DIR=\"$CLAUDE_PROJECT_DIR/.claude/subagents\"\nmkdir -p \"$RESULT_DIR\"\ncat > \"$RESULT_DIR/$CLAUDE_SUBAGENT_NAME-$(date '+%Y%m%d-%H%M%S').json\" << EOF\n{\n \"subagent\": \"$CLAUDE_SUBAGENT_NAME\",\n \"stopped_at\": \"$(date '+%Y-%m-%d %H:%M:%S')\",\n \"exit_code\": $CLAUDE_SUBAGENT_EXIT_CODE,\n \"session_id\": \"$CLAUDE_SESSION_ID\"\n}\nEOF\necho \"📦 子 Agent [$CLAUDE_SUBAGENT_NAME] 结果已保存\""
],
"env": {
"CLAUDE_SUBAGENT_NAME": "{{subagent_name}}",
"CLAUDE_SUBAGENT_EXIT_CODE": "{{exit_code}}"
}
}
]
}
]
}7.3 示例 2:特定子 Agent 的定制处理
{
"hooks": [
{
"hook": "SubagentStop",
"matcher": "test-runner",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 专门处理测试运行器子 Agent 的结果\nif [ \"$CLAUDE_SUBAGENT_EXIT_CODE\" -eq 0 ]; then\n echo \"✅ 测试全部通过\"\nelse\n echo \"❌ 测试失败,退出码: $CLAUDE_SUBAGENT_EXIT_CODE\"\n # 自动将失败信息写入报告\n echo \"[FAIL] test-runner 退出码 $CLAUDE_SUBAGENT_EXIT_CODE\" >> \"$CLAUDE_PROJECT_DIR/.claude/test-results.log\"\nfi"
]
}
]
}
]
}八、PreCompact 钩子
8.1 概述
当 Claude Code 的上下文窗口即将达到上限时,系统会自动触发上下文压缩(Compact)。PreCompact 钩子在压缩之前被触发,允许你:
- 标记需要保留的关键上下文(如重要的代码片段、配置信息)
- 清理无用的中间输出
- 自定义压缩策略
这在长会话中非常关键——压缩会丢弃部分历史上下文,如果重要的指令或代码被压缩掉了,Claude Code 可能"忘记"你的要求。
8.2 示例 1:保留关键上下文
{
"hooks": [
{
"hook": "PreCompact",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 在压缩前保存当前对话摘要\nSUMMARY_FILE=\"$CLAUDE_PROJECT_DIR/.claude/compact/summary-$(date '+%H%M%S').md\"\necho \"# 上下文压缩摘要\" > \"$SUMMARY_FILE\"\necho \"- 压缩原因: $CLAUDE_COMPACT_REASON\" >> \"$SUMMARY_FILE\"\necho \"- 压缩时间: $(date '+%Y-%m-%d %H:%M:%S')\" >> \"$SUMMARY_FILE\"\necho \"- 会话 ID: $CLAUDE_SESSION_ID\" >> \"$SUMMARY_FILE\"\n\n# 保存当前 Git 状态\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\necho \"## Git 状态\" >> \"$SUMMARY_FILE\"\ngit status --short >> \"$SUMMARY_FILE\" 2>/dev/null\necho \"\\n## 未暂存变更\" >> \"$SUMMARY_FILE\"\ngit diff --stat >> \"$SUMMARY_FILE\" 2>/dev/null\necho \"📝 压缩前摘要已保存: $SUMMARY_FILE\""
],
"env": {
"CLAUDE_COMPACT_REASON": "{{compact_reason}}",
"CLAUDE_PROJECT_DIR": "{{project_dir}}"
}
}
]
}
]
}8.3 示例 2:压缩前自动保存待办事项
{
"hooks": [
{
"hook": "PreCompact",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 检查是否有未完成的 TODO 注释\nif grep -rn 'TODO\\|FIXME\\|HACK' --include='*.py' --include='*.ts' --include='*.js' \"$CLAUDE_PROJECT_DIR/src/\" 2>/dev/null | head -20; then\n echo \"⚠️ 发现未完成的 TODO 项,已记录到压缩摘要\"\nfi"
]
}
]
}
]
}九、SessionStart 钩子
9.1 概述
SessionStart 钩子在 Claude Code 会话启动时触发,是初始化工作环境的理想位置:
- 检查项目依赖是否安装
- 加载项目特定的环境变量
- 显示当前分支状态
- 初始化审计目录
9.2 示例 1:项目环境初始化
{
"hooks": [
{
"hook": "SessionStart",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"echo '🚀 Claude Code 会话启动'\necho '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'\necho \"📁 项目: $CLAUDE_PROJECT_DIR\"\n\n# 显示 Git 信息\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\nif [ -d .git ]; then\n BRANCH=$(git branch --show-current 2>/dev/null)\n echo \"🌿 分支: $BRANCH\"\n UNSAVED=$(git status --porcelain 2>/dev/null | wc -l)\n echo \"📝 未保存更改: $UNSAVED 个文件\"\nfi\n\n# 检查虚拟环境\nif [ -d .venv ]; then\n echo \"🐍 Python 虚拟环境: 已激活\"\nelif [ -f package.json ]; then\n echo \"📦 Node.js 项目: $(node --version 2>/dev/null)\"\nfi\n\n# 确保审计目录存在\nmkdir -p \"$CLAUDE_PROJECT_DIR/.claude/audit\"\nmkdir -p \"$CLAUDE_PROJECT_DIR/.claude/sessions\"\n\n# 记录会话启动\necho \"[$(date '+%Y-%m-%d %H:%M:%S')] Session=$CLAUDE_SESSION_ID Started\" >> \"$CLAUDE_PROJECT_DIR/.claude/audit/sessions.log\"\necho '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'"
]
}
]
}
]
}9.3 示例 2:自动加载项目规则
{
"hooks": [
{
"hook": "SessionStart",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 如果存在项目特定的 hooks 配置,加载它\nif [ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks-extra.json\" ]; then\n echo \"📋 加载项目扩展 Hooks 配置\"\n cat \"$CLAUDE_PROJECT_DIR/.claude/hooks-extra.json\"\nfi\n\n# 检查是否有未解决的 merge conflict\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\nif git diff --name-only --diff-filter=U 2>/dev/null | grep -q .; then\n echo \"⚠️ 警告: 当前分支存在未解决的合并冲突\"\n git diff --name-only --diff-filter=U\nfi"
]
}
]
}
]
}十、UserPromptSubmit 钩子
10.1 概述
UserPromptSubmit 钩子在用户消息提交给 Claude 模型之前触发,是输入层的第一道关卡:
- 过滤敏感信息(如密码、API Key)
- 自动附加项目上下文
- 预处理特殊指令
- 校验输入格式
10.2 示例 1:敏感信息过滤
{
"hooks": [
{
"hook": "UserPromptSubmit",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 检测用户输入中是否包含敏感信息\nMSG=\"$CLAUDE_USER_MESSAGE\"\n\n# 检测 API Key 模式\nif echo \"$MSG\" | grep -qiE '(sk-[a-zA-Z0-9]{40,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36})'; then\n echo \"⚠️ 警告: 检测到可能的 API Key,建议不要在对话中直接粘贴密钥\"\n echo \"建议使用环境变量或 .env 文件管理密钥\"\nfi\n\n# 检测密码模式\nif echo \"$MSG\" | grep -qiE 'password[=\": ]+[a-zA-Z0-9]{8,}'; then\n echo \"⚠️ 警告: 检测到可能的明文密码,请注意信息安全\"\nfi\n\nexit 0"
],
"env": {
"CLAUDE_USER_MESSAGE": "{{user_message}}"
}
}
]
}
]
}10.3 示例 2:自动附加上下文
{
"hooks": [
{
"hook": "UserPromptSubmit",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"# 在用户提交消息时自动附加当前环境信息\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\nBRANCH=$(git branch --show-current 2>/dev/null || echo 'unknown')\nLAST_COMMIT=$(git log -1 --format='%h %s' 2>/dev/null || echo 'N/A')\necho \"[环境] 分支=$BRANCH | 最新提交=$LAST_COMMIT\""
]
}
]
}
]
}十一、SessionEnd 钩子
11.1 概述
SessionEnd 在会话完全结束(包括所有子进程清理完毕)后触发,与 Stop 的区别在于:Stop 在用户主动停止时立即触发,而 SessionEnd 等待所有资源释放后才触发。这是做数据持久化和最终上报的最佳位置。
11.2 示例:会话数据上报
{
"hooks": {
"SessionEnd": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"REPORT=\"$CLAUDE_PROJECT_DIR/.claude/analytics/sessions.jsonl\"\nmkdir -p \"$(dirname $REPORT)\"\necho \"{\\\"session\\\": \\\"$CLAUDE_SESSION_ID\\\", \\\"ended_at\\\": \\\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\\\", \\\"project\\\": \\\"$(basename $CLAUDE_PROJECT_DIR)\\\"}\" >> \"$REPORT\""
]
}
]
}
]
}
}十二、MessageDisplay 钩子
12.1 概述
MessageDisplay 是 v2.1.154 新增的钩子,在 Claude 的回复消息展示给用户之前触发。它允许你拦截、过滤或修改 Claude 的输出内容,非常适合用于:
- 过滤输出中的敏感信息(如 API Key、内部 URL)
- 自动为输出添加格式化标记
- 在特定关键词出现时触发额外操作
12.2 示例:过滤输出中的敏感信息
{
"hooks": {
"MessageDisplay": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"MSG=\"$CLAUDE_MESSAGE\"\n# 替换可能的 API Key 为占位符\nCLEANED=$(echo \"$MSG\" | sed -E 's/sk-[a-zA-Z0-9]{40,}/[REDACTED_API_KEY]/g')\nif [ \"$MSG\" != \"$CLEANED\" ]; then\n echo \"$CLEANED\"\n exit 2 # exit 2 = 用新内容替换原消息\nfi\nexit 0 # exit 0 = 不修改"
],
"env": {
"CLAUDE_MESSAGE": "{{message}}"
}
}
]
}
]
}
}退出码说明:
exit 0:不修改输出,正常展示exit 2:用脚本的 stdout 内容替换原消息- 非零非 2:阻止消息展示
十三、HTTP Hooks
13.1 概述
除了传统的 shell 命令,Claude Code 还支持将 HTTP 端点作为钩子处理程序。这使得你可以将钩子事件转发到远程服务(如 Webhook、API、监控系统),实现跨系统的自动化联动。
13.2 配置格式
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "https://api.company.com/claude/audit",
"method": "POST",
"headers": {
"Authorization": "Bearer ${AUDIT_API_TOKEN}",
"Content-Type": "application/json"
},
"timeout": 5000
}
]
}
]
}
}13.3 适用场景
- 将操作日志发送到企业 SIEM 系统
- 在工具执行后触发 CI/CD Webhook
- 远程调用合规检查服务
- 与 Slack/Teams 集成发送实时通知
踩坑经验:HTTP Hooks 默认等待响应,超时时间为 5 秒。如果远程服务响应慢,会拖慢 Claude Code 的整体执行速度。对于不需要等待结果的场景,建议结合异步 Hooks 使用。
十四、异步 Hooks
14.1 概述
普通钩子是同步执行的,会阻塞 Claude Code 的主流程直到钩子完成。异步 Hooks 允许你在后台执行耗时操作,不干扰 Claude Code 的正常工作流。
14.2 配置方法
在钩子配置中添加 "async": true 字段:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash",
"args": ["-c", "sleep 2 && npx eslint --fix $CLAUDE_FILE_PATH"],
"async": true,
"env": {
"CLAUDE_FILE_PATH": "{{file_path}}"
}
}
]
}
]
}
}14.3 适用场景
- 耗时的代码格式化或 lint 检查(如 ESLint、Prettier)
- 上传审计日志到远程服务器
- 触发耗时的测试套件
- 构建产物或生成文档
注意:异步钩子无法阻断操作(因为主流程不会等待),也无法通过 stdout 向 Claude Code 传递反馈。如果需要阻断行为,必须使用同步钩子。
十五、LLM Prompt Hooks
15.1 概述
LLM Prompt Hooks 是 v2.1.154 引入的最新特性,允许你使用另一个 AI 模型来处理钩子事件。这意味着你可以用自然语言定义钩子逻辑,而无需编写 shell 脚本。
15.2 配置格式
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "检查这个 shell 命令是否会删除或覆盖任何用户数据。如果会,输出 BLOCK 并解释原因;否则输出 ALLOW。",
"model": "claude-haiku-3-20240307"
}
]
}
]
}
}15.3 适用场景
- 复杂的安全策略判断(难以用正则表达)
- 代码质量审查(自然语言描述规范)
- 上下文相关的智能过滤
- 业务逻辑验证(需要理解代码语义)
踩坑经验:Prompt Hooks 会额外消耗一次 API 调用,对于高频触发的事件(如每次文件读取),可能导致成本快速上升。建议仅在对 PreToolUse 等高价值事件使用,并搭配 matcher 缩小触发范围。
十六、安全钩子配置方案
16.1 生产级完整安全配置
以下是一套面向生产环境的完整安全钩子配置,建议保存为 ~/.claude/hooks.json:
{
"hooks": [
{
"_comment": "=== 第 1 层:危险命令拦截 ===",
"hook": "PreToolUse",
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"DANGEROUS='rm -rf /|rm -rf ~[^/]|mkfs|dd if=|:>.*passwd|chmod -R 777 /|wget.*\\|.*sh|curl.*\\|.*bash|nc -e|ncat -e|reverse.*shell'\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qiE \"$DANGEROUS\"; then\n echo \"🚫 [安全拦截] 危险命令已阻止\"\n echo \"命令: $CLAUDE_TOOL_INPUT\"\n exit 1\nfi\nexit 0"
],
"env": {
"CLAUDE_TOOL_INPUT": "{{tool_input}}"
}
}
]
},
{
"_comment": "=== 第 2 层:关键文件保护 ===",
"hook": "PreToolUse",
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"PROTECTED='\\.env$|\\.pem$|id_rsa|authorized_keys|shadow$|sudoers$|\\.kube/config$|secrets\\.(json|yaml|yml)$'\nif echo \"$CLAUDE_FILE_PATH\" | grep -qiE \"$PROTECTED\"; then\n echo \"🚫 [安全拦截] 禁止写入受保护文件: $CLAUDE_FILE_PATH\"\n exit 1\nfi\nexit 0"
],
"env": {
"CLAUDE_FILE_PATH": "{{file_path}}"
}
}
]
},
{
"_comment": "=== 第 3 层:Git 分支保护 ===",
"hook": "PreToolUse",
"matcher": "git push.*--force",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE '(main|master|production|release)'; then\n echo \"🚫 [安全拦截] 禁止 force push 到保护分支\"\n exit 1\nfi\nexit 0"
],
"env": {
"CLAUDE_TOOL_INPUT": "{{tool_input}}"
}
}
]
},
{
"_comment": "=== 第 4 层:敏感信息检测 ===",
"hook": "UserPromptSubmit",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"MSG=\"$CLAUDE_USER_MESSAGE\"\n# 检测 API Key / Token / 密码\nif echo \"$MSG\" | grep -qiE '(sk-[a-zA-Z0-9]{20,}|ghp_|AKIA|password[=\": ]+[a-zA-Z0-9]{6,})'; then\n echo \"⚠️ [安全警告] 输入中可能包含敏感信息,请注意安全\"\nfi\nexit 0"
],
"env": {
"CLAUDE_USER_MESSAGE": "{{user_message}}"
}
}
]
},
{
"_comment": "=== 第 5 层:全量审计日志 ===",
"hook": "PostToolUse",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"LOG=\"$CLAUDE_PROJECT_DIR/.claude/audit/$(date '+%Y-%m-%d').log\"\nmkdir -p \"$(dirname $LOG)\"\necho \"[$(date '+%H:%M:%S')] $CLAUDE_TOOL_NAME → ${CLAUDE_FILE_PATH:-<no-path>}\" >> \"$LOG\""
],
"env": {
"CLAUDE_TOOL_NAME": "{{tool_name}}",
"CLAUDE_FILE_PATH": "{{file_path}}"
}
}
]
},
{
"_comment": "=== 第 6 层:会话启动安全检查 ===",
"hook": "SessionStart",
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash",
"args": [
"-c",
"cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\n# 检查是否有未提交的敏感文件\nif git ls-files '*.env *.pem id_rsa secrets.*' 2>/dev/null | grep -q .; then\n echo \"⚠️ 警告: 仓库中包含可能的敏感文件\"\n git ls-files '*.env *.pem id_rsa secrets.*'\nfi\n# 创建审计目录\nmkdir -p \"$CLAUDE_PROJECT_DIR/.claude/audit\""
]
}
]
}
]
}16.2 安全配置层级说明
上述配置采用**纵深防御(Defense in Depth)**策略:
| 层级 | 钩子 | 防护目标 |
|---|---|---|
| 第 1 层 | PreToolUse (Bash) | 拦截系统级危险命令 |
| 第 2 层 | PreToolUse (Write) | 保护敏感文件不被覆盖 |
| 第 3 层 | PreToolUse (Git) | 防止 force push 到保护分支 |
| 第 4 层 | UserPromptSubmit | 检测输入中的敏感信息泄露 |
| 第 5 层 | PostToolUse | 全量操作审计记录 |
| 第 6 层 | SessionStart | 启动时的安全检查与初始化 |
16.3 团队级 Hooks 分发
在团队项目中,建议通过版本控制分发 Hooks 配置:
# 在项目的 .claude/settings.json 中引用团队标准配置
{
"hooks": [
// 从版本库加载的团队标准安全策略
]
}
# 使用 CLAUDE_CONFIG_PATH 环境变量指定配置路径
export CLAUDE_CONFIG_PATH=/shared/team-hooks/
claude十七、最佳实践
17.1 1. 钩子脚本应快速执行
Hooks 会阻塞 Claude Code 的主流程,因此钩子脚本应当:
- 避免网络请求(除非是异步的)
- 避免长时间运行的计算
- 超时时间建议 < 2 秒
17.2 2. 使用 exit 0 / exit 1 明确信号
exit 0= 允许继续exit 1= 阻止操作(仅 PreToolUse 等可阻断的钩子)
17.3 3. 日志输出格式统一
echo "🚫 [安全拦截] ..." # 阻止
echo "⚠️ [安全警告] ..." # 警告
echo "✅ [检查通过] ..." # 放行17.4 4. 项目级覆盖全局
安全规则放在全局(~/.claude/hooks.json),业务规则放在项目级(.claude/settings.json)。
十八、真实经验与踩坑
18.1 经验 1:PostToolUse 钩子拖慢编码速度
- 场景:PostToolUse 钩子对每次文件编辑后运行 ESLint 格式化(同步执行)
- 问题:每次编辑一个大文件(2000 行)需等待 8 秒,一个下午累计浪费 15 分钟
- 解决方案:将格式化钩子改为
"async": true(异步执行),或者仅在 PostToolUse 中运行轻量级检查,将完整格式化放在 Stop 钩子中统一执行
18.2 经验 2:PreToolUse 正则匹配过于粗糙
- 场景:PreToolUse 钩子用
grep -qiE "rm -rf"拦截所有删除操作 - 问题:安全的清理操作
rm -rf build/也被拦截,严重影响开发效率 - 解决方案:改用精确正则
rm -rf /[^ ]*$|rm -rf ~[^ /],仅拦截根目录和用户目录的删除,允许项目内的清理操作
十九、落地检查清单
- Claude Code 版本 >= v2.1.154(支持最新 Hook 格式和高级特性)
-
~/.claude/hooks.json或.claude/settings.json中的 Hook 配置使用对象格式(非旧版数组格式) - PreToolUse 的危险命令拦截已测试可阻断
rm -rf /和dd if=/dev/zero - PostToolUse 的审计日志可正常写入
.claude/audit/目录 - SessionStart 的环境初始化可正常执行
- 钩子脚本执行时间 < 2 秒,不会拖慢 Claude Code
- 敏感信息检测已覆盖 API Key、密码、Token 等常见格式
- 耗时操作已配置为异步钩子(
"async": true) - LLM Prompt Hooks 搭配
matcher缩小触发范围,控制额外 API 调用成本 - 团队已通过版本控制共享统一的 Hook 配置
二十、总结
Hooks 系统是 Claude Code 从“AI 辅助工具”升级为“可编程自动化平台”的核心机制。本文介绍的 10 种事件钩子覆盖了 Claude Code 运行生命周期的每一个关键节点:
- PreToolUse — 工具执行前的最后一道安全闸门
- PostToolUse — 工具执行后的审计与自动化触发点
- Notification — 通知事件的自定义处理入口
- Stop — 会话停止时的清理与统计
- SubagentStop — 子 Agent 工作结果的收集点
- SessionEnd — 会话完全结束后的数据持久化与上报
- PreCompact — 上下文压缩前的信息保留机制
- SessionStart — 会话启动时的环境初始化
- UserPromptSubmit — 用户输入的第一道过滤关卡
- MessageDisplay — Claude 输出内容的拦截与脱敏
以及三种高级处理程序类型:HTTP Hooks(远程端点调用)、异步 Hooks(非阻塞执行)和 LLM Prompt Hooks(AI 驱动的智能判断)。
通过合理组合这些钩子与处理程序类型,你可以构建出涵盖安全防护、审计追踪、自动化测试、通知推送等完整功能的企业级开发工作流。
二十一、下篇预告
Git 工作树与 PR Review 实战 — 深入学习 Claude Code 的 -w 隔离工作树功能、--from-pr 命令和 /review Slash 命令,掌握如何在独立的工作树中进行安全的代码审查,完全不干扰主开发分支的正常工作。