Skip to content

Hooks(钩子)

Hooks 是在 Claude Code 生命周期的特定事件点自动触发的用户自定义脚本/请求,用于观测、注入上下文或拦截/否决某些操作。

你的真实配置

没写任何自定义 hooks(全局/项目 settings 里都没有),但你启用的插件自带 hooks 在跑superpowersralph-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_inputcwd
加一个自己的 hook编辑 ~/.claude/settings.json 或项目 .claude/settings.jsonhooks 字段

真实案例:把你 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; fi

Skill/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。

官方出处:https://code.claude.com/docs/en/hooks