首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Claude Code Hooks 深度解析:16 种事件 + 6 个实战场景让你的工作流自动化

Claude Code Hooks 深度解析:16 种事件 + 6 个实战场景让你的工作流自动化

作者头像
运维有术
发布2026-04-01 19:35:11
发布2026-04-01 19:35:11
6230
举报
文章被收录于专栏:运维有术运维有术

🚩 2026 年「术哥无界」系列实战文档 X 篇原创计划 第 46 篇,AI 编程最佳实战「2026」系列第 7 篇 大家好,欢迎来到 术哥无界 | ShugeX | 运维有术。 我是术哥,一名专注于 AI 编程、AI 智能体、Agent Skills、MCP、云原生、Milvus 向量数据库的技术实践者与开源布道者Talk is cheap, let's explore。无界探索,有术而行。

Claude Code Hooks 信息图封面
Claude Code Hooks 信息图封面

图 1:Claude Code Hooks 核心概念可视化

你在用 Claude Code 时,可能遇到过这些情况:

  • 聊着聊着,它就忘了最开始说的项目约定(比如"用 Bun,不用 npm")
  • Claude 修改了 .env 文件,导致敏感配置泄露
  • 生成的代码格式不符合项目规范,每次都要手动运行 Prettier,或者等 CI 报错才发现
  • 并行运行多个 Claude 时,错过某个等待输入的提示,任务卡在那里不动
  • 想记录 Claude 执行的所有 Bash 命令,用于审计或问题复现,但不知道怎么做

这些问题的根源是同一个:Claude Code 的行为是"黑盒",缺乏确定性控制。 AI 的判断不可预测,它可能记住约定,也可能忘记。

Claude Code Hooks 用生命周期钩子解决了这个问题——在特定节点自动执行自定义脚本,确保某些操作一定会发生,而不是依赖 AI 的记忆力和判断力。

官方文档的描述很直白:

Hooks provide deterministic control over Claude Code's behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them.

说人话就是:别指望 AI 每次都记得,用代码强制执行。

Hooks 是什么?

核心概念

Hooks 是用户定义的 shell 命令,在 Claude Code 生命周期的特定点自动执行。

三个关键词:

生命周期钩子(Lifecycle Hooks)

Claude Code 运行时有固定的流程:会话开始 → 加载配置 → 接收用户输入 → 调用工具(Bash、Edit、Write 等)→ 生成响应 → 会话结束。Hooks 可以在这些关键节点插入自定义逻辑,就像 Git 的 pre-commit 钩子或 npm 的 postinstall 脚本。

确定性控制(Deterministic Control)

AI 的行为是不可预测的——同一个问题问两次,可能得到不同的回答。但 Hooks 是代码,执行结果 100% 确定。配置了 PreToolUse Hook 检查文件路径,就一定会检查,不会因为 AI "忘记了" 而跳过。这是 Hooks 区别于 CLAUDE.md 指令文件的关键:CLAUDE.md 是给 AI 看的建议,Hooks 是强制执行的规则。

自动化工作流(Automated Workflow)

不需要人工干预,Hook 脚本自动执行:格式化代码、发送通知、记录日志、拦截危险操作。你可以专注于写代码,让 Hooks 处理重复性的自动化任务。

官方图表解析:Hook 生命周期

官方文档提供了一个完整的生命周期图,展示了所有 Hook 事件的触发顺序。理解这个图是掌握 Hooks 的关键。

Hook 生命周期流程图
Hook 生命周期流程图

图 2:Hook 生命周期 - 从 SessionStart 到 SessionEnd 的完整流程

会话开始阶段

代码语言:javascript
复制
SessionStart → 会话开始或恢复
InstructionsLoaded → 加载 CLAUDE.md 或规则文件

每个会话只触发一次。SessionStart 是注入上下文的好时机,比如读取项目的 README.md、显示最近的 Git 提交历史、设置环境变量。InstructionsLoaded 用于审计哪些指令文件被加载了。

代理循环阶段(Agentic Loop,反复触发)

代码语言:javascript
复制
UserPromptSubmit → 用户提交提示
PreToolUse → 工具执行前(可阻止)
PermissionRequest → 权限请求
PostToolUse → 工具执行成功后
PostToolUseFailure → 工具执行失败后
Stop → Claude 完成响应
Notification → 发送通知

这是最常用的部分,也是 Hooks 发挥最大作用的地方:

  • PreToolUse:在 Claude 执行工具前拦截,可以阻止危险命令、验证参数、注入额外上下文
  • PermissionRequest:自动批准或拒绝权限请求,比如允许 Claude 访问特定目录
  • PostToolUse:在工具执行成功后做后处理,比如格式化代码、运行 lint、记录操作日志
  • PostToolUseFailure:在工具执行失败后处理错误,比如自动重试、发送告警
  • Stop:Claude 完成响应后触发,适合发送通知、运行测试、验证任务完成
  • Notification:Claude 需要用户输入时触发,可以发送桌面通知、声音提醒

子代理相关

代码语言:javascript
复制
SubagentStart → 子代理启动
SubagentStop → 子代理完成

Claude Code 可以启动子代理(Subagent)执行特定任务,比如用 Bash agent 运行命令、用 Explore agent 搜索代码。这两个事件用于监控和审计子代理的行为。

其他事件

代码语言:javascript
复制
TeammateIdle → 团队成员空闲
TaskCompleted → 任务标记完成
ConfigChange → 配置文件变更
WorktreeCreate → 创建工作树
WorktreeRemove → 移除工作树
PreCompact → 上下文压缩前
SessionEnd → 会话终止

这些是高级用法:

  • TeammateIdle:团队协作时,某个成员空闲了,可以分配新任务
  • TaskCompleted:任务被标记完成时触发,可以验证完成标准
  • ConfigChange:配置文件被修改时触发,可以审计或阻止未授权变更
  • WorktreeCreate/Remove:Git 工作树操作,用于管理多分支开发
  • PreCompact:上下文压缩前触发,可以保存关键信息到文件
  • SessionEnd:会话结束时清理资源

关键洞察

  1. 会话级事件:SessionStart、SessionEnd 每个会话只触发一次
  2. 循环事件:在代理循环中,某些事件会反复触发(PreToolUse、PostToolUse、Stop)
  3. 异步事件:WorktreeCreate、WorktreeRemove、InstructionsLoaded 可以独立触发
  4. 决策点:PreToolUse、PermissionRequest 可以阻止操作执行

官方图表解析:Hook 解析流程

单个 Hook 的执行流程是这样的:

代码语言:javascript
复制
1. 事件触发(比如 PreToolUse)
   ↓
2. 匹配器检查(可选)
   - 如果匹配 → 继续
   - 如果不匹配 → 跳过
   ↓
3. Hook 处理器执行
   - 读取 stdin 获取 JSON 输入
   - 执行逻辑判断
   - 输出结果到 stdout
   ↓
4. Claude Code 处理结果
   - Exit 0:允许继续,解析 JSON 输出
   - Exit 2:阻止操作,显示错误反馈
   - 其他:非阻塞错误,记录日志
Hook 解析流程图
Hook 解析流程图

图 3:Hook 解析流程 - 从事件触发到 Claude Code 处理结果的完整链路

匹配器是可选的

不指定匹配器时,每次事件都会触发 Hook。指定匹配器可以过滤特定工具、特定文件、特定场景。比如 matcher: "Bash" 只匹配 Bash 工具,matcher: "Edit|Write" 匹配 Edit 或 Write 工具。

并行执行

同一事件的所有匹配 Hook 会并行运行,不会互相阻塞。这提高了性能,但也意味着要注意共享资源冲突(比如多个 Hook 同时写同一个文件)。

自动去重

相同的命令或 URL 会自动去重。如果你在用户配置和项目配置中都定义了同一个 Hook,它只会执行一次。

