JohnXu22786/hooks-adapter
DeepSeek Harness 的通用钩子兼容层:在 dsh 上运行 Claude Code / Codex / opencode 配置中声明的钩子
项目介绍Project Overview
hooks-adapter 是 DSH 的 hooks 配置兼容层插件,读取 Claude、Codex、OpenCode 等现有 hooks 配置,将生命周期事件映射到 DSH 扩展点,并执行 shell、webhook、oracle、proxy 四类处理器。适用于想在不同 harness 间复用同一份 hooks 配置、无需迁移即可在 DSH 中拦截工具调用、注入上下文或委派子代理的场景。注意:配置文件必须为严格 JSON(不支持注释),且配置只读、不会被改写。
hooks-adapter is a DSH plugin that acts as a hooks compatibility layer: it reads existing hooks configs from Claude, Codex, and OpenCode, maps their lifecycle events to DSH extension points, and runs four handler kinds—shell, webhook, oracle, and proxy. Use it to reuse the same hooks declarations across harnesses for tool interception, context injection, or subagent delegation without migration. Caveat: config files must be strict JSON with no comments, and configs are read-only, never rewritten.
请帮我了解并安装插件:【hooks-adapter】【https://github.com/JohnXu22786/hooks-adapter】
把上面这条消息直接发给当前会话里的 DSH,让它帮你了解并安装。安装命令不一定准确,发给 DSH 更稳。Send this message to DSH in your current session. CLI install commands may not be accurate across systems — DSH will figure it out for you.
或使用命令行安装(适合开发者)Or use CLI install (for developers)
命令行安装CLI Install
dsh plugin --profile demo add github:JohnXu22786/hooks-adapter
把 JohnXu22786/hooks-adapter 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
hooks-adapter
A hooks configuration compatibility layer for DeepSeek Harness (dsh): it reads existing hooks config files from mainstream agent harnesses (such as the hooks declarations in .claude/settings.json, .codex/hooks.json, and the hooks section of opencode.json), maps their lifecycle events to dsh extension points, and executes four kinds of handlers — shell / webhook / oracle / proxy — so the same hooks config can be reused as-is across different harnesses.
- Zero runtime dependencies (Node ≥ 18, pure ESM + JSDoc types)
- Config is read-only, not migrated: your existing hooks declarations stay unchanged
- All four handler kinds supported: command execution, HTTP callbacks, LLM evaluation, subagent delegation
- Timeout control, failure degradation policy, and friendly config validation (
validatesubcommand) - Three integration modes: dsh plugin (Cordis
apply), stdio JSON-lines protocol (any host), one-shot CLI
hooks-adapter/
├── package.json # dsh bundle manifest (dsh.bundle + exports)
├── cordis.patch.yml # composite package layer: inserts this plugin into the plugin tree
├── dsh/plugin.js # dsh entry: Cordis plugin (name + apply(ctx, config))
├── lib/ # runtime core (usable independently of dsh)
│ ├── index.js # CLI entry + programmatic API exports
│ ├── events.js # canonical event catalog + four-dialect mapping table + matcher semantics
│ ├── discover.js # config file discovery (global/project/local)
│ ├── parse.js # four-dialect parsers (all go through diagnostics, never throw)
│ ├── config.js # runtime assembly: merging, disableAllHooks, defaults
│ ├── contract.js # stdin JSON contract construction + response decoding + decision folding
│ ├── execute.js # four-kind handler executor + timeout + process tree cleanup
│ ├── dispatch.js # dispatch pipeline: matcher matching, ordered execution, blockable constraints
│ └── serve.js # stdio JSON-lines protocol server
├── docs/ # config formats, event mapping, contract, integration notes, CLI guide
├── examples/ # four-dialect example configs + local mock LLM
└── test/ # node:test tests (111 items)
What It Can Do
Declare hooks in .claude/settings.json (no matter which harness you wrote them for) and they keep working in dsh:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "guard.sh", "timeout": 10 }
]
}
],
"Stop": [
{ "hooks": [ { "type": "command", "command": "notify-send done" } ] }
]
}
}
Config files must be strict JSON (no comments); see examples/ for complete four-dialect examples.
PreToolUse→ interception point before tool execution: handler exit code 2 / JSONdecision: "block"will block the tool call (or turn it into ask for human confirmation)PostToolUse/PostToolUseFailure→ after tool execution (mutually exclusive triggers): reject the write-back as result feedback, append contextUserPromptSubmit/SessionStart/Stop/SubagentStart/SubagentStop/SessionEnd→ inject context, reject prompts, force the model to continueNotification/PreCompact→ triggered manually or via the stdio protocol
The four handler kinds (the type field in config follows each harness's conventions; normalized internally):
| Config type | Internal kind | Behavior | Default timeout |
|---|---|---|---|
command |
shell |
spawn a shell process, feed the JSON contract on stdin | 600s |
http |
webhook |
POST JSON to a URL, the response body is the decision | 600s |
prompt |
oracle |
call an LLM endpoint to evaluate, {ok:false} rejects |
30s |
agent / subagent |
proxy |
delegate to a subagent runner (configurable command) | 60s |
Quick Start
Mode one: dsh plugin (recommended)
# From a directory containing this plugin checkout
dsh plugin --profile demo add ./hooks-adapter
dsh --profile demo
After loading, the plugin automatically discovers hooks configs in project and user directories (see below). You can also override the config line in the profile's cordis.patch.yml:
- replace:
- id: hooks-adapter
config:
configPath: /abs/path/to/hooks.json # pin a single file (skip discovery)
discover: false
llm: { baseUrl: "https://api.example.com/v1", model: "eval-small" }
proxy: { command: "dsh run --quiet" }
Integration details: docs/INTEGRATION.md.
Installing in DSH
Install directly from the GitHub repository with the dsh plugin command:
dsh plugin --profile demo add github:JohnXu22786/hooks-adapter
The package is a dsh bundle (dsh.bundle.patch → cordis.patch.yml); once added, it inserts itself into the plugin tree and automatically discovers hooks configs on the next dsh run. Remove it with:
dsh plugin --profile demo remove hooks-adapter
Mode two: stdio protocol (any host)
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
Protocol details: docs/CONTRACT.md.
Mode three: one-shot CLI
node lib/index.js validate # check all discoverable configs, exit code 0/1
node lib/index.js run --event PreToolUse --payload payload.json
node lib/index.js dump # print the merged effective config
node lib/index.js list # list discovered config files
Where the Config Comes From
Auto-discovered and merged in order (later files append groups for same-named events; disableAllHooks follows the most specific file):
| Order | File | Dialect |
|---|---|---|
| 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 |
- Environment variables
HOOKS_ADAPTER_CONFIG(same as--config) andHOOKS_ADAPTER_HOME(same as--home) - Any missing file is silently skipped; if an existing file has issues, it only produces diagnostics, it never blocks startup
- Config file format details: docs/CONFIG.md
Event Mapping
Every harness's event names map to a set of canonical events (session:start, tool:before, ...), which then bind to dsh extension points:
| Canonical event | claude dialect | codex dialect | opencode dialect | dsh extension point |
|---|---|---|---|---|
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 |
Full semantics (blockability, matcher rules, payload fields): docs/EVENTS.md.
Contract
- stdin JSON:
session_id,transcript_path,cwd,hook_event_name,permission_mode+ event fields (tool_name/tool_input/tool_use_id/tool_response/prompt/source...) - Exit codes:
0= allow (when stdout is JSON, the decision is parsed from it);2= block (stderr is the reason); any other non-zero = non-blocking error - stdout JSON:
decision,continue/stopReason,systemMessage,hookSpecificOutput.permissionDecision(allow/deny/ask),additionalContext,updatedInput; oracle answers{ok: true|false, reason} - Multi-hook folding:
deny > ask > allow; anycontinue:falsestops; context accumulates in hook order - Details and the stdio protocol: docs/CONTRACT.md
Testing
node --test
(The default test-discovery mode runs all 111 tests; helper scripts live in test-support/ and are not mistaken for tests.)
License
Released under the MIT License.
nexu-io/open-design
freestylefly/awesome-gpt-image-2
anywhere-labs/dsh-desktop
walkinglabs/learn-harness-engineering
awesome-dsh-plugin/awesome-dsh-plugin
MemTensor/MemOS