mcpField
C
create-hook

create-hook

github.com/neolabhqAgent 工作流1.8k1.2k 次安装9 周前更新
代码仓库

关于 create-hook

create-hook 是面向 Claude Code 的技能,可分析项目工具配置、建议合适的钩子,并创建和测试钩子,帮助开发者自动执行工作流检查。

主要功能

  1. 01检测项目工具和现有钩子
  2. 02根据项目配置建议钩子
  3. 03创建脚本并更新设置
  4. 04测试成功与失败场景

适用场景

  1. 01编辑后运行类型检查或格式化
  2. 02提交前验证测试或构建
  3. 03阻止可能暴露机密的操作
  4. 04将问题作为可操作上下文传给 Claude Code

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/neolabhq/context-engineering-kit --skill create-hook

安装后在 Claude Code 中输入/create-hook确认已加载。

详细介绍

创建 Hook 命令

分析项目、建议实用的 Hook,并在充分测试后创建它们。

你的任务(/create-hook)

  1. 分析环境 - 检测工具和现有 Hook
  2. 建议 Hook - 根据项目配置提出建议
  3. 配置 Hook - 提出针对性问题并创建脚本
  4. 测试并验证 - 确保 Hook 正常工作

工作流程

1. 环境分析与建议

自动检测项目工具,并建议相关 Hook:

检测到 TypeScript(tsconfig.json)时:

  • PostToolUse Hook:“编辑后对文件进行类型检查”
  • PreToolUse Hook:“阻止存在类型错误的编辑”

检测到 Prettier(.prettierrc、prettier.config.js)时:

  • PostToolUse Hook:“编辑后自动格式化文件”
  • PreToolUse Hook:“要求代码格式正确”

检测到 ESLint(.eslintrc.*)时:

  • PostToolUse Hook:“编辑后进行 Lint 检查并自动修复”
  • PreToolUse Hook:“阻止提交存在 Lint 错误的代码”

当 package.json 中有脚本时:

  • test 脚本 → “提交前运行测试”
  • build 脚本 → “提交前验证构建”

检测到 Git 仓库时:

  • PreToolUse/Bash Hook:“阻止提交包含密钥的内容”
  • PostToolUse Hook:“文件更改时执行安全扫描”

决策树:

Project has TypeScript? → Suggest type checking hooks
Project has formatter? → Suggest formatting hooks
Project has tests? → Suggest test validation hooks
Security sensitive? → Suggest security hooks
+ Scan for additional patterns and suggest custom hooks based on:
  - Custom scripts in package.json
  - Unique file patterns or extensions
  - Development workflow indicators
  - Project-specific tooling configurations
2. Hook 配置

首先询问:“这个 Hook 应该做什么?” 并根据分析结果提供相关建议。

然后根据用户的描述了解上下文,只询问你不确定的细节:

  1. 触发时机:它应该何时运行?

    • PreToolUse:文件操作之前(可以阻止操作)
    • PostToolUse:文件操作之后(提供反馈/修复)
    • UserPromptSubmit:处理请求之前
    • 根据需要使用其他事件类型
  2. 工具匹配器:哪些工具应该触发它?(Write、Edit、Bash、* 等)

  3. 适用范围:global、project 或 project-local

  4. 响应方式:

    • 仅使用退出码:简单(退出码为 0 = 成功,PreToolUse 中退出码为 2 = 阻止操作)
    • JSON 响应:高级控制(阻止操作、上下文、决策)
    • 根据复杂度提供建议:简单的通过/失败 → 退出码,丰富的反馈 → JSON
  5. 阻止行为(如适用):“发现问题时是否应停止操作?”

    • PreToolUse:可以阻止操作(安全、验证)
    • PostToolUse:通常只提供反馈
  6. Claude 集成(重要):“Claude Code 是否应该自动看到并修复此 Hook 检测到的问题?”

    • 如果是:使用 additionalContext 传递错误信息
    • 如果否:使用 suppressOutput: true 静默运行
  7. 上下文干扰:“为避免产生干扰,成功的操作是否应该保持静默?”

    • 格式化、例行检查建议选“是”
    • 安全警报、严重错误建议选“否”
  8. 文件筛选:此 Hook 应处理哪些文件类型?

3. 创建 Hook

你应该:

  • 创建 hooks 目录:根据作用范围,使用 ~/.claude/hooks/ 或 .claude/hooks/
  • 生成脚本:创建 Hook 脚本,并确保:
    • 使用正确的 shebang,并设置可执行权限
    • 包含特定于项目的命令(使用检测到的配置路径)
    • 添加注释,说明 Hook 的用途
  • 更新设置:将 Hook 配置添加到相应的 settings.json
  • 使用绝对路径:避免使用脚本和可执行文件的相对路径。使用 $CLAUDE_PROJECT_DIR 引用项目根目录
  • 提供验证选项:询问用户是否要测试 Hook

关键实现标准:

  • 从 stdin 读取 JSON(绝不使用 argv)
  • 使用顶层 additionalContext/systemMessage 与 Claude 通信
  • 操作成功时包含 suppressOutput: true
  • 提供具体的错误数量和可执行的反馈
  • 重点处理已更改的文件,而非整个代码库
  • 支持常见的开发工作流

⚠️ 重要:输入/输出格式

大多数 Hook 实现都会在这里出错。请特别注意:

  • 输入:正确地从 stdin 读取 JSON(而不是 argv)
  • 输出:使用正确的顶层 JSON 结构与 Claude 通信
  • 文档:有疑问时,查阅官方文档以确认确切的架构
4. 测试与验证

重要:同时测试正常和异常路径:

正常路径测试:

  1. 测试预期的成功场景——创建 Hook 应该通过的条件
    • 示例:TypeScript(有效代码)、Linting(已格式化的代码)、Security(安全命令)

异常路径测试: 2. 测试预期的失败场景——创建 Hook 应该失败或发出警告的条件

  • 示例:TypeScript(类型错误)、Linting(未格式化的代码)、Security(危险操作)

验证步骤: 3. 验证预期行为:检查 Hook 是否按预期拦截、发出警告或提供上下文

测试流程示例:

  • 对于防止删除文件的 Hook:创建一个测试文件,尝试执行受保护的操作,并验证 Hook 是否阻止了该操作

如果遇到问题,你应该:

  • 检查 settings 中的 Hook 注册情况
  • 验证脚本权限(chmod +x)
  • 先使用简化版本进行测试
  • 通过详细分析 Hook 执行情况进行调试

Hook 模板

类型检查(PostToolUse)
#!/usr/bin/env node
// Read stdin JSON, check .ts/.tsx files only
// Run: npx tsc --noEmit --pretty
// Output: JSON with additionalContext for errors
自动格式化(PostToolUse)
#!/usr/bin/env node
// Read stdin JSON, check supported file types
// Run: npx prettier --write [file]
// Output: JSON with suppressOutput: true
安全扫描(PreToolUse)
#!/bin/bash
# Read stdin JSON, check for secrets/keys
# Block if dangerous patterns found
# Exit 2 to block, 0 to continue

完整模板请参阅:https://docs.claude.com/en/docs/claude-code/hooks#examples

快速参考

📖 官方文档:https://docs.claude.com/en/docs/claude-code/hooks.md

常见模式:

  • stdin 输入:JSON.parse(process.stdin.read())
  • 文件筛选:处理前检查扩展名
  • 成功响应:{continue: true, suppressOutput: true}
  • 错误响应:{continue: true, additionalContext: "error details"}
  • 拦截操作:在 PreToolUse Hook 中使用 exit(2)

按用途分类的 Hook 类型:

  • 代码质量:使用 PostToolUse 提供反馈和修复
  • 安全:使用 PreToolUse 拦截危险操作
  • CI/CD:使用 PreToolUse 在提交前进行验证
  • 开发:使用 PostToolUse 自动改进

Hook 执行最佳实践:

  • 根据官方文档,Hook 会并行运行
  • 确保设计彼此独立,因为无法保证执行顺序
  • 当多个 Hook 会影响同一文件时,仔细规划它们之间的交互

成功标准

✅ 满足以下条件时,Hook 即创建成功:

  • 脚本具有可执行权限
  • 已在正确的 settings.json 中注册
  • 能正确响应测试场景
  • 能与 Claude 正确集成,以实现自动修复
  • 遵循项目约定和检测到的工具配置

结果:用户将获得一个可用的 Hook,通过智能自动化和质量检查改进开发工作流。


文档索引

请从以下地址获取完整的文档索引:https://code.claude.com/docs/llms.txt 在继续探索之前,使用此文件查找所有可用页面。

