跳转到主要内容
Hooks 允许在 Qoder CLI 的关键节点介入 Agent 的主执行流,同时与 CLI 保持解耦。常见用途包括:工具执行前拦截危险操作、任务完成后发送桌面通知、写文件后自动跑 lint 等。 Hooks 通过 JSON 配置文件定义,不需要修改代码,编辑配置文件即可生效。

快速开始

以下示例演示如何用 Hook 拦截危险命令——当 Agent 尝试执行 rm -rf 时自动阻止。 第一步:创建脚本
mkdir -p ~/.qoder/hooks
cat > ~/.qoder/hooks/block-rm.sh << 'EOF'
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')

if echo "$command" | grep -q 'rm -rf'; then
  echo "危险命令已被阻止: $command" >&2
  exit 2
fi

exit 0
EOF
chmod +x ~/.qoder/hooks/block-rm.sh
第二步:编辑配置文件 ~/.qoder/settings.json 中添加:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoder/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
第三步:验证效果 启动 Qoder CLI,让 Agent 执行包含 rm -rf 的命令,Hook 会阻止执行并提示 Agent。

配置

配置文件位置

Hook 配置从以下三个文件加载,三级配置会被合并执行:
~/.qoder/settings.json                    # 用户级别,对所有项目生效
${project}/.qoder/settings.json           # 项目级别,对当前项目生效,可提交 git 共享给团队
${project}/.qoder/settings.local.json     # 项目本地级别,优先级最高,建议加到 .gitignore

配置格式

{
  "hooks": {
    "事件名": [
      {
        "matcher": "匹配条件",
        "hooks": [
          {
            "type": "command",
            "command": "要执行的命令",
            "timeout": 60
          }
        ]
      }
    ]
  }
}
字段说明:
字段必填说明
type固定为 "command"
command要执行的 shell 命令
timeout超时时间(秒),默认 60
matcher匹配条件,不填则匹配所有
一个事件下可以配置多个 matcher 分组,每个分组可以包含多个 hook 命令。

matcher 匹配规则

matcher 用于过滤 hook 的触发范围,不同事件匹配不同的字段(见各事件说明)。
写法含义示例
不填或 "*"匹配所有所有工具都触发
精确值精确匹配"Bash" 只匹配 Bash 工具
| 分隔匹配多个值"Write|Edit" 匹配 Write 或 Edit
正则表达式正则匹配"mcp__.*" 匹配所有 MCP 工具

Hook 脚本编写

Hook 脚本通过 stdin 接收 JSON 输入,通过 exit code 和 stdout 输出来控制行为。本节说明所有事件通用的输入输出格式,各事件的额外字段见支持的事件

输入

Hook 脚本通过 stdin 接收 JSON 数据。所有事件都包含以下通用字段:
字段说明
session_id当前会话 ID
cwd当前工作目录
hook_event_name触发的事件名称
不同事件会在此基础上附加额外字段(见各事件说明)。 jq 解析输入:
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

输出

Hook 通过 exit code 和 stdout 控制行为。 exit 0 表示成功。Qoder CLI 会解析 stdout,部分事件(如 UserPromptSubmit、SessionStart)支持纯文本上下文注入或 JSON 精细控制,具体见各事件说明。 exit 2 表示阻塞错误,仅对支持阻塞的事件生效。stdout 被忽略,stderr 作为错误信息反馈给 Agent。具体效果取决于事件:PreToolUse 阻止工具执行,UserPromptSubmit 拒绝 prompt,Stop 阻止 Agent 停止,等等。 其他 exit code 视为非阻塞错误,不影响执行流程,stderr 仅记录在日志中。

环境变量

Hook 脚本执行时可以使用以下环境变量:
变量说明
QODER_PROJECT_DIR当前项目的工作目录

支持的事件

Qoder CLI 支持以下 Hook 事件,覆盖会话生命周期的各个阶段。

SessionStart

会话开始时触发。 matcher 匹配: 会话来源
matcher 值触发场景
startup新会话启动
resume恢复已有会话
compact上下文压缩完成后
额外输入字段:
{
  "source": "startup",
  "model": "Auto"
}

SessionEnd

会话结束时触发。 matcher 匹配: 结束原因
matcher 值触发场景
prompt_input_exit用户退出输入(Ctrl+D 等)
other其他原因
额外输入字段:
{
  "reason": "prompt_input_exit"
}

UserPromptSubmit

用户提交 Prompt 后、Agent 处理前触发。可以为 Agent 注入额外上下文,也可以验证并阻止特定类型的 prompt。不支持 matcher,所有配置的 hook 都会执行。 通用字段之外,UserPromptSubmit 额外收到 prompt 字段,包含用户提交的文本:
{
  "prompt": "帮我写一个排序函数"
}
输出控制: exit 0 时有两种方式为 Agent 添加上下文:直接在 stdout 输出纯文本,或通过 JSON 的 additionalContext 字段注入。纯文本更简单,但注意不要以 { 开头,否则会被当作 JSON 解析。 要阻止 prompt,可以用 exit 2(stderr 内容展示给用户),也可以 exit 0 输出包含 decision: "block" 的 JSON。exit 2 适合纯拦截场景;JSON 方式更灵活,适合在同一脚本中根据条件决定阻止还是放行。
字段说明
decision设为 "block" 阻止 prompt 处理并从上下文中移除,不设置则放行
reason阻止时展示给用户的原因说明,不会添加到上下文
additionalContext注入给 Agent 的上下文字符串(通过 hookSpecificOutput 传递)
{
  "decision": "block",
  "reason": "阻止原因说明",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "注入的上下文内容"
  }
}
JSON 格式不是必须的。简单场景下 exit 0 直接输出纯文本即可注入上下文,需要阻止 prompt 或精细控制时才需要 JSON。
完整示例见提交 Prompt 时注入上下文阻止包含敏感信息的 Prompt

