关于 create-hook
create-hook 是面向 Claude Code 的技能,可分析项目工具配置、建议合适的钩子,并创建和测试钩子,帮助开发者自动执行工作流检查。
主要功能
- 01检测项目工具和现有钩子
- 02根据项目配置建议钩子
- 03创建脚本并更新设置
- 04测试成功与失败场景
适用场景
- 01编辑后运行类型检查或格式化
- 02提交前验证测试或构建
- 03阻止可能暴露机密的操作
- 04将问题作为可操作上下文传给 Claude Code
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ neolabhq/ context-engineering-kit --skill create-hook安装后在 Claude Code 中输入/create-hook确认已加载。
详细介绍
创建 Hook 命令
分析项目、建议实用的 Hook,并在充分测试后创建它们。
你的任务(/create-hook)
- 分析环境 - 检测工具和现有 Hook
- 建议 Hook - 根据项目配置提出建议
- 配置 Hook - 提出针对性问题并创建脚本
- 测试并验证 - 确保 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 应该做什么?” 并根据分析结果提供相关建议。
然后根据用户的描述了解上下文,只询问你不确定的细节:
-
触发时机:它应该何时运行?
PreToolUse:文件操作之前(可以阻止操作)PostToolUse:文件操作之后(提供反馈/修复)UserPromptSubmit:处理请求之前- 根据需要使用其他事件类型
-
工具匹配器:哪些工具应该触发它?(
Write、Edit、Bash、*等) -
适用范围:
global、project或project-local -
响应方式:
- 仅使用退出码:简单(退出码为 0 = 成功,PreToolUse 中退出码为 2 = 阻止操作)
- JSON 响应:高级控制(阻止操作、上下文、决策)
- 根据复杂度提供建议:简单的通过/失败 → 退出码,丰富的反馈 → JSON
-
阻止行为(如适用):“发现问题时是否应停止操作?”
- PreToolUse:可以阻止操作(安全、验证)
- PostToolUse:通常只提供反馈
-
Claude 集成(重要):“Claude Code 是否应该自动看到并修复此 Hook 检测到的问题?”
- 如果是:使用
additionalContext传递错误信息 - 如果否:使用
suppressOutput: true静默运行
- 如果是:使用
-
上下文干扰:“为避免产生干扰,成功的操作是否应该保持静默?”
- 格式化、例行检查建议选“是”
- 安全警报、严重错误建议选“否”
-
文件筛选:此 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. 测试与验证
重要:同时测试正常和异常路径:
正常路径测试:
- 测试预期的成功场景——创建 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 等待你输入时,你都会收到提醒,而无需一直盯着终端。
<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 进行配置:
编辑后自动格式化代码
每当 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 | 工具调用失败后 |
Notification | Claude Code 发送通知时 |
SubagentStart | 子 Agent 启动时 |
SubagentStop | 子 Agent 完成时 |
Stop | Claude 完成响应时 |
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和SessionStartHook,写入 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 |
SubagentStart | Agent 类型 | Bash、Explore、Plan 或自定义 Agent 名称 |
PreCompact | 触发压缩的原因 | manual、auto |
UserPromptSubmit、Stop | 不支持匹配器 | 每次发生时都会触发 |
SubagentStop | Agent 类型 | 与 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 配置。 PostToolUsehook 无法撤销操作,因为工具已经执行完毕。PermissionRequesthook 在非交互模式(-p)下不会触发。请使用PreToolUsehook 自动处理权限决策。Stophook 会在 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,查看完整的执行详情,包括哪些钩子匹配以及它们的退出代码。
了解更多
- 钩子参考:完整的事件架构、JSON 输出格式、异步钩子和 MCP 工具钩子
- 安全注意事项:在共享环境或生产环境中部署钩子前,请先查看
- Bash 命令验证器示例:完整的参考实现
文档索引
获取完整文档索引:https://code.claude.com/docs/llms.txt 使用此文件,在继续探索之前查找所有可用页面。
钩子参考
如需查看包含示例的快速入门指南,请参阅[使用钩子自动化工作流](/en/hooks-guide)。Claude Code 钩子事件、配置架构、JSON 输入/输出格式、退出代码、异步钩子、提示词钩子和 MCP 工具钩子的参考。
钩子是用户定义的 shell 命令或大模型提示词,会在 Claude Code 生命周期的特定时间点自动执行。请使用本参考查阅事件架构、配置选项、JSON 输入/输出格式,以及异步钩子和 MCP 工具钩子等高级功能。如果你是首次设置钩子,请先阅读指南。
Hook 生命周期
Hook 会在 Claude Code 会话中的特定时间点触发。当事件触发且匹配器匹配时,Claude Code 会将有关该事件的 JSON 上下文传递给 Hook 处理程序。对于命令 Hook,这些内容会通过标准输入(stdin)传入。随后,处理程序可以检查输入、采取操作,并选择是否返回决策。有些事件每个会话只触发一次,另一些则会在 Agent 循环中反复触发:
下表概述了各事件的触发时机。Hook 事件一节详细说明了每个事件的完整输入架构和决策控制选项。
| Event | When it fires |
|---|---|
SessionStart | 会话开始或恢复时 |
UserPromptSubmit | 提交提示词时,在 Claude 处理之前 |
PreToolUse | 工具调用执行之前。可以阻止该调用 |
PermissionRequest | 权限对话框出现时 |
PostToolUse | 工具调用成功后 |
PostToolUseFailure | 工具调用失败后 |
Notification | Claude Code 发送通知时 |
SubagentStart | 子 Agent 启动时 |
SubagentStop | 子 Agent 完成时 |
Stop | Claude 完成响应时 |
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"。接下来会发生以下情况:
```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 设置文件中定义。配置分为三层:
如需查看完整的操作流程和带注释的示例,请参阅上文的 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 |
SubagentStart | Agent 类型 | Bash、Explore、Plan 或自定义 Agent 名称 |
PreCompact | 触发压缩的原因 | manual、auto |
SubagentStop | Agent 类型 | 与 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}:插件的根目录,适用于随插件打包的脚本。
```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字段,其值必须设为事件名称。
| 字段 | 默认值 | 说明 |
|---|---|---|
continue | true | 如果为 false,钩子运行后 Claude 将完全停止处理。其优先级高于任何事件专属决策字段 |
stopReason | 无 | 当 continue 为 false 时向用户显示的消息。不会显示给 Claude |
suppressOutput | false | 如果为 true,则在详细模式输出中隐藏标准输出 |
systemMessage | 无 | 向用户显示的警告消息 |
无论事件类型如何,都要让 Claude 完全停止:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
决策控制
并非所有事件都支持通过 JSON 阻止或控制行为。支持此功能的事件会使用不同的字段来表达决策。编写钩子前,可将此表作为速查表:
| 事件 | 决策模式 | 关键字段 |
|---|---|---|
| UserPromptSubmit、PostToolUse、PostToolUseFailure、Stop、SubagentStop | 顶层 decision | decision: "block"、reason |
| PreToolUse | hookSpecificOutput | permissionDecision(allow/deny/ask)、permissionDecisionReason |
| PermissionRequest | hookSpecificOutput | decision.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 命令。
| 字段 | 类型 | 示例 | 描述 |
|---|---|---|---|
command | string | "npm test" | 要执行的 shell 命令 |
description | string | "Run test suite" | 命令功能的可选描述 |
timeout | number | 120000 | 可选超时时间,单位为毫秒 |
run_in_background | boolean | false | 是否在后台运行命令 |
Write
创建或覆盖文件。
| 字段 | 类型 | 示例 | 描述 |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | 要写入文件的绝对路径 |
content | string | "file content" | 要写入文件的内容 |
Edit
替换现有文件中的字符串。
| 字段 | 类型 | 示例 | 描述 |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | 要编辑文件的绝对路径 |
old_string | string | "original text" | 要查找并替换的文本 |
new_string | string | "replacement text" | 替换文本 |
replace_all | boolean | false | 是否替换所有匹配项 |
读取
读取文件内容。
| Field | Type | Example | Description |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | 要读取的文件的绝对路径 |
offset | number | 10 | 可选的起始行号 |
limit | number | 50 | 可选的读取行数 |
Glob
查找与 glob 模式匹配的文件。
| Field | Type | Example | Description |
|---|---|---|---|
pattern | string | "**/*.ts" | 用于匹配文件的 glob 模式 |
path | string | "/path/to/dir" | 可选的搜索目录。默认为当前工作目录 |
Grep
使用正则表达式搜索文件内容。
| Field | Type | Example | Description |
|---|---|---|---|
pattern | string | "TODO.*fix" | 要搜索的正则表达式模式 |
path | string | "/path/to/dir" | 可选的搜索文件或目录 |
glob | string | "*.ts" | 可选的 glob 模式,用于筛选文件 |
output_mode | string | "content" | "content"、"files_with_matches" 或 "count"。默认为 "files_with_matches" |
-i | boolean | true | 不区分大小写地搜索 |
multiline | boolean | false | 启用多行匹配 |
WebFetch
获取并处理网页内容。
| Field | Type | Example | Description |
|---|---|---|---|
url | string | "https://example.com/api" | 要获取内容的 URL |
prompt | string | "Extract the API endpoints" | 要对获取的内容执行的提示词 |
WebSearch
搜索网络。
| Field | Type | Example | Description |
|---|---|---|---|
query | string | "react hooks best practices" | 搜索查询 |
allowed_domains | array | ["docs.example.com"] | 可选:仅包含来自这些域名的结果 |
blocked_domains | array | ["spam.example.com"] | 可选:排除来自这些域名的结果 |
Task
启动一个 子 Agent。
| Field | Type | Example | Description |
|---|---|---|---|
prompt | string | "Find all API endpoints" | Agent 要执行的任务 |
description | string | "Find API endpoints" | 任务的简短描述 |
subagent_type | string | "Explore" | 要使用的专用 Agent 类型 |
model | string | "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 命令,而是:
- 将钩子输入和你的提示词发送给 Claude 模型,默认使用 Haiku
- 大模型返回包含决策的结构化 JSON
- 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"
}
| 字段 | 说明 |
|---|---|
ok | true 表示允许该操作,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 钩子触发时:
- Claude Code 会根据你的提示词和钩子的 JSON 输入启动一个子 Agent
- 子 Agent 可以使用 Read、Grep 和 Glob 等工具进行调查
- 最多经过 50 轮后,子 Agent 会返回结构化的
{ "ok": true/false }判定 - 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 钩子无限循环或配置错误等常见问题,请参阅指南中的限制和故障排除。