Hooks 让您可以在通义Qoder CN IDE 执行的关键节点插入自定义逻辑,而无需修改任何代码。您只需编辑 JSON 配置文件,就能实现诸如:
-
在工具执行前拦截危险操作
-
写文件后自动跑 lint,保持代码风格一致
-
Agent 完成任务时弹出桌面通知,不用一直盯着 IDE
与 Prompt 指令不同,Hooks 是确定性的——只要事件触发,脚本就一定执行,不受模型理解偏差的影响。
支持的事件
当前通义Qoder CN IDE 支持以下五种 Hook 事件:
|
事件名称 |
触发时机 |
可阻断 |
|
|
用户提交 Prompt 后、Agent 处理前 |
是 |
|
|
工具调用执行前 |
是 |
|
|
工具调用成功后 |
否 |
|
|
工具调用失败后 |
否 |
|
|
Agent 完成响应时 |
否 |
快速开始
下面以拦截 rm -rf 这类危险命令为例,演示 Hooks 的配置流程。
步骤一:创建脚本
mkdir -p ~/.lingma/hooks
cat > ~/.lingma/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 ~/.lingma/hooks/block-rm.sh
步骤二:添加配置
在 ~/.lingma/settings.json 中添加:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.lingma/hooks/block-rm.sh"
}
]
}
]
}
}
步骤三:验证效果
重启 IDE,在Qoder CN面板中让 Agent 执行包含 rm -rf 的命令。Hook 会阻止执行,并把错误信息反馈给 Agent。
适用场景
|
场景 |
对应事件 |
说明 |
|
拦截危险命令 |
|
在 Agent 执行 |
|
文件路径校验 |
|
限制 Agent 只能在指定目录下创建或编辑文件 |
|
自动 Lint / 格式化 |
|
每次写文件后自动执行 ESLint / Prettier |
|
日志审计 |
|
记录 Agent 的所有工具调用,用于安全审计 |
|
失败监控告警 |
|
工具调用失败时发送告警或记录错误日志 |
|
Prompt 内容审查 |
|
检测用户输入中是否包含敏感信息(密码、密钥等) |
|
自动注入上下文 |
|
自动在 Prompt 末尾追加项目规范或编码约定 |
|
桌面通知 |
|
Agent 完成后弹出系统通知提醒用户 |
工作原理
Hook 的使用流程可以概括为三步:编写脚本 → 注册配置 → 自动生效。
当 Agent 执行到某个生命周期节点时(如工具调用前),插件会检查配置中是否有对应的 Hook:
-
IDE 在启动时加载所有 Hook 配置。
-
Agent 运行过程中,到达某个事件节点(如 PreToolUse)。
-
遍历该事件下所有 Hook 分组,用 matcher 匹配当前上下文。
-
匹配成功的 Hook 按顺序执行对应的 shell 脚本。
-
脚本通过 stdin 接收事件上下文(JSON),通过 exit code 和 stdout 返回决策。
-
IDE 根据返回结果决定后续行为(放行或阻断)。
前置条件
-
jq:示例脚本依赖 jq 解析 JSON。macOS 下执行
brew install jq,Linux 下执行apt install jq。 -
脚本权限:所有 Hook 脚本需要有可执行权限(
chmod +x)。
创建 Hooks
步骤一: 确定需求:选择事件和匹配范围
首先明确您要在哪个节点介入,以及匹配什么条件:
我想在「什么时候」拦截 / 处理「什么操作」?
↓ ↓
选择事件 编写 matcher
|
需求 |
事件 |
matcher |
|
Agent 执行任何 Shell 命令前检查 |
PreToolUse |
|
|
Agent 写入或编辑文件后做处理 |
PostToolUse |
|
|
Agent 工具调用失败时记录 |
PostToolUseFailure |
|
|
用户提交的所有 Prompt 做审查 |
UserPromptSubmit |
不填(匹配所有) |
|
Agent 停止时触发通知 |
Stop |
不填(匹配所有) |
|
只拦截 MCP 工具 |
PreToolUse |
|
步骤二:编写 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
# 阻断:exit 2 + stderr 消息会反馈给 Agent
echo "操作被拒绝: $command" >&2
exit 2
fi
fi
# 4. 放行
exit 0
除了 exit code,您还可以在 exit 0时输出 JSON 来提供更精细的控制:
#!/bin/bash
input=$(cat)
# 输出 JSON 实现精细控制(仅 exit 0 时生效)
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"该操作不被允许"}}'
exit 0
步骤三:注册到配置文件
将脚本路径写入配置文件的对应事件下:
{
"hooks": {
"事件名": [
{
"matcher": "匹配条件(可选)",
"hooks": [
{
"type": "command",
"command": "脚本路径"
}
]
}
]
}
}
步骤四:测试与调试
可以直接在终端用管道模拟测试:
# 模拟 PreToolUse 事件
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"},"hook_event_name":"PreToolUse"}' \
| ~/.lingma/hooks/block-rm.sh
echo "Exit code: $?"
检查 stderr 输出(阻断消息):
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
| ~/.lingma/hooks/block-rm.sh 2>&1
配置 Hooks
配置文件位置
Hook 配置从以下文件加载,多级配置会被合并执行(优先级从低到高):
|
位置 |
作用域 |
优先级 |
可共享 |
说明 |
|
|
用户级 |
1(最低) |
否 |
用户个人配置,对所有项目生效 |
|
|
项目级 |
2 |
是 |
可提交到 Git,团队共享 |
|
|
项目级(本地) |
3 |
否 |
建议加入 .gitignore,个人开发配置 |
当前版本暂不支持热加载,修改配置文件后需要重启 IDE 才能生效。
配置格式
{
"hooks": {
"事件名": [
{
"matcher": "匹配条件",
"hooks": [
{
"type": "command",
"command": "要执行的命令"
}
]
}
]
}
}
|
字段 |
必填 |
说明 |
|
|
是 |
固定为 |
|
|
是 |
要执行的 shell 命令或脚本路径 |
|
|
否 |
超时时间(秒),默认 30 |
|
|
否 |
匹配条件,不填则匹配该事件的所有触发 |
一个事件下可以配置多个 matcher 分组,每个分组可以包含多个 Hook 命令。
matcher 匹配规则
matcher 用于过滤 Hook 的触发范围,不同事件匹配不同的字段(见各事件说明)。
|
写法 |
含义 |
示例 |
|
不填或 |
匹配所有 |
所有工具都会触发 |
|
精确值 |
精确匹配 |
|
|
|
匹配多个值 |
|
|
正则表达式 |
正则匹配 |
|
工具名映射
Qoder CN支持两套工具名——原生工具名与兼容工具名。配置 Hook 时可使用任意一套,运行时会统一映射后执行匹配。例如 matcher: "Bash" 和 matcher: "run_in_terminal" 等价。
|
原生名 |
兼容名 |
说明 |
|
run_in_terminal |
Bash |
执行 shell 命令 |
|
read_file |
Read |
读取文件内容 |
|
create_file |
Write |
创建 / 写入文件 |
|
search_replace |
Edit |
编辑文件 |
|
delete_file |
- |
删除文件 |
|
grep_code |
Grep |
搜索文件内容 |
|
search_file |
Glob |
文件名匹配 |
|
list_dir |
LS |
列出目录 |
|
task |
Task |
启动子任务 / 子代理 |
|
mcp__<server>__<tool> |
同左 |
MCP 工具 |
编写 Hook 脚本
Hook 脚本通过 stdin 接收 JSON 输入,通过 exit code 和 stdout 输出来控制行为。本节说明所有事件通用的输入输出格式,各事件的额外字段见事件参考。
输入
Hook 脚本通过 stdin 接收 JSON 数据。所有事件都包含以下通用字段:
|
字段 |
说明 |
|
|
当前会话 ID |
|
|
当前工作目录 |
|
|
触发的事件名称 |
|
|
会话上下文 JSON 文件路径 |
不同事件会在此基础上附加额外字段(见下文事件参考)。用 jq 解析输入:
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')
输出
Hook 通过 exit code 和 stdout 控制行为。
exit code 决定基本行为:
|
Exit Code |
含义 |
行为 |
|
0 |
成功 |
继续执行,尝试解析 stdout JSON |
|
2 |
阻断 |
停止操作,stderr 内容注入对话(仅对支持阻断的事件) |
|
其他值 |
错误 |
继续执行,stderr 内容仅展示给用户 |
stdout JSON(仅 exit 0 时生效,可选):
{
"continue": true,
"stopReason": "",
"suppressOutput": false,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow | deny | ask",
"permissionDecisionReason": "说明原因"
}
}
事件参考
UserPromptSubmit
-
触发:用户提交 Prompt 后、Agent 处理前
-
可阻断:是(exit 2 阻止本次请求)
-
额外字段:
prompt(用户输入的原始文本)
PreToolUse
-
触发:工具调用执行前
-
可阻断:是(exit 2 拒绝本次工具调用)
-
匹配字段:
tool_name -
额外字段:
tool_name、tool_input
PostToolUse
-
触发:工具调用成功后
-
可阻断:否
-
匹配字段:
tool_name -
额外字段:
tool_name、tool_input、tool_response
PostToolUseFailure
-
触发:工具调用失败后
-
可阻断:否
-
匹配字段:
tool_name -
额外字段:
tool_name、tool_input、error
Stop
-
触发:Agent 完成响应时
-
可阻断:否
-
额外字段:
stop_hook_active、last_assistant_message
常见问题
修改配置后为什么没生效?
当前版本不支持热加载,修改 settings.json 后需要重启 IDE。
Hook 脚本执行超时会怎样?
默认 30 秒超时,超时视为错误(继续执行,stderr 展示给用户)。
多个 Hook 执行顺序如何确定?
同一事件下按配置声明顺序执行;跨配置文件时,按「用户级 → 项目级 → 项目本地」优先级从低到高依次执行。
Hook 脚本能访问哪些环境变量?
Hook 在当前项目根目录(cwd)下以当前用户身份执行,继承 IDE 进程的环境变量。
如何查看 Hook 执行日志?
Hook 的 stderr 输出会反馈给 Agent 或用户;详细调用日志可在Qoder CN运行时日志中查找 [hook] 前缀的条目。