退出码决定结果

  • exit 0:一切正常,允许操作继续。如果 stdout 有 JSON 输出,Claude Code 会解析它
  • exit 2:阻止操作,Claude 会看到 stderr 中的错误消息并调整行为
  • exit 1 或其他:非阻塞错误,操作继续,但 Claude Code 会记录错误日志

这个退出码机制非常关键:exit 2 是唯一能阻止操作的方式。

四种 Hook 类型

官方定义了四种 Hook 类型,按复杂度和能力递增:

Command Hook(最简单直接)

执行 shell 命令,通过 stdin/stdout 通信。默认超时 600 秒。

适用场景:确定性规则、简单判断(如检查文件路径、运行格式化工具)

示例:阻止 rm -rf、自动运行 Prettier、发送通知

HTTP Hook

POST JSON 数据到 HTTP 端点。适合远程服务、团队共享审计。

适用场景:需要团队共享的审计日志、调用外部 API、Webhook 集成

示例:发送操作日志到 ELK、调用 Slack Webhook、集成企业审计系统

Prompt Hook

单轮 LLM 评估,默认超时 30 秒。用于需要判断力的决策。

适用场景:检查任务是否完成、判断代码质量、验证响应是否符合要求

示例:判断是否所有测试都通过、评估代码可读性、检查是否遵循项目规范

Agent Hook

多轮验证 + 工具访问,默认超时 60 秒。可以读取文件、执行命令验证实际状态。

适用场景:需要验证实际状态、需要多步骤检查的场景

示例:运行测试套件并验证结果、检查代码覆盖率、验证部署是否成功

选择建议

  • 确定性规则 → Command Hook(如:格式化、阻止危险命令)
  • 需要远程服务 → HTTP Hook(如:团队审计、Webhook 通知)
  • 需要判断力 → Prompt Hook(如:检查任务完成度)
  • 需要验证状态 → Agent Hook(如:运行测试验证)

16 种 Hook 事件完整列表

官方文档定义了 16 种 Hook 事件,按生命周期阶段分类:

会话管理(4 种)

事件

触发时机

可阻止

匹配器支持

典型用途

SessionStart

会话开始或恢复

加载上下文、设置环境变量、显示项目状态

SessionEnd

会话终止

清理临时文件、审计日志、保存会话摘要

InstructionsLoaded

加载 CLAUDE.md 文件

审计指令加载、注入额外上下文

ConfigChange

配置文件变更

审计配置修改、阻止未授权变更

用户交互(2 种)

事件

触发时机

可阻止

匹配器支持

典型用途

UserPromptSubmit

用户提交提示

过滤敏感词、注入上下文、验证输入

Notification

发送通知

桌面提醒、声音提示、集成 IM

工具调用(4 种)

事件

触发时机

可阻止

匹配器支持

典型用途

PreToolUse

工具执行前

权限检查、参数验证、阻止危险操作

PermissionRequest

权限请求

自动批准/拒绝权限、审计权限使用

PostToolUse

工具执行成功后

格式化、验证输出、记录日志

PostToolUseFailure

工具执行失败后

错误日志、自动重试、发送告警

子代理与团队(4 种)

事件

触发时机

可阻止

匹配器支持

典型用途

SubagentStart

子代理启动

审计子代理创建、限制子代理数量

SubagentStop

子代理完成

验证结果、清理资源、审计子代理行为

TeammateIdle

团队成员空闲

防止空闲、分配新任务、负载均衡

TaskCompleted

任务标记完成

验证完成标准、运行验收测试、更新状态

其他事件(2 种)

事件

触发时机

可阻止

匹配器支持

典型用途

Stop

Claude 完成响应

运行测试、发送通知、验证任务完成

PreCompact

上下文压缩前

保存关键信息到文件、记录压缩前状态

关键信息

  • 可阻止:标记 ✅ 的事件可以通过 exit 2 阻止操作
  • 匹配器支持:标记 ✅ 的事件可以使用 matcher 过滤特定场景
  • PostToolUseFailure 不能阻止:工具已经失败了,只能做后处理

实战案例:从简单到高级

接下来看 6 个实战场景,从最简单的通知到复杂的 Agent Hook。