PreToolUse

工具执行前触发。可以阻止工具执行。 matcher 匹配: 工具名(如 BashWriteEditReadGlobGrep,MCP 工具名如 mcp__server__tool 额外输入字段:
{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf /tmp/build"},
  "tool_use_id": "toolu_01ABC123"
}
阻止工具执行: exit code 2,stderr 内容作为错误返回给 Agent。完整示例见快速开始

PostToolUse

工具执行成功后触发。 matcher 匹配: 工具名 额外输入字段:
{
  "tool_name": "Write",
  "tool_input": {"file_path": "/path/to/file.ts", "content": "..."},
  "tool_response": "File written successfully",
  "tool_use_id": "toolu_01ABC123"
}

PostToolUseFailure

工具执行失败后触发。 matcher 匹配: 工具名 额外输入字段:
{
  "tool_name": "Bash",
  "tool_input": {"command": "npm test"},
  "tool_use_id": "toolu_01ABC123",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false
}

Stop

Agent 完成响应后触发(主 Agent,无待执行的工具调用时)。可以阻止 Agent 停止,让其继续工作。 阻止 Agent 停止: exit code 2,stderr 内容作为消息注入对话,Agent 继续工作。

SubagentStart / SubagentStop

子 Agent 启动和完成时触发。SubagentStop 与 Stop 类似,可以阻止子 Agent 停止。 matcher 匹配: Agent 类型名 额外输入字段:
{
  "agent_id": "a1b2c3d4",
  "agent_type": "task"
}

PreCompact

上下文压缩前触发。 matcher 匹配: 触发方式
matcher 值触发场景
manual用户手动执行 /compact
auto上下文窗口满时自动触发
额外输入字段:
{
  "trigger": "manual",
  "custom_instructions": "保留所有工具调用结果"
}

Notification

通知事件触发(权限请求、任务完成等)。 matcher 匹配: 通知类型
matcher 值触发场景
permission权限请求通知
resultAgent 产出结果通知
额外输入字段:
{
  "message": "Agent is requesting permission to run: rm -rf node_modules",
  "title": "Permission Required",
  "notification_type": "permission"
}

PermissionRequest

工具执行需要用户授权时触发。 matcher 匹配: 工具名 额外输入字段:
{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf node_modules"}
}

实用场景

桌面通知提醒

当 Agent 完成任务或需要授权时,弹出桌面通知。 脚本 ~/.qoder/hooks/notify.sh(macOS):
#!/bin/bash
input=$(cat)
message=$(echo "$input" | jq -r '.message')

if echo "$message" | grep -q "^Agent"; then
  osascript -e 'display notification "任务执行完成" with title "Qoder CLI"'
else
  osascript -e 'display notification "任务需要授权" with title "Qoder CLI"'
fi

exit 0
配置:
{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoder/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

写文件后自动 Lint

每次 Agent 写入或编辑文件后,自动执行 lint 检查。 脚本 ${project}/.qoder/hooks/auto-lint.sh
#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

# 只检查 JS/TS 文件
case "$file_path" in
  *.js|*.ts|*.jsx|*.tsx)
    npx eslint "$file_path" --fix 2>/dev/null
    ;;
esac

exit 0
配置:事件 PostToolUse,matcher Write|Edit,command .qoder/hooks/auto-lint.sh

让 Agent 继续工作

在 Agent 停止时检查是否还有未完成的任务,如果有则注入消息让 Agent 继续。 脚本 ~/.qoder/hooks/check-continue.sh
#!/bin/bash
# 检查是否有未提交的 git 变更
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
  echo "检测到未提交的变更,请完成 git commit" >&2
  exit 2
fi

exit 0
配置:事件 Stop,command ~/.qoder/hooks/check-continue.sh

提交 Prompt 时注入上下文

在每次用户提问前,自动注入当前 git 分支信息作为 Agent 上下文。 脚本 ~/.qoder/hooks/inject-branch.sh
#!/bin/bash
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -n "$branch" ]; then
  echo "当前 git 分支: $branch"
fi
exit 0
配置:事件 UserPromptSubmit,command ~/.qoder/hooks/inject-branch.sh

阻止包含敏感信息的 Prompt

拦截包含密码、密钥等敏感关键词的 prompt,阻止其发送给 Agent。 脚本 ~/.qoder/hooks/block-sensitive.sh
#!/bin/bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt')

if echo "$prompt" | grep -qi 'password\|secret\|credential'; then
  echo "prompt 中包含敏感信息,已阻止" >&2
  exit 2
fi

exit 0
配置:事件 UserPromptSubmit,command ~/.qoder/hooks/block-sensitive.sh