文档
推荐给好友,福利领不停!好友同步开通最高 1000 万词元额度 · 后续消费分佣最高 30%。
+50万 Token生成链接

Hooks

在 CrabCode 关键事件触发时跑你自己的逻辑:shell 命令、HTTP 回调、LLM 验证。配置走 settings.json。

是什么

Hook 是挂在 CrabCode 生命周期事件上的回调。事件触发时,CrabCode 把一段 JSON 通过 stdin(或 HTTP POST)喂给你的钩子;返回值决定后续动作——放行、阻止、修改输入、或者把额外上下文喂回模型。

四种钩子类型:

type形态适合
command跑一个 shell 命令跑 lint、写日志、阻止危险操作
httpPOST 到一个 URL集中式审计、转发到 SaaS
prompt用小模型评一段 prompt轻量语义判断("这次写的是 secret 吗")
agent起一个 agentic verifier复杂任务后验证("测试真的跑过了吗")

何时会看到这个文档

  • /hooks 命令的"添加"页面(只读,提示你直接编辑 settings.json)
  • settings.json 校验报错指向 hooks 字段

支持的事件

事件何时触发
SessionStart会话启动
SessionEnd会话结束(超时极紧,默认 1.5s)
Setup--init / --maintenance 启动时一次
UserPromptSubmit用户回车提交输入
PreToolUse模型请求调用工具,在执行前
PostToolUse工具执行成功后
PostToolUseFailure工具执行失败 / 超时 / 中断后
Stop / StopFailure模型回合结束 / 因 API 错误结束
Notification等待用户输入等系统通知
SubagentStart / SubagentStop子智能体启停
PermissionRequest / PermissionDenied权限询问 / 拒绝
PreCompact / PostCompact对话压缩前 / 后
CwdChanged / FileChanged工作目录或被监视文件变化
WorktreeCreate / WorktreeRemovegit worktree 创建 / 删除
Elicitation / ElicitationResultMCP elicitation 流程
TeammateIdle / TaskCreated / TaskCompleted多智能体协作场景
ConfigChange / InstructionsLoadedsettings 变更 / CRABCODE.md 加载

完整可用事件以 /hooks 里的下拉列表为准——SDK 与运行时同源。

配置示例

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.crabcode/hooks/check-bash.sh",
            "if": "Bash(git push *)",
            "timeout": 5
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write \"$FILE_PATH\"" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "验证最近的改动跑过了测试,否则报错。",
            "timeout": 60
          }
        ]
      }
    ]
  }
}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.crabcode/hooks/check-bash.sh",
            "if": "Bash(git push *)",
            "timeout": 5
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write \"$FILE_PATH\"" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "验证最近的改动跑过了测试,否则报错。",
            "timeout": 60
          }
        ]
      }
    ]
  }
}
  • matcher:字符串/正则,匹配 hook 事件相关的值(PreToolUse/PostToolUse 时匹配工具名)。省略 = 匹配所有
  • if:用权限规则语法过滤(如 Bash(git push *)),不匹配就不 spawn,省 fork 成本
  • timeout:单次执行秒数,超时会被 kill
  • shell:command 类型可选 bash / powershell(默认 bash,跟随 $SHELL

Hook 协议(command 类型)

  • stdin:JSON 对象,事件特定字段。常见键:
    • tool_nametool_inputtool_use_id(PreToolUse / PostToolUse / PostToolUseFailure)
    • session_idhook_event_name
    • PostToolUse 还有 response;PostToolUseFailure 还有 errorerror_typeis_interruptis_timeout
    • PreCompact 有压缩详情;UserPromptSubmit 有原始 prompt 文本
  • stdout:可选 JSON 响应。常用形态:
    • {"continue": false, "stopReason": "..."} 终止本轮
    • {"decision": "block", "reason": "..."} 阻止动作
    • {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "..."}} 显式拒绝权限
    • {"async": true, "asyncTimeout": 30} 切换为后台异步
  • 退出码语义(每个事件略有差异,以 /hooks 内说明为准):
    • 0 — 通过;stdout 内容按事件决定是否展示
    • 2 — 阻塞 / 把 stderr 反馈给模型;常用于 PreToolUse、UserPromptSubmit、Stop
    • 其他 — 报错给用户,会话继续

HTTP 钩子把同样的 JSON POST 到 urlheaders 支持 $VAR_NAME 占位符引用 allowedEnvVars 白名单里的环境变量。

完整 stdin / stdout schema

stdin 公共字段(每个事件都有):session_idhook_event_namecwdtranscript_path(当前会话 transcript 文件路径)。

事件特定字段

  • PreToolUse / PostToolUse / PostToolUseFailuretool_nametool_inputtool_use_idPostToolUseresponsePostToolUseFailureerror / error_type / is_interrupt / is_timeout
  • UserPromptSubmitprompt(用户输入原文)
  • PreCompact / PostCompacttriggercustom_instructions
  • FileChangedchanged_pathschange_kind
  • PermissionRequest:待批的工具 / 权限规则
  • InstructionsLoadedfile_pathmemory_type(User / Project / Local / Managed)、load_reason(session_start / nested_traversal / path_glob_match / include / compact)、globs?trigger_file_path?parent_file_path?

stdout 响应字段(全部可选;不输出 = 默认通过):continuefalse 终止本轮)、suppressOutput(隐藏 stdout)、stopReasondecision"approve" / "block")、reasonsystemMessage(用户侧警告)、async + asyncTimeout(后台异步)、hookSpecificOutput(事件维度高级控制)。

hookSpecificOutput 常用形态

  • PreToolUsepermissionDecision (allow / deny / ask)、permissionDecisionReasonupdatedInput(改写工具入参)、additionalContext
  • UserPromptSubmit / SessionStart / Setup / SubagentStart / PostToolUse / PostToolUseFailure / NotificationadditionalContext
  • SessionStart / CwdChanged / FileChangedwatchPaths(注册要监听的绝对路径)
  • PermissionRequestdecision: { behavior: "allow" | "deny", ... }
  • Elicitation / ElicitationResultaction (accept / decline / cancel) + content
  • WorktreeCreateworktreePath

Hook 失败行为

CrabCode 对 hook 失败是软失败

  • 退出码 0 → 通过
  • 退出码 2 → 阻塞 / 把 stderr 反馈给模型(PreToolUse / UserPromptSubmit / Stop / TeammateIdle / TaskCreated / TaskCompleted 等)
  • 其他非零 → 标记为 non_blocking_error:用户看到一条系统提示(带命令名与 stderr),会话不会中断
  • 超时 → 进程被 kill,按非零退出处理
  • JSON 输出格式错 → 视作 non_blocking_error,stderr 提示 schema 错误

只有退出码 2(或显式 {"decision":"block"} / permissionDecision:"deny")才会真的拦住模型;其他失败模式只打日志、继续走。

调试

  • 打开 debug 日志:用 --debug 启动 CrabCode,会把 hook 执行细节(命令行、stdout、stderr、退出码、耗时)写进 ~/.crabcode/debug/<session-id>.txt(用 CRABCODE_DEBUG_LOGS_DIR 改路径;-d2e / --debug-to-stderr 直输 stderr)
  • 看历史最近一条~/.crabcode/debug/latest 是符号链接,永远指向上一次 debug 日志
  • 看 hook 视角的 stdin:在 hook 脚本里 cat > /tmp/hook-input.json 然后 exit 0,离线分析 JSON 结构
  • hook 卡住:CrabCode 主进程对单个 hook 默认 TOOL_HOOK_EXECUTION_TIMEOUT_MS = 10minSessionEnd 默认 1.5s(用 CRABCODE_SESSIONEND_HOOKS_TIMEOUT_MS 调);超时直接 kill
  • HTTP hook 失败排查:把 url 临时换成 httpbin.org/post 或本地 listener,确认 payload 形状;headers 里的 $VAR_NAME 只解析 allowedEnvVars 白名单里的变量

真实示例

示例 1:拦截 git push --force 到 main——PreToolUse 上挂一个 hook,读 stdin 取 tool_input.command,匹配到 force-push 就 exit 2

bash
# ~/.crabcode/hooks/guard-force-push.sh
#!/usr/bin/env bash
payload=$(cat)
cmd=$(echo "$payload" | jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'git push.*(--force|-f)\s.*(main|master)'; then
  echo "禁止 force-push 到 main/master" >&2
  exit 2
fi
# ~/.crabcode/hooks/guard-force-push.sh
#!/usr/bin/env bash
payload=$(cat)
cmd=$(echo "$payload" | jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'git push.*(--force|-f)\s.*(main|master)'; then
  echo "禁止 force-push 到 main/master" >&2
  exit 2
fi

配上 settings.json

json
{ "hooks": { "PreToolUse": [
  { "matcher": "Bash", "hooks": [
    { "type": "command", "command": "~/.crabcode/hooks/guard-force-push.sh", "timeout": 3 }
  ]}
]}}
{ "hooks": { "PreToolUse": [
  { "matcher": "Bash", "hooks": [
    { "type": "command", "command": "~/.crabcode/hooks/guard-force-push.sh", "timeout": 3 }
  ]}
]}}

示例 2:写后自动 format——PostToolUseEdit|Write,从 tool_input.file_path 拿到刚写的文件路径丢 prettier:

json
{ "hooks": { "PostToolUse": [
  { "matcher": "Edit|Write", "hooks": [
    { "type": "command",
      "command": "FILE=$(jq -r '.tool_input.file_path'); [ -n \"$FILE\" ] && prettier --write \"$FILE\" 2>&1 || true",
      "timeout": 10 }
  ]}
]}}
{ "hooks": { "PostToolUse": [
  { "matcher": "Edit|Write", "hooks": [
    { "type": "command",
      "command": "FILE=$(jq -r '.tool_input.file_path'); [ -n \"$FILE\" ] && prettier --write \"$FILE\" 2>&1 || true",
      "timeout": 10 }
  ]}
]}}

限制与注意

  • PreToolUse 默认 10 分钟超时,但执行期间会卡住会话——别在里面跑重活
  • 绝对路径优先~ 在某些环境下不展开
  • 谨慎放权:hook 跑在你的 shell 用户下,等同于 bash
  • PostToolUseFailure 是兜底事件:API 错误 / 工具超时 / Esc 中断都会走这里,可在此清场(删临时文件、回滚 git stash)
  • async / asyncRewake:长任务用 "async": true 让 hook 后台跑,不阻塞会话;asyncRewake 在退出码 2 时唤醒模型继续

相关