使用钩子自动执行工作流

在 Claude Code 编辑文件、完成任务或需要输入时,自动运行 shell 命令。格式化代码、发送通知、验证命令并执行项目规则。

钩子是用户定义的 shell 命令,会在 Claude Code 生命周期的特定节点执行。它们可以确定性地控制 Claude Code 的行为,确保某些操作始终执行,而不是依赖大模型选择是否运行这些操作。使用钩子执行项目规则、自动完成重复性任务,并将 Claude Code 集成到现有工具中。

对于需要判断而非遵循确定性规则的决策,你还可以使用基于提示词的钩子或基于 Agent 的钩子,通过 Claude 模型评估条件。

如需了解扩展 Claude Code 的其他方式,请参阅技能,为 Claude 提供额外指令和可执行命令;参阅子 Agent,在隔离的上下文中运行任务;参阅插件,将扩展打包,以便在不同项目间共享。

本指南介绍常见用例以及如何入门。有关完整的事件架构、JSON 输入/输出格式,以及异步钩子和 MCP 工具钩子等高级功能,请参阅[钩子参考](/en/hooks)。 ## 设置你的第一个钩子

通过 Claude Code 中的 /hooks 交互式菜单创建钩子是最快的方法。本教程会创建一个桌面通知钩子,这样每当 Claude 等待你输入时,你都会收到提醒,而无需一直盯着终端。

在 Claude Code CLI 中输入 `/hooks`。你会看到所有可用钩子事件的列表,以及一个禁用所有钩子的选项。每个事件都对应 Claude 生命周期中的一个时刻,你可以在该时刻运行自定义代码。选择 `Notification`,创建一个在 Claude 需要你注意时触发的钩子。 菜单会显示匹配器列表,用于筛选钩子的触发时机。将匹配器设为 `*`,即可在所有通知类型下触发。之后你可以将匹配器改为 `permission_prompt` 或 `idle_prompt` 等特定值,进一步缩小范围。 选择 `+ Add new hook…`。菜单会提示你输入事件触发时要运行的 shell 命令。钩子可以运行你提供的任意 shell 命令,因此你可以使用平台内置的通知工具。复制适用于你操作系统的命令:
<Tabs>
  <Tab title="macOS">
    使用 [`osascript`](https://ss64.com/mac/osascript.html) 通过 AppleScript 触发原生 macOS 通知:

    ```
    osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'
    ```
  </Tab>

  <Tab title="Linux">
    使用 `notify-send`。大多数安装了通知守护进程的 Linux 桌面环境都预装了此工具:

    ```
    notify-send 'Claude Code' 'Claude Code needs your attention'
    ```
  </Tab>

  <Tab title="Windows (PowerShell)">
    使用 PowerShell 通过 .NET 的 Windows Forms 显示原生消息框:

    ```
    powershell.exe -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')"
    ```
  </Tab>
</Tabs>
菜单会询问你要将钩子配置保存在哪里。选择 `User settings`,将配置保存到 `~/.claude/settings.json`,这样钩子便会应用于你的所有项目。你也可以选择 `Project settings`,将其限定在当前项目中。有关所有可用范围,请参阅[配置钩子位置](#configure-hook-location)。 按 `Esc` 返回 CLI。让 Claude 执行需要权限的操作,然后切换离开终端。你应该会收到桌面通知。

你可以自动化的操作

钩子可以让你在 Claude Code 生命周期中的关键时刻运行代码:编辑后格式化文件、执行前阻止命令、在 Claude 需要输入时发送通知、在会话开始时注入上下文等。钩子事件的完整列表请参阅钩子参考。

每个示例都包含一个可直接使用的配置块,你可以将其添加到设置文件中。最常见的模式包括:

在 Claude 需要输入时接收通知

每当 Claude 完成工作并需要你输入时,接收桌面通知,这样你就可以切换到其他任务,而不必检查终端。

此钩子使用 Notification 事件,该事件会在 Claude 等待输入或权限时触发。下面的每个选项卡都使用平台原生通知命令。将其添加到 ~/.claude/settings.json,或使用上方的交互式引导,通过 /hooks 进行配置:

```json theme={null} { "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" } ] } ] } } ``` ```json theme={null} { "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "notify-send 'Claude Code' 'Claude Code needs your attention'" } ] } ] } } ``` ```json theme={null} { "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\"" } ] } ] } } ```
编辑后自动格式化代码

每当 Claude 编辑文件时,自动运行 Prettier,让格式保持一致,无需手动操作。

此钩子使用带有 Edit|Write 匹配器的 PostToolUse 事件,因此只会在文件编辑工具运行后触发。该命令使用 jq 提取已编辑文件的路径,并将其传递给 Prettier。将其添加到项目根目录下的 .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}
本页面中的 Bash 示例使用 `jq` 解析 JSON。可通过 `brew install jq`(macOS)、`apt-get install jq`(Debian/Ubuntu)安装,或查看 [`jq` downloads](https://jqlang.github.io/jq/download/)。 ### 阻止编辑受保护的文件

防止 Claude 修改 .env、package-lock.json 或 .git/ 中的任何文件等敏感文件。Claude 会收到说明编辑被阻止原因的反馈,以便调整处理方式。

此示例使用由钩子调用的独立脚本文件。脚本会将目标文件路径与受保护模式列表进行匹配,并以代码 2 退出以阻止编辑。

将以下内容保存到 `.claude/hooks/protect-files.sh`:
```bash  theme={null}
#!/bin/bash
# protect-files.sh

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

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0
```
Claude Code 运行钩子脚本时,脚本必须具有可执行权限:
```bash  theme={null}
chmod +x .claude/hooks/protect-files.sh
```
在 `.claude/settings.json` 中添加一个 `PreToolUse` 钩子,使其在任何 `Edit` 或 `Write` 工具调用前运行该脚本:
```json  theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}
```
压缩后重新注入上下文

当 Claude 的上下文窗口填满时,压缩会总结对话以释放空间。这可能会丢失重要细节。使用带有 compact 匹配器的 SessionStart 钩子,在每次压缩后重新注入关键上下文。

命令写入标准输出的任何文本都会添加到 Claude 的上下文中。此示例会提醒 Claude 注意项目规范和近期工作。在项目根目录的 .claude/settings.json 中添加以下内容:

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

你可以将 echo 替换为任何能生成动态输出的命令,例如 git log --oneline -5,以显示近期提交。如果要在每次会话开始时注入上下文,可以改用 CLAUDE.md。有关环境变量,请参阅参考文档中的 CLAUDE_ENV_FILE。

Hook 的工作原理

Hook 事件会在 Claude Code 生命周期的特定阶段触发。事件触发时,所有匹配的 Hook 都会并行运行,且相同的 Hook 命令会自动去重。下表列出了各个事件及其触发时机:

事件触发时机
SessionStart会话开始或恢复时
UserPromptSubmit提交提示词后、Claude 处理提示词前
PreToolUse工具调用执行前。可以阻止调用
PermissionRequest权限对话框出现时
PostToolUse工具调用成功后
PostToolUseFailure工具调用失败后
NotificationClaude Code 发送通知时
SubagentStart子 Agent 启动时
SubagentStop子 Agent 完成时
StopClaude 完成响应时
PreCompact压缩上下文前
SessionEnd会话终止时

每个 Hook 都有一个 type,用于决定其运行方式。大多数 Hook 使用 "type": "command",即运行 shell 命令。另外两种选项会使用 Claude 模型进行决策:"type": "prompt" 用于单轮评估,"type": "agent" 用于通过工具访问进行多轮验证。详情请参阅基于提示词的 Hook和基于 Agent 的 Hook。

读取输入并返回输出

Hook 通过 stdin、stdout、stderr 和退出代码与 Claude Code 通信。事件触发时,Claude Code 会将特定于事件的数据以 JSON 格式传递给脚本的 stdin。脚本读取这些数据并执行相应操作,然后通过退出代码告知 Claude Code 接下来该做什么。

Hook 输入

每个事件都包含 session_id 和 cwd 等通用字段,但不同事件类型还会添加不同的数据。例如,Claude 运行 Bash 命令时,PreToolUse Hook 会通过 stdin 接收类似以下内容:

{
  "session_id": "abc123",          // unique ID for this session
  "cwd": "/Users/sarah/myproject", // working directory when the event fired
  "hook_event_name": "PreToolUse", // which event triggered this hook
  "tool_name": "Bash",             // the tool Claude is about to use
  "tool_input": {                  // the arguments Claude passed to the tool
    "command": "npm test"          // for Bash, this is the shell command
  }
}

脚本可以解析该 JSON,并根据其中任意字段执行操作。UserPromptSubmit Hook 获取的是 prompt 文本,SessionStart Hook 获取的是 source(startup、resume、compact),以此类推。有关共享字段,请参阅参考文档中的通用输入字段;有关特定于事件的架构,请参阅各事件对应的章节。

Hook 输出

脚本通过写入 stdout 或 stderr 并以特定代码退出,告知 Claude Code 接下来该做什么。例如,想要阻止命令执行的 PreToolUse Hook:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "drop table"; then
  echo "Blocked: dropping tables is not allowed" >&2  # stderr becomes Claude's feedback
  exit 2                                               # exit 2 = block the action
fi

exit 0  # exit 0 = let it proceed

退出代码决定接下来会发生什么:

  • 退出代码 0:操作继续。对于 UserPromptSubmit 和 SessionStart Hook,写入 stdout 的内容会添加到 Claude 的上下文中。
  • 退出代码 2:操作被阻止。将原因写入 stderr,Claude 会收到该原因作为反馈,以便进行调整。
  • 其他退出代码:操作继续。stderr 会记入日志,但不会显示给 Claude。按 Ctrl+O 切换详细模式,即可在对话记录中查看这些消息。
结构化 JSON 输出

退出代码提供了两种选择:允许或阻止。若要更精细地控制,请使用退出代码 0,并将 JSON 对象打印到 stdout。

使用退出代码 2 可通过 stderr 消息阻止操作;也可以使用退出代码 0 和 JSON 来进行结构化控制。不要混用:如果退出代码为 2,Claude Code 会忽略 JSON。

例如,PreToolUse 钩子可以拒绝工具调用并告知 Claude 原因,也可以将其升级为请求用户批准:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}

Claude Code 会读取 permissionDecision 并取消工具调用,然后将 permissionDecisionReason 作为反馈传回 Claude。以下三个选项仅适用于 PreToolUse:

  • "allow":继续操作,不显示权限提示
  • "deny":取消工具调用,并将原因发送给 Claude
  • "ask":像往常一样向用户显示权限提示

其他事件使用不同的决策模式。例如,PostToolUse 和 Stop 钩子使用顶层的 decision: "block" 字段,而 PermissionRequest 使用 hookSpecificOutput.decision.behavior。有关各事件的完整说明,请参阅参考文档中的汇总表。

对于 UserPromptSubmit 钩子,请改用 additionalContext,将文本注入 Claude 的上下文。基于提示词的钩子(type: "prompt")以不同方式处理输出:请参阅基于提示词的钩子。

使用匹配器筛选钩子

如果没有匹配器,钩子会在其事件每次发生时触发。匹配器可以缩小触发范围。例如,如果你只想在文件编辑后运行格式化程序(而不是每次工具调用后都运行),请为 PostToolUse 钩子添加匹配器:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}

"Edit|Write" 匹配器是一个用于匹配工具名称的正则表达式模式。只有当 Claude 使用 Edit 或 Write 工具时,钩子才会触发;使用 Bash、Read 或任何其他工具时都不会触发。

每种事件类型都根据特定字段进行匹配。匹配器支持精确字符串和正则表达式模式:

事件匹配器筛选的内容匹配器值示例
PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest工具名称Bash、Edit|Write、mcp__.*
SessionStart会话的启动方式startup、resume、clear、compact
SessionEnd会话结束的原因clear、logout、prompt_input_exit、other
Notification通知类型permission_prompt、idle_prompt、auth_success、elicitation_dialog
SubagentStartAgent 类型Bash、Explore、Plan 或自定义 Agent 名称
PreCompact触发压缩的原因manual、auto
UserPromptSubmit、Stop不支持匹配器每次发生时都会触发
SubagentStopAgent 类型与 SubagentStart 相同的值

下面还有一些示例,展示了如何在不同事件类型上使用匹配器: 仅匹配 Bash 工具调用,并将每条命令记录到文件中。PostToolUse 事件会在命令完成后触发,因此 tool_input.command 包含实际运行的命令。钩子会通过 stdin 接收 JSON 格式的事件数据,jq -r '.tool_input.command' 会提取命令字符串,然后由 >> 将其追加到日志文件中:

```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}
```
MCP 工具使用的命名约定与内置工具不同:`mcp____`,其中 `` 是 MCP 服务名称,`` 是该服务提供的工具。例如,`mcp__github__search_repositories` 或 `mcp__filesystem__read_file`。使用正则表达式匹配器可指定某个服务中的所有工具,也可以使用 `mcp__.*__write.*` 这样的模式匹配多个服务中的工具。完整示例列表请参阅参考文档中的[匹配 MCP 工具](/en/hooks#match-mcp-tools)。
下面的命令使用 `jq` 从钩子的 JSON 输入中提取工具名称,并将其写入 stderr;该名称会在详细模式(`Ctrl+O`)下显示:

```json  theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
          }
        ]
      }
    ]
  }
}
```
`SessionEnd` 事件支持根据会话结束原因进行匹配。此钩子仅在 `clear` 时触发(即运行 `/clear` 时),正常退出时不会触发:
```json  theme={null}
{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "rm -f /tmp/claude-scratch-*.txt"
          }
        ]
      }
    ]
  }
}
```

有关完整的匹配器语法,请参阅 Hooks 参考。

配置 hook 位置

添加 hook 的位置决定了其作用范围:

位置作用范围可共享
~/.claude/settings.json你的所有项目否,仅限本机
.claude/settings.json单个项目是,可提交到代码库
.claude/settings.local.json单个项目否,会被 git 忽略
受管策略设置整个组织是,由管理员控制
插件 hooks/hooks.json启用插件时是,随插件打包
技能或Agent前置元数据技能或 Agent 处于活动状态时是,在组件文件中定义

你也可以在 Claude Code 中使用 /hooks 菜单以交互方式添加、删除和查看 hook。若要一次性禁用所有 hook,请使用 /hooks 菜单底部的切换开关,或在设置文件中设置 "disableAllHooks": true。

通过 /hooks 菜单添加的 hook 会立即生效。如果 Claude Code 正在运行时直接编辑设置文件,则必须在 /hooks 菜单中查看更改或重启会话后,更改才会生效。

基于提示词的 hook

对于需要判断而非确定性规则的决策,请使用 type: "prompt" hook。Claude Code 不会运行 shell 命令,而是将你的提示词和 hook 的输入数据发送给 Claude 模型(默认为 Haiku)来做出决策。如果需要更强的能力,可以通过 model 字段指定其他模型。

模型只需以 JSON 格式返回是或否的决策:

  • "ok": true:继续执行操作
  • "ok": false:阻止操作。模型的 "reason" 会反馈给 Claude,以便它进行调整。

此示例使用 Stop hook 询问模型是否已完成所有请求的任务。如果模型返回 "ok": false,Claude 会继续工作,并将 reason 作为下一条指令:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

完整配置选项请参阅参考文档中的基于提示词的 hook。

基于 Agent 的 hook

如果验证需要检查文件或运行命令,请使用 type: "agent" hook。与只进行一次大模型调用的提示词 hook 不同,Agent hook 会启动一个子 Agent,它可以读取文件、搜索代码并使用其他工具验证条件,然后再返回决策。

Agent hook 使用与提示词 hook 相同的 "ok" / "reason" 响应格式,但默认超时时间更长,为 60 秒,最多可进行 50 轮工具调用。

此示例会在允许 Claude 停止前验证测试是否通过:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

当仅凭 hook 输入数据就足以做出决策时,请使用提示词 hook。当你需要根据代码库的实际状态进行验证时,请使用 Agent hook。

完整配置选项请参阅参考文档中的基于 Agent 的 hook。

限制和故障排查

限制
  • Hook 只能通过 stdout、stderr 和退出代码进行通信,无法直接触发斜杠命令或工具调用。
  • Hook 默认超时时间为 10 分钟;可使用 timeout 字段(单位为秒)按 hook 配置。
  • PostToolUse hook 无法撤销操作,因为工具已经执行完毕。
  • PermissionRequest hook 在非交互模式(-p)下不会触发。请使用 PreToolUse hook 自动处理权限决策。
  • Stop hook 会在 Claude 每次完成响应时触发,而不仅仅是在任务完成时触发。用户中断时不会触发。
钩子未触发

钩子已配置,但始终不执行。

  • 运行 /hooks,确认钩子显示在正确的事件下
  • 检查匹配器模式是否与工具名称完全匹配(匹配器区分大小写)
  • 确认触发的是正确的事件类型(例如,PreToolUse 在工具执行前触发,PostToolUse 在工具执行后触发)
  • 如果在非交互模式(-p)下使用 PermissionRequest 钩子,请改用 PreToolUse
输出中出现钩子错误

你会在对话记录中看到类似 "PreToolUse hook error: ..." 的消息。

  • 脚本意外地以非零代码退出。通过管道传入示例 JSON,手动测试:

    echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
    echo $?  # Check the exit code
    
  • 如果看到 "command not found",请使用绝对路径或 $CLAUDE_PROJECT_DIR 引用脚本
  • 如果看到 "jq: command not found",请安装 jq,或使用 Python/Node.js 解析 JSON
  • 如果脚本完全没有运行,请使其可执行:chmod +x ./my-hook.sh
/hooks 显示未配置钩子

你编辑了设置文件,但钩子没有出现在菜单中。

  • 重启会话或打开 /hooks 以重新加载。通过 /hooks 菜单添加的钩子会立即生效,但手动编辑文件需要重新加载。
  • 验证 JSON 是否有效(不允许使用尾随逗号和注释)
  • 确认设置文件位于正确的位置:项目钩子使用 .claude/settings.json,全局钩子使用 ~/.claude/settings.json
Stop 钩子无限运行

Claude 不断工作,陷入无限循环,无法停止。

Stop 钩子脚本需要检查是否已经触发过继续执行。解析 JSON 输入中的 stop_hook_active 字段,如果其值为 true,则提前退出:

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Allow Claude to stop
fi
# ... rest of your hook logic
JSON 验证失败

尽管钩子脚本输出了有效 JSON,Claude Code 仍显示 JSON 解析错误。

Claude Code 运行钩子时,会启动一个 shell 并加载你的配置文件(~/.zshrc 或 ~/.bashrc)。如果配置文件中包含无条件执行的 echo 语句,这些输出就会附加到钩子的 JSON 前面:

Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}

Claude Code 会尝试将其解析为 JSON 并失败。要解决此问题,请在 shell 配置文件中为 echo 语句添加条件,使其仅在交互式 shell 中运行:

# In ~/.zshrc or ~/.bashrc
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

$- 变量包含 shell 标志,其中 i 表示交互式。钩子在非交互式 shell 中运行,因此 echo 会被跳过。

调试技巧

按 Ctrl+O 切换详细模式,以在对话记录中查看钩子输出;也可以运行 claude --debug,查看完整的执行详情,包括哪些钩子匹配以及它们的退出代码。

了解更多


文档索引

获取完整文档索引:https://code.claude.com/docs/llms.txt 使用此文件,在继续探索之前查找所有可用页面。

钩子参考

Claude Code 钩子事件、配置架构、JSON 输入/输出格式、退出代码、异步钩子、提示词钩子和 MCP 工具钩子的参考。

如需查看包含示例的快速入门指南,请参阅[使用钩子自动化工作流](/en/hooks-guide)。

钩子是用户定义的 shell 命令或大模型提示词,会在 Claude Code 生命周期的特定时间点自动执行。请使用本参考查阅事件架构、配置选项、JSON 输入/输出格式,以及异步钩子和 MCP 工具钩子等高级功能。如果你是首次设置钩子,请先阅读指南。

Hook 生命周期

Hook 会在 Claude Code 会话中的特定时间点触发。当事件触发且匹配器匹配时,Claude Code 会将有关该事件的 JSON 上下文传递给 Hook 处理程序。对于命令 Hook,这些内容会通过标准输入(stdin)传入。随后,处理程序可以检查输入、采取操作,并选择是否返回决策。有些事件每个会话只触发一次,另一些则会在 Agent 循环中反复触发:

Hook 生命周期示意图,展示了从 SessionStart 到 Agent 循环再到 SessionEnd 的 Hook 顺序

下表概述了各事件的触发时机。Hook 事件一节详细说明了每个事件的完整输入架构和决策控制选项。

EventWhen it fires
SessionStart会话开始或恢复时
UserPromptSubmit提交提示词时,在 Claude 处理之前
PreToolUse工具调用执行之前。可以阻止该调用
PermissionRequest权限对话框出现时
PostToolUse工具调用成功后
PostToolUseFailure工具调用失败后
NotificationClaude Code 发送通知时
SubagentStart子 Agent 启动时
SubagentStop子 Agent 完成时
StopClaude 完成响应时
PreCompact压缩上下文之前
SessionEnd会话结束时
钩子的解析过程

要了解这些部分如何协同工作,请看这个会阻止破坏性 Shell 命令的 PreToolUse 钩子。该钩子会在每次调用 Bash 工具前运行 block-rm.sh:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

脚本从 stdin 读取 JSON 输入,提取命令;如果命令包含 rm -rf,则返回 "deny" 的 permissionDecision:

#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0  # allow the command
fi

现在假设 Claude Code 决定运行 Bash "rm -rf /tmp/build"。接下来会发生以下情况:

