dsh-tower 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.en.md +22 -8
  4. package/README.md +20 -8
  5. package/cordis.patch.yml +10 -0
  6. package/docs/README.md +8 -4
  7. package/docs/architecture/overview.md +13 -9
  8. package/docs/faq.md +33 -2
  9. package/docs/guide/hot-reload.md +189 -0
  10. package/docs/guide/install-upgrade-migration.md +11 -0
  11. package/docs/guide/quickstart.md +33 -1
  12. package/docs/operations/runbook.md +23 -0
  13. package/docs/operations/security.md +23 -0
  14. package/docs/reference/blockers.md +10 -2
  15. package/docs/reference/config.md +65 -2
  16. package/docs/reference/doctor.md +31 -0
  17. package/docs/reference/errors.md +50 -0
  18. package/docs/reference/events.md +45 -0
  19. package/docs/reference/operations.md +52 -0
  20. package/docs/reference/testing.md +61 -0
  21. package/docs/reference/tools.md +65 -23
  22. package/docs/zh/README.md +58 -0
  23. package/docs/zh/faq.md +72 -0
  24. package/docs/zh/guide/hot-reload.md +178 -0
  25. package/docs/zh/guide/install-upgrade-migration.md +57 -0
  26. package/docs/zh/guide/quickstart.md +96 -0
  27. package/docs/zh/operations/security.md +42 -0
  28. package/docs/zh/reference/blockers.md +62 -0
  29. package/docs/zh/reference/config.md +113 -0
  30. package/docs/zh/reference/doctor.md +30 -0
  31. package/docs/zh/reference/errors.md +52 -0
  32. package/docs/zh/reference/events.md +46 -0
  33. package/docs/zh/reference/operations.md +48 -0
  34. package/docs/zh/reference/testing.md +60 -0
  35. package/docs/zh/reference/tools.md +75 -0
  36. package/index.js +261 -13
  37. package/lib/batch.js +578 -0
  38. package/lib/config.js +270 -8
  39. package/lib/doctor.js +165 -0
  40. package/lib/errors.js +111 -0
  41. package/lib/hmr.js +196 -0
  42. package/lib/leases.js +317 -0
  43. package/lib/legacy.js +413 -0
  44. package/lib/modeStore.js +28 -1
  45. package/lib/store.js +24 -1
  46. package/lib/tests.js +628 -0
  47. package/lib/tools.js +1554 -182
  48. package/lib/tower.js +957 -102
  49. package/lib/workspaceLock.js +117 -0
  50. package/lib/worktreeOwnership.js +166 -0
  51. package/package.json +4 -1
  52. package/reviewer.js +1 -0
  53. package/scripts/check-docs.mjs +135 -0
  54. package/scripts/enable-dev-hmr.mjs +347 -0
  55. package/skills/tower-review.md +4 -2
  56. package/skills/tower-worker-protocol.md +3 -1
  57. package/worker.js +4 -2
