dsh-workbuddy-mcp
by ksoulness
README.md
# DSH WorkBuddy Kit
[中文](README.md) · [English](README.en.md)
[](LICENSE) [](https://nodejs.org) [](https://modelcontextprotocol.io)
> **DeepSeek Harness 负责想清楚并验收,本机 WorkBuddy 负责动手做:一次派发、等到终态、自己复核证据、按完整 sessionId 归档。**
一个本机工具包,把「DSH 派活」和「WorkBuddy 干活」接成闭环。两个前门共用同一张工具表:
- **原生 DSH 工具**(Cordis 插件 `dsh-workbuddy-kit`,注册 `wb_*` 到 `ctx.tools`)
- **stdio MCP 服务**(`dsh-workbuddy-mcp`,任何 MCP 客户端可用)
两者行为完全一致,因为它们调用的是同一份 `src/tools.mjs`。
## 这是插件、技能,还是 MCP 服务?
四个形态,共用同一张 src/tools.mjs,所以行为一致;区别是**谁在用**和**怎么装**:
| 形态 | 文件 | 怎么装 | 作用 | 改完要重启吗 |
| --- | --- | --- | --- | --- |
| **DSH 插件**(主体) | plugin/index.mjs + cordis.patch.yml | dsh plugin --profile <profile> add . | 把 18 个 wb_* 注册进 ctx.tools,在 DSH 里直接调用 | **要**(工具表在启动时快照) |
| **技能**(附带,可选) | skills/delegate-to-workbuddy/SKILL.md | 拷到 <DSH_HOME>/agents-home/skills/ | 教 agent 何时派发、怎么等终态、怎么复核证据、怎么按 ID 归档 | 不要(热加载) |
| **MCP stdio 服务** | mcp/server.mjs | 写进任意 MCP 客户端配置 | 给非 DSH 的客户端(Claude / Codex / 自研)用同一套工具 | 不要 |
| **CLI** | in/wb.mjs |
ode bin/wb.mjs … 或
pm i -g | 手工验证、脚本化,不接任何客户端 | 不要 |
**插件 = 能力(工具),技能 = 行为指南(什么时候用、按什么顺序用)。** 插件可以单独用;技能离开插件没有意义。只想要工具就装插件;想让 agent 自觉按「派发 → 等终态 → 复核 → 归档」的规矩办事,再叠加技能。
## 解决什么问题
| 没有它的时候 | 有了它 |
| --- | --- |
| 想让 WorkBuddy 干活,只能把上下文、路径、要求手打一遍,还得自己盯着它跑完没有 | 一次派发拿到 `runId`,`wait_run` 拉到终态;`state` / `stopReason` / `toolCalls` / `workspaceChanges` 都是结构化字段,超时不算终态 |
| 「它说做完了」= 做完了? | 工具返回的是**证据**:git 前后对比、工具调用数、完整转录、sha256 归档副本;调用方自己复核才算完 |
| 派出去的会话在桌面 App 里看不见,等于没发生过 | 默认自动登记进 App 索引,会话就出现在**同一个工作区**下(跟随 DSH 当前目录),点开就是完整对话 |
| 想批量盯桌面 App 的定时任务,只能一个个点开看 | 三个只读工具直接读 `workbuddy.db`:任务清单、每次运行、成功率与失败码、逾期任务 |
| 它的权限边界靠猜 | 实测过的矩阵写死在文档里:工作区内读写用 `acceptEdits`;跨目录读、`WebFetch`/`curl` 要 `bypassPermissions`;`WebSearch` 无头下不可用 |
## 它控制的是什么
本机 WorkBuddy 桌面版(腾讯,实测 `5.8.3`)在自己的安装目录里**自带一个 CodeBuddy agent CLI**:
```text
D:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\
bin\codebuddy # @genie/agent-cli 入口(实测 2.161.0)
dist\codebuddy-lite-wb.mjs # 当前发行只带 lite-wb 包
```
这个 CLI 支持无头运行:`-p/--print`、`--output-format stream-json`、`--session-id`、`--permission-mode`、`--model`、`--max-turns`、`--effort`、`--mcp-config`……本工具包就是把这条通道变成一组可验收的工具:
| 工具 | 作用 |
| --- | --- |
| `wb_doctor` | 只读自检:CLI、Node、数据目录、版本、自定义模型、最近是否撞到登录墙 |
| `wb_list_capabilities` | 真实可用的 permission mode / 自定义模型 |
| `wb_list_projects` / `wb_list_sessions` | 找到 WorkBuddy 的工程目录与会话 |
| `wb_start_review` | **只读**派发(`--permission-mode plan`,并拒绝 Write/Edit/NotebookEdit) |
| `wb_start_run` | 实施派发(默认 `acceptEdits`) |
| `wb_wait_run` / `wb_get_run` | 盯到终态;超时**不算**终态(`hostMustCallWaitRunAgain`) |
| `wb_cancel_run` | 终止整个子进程树 |
| `wb_list_runs` | 本机派发台账 |
| `wb_read_session` | 直接读对话记录(消息、工具调用、模型),自己复核 |
| `wb_archive_session` | 按完整 sessionId 归档:带 SHA-256 的副本 + manifest + 索引 |
| `wb_list_automations` | **桌面 App 调度器**:定时任务清单(节奏/下次触发/最近结果/模型/权限/推送) |
| `wb_get_automation_runs` | 某个定时任务的运行历史、成功率、失败码 |
| `wb_automation_health` | 调度器体检:生效 vs 软删、总运行/失败率、失败码分布、过期未触发 |
调度器三个工具**只读** `workbuddy.db`(`automations` / `automation_runs` / `automation_runtime_state`),不写任何调度状态。
## 最后效果
真实跑过一次的形态(这就是本仓库在制作机上的验收记录):
```text
你 ❯ 用 WorkBuddy 只读核对 notes.md 里的标记,别改文件
DSH ❯ wb_start_review { workspace:"E:\\Code\\demo",
task:"读出形如 DWB-ACCEPT-XXXX 的标记,原样引用,不要修改任何文件" }
→ runId, sessionId:<uuid>, permissionMode:"plan", state:"running"
wb_wait_run { runId } → state:"succeeded", stopReason:"end_turn", toolCalls:3, durationMs:22485
(assistantText 里就是 "DWB-ACCEPT-9F3A,位于第 3 行",自己去文件对一遍)
wb_archive_session { sessionId:"<uuid>" }
→ archived:true, bytes:15124, sha256:a0c6cc74…, target:…\\session-archive\\<date>\\e-Code-demo\\
```
拿到的不是「已完成」四个字,而是:结论 + 工具调用次数 + 真实 sessionId + 归档摘要。
## 安装
前置:本机已装 WorkBuddy 桌面版;Node ≥ 22.19(跑 DSH 用,本工具包自身零依赖)。
### A. 丢给 AI(推荐,零手工)
把下面这段原样发给任何有终端权限的 AI(DSH / Claude / Codex 都行):
``text
请安装并验收 https://github.com/ksoulness/dsh-workbuddy-kit 这个 DSH 插件。
先读 https://raw.githubusercontent.com/ksoulness/dsh-workbuddy-kit/main/AGENTS.md 并按它执行;读不到就按下面四步:
1. 前置检查:node -v 需 >= 22.19;git、dsh 可用;确认本机存在 WorkBuddy 的 CLI 目录
(Windows: C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\bin)。
2. 安装:git clone 仓库后 dsh plugin --profile <我的profile> add .,
再用 dsh --profile <profile> --dump-config 确认出现 dsh-workbuddy-kit 行。
3. 验收:
pm test(应 pass 65 / fail 0)、
pm run verify(应 18 tools / 0 failures)、
ode bin/wb.mjs doctor(应 found true)。
4. 报告:用表格列出每一步的命令、真实输出、结论;最后告诉我是否需要重启 DSH。
铁律:不要提权、不要 sudo、不要写 workbuddy.db、不要改我的 models.json 和会话文件、
不要调用 wb_publish_session、不要在没有亲眼看到输出时声称成功。失败就贴原始报错。
``
装完记得**重启 DSH**(工具表在启动时快照);技能是热加载的,改了立刻生效。
### B. 原生 DSH 工具(手工)
```sh
git clone <this repo> dsh-workbuddy-kit
cd dsh-workbuddy-kit
dsh plugin --profile web add .
dsh --profile web --dump-config | grep -n workbuddy
```
重启 DSH(Web 端刷新页面)后,`wb_*` 就是原生工具。
### C. MCP stdio(任何 MCP 客户端)
`cordis.patch.yml` 里追加一行,不要在同一个 profile 同时开 A 和 B(工具名会重复):
```yaml
- id: mcp-workbuddy
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: workbuddy
transport: stdio
command: node
args: ['E:\\Code\\dsh-workbuddy-kit\\mcp\\server.mjs']
```
### D. 离线 tarball(不联网、不装 npm 包)
每次 Release 都附带 dsh-workbuddy-kit-<version>.tgz,内含 in/ mcp/ plugin/ src/ docs/ skills/ scripts/ LICENSE README*:
`sh
npm pack # 或直接从 Release 下载
npm i -g ./dsh-workbuddy-kit-0.1.0.tgz # 拿到 dwb / dsh-workbuddy-mcp 两个命令
dwb doctor
`
不装全局也行:解包后
ode package/bin/wb.mjs doctor 就能用。
### E. 手工验证(不接任何客户端)
```sh
node bin/wb.mjs doctor
node bin/wb.mjs review --workspace E:\Code\demo --task "只读核对 README 第 1 节,给出精确行号" --wait 300
node bin/wb.mjs sessions --limit 5
```
## 关键约束(都是实测出来的,不是猜的)
- **CLI 必须指向 WorkBuddy 的数据目录**:不设 `CODEBUDDY_CONFIG_DIR` / `WORKBUDDY_CONFIG_DIR`,CLI 会去读 `~/.codebuddy`,那里没有登录态。本工具包自动指向 `~/.workbuddy`。
- **当前发行只带 `lite-wb` 包**:必须带 `CODEBUDDY_FORCE_LITE_WB_BUNDLE=1`,否则启动器会去找不存在的 `dist/codebuddy`。工具包自动判定。
- **无头 CLI 用不了桌面版的登录态**:账号默认模型会直接返回 `Authentication required`。工具包默认挑 `<configDir>/models.json` 里的第一个自定义模型(本机实测 `custom-local:cn:space-bunny` 可用),并把这个选择记在 `modelSource` 里;用 `DWB_DEFAULT_MODEL` 可覆盖。
- **无头下工具调用要 `-y`**:腾讯文档明确「`-y` 是非交互模式必需参数」,否则读写/命令会被拦(本仓库实测:`plan` 模式下 Bash 被拒,模型改走 Grep/Read)。`wb_start_run` 提供显式的 `skipPermissions: true`,从不默认打开;HIGH/CRITICAL 命令仍可能要求确认。
- **会话落在 `<configDir>/projects/<工程名>/<sessionId>.jsonl`**,工程名是工作目录的映射(`E:\Code\acme-app` → `e-Code-acme-app`)。归档就是对这个文件做带摘要的副本。
- **归档不动厂商数据**:默认 `copy`,WorkBuddy 自己的会话列表不变;`move` 是显式的可逆选项。
## 桌面 App 的调度器(定时任务)
定时任务**不在文件里**,在 `~/.workbuddy/workbuddy.db` 的三张表(`automations.db` 是 0 字节的遗留空文件):
| 表 | 内容 |
| --- | --- |
| `automations` | 任务定义:名字、完整 prompt、状态、`rrule`、`next_run_at`、`last_run_at`、模型、权限模式、工作区、推送渠道、`deleted_at` |
| `automation_runs` | 每次触发:`thread_id`、状态、`result_success`、`failure_code`、`reason_code`、标题 |
| `automation_runtime_state` | `running`、`last_error` |
```sh
node bin/wb.mjs automations # 生效中的任务
node bin/wb.mjs automations --all # 含软删除
node bin/wb.mjs automations --prompt # 附带 prompt 预览(默认不返回)
node bin/wb.mjs automation-runs --name "研究流水线监护" --limit 5 --failures
node bin/wb.mjs automation-health
```
两个必须知道的坑(实测):
- **软删除行 `status` 仍是 `ACTIVE`**:真正的活性信号是 `deleted_at`。本工具包输出 `softDeleted` 与 `live` 两个显式字段,避免把僵尸任务当活跃任务。
- **prompt 全文就在库里**(很长、很私人):默认只返回 `promptBytes`,要预览得显式 `includePrompt: true`,且截断到 2000 字符。
读法是 `readOnly: true` 打开 SQLite(WAL 读者不影响正在运行的桌面 App)。调度器**只读**:暂停/改 RRULE/新建/删除都要走桌面自己的 IPC 或 UI,直写会让界面与库不一致。
## 让派发的会话出现在桌面 App 里(写库,可撤销)
默认情况下 CLI 会话**不会**出现在桌面 App 的会话列表里:它只写 `projects/<slug>/<id>.jsonl`,不写 App 自己的索引。桌面列表的查询是:
```sql
SELECT … FROM sessions
WHERE transport = 'local' AND deleted_at IS NULL
AND (user_id = ? OR user_id = '')
AND id IN (…)
```
所以转录再完整,**没有那一行 `sessions` 记录就是不可见**。补上那一行(外加一行 `session_usage`)就会出现:
```sh
node bin/wb.mjs publish --session <sessionId> --title "【DSH 派发】…"
node bin/wb.mjs published # 本工具包发布过哪些
node bin/wb.mjs unpublish --session <sessionId> # 只删自己插的那两行
```
工具:`wb_publish_session` / `wb_unpublish_session` / `wb_list_published`。
`publish` 也接受 `--run <runId>`,直接从派发台账解析 sessionId。**默认就会发布**:派发跑完自动登记,结果记在运行的 `publishResult` 字段里,发布失败不会改写运行的终态。要**不**污染 App 列表就显式传 `publishToApp: false`(CLI 是 `--no-publish`),此时只写转录、不进 App。
> **工作区归属 = DSH 会话当前的工作目录**(不是写死的路径)。省略 `workspace` 时插件每次调用都会向 `workingDirectory` 服务要当前目录,所以你在 DSH 里 `cd` 到哪,WorkBuddy 就在哪开工;解析不到就报错,不会退回 harness 安装目录。`workspace` 只在你需要**故意**换目录时显式传。
>
> **工作区归属由 `workspace` 决定**:桌面 App 按会话的 `cwd` 分组,所以派发时把 `workspace` 设成你平时用 DSH 的那个目录(例如 `E:\Code\acme-app`),这个会话就会出现在同一个工作区下;换成别的目录,就会多出一个新工作区。`wb_start_run` / `wb_start_review` 现在 `workspace` 可省略,省略时用当前 DSH 会话的工作目录(插件注入 `workingDirectory` 服务);解析不到时**直接报错**而不是退回 `process.cwd()`——那是 harness 安装目录,发到那里会让 App 多出一个垃圾工作区。
写什么:一行 `sessions`(`id`、`cwd` 归一成 `E:/Code/x`、`user_id` 取当前登录用户、`title`、`status`、`created_at`/`updated_at`/`last_activity_at`、`transport='local'`、`source_mode='craft'`、`mode='craft'`、`model`)+ 一行 `session_usage`(`size` = 转录字节数)。**转录文件一个字节都不改。**
安全边界:
- 会话不存在、读不到 `cwd`、或库里已有该 id → **拒绝**并说明原因;
- 每次插入记进 `<stateDir>/published.jsonl`;`unpublish` 只删账本里登记过的行,且不动其他会话行;
- 建议先备份库(`docs/setup.md` 有一条实测命令);
- 手法不是本文独创:GitHub 上的 [`workbuddy-account-migrate`](https://github.com/xiaoliuzhuan666/workbuddy-account-migrate) 用同样方式(写 `sessions`/`session_usage` + 搬 `projects/<slug>/`)做跨账号会话复制,并明确写了「不写 sessions 行,客户端就找不到」。
## 与已有项目的关系
搜过一圈,方向不同,没有一个在做「DSH 派活给 WorkBuddy 并验收」:
| 项目 | 它做什么 | 与本仓库的区别 |
| --- | --- | --- |
| [`dsh-connect-workbuddy`](https://dsh.directory/plugins/dingminhua/dsh-connect-workbuddy) / [`corrinehu/dsh-workbuddy-connect`](https://github.com/corrinehu/dsh-workbuddy-connect) | 把 WorkBuddy 的**模型**接进 DSH 模型选择器 + 账号池/积分 | 解决「用哪个模型」,不涉及把任务交给 WorkBuddy agent 执行 |
| [`ithtelab/workbuddy-manager`](https://github.com/ithtelab/workbuddy-manager) | WorkBuddy/CodeBuddy 账号池 + OpenAI 兼容反代(`workbuddy2api`) | 网关与账号运维,不是 agent 委派 |
| 腾讯官方 [无头模式文档](https://www.workbuddy.ai/docs/zh/cli/headless) | `-p` / `--output-format` / `--resume` 等 CLI 用法 | 只用 CLI,无派发台账、无终态机、无会话归档 |
| [`ksoulness/dsh-codex-kit`](https://github.com/ksoulness/dsh-codex-kit) | Codex → DSH 的委派(本仓库的结构参照) | 方向相反:本仓库是 DSH → WorkBuddy |
## 安全与边界
- `permissionMode: plan` 与拒绝写工具是**给模型的声明**,不是进程级沙箱:CLI 仍以自己的权限运行,Bash 之类的工具能做什么由它自己的策略决定。
- 派发内容是「任务描述」,不是源码:只说工作区、范围、授权、验收标准,让 WorkBuddy 自己去读文件。
- 不读取、不打印、不落盘任何 token / cookie / API key;`models.json` 只取 `id`、`vendor` 这类身份字段。
- 归档在会话仍活跃、或本工具包没有该会话的终态记录、或目标文件已存在时会**拒绝**并给出原因,不伪造 `archived:true`。
- 本包不是腾讯 / DeepSeek 官方产品。
## 已验证 / 未验证
制作机(Windows、Node `v24.19.0`、WorkBuddy `5.8.3`、CLI `2.161.0`、DSH `0.2.1-alpha.2`)实测:
- ✅ 真实只读派发 → `succeeded` / `end_turn` / 3 次工具调用 / 22.5s,`assistantText` 与工作区文件一致;
- ✅ 真实实施派发 → `succeeded` / `end_turn`,工作区里确实多出 `hello.txt`,内容与任务要求逐字一致(`DWB-RUN-OK`);
- ✅ 真实归档两次:`archived:true`,15124 / 7973 字节,`sha256:a0c6cc74…` / `9887ba08…`;
- ✅ `wb_doctor` 正确识别安装路径、自带 Node、`liteBundleOnly`、自定义模型;
- ✅ `node --test` 65/65:流解析、工程名映射、schema 子集、MCP 握手、归档的全部拒绝分支、调度器只读读取(含软删除与失败码)、git 侧改动证据、发布/撤销/默认发布;
- ✅ MCP stdio 真实握手(`initialize` + `tools/list`);
- ✅ 在真实 cordis 里挂载插件(`inject: ['tools', 'workingDirectory']`)→ 18 个工具全部通过 `dsh-tools` 自己的 `assertObjectJsonSchema` / `assertSupportedJsonSchema` 校验(`npm run verify`);
- ✅ 委派真实产出:WorkBuddy 按任务在 `E:\Code\demo` 写出 `CHEATSHEET.md`,其中 5 条命令逐字核对全部命中源文档(`README.md:71/72/85/94/95`);另一次运行由 `workspaceChanges` 抓到新增 `demo-workspace-change.md`(`addedCount: 1`);
- ✅ 跟随 DSH 工作目录:同一会话在 E:\Code\acme-app 时省略 workspace → 落在 E:\Code\acme-app;把 DSH cwd 切到 E:\Code\dsh-workbuddy-kit 后再派发 → 落在 E:\Code\dsh-workbuddy-kit(projectSlug: e-Code-dsh-workbuddy-kit),没有写死路径;
- ✅ 诚实的失败样本:跨 `--add-dir` 目录读取被拦(即使 `-y`),运行层仍 `succeeded` 但零改动 —— 证明「终态 ≠ 完成」,必须看 `workspaceChanges` / 文件;
- ✅ 桌面调度器真实读取:6 条生效 + 3 条软删、累计 1000+ 次运行(成功率约 0.91)、失败码分布与桌面 App 界面一致;
- ✅ `dsh plugin --profile web add .` 装进本机 profile,`dsh --profile web --dump-config` 出现 `dsh-workbuddy-kit` 行。
未验证:其他 WorkBuddy 版本 / 其他平台的路径、`--acp` 与 `--serve` 通道、`move` 归档对桌面端索引的影响。
## 目录结构
```text
src/discover.mjs 安装/CLI/Node/数据目录定位,工程名映射
src/cli.mjs 组装无头调用、spawn、终止进程树
src/parse.mjs stream-json 与 session JSONL 解析
src/runs.mjs 派发台账、终态判定、恢复与取消
src/sessions.mjs 会话枚举/读取/精确归档
src/automations.mjs 桌面调度器只读(automations / runs / runtime_state)
src/tools.mjs 唯一工具表(两个前门共用)
plugin/index.mjs 原生 DSH 插件
mcp/server.mjs stdio MCP 服务(零依赖)
bin/wb.mjs 手工 CLI
cordis.patch.yml DSH bundle 层(只插一行)
skills/delegate-to-workbuddy/SKILL.md
docs/ 接入、架构、安全
tests/ 37 个 node:test 用例
```
## 文档
- [接入与故障定位](docs/setup.md)
- [架构与状态机](docs/architecture.md)
- [安全与边界](docs/security.md)
- [DSH 交接清单](DSH-HANDOFF.md)
## 许可
MIT。WorkBuddy、CodeBuddy、DeepSeek Harness 不在本包内,各按其自身许可与条款。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues