hooks-adapter:让现有 hooks 配置在 DSH 上继续生效的兼容层

前言

写 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.jsonopencode.json 的 hooks 部分,以及原生配置(~/.config/hooks-adapter/hooks.json<project>/.dsh-hooks.json),把各 harness 的生命周期事件映射为规范事件(如 session:starttool:before),再绑定到 dsh 扩展点(如 agent/session-starttools/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,或返回 JSON decision: "block",会阻止工具调用(或转为人工确认)。
  • PostToolUse / PostToolUseFailure(互斥触发)可拒绝写回作为结果反馈,或附加上下文。
  • UserPromptSubmit / SessionStart / Stop / SubagentStart / SubagentStop / SessionEnd 可注入上下文、拒绝提示、强制模型继续。
  • NotificationPreCompact(opencode 方言对应 compact:before)仅支持 manual / stdio 触发。

三种集成模式

  1. dsh 插件(Cordis apply):推荐方式,装完自动生效。
  2. stdio JSON-lines 协议:任意 host 可接入。
  3. 一次性 CLI:validaterundumplist

安装与启用

从 GitHub 仓库安装:

dsh plugin --profile demo add github:JohnXuXianyu22786/hooks-adapter

这个包是一个 dsh bundle(dsh.bundle.patchcordis.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 方言的 NotificationPreCompact 仅支持 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 / 幻方无官方从属关系)
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye