设置你的第一个 hook
要创建 hook,请将hooks 块添加到 设置文件。本演练创建一个桌面通知 hook,这样每当 Claude 等待你的输入而不是监视终端时,你都会收到警报。
将 hook 添加到你的设置
打开 如果你的设置文件已经有一个 你也可以通过在 CLI 中描述你想要的内容来要求 Claude 为你编写 hook。
~/.claude/settings.json 并添加一个 Notification hook。下面的示例使用 osascript 用于 macOS;有关 Linux 和 Windows 命令,请参阅 在 Claude 需要输入时获得通知。hooks 键,请将 Notification 作为现有事件键的同级添加,而不是替换整个对象。每个事件名称是单个 hooks 对象内的一个键:验证配置
输入
/hooks 打开 hooks 浏览器。你将看到所有可用 hook 事件的列表,每个配置了 hooks 的事件旁边都有一个计数。选择 Notification 以确认你的新 hook 出现在列表中。选择 hook 会显示其详细信息:事件、匹配器、类型、源文件和命令。你可以自动化什么
Hooks 让你在 Claude Code 生命周期中的关键点运行代码:编辑后格式化文件、在执行前阻止命令、在 Claude 需要输入时发送通知、在会话开始时注入上下文等。有关完整的 hook 事件列表,请参阅 Hooks 参考。 每个示例都包含一个现成的配置块,你可以将其添加到 设置文件。最常见的模式:在 Claude 需要输入时获得通知
每当 Claude 完成工作并需要你的输入时获得桌面通知,这样你可以切换到其他任务而无需检查终端。 此 hook 使用Notification 事件,当 Claude 等待输入或权限时触发。下面的每个选项卡使用平台的原生通知命令。将其添加到 ~/.claude/settings.json:
- macOS
- Linux
- Windows (PowerShell)
如果没有通知出现
如果没有通知出现
osascript 通过内置的 Script Editor 应用程序路由通知。如果 Script Editor 没有通知权限,命令会静默失败,macOS 不会提示你授予它。在 Terminal 中运行一次以使 Script Editor 出现在你的通知设置中:编辑后自动格式化代码
在 Claude 编辑的每个文件上自动运行 Prettier,以便格式保持一致而无需手动干预。 此 hook 使用带有Edit|Write 匹配器的 PostToolUse 事件,因此它仅在文件编辑工具之后运行。该命令使用 jq 提取编辑的文件路径并将其传递给 Prettier。将其添加到项目根目录中的 .claude/settings.json:
本页上的 Bash 示例使用
jq 进行 JSON 解析。使用 brew install jq(macOS)、apt-get install jq(Debian/Ubuntu)安装它,或参阅 jq 下载。阻止对受保护文件的编辑
防止 Claude 修改敏感文件,如.env、package-lock.json 或 .git/ 中的任何内容。Claude 会收到解释编辑被阻止原因的反馈,因此它可以调整其方法。
此示例使用 hook 调用的单独脚本文件。该脚本根据受保护模式列表检查目标文件路径,并以代码 2 退出以阻止编辑。
压缩后重新注入上下文
当 Claude 的上下文窗口填满时,压缩会总结对话以释放空间。这可能会丢失重要细节。使用带有compact 匹配器的 SessionStart hook 在每次压缩后重新注入关键上下文。
你的命令写入 stdout 的任何文本都会添加到 Claude 的上下文中。此示例提醒 Claude 项目约定和最近的工作。将其添加到项目根目录中的 .claude/settings.json:
echo,如 git log --oneline -5 来显示最近的提交。有关在每个会话开始时注入上下文,请考虑改用 CLAUDE.md。有关环境变量,请参阅参考中的 CLAUDE_ENV_FILE。
审计配置更改
跟踪会话期间设置或 skills 文件何时更改。ConfigChange 事件在外部进程或编辑器修改配置文件时触发,因此你可以记录更改以进行合规性检查或阻止未授权的修改。
此示例将每个更改附加到审计日志。将其添加到 ~/.claude/settings.json:
user_settings、project_settings、local_settings、policy_settings 或 skills。要阻止更改生效,以代码 2 退出或返回 {"decision": "block"}。有关完整的输入架构,请参阅 ConfigChange 参考。
当目录或文件更改时重新加载环境
某些项目根据你所在的目录设置不同的环境变量。direnv 之类的工具在你的 shell 中自动执行此操作,但 Claude 的 Bash 工具不会自动拾取这些更改。 配对SessionStart hook 和 CwdChanged hook 可以解决这个问题。SessionStart 加载你启动时所在目录的变量,CwdChanged 在 Claude 每次更改目录时重新加载它们。两者都写入 CLAUDE_ENV_FILE,Claude Code 在每个 Bash 命令之前作为脚本前导运行。将其添加到 ~/.claude/settings.json:
.envrc 的目录中运行一次 direnv allow,以便 direnv 被允许加载它。如果你使用 devbox 或 nix 而不是 direnv,相同的模式适用于 devbox shellenv 或 devbox global shellenv 代替 direnv export bash。
要对特定文件而不是每个目录更改做出反应,请使用 FileChanged 和 matcher 列出要监视的文件名,用 | 分隔。要构建监视列表,此值被分割为文字文件名而不是作为正则表达式进行评估。有关当文件更改时相同值如何也过滤哪些 hook 组运行,请参阅 FileChanged。此示例监视工作目录中 .envrc 和 .env 的更改:
watchPaths 输出和 CLAUDE_ENV_FILE 详情,请参阅 CwdChanged 和 FileChanged 参考条目。
自动批准特定权限提示
跳过你总是允许的工具调用的批准对话。此示例自动批准ExitPlanMode,这是 Claude 在完成呈现计划并要求继续时调用的工具,因此你不会在每次计划准备好时被提示。
与上面的退出代码示例不同,自动批准需要你的 hook 将 JSON 决策写入 stdout。PermissionRequest hook 在 Claude Code 即将显示权限对话时触发,返回 "behavior": "allow" 代表你回答它。
匹配器将 hook 的范围限制为仅 ExitPlanMode,因此没有其他提示受到影响。将其添加到 ~/.claude/settings.json:
updatedPermissions 数组,其中包含 setMode 条目。mode 值是任何权限模式,如 default、acceptEdits 或 bypassPermissions,destination: "session" 仅将其应用于当前会话。
bypassPermissions 仅在会话已启动时应用,具有绕过模式可用:--dangerously-skip-permissions、--permission-mode bypassPermissions、--allow-dangerously-skip-permissions 或 permissions.defaultMode: "bypassPermissions" 在设置中,且未被 permissions.disableBypassPermissionsMode 禁用。它永远不会作为 defaultMode 持久化。acceptEdits,你的 hook 将此 JSON 写入 stdout:
.* 或留下匹配器为空会自动批准每个权限提示,包括文件写入和 shell 命令。有关完整的决策字段集,请参阅 PermissionRequest 参考。
Hooks 如何工作
Hook 事件在 Claude Code 中的特定生命周期点触发。当事件触发时,所有匹配的 hooks 并行运行,相同的 hook 命令会自动去重。下表显示每个事件及其触发时间:| Event | When it fires |
|---|---|
SessionStart | When a session begins or resumes |
UserPromptSubmit | When you submit a prompt, before Claude processes it |
UserPromptExpansion | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
PreToolUse | Before a tool call executes. Can block it |
PermissionRequest | When a permission dialog appears |
PermissionDenied | When a tool call is denied by the auto mode classifier. Return {retry: true} to tell the model it may retry the denied tool call |
PostToolUse | After a tool call succeeds |
PostToolUseFailure | After a tool call fails |
PostToolBatch | After a full batch of parallel tool calls resolves, before the next model call |
Notification | When Claude Code sends a notification |
SubagentStart | When a subagent is spawned |
SubagentStop | When a subagent finishes |
TaskCreated | When a task is being created via TaskCreate |
TaskCompleted | When a task is being marked as completed |
Stop | When Claude finishes responding |
StopFailure | When the turn ends due to an API error. Output and exit code are ignored |
TeammateIdle | When an agent team teammate is about to go idle |
InstructionsLoaded | When a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a session |
ConfigChange | When a configuration file changes during a session |
CwdChanged | When the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv |
FileChanged | When a watched file changes on disk. The matcher field specifies which filenames to watch |
WorktreeCreate | When a worktree is being created via --worktree or isolation: "worktree". Replaces default git behavior |
WorktreeRemove | When a worktree is being removed, either at session exit or when a subagent finishes |
PreCompact | Before context compaction |
PostCompact | After context compaction completes |
Elicitation | When an MCP server requests user input during a tool call |
ElicitationResult | After a user responds to an MCP elicitation, before the response is sent back to the server |
SessionEnd | When a session terminates |
deny 的 PreToolUse hook 会取消工具调用,无论其他的返回什么。一个返回 ask 的 hook 会强制权限提示,即使其余的返回 allow。来自 additionalContext 的文本从每个 hook 保留并一起传递给 Claude。
每个 hook 都有一个 type 来确定它如何运行。大多数 hooks 使用 "type": "command",它运行 shell 命令。还有四种其他类型可用:
"type": "http":将事件数据 POST 到 URL。请参阅 HTTP hooks。"type": "mcp_tool":在已连接的 MCP 服务器上调用工具。请参阅 MCP tool hooks。"type": "prompt":单轮 LLM 评估。请参阅 基于提示的 hooks。"type": "agent":具有工具访问权限的多轮验证。代理 hooks 是实验性的,可能会改变。请参阅 基于代理的 hooks。
读取输入并返回输出
Hooks 通过 stdin、stdout、stderr 和退出代码与 Claude Code 通信。当事件触发时,Claude Code 将事件特定的数据作为 JSON 传递到脚本的 stdin。你的脚本读取该数据,完成其工作,并通过退出代码告诉 Claude Code 接下来要做什么。Hook 输入
每个事件都包含常见字段,如session_id 和 cwd,但每个事件类型添加不同的数据。例如,当 Claude 运行 Bash 命令时,PreToolUse hook 在 stdin 上接收类似以下内容:
UserPromptSubmit hooks 获取 prompt 文本,SessionStart hooks 获取 source(启动、恢复、清除、压缩),等等。有关共享字段,请参阅参考中的 常见输入字段,以及每个事件的部分了解事件特定的架构。
Hook 输出
你的脚本通过写入 stdout 或 stderr 并以特定代码退出来告诉 Claude Code 接下来要做什么。例如,一个想要阻止命令的PreToolUse hook:
- 退出 0:操作继续。对于
UserPromptSubmit、UserPromptExpansion和SessionStarthooks,你写入 stdout 的任何内容都会添加到 Claude 的上下文中。 - 退出 2:操作被阻止。写入原因到 stderr,Claude 会收到它作为反馈,以便它可以调整。
- 任何其他退出代码:操作继续。成绩单显示
<hook name> hook error通知,后跟 stderr 的第一行;完整的 stderr 进入 调试日志。
结构化 JSON 输出
退出代码给你两个选项:允许或阻止。为了获得更多控制,退出 0 并改为将 JSON 对象打印到 stdout。使用退出 2 以 stderr 消息阻止,或使用 JSON 退出 0 以获得结构化控制。不要混合它们:Claude Code 在你退出 2 时忽略 JSON。
PreToolUse hook 可以拒绝工具调用并告诉 Claude 为什么,或将其升级给用户以获得批准:
"deny",Claude Code 取消工具调用并将 permissionDecisionReason 反馈给 Claude。这些 permissionDecision 值特定于 PreToolUse:
"allow":跳过交互式权限提示。拒绝和询问规则,包括企业托管拒绝列表,仍然适用"deny":取消工具调用并将原因发送给 Claude"ask":照常向用户显示权限提示
"defer" 在 非交互模式 中使用 -p 标志时可用。它以保留的工具调用退出进程,以便 Agent SDK 包装器可以收集输入并恢复。请参阅参考中的 延迟工具调用以供稍后使用。
返回 "allow" 跳过交互式提示但不覆盖 权限规则。如果拒绝规则与工具调用匹配,即使你的 hook 返回 "allow",调用也会被阻止。如果询问规则匹配,用户仍然会被提示。这意味着来自任何设置范围的拒绝规则,包括 托管设置,总是优先于 hook 批准。
其他事件使用不同的决策模式。例如,PostToolUse 和 Stop hooks 使用顶级 decision: "block" 字段,而 PermissionRequest 使用 hookSpecificOutput.decision.behavior。有关按事件的完整分解,请参阅参考中的 摘要表。
对于 UserPromptSubmit hooks,改用 additionalContext 将文本注入到 Claude 的上下文中。基于提示的 hooks(type: "prompt")处理输出的方式不同:请参阅 基于提示的 hooks。
使用匹配器过滤 hooks
没有匹配器,hook 会在其事件的每次出现时触发。匹配器让你缩小范围。例如,如果你只想在文件编辑后运行格式化程序(而不是在每个工具调用后),将匹配器添加到你的PostToolUse hook:
"Edit|Write" 匹配器仅在 Claude 使用 Edit 或 Write 工具时触发,而不是在它使用 Bash、Read 或任何其他工具时触发。请参阅 匹配器模式 了解纯名称和正则表达式如何被评估。
每个事件类型在特定字段上匹配:
| 事件 | 匹配器过滤的内容 | 示例匹配器值 |
|---|---|---|
PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDenied | 工具名称 | Bash、Edit|Write、mcp__.* |
SessionStart | 会话如何启动 | startup、resume、clear、compact |
SessionEnd | 会话为什么结束 | clear、resume、logout、prompt_input_exit、bypass_permissions_disabled、other |
Notification | 通知类型 | permission_prompt、idle_prompt、auth_success、elicitation_dialog |
SubagentStart | 代理类型 | Bash、Explore、Plan 或自定义代理名称 |
PreCompact、PostCompact | 什么触发了压缩 | manual、auto |
SubagentStop | 代理类型 | 与 SubagentStart 相同的值 |
ConfigChange | 配置源 | user_settings、project_settings、local_settings、policy_settings、skills |
StopFailure | 错误类型 | rate_limit、authentication_failed、billing_error、invalid_request、server_error、max_output_tokens、unknown |
InstructionsLoaded | 加载原因 | session_start、nested_traversal、path_glob_match、include、compact |
Elicitation | MCP 服务器名称 | 你配置的 MCP 服务器名称 |
ElicitationResult | MCP 服务器名称 | 与 Elicitation 相同的值 |
FileChanged | 文字文件名来监视(请参阅 FileChanged) | .envrc|.env |
UserPromptExpansion | 命令名称 | 你的 skill 或命令名称 |
UserPromptSubmit、PostToolBatch、Stop、TeammateIdle、TaskCreated、TaskCompleted、WorktreeCreate、WorktreeRemove、CwdChanged | 不支持匹配器 | 始终在每次出现时触发 |
- 记录每个 Bash 命令
- 匹配 MCP 工具
- 在会话结束时清理
仅匹配
Bash 工具调用并将每个命令记录到文件。PostToolUse 事件在命令完成后触发,因此 tool_input.command 包含运行的内容。hook 在 stdin 上接收事件数据作为 JSON,jq -r '.tool_input.command' 仅提取命令字符串,>> 将其附加到日志文件:使用 if 字段按工具名称和参数过滤
if 字段需要 Claude Code v2.1.85 或更高版本。早期版本忽略它并在每个匹配的调用上运行 hook。if 字段使用 权限规则语法 按工具名称和参数一起过滤 hooks,因此 hook 进程仅在工具调用匹配时生成,或当 Bash 命令太复杂而无法解析时。这超越了 matcher,它仅在工具名称级别按组过滤。
例如,要仅在 Claude 使用 git 命令而不是所有 Bash 命令时运行 hook:
git * 匹配时生成,或当命令太复杂而无法解析为子命令时。对于像 npm test && git push 这样的复合命令,Claude Code 评估每个子命令并触发 hook,因为 git push 匹配。if 字段接受与权限规则相同的模式:"Bash(git *)"、"Edit(*.ts)" 等。要匹配多个工具名称,使用单独的处理程序,每个都有自己的 if 值,或在 matcher 级别匹配,其中支持管道交替。
if 仅适用于工具事件:PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest 和 PermissionDenied。将其添加到任何其他事件会阻止 hook 运行。
配置 hook 位置
你添加 hook 的位置决定了其范围:
在 Claude Code 中运行
/hooks 以浏览所有按事件分组的配置 hooks。要一次禁用所有 hooks,在设置文件中设置 "disableAllHooks": true。
如果你在 Claude Code 运行时直接编辑设置文件,文件监视器通常会自动拾取 hook 更改。
基于提示的 hooks
对于需要判断而不是确定性规则的决策,使用type: "prompt" hooks。Claude Code 不运行 shell 命令,而是将你的提示和 hook 的输入数据发送到 Claude 模型(默认为 Haiku)来做出决策。如果你需要更多功能,可以使用 model 字段指定不同的模型。
模型的唯一工作是返回一个是/否决策作为 JSON:
"ok": true:操作继续"ok": false:操作被阻止。模型的"reason"被反馈给 Claude,以便它可以调整。
Stop hook 询问模型是否所有请求的任务都已完成。如果模型返回 "ok": false,Claude 继续工作并使用 reason 作为其下一条指令:
基于代理的 hooks
当验证需要检查文件或运行命令时,使用type: "agent" hooks。与只进行单个 LLM 调用的提示 hooks 不同,代理 hooks 生成一个 subagent,它可以读取文件、搜索代码和使用其他工具来验证条件,然后返回决策。
代理 hooks 使用与提示 hooks 相同的 "ok" / "reason" 响应格式,但默认超时更长(60 秒)和最多 50 个工具使用轮次。
此示例验证在允许 Claude 停止之前测试通过:
HTTP hooks
使用type: "http" hooks 将事件数据 POST 到 HTTP 端点,而不是运行 shell 命令。端点接收命令 hook 在 stdin 上接收的相同 JSON,并使用相同的 JSON 格式通过 HTTP 响应体返回结果。
HTTP hooks 在你想要 web 服务器、云函数或外部服务处理 hook 逻辑时很有用:例如,一个跨团队记录工具使用事件的共享审计服务。
此示例将每个工具使用 POST 到本地日志服务:
hookSpecificOutput 字段。HTTP 状态代码本身无法阻止操作。
标头值支持使用 $VAR_NAME 或 ${VAR_NAME} 语法的环境变量插值。仅解析 allowedEnvVars 数组中列出的变量;所有其他 $VAR 引用保持为空。
有关完整的配置选项和响应处理,请参阅参考中的 HTTP hooks。
限制和故障排除
限制
- 命令 hooks 仅通过 stdout、stderr 和退出代码通信。它们无法触发
/命令或工具调用。通过additionalContext返回的文本被注入为 Claude 作为纯文本读取的系统提醒。HTTP hooks 改为通过响应体通信。 - Hook 超时默认为 10 分钟,可通过
timeout字段(以秒为单位)按 hook 配置。 PostToolUsehooks 无法撤销操作,因为工具已经执行。PermissionRequesthooks 不在 非交互模式(-p)中触发。对于自动化权限决策,使用PreToolUsehooks。Stophooks 在 Claude 完成响应时触发,而不仅仅在任务完成时。它们不在用户中断时触发。API 错误触发 StopFailure 代替。- 当多个 PreToolUse hooks 返回
updatedInput来重写工具的参数时,最后完成的获胜。由于 hooks 并行运行,顺序是非确定性的。避免有多个 hook 修改同一工具的输入。
Hooks 和权限模式
PreToolUse hooks 在任何权限模式检查之前触发。返回permissionDecision: "deny" 的 hook 会阻止工具,即使在 bypassPermissions 模式或使用 --dangerously-skip-permissions 时也是如此。这让你强制执行用户无法通过更改其权限模式来绕过的策略。
反面不成立:返回 "allow" 的 hook 不会绕过来自设置的拒绝规则。Hooks 可以收紧限制,但不能放松它们超过权限规则允许的范围。
Hook 未触发
Hook 已配置但从不执行。- 运行
/hooks并确认 hook 出现在正确的事件下 - 检查匹配器模式是否与工具名称完全匹配(匹配器区分大小写)
- 验证你是否触发了正确的事件类型(例如,
PreToolUse在工具执行前触发,PostToolUse在之后触发) - 如果在非交互模式(
-p)中使用PermissionRequesthooks,改用PreToolUse
Hook 输出中的错误
你在成绩单中看到类似”PreToolUse hook error: …”的消息。- 你的脚本意外以非零代码退出。通过管道传递示例 JSON 来手动测试它:
- 如果你看到”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用于项目 hooks,~/.claude/settings.json用于全局 hooks
Stop hook 永远运行
Claude 继续工作在无限循环中而不是停止。 你的 Stop hook 脚本需要检查它是否已经触发了继续。从 JSON 输入中解析stop_hook_active 字段,如果为 true 则提前退出:
JSON 验证失败
Claude Code 显示 JSON 解析错误,即使你的 hook 脚本输出有效的 JSON。 当 Claude Code 运行 hook 时,它生成一个 shell,该 shell 源你的配置文件(~/.zshrc 或 ~/.bashrc)。如果你的配置文件包含无条件的 echo 语句,该输出会被添加到你的 hook 的 JSON 前面:
$- 变量包含 shell 标志,i 表示交互式。Hooks 在非交互式 shell 中运行,因此 echo 被跳过。
调试技术
成绩单视图,使用Ctrl+O 切换,显示每个触发的 hook 的单行摘要:成功是无声的,阻止错误显示 stderr,非阻止错误显示 <hook name> hook error 通知,后跟 stderr 的第一行。
有关完整的执行详情,包括哪些 hooks 匹配、它们的退出代码、stdout 和 stderr,请阅读调试日志。使用 claude --debug-file /tmp/claude.log 启动 Claude Code 以写入已知路径,然后在另一个终端中 tail -f /tmp/claude.log。如果你启动时没有该标志,在会话中运行 /debug 以启用日志记录并找到日志路径。
了解更多
- Hooks 参考:完整的事件架构、JSON 输出格式、异步 hooks 和 MCP 工具 hooks
- 安全考虑:在共享或生产环境中部署 hooks 之前查看
- Bash 命令验证器示例:完整的参考实现