跳转到主要内容
Hooks 让你可以在 QoderWork 执行的关键节点插入自定义逻辑,而无需修改任何代码。你只需编辑 JSON 配置文件,就能实现诸如:
  • 在工具执行前拦截危险操作
  • 写文件后自动跑 lint,保持代码风格一致
  • Agent 完成任务时弹出桌面通知,不用一直盯着屏幕
与 Prompt 指令不同,Hooks 是确定性的——只要事件触发,脚本就一定执行,不受模型理解偏差的影响。

快速开始

下面用一个例子,演示如何拦截 rm -rf 这类危险命令。
1

创建脚本

mkdir -p ~/.qoderwork/hooks
cat > ~/.qoderwork/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 ~/.qoderwork/hooks/block-rm.sh
2

添加配置

~/.qoderwork/settings.json 中添加:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
3

验证效果

打开 QoderWork,让 Agent 执行包含 rm -rf 的命令。Hook 会阻止执行,并把错误信息反馈给 Agent。

工作原理

Hook 的使用流程可以概括为三步:编写脚本 → 注册配置 → 自动生效 当 Agent 执行到某个生命周期节点时(如工具调用前),QoderWork 会检查配置中是否有对应的 Hook:
  1. QoderWork 在启动时加载所有 Hook 配置
  2. Agent 运行过程中,到达某个事件节点(如 PreToolUse
  3. QoderWork 遍历该事件下所有 Hook 分组,用 matcher 匹配当前上下文
  4. 匹配成功的 Hook 按顺序执行对应的 shell 脚本
  5. 脚本通过 stdin 接收事件上下文(JSON),通过 exit codestdout 返回决策
  6. QoderWork 根据返回结果决定后续行为(放行 / 阻断)

前置条件

  • jq:示例脚本依赖 jq 解析 JSON。macOS 下执行 brew install jq,Linux 下执行 apt install jq
  • 脚本权限:所有 Hook 脚本需要有可执行权限(chmod +x)。

创建 Hooks

1. 确定需求:选择事件和匹配范围

首先明确你要在哪个节点介入,以及匹配什么条件:
我想在「什么时候」拦截/处理「什么操作」?
   ↓                        ↓
 选择事件                 编写 matcher
需求事件matcher
Agent 执行任何 Shell 命令前检查PreToolUse"Bash"
Agent 写入或编辑文件后做处理PostToolUse"Write | Edit"
Agent 工具调用失败时记录PostToolUseFailure"Bash" 或不填
用户提交的所有 Prompt 做审查UserPromptSubmit不填(匹配所有)
Agent 停止时触发通知Stop不填(匹配所有)
只拦截 MCP 工具PreToolUse"mcp__.*"

2. 编写 Hook 脚本

Hook 脚本是一个标准的 shell 脚本,遵循以下协议: 输入:通过 stdin 接收 JSON 格式的事件上下文 输出:通过 exit code 控制行为
exit 0   →  放行(继续执行)
exit 2   →  阻断(停止操作,stderr 注入对话)
其他值   →  错误(继续执行,stderr 展示给用户)
脚本模板:
#!/bin/bash

# 1. 读取 stdin 的 JSON 输入
input=$(cat)

# 2. 用 jq 提取你关心的字段
tool_name=$(echo "$input" | jq -r '.tool_name')
tool_input=$(echo "$input" | jq -r '.tool_input')

# 3. 编写你的判断逻辑
if [ "$tool_name" = "Bash" ]; then
  command=$(echo "$input" | jq -r '.tool_input.command')

  if echo "$command" | grep -qE 'rm\s+-rf|DROP\s+TABLE'; then
    echo "操作被拒绝: $command" >&2
    exit 2
  fi
fi

# 4. 放行
exit 0
除了 exit code,你还可以在 exit 0 时输出 JSON 来提供更精细的控制:
#!/bin/bash
input=$(cat)

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"该操作不被允许"}}'
exit 0

3. 注册到配置文件

将脚本路径写入 ~/.qoderwork/settings.json 的对应事件下:
{
  "hooks": {
    "事件名": [
      {
        "matcher": "匹配条件(可选)",
        "hooks": [
          {
            "type": "command",
            "command": "脚本路径"
          }
        ]
      }
    ]
  }
}

4. 测试与调试

可以直接在终端用管道模拟测试:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"},"hook_event_name":"PreToolUse"}' \
  | ~/.qoderwork/hooks/block-rm.sh
echo "Exit code: $?"
检查 stderr 输出(阻断消息):
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
  | ~/.qoderwork/hooks/block-rm.sh 2>&1

配置 Hooks

配置文件位置

QoderWork 从以下用户级配置文件加载 Hook 配置:
位置作用域说明
~/.qoderwork/settings.json用户级(全局)用户个人配置,对所有 QoderWork 会话生效
当前版本暂不支持热加载,修改配置文件后需要重启 QoderWork 才能生效。

配置格式

{
  "hooks": {
    "事件名": [
      {
        "matcher": "匹配条件",
        "hooks": [
          {
            "type": "command",
            "command": "要执行的命令"
          }
        ]
      }
    ]
  }
}
字段必填说明
type固定为 "command"
command要执行的 shell 命令或脚本路径
timeout超时时间(秒),默认 30。当前版本不支持自定义,下个版本开放配置
matcher匹配条件,不填则匹配该事件的所有触发
一个事件下可以配置多个 matcher 分组,每个分组可以包含多个 Hook 命令。

matcher 匹配规则

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

工具名映射

QoderWork 支持两套工具名——原生工具名与兼容工具名。配置 Hook 时可使用任意一套,QoderWork 内部会统一映射后执行匹配。
原生名兼容名说明
run_in_terminalBash执行 shell 命令
read_fileRead读取文件内容
create_fileWrite创建 / 写入文件
search_replaceEdit编辑文件
delete_file-删除文件
grep_codeGrep搜索文件内容
search_fileGlob文件名匹配
list_dirLS列出目录
taskTask启动子任务 / 子代理
Skill-调用技能
search_webWebSearch网页搜索
fetch_contentWebFetch获取网页内容
todo_writeTodoWrite写入 TODO
ask_user_question-向用户提问
search_memory-搜索记忆
update_memory-更新记忆
mcp__<server>__<tool>同左MCP 工具

编写 Hook 脚本

Hook 脚本通过 stdin 接收 JSON 输入,通过 exit code 和 stdout 输出来控制行为。本节说明所有事件通用的输入输出格式,各事件的额外字段见 Hooks 事件
QoderWork 目前不会向 Hook 脚本注入环境变量。所有数据通过 stdin JSON 传递。如需会话 ID、工作目录或工具信息,请从 stdin JSON 输入中解析。

输入

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

输出

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

Hooks 事件

UserPromptSubmit

用户提交 Prompt 后、Agent 开始处理前触发。可用于 Prompt 审查、内容过滤、自动补充上下文等场景。 matcher 匹配: 无匹配字段,该事件对所有用户输入统一触发。 额外输入字段:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "帮我整理下载文件夹"
}
阻止 Prompt 提交: exit code 2,stderr 内容作为错误信息反馈给用户,Agent 不会处理该 Prompt。 stdout JSON 输出字段(exit 0 时):
字段类型说明
hookSpecificOutput.hookEventNamestring固定为 "UserPromptSubmit"
hookSpecificOutput.additionalContextstring附加上下文信息,会注入到 Agent 的对话中

PreToolUse

工具执行前触发。可以阻止工具执行。 适用于危险命令拦截、文件路径校验、权限检查等场景。 matcher 匹配: 工具名(如 BashWriteEditRead,MCP 工具名如 mcp__server__tool 额外输入字段:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "rm -rf /tmp/build" }
}
阻止工具执行: exit code 2,stderr 内容作为错误返回给 Agent。 stdout JSON 输出字段(exit 0 时):
字段类型说明
hookSpecificOutput.hookEventNamestring固定为 "PreToolUse"
hookSpecificOutput.permissionDecisionstring"allow"(放行)、"deny"(拒绝)或 "ask"(询问用户)
hookSpecificOutput.permissionDecisionReasonstring决策原因
hookSpecificOutput.updatedInputobject修改后的工具输入参数(可选)
hookSpecificOutput.additionalContextstring附加上下文信息(可选)

PostToolUse

工具执行成功后触发。不可阻断。适用于自动 lint、日志记录、结果分析等场景。 matcher 匹配: 工具名 额外输入字段:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": { "file_path": "/path/to/file.ts", "content": "..." },
  "tool_response": "File written successfully"
}
stdout JSON 输出字段(exit 0 时):
字段类型说明
hookSpecificOutput.hookEventNamestring固定为 "PostToolUse"
hookSpecificOutput.feedbackstring反馈信息,会展示给用户

PostToolUseFailure

工具调用失败后触发。不可阻断。适用于错误监控、失败重试提示、日志记录等场景。 matcher 匹配: 工具名 额外输入字段:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" },
  "error": "Command exited with non-zero status code 1"
}
字段类型说明
errorstring工具执行的错误信息

Stop

