Claude Code Hooks 类型与使用指南实践整理

作者:袖梨 2026-08-19

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Claude Code Hooks 类型与使用指南”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际使用顺序,把思路、关键写法和容易踩坑的地方讲清楚,方便你直接对照操作。

在这个场景下,本文总结 Claude Code / 本仓库恢复版 claude-code 中 hooks 的类型、触发时机、典型采用场景、设置方式与注意事项。

从实现思路看,Hooks 是 Claude Code 在会话、工具调用、权限、压缩、子代理、任务等生命周期节点执行的自定义动作。它们适合做自动校验、权限治理、日志审计、上下文注入、格式化、通知和资源清理。

0. 整体流程图

整体流转可以理解为:会话启动后触发 SessionStart;用户提交 prompt 后、Claude 处理前触发 UserPromptSubmit;Claude 推理中如果要调用工具,先在权限弹窗前触发 PermissionRequest,再在工具真正执行前触发 PreToolUse;工具成功后触发 PostToolUse,失败后触发 PostToolUseFailure,权限被拒绝后触发 PermissionDenied;子代理、上下文压缩、通知和任务事件会在各自生命周期点触发;最后在回复结束前触发 Stop,会话退出时触发 SessionEnd

1. 设置位置

作用域设置文件或来源适用场景是否建议提交到仓库
用户级~/.claude/settings.json个人通用习惯,比如所有项目都禁止危险 Bash 命令
项目级.claude/settings.json团队共享规则,比如项目统一 formatter、lint、测试钩子
项目本地级.claude/settings.local.json个人在当前项目的本地设置,比如本机路径、本地通知脚本
插件级~/.claude/plugins/*/hooks/hooks.json由插件提供的通用 hook 能力
会话级运行时内存注册临时 hook,随会话结束清除

基础设置结构:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/check.py",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

2. Hook 执行类型

理解这一步时,Hook 事件描述“什么时候触发”,hook 执行类型描述“触发后用什么方式执行”。

执行类型关键字段什么时候采用如何采用注意事项
commandcommand, shell, timeout, statusMessage, once, async, asyncRewake, if需执行本地脚本或 shell 命令时;最常用从实现思路看,写一个脚本从 stdin 读取 JSON,输出文本或 JSON,再在 settings.json 设置 commandhook 可执行任意命令,只运行可信脚本;建议设置合理 timeout
promptprompt, model, timeout, statusMessage, once, if需用模型判断某个事件是否合规,或生成简短建议落到代码里,设置 prompt,让 Claude Code 把 hook 输入交给模型分析适合判断和文本分析,不适合强制性安全边界
agentprompt, model, timeout, statusMessage, once, if需启动代理做更完整的验证或检查设置 agent prompt,要求代理审查输入、输出结论成本和延迟更高;适合较重的检查
httpurl, headers, allowedEnvVars, timeout, statusMessage, once, if需把 hook 事件发送到外部服务,比如审计、通知、策略引擎结合项目来看,Claude Code 向 url POST hook 输入 JSON,服务得到 JSONHTTP hook 必须得到 JSON;不要把敏感信息发到不可信服务

callbackfunction 类型属于程序内或会话内注册机制,不适合直接写入持久化 settings.json

3. Hook 事件类型总表

Hook 类型触发时机什么时候采用如何采用常用 matcher
PreToolUseClaude 准备调用工具之前阻止危险命令、限制文件写入范围、校验工具输入、改写工具输入在这个场景下,设置在 hooks.PreToolUse 下,按工具名匹配;脚本读取 tool_name 和 tool_input,必要时得到 permissionDecision 或 updatedInput工具名,比如 Bash, Write, Edit, Read
PostToolUse工具调用成功之后自动格式化、运行 lint/test、记录工具结果、补充上下文、处理 MCP 工具输出结合项目来看,设置在 hooks.PostToolUse 下;脚本读取 tool_response 同时得到普通输出或结构化 JSON工具名
PostToolUseFailure工具调用失败之后收集失败诊断、记录错误、提示修复建议理解这一步时,设置在 hooks.PostToolUseFailure 下;脚本读取 error 字段工具名
PermissionRequestClaude Code 需权限决策时自动批准低风险命令、拒绝高风险命令、改写工具输入、接入组织策略在这个场景下,设置在 hooks.PermissionRequest 下,得到 hookSpecificOutput.decision.behavior 为 allow 或 deny,可附带 updatedInput工具名
PermissionDenied工具权限被拒绝后记录拒绝原因、通知用户、决定是否引导换方案结合项目来看,设置在 hooks.PermissionDenied 下,读取被拒绝的工具和原因工具名
NotificationClaude Code 发出通知时转发到桌面、Slack、企业 IM、日志系统设置在 hooks.Notification 下;按通知类型匹配notification_type
UserPromptSubmit用户提交 prompt 后、Claude 处理前注入项目上下文、审计用户输入、阻止违规请求、补充团队规则落到代码里,设置在 hooks.UserPromptSubmit 下;得到 additionalContext 可给模型增加上下文通常不按工具匹配
SessionStart会话开始、恢复、清空后重新进入等场景加载项目上下文、设置 watch paths、输出初始化提示在这个场景下,设置在 hooks.SessionStart 下;可得到 additionalContext, initialUserMessage, watchPathssource
SessionEnd会话结束、清空或退出时清理资源、保存状态、发送结束通知设置在 hooks.SessionEnd 下;脚本应很快完成reason
StopClaude 完成一次响应、即将停止时最后质量检查、检查待办未完成项、要求 Claude 继续修复设置在 hooks.Stop 下;得到阻塞结果可促使继续处理通常无 matcher
StopFailureStop hook 或停止流程失败时记录停止失败、诊断异常设置在 hooks.StopFailure 下,读取错误信息error
SubagentStart子代理启动时给子代理注入上下文、记录代理开始执行设置在 hooks.SubagentStart 下agent_type
SubagentStop子代理完成时校验代理输出、收集报告、阻止不合格结果进入主流程设置在 hooks.SubagentStop 下agent_type
PreCompact上下文压缩前保存关键状态、导出中间结论、阻止不合适的压缩设置在 hooks.PreCompact 下trigger
PostCompact上下文压缩后恢复关键上下文、重新注入摘要或提醒设置在 hooks.PostCompact 下trigger
Setup初始化或 setup 流程触发时初始化环境、准备上下文、执行一次性设置设置在 hooks.Setup 下trigger
TeammateIdle协作 agent 或 teammate 空闲时自动分派任务、提醒、状态检查设置在 hooks.TeammateIdle 下通常无 matcher
TaskCreated任务新建时记录任务、同步到外部系统、通知协作方设置在 hooks.TaskCreated 下通常无 matcher
TaskCompleted任务完成时结果校验、归档、通知、触发后续流程设置在 hooks.TaskCompleted 下通常无 matcher
ElicitationMCP elicitation 请求发起时自动接受、拒绝或取消 MCP 服务提出的问题设置在 hooks.Elicitation 下mcp_server_name
ElicitationResultMCP elicitation 得到结果时校验得到内容、审计用户/系统响应设置在 hooks.ElicitationResult 下mcp_server_name
ConfigChange设置发生变化时刷新缓存、记录设置变更、重新加载策略设置在 hooks.ConfigChange 下source
WorktreeCreate新建 worktree 时接管或参与 worktree 新建,并在得到路径前完成必要初始化在这个场景下,设置在 hooks.WorktreeCreate 下;必须借助 stdout 或 hookSpecificOutput.worktreePath 得到非空 worktree 路径通常无 matcher
WorktreeRemove删除 worktree 时清理资源、释放锁、删除临时文件设置在 hooks.WorktreeRemove 下通常无 matcher
InstructionsLoaded指令文件加载完成后审计或追加说明上下文、提示冲突规则设置在 hooks.InstructionsLoaded 下load_reason
CwdChanged当前工作目录变化时更新路径相关环境、刷新项目上下文设置在 hooks.CwdChanged 下通常无 matcher
FileChanged被的文件变化时自动刷新上下文、重新加载设置或规则在这个场景下,先借助 SessionStart 得到 watchPaths,再设置 FileChanged 处理变更文件 basename

4. 常用目标与建议 hook

目标建议 hook建议 matcher典型做法
阻止危险 Bash 命令PreToolUseBash在这个场景下,读取 tool_input.command,命中 rm -rf, git reset --hard, `curl
限制只能改某些目录PreToolUse`WriteEdit
编辑后自动格式化PostToolUse`WriteEdit
工具失败后自动诊断PostToolUseFailure`BashRead
自动批准低风险只读命令PermissionRequestBash对 git status, ls, pwd 等得到 hookSpecificOutput.decision.behavior: "allow"
用户输入后补充上下文UserPromptSubmit得到 additionalContext,比如项目当前规范或安全边界
会话启动时加载项目信息SessionStartsource得到项目说明、默认任务提醒、需的文件路径
响应结束前质量门禁Stop检查是否运行测试、是否完成待办;不满足则阻塞并说明原因
子代理完成后校验SubagentStopagent_type检查代理输出是否包含必需字段或是否发现高危问题
上下文压缩前保存状态PreCompacttrigger把关键任务状态写入文件或外部系统
任务完成后通知TaskCompleted发桌面通知、Webhook 或企业 IM 消息
接管 worktree 新建WorktreeCreate新建或初始化 worktree 后,得到最后 worktree 路径

5. Hook 输入协议

实际处理时,Claude Code 会把结构化 JSON 写入 hook 进程的 stdin

通用字段:

字段类型含义
session_idstring当前会话 ID
transcript_pathstring当前 transcript 文件路径
cwdstring当前工作目录
permission_modestring?当前权限模式,可能不存在
agent_idstring?子代理 ID,主线程通常不存在
agent_typestring?agent 类型,可能不存在
hook_event_namestring当前 hook 事件名

工具相关 hook 额外字段:

字段出现于含义
tool_namePreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied工具名
tool_input工具相关 hook工具输入
tool_responsePostToolUse工具成功输出
error失败类事件错误信息

示例脚本读取输入:

#!/usr/bin/env python3
import json
import sys

payload = json.load(sys.stdin)
print(json.dumps({"suppressOutput": True}))

6. Hook 输出协议

command hook 的 stdout 如果去掉空白后以 { 开头,会被当成 JSON 解析;否则按普通文本处理。http hook 必须得到 JSON。

通用输出字段:

字段类型作用
continuebooleanfalse 表示阻止 Claude 继续
suppressOutputboolean是否隐藏 hook stdout
stopReasonstringcontinue: false 时展示的停止原因
decisionstring常用值为 approve 或 block
reasonstring决策原因
systemMessagestring展示给用户的系统消息或警告
hookSpecificOutputobject针对具体 hook 的结构化输出

常用 hookSpecificOutput

Hook可用字段用法
PreToolUsepermissionDecision, permissionDecisionReason, updatedInput允许、拒绝或改写即将执行的工具输入
PermissionRequestdecision.behavior, decision.updatedInput, decision.message对权限请求得到允许或拒绝决策
UserPromptSubmitadditionalContext给 Claude 追加上下文
SessionStartadditionalContext, initialUserMessage, watchPaths初始化会话上下文和文件
PostToolUseupdatedMCPToolOutput更新 MCP 工具输出,仅对 MCP 工具有效
WorktreeCreateworktreePath理解这一步时,HTTP hook 得到新建好的 worktree 路径;command hook 可直接输出路径

权限请求允许示例:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {}
    }
  }
}

权限请求拒绝示例:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "deny",
      "message": "该命令不允许自动执行"
    }
  }
}

阻止危险命令示例:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "禁止执行破坏性 rm 命令"
  }
}

注入上下文示例:

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "本项目要求修改代码后运行相关测试。"
  }
}

会话启动示例:

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "已加载项目上下文。",
    "initialUserMessage": "请先阅读 README.md 和 AGENTS.md。",
    "watchPaths": ["/absolute/path/to/AGENTS.md"]
  }
}

7. 退出码语义

退出码行为什么时候采用
0成功hook 检查借助,或只输出信息
2阻塞错误,Claude 会收到 hook feedback需明确阻止当前动作或要求 Claude 修正
其他非零值非阻塞错误,通常展示给用户但不一定阻止流程hook 自身失败但不应中断主流程

也可以用 JSON 表达阻塞:

{
  "decision": "block",
  "reason": "该命令不允许执行"
}

8. Matcher 与条件

8.1 matcher

matcher 用来筛选 hook 是否执行。

写法含义示例
省略或 *匹配全部所有工具调用后都运行日志 hook
精确工具名只匹配单个工具Bash
管道分隔匹配多个工具`Write
正则表达式更灵活的匹配^mcp__.*

8.2 if 条件

if 是更细粒度的工具条件,适用来工具相关事件:

  • PreToolUse
  • PostToolUse
  • PostToolUseFailure
  • PermissionRequest

示例:

{
  "type": "command",
  "command": "python3 .claude/hooks/check-git.py",
  "if": "Bash(git *)"
}

9. 设置示例

9.1 禁止危险 Bash 命令

.claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/block-dangerous-bash.py",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

.claude/hooks/block-dangerous-bash.py

从实现思路看,接下来脚本只是最小演示,不能覆盖所有 shell 绕过方式;生产环境更建议 allowlist、命令解析器或组织级策略引擎。

#!/usr/bin/env python3
import json
import re
import sys
payload = json.load(sys.stdin)
command = payload.get("tool_input", {}).get("command", "")
blocked = [r"brms+-rfb", r"bgits+resets+--hardb", r"bgits+pushs+--forceb"]
if any(re.search(pattern, command) for pattern in blocked):
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": "检测到高风险命令,请先获得用户明确确认。"
        }
    }))
    sys.exit(0)
print(json.dumps({"suppressOutput": True}))

9.2 编辑后自动格式化

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/format-changed-file.py",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

9.3 会话开始时注入项目上下文

settings.json 里只设置 hook 什么时候运行、运行什么命令;hookSpecificOutput 是该命令执行后输出到 stdout 的 JSON,不是直接嵌在 settings.json 里的字段。

.claude/settings.json

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/session-context.py",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

matcher 匹配 source 字段,可用值如下所示:

source含义适用场景
startup新会话启动加载项目上下文、初始化提示
resume恢复旧会话恢复会话状态、提醒历史上下文
clear清空会话后重新开始重新注入基础规则
compact压缩上下文后继续重新注入关键摘要或状态

SessionStart hook 收到的输入 JSON 结构大致如下所示:

{
  "session_id": "session-id",
  "transcript_path": "/absolute/path/to/transcript.jsonl",
  "cwd": "/absolute/path/to/project",
  "permission_mode": "default",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "agent_type": "default",
  "model": "claude-sonnet-4-6"
}

字段说明:

字段类型必填说明
session_idstring当前会话 ID
transcript_pathstring当前 transcript 文件路径
cwdstring当前工作目录
permission_modestring当前权限模式
hook_event_name"SessionStart"固定为 SessionStart
source"startup" | "resume" | "clear" | "compact"SessionStart 的触发来源,也是 matcher 匹配字段
agent_typestring当前 agent 类型
modelstring当前会话采用的模型

脚本 stdout 得到的完整 hookSpecificOutput JSON 结构:

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "本仓库是 Claude Code 恢复版源码;修改前请优先阅读 README.md。",
    "initialUserMessage": "请先阅读 README.md 和 AGENTS.md,然后再开始任务。",
    "watchPaths": [
      "/absolute/path/to/project/README.md",
      "/absolute/path/to/project/AGENTS.md"
    ]
  }
}

SessionStart.hookSpecificOutput 字段说明:

字段类型必填作用
hookEventName"SessionStart"必须与当前 hook 事件一致,否则会被视为错误输出
additionalContextstring注入给 Claude 的额外上下文,适合放项目规则、恢复提示、环境说明
initialUserMessagestring作为会话开始时的初始用户消息,适合自动触发启动任务或提醒
watchPathsstring[]要的绝对路径;文件变化后可触发 FileChanged hook

最小脚本示例:

#!/usr/bin/env python3
import json
print(json.dumps({
    "hookSpecificOutput": {
        "hookEventName": "SessionStart",
        "additionalContext": "本项目要求修改代码后运行相关测试。"
    }
}))

9.4 用户提交 prompt 后追加规则

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/user-prompt-context.py",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

脚本输出:

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "如果要修改代码,请先检查现有测试和相关实现。"
  }
}

10. 关键注意事项

注意事项说明建议
Hooks 能执行任意命令.claude/settings.json 中的 hook 是代码执行入口只信任可信仓库与可信设置;审查外部 PR 中的 hook 变更
交互模式可能需 workspace trust未信任工作区时,交互模式下 hook 可能被跳过对安全边界不要只依赖 hook;结合权限模式和人工确认
同一事件的多个 hook 可能并行执行不应依赖 hook 之间的执行顺序需顺序时把逻辑放进同一个脚本
默认超时可能较长工具 hook 默认可能等待较久为每个 hook 显式设置 timeout
SessionEnd 要更快完成结束 hook 默认超时很短只做轻量清理;重任务放到后台或外b队列
stdout JSON 解析敏感stdout.trim() 以 { 开头会按 JSON 解析普通文本不要以 { 开头;结构化输出确保合法 JSON
HTTP hook 必须得到 JSON空响应会按 {} 处理,非 JSON 是错误外部服务统一得到 JSON envelope
HTTP hook 不适合所有事件SessionStart / Setup 等场景不适合 HTTP hook初始化类逻辑优先用 command
PreToolUse 可改写输入updatedInput 会影响即将执行的工具只做确定、安全、可审计的改写
PostToolUse 不应假设能改所有输出updatedMCPToolOutput 只对 MCP 工具有效普通工具结果用日志或上下文提示处理
async hook 后台运行可减少等待,但结果不会同步阻塞当前流程只用来通知、审计、异步归档等非关键路径
asyncRewake 会唤醒模型后台 hook 退出码为 2 时可注入阻塞反馈谨慎采用,避免噪音或循环唤醒
once 只运行一次执行后 hook 会被移除适合一次性初始化,不适合长期策略
shell 可设置兼容 bash 和 powershell跨平台项目要明确 shell 与路径差异

11. 设计建议

设计原则建议做法
安全优先从实现思路看,对破坏性操作采用 PreToolUse 或 PermissionRequest,但仍保留人工确认
更快失败hook 内部校验失败时输出清晰原因,不要静默失败
最小权限hook 只读取必要字段,只访问必要文件或外部服务
可观察关键 hook 记录事件、决策和原因,便于排查
不依赖顺序多个独立 hook 不共享隐式状态
保持轻量同步 hook 只做更快检查;耗时任务改为异步或外b队列
设置分层团队规则放项目级,个人偏好放用户级或本地级
明确边界不把 hook 当作唯一安全机制;配合权限模式、代码审查和测试

12. 速查表

你想做什么首选 hook执行类型得到什么
阻止命令执行PreToolUsecommandpermissionDecision: "deny"
自动批准权限PermissionRequestcommandhookSpecificOutput.decision.behavior: "allow"
改写工具输入PreToolUsecommandupdatedInput
编辑后格式化PostToolUsecommand普通输出或 suppressOutput
工具失败后提示PostToolUseFailurecommand / prompt文本建议或系统消息
增加用户 prompt 上下文UserPromptSubmitcommandadditionalContext
会话开始加载上下文SessionStartcommandadditionalContext, initialUserMessage, watchPaths
响应结束前检查Stopcommand / agentdecision: "block" 或退出码 2
通知外部系统Notification / TaskCompletedhttp / command{} 或通知结果
压缩前保存状态PreCompactcommand成功状态或阻塞原因
新建 worktreeWorktreeCreatecommand / httpstdout 路径或 hookSpecificOutput.worktreePath

结合项目来看,总的来说,Claude这部分内容适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。

相关文章

精彩推荐