钩子解析流程:触发 PreToolUse 事件,匹配器检查是否匹配 Bash,钩子处理程序运行,并将结果返回给 Claude Code `PreToolUse` 事件触发。Claude Code 通过 stdin 将工具输入作为 JSON 发送给钩子:
```json  theme={null}
{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
```
匹配器 `"Bash"` 与工具名称匹配,因此运行 `block-rm.sh`。如果省略匹配器或使用 `"*"`,钩子会在每次发生该事件时运行。只有在定义了匹配器且不匹配时,钩子才会跳过。 脚本从输入中提取 `"rm -rf /tmp/build"` 并找到 `rm -rf`,因此会向 stdout 输出一项判定:
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook"
  }
}
```

如果命令是安全的(例如 `npm test`),脚本则会执行 `exit 0`,这会告知 Claude Code 允许此次工具调用,无需采取进一步操作。
Claude Code 读取 JSON 判定,阻止此次工具调用,并向 Claude 显示原因。

下文的配置部分介绍了完整的架构;每个钩子事件部分则说明命令会接收哪些输入,以及可以返回哪些输出。

配置

Hook 在 JSON 设置文件中定义。配置分为三层:

  1. 选择要响应的Hook 事件,例如 PreToolUse 或 Stop
  2. 添加匹配器组,用于筛选触发条件,例如“仅针对 Bash 工具”
  3. 定义一个或多个Hook 处理器,在匹配时运行

如需查看完整的操作流程和带注释的示例,请参阅上文的 Hook 的解析方式。

本页面为每一层使用了特定术语:生命周期节点称为 **Hook 事件**,筛选器称为 **匹配器组**,运行的 shell 命令、提示词或 Agent 称为 **Hook 处理器**。单独使用“Hook”时,指的是这一通用功能。
Hook 位置

Hook 的定义位置决定其作用范围:

位置作用范围可共享性
~/.claude/settings.json你的所有项目否,仅限本机
.claude/settings.json单个项目是,可提交到代码库
.claude/settings.local.json单个项目否,由 gitignore 忽略
受管策略设置整个组织是,由管理员控制
插件 hooks/hooks.json插件启用期间是,与插件一同打包
Skill 或 Agent 前置元数据组件处于活动状态期间是,在组件文件中定义

有关设置文件解析的详细信息,请参阅设置。企业管理员可以使用 allowManagedHooksOnly 来阻止用户、项目和插件 Hook。请参阅 Hook 配置。

匹配器模式

matcher 字段是一个正则表达式字符串,用于筛选钩子触发的时机。使用 "*"、"" 或完全省略 matcher,即可匹配所有情况。不同事件类型会匹配不同字段:

事件匹配器筛选的内容匹配器值示例
PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest工具名称Bash、Edit|Write、mcp__.*
SessionStart会话的启动方式startup、resume、clear、compact
SessionEnd会话结束的原因clear、logout、prompt_input_exit、bypass_permissions_disabled、other
Notification通知类型permission_prompt、idle_prompt、auth_success、elicitation_dialog
SubagentStartAgent 类型Bash、Explore、Plan 或自定义 Agent 名称
PreCompact触发压缩的原因manual、auto
SubagentStopAgent 类型与 SubagentStart 相同的值
UserPromptSubmit、Stop不支持匹配器每次出现时都会触发

匹配器是一个正则表达式,因此 Edit|Write 会匹配这两种工具,Notebook.* 会匹配所有以 Notebook 开头的工具。匹配器会针对 Claude Code 通过 stdin 发送给钩子的 JSON 输入中的某个字段进行匹配。对于工具事件,该字段是 tool_name。每个钩子事件章节都会列出该事件的全部匹配器值和输入架构。

此示例仅在 Claude 写入或编辑文件时运行代码检查脚本:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}

UserPromptSubmit 和 Stop 不支持匹配器,每次出现时都会触发。如果为这些事件添加 matcher 字段,该字段会被静默忽略。

匹配 MCP 工具

MCP 服务工具在工具事件(PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest)中会显示为常规工具,因此你可以像匹配其他工具名称一样匹配它们。

MCP 工具遵循 mcp__<server>__<tool> 命名模式,例如:

  • mcp__memory__create_entities:Memory 服务的创建实体工具
  • mcp__filesystem__read_file:Filesystem 服务的读取文件工具
  • mcp__github__search_repositories:GitHub 服务的搜索工具

使用正则表达式模式匹配特定 MCP 工具或工具组:

  • mcp__memory__.* 匹配 memory 服务中的所有工具
  • mcp__.*__write.* 匹配任何服务中名称包含“write”的工具

此示例记录 memory 服务的所有操作,并验证任何 MCP 服务的写入操作:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}
Hook 处理器字段

内部 hooks 数组中的每个对象都是一个 Hook 处理器:当匹配器匹配时运行的 shell 命令、LLM 提示词或 Agent。共有三种类型:

  • 命令 Hook(type: "command"):运行 shell 命令。脚本会通过 stdin 接收事件的 JSON 输入,并通过退出代码和 stdout 返回结果。
  • 提示词 Hook(type: "prompt"):向 Claude 模型发送提示词进行单轮评估。模型会以 JSON 格式返回是/否判断。请参阅基于提示词的 Hook。
  • Agent Hook(type: "agent"):启动一个子 Agent,它可以使用 Read、Grep 和 Glob 等工具来验证条件,然后返回判断。请参阅基于 Agent 的 Hook。
通用字段

以下字段适用于所有 Hook 类型:

字段必需说明
type是"command"、"prompt" 或 "agent"
timeout否取消前等待的秒数。默认值:command 为 600,prompt 为 30,agent 为 60
statusMessage否Hook 运行期间显示的自定义加载提示
once否如果为 true,则每个会话只运行一次,随后移除。仅适用于 Skills,不适用于 Agent。请参阅Skills 和 Agent 中的 Hook
命令 Hook 字段

除了通用字段外,命令 Hook 还接受以下字段:

字段必需说明
command是要执行的 shell 命令
async否如果为 true,则在后台运行而不阻塞。请参阅在后台运行 Hook
提示词和 Agent Hook 字段

除了通用字段外,提示词和 Agent Hook 还接受以下字段:

字段必需说明
prompt是发送给模型的提示词文本。使用 $ARGUMENTS 作为 Hook 输入 JSON 的占位符
model否用于评估的模型。默认为快速模型

所有匹配的 Hook 都会并行运行,并自动对相同的处理器去重。处理器会在当前目录中使用 Claude Code 的环境运行。在远程 Web 环境中,$CLAUDE_CODE_REMOTE 环境变量会被设为 "true";在本地 CLI 中则不会设置该变量。

按路径引用脚本

使用环境变量引用相对于项目根目录或插件根目录的钩子脚本,无论钩子运行时的工作目录是什么:

  • $CLAUDE_PROJECT_DIR:项目根目录。请用引号括起来,以处理包含空格的路径。
  • ${CLAUDE_PLUGIN_ROOT}:插件的根目录,适用于随插件打包的脚本。
此示例使用 `$CLAUDE_PROJECT_DIR`,在任何 `Write` 或 `Edit` 工具调用后,从项目的 `.claude/hooks/` 目录运行样式检查器:
```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}
```
在 `hooks/hooks.json` 中定义插件钩子,可选择添加顶层 `description` 字段。启用插件后,其钩子会与用户和项目钩子合并。
此示例运行随插件打包的格式化脚本:

```json  theme={null}
{
  "description": "自动格式化代码",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

有关创建插件钩子的详细信息,请参阅[插件组件参考](/en/plugins-reference#hooks)。
技能和 Agent 中的钩子

除了设置文件和插件之外,还可以使用 frontmatter 直接在技能和子 Agent中定义钩子。这些钩子的作用范围限于组件的生命周期,且仅在该组件处于活动状态时运行。

支持所有钩子事件。对于子 Agent,Stop 钩子会自动转换为 SubagentStop,因为子 Agent 完成时触发的是该事件。

钩子使用与基于设置的钩子相同的配置格式,但作用范围限于组件的生命周期,并会在组件结束时清理。

此技能定义了一个 PreToolUse 钩子,会在每条 Bash 命令执行前运行安全验证脚本:

---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

Agent 在其 YAML frontmatter 中使用相同格式。

/hooks 菜单

在 Claude Code 中输入 /hooks,即可打开交互式钩子管理器,无需直接编辑设置文件,便可查看、添加和删除钩子。如需逐步操作指南,请参阅指南中的设置你的第一个钩子。

菜单中的每个钩子都会带有一个表示其来源的方括号前缀:

  • [User]:来自 ~/.claude/settings.json
  • [Project]:来自 .claude/settings.json
  • [Local]:来自 .claude/settings.local.json
  • [Plugin]:来自插件的 hooks/hooks.json,只读
禁用或移除钩子

要移除钩子,请从设置 JSON 文件中删除其条目,或使用 /hooks 菜单并选择要删除的钩子。

若要暂时禁用所有钩子但不将其移除,请在设置文件中设置 "disableAllHooks": true,或使用 /hooks 菜单中的开关。无法在保留配置的情况下单独禁用某个钩子。

直接修改设置文件中的钩子不会立即生效。Claude Code 会在启动时捕获钩子快照,并在整个会话期间使用该快照。这样可以防止恶意或意外的钩子修改未经你审核便在会话中途生效。如果钩子在外部被修改,Claude Code 会发出警告,并要求你在 /hooks 菜单中审核后才应用更改。

Hook 输入和输出

Hook 通过 stdin 接收 JSON 数据,并通过退出码、stdout 和 stderr 传递结果。本节介绍所有事件通用的字段和行为。Hook 事件下的各事件小节包含相应的输入架构和决策控制选项。

通用输入字段

除各个 Hook 事件小节中记录的特定事件字段外,所有 Hook 事件还会通过 stdin 接收以下 JSON 字段:

字段说明
session_id当前会话标识符
transcript_path对话 JSON 的路径
cwd调用 Hook 时的当前工作目录
permission_mode当前权限模式:"default"、"plan"、"acceptEdits"、"dontAsk" 或 "bypassPermissions"
hook_event_name触发的事件名称

例如,用于 Bash 命令的 PreToolUse Hook 会通过 stdin 接收以下内容:

{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

tool_name 和 tool_input 字段特定于事件。每个 Hook 事件小节都会说明该事件的其他字段。

退出码输出

Hook 命令的退出码会告知 Claude Code 是否应继续、阻止或忽略该操作。

退出码 0 表示成功。Claude Code 会解析 stdout 中的 JSON 输出字段。只有退出码为 0 时才会处理 JSON。对于大多数事件,stdout 仅在详细模式(Ctrl+O)下显示。例外情况是 UserPromptSubmit 和 SessionStart:stdout 会作为上下文提供给 Claude,供其查看和处理。

退出码 2 表示阻止性错误。Claude Code 会忽略 stdout 及其中的任何 JSON,而是将 stderr 文本作为错误消息传回给 Claude。具体影响取决于事件:PreToolUse 会阻止工具调用,UserPromptSubmit 会拒绝提示词,依此类推。完整列表请参阅各事件的退出码 2 行为。

任何其他退出码都表示非阻止性错误。stderr 会在详细模式(Ctrl+O)下显示,执行会继续。

例如,以下 Hook 命令脚本会阻止危险的 Bash 命令:

#!/bin/bash
# Reads JSON input from stdin, checks the command
command=$(jq -r '.tool_input.command' < /dev/stdin)

if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2  # Blocking error: tool call is prevented
fi

exit 0  # Success: tool call proceeds
各事件的退出码 2 行为

退出码 2 是钩子发出“停止,不要执行此操作”信号的方式。具体效果取决于事件,因为有些事件表示可以阻止的操作(例如尚未执行的工具调用),而其他事件表示已经发生或无法阻止的事情。

钩子事件可以阻止?退出码为 2 时会发生什么
PreToolUse是阻止工具调用
PermissionRequest是拒绝权限
UserPromptSubmit是阻止提示词处理并清除提示词
Stop是阻止 Claude 停止,继续对话
SubagentStop是阻止子 Agent 停止
PostToolUse否向 Claude 显示标准错误输出(工具已运行)
PostToolUseFailure否向 Claude 显示标准错误输出(工具已失败)
Notification否仅向用户显示标准错误输出
SubagentStart否仅向用户显示标准错误输出
SessionStart否仅向用户显示标准错误输出
SessionEnd否仅向用户显示标准错误输出
PreCompact否仅向用户显示标准错误输出
JSON 输出

退出码可用于允许或阻止操作,而 JSON 输出则能提供更精细的控制。无需以代码 2 退出以阻止操作,而是以代码 0 退出并向标准输出打印 JSON 对象。Claude Code 会读取该 JSON 中的特定字段来控制行为,包括用于阻止、允许或升级至用户处理的决策控制。

每个钩子必须选择一种方式,不能同时使用两种:要么仅使用退出码发出信号,要么以代码 0 退出并打印 JSON 进行结构化控制。Claude Code 仅在退出码为 0 时处理 JSON。如果以代码 2 退出,任何 JSON 都会被忽略。

钩子的标准输出必须仅包含 JSON 对象。如果你的 shell 配置文件在启动时打印文本,可能会干扰 JSON 解析。请参阅故障排除指南中的 JSON 验证失败。

JSON 对象支持三类字段:

  • 通用字段(如 continue)适用于所有事件。下表列出了这些字段。
  • 顶层 decision 和 reason 用于某些事件,以阻止操作或提供反馈。
  • hookSpecificOutput 是一个嵌套对象,用于需要更丰富控制的事件。它要求包含一个 hookEventName 字段,其值必须设为事件名称。
字段默认值说明
continuetrue如果为 false,钩子运行后 Claude 将完全停止处理。其优先级高于任何事件专属决策字段
stopReason无当 continue 为 false 时向用户显示的消息。不会显示给 Claude
suppressOutputfalse如果为 true,则在详细模式输出中隐藏标准输出
systemMessage无向用户显示的警告消息

无论事件类型如何,都要让 Claude 完全停止:

{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
决策控制

并非所有事件都支持通过 JSON 阻止或控制行为。支持此功能的事件会使用不同的字段来表达决策。编写钩子前,可将此表作为速查表:

事件决策模式关键字段
UserPromptSubmit、PostToolUse、PostToolUseFailure、Stop、SubagentStop顶层 decisiondecision: "block"、reason
PreToolUsehookSpecificOutputpermissionDecision(allow/deny/ask)、permissionDecisionReason
PermissionRequesthookSpecificOutputdecision.behavior(allow/deny)

以下示例展示了每种模式的实际用法:

用于 `UserPromptSubmit`、`PostToolUse`、`PostToolUseFailure`、`Stop` 和 `SubagentStop`。唯一可用的值是 `"block"`——若要允许操作继续,请在 JSON 中省略 `decision`,或在完全不输出 JSON 的情况下以 0 退出:
```json  theme={null}
{
  "decision": "block",
  "reason": "测试套件必须通过后才能继续"
}
```
使用 `hookSpecificOutput` 进行更精细的控制:允许、拒绝或升级给用户。你还可以在工具运行前修改其输入,或向 Claude 注入额外上下文。有关所有选项,请参阅 [PreToolUse 决策控制](#pretooluse-decision-control)。
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "不允许写入数据库"
  }
}
```
使用 `hookSpecificOutput` 代表用户允许或拒绝权限请求。允许请求时,你还可以修改工具的输入,或应用权限规则,以免再次提示用户。有关所有选项,请参阅 [PermissionRequest 决策控制](#permissionrequest-decision-control)。
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}
```

有关更多示例,包括 Bash 命令验证、提示词过滤和自动批准脚本,请参阅指南中的可自动化的内容,以及 Bash 命令验证器参考实现。

钩子事件

每个事件都对应 Claude Code 生命周期中的一个节点,钩子可在此运行。以下各节按照生命周期顺序排列:从会话设置,到 Agent 循环,再到会话结束。每节都会说明事件何时触发、支持哪些匹配器、接收什么 JSON 输入,以及如何通过输出控制行为。

SessionStart

在 Claude Code 开始新会话或恢复现有会话时运行。可用于加载开发上下文,例如现有问题或代码库的近期更改,或设置环境变量。对于不需要脚本的静态上下文,请改用 CLAUDE.md。

SessionStart 会在每个会话中运行,因此请确保这些钩子运行迅速。

匹配器值对应会话的启动方式:

匹配器触发时机
startup新会话
resume--resume、--continue 或 /resume
clear/clear
compact自动或手动压缩
SessionStart 输入

除了通用输入字段外,SessionStart 钩子还会接收 source、model,以及可选的 agent_type。source 字段表示会话的启动方式:新会话为 "startup",恢复的会话为 "resume",执行 /clear 后为 "clear",压缩后为 "compact"。model 字段包含模型标识符。如果使用 claude --agent <name> 启动 Claude Code,agent_type 字段会包含 Agent 名称。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-sonnet-4-5-20250929"
}
SessionStart 决策控制

钩子脚本输出到 stdout 的任何文本都会添加到 Claude 的上下文中。除了所有钩子都可用的 JSON 输出字段外,你还可以返回以下特定于事件的字段:

字段描述
additionalContext添加到 Claude 上下文中的字符串。多个钩子的值会合并
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}
持久化环境变量

SessionStart 钩子可以访问 CLAUDE_ENV_FILE 环境变量,该变量提供一个文件路径,你可以在此持久化环境变量,供后续 Bash 命令使用。

若要设置单个环境变量,请将 export 语句写入 CLAUDE_ENV_FILE。使用追加(>>)以保留其他钩子设置的变量:

#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0

若要捕获设置命令所做的所有环境更改,请比较导出的变量前后状态:

#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0

写入此文件的任何变量都会在会话期间由 Claude Code 执行的所有后续 Bash 命令中生效。

`CLAUDE_ENV_FILE` 可供 SessionStart 钩子使用。其他钩子类型无法访问此变量。
UserPromptSubmit

在用户提交提示词后、Claude 处理提示词前运行。你可以据此根据提示词或对话添加其他上下文、验证提示词,或阻止某些类型的提示词。

UserPromptSubmit 输入

除了通用输入字段外,UserPromptSubmit 钩子还会接收包含用户所提交文本的 prompt 字段。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}
UserPromptSubmit 决策控制