这些场景来自社区实践和官方文档,每个都包含完整配置代码和实际效果说明。

场景一:任务完成通知(Stop Hook)

问题:让 Claude 跑一个耗时任务(比如重构代码、运行测试套件、生成文档),切换到其他窗口工作,不知道它什么时候完成。

传统做法是一直盯着终端,或者每隔几分钟切回去看看。但这很浪费时间,也打断工作节奏。社区里有人吐槽:"让 Claude 跑测试,结果等了半小时才发现早就跑完了。"

解决方案:Stop Hook + macOS 原生通知

脚本~/.claude/complete-notification.sh):

代码语言:javascript
复制
#!/bin/bash
# 发送 macOS 桌面通知
osascript -e 'display notification "Claude 已完成任务" with title "Claude Code"'

配置~/.claude/settings.json):

代码语言:javascript
复制
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/Users/你的用户名/.claude/complete-notification.sh"
          }
        ]
      }
    ]
  }
}

关键点

  • Stop Hook 在 Claude 完成响应后触发
  • 不需要匹配器,每次 Stop 都触发
  • 使用绝对路径,避免路径问题
  • macOS 原生通知不需要额外依赖

效果:Claude 完成每次响应后,桌面弹出通知,不用一直盯着终端。

进阶版本(带项目名称和声音提醒):

代码语言:javascript
复制
#!/bin/bash
INPUT=$(cat)

# 检查是否已经触发过(避免无限循环)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

# 获取项目名称
DIR_NAME=$(basename "$PWD")

# 发送带声音的通知
osascript -e "display notification \"项目: $DIR_NAME\" with title \"Claude Code 已完成\" sound name \"Glass\""

这个版本只在首次 Stop 时触发通知(避免无限循环),并显示项目名称和播放提示音。社区里很多人说:"用了这个之后,终于不用一直盯着终端看了。"

场景二:等待输入提醒(Notification Hook)

问题:你并行运行了 3 个 Claude,分别处理不同任务(前端重构、后端 API、文档更新)。切换到其他工作时,不知道哪个 Claude 在等你输入,任务卡在那里不动。

更糟糕的是,有些任务有时效性——比如你让 Claude 修复一个 hotfix,结果它等输入等了 2 小时,你才看到提示。

解决方案:Notification Hook + 声音提醒 + 项目名称

脚本~/.claude/idle-notification.sh):

代码语言:javascript
复制
#!/bin/bash
# 获取当前项目名称
DIR_NAME=$(basename "$PWD")

# 发送带声音的通知
osascript -e "display notification \"Claude Code 正在等待您的输入\n项目: $DIR_NAME\" with title \"Claude Code\" sound name \"Glass\""

配置

代码语言:javascript
复制
{
  "hooks": {
    "Notification": [
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/Users/你的用户名/.claude/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

关键点

  • "matcher": "idle_prompt" 只匹配等待输入的通知,不匹配其他通知类型
  • \n 在通知中换行,显示更多信息
  • sound name "Glass" 播放提示音

效果

  • 即使不看屏幕也能听到提醒(Glass 声音)
  • 通知显示项目名称,快速定位是哪个 Claude 在等待
  • 不需要一直盯着 3 个终端窗口

场景三:代码自动格式化(PostToolUse Hook)

问题:Claude 生成的代码格式不符合项目规范(缩进不一致、缺少分号、引号不统一),每次都要手动运行 Prettier/Black/gofmt,或者等 CI 报错才发现。

更糟糕的是,多人协作时,每个人格式化工具的版本不同,导致 Git diff 里全是格式变化,看不出真正的代码改动。

解决方案:PostToolUse Hook + 多格式化工具支持

脚本.claude/hooks/format-code.sh):

代码语言:javascript
复制
#!/bin/bash
set -euo pipefail

# 读取 JSON 输入
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 检查文件是否存在且是支持的格式
if [ -n "$FILE_PATH" ] && [ -f "$FILE_PATH" ]; then
  EXT="${FILE_PATH##*.}"
  
  case "$EXT" in
    js|jsx|ts|tsx|json|css|scss|md)
      npx prettier --write "$FILE_PATH" 2>/dev/null
      ;;
    py)
      black "$FILE_PATH" 2>/dev/null
      ;;
    go)
      gofmt -w "$FILE_PATH" 2>/dev/null
      ;;
  esac
