主题
Hooks(钩子)
Hooks 是在 Claude Code 生命周期的特定事件点自动触发的用户自定义脚本/请求,用于观测、注入上下文或拦截/否决某些操作。
你的真实配置
你没写任何自定义 hooks(全局/项目 settings 里都没有),但你启用的插件自带 hooks 在跑:superpowers、ralph-loop。所以你其实已经在用 hooks,只是没自己配过。
更关键的一点 —— 你全局 defaultMode: "bypassPermissions":
权限弹窗被全部跳过了。 这意味着权限规则这道闸基本不拦你;此时 PreToolUse hook 几乎是你唯一的"硬拦截"安全闸。你 CLAUDE.md 里那些「git push / rm -rf / 改 AGENTS.md / 改 SpringBlade 启动配置 必须先确认」——目前只是给 Claude 的建议(CLAUDE.md 不强制),在 bypassPermissions 下尤其拦不住。要真正拦住,得用 hook。
去真实体验
| 想验证什么 | 这样做 |
|---|---|
| 看当前所有生效的 hook(含 superpowers/ralph-loop 插件的) | 输入 /hooks(只读浏览器) |
| 看 hook 怎么收数据 | hook 进程从 stdin 收 JSON,含 tool_input、cwd 等 |
| 加一个自己的 hook | 编辑 ~/.claude/settings.json 或项目 .claude/settings.json 的 hooks 字段 |
真实案例:把你 CLAUDE.md 的"谨慎执行"从建议升级成强制
你 CLAUDE.md 写了一串"必须先确认"的危险操作。在 bypassPermissions 下,用 PreToolUse hook 真正拦住它们。放进项目 .claude/settings.json:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/guard.sh" }
]
},
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/protect-agents.sh" }
]
}
]
}
}.claude/guard.sh(拦 git push / rm -rf,对应你 CLAUDE.md 的红线):
bash
#!/bin/bash
CMD=$(jq -r '.tool_input.command' < /dev/stdin)
if echo "$CMD" | grep -qiE 'git push|git reset --hard|rm -rf|git push --force'; then
jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"项目红线:该命令需人工执行,hook 已拦截"}}'
else
exit 0
fi.claude/protect-agents.sh(保护任意 AGENTS.md,对应你"改 AGENTS.md 需确认"):
bash
#!/bin/bash
F=$(jq -r '.tool_input.file_path // empty' < /dev/stdin)
if echo "$F" | grep -q 'AGENTS.md'; then
echo "AGENTS.md 受保护,需人工改" >&2
exit 2 # exit 2 = 阻断,stderr 回喂给 Claude
fi
exit 0
permissionDecision: "deny"和exit 2都能阻断 PreToolUse;前者更结构化、能给原因。记得chmod +x脚本。
真实案例:会话开始自动注入 SpringBlade 端口
既然你 AGENTS.md 的端口表默认没进上下文(见 Memory 节),可以用 SessionStart hook 每次开会话时把它塞进去:
json
{ "hooks": { "SessionStart": [ { "hooks": [
{ "type": "command", "command": "echo 'Gateway:15800 auth:8100 system:8106 Nacos:127.0.0.1:8848 登录:POST /blade-auth/token Tenant-Id:000000'" }
] } ] } }SessionStart 的 stdout 会作为 additionalContext 注入——比 @AGENTS.md 更可控(只塞你要的几行)。
官方文档要点
以下为按官方文档整理的系统性参考。
是什么
Hooks 是配置在 settings.json(或插件、Skill/Agent frontmatter)里的处理器,会在 Claude Code 运行过程中的特定事件(如 PreToolUse、UserPromptSubmit、Stop、SessionStart 等)被触发执行。处理器有 5 种类型:command(执行命令)、http(调用 HTTP 端点)、mcp_tool(调用 MCP 工具)、prompt(用小模型评估)、agent(用 agent 评估)。Hook 通过 stdin 收到 JSON 输入,通过 exit code 和 stdout 的 JSON 返回结果,从而能在事件点做日志、注入额外上下文、甚至阻断/否决工具调用与提示处理。
怎么工作
- 每个 hook 配置挂在某个事件名(EventName)下,事件触发时 Claude Code 按 matcher 匹配后执行对应处理器。
- 数据流:hook 进程通过 stdin 收到 JSON 输入(含 session_id、cwd、hook_event_name、permission_mode 以及该事件特有字段如 tool_input、prompt 等)。
- 返回方式有两种:(1) 退出码语义 —— exit 0 成功并解析 stdout 里的 JSON 输出;exit 2 是阻断错误,stderr 会被回喂给 Claude;其他非零是非阻断错误,只显示 stderr 第一行。(2) stdout 输出结构化 JSON 来做更细粒度的决策控制。
- matcher 匹配规则:''/''/省略 表示匹配全部;只含字母数字、_、| 时按精确字符串或竖线分隔列表匹配(如 Bash、Edit|Write);含其他字符时按 JavaScript 正则匹配(如 ^Notebook、mcp__memory__.)。
- 决策控制分两种形态:PreToolUse 用 hookSpecificOutput.permissionDecision(allow/deny/ask/defer);UserPromptSubmit/PostToolUse/Stop 等用顶层 decision:'block' + reason。
- exit 2 的可阻断性因事件而异:PreToolUse 阻断工具调用、UserPromptSubmit 阻断提示处理、Stop 阻止结束并继续对话;而 PostToolUse、SessionStart、Notification 等不可阻断,只把 stderr 显示出来。
- 注入上下文:SessionStart / UserPromptSubmit / Setup / SubagentStart 可通过 hookSpecificOutput.additionalContext 把内容注入到 Claude 的上下文。
- 配置文件被改动时由文件监听器(file watcher)自动重新加载,无需重启。
- 多来源 hook(User/Project/Local/Plugin/Session/Built-in)会合并;相同处理器会自动去重(command 按 command+args、http 按 URL)。
怎么配置 / 用法
配置位置(优先级从低到高):~/.claude/settings.json(user,全局)< .claude/settings.json(project,可提交)< .claude/settings.local.json(local,gitignore)< Managed policy settings(企业策略,覆盖一切);另有插件 hooks/hooks.json 与 Skill/Agent frontmatter 内联。
JSON 结构:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/scripts/check.sh", "timeout": 30 }
]
}
]
}
}command hook 典型:通过 stdin 读 JSON,用 jq 解析,输出 hookSpecificOutput 决定 deny:
bash
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Destructive command blocked"}}'
else exit 0; fiSkill/Agent frontmatter 内联:
yaml
hooks:
PreToolUse:
- matcher: "Tool"
hooks:
- type: command
command: "./script.sh"全局关闭:在 settings 里设 "disableAllHooks": true。运行时输入 /hooks 可打开只读浏览器查看所有事件与处理器(编辑只能改 JSON)。
什么时候用
- 何时用:需要在工具执行前做安全校验/拦截(PreToolUse 阻断 rm -rf)
- 何时用:会话开始时注入项目上下文或写环境变量(SessionStart + CLAUDE_ENV_FILE)
- 何时用:提交前/编辑后自动跑 lint、format、测试(PostToolUse / Stop)
- 何时用:把审计日志、通知(terminalSequence 桌面通知)接到外部系统(http hook)
- 别用:需要让 Claude 自己判断逻辑的业务行为——hook 是确定性的自动化,不该塞复杂业务逻辑
- 别用:依赖交互式终端的脚本——hook 进程没有 controlling terminal,不能开 /dev/tty
限制 / 坑
- Hook 输出字符串(含 additionalContext、systemMessage、纯 stdout)上限 10,000 字符,超出会存文件只显示预览+路径。
- Hook 进程无 controlling terminal(macOS/Linux v2.1.139+),不能打开 /dev/tty 或直接发转义序列,需用 systemMessage / terminalSequence 向用户输出。
- terminalSequence 有白名单:只允许 OSC 0/1/2、9、9;4、99、777 和裸 BEL;CSI 光标、颜色、OSC 8 超链接、OSC 52 剪贴板、OSC 1337 会被拒绝并忽略该字段。
- http hook 的 header 变量插值必须用 allowedEnvVars 显式声明,未列出的环境变量解析为空字符串。
- Windows 下 exec form(设了 args)不能跑 .cmd/.bat 垫片,需要真正的 .exe;建议用 node script.js 包装。
- Managed policy 的 allowManagedHooksOnly 会屏蔽 user/project/plugin hook(force-enabled 插件 hook 除外);disableAllHooks 也不会关掉 policy 级 hook。
- exit 2 并非所有事件都可阻断:StopFailure、PostToolUse、PermissionDenied、Notification、SessionStart、SessionEnd 等不可阻断。
- reloadSkills 只对 settings 里的 SessionStart hook 生效;skill hook 的 once: true 只在 skill frontmatter 内被认可。
硬事实速查(25 条)
- 配置文件:~/.claude/settings.json(user)、.claude/settings.json(project,可提交)、.claude/settings.local.json(local,gitignore)、Managed policy settings(企业,覆盖全部)、插件 hooks/hooks.json、Skill/Agent frontmatter。
- 优先级:local 覆盖 project 覆盖 user;managed policy 覆盖一切;插件 hook 在插件启用时合并。
- 处理器类型 type:command、http、mcp_tool、prompt、agent。
- 默认 timeout(秒):command/http/mcp_tool = 600,prompt = 30,agent = 60;在 UserPromptSubmit 下 command/http/mcp_tool/prompt 默认 30、agent 60。
- 通用输入字段:session_id、transcript_path、cwd、permission_mode(default|plan|acceptEdits|auto|dontAsk|bypassPermissions)、effort.level(low|medium|high|xhigh|max;ultracode 上报为 xhigh)、hook_event_name、agent_id、agent_type。
- 退出码:0 = 成功并解析 stdout JSON;2 = 阻断错误,stderr 回喂给 Claude;其他非零 = 非阻断错误,显示 stderr 第一行。
- PreToolUse 决策:hookSpecificOutput.permissionDecision = allow|deny|ask|defer,配 permissionDecisionReason。
- PermissionRequest 决策:hookSpecificOutput.decision.behavior = allow|deny(可带 updatedInput、appliedRules)。
- 顶层决策事件(UserPromptSubmit/UserPromptExpansion/PostToolUse/PostToolUseFailure/PostToolBatch/Stop/SubagentStop/ConfigChange/PreCompact)用 decision:"block" + reason。
- 通用输出字段:continue、stopReason、suppressOutput、systemMessage、terminalSequence、decision、reason、hookSpecificOutput。
- matcher:'*'/''/省略=全部;纯字母数字+_+|=精确或竖线列表(Bash、Edit|Write);含其他字符=JavaScript 正则。
- MCP 工具命名格式 mcp__<server><tool>,例 mcp__memory__create_entities;matcher 可写 mcp__memory.* 或 mcp__.__write.。
- 路径占位符:${CLAUDE_PROJECT_DIR}、${CLAUDE_PLUGIN_ROOT}、${CLAUDE_PLUGIN_DATA}。
- 环境变量:CLAUDE_PROJECT_DIR(所有 hook 进程)、CLAUDE_PLUGIN_ROOT/CLAUDE_PLUGIN_DATA(插件 hook)、CLAUDE_ENV_FILE(SessionStart/Setup/CwdChanged/FileChanged,写 export 持久化到 Bash)、CLAUDE_CODE_REMOTE(web 为 "true",CLI 未设)、CLAUDE_EFFORT(low|medium|high|xhigh|max;ultracode 上报为 xhigh)。
- 输出字符串上限 10,000 字符。
- 版本要求:terminalSequence 需 v2.1.141+;hook 无 controlling terminal 为 macOS/Linux v2.1.139+。
- 主要事件:SessionStart、Setup、UserPromptSubmit、UserPromptExpansion、PreToolUse、PermissionRequest、PermissionDenied、PostToolUse、PostToolUseFailure、PostToolBatch、Notification、MessageDisplay、SubagentStart、SubagentStop、TaskCreated、TaskCompleted、Stop、StopFailure、TeammateIdle、InstructionsLoaded、ConfigChange、CwdChanged、FileChanged、WorktreeCreate、WorktreeRemove、PreCompact、PostCompact、Elicitation、ElicitationResult、SessionEnd。
- SessionStart matcher:startup|resume|clear|compact;PreCompact/PostCompact matcher:manual|auto;SessionEnd 结束原因:clear|resume|logout|prompt_input_exit|bypass_permissions_disabled|other。
- Exec form(设 args)不经 shell 解释、特殊字符原样传;Shell form(省略 args)走 shell 分词/展开/管道,可用 shell:"powershell" 强制 PowerShell。
- 去重:command 按 command+args,http 按 URL,其他按相同处理器。
- 全局开关:"disableAllHooks": true;运行时 /hooks 打开只读浏览器。
- Stop hook 防死循环:连续 block 8 次无进展后 Claude Code 强制放行;脚本要解析 JSON 输入的
stop_hook_active,为 true 时exit 0提前退出;确需更多轮迭代用CLAUDE_CODE_STOP_HOOK_BLOCK_CAP提高上限。 if字段(permission rule 语法按工具名+参数过滤,比 matcher 更细)需 v2.1.85+,旧版忽略并每次都跑;仅对 PreToolUse/PostToolUse/PostToolUseFailure/PermissionRequest/PermissionDenied 生效,加到别的事件会令 hook 不运行。- prompt 型与 agent 型 hook 用
{"ok": true|false, "reason": ...}返回:ok=false 时 Stop/SubagentStop 把 reason 喂回让 Claude 继续、PreToolUse 则拒绝并把 reason 作工具错误返回;agent 型默认 60 秒超时、最多 50 个工具轮。 - 多 hook 命中同一事件:每个 hook 都会跑完(一个返回 deny 不会阻止其它 hook 执行);合并时权限决策取最严——deny 覆盖 ask 覆盖 allow;各 hook 的 additionalContext 全部保留并一起传给 Claude。