Skip to content

hooks 子系统让外部 CLI 代理——Claude CodeCodex——通过它们原生的 command-hook 机制参与 dsh 的代理循环。桥接插件读取该方言的 hooks.json 匹配器组,通过 ctx.shell 执行命中的 hook,把其 exit-code/stdout 契约解码成中性形态,以最严格的规则合并结果,并把一切记录为持久的 hook/* 会话事件。

角色
@deepseek-ai/dsh-hook-protocol方言中立的协议库(非插件)
@deepseek-ai/dsh-hooks-claude-codeClaude Code hook 桥接插件
@deepseek-ai/dsh-hooks-codexCodex hook 桥接插件

共享的协议库

packages/hooks/hook-protocol 是 Claude Code / Codex hook 通信协议的共享核心。它不是一个 Cordis 插件——它既不注册任何东西,也不注入任何东西。它是一堆方言中立的原语,供两个桥接插件导入,这样两者都无需重复实现协议中相同的那些部分。Codex 刻意重新实现的是 Claude Code hook 协议的子集——同样的 hooks.json 匹配器组形态、同样的 exit-code/stdout 输出契约、同样的命令 hook 执行模型——因此真正共享的部分留在这里,而每个桥接只负责那些不同的部分。

取自 README 的拆分表:

关注点这里(dsh-hook-protocol桥接插件
匹配器校验 + 测试matcherDiagnostic(pattern, mode)matchesMatcher(pattern, query, mode)挑选自己的 modeclaude-code = 字面量或正则,codex = 始终正则),拒绝携带诊断的配置组
运行 hookrunHook(bash, hook, opts, now) —— 通过 ctx.shell 发送 stdin 载荷 + 环境,并解码针对每个事件构建 stdin 载荷 + 该方言的 环境
解码输出parseHookOutput(exit, stdout, stderr) → 中性的 HookOutputHookOutput 映射到扩展点特有的类型化 Decision
合并 N 个 hookmergeHookOutputs(outputs) → 最严格的 MergedHookOutcome
持久记录appendHookInvoked / appendHookResulthook/* 会话事件)在每次调用前后调用它们
分离式运行createDetachedRuns() —— 追踪 fire-and-forget 链;drain() 先中止再等待signal 传给每个分离的 runHook,将 drain 注册为自身的效果处置器

该库位于 packages/hooks/hook-protocol/src/——matcher.ts(模式诊断与匹配)、runner.ts(通过 ShellExecutor 形态的 bash 参数执行 hook)、merge.ts(最严格合并)、events.tshook/* 事件形态)、codec.tsdetached.ts(静止态追踪)以及 types.ts

桥接插件

dsh-hooks-claude-codedsh-hooks-codex 是薄适配器:每个都拥有其方言的配置(src/config.ts)、每个事件的 stdin 载荷与环境,以及从中性 HookOutput 到其扩展点所期望的类型化决策的映射。匹配器模式因设计而异——Claude Code 把匹配器当作字面量或正则,Codex 始终当作正则——而一个携带诊断的配置组会由拥有它的那个桥接拒绝。

配置(两个桥接,取自 src/config.ts)驱动:哪些 hook 事件被启用、匹配器组,以及被执行的命令行。hook 执行经由 ctx.shell,因此一条 hook 命令会继承会话的沙箱/策略世界——包括所组合进来的任意 shell provider(参见 Shell & Terminal)。

hook 决策如何流转

text
agent loop reaches an extension point (e.g. PreToolUse / Stop)


bridge plugin builds stdin payload + env for its dialect


runHook(bash, hook, opts) ──► ctx.shell executes the hook command


parseHookOutput(exit, stdout, stderr) ──► neutral HookOutput


bridge maps HookOutput ──► typed Decision (allow / deny / modify ...)


mergeHookOutputs across hooks ──► most-restrictive MergedHookOutcome


appendHookInvoked / appendHookResult ──► durable hook/* session events

分离式(fire-and-forget)运行由 createDetachedRuns() 追踪,以便进程销毁时可以对它们执行 drain()——先中止再等待——而不会泄漏链。

这对 dsh 的意义

hooks 子系统是 dsh 与更广的代理生态互操作的方式之一:与其重新实现 Claude Code 或 Codex,dsh 组合可以承载它们的 hook 协议,让它们的命令 hook 对 dsh 会话生效。这与把那些 CLI 当作代理运行的 subagent 后端(subagent-claude-codesubagent-codex)互为镜像(参见 Subagents)——在那里的 CLI 是工人,而在这里它的 hook 契约是扩展点。

关键源文件

仓库相对路径提供内容
packages/hooks/hook-protocol/src/matcher.ts匹配器诊断 + 匹配
packages/hooks/hook-protocol/src/runner.ts通过 ctx.shellrunHook
packages/hooks/hook-protocol/src/merge.tsmergeHookOutputs
packages/hooks/hook-protocol/src/events.tshook/* 会话事件
packages/hooks/hooks-claude-code/src/config.tsClaude Code 桥接配置
packages/hooks/hooks-codex/src/config.tsCodex 桥接配置

延伸阅读

  • Subagents —— 另一个方向:把 Claude Code / Codex 作为 subagent 运行
  • Shell & Terminal —— ctx.shell,hook 运行的执行世界
  • Session Management —— hook/* 事件在日志中的落点
  • Repo: packages/hooks/hook-protocol/README.mdpackages/hooks/hooks-claude-code/README.mdpackages/hooks/hooks-codex/README.md