fi

exit 0

配置.claude/settings.json):

代码语言:javascript
复制
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-code.sh",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

关键点

  • "matcher": "Write|Edit|MultiEdit" 匹配所有文件编辑工具
  • "timeout": 15 限制格式化时间,避免大文件卡死
  • set -euo pipefail 确保脚本健壮性
  • 2>/dev/null 隐藏格式化工具的警告信息

效果

  • 每次 Claude 修改文件后,自动格式化
  • 支持多种语言(JS/TS/Python/Go/JSON/CSS/Markdown)
  • 避免 CI 因格式问题失败
  • 团队成员使用相同格式化规则,减少 Git diff 噪音

场景四:保护敏感文件(PreToolUse Hook)

问题:Claude 误修改了 .env(敏感配置泄露)、package-lock.json(导致依赖冲突)、SSH 密钥(安全风险)等文件,导致配置泄露或依赖冲突。

更严重的是,有些操作是不可逆的——比如 Claude 执行了 rm -rf node_modules,虽然有 .gitignore,但重新安装依赖需要 5 分钟。

解决方案:PreToolUse Hook + 文件保护规则

脚本.claude/hooks/protect-files.sh):

代码语言:javascript
复制
#!/bin/bash
set -euo pipefail

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 定义保护模式(可以扩展)
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/" "*.pem" "*.key" "secrets.yaml")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    # 输出阻止消息到 stderr
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2  # 阻止操作
  fi
done

exit 0  # 允许操作

配置

代码语言:javascript
复制
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

关键点

  • exit 2 会阻止操作,Claude 会看到错误消息:"Blocked: .env matches protected pattern '.env'",然后调整行为
  • 支持通配符模式匹配(*.pem*.key
  • set -euo pipefail 确保脚本健壮性

效果:敏感文件受到保护,Claude 无法修改,即使它"以为"需要修改。社区里有人分享:"配置了这个之后,终于不用担心 Claude 误删 .env 文件了。"

场景五:上下文重新注入(SessionStart Hook)

问题:上下文压缩后,Claude 忘了项目约定(比如"用 Bun,不用 npm"、"测试在提交前必须通过"、"当前 Sprint 是 auth refactor")。

更严重的是,Claude 开始使用错误的工具或做错误的决定——比如用 npm 安装依赖(项目用 Bun)、跳过测试直接提交、重构了错误的模块。

解决方案:SessionStart Hook + Git 历史注入

基础版本(固定上下文):

代码语言:javascript
复制
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}

进阶版本(动态上下文):

脚本.claude/hooks/inject-context.sh):

代码语言:javascript
复制
#!/bin/bash
# 显示最近 5 次提交
echo "Recent commits:"
git log --oneline -5

# 显示当前分支
echo "\nCurrent branch: $(git branch --show-current)"

# 显示未提交的更改
if [ -n "$(git status --porcelain)" ]; then
  echo "\nUncommitted changes:"
  git status --short
fi

# 显示项目约定
if [ -f "PROJECT_RULES.md" ]; then
  echo "\nProject Rules:"
  cat PROJECT_RULES.md
fi

配置

代码语言:javascript
复制
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-context.sh"
          }
        ]
      }
    ]
  }
}

关键点

  • "matcher": "compact" 只在上下文压缩后触发,不在新会话时触发
  • 动态读取 Git 状态,让 Claude 看到最新的项目进度
  • 可以读取项目约定文件(如 PROJECT_RULES.md

效果:每次会话恢复(特别是上下文压缩后)自动注入最新项目状态,Claude 能"看到"最近的改动和当前进度,减少错误决定。

场景六:命令审计日志(PostToolUse Hook)

问题:想知道 Claude 到底执行了哪些 Bash 命令,用于审计、问题复现、性能分析。

传统做法是在 Claude 执行时盯着看,但这不现实——你不可能一直盯着。而且有些命令执行很快,根本看不清。

解决方案:PostToolUse Hook + 结构化日志

基础版本(简单日志):

代码语言:javascript
复制
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}

进阶版本(带时间戳和会话 ID):

脚本.claude/hooks/audit-commands.sh):

代码语言:javascript
复制
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
CWD=$(echo "$INPUT" | jq -r '.cwd')

# 格式化日志
echo "[$TIMESTAMP] [$SESSION_ID] [$CWD] $COMMAND" >> ~/.claude/command-audit.log

配置

代码语言:javascript
复制
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-commands.sh"
          }
        ]
      }
    ]
  }
}

日志示例

代码语言:javascript
复制
[2026-03-07 14:23:15] [abc123] [/Users/dev/myproject] npm test
[2026-03-07 14:24:32] [abc123] [/Users/dev/myproject] git add .
[2026-03-07 14:25:01] [abc123] [/Users/dev/myproject] git commit -m "feat: add auth"
[2026-03-07 14:30:45] [def456] [/Users/dev/another] docker build -t app .

关键点

  • 记录时间戳,可以追溯问题发生的时间
  • 记录会话 ID,可以区分不同会话的操作
  • 记录工作目录,可以区分不同项目的操作

效果

  • 完整记录 Claude 的所有命令执行
  • 可以用 SIEM 工具分析 AI 行为模式
  • 问题复现时可以查看具体执行了什么命令
  • 高级分析:统计最常用的命令、识别危险操作模式

进阶技巧:Prompt/Agent/HTTP Hook

前面 6 个场景用的都是 Command Hook,执行确定的 shell 命令。但有些场景需要 AI 判断或远程服务,这时候需要用 Prompt/Agent/HTTP Hook。

Prompt Hook:AI 判断任务完成

场景:Claude 完成响应后,你想让它判断是否真的完成了所有任务,而不是简单地说"完成了"。

配置

代码语言:javascript
复制
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks mentioned in the conversation are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}. If complete, respond with {\"ok\": true}.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

关键点

  • Prompt Hook 会启动一个单轮 LLM 评估
  • 响应必须是 JSON 格式,Claude Code 会解析它
  • 默认超时 30 秒,复杂判断可以增加超时时间

效果:Claude 会自己检查任务完成度,如果没有完成会继续工作,而不是过早停止。

Agent Hook:验证测试通过

场景:Claude 完成代码修改后,你想让它运行测试套件并验证所有测试通过,而不是简单地说"测试应该能通过"。

配置

代码语言:javascript
复制
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. If any tests fail, report the failures. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

关键点

  • Agent Hook 会启动一个完整的子代理,可以读取文件、执行命令
  • 可以验证实际状态,而不是猜测
  • 默认超时 60 秒,运行测试可以增加超时时间

效果:Claude 会实际运行测试,如果测试失败会看到具体错误,而不是盲目地说"完成了"。

HTTP Hook:远程审计服务

场景:团队共享审计日志,所有 Claude 操作都记录到中央审计服务,用于安全审计和性能分析。

配置

代码语言:javascript
复制
{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "https://audit.company.com/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN",
              "Content-Type": "application/json"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

关键点

  • HTTP Hook 会 POST JSON 数据到指定 URL
  • 可以使用环境变量(allowedEnvVars 控制哪些环境变量可用)
  • 适合团队共享的审计服务、Webhook 集成

效果:所有 Claude 操作自动上报到中央审计服务,团队可以看到 AI 的行为模式,识别安全风险。

最佳实践

配置策略:分层管理

官方支持多层配置,推荐这样组织:

用户级配置~/.claude/settings.json

  • 通用通知脚本(任务完成通知、等待输入提醒)
  • 通用保护规则(阻止 rm -rf、保护 .env
  • 适用于所有项目

项目级配置.claude/settings.json

  • 项目特定格式化规则(Prettier 配置、Black 配置)
  • 项目保护文件列表(特定的敏感文件)
  • 项目约定注入(用 Bun、测试命令、当前 Sprint)
  • 提交到 Git,团队成员共享

本地级配置.claude/settings.local.json

  • 本地路径(如特定的脚本路径)
  • 敏感环境变量(如 API Token、Webhook URL)
  • gitignored,不会提交到 Git

策略级配置(企业环境)

  • 由管理员控制
  • 强制执行安全规则(如禁止访问特定目录)
  • 用户无法覆盖

推荐配置层级

代码语言:javascript
复制
用户级(通用)→ 项目级(共享)→ 本地级(私密)→ 策略级(强制)

错误处理:健壮脚本模板

社区总结了一个健壮的 Hook 脚本模板,建议所有脚本都遵循:

代码语言:javascript
复制
#!/bin/bash
set -euo pipefail  # 严格模式:出错即退出,未定义变量报错,管道失败即退出

# 读取输入
INPUT=$(cat)

# 验证 JSON 格式
if ! echo "$INPUT" | jq empty 2>/dev/null; then
  echo "Invalid JSON input" >&2
  exit 1
fi

# 提取字段(带默认值)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 主要逻辑
if [ -n "$FILE_PATH" ]; then
  # 执行操作
  # ...
fi

exit 0

关键点

  • set -euo pipefail:脚本出错立即退出,避免继续执行错误逻辑
  • JSON 验证:确保输入格式正确,避免 jq 解析错误
  • 默认值:// empty 避免 jq 对 null 值报错
  • 清晰的退出码:exit 0 允许,exit 2 阻止

性能优化:高频事件保持快速

建议

  1. SessionStart、Notification 等高频事件保持快速(< 1 秒),避免影响用户体验
  2. 合理设置超时:格式化 15s,测试 120s,Agent 任务 180s
  3. 非关键任务使用异步"async": true,不阻塞主流程
  4. 避免共享资源冲突:多个 Hook 并行执行,注意文件锁、数据库连接

异步 Hook 示例

代码语言:javascript
复制
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "./audit.sh",
            "async": true  # 不阻塞主流程
          }
        ]
      }
    ]
  }
}

效果:审计日志上报不阻塞 Claude 的响应,用户不会感觉到延迟。

调试技巧:手动测试

启用详细模式

  • Ctrl+O 切换详细模式,可以看到 Hook 执行的详细信息
  • 或运行 claude --debug 查看完整执行详情

手动测试 Hook 脚本

代码语言:javascript
复制
# 模拟 PreToolUse 事件
echo '{
  "tool_name": "Bash",
  "tool_input": {"command": "npm test"},
  "session_id": "test123",
  "cwd": "/Users/dev/myproject"
}' | ./my-hook.sh

# 检查退出码
echo "Exit code: $?"

# 检查输出(应该是 JSON 或空)

效果:可以在不启动 Claude Code 的情况下测试 Hook 脚本,快速定位问题。

常见问题排查

问题 1:Hook 不触发

症状:配置了 Hook 但从未执行

排查步骤:

  1. 运行 /hooks 确认 Hook 出现在正确的事件下
  2. 检查匹配器是否精确匹配(区分大小写,Bashbash
  3. 确认触发的是正确的事件类型(PreToolUse ≠ PostToolUse)
  4. 检查脚本是否有执行权限:chmod +x ./my-hook.sh

问题 2:Hook 输出错误

症状:看到 "PreToolUse hook error: ..."

原因:

  • 脚本意外退出(非零退出码)
  • Shell profile 中有 echo 语句干扰 JSON 解析

解决方案:

检查 Shell profile(~/.zshrc~/.bashrc):

代码语言:javascript
复制
# 只在交互式 shell 中输出
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

问题 3:Stop Hook 无限循环

症状:Claude 一直工作,从不停止

原因:Stop Hook 没有检查 stop_hook_active 字段,导致 Hook 触发后 Claude 继续,然后又触发 Stop Hook

解决方案:

代码语言:javascript
复制
#!/bin/bash
INPUT=$(cat)

# 检查是否已经触发过
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # 允许停止
fi

# 其他逻辑
# ...

问题 4:JSON 验证失败

症状:脚本输出有效 JSON 但 Claude Code 报解析错误

原因:Shell profile 的输出被添加到 JSON 前面

示例错误输出:

代码语言:javascript
复制
Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}

解决方案:修复 Shell profile(见问题 2)

总结

Claude Code Hooks 的核心价值是确定性控制

AI 的行为不可预测,但代码是确定的。配置了 Hook,就一定会执行,不会因为 AI "忘记了" 而跳过。这是 Hooks 区别于 CLAUDE.md 指令文件的关键:指令是建议,Hook 是规则。

16 种 Hook 事件覆盖了 Claude Code 的完整生命周期:

  • 会话管理:SessionStart、SessionEnd、InstructionsLoaded、ConfigChange
  • 用户交互:UserPromptSubmit、Notification
  • 工具调用:PreToolUse、PermissionRequest、PostToolUse、PostToolUseFailure
  • 子代理与团队:SubagentStart、SubagentStop、TeammateIdle、TaskCompleted
  • 其他:Stop、PreCompact

4 种 Hook 类型满足不同复杂度需求:

  • Command:确定性规则,最简单最快,适合格式化、通知、简单判断
  • HTTP:远程服务,团队共享,适合审计、Webhook、集成外部系统
  • Prompt:AI 判断,单轮评估,适合检查任务完成度、代码质量
  • Agent:状态验证,多轮交互,适合运行测试、验证部署、复杂检查

推荐配置组合

基础必备(每个项目都应该有):

  1. 任务完成通知(Stop Hook)—— 不用盯着终端
  2. 等待输入提醒(Notification Hook)—— 不错过任何提示
  3. 代码自动格式化(PostToolUse Hook)—— 避免 CI 格式报错

进阶推荐(提升开发体验):

  1. 保护敏感文件(PreToolUse Hook)—— 防止误操作
  2. 上下文重新注入(SessionStart Hook)—— 减少错误决定
  3. 命令审计日志(PostToolUse Hook)—— 追溯和复现

高级应用(企业级场景):

  1. 测试验证(Agent Hook)—— 确保测试真的通过
  2. 远程审计服务(HTTP Hook)—— 团队共享审计
  3. 智能任务判断(Prompt Hook)—— AI 自我检查

Hooks 让 Claude Code 从一个"黑盒 AI"变成一个"可控的自动化工具"。你定义规则,它强制执行,这就是确定性控制的价值。

你的项目用了哪些 Hook?有没有遇到什么有趣的使用场景?欢迎在评论区分享你的配置。

相关资源

官方文档 - Hooks Guide:https://code.claude.com/docs/en/hooks-guide

官方文档 - Hooks Reference:https://code.claude.com/docs/en/hooks

参考文章 - 玩转 Claude Code Hooks:https://mp.weixin.qq.com/s/evF1bkuUm88uFKejQDGGdA

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 运维有术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Hooks 是什么?
    • 核心概念
    • 官方图表解析:Hook 生命周期
    • 官方图表解析:Hook 解析流程
    • 四种 Hook 类型
    • 16 种 Hook 事件完整列表
  • 实战案例:从简单到高级
    • 场景一:任务完成通知(Stop Hook)
    • 场景二:等待输入提醒(Notification Hook)
    • 场景三:代码自动格式化(PostToolUse Hook)
    • 场景四:保护敏感文件(PreToolUse Hook)
    • 场景五:上下文重新注入(SessionStart Hook)
    • 场景六:命令审计日志(PostToolUse Hook)
  • 进阶技巧:Prompt/Agent/HTTP Hook
    • Prompt Hook:AI 判断任务完成
    • Agent Hook:验证测试通过
    • HTTP Hook:远程审计服务
  • 最佳实践
    • 配置策略:分层管理
    • 错误处理:健壮脚本模板
    • 性能优化:高频事件保持快速
    • 调试技巧:手动测试
    • 常见问题排查
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档