UserPromptSubmit 钩子可以控制是否处理用户提示词,并添加上下文。所有JSON 输出字段均可用。

在退出代码为 0 时,有两种方式可以向对话添加上下文:

  • 纯文本 stdout:写入 stdout 的任何非 JSON 文本都会作为上下文添加
  • 包含 additionalContext 的 JSON:使用下方的 JSON 格式以获得更多控制。additionalContext 字段会作为上下文添加

纯文本 stdout 会作为钩子输出显示在对话记录中。additionalContext 字段则会以更低调的方式添加。

要阻止提示词,请返回一个将 decision 设为 "block" 的 JSON 对象:

字段描述
decision"block" 会阻止处理提示词,并将其从上下文中移除。省略此字段则允许继续处理提示词
reason当 decision 为 "block" 时向用户显示。不添加到上下文中
additionalContext添加到 Claude 上下文中的字符串
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}
对于简单用例,不需要使用 JSON 格式。要添加上下文,可以在退出代码为 0 时将纯文本输出到 stdout。需要阻止提示词或进行更结构化的控制时,请使用 JSON。
PreToolUse

在 Claude 创建工具参数后、处理工具调用前运行。按工具名称匹配:Bash、Edit、Write、Read、Glob、Grep、Task、WebFetch、WebSearch,以及任何 MCP 工具名称。

使用 PreToolUse 决策控制来允许、拒绝或请求使用该工具的许可。

PreToolUse 输入

除通用输入字段外,PreToolUse 钩子还会接收 tool_name、tool_input 和 tool_use_id。tool_input 字段取决于工具:

Bash

执行 shell 命令。

字段类型示例描述
commandstring"npm test"要执行的 shell 命令
descriptionstring"Run test suite"命令功能的可选描述
timeoutnumber120000可选超时时间,单位为毫秒
run_in_backgroundbooleanfalse是否在后台运行命令
Write

创建或覆盖文件。

字段类型示例描述
file_pathstring"/path/to/file.txt"要写入文件的绝对路径
contentstring"file content"要写入文件的内容
Edit

替换现有文件中的字符串。

字段类型示例描述
file_pathstring"/path/to/file.txt"要编辑文件的绝对路径
old_stringstring"original text"要查找并替换的文本
new_stringstring"replacement text"替换文本
replace_allbooleanfalse是否替换所有匹配项
读取

读取文件内容。

FieldTypeExampleDescription
file_pathstring"/path/to/file.txt"要读取的文件的绝对路径
offsetnumber10可选的起始行号
limitnumber50可选的读取行数
Glob

查找与 glob 模式匹配的文件。

FieldTypeExampleDescription
patternstring"**/*.ts"用于匹配文件的 glob 模式
pathstring"/path/to/dir"可选的搜索目录。默认为当前工作目录
Grep

使用正则表达式搜索文件内容。

FieldTypeExampleDescription
patternstring"TODO.*fix"要搜索的正则表达式模式
pathstring"/path/to/dir"可选的搜索文件或目录
globstring"*.ts"可选的 glob 模式,用于筛选文件
output_modestring"content""content"、"files_with_matches" 或 "count"。默认为 "files_with_matches"
-ibooleantrue不区分大小写地搜索
multilinebooleanfalse启用多行匹配
WebFetch

获取并处理网页内容。

FieldTypeExampleDescription
urlstring"https://example.com/api"要获取内容的 URL
promptstring"Extract the API endpoints"要对获取的内容执行的提示词
WebSearch

搜索网络。

FieldTypeExampleDescription
querystring"react hooks best practices"搜索查询
allowed_domainsarray["docs.example.com"]可选:仅包含来自这些域名的结果
blocked_domainsarray["spam.example.com"]可选:排除来自这些域名的结果
Task

启动一个 子 Agent。

FieldTypeExampleDescription
promptstring"Find all API endpoints"Agent 要执行的任务
descriptionstring"Find API endpoints"任务的简短描述
subagent_typestring"Explore"要使用的专用 Agent 类型
modelstring"sonnet"可选的模型别名,用于覆盖默认模型
PreToolUse 决策控制

PreToolUse 钩子可以控制工具调用是否继续。与使用顶层 decision 字段的其他钩子不同,PreToolUse 会在 hookSpecificOutput 对象中返回决策。这让它能够进行更精细的控制:三种结果(允许、拒绝或询问),以及在执行前修改工具输入的能力。

字段说明
permissionDecision"allow" 绕过权限系统,"deny" 阻止工具调用,"ask" 提示用户确认
permissionDecisionReason对于 "allow" 和 "ask",内容会显示给用户,但不会显示给 Claude。对于 "deny",内容会显示给 Claude
updatedInput在执行前修改工具的输入参数。与 "allow" 配合可自动批准,或与 "ask" 配合向用户显示修改后的输入
additionalContext在工具执行前添加到 Claude 上下文中的字符串
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}
PreToolUse 之前使用顶层 `decision` 和 `reason` 字段,但这些字段已在此事件中弃用。请改用 `hookSpecificOutput.permissionDecision` 和 `hookSpecificOutput.permissionDecisionReason`。弃用值 `"approve"` 和 `"block"` 分别对应 `"allow"` 和 `"deny"`。PostToolUse 和 Stop 等其他事件仍继续使用顶层 `decision` 和 `reason` 作为当前格式。
PermissionRequest

在向用户显示权限对话框时运行。 使用 PermissionRequest 决策控制 代表用户允许或拒绝。

按工具名称匹配,取值与 PreToolUse 相同。

PermissionRequest 输入

PermissionRequest 钩子接收 tool_name 和 tool_input 字段,与 PreToolUse 钩子相同,但不包含 tool_use_id。可选的 permission_suggestions 数组包含用户通常会在权限对话框中看到的“始终允许”选项。两者的区别在于钩子的触发时机:即将向用户显示权限对话框时运行 PermissionRequest 钩子,而无论权限状态如何,工具执行前都会运行 PreToolUse 钩子。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    { "type": "toolAlwaysAllow", "tool": "Bash" }
  ]
}
PermissionRequest 决策控制

PermissionRequest 钩子可以允许或拒绝权限请求。除了所有钩子都可用的 JSON 输出字段 外,钩子脚本还可以返回一个 decision 对象,其中包含以下特定于此事件的字段:

字段说明
behavior"allow" 授予权限,"deny" 拒绝权限
updatedInput仅适用于 "allow":在执行前修改工具的输入参数
updatedPermissions仅适用于 "allow":应用权限规则更新,等同于用户选择“始终允许”选项
message仅适用于 "deny":告知 Claude 拒绝权限的原因
interrupt仅适用于 "deny":如果为 true,则停止 Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}
PostToolUse

在工具成功完成后立即运行。

按工具名称匹配,取值与 PreToolUse 相同。

PostToolUse 输入

PostToolUse 钩子会在工具成功执行后触发。输入包含 tool_input(发送给工具的参数)和 tool_response(工具返回的结果)。两者的具体架构取决于工具。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  },
  "tool_use_id": "toolu_01ABC123..."
}
PostToolUse 决策控制

PostToolUse 钩子可以在工具执行后向 Claude 提供反馈。除了所有钩子均可使用的 JSON 输出字段 外,钩子脚本还可以返回以下事件专属字段:

字段描述
decision"block" 会向 Claude 显示 reason。省略此字段则允许操作继续
reason当 decision 为 "block" 时,向 Claude 显示的说明
additionalContext供 Claude 考虑的附加上下文
updatedMCPToolOutput仅适用于 MCP 工具:将工具输出替换为提供的值
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}
PostToolUseFailure

工具执行失败时运行。对于抛出错误或返回失败结果的工具调用,此事件会触发。可用它记录失败、发送警报或向 Claude 提供纠正性反馈。

按工具名称匹配,取值与 PreToolUse 相同。

PostToolUseFailure 输入

PostToolUseFailure 钩子接收与 PostToolUse 相同的 tool_name 和 tool_input 字段,以及作为顶层字段的错误信息:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false
}
字段描述
error描述问题所在的字符串
is_interrupt可选布尔值,表示失败是否由用户中断导致
PostToolUseFailure 决策控制

PostToolUseFailure 钩子可以在工具失败后向 Claude 提供上下文。除了所有钩子均可使用的 JSON 输出字段 外,钩子脚本还可以返回以下事件专属字段:

字段描述
additionalContext供 Claude 结合错误信息一并考虑的附加上下文
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}
Notification

Claude Code 发送通知时运行。按通知类型匹配:permission_prompt、idle_prompt、auth_success、elicitation_dialog。省略匹配器则对所有通知类型运行钩子。

使用不同的匹配器,根据通知类型运行不同的处理程序。此配置会在 Claude 需要权限批准时触发专用的权限警报脚本,并在 Claude 处于空闲状态时发送另一条通知:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}
Notification 输入

除了通用输入字段外,Notification 钩子还会接收包含通知文本的 message、可选的 title,以及用于指明触发通知类型的 notification_type。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}

Notification 钩子无法阻止或修改通知。除了所有钩子均可使用的 JSON 输出字段 外,你还可以返回 additionalContext,为对话添加上下文:

字段描述
additionalContext添加到 Claude 上下文中的字符串
SubagentStart

通过 Task 工具生成 Claude Code 子 Agent 时运行。支持使用匹配器按 Agent 类型名称筛选(内置 Agent,如 Bash、Explore、Plan,或来自 .claude/agents/ 的自定义 Agent 名称)。

SubagentStart 输入

除了通用输入字段外,SubagentStart 钩子还会接收 agent_id(子 Agent 的唯一标识符)和 agent_type(Agent 名称,例如内置 Agent "Bash"、"Explore"、"Plan",或自定义 Agent 名称)。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}

SubagentStart 钩子无法阻止创建子 Agent,但可以向子 Agent 注入上下文。除了所有钩子都可用的 JSON 输出字段外,你还可以返回:

字段描述
additionalContext添加到子 Agent 上下文中的字符串
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}
SubagentStop

在 Claude Code 子 Agent 完成响应时运行。按 Agent 类型匹配,取值与 SubagentStart 相同。

SubagentStop 输入

除了通用输入字段外,SubagentStop 钩子还会接收 stop_hook_active、agent_id、agent_type 和 agent_transcript_path。agent_type 字段的值用于匹配器筛选。transcript_path 是主会话的记录文件,而 agent_transcript_path 是子 Agent 自己的记录文件,存储在嵌套的 subagents/ 文件夹中。

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl"
}

SubagentStop 钩子使用与 Stop 钩子相同的决策控制格式。

Stop

在 Claude Code 主 Agent 完成响应时运行。如果是由用户中断导致停止,则不会运行。

Stop 输入

除了通用输入字段外,Stop 钩子还会接收 stop_hook_active。当 Claude Code 因停止钩子而继续运行时,此字段的值为 true。请检查此值或处理记录文件,以防止 Claude Code 无限运行。

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
Stop 决策控制

Stop 和 SubagentStop 钩子可以控制 Claude 是否继续运行。除了所有钩子都可用的 JSON 输出字段外,钩子脚本还可以返回以下事件专属字段:

字段描述
decision"block" 可阻止 Claude 停止。省略此字段则允许 Claude 停止
reason当 decision 为 "block" 时必须提供。告知 Claude 应继续运行的原因
{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}
PreCompact

在 Claude Code 即将执行压缩操作之前运行。

匹配器的值表示压缩是手动触发还是自动触发:

匹配器触发时机
manual/compact
auto上下文窗口已满时自动压缩
PreCompact 输入

除了通用输入字段外,PreCompact 钩子还会接收 trigger 和 custom_instructions。对于 manual,custom_instructions 包含用户传入 /compact 的内容。对于 auto,custom_instructions 为空。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}
SessionEnd

在 Claude Code 会话结束时运行。适用于清理任务、记录会话统计信息或保存会话状态。支持匹配器,可按退出原因筛选。

钩子输入中的 reason 字段表示会话结束的原因:

原因描述
clear使用 /clear 命令清除会话
logout用户已登出
prompt_input_exit提示词输入可见时用户退出
bypass_permissions_disabled已禁用绕过权限模式
other其他退出原因
SessionEnd 输入

除了通用输入字段外,SessionEnd 钩子还会接收一个 reason 字段,用于说明会话结束的原因。有关所有取值,请参阅上方的 reason 表格。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

SessionEnd 钩子没有决策控制权。它们无法阻止会话终止,但可以执行清理任务。

基于提示词的钩子

除了 Bash 命令钩子(type: "command")外,Claude Code 还支持基于提示词的钩子(type: "prompt"),它们使用大模型评估是否允许或阻止某项操作。基于提示词的钩子适用于以下事件:PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、UserPromptSubmit、Stop 和 SubagentStop。

基于提示词的钩子的工作方式

基于提示词的钩子不会执行 Bash 命令,而是:

  1. 将钩子输入和你的提示词发送给 Claude 模型,默认使用 Haiku
  2. 大模型返回包含决策的结构化 JSON
  3. Claude Code 自动处理该决策
提示词钩子配置

将 type 设为 "prompt",并提供 prompt 字符串,而不是 command。使用 $ARGUMENTS 占位符,将钩子的 JSON 输入数据注入提示词文本。Claude Code 会将组合后的提示词和输入发送给一个快速 Claude 模型,由其返回 JSON 决策。

此 Stop 钩子会要求大模型评估所有任务是否已完成,然后再允许 Claude 结束:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
字段必填说明
type是必须为 "prompt"
prompt是发送给大模型的提示词文本。使用 $ARGUMENTS 作为钩子输入 JSON 的占位符。如果未包含 $ARGUMENTS,则会将输入 JSON 附加到提示词后
model否用于评估的模型。默认为快速模型
timeout否超时时间,单位为秒。默认值:30
响应架构

大模型必须返回包含以下内容的 JSON:

{
  "ok": true | false,
  "reason": "Explanation for the decision"
}
字段说明
oktrue 表示允许该操作,false 表示阻止该操作
reason当 ok 为 false 时必填。向 Claude 显示的说明
示例:多条件 Stop 钩子

此 Stop 钩子使用详细提示词,在允许 Claude 停止之前检查三个条件。如果 "ok" 为 false,Claude 会继续工作,并将提供的原因作为下一条指令。SubagentStop 钩子使用相同格式来评估子 Agent是否应该停止:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

基于 Agent 的钩子

基于 Agent 的钩子(type: "agent")类似于基于提示词的钩子,但具备多轮工具访问能力。Agent 钩子不会只调用一次大模型,而是会启动一个子 Agent,使其能够读取文件、搜索代码并检查代码库,以验证条件。Agent 钩子支持与基于提示词的钩子相同的事件。

Agent 钩子的工作原理

Agent 钩子触发时:

  1. Claude Code 会根据你的提示词和钩子的 JSON 输入启动一个子 Agent
  2. 子 Agent 可以使用 Read、Grep 和 Glob 等工具进行调查
  3. 最多经过 50 轮后,子 Agent 会返回结构化的 { "ok": true/false } 判定
  4. Claude Code 会像处理提示词钩子一样处理该判定

如果验证需要检查实际文件或测试输出,而不只是评估钩子输入数据,那么 Agent 钩子就很有用。

Agent 钩子配置

将 type 设为 "agent",并提供一个 prompt 字符串。配置字段与提示词钩子配置相同,但默认超时时间更长:

字段必填描述
type是必须为 "agent"
prompt是描述要验证内容的提示词。使用 $ARGUMENTS 作为钩子 JSON 输入的占位符
model否要使用的模型。默认为快速模型
timeout否超时时间,单位为秒。默认值:60

响应架构与提示词钩子相同:使用 { "ok": true } 允许操作,或使用 { "ok": false, "reason": "..." } 阻止操作。

此 Stop 钩子会验证所有单元测试是否通过,通过后才允许 Claude 结束:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

在后台运行钩子

默认情况下,钩子会阻塞 Claude 的执行,直到运行完毕。对于部署、测试套件或外部 API 调用等长时间运行的任务,可设置 "async": true,让钩子在后台运行,同时 Claude 继续工作。异步钩子无法阻止或控制 Claude 的行为:decision、permissionDecision 和 continue 等响应字段不会生效,因为它们原本要控制的操作已经完成。

配置异步钩子

在命令钩子的配置中添加 "async": true,即可在后台运行钩子而不阻塞 Claude。此字段仅适用于 type: "command" 钩子。

此钩子会在每次调用 Write 工具后运行测试脚本。run-tests.sh 最多执行 120 秒,期间 Claude 会立即继续工作。脚本运行完毕后,其输出会在下一轮对话中传递:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}

timeout 字段用于设置后台进程的最长运行时间,单位为秒。如果未指定,异步钩子与同步钩子一样,默认超时时间为 10 分钟。

异步钩子的执行方式

异步钩子触发时,Claude Code 会启动钩子进程,然后立即继续执行,不会等待进程结束。钩子通过 stdin 接收与同步钩子相同的 JSON 输入。

后台进程退出后,如果钩子生成的 JSON 响应中包含 systemMessage 或 additionalContext 字段,这些内容会在下一轮对话中作为上下文传递给 Claude。

示例:文件更改后运行测试

每当 Claude 写入文件时,此钩子都会在后台启动测试套件,并在测试结束后将结果报告给 Claude。将此脚本保存到项目中的 .claude/hooks/run-tests-async.sh,并使用 chmod +x 赋予其可执行权限:

#!/bin/bash
# run-tests-async.sh

# Read hook input from stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Only run tests for source files
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi

# Run tests and report results via systemMessage
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
  echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}"
else
  echo "{\"systemMessage\": \"Tests failed after editing $FILE_PATH: $RESULT\"}"
fi

然后将此配置添加到项目根目录下的 .claude/settings.json。async: true 标志可让 Claude 在测试运行期间继续工作:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}
限制

与同步钩子相比,异步钩子有几项限制:

  • 只有 type: "command" 钩子支持 async。基于提示词的钩子无法异步运行。
  • 异步钩子无法阻止工具调用或返回决策。钩子完成时,触发它的操作已经继续执行。
  • 钩子输出会在下一轮对话中传递。如果会话处于空闲状态,响应会等到下一次用户交互时才传递。
  • 每次执行都会创建一个单独的后台进程。同一个异步钩子多次触发时,不会进行去重。

安全注意事项

免责声明

钩子以系统用户的完整权限运行。

钩子会以你的完整用户权限执行 shell 命令。它们可以修改、删除或访问你的用户帐户可访问的任何文件。将钩子命令添加到配置之前,请先检查并测试所有命令。
安全最佳实践

编写钩子时,请牢记以下做法:

  • 验证并清理输入:切勿盲目信任输入数据
  • 始终为 shell 变量加引号:使用 "$VAR",而不是 $VAR
  • 阻止路径遍历:检查文件路径中是否包含 ..
  • 使用绝对路径:为脚本指定完整路径,项目根目录使用 "$CLAUDE_PROJECT_DIR"
  • 跳过敏感文件:避免处理 .env、.git/、密钥等内容

调试钩子

运行 claude --debug 查看钩子执行详情,包括哪些钩子匹配、它们的退出代码和输出。按 Ctrl+O 切换详细模式,在对话记录中查看钩子进度。

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

如需排查钩子未触发、Stop 钩子无限循环或配置错误等常见问题,请参阅指南中的限制和故障排除。

评论

支持 Markdown