Agent 完成响应后触发。可以阻止 Agent 停止。 适用于质量门禁、桌面通知、日志记录等场景。 matcher 匹配: 无匹配字段,该事件在 Agent 停止时统一触发。 额外输入字段:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}
字段类型说明
stop_hook_activeboolean当 Agent 因 Stop Hook 阻断后重试时为 true脚本必须检查此字段,在 true 时 exit 0,防止无限循环。
阻止 Agent 停止: exit code 2,阻止原因注入对话,Agent 继续工作。 stdout JSON 输出字段(exit 0 时):
{
  "decision": "block",
  "reason": "Tests failing. Fix them before completing."
}
字段类型说明
decisionstring"block"(阻止停止,让 Agent 继续工作)
reasonstring阻止的原因,会作为消息注入对话
防止无限循环: 当 Stop Hook 阻断 Agent(exit 2)后,Agent 重试时 Stop 事件会再次触发,此时 stop_hook_active: true。脚本必须检查此字段并在 trueexit 0,否则 Hook 会无限阻断。

SessionStart

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

SessionEnd

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

SubagentStart / SubagentStop

子 Agent 启动和完成时触发。SubagentStop 与 Stop 类似,可以阻止子 Agent 停止。 matcher 匹配: Agent 类型名 SubagentStart 额外输入字段:
{
  "agent_id": "83f488ef",
  "agent_type": "general-purpose"
}
SubagentStop 额外输入字段:
{
  "agent_id": "83f488ef",
  "agent_type": "general-purpose",
  "agent_transcript_path": "",
  "stop_hook_active": false
}
字段类型说明
agent_idstring子 Agent ID
agent_typestring子 Agent 类型(如 "general-purpose"
agent_transcript_pathstring子 Agent 的 Transcript 文件路径(仅 SubagentStop)
stop_hook_activeboolean是否为上次 SubagentStop 阻断后的重试(仅 SubagentStop)

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"}
}

场景示例

拦截危险命令

脚本 ~/.qoderwork/hooks/block-dangerous.sh
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')

if echo "$command" | grep -qE 'rm\s+-rf|DROP\s+TABLE|mkfs|dd\s+if='; then
  echo "危险命令已被阻止: $command" >&2
  exit 2
fi

exit 0
配置:事件 PreToolUse,matcher Bash,command ~/.qoderwork/hooks/block-dangerous.sh

写文件后自动 Lint

每次 Agent 写入或编辑文件后,自动执行 lint 检查。 脚本 ~/.qoderwork/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 ~/.qoderwork/hooks/auto-lint.sh

桌面通知提醒

当 Agent 完成任务或需要授权时,弹出桌面通知。 脚本 ~/.qoderwork/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 "QoderWork"'
else
  osascript -e 'display notification "任务需要授权" with title "QoderWork"'
fi

exit 0
配置:事件 Notification,无 matcher,command ~/.qoderwork/hooks/notify.sh

Prompt 内容审查

脚本 ~/.qoderwork/hooks/check-prompt.sh
#!/bin/bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt')

if echo "$prompt" | grep -qiE '(password|secret|api_key|token)\s*[:=]\s*\S+'; then
  echo "检测到 Prompt 中可能包含敏感信息,请检查后重新提交" >&2
  exit 2
fi

exit 0
配置:事件 UserPromptSubmit,无 matcher,command ~/.qoderwork/hooks/check-prompt.sh

工具失败时记录日志

脚本 ~/.qoderwork/hooks/log-failure.sh
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')
error=$(echo "$input" | jq -r '.error')
timestamp=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$timestamp] $tool_name 执行失败: $error" >> ~/.qoderwork/hooks/failure.log

exit 0
配置:事件 PostToolUseFailure,无 matcher,command ~/.qoderwork/hooks/log-failure.sh

让 Agent 继续工作

在 Agent 停止时检查是否还有未完成的任务,如果有则注入消息让 Agent 继续。 脚本 ~/.qoderwork/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 ~/.qoderwork/hooks/check-continue.sh

提交 Prompt 时注入上下文

在每次用户提问前,自动注入当前 git 分支信息作为 Agent 上下文。 脚本 ~/.qoderwork/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 ~/.qoderwork/hooks/inject-branch.sh

阻止包含敏感信息的 Prompt

拦截包含密码、密钥等敏感关键词的 prompt,阻止其发送给 Agent。 脚本 ~/.qoderwork/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 ~/.qoderwork/hooks/block-sensitive.sh

完整配置示例

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/check-prompt.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/block-dangerous.sh"
          }
        ]
      }
    ],
    "PostToolUseFailure": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/log-failure.sh"
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

注意事项

  • 超时处理: Hook 脚本默认超时 30 秒。超时后脚本将被终止,按放行处理。自定义超时时间将在下个版本中支持。
  • 错误处理: 脚本异常退出(exit code 非 0 且非 2)时,错误信息显示给用户,Agent 流程不受影响继续执行。
  • 脚本权限: 确保脚本具有可执行权限(chmod +x)。
  • jq 依赖: 示例脚本依赖 jq 命令行工具来解析 JSON。请确保系统已安装 jq(macOS: brew install jq,Linux: apt install jq)。
  • 重启生效: 修改 ~/.qoderwork/settings.json 后,需要重启 QoderWork 才能生效。