前言¶
写 hooks 是 agent 工作流里最常见的定制手段:工具执行前拦截危险命令、任务结束时发通知、会话启动时注入上下文。如果你在 Claude Code、Codex 或 opencode 里写过这类声明,切到 DeepSeek Harness(dsh)时会遇到一个现实问题——这些配置跟着原 harness 的格式走,dsh 不能直接识别,要么重写一遍,要么在几套配置之间手动同步。
下面介绍的 hooks-adapter 处理的就是这个问题:它读取已有的 hooks 配置文件,把各家的事件映射到 dsh 扩展点上,原有声明不做改写,就能在 dsh 里继续跑。
这是什么¶
hooks-adapter(v0.1.0,MIT 许可证,作者 JohnXuXianyu22786)定位是通用的 hooks 兼容层:读取 .claude/settings.json、.codex/hooks.json、opencode.json 的 hooks 部分,以及原生配置(~/.config/hooks-adapter/hooks.json、<project>/.dsh-hooks.json),把各 harness 的生命周期事件映射为规范事件(如 session:start、tool:before),再绑定到 dsh 扩展点(如 agent/session-start、tools/pre-execute),并以 shell、webhook、oracle、proxy 四种 handler 执行。
几个设计决策值得先说清楚:
- 配置只读、不迁移:现有 hooks 声明保持不变。
- 零运行时依赖:要求 Node >= 18,纯 ESM + JSDoc 类型。
- 失败不阻塞启动:缺失的配置文件静默跳过;已有文件有问题时只产生诊断(diagnostics),绝不阻塞 dsh 启动。
核心功能¶
配置来源与发现¶
插件按固定顺序自动发现并合并 9 个配置文件位置(全局 / 项目 / 本地):
| 顺序 | 文件 | 方言 |
|---|---|---|
| 1 | ~/.claude/settings.json |
claude |
| 2 | ~/.codex/hooks.json |
codex |
| 3 | ~/.config/opencode/opencode.json |
opencode |
| 4 | ~/.config/hooks-adapter/hooks.json |
native |
| 5 | <project>/.claude/settings.json |
claude |
| 6 | <project>/.codex/hooks.json |
codex |
| 7 | <project>/opencode.json |
opencode |
| 8 | <project>/.dsh-hooks.json |
native |
| 9 | <project>/.claude/settings.local.json |
claude |
合并规则:同名事件由后读的文件追加组;disableAllHooks 遵循最具体的文件。两个环境变量可以改变行为:HOOKS_ADAPTER_CONFIG(同 --config)与 HOOKS_ADAPTER_HOME(同 --home)。配置文件必须是严格 JSON,不允许注释。
四种 handler¶
配置里的 type 字段沿用各 harness 的习惯写法,内部归一化为四种 kind:
| 配置 type | 内部 kind | 行为 | 默认超时 |
|---|---|---|---|
command |
shell |
启动 shell 进程,从 stdin 喂入 JSON 契约 | 600s |
http |
webhook |
POST JSON 到 URL,响应体作为 decision | 600s |
prompt |
oracle |
调用 LLM 端点评估,{ok:false} 表示拒绝 |
30s |
agent / subagent |
proxy |
委托给子代理运行器(命令可配置) | 60s |
超时控制与失败降级策略内建;validate 子命令提供友好的配置校验。
事件映射与拦截行为¶
各 harness 的事件名先映射为规范事件,再绑定到 dsh 扩展点:
| 规范事件 | claude | codex | opencode | dsh 扩展点 |
|---|---|---|---|---|
session:start |
SessionStart |
SessionStart |
session.created |
agent/session-start |
session:end |
SessionEnd |
SessionEnd |
session.deleted |
session/disposed |
prompt:submit |
UserPromptSubmit |
UserPromptSubmit |
chat.message |
agent/pre-step |
tool:before |
PreToolUse |
PreToolUse |
tool.execute.before |
tools/pre-execute |
tool:after |
PostToolUse / PostToolUseFailure |
PostToolUse |
tool.execute.after |
tools/post-execute |
turn:stop |
Stop |
Stop |
session.idle |
agent/turn-stopping |
subagent:start |
SubagentStart |
SubagentStart |
tool.execute.before.subagent |
subagent/start |
subagent:end |
SubagentStop |
SubagentStop |
tool.execute.after.subagent |
subagent/end |
notice |
Notification |
Notification |
notification |
manual / stdio |
compact:before |
PreCompact |
— | experimental.session.compacting |
manual / stdio |
行为上的关键点:
PreToolUse是拦截点:handler 退出码 2,或返回 JSONdecision: "block",会阻止工具调用(或转为人工确认)。PostToolUse/PostToolUseFailure(互斥触发)可拒绝写回作为结果反馈,或附加上下文。UserPromptSubmit/SessionStart/Stop/SubagentStart/SubagentStop/SessionEnd可注入上下文、拒绝提示、强制模型继续。Notification与PreCompact(opencode 方言对应compact:before)仅支持 manual / stdio 触发。
三种集成模式¶
- dsh 插件(Cordis
apply):推荐方式,装完自动生效。 - stdio JSON-lines 协议:任意 host 可接入。
- 一次性 CLI:
validate、run、dump、list。
安装与启用¶
从 GitHub 仓库安装:
dsh plugin --profile demo add github:JohnXuXianyu22786/hooks-adapter
这个包是一个 dsh bundle(dsh.bundle.patch → cordis.patch.yml),添加后会插入插件树。之后启动 dsh:
dsh --profile demo
插件会自动发现项目目录和用户目录下的 hooks 配置。也可以从本地 checkout 目录安装:
dsh plugin --profile demo add ./hooks-adapter
dsh --profile demo
卸载用:
dsh plugin --profile demo remove hooks-adapter
典型用法¶
沿用现有 hooks 声明¶
假设你在 .claude/settings.json 里已经有这样的声明:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "guard.sh", "timeout": 10 }
]
}
],
"Stop": [
{ "hooks": [ { "type": "command", "command": "notify-send done" } ] }
]
}
}
含义:Bash 工具执行前先跑 guard.sh(超时 10 秒),可通过退出码拦截;每次 Stop 事件触发 notify-send done。装好插件后这份配置不用改,dsh 下次运行时自动生效。
覆盖插件配置¶
需要调整行为时,在 profile 的 cordis.patch.yml 中 replace id: hooks-adapter:
- replace:
- id: hooks-adapter
config:
configPath: /abs/path/to/hooks.json # 固定单个文件,跳过发现
discover: false
llm: { baseUrl: "https://api.example.com/v1", model: "eval-small" }
proxy: { command: "dsh run --quiet" }
configPath 固定单个配置文件并跳过自动发现;discover: false 关闭发现;llm 配置 oracle handler 使用的 LLM 端点;proxy 配置 proxy handler 委托的子代理运行器命令。
stdio 协议与一次性 CLI¶
想在 dsh 之外的宿主程序里复用同一套 hooks 执行器,走 stdio JSON-lines 协议:
echo '{"op":"ping"}' | node lib/index.js listen --config hooks.json
echo '{"op":"dispatch","event":"PreToolUse","payload":{"tool_name":"Bash","tool_input":{}}}' | node lib/index.js listen
调试和检查用一次性 CLI。先做校验,再看合并结果,是比较稳的顺序:
node lib/index.js validate # 校验所有可发现的配置,退出码 0/1
node lib/index.js run --event PreToolUse --payload payload.json
node lib/index.js dump # 打印合并后的生效配置
node lib/index.js list # 列出发现的配置文件
经过上面的步骤,配置是否被正确发现、事件是否按预期触发,都可以在不启动完整 dsh 会话的情况下确认。更多格式细节见仓库的 docs/ 与 examples/ 目录。
适用场景与注意¶
适合的场景:
- 从 Claude Code / Codex / opencode 迁到 dsh,已有的 hooks 逻辑想原样复用。
- 多个 harness 并行使用,希望维护一份 hooks 声明而不是几份。
- 想把 hooks 执行器嵌进自己的宿主程序(stdio 协议)。
使用前注意:
- 需要 Node >= 18;配置文件必须为严格 JSON,不允许注释。
- opencode 方言的
Notification与PreCompact仅支持 manual / stdio 触发。 - 安全方面:插件以当前 dsh 进程的权限运行,hooks 里声明的命令、webhook 和 LLM 调用也在这个权限范围内执行。安装任何第三方插件前,建议通读源码并确认许可证(本项目为 MIT)。
结尾¶
hooks-adapter 做的事情不复杂:把各家 harness 的 hooks 声明翻译成 dsh 能执行的事件绑定,配置只读不改写,装上即用,卸掉即回。对从其他 harness 迁移过来的开发者,它省掉的是重写和同步多份配置的成本。
- GitHub:https://github.com/JohnXuXianyu22786/hooks-adapter
- 社区插件目录:https://www.skillhub.cn/plugins/JohnXuXianyu22786/hooks-adapter (目录为社区独立站点维护,与 DeepSeek / 幻方无官方从属关系)