@@ -0,0 +1,58 @@
1
+ # dsh-tower 中文文档
2
+
3
+ [English docs](../README.md) | **简体中文**
4
+
5
+ 英文文档是权威来源;本页是中文翻译入口。英文文档位于 [docs/](../README.md),仓库主页见
6
+ [README.md](../../README.md)。
7
+
8
+ ## 2.1 能力概览
9
+
10
+ - **只读健康检查**:[Doctor](reference/doctor.md)(`tower_doctor`)检查 git、state、配置、preset、
11
+ workspace 锁、roster、worktree 归属、测试策略与租约,不写任何状态。
12
+ - **测试发现与完成证据**:[测试、发现与证据](reference/testing.md)。仓库可在
13
+ `package.json.tower.testDiscovery` 声明发现命令;回执可自动附加到 `tower_complete`,
14
+ `gates.requireTests` 可要求针对被审查 tip 的通过回执。
15
+ - **证据批次、咨询式租约、旧版导入**:[批次、租约与旧版导入](reference/operations.md)
16
+ (`tower_batch`、`tower_resource`、`tower_import_legacy`、`tower_legacy_map`)。
17
+ - **workspace 锁与计划模式**:`mode.workspaceLock` 限制每个仓库同时只有一个 Tower 会话,
18
+ `mode.workspaceLockScope:"mutations"` 对只读工具跳过锁;`mode.planModePolicy` 控制进入 Tower
19
+ 时如何处理活跃的 plan mode。
20
+ - **可观察性**:成功的工具调用发出进程内 [Tower 事件](reference/events.md);稳定
21
+ [工具错误码](reference/errors.md) 与 [blocker code](reference/blockers.md) 可被程序匹配。
22
+ - **工具面**:本构建最多 28 个工具(含可选的 `tower_control` 与 `tower_reload`);详见
23
+ [工具参考](reference/tools.md)。
24
+
25
+ ## 选择阅读路径
26
+
27
+ | 你是 | 从这里开始 |
28
+ |---|---|
29
+ | 新用户 | [快速开始](guide/quickstart.md) → [工具参考](reference/tools.md) → [第一个 mission 示例(英文)](../examples/first-mission.md) |
30
+ | 运维 | [运维 runbook(英文)](../operations/runbook.md) → [blocker 与失败码](reference/blockers.md) → [错误码](reference/errors.md) → [事件](reference/events.md) → [Doctor](reference/doctor.md) → [测试](reference/testing.md) → [批次/租约/旧版](reference/operations.md) → [安全模型](operations/security.md) |
31
+ | 维护者 | [架构与状态机(英文)](../architecture/overview.md) → [配置参考](reference/config.md) → [热重载](guide/hot-reload.md) → [贡献指南](../../CONTRIBUTING.md) |
32
+
33
+ 参考页是当前发行版的权威说明;CI 中的 `npm run docs:check` 会校验文档与实现一致(包括中文页)。
34
+
35
+ ## 全部中文页面
36
+
37
+ - [快速开始](guide/quickstart.md)
38
+ - [安装、升级与迁移](guide/install-upgrade-migration.md)
39
+ - [热重载(开发)](guide/hot-reload.md)
40
+ - [工具参考](reference/tools.md)
41
+ - [配置参考](reference/config.md)
42
+ - [blocker 与失败码](reference/blockers.md)
43
+ - [工具错误码](reference/errors.md)
44
+ - [Tower 事件](reference/events.md)
45
+ - [测试、发现与证据](reference/testing.md)
46
+ - [批次、租约与旧版导入](reference/operations.md)
47
+ - [Doctor](reference/doctor.md)
48
+ - [FAQ](faq.md)
49
+ - [安全模型](operations/security.md)
50
+
51
+ 以下页面目前只有英文版:[架构与状态机](../architecture/overview.md)、
52
+ [运维 runbook](../operations/runbook.md)、[示例:第一个 mission](../examples/first-mission.md)。
53
+
54
+ ## 语言 / Language
55
+
56
+ - [English docs](../README.md)
57
+ - **中文文档**(本页)
58
+ - [仓库主页 README(中文)](../../README.md) · [English README](../../README.en.md)
package/docs/zh/faq.md ADDED
@@ -0,0 +1,72 @@
1
+ # FAQ
2
+
3
+ [English](../faq.md) | **简体中文**
4
+
5
+ **为什么 worker 刚完成,`tower_preflight` 就因 `no-review` 失败?**
6
+ 这是预期行为:`completed` 的 mission 正在等待审查。spawn 一个 reviewer;干净审查后门禁就会变绿。
7
+
8
+ **需要轮询 worker 吗?**
9
+ 不需要。worker 的最终报告会送回本会话;`tower_roster` 显示实时活动。轮询只会浪费回合。
10
+
11
+ **`/tower` 显示模式已开启,但没有 `tower_control`。**
12
+ `mode.allowAgentControl` 关闭了。在 profile patch 中把它设为 `true` 并重启 DSH。
13
+
14
+ **合并钩子要跑几分钟,`tower_merge` 失败了。**
15
+ 用更大的 `timeoutMs` 重试:`tower_merge({ missionId, timeoutMs: 1800000 })`。默认超时是 15 分钟,
16
+ 合并输出最多缓冲 64 MB。钩子失败返回 `merge-hook-failed`,并保持仓库干净以便重试。
17
+
18
+ **合并提交已存在,但 Tower 说 mission 未合并。**
19
+ 再次调用 `tower_merge`。它会检测已落地的合并提交(`reconciled`),或恢复“命令失败但提交已存在”的
20
+ 情况(`recoveredFromFailure`)并记录。
21
+
22
+ **`tower_rebase` 返回 `alreadyMerged` 且什么都没改。**
23
+ 正确:分支已经包含在基线中。此时调用 `tower_merge` 记录已落地的合并,而不是 rebase。
24
+
25
+ **reviewer 能修改文件吗?**
26
+ 不能。reviewer 使用只读的 `tower-reviewer` preset(没有 write/edit/pwsh);reviewer spawn 之后分支
27
+ 被改动,审查会被拒绝。mission 之外的问题用 `tower_finding` 记录,不要直接改文件。
28
+
29
+ **`tower_finding` 与 `tower_findings` 有什么区别?**
30
+ `tower_finding` 创建单条 finding(`open`)。`tower_findings` 是生命周期工具:列出/读取,并在
31
+ `open → triaged → fixed`/`wontfix` 之间流转(允许重新打开)。只有控制塔或 worker 可以修改状态;
32
+ roster 中的 reviewer 只能列出和读取。
33
+
34
+ **如何只给一个仓库设置配置?**
35
+ 创建 `<git-common-dir>/dsh-tower/config.json`(普通检出通常是 `.git/dsh-tower/config.json`),
36
+ 使用与 profile 配置相同的 JSON 分组(`review`、`merge`、`worktrees`、`spawn`,以及兼容别名)。
37
+ 仓库配置层优先于 profile;单次工具参数又优先于两者。不需要重启,因为每次 Tower 工具打开仓库时都会
38
+ 读取该文件。
39
+
40
+ **worker 能用 Tower 工具 merge 或 spawn 吗?**
41
+ 不能。worker preset 只包含 mission/进度工具;merge 与 spawn 属于上级控制塔。
42
+
43
+ **worker 卡住或失控了,怎么停下它?**
44
+ 用 `tower_interrupt({to:"worker-m1",reason:"..."})`;它会调用宿主子代理中断,并把 roster 条目记录为
45
+ `interrupted`。如果你已经中断过它并想要一个全新的 owner,用 `takeover:true` spawn。仍在运行的 worker
46
+ 不能被接管(`takeover-refused`)。
47
+
48
+ **两个会话想驱动同一个仓库。**
49
+ 开启 `mode.workspaceLock:true`:第一个调用仓库级工具的 Tower 会话拥有
50
+ `<git-common-dir>/dsh-tower/tower.lock`;其他会话会得到 `[tower:workspace-busy]`,直到 owner 执行
51
+ `/tower off` 或锁过期(`mode.workspaceStaleMs`)。
52
+
53
+ **能否不重启 DSH 就重载插件?**
54
+ 可以,在装有 DSH HMR 服务的 profile 中:`/tower reload` 或 `tower_reload` 工具会对账 profile 并替换
55
+ 变更的 linked 包。先运行一次 `npm run dev:hmr` 把仓库加入 profile 的 HMR 监视 root(然后重启 DSH
56
+ 一次);见[热重载](guide/hot-reload.md)。`plugin_manager` 的安装/更新本身就会即时生效。
57
+
58
+ **如何让完成证据成为强制要求?**
59
+ worker 可以给 `tower_complete` 传 `checks`;设置 `gates.requireChecks:true` 后,没有 checks 的
60
+ completed mission 会被阻止合并(`no-check-evidence`)。
61
+
62
+ **Tower 把状态存在哪里?**
63
+ 目标仓库的 `<git-common-dir>/dsh-tower/state.json`、`activity.jsonl`,以及可供人阅读的
64
+ `MISSIONS.md`;模式状态在 `$DSH_HOME/tower-mode/modes.json`。worktree 位于检出旁边的
65
+ `.dsh-tower-worktrees/`。
66
+
67
+ **如何修改审查轮数上限或 preset?**
68
+ 使用 `review.maxRoundsPerReviewer` 与 `spawn.workerPreset`/`spawn.reviewerPreset`;见
69
+ [配置](reference/config.md)。修改 profile 配置后重启 DSH。
70
+
71
+ **Tower 会把数据发到别处吗?**
72
+ 不会。它没有遥测,也没有网络服务;所有状态都在本地。
@@ -0,0 +1,178 @@
1
+ # 热重载(开发)
2
+
3
+ [English](../../guide/hot-reload.md) | **简体中文**
4
+
5
+ Tower 是 Host 插件:它的工具与提示段落在 DSH 启动时加载。DSH 已内置 `@deepseek-ai/dsh-hmr` 服务,
6
+ 它**在原位对账 profile**——重新读取 profile 的 patch 层,替换文件已变更的模块(包括 `dsh-tower`
7
+ 检出这样的 linked 包),重启受影响的 Loader 条目,并报告哪些情况需要完整重启。Tower 在开发中使用
8
+ 该服务,因此 Host 侧改动无需重启 DSH 即可生效。
9
+
10
+ 本页讲的是开发循环,使用 Tower 本身并不需要它。
11
+
12
+ ## HMR 做什么、不做什么
13
+
14
+ `dsh --profile <name> --dump-config` 会打印组合后的 hmr 行:
15
+
16
+ ```yaml
17
+ - id: hmr
18
+ name: '@deepseek-ai/dsh-hmr'
19
+ disabled: !!js '!ctx.get(''profileContext'')'
20
+ config:
21
+ root: []
22
+ ```
23
+
24
+ - `root: []` 是随包默认值:**不监视任何源码文件**。HMR 已挂载,但文件监视处于空闲状态,因此编辑
25
+ 检出后运行中的 DSH 不会自动重载。
26
+ - 仍然可以显式请求对账(`/tower reload`、`tower_reload` 或 `plugin_manager`);即使 `root: []`,
27
+ 它也会比较已加载模块与磁盘,并替换发生变化的模块。监视只是让保存后自动触发这次对账。
28
+ - HMR 只为 profile 启动挂载(没有 `profileContext` 时 `disabled`)。没有 profile 启动的进程会报告
29
+ reload 不可用(`dev.hotReload` 开启时 `tower_reload` 仍注册,因此返回 `reload-unavailable`,
30
+ 而不是消失)。
31
+ - profile patch 层在 DSH 启动时加载。编辑它——实际就是 hmr 的 `root` 列表——需要**重启一次 DSH**
32
+ 才生效。运行中的进程不会重新读取 HMR 行自身的配置。
33
+
34
+ ## 为检出开启监视
35
+
36
+ 在本仓库的检出目录中执行:
37
+
38
+ ```powershell
39
+ npm run dev:hmr
40
+ # equivalent to:
41
+ node scripts/enable-dev-hmr.mjs
42
+ ```
43
+
44
+ 脚本会向 profile 的用户 patch 层
45
+ `<DSH_HOME>/profiles/<profile>/cordis.patch.yml` 追加一个带标记的块(`DSH_HOME` 默认 `~/.dsh`;
46
+ Windows 上通常是 `C:\Users\<you>\.dsh`):
47
+
48
+ ```yaml
49
+ # dsh-tower:dev-hmr
50
+ - id: hmr
51
+ config:
52
+ root:
53
+ - 'D:/work/dsh-tower'
54
+ ```
55
+
56
+ 选项:
57
+
58
+ | 参数 | 含义 |
59
+ |---|---|
60
+ | `--profile <name>` | 要编辑的 profile。默认 `desktop`(桌面应用启动的 profile)。 |
61
+ | `--root <dir>` | 要监视的仓库目录。默认:当前工作目录的规范路径。 |
62
+ | `--dry-run` | 打印 patch 文件的 diff 后退出,不写入。 |
63
+ | `--remove` | 再次删除该标记块。 |
64
+
65
+ 说明:
66
+
67
+ - 脚本是幂等的:重复运行会替换标记块,且不触碰 patch 文件的其余行。请在仓库根目录运行,或传入
68
+ `--root D:\work\dsh-tower`。
69
+ - 写入的值是 root 的 `realpath`,使用正斜杠。**HMR 的 `root` 接受绝对路径**;相对路径会相对
70
+ profile 目录解析,很容易出错,因此脚本总是写入绝对路径。
71
+ - profile 必须已存在。脚本不会创建 profile,也不会在 `<DSH_HOME>/profiles/<profile>/` 之外创建目录。
72
+ - 选择桌面应用实际启动的 profile(默认 `desktop`)。desktop profile 由 Electron 应用管理,请退出并
73
+ 重新启动 DSH 来完成重启。
74
+
75
+ 写入该块后**重启一次 DSH**。重启之后:
76
+
77
+ - HMR 会监视检出(`node_modules`、点目录、`cache` 与 `data` 被随包 `ignored` 列表排除),并在 DSH
78
+ 保持打开时于编辑后对账 profile。
79
+ - `/tower reload`(以及注册后的 `tower_reload` 工具)可以按需对账,即使文件不在监视范围内。
80
+
81
+ 停止监视:运行 `npm run dev:hmr -- --remove`,再重启一次 DSH。
82
+
83
+ ## 编辑循环
84
+
85
+ 1. 编辑 `lib/*.js`、`index.js`、`worker.js`、`reviewer.js` 或 `skills.js`。
86
+ 2. 保存文件。监视器会去抖(DSH 默认 100 ms)并对账 profile;变更的模块被替换,受影响的 Loader
87
+ 条目重启。
88
+ 3. 观察 DSH 日志中的 `reload plugin at <path>` / `restart required: …` 消息。
89
+ 4. 可选:调用 `/tower reload` 或 `tower_reload` 立即对账,而不必等监视器。
90
+
91
+ 编辑仓库的 bundle patch(`cordis.patch.yml`)会改变插件声明本身;此类改动后请重启 DSH,不要假设
92
+ 热替换会生效。
93
+
94
+ 工具背后的桥接遵循 `dev` 配置组:
95
+
96
+ | 配置键 | 含义 |
97
+ |---|---|
98
+ | `dev.hotReload` | 注册 `tower_reload`;关闭时 `/tower reload` 也被禁用(默认 `true`)。无 HMR 服务时工具仍注册,以便返回 `reload-unavailable`。 |
99
+ | `dev.reloadDelayMs` | 计划中的对账运行前的延迟,单位毫秒(默认 `50`),让工具/命令结果先送达模型,再替换插件。 |
100
+ | `dev.reloadRequiredIds` | profile 必须保持活跃的 Loader 条目 id(默认 `tower`、`tower-skills`、`preset-tower-worker`、`preset-tower-reviewer`)。即使某个 required 条目早已处于非活跃状态,也会被报告为重载失败。 |
101
+
102
+ 重载是**调度而非等待**:`/tower reload` 与 `tower_reload` 立即返回 `scheduled`、`delayMs`、
103
+ `requiredIds`(以及存在待处理错误时的 `lastError`),随后 HMR 在延迟后对账 profile。这是有意为之——
104
+ 工具结果必须先送达,Tower 插件才能替换自身。两个后果:
105
+
106
+ - 延迟窗口内的一批重载请求会合并为一次对账;
107
+ - 对账失败会记录为 `lastError`,而不会从工具调用返回,因此重载后请检查 `tower_mode.hotReload`
108
+ 与 DSH 日志。
109
+
110
+ ### 阅读重载结果
111
+
112
+ 计划中的对账结果会出现在:
113
+
114
+ - **DSH 日志** —— 被替换模块的 `reload plugin at <path>`,以及 HMR 自身的告警与
115
+ `restart required: <reason>` 消息。
116
+ - **`tower_mode.hotReload`** —— `{available, requiredIds, delayMs, scheduled, lastRunAt, lastError}`;
117
+ `lastError` 保留最近一次失败,并在下次成功重载后清除。
118
+
119
+ 对账本身还会返回 `warnings`、`pending`(仍运行旧代码的模块)以及可选的 `restartReason`。由 Tower
120
+ 触发重载时,这些不会返回给模型,因此把“新代码没有出现”视为需要重启 DSH 的信号,而不是反复重试。
121
+
122
+ 没有 HMR 时,`/tower reload` 会回答不可用消息,且 `tower_reload` 根本不会注册(两者都在会话启动时
123
+ 由 HMR 服务决定是否启用)。
124
+
125
+ ## plugin_manager 与 linked 包
126
+
127
+ `plugin_manager` 操作(`install_bundle`、`set_plugin`、升级)会立即作用于运行中的 profile——它们
128
+ 自己会对账 profile,因此安装或更新 bundle 不需要重启。开发时安装 `dsh-tower` 的推荐方式就是如此:
129
+ 把检出作为 bundle 添加(`dsh plugin --profile <profile> add <repo-root>` 或 `install_bundle`),
130
+ 然后让 HMR 与重载工具接住代码编辑。
131
+
132
+ 检出作为 linked 包目标时,检出内的文件变更会在下一次 `reconcileProfile` 或 `plugin_manager` 操作时
133
+ 被替换,**无需重启**——即使 `root: []` 意味着该变更没有被监视。真正需要重启的是:
134
+
135
+ - hmr 行自身的配置(`root`),因为该列表在服务激活时读取;
136
+ - 报告在 `pending` 下或带 `restartReason` 的模块。
137
+
138
+ ## 故障恢复
139
+
140
+ | 症状 | 处理 |
141
+ |---|---|
142
+ | `[tower:reload-unavailable]` / “Tower reload is unavailable” | 该进程没有 HMR 服务(未带 profile 启动、profile 中 HMR 被禁用,或服务在关闭过程中已 dispose)。重启 DSH。要检查 hmr 行,在非 desktop profile 上运行 `dsh --profile <name> --dump-config`(desktop profile 由 Electron 应用管理,拒绝 dump)。 |
143
+ | `hotReload.lastError` 中出现 `[tower:reload-failed]` 且伴随语法/导入错误 | 修复文件后重启 DSH。替换失败会停掉旧插件实例,旧代码不再运行;重载本身不会回滚。 |
144
+ | `HMR is disposed` | 服务随进程关闭。重启 DSH。 |
145
+ | `HMR transactions cannot be nested` | 在另一个 HMR 事务内部请求了显式重载;请从普通工具回合调用。 |
146
+ | `pending` 非空或设置了 `restartReason` | 先重启 DSH,再相信重载结果。 |
147
+ | 已保存但没有任何重载 | 确认文件位于配置的 `root`(绝对路径)下,未被 `ignored`(`node_modules`、点目录、`cache`、`data`)排除,且 profile patch 包含标记块。运行 `/tower reload` 手动对账。 |
148
+ | 重载持续报告某个 required 条目非活跃 | 该 Loader 条目被禁用或启动失败:检查 profile 的插件/bundle(移除 Tower preset bundle 后 `preset-tower-*` 条目会缺失),修复后重启。 |
149
+
150
+ 离线桥接测试属于常规测试套件:
151
+
152
+ ```powershell
153
+ node --test tests/hmr.test.mjs
154
+ ```
155
+
156
+ ## Client bundle(`ui-bundle`)不同
157
+
158
+ HMR 替换的是 **Host** 插件与提示/工具代码。可选的 `ui-bundle` 是 Client 小部件:它的 JavaScript 由
159
+ Web 页面下载并执行,而不是 Host 进程。Host 重载或 Client bundle 安装/更新后,已打开的页面可能仍保留
160
+ 旧小部件——在认为改动失败之前,先刷新 Web 页面(如果工具 schema 有变化,再打开新会话)。Tower UI
161
+ 小部件只是一个提示,与热重载没有依赖关系。
162
+
163
+ 要在隔离 profile 上对重载路径做实机检查,使用
164
+ [`tests/live/hmr.cjs`](../../../tests/live/hmr.cjs),并把 `DSH_TEST_URL` 指向一次性服务器 URL
165
+ (见 [tests/README.md](../../../tests/README.md));它不会触碰 desktop profile 或在线会话。
166
+
167
+ ## 安全
168
+
169
+ HMR 会在 **DSH Host 进程内**执行它在被监视 root 中找到的代码,使用你的操作系统用户权限,并在保存时
170
+ 重载。请像对待 shell 启动文件一样对待 profile patch 层:
171
+
172
+ - 只把 `root` 指向你信任的仓库(本检出),并保持列表小而绝对;
173
+ - 不要加入主目录、下载目录或共享/网络路径;
174
+ - 被 patch 的 profile 属于同用户可信配置;能写它的人就能在你的 DSH 进程中执行代码;
175
+ - 不再需要监视时,移除该块(`--remove`)并重启一次。
176
+
177
+ 另见:[安装、升级与迁移](install-upgrade-migration.md)、
178
+ [配置参考](../reference/config.md)、[错误码](../reference/errors.md)。
@@ -0,0 +1,57 @@
1
+ # 安装、升级与迁移
2
+
3
+ [English](../../guide/install-upgrade-migration.md) | **简体中文**
4
+
5
+ ## 环境要求
6
+
7
+ - DSH `0.2.1-rc.2` 或更新版本
8
+ - Node.js 22+
9
+ - Git(本地 worktree;不需要网络访问)
10
+
11
+ ## 安装
12
+
13
+ ```bash
14
+ dsh plugin --profile web add dsh-tower
15
+ ```
16
+
17
+ 或在 DSH 内执行:`plugin_manager({ action: "install_bundle", target: "dsh-tower" })`。从本地 checkout 安装:
18
+
19
+ ```bash
20
+ dsh plugin --profile web add github:louifinn/dsh-tower
21
+ ```
22
+
23
+ Host 插件在 DSH 启动时加载:**安装或更新后需要重启 DSH**。
24
+
25
+ ## 升级
26
+
27
+ 1. 阅读 [CHANGELOG.md](../../../CHANGELOG.md) 了解行为变化。
28
+ 2. 更新依赖(`dsh plugin --profile web add dsh-tower@latest` 或你固定的版本)。
29
+ 3. 重启 DSH;用 `tower_mode` 检查 `allowAgentControl` 是否符合预期。
30
+ 4. 既有 mission state 保持兼容:1.2/1.3 的状态 schema 未变。
31
+
32
+ ## 迁移说明
33
+
34
+ - **1.x → 2.0(未发布)**:不需要迁移状态(`schemaVersion` 保持 1;新字段都是可选的)。需要留意的
35
+ 行为变化:Tower 模式激活期间,`mode.denyTools` 现在还会拒绝 `workflow`、`ralph`、`subagent`、
36
+ `subagent_fork`、`wait_agent` 与 `team_task_list`(可用显式列表覆盖);`spawn.maxLiveWorkers`
37
+ 默认为 4;`mode.workspaceLock` 默认关闭。worker 不能再直接完成/放弃/扩大 mission 的 scope——
38
+ 请使用 `tower_complete`。新工具:`tower_interrupt`,以及 HMR 可用时的 `tower_reload`。升级后重启
39
+ 一次 DSH;见[热重载](hot-reload.md)。
40
+ - **1.3 → 1.4**:不需要迁移状态。新增:仓库级配置(`<git-common-dir>/dsh-tower/config.json`)、
41
+ 稳定的 `[tower:<code>]` 工具错误、`tower_findings`,以及 `id`/`missionId` 与 snake/camel 输入
42
+ 别名。既有调用保持可用;重启 DSH 以加载新工具面(`tower_findings` 与更新的 output schema)。
43
+ - **1.2 → 1.3**:reviewer 从共享的 `tower-worker` preset 迁移到新的只读 `tower-reviewer` preset。
44
+ 重新安装/重启,确保两个 preset 行都存在。`tower_spawn role=reviewer` 现在要求 mission 为
45
+ `completed`。
46
+ - **1.1 → 1.2**:合并失败新增 `merge-hook-failed`、`cleanup` 与对账行为;不需要迁移状态。
47
+ - **1.0 → 1.1**:包名从 `@local/dsh-tower` 改为 `dsh-tower`。先移除旧 bundle
48
+ (`remove_bundle` 目标 `@local/dsh-tower`),再安装 `dsh-tower`。
49
+
50
+ ## 卸载
51
+
52
+ ```text
53
+ plugin_manager({ action: "remove_bundle", target: "dsh-tower" })
54
+ ```
55
+
56
+ 重启 DSH。`<git-common-dir>/dsh-tower/` 下的 mission state 与
57
+ `<主检出父目录>/.dsh-tower-worktrees/` 下的 worktree 会保留;确认没有活跃 mission 后再手工清理。
@@ -0,0 +1,96 @@
1
+ # 快速开始
2
+
3
+ [English](../../guide/quickstart.md) | **简体中文**
4
+
5
+ 前置要求:DSH `0.2.1-rc.2`+、Node.js 22+、Git。安装 `dsh-tower` 并重启 DSH(见
6
+ [安装](install-upgrade-migration.md))。
7
+
8
+ ## 1. 进入 Tower 模式
9
+
10
+ ```
11
+ /tower
12
+ ```
13
+
14
+ 会话会报告 `Tower mode ON`。此时你是控制塔;goal/todo/team 委派工具保留给普通模式。
15
+
16
+ ## 2. 初始化与计划
17
+
18
+ ```
19
+ tower_init repoRoot=D:\work\repo
20
+ tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement the feature","commit the change"],"kind":"build"}]
21
+ ```
22
+
23
+ `tower_plan` 返回 mission id(`M1`)。每个 mission 在 spawn 时获得独立分支和 worktree。
24
+ Tower 工具同时接受 `missionId` 与 `id` 两种写法(见[输入别名](../reference/tools.md#输入别名))。
25
+
26
+ ## 3. 运行 worker
27
+
28
+ ```
29
+ tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
30
+ ```
31
+
32
+ 调用立即返回子代理 id。worker 在 mission worktree 内工作、提交、把任务标记完成,并调用
33
+ `tower_complete`;它的最终报告会送回本会话。用 `tower_roster` 观察活动,不要循环轮询。
34
+
35
+ ## 4. 审查
36
+
37
+ ```
38
+ tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
39
+ ```
40
+
41
+ reviewer 使用只读的 `tower-reviewer` preset。它查看 `tower_diff` 并提交 `tower_review`。只有
42
+ `clean` + `merge` 的结论才允许合并。
43
+
44
+ ## 5. 门禁、合并与清理
45
+
46
+ ```
47
+ tower_preflight repoRoot=D:\work\repo missionId=M1
48
+ tower_merge repoRoot=D:\work\repo missionId=M1
49
+ tower_teardown repoRoot=D:\work\repo
50
+ ```
51
+
52
+ 审查前运行 `tower_preflight` 预期报告 `no-review`;干净审查之后应为 `ok:true`。存在 blocker 时
53
+ `tower_merge` 拒绝合并。`tower_teardown` 移除已 merged/abandoned 的 worktree 与分支。
54
+
55
+ ```
56
+ /tower off
57
+ ```
58
+
59
+ ## 如果失败
60
+
61
+ 阅读 blocker code 并查阅 [blocker 参考](../reference/blockers.md)。最常见的情况:`stale-base`
62
+ (执行 `tower_rebase`)、`no-review`(spawn reviewer)、`merge-hook-failed`(重试,或调大
63
+ `timeoutMs`)、`tip-moved`(审查后分支已变化,需要重新审查)。
64
+
65
+ 工具抛出的错误带 `[tower:<code>]` 前缀;见[错误码](../reference/errors.md)(例如
66
+ `preset-missing` 表示安装 bundle 后需要重启 DSH,`not-initialized` 表示先运行 `tower_init`)。
67
+
68
+ ## 可选:仓库级配置
69
+
70
+ 不修改 DSH profile,仓库也能调整 review、merge、worktree 与 spawn 策略。把 JSON 文件放到
71
+ `<git-common-dir>/dsh-tower/config.json`(普通检出通常是 `.git/dsh-tower/config.json`):
72
+
73
+ ```json
74
+ {
75
+ "review": { "maxRoundsPerReviewer": 3 },
76
+ "merge": { "timeoutMs": 1800000 },
77
+ "worktrees": { "root": "D:/tower-worktrees" }
78
+ }
79
+ ```
80
+
81
+ 仓库配置层覆盖 profile 配置;单次工具参数又覆盖两者。完整键表与优先级见
82
+ [配置参考](../reference/config.md)。
83
+
84
+ ## 可选:finding
85
+
86
+ scope 之外的观察用 `tower_finding` 记录。之后可以列出或关闭它们,而不必改代码:
87
+
88
+ ```
89
+ tower_findings repoRoot=D:\work\repo
90
+ tower_findings repoRoot=D:\work\repo id=F1 status=fixed note="fixed in M2"
91
+ ```
92
+
93
+ 状态为 `open → triaged → fixed` / `wontfix`,且 `fixed`/`wontfix` 可重新打开为 `open`。只有控制塔
94
+ 或 worker 可以修改 finding;reviewer 只能列出和读取。
95
+
96
+ 完整可复制示例:[examples/first-mission.md](../../examples/first-mission.md)(英文)。
@@ -0,0 +1,42 @@
1
+ # 安全模型
2
+
3
+ [English](../../operations/security.md) | **简体中文**
4
+
5
+ Tower 提供的是**协作层面的保证**,不是沙箱。需要理解哪些约束由插件强制执行、哪些依赖部署环境。
6
+
7
+ ## 由插件强制执行的约束
8
+
9
+ - **审查绑定。** `tower_review` 会记录它检查的分支 tip;tip 变化后门禁会拒绝(`tip-moved`),除非
10
+ 一次干净且无冲突的 rebase 给予了豁免。reviewer 在 spawn 之后修改分支会被拒绝
11
+ (`reviewer must not modify the mission branch`)。
12
+ - **角色强制。** 调用方会根据 roster 解析。worker 的 mission 更新仅限于 task/blocker/note;
13
+ `completed`、`abandoned`、扩大 scope 与清除 blocker 仅限控制塔,完成(连同未读消息/提交门禁)必须
14
+ 走 `tower_complete`。仅限控制塔的工具会以 `[tower:not-authorized]` 拒绝 roster 中的 worker。
15
+ 每个 mission 只能有一个活跃 worker;接管已停止的 worker 必须显式声明。
16
+ - **只读 reviewer preset。** `tower-reviewer` preset 从工具面移除 `write`、`edit`、`pwsh`、
17
+ `tower_mission` 与 `tower_complete`;reviewer 只能读、看 diff、发消息、记录 finding 和提交审查。
18
+ - **Scope 门禁。** mission 只能修改其声明 scope glob 匹配的文件;越界改动会阻止合并。
19
+ - **Worktree 隔离。** worker 在独立 worktree 与分支上工作;主检出只会被 merge/teardown 触及。
20
+ - **人类掌握模式。** `/tower` 归用户所有。开启 `mode.allowAgentControl` 后模型可以切换,但必须
21
+ 给出理由;随包 patch 默认开启——设为 `false` 可让模式只由人类控制。
22
+ - **不自动恢复 goal。** 进入模式会暂停活跃 goal;退出时不会恢复它。
23
+ - **建议性 workspace 锁。** `mode.workspaceLock` 防止两个 Tower 会话意外驱动同一个仓库;它是同用户
24
+ 之间的协调锁,不是用户间的认证机制。
25
+
26
+ ## 不由插件强制执行的约束(部署关注点)
27
+
28
+ - **沙箱。** Host 插件运行在文件系统沙箱之外;reviewer 的 shell 能力是在工具面层面移除的,但底层
29
+ 进程仍拥有 DSH 的 OS 权限。需要硬隔离的部署必须自行限制进程/用户。
30
+ - **reviewer 信任。** 审查是 reviewer agent/模型给出的背书;插件能把它绑定到提交,但无法证明
31
+ reviewer 真的推理正确。高价值分支建议使用独立模型/角色。
32
+ - **仓库钩子。** `git merge` 会运行目标仓库的钩子(`pre-merge-commit` 等)。Tower 无法让这些钩子
33
+ 变安全;在不受信任的仓库上运行 Tower 前请先审查钩子。
34
+ - **HMR 重载。** 开启 `dev.hotReload` 时,`/tower reload`/`tower_reload` 会让 DSH 从磁盘重新导入
35
+ 插件。只为你信任的检出配置 HMR 监视 root;重载会执行这些文件当前的代码。
36
+ - **提示注入。** reviewer 会读取不受信任的分支内容(diff/评论)。只读 preset 限制了影响面,但模型侧
37
+ 没有绝对防护。
38
+ - **基线分支保护。** Tower 会写入本地基线分支;服务端的 branch protection/push 规则不属于本插件。
39
+
40
+ ## 报告问题
41
+
42
+ 不要在公开 issue 中粘贴密钥、token 或会话日志。见 [SECURITY.md](../../../SECURITY.md)。
@@ -0,0 +1,62 @@
1
+ # blocker 与失败码
2
+
3
+ [English](../../reference/blockers.md) | **简体中文**
4
+
5
+ `tower_preflight` 返回 `{ok:false, blockers:[{code, detail}]}`。`tower_merge` 与 `tower_rebase`
6
+ 在操作失败时返回相同结构。code 稳定;detail 面向人。
7
+
8
+ ## 门禁 blocker(26 个)
9
+
10
+ | Code | 含义 / 下一步 |
11
+ |---|---|
12
+ | `mission-missing` | 未知 mission id。用 `tower_status` 查看。 |
13
+ | `mission-not-completed` | mission 不是 `completed`;只有 `completed`(等待审查)的 mission 才能合并。 |
14
+ | `tasks-open` | 仍有未完成任务;worker 必须先完成,或带理由 `task_drop`。 |
15
+ | `blockers-present` | mission 仍有 blocker;解决后清除。 |
16
+ | `no-check-evidence` | 启用了 `gates.requireChecks` 且完成报告没有 `checks`。把 completed mission 退回 `active`,带 checks 重新调用 `tower_complete`,然后重新审查。 |
17
+ | `branch-missing` | 还没有 worktree/分支:先 spawn worker。 |
18
+ | `stale-base` | 分支不包含基线 tip:执行 `tower_rebase`。 |
19
+ | `read-only-survey` | survey mission 修改了文件;survey mission 必须只读。 |
20
+ | `out-of-scope` | 改动文件超出 mission scope;有意为之就扩大 scope,否则回退。 |
21
+ | `base-dirty` | 主检出存在未提交改动,合并会覆盖它们。 |
22
+ | `deps-unmerged` | 依赖 mission 尚未 merged/abandoned。 |
23
+ | `no-review` | 分支上没有审查:spawn reviewer。 |
24
+ | `review-mission-mismatch` | 最新审查属于另一个 mission。 |
25
+ | `not-clean` | 最新审查是 `p1-Nitems`/`p2-Nitems`;需要再来一轮。 |
26
+ | `review-hold` | 最新审查建议 hold;必须由后续的干净审查取代。 |
27
+ | `review-not-approved` | 最新干净审查给出的是 `fix-then-merge` 或 `hold`;只有 `merge` 才算批准。 |
28
+ | `tip-moved` | 审查之后分支发生变化;重新审查(或使用干净 rebase 的免审豁免)。 |
29
+ | `worker-busy` | 该 mission 的 roster agent 正在运行;等待或让其退役。 |
30
+ | `worker-status-unknown` | 父目录不可用,无法证明 live roster agent 已停止;等目录恢复后重试,或先 adopt。 |
31
+ | `base-wrong-branch` | 主检出不在记录的基线分支上。 |
32
+ | `branch-owned-by-closed-missions` | mission 已 merged/abandoned;用新标题重新 plan。 |
33
+ | `worker-stale` | 该 mission 的 roster worker 超过 `spawn.staleMs` 没有目录活动;合并前 adopt(只有确实还在运行时才 interrupt)。 |
34
+ | `tests-missing` | 启用了 `gates.requireTests` 且没有针对被审查 tip 的通过测试回执;运行 `tower_test` 或 `tower_complete`。 |
35
+ | `tests-failed` | 最新测试发现回执失败,或不满足 minCount/required 检查。修复测试并重跑。 |
36
+ | `review-exemption-invalid` | mission 请求 `reviewPolicy:"none"`,但 `gates.allowReviewExemption` 关闭(或缺少理由)。 |
37
+ | `merge-intent-pending` | 崩溃/失败前记录了合并意图;先运行 `tower_merge` 对账,再进行其他门禁。 |
38
+
39
+ ## 合并失败(4 个)
40
+
41
+ | Code | 含义 / 下一步 |
42
+ |---|---|
43
+ | `merge-conflict` | 真实的 index 冲突;mission 变为 `blocked`。解决后把 mission 恢复为 `active`/`completed`,再重试。 |
44
+ | `merge-hook-failed` | 在产生提交之前钩子/进程失败;仓库已恢复,mission 保持 `completed`。重试;`timeout: true` 表示 git 进程超时,慢钩子请调大 `timeoutMs`。 |
45
+ | `merge-shape` | Git 产生了意外的合并父提交;检查仓库,不要强推。 |
46
+ | `batch-invalid` | batch id/成员/回执/检查引用无效,或 close 证据不完整。 |
47
+
48
+ 已经落地的合并不是 blocker:`tower_merge` 会记录它(`reconciled`、`alreadyLanded`),或从失败调用中恢复
49
+ (`recoveredFromFailure`、`warnings`)。
50
+
51
+ ## rebase 失败(3 个;`worker-busy` 与门禁共用)
52
+
53
+ | Code | 含义 / 下一步 |
54
+ |---|---|
55
+ | `worktree-dirty` | 提交或丢弃 worktree 改动后重试。 |
56
+ | `rebase-conflict` | 真实冲突;mission 变为 `blocked`。手工解决后将其恢复为 `active`,再重试。 |
57
+ | `worker-busy` | 该 mission 有 agent 正在运行;先等待或让其退役。 |
58
+
59
+ 分支已包含在基线中时,`tower_rebase` 返回 `alreadyMerged: true`;此时调用 `tower_merge` 记录这次
60
+ 已落地的合并。
61
+
62
+ 稳定的 blocker code 共 32 个(26 个门禁 + 4 个合并失败 + 2 个 rebase 专属;`worker-busy` 与门禁共用)。