@twinsearth/dsh-master 3.3.3 → 3.3.4

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.
@@ -0,0 +1,153 @@
1
+ # 房主 PM/PJM 智能体:产品盘点与项目计划(3.3.4)
2
+
3
+ > 产出时间:2026-11-14 · 范围:`E:\GPT\dsh-master`(房主)· 版本:已发布 3.3.3 / 在研 3.3.4-dev
4
+ > 本文件由「房主智能体」的角色契约产出(PM 六类盘点 + PJM 七类项目管理),**只写有证据的结论**。
5
+
6
+ ---
7
+
8
+ ## 第一部分 · 产品经理(PM)
9
+
10
+ ### WHY · 解决什么问题
11
+
12
+ 用户与开发者的需求是口语的,而代码、测试、发布是严格的。中间缺一层**可核对的翻译**:
13
+
14
+ - 需求含糊时没人先问清楚 → 做完了才发现理解错,返工成本最高;
15
+ - "完成度"靠口头声明 → 设计稿被当成交付,测试没跑也敢说 100%;
16
+ - 冲突与歧义埋在聊天记录里 → 决策反复,前后矛盾没人指出;
17
+ - 项目执行没有 WBS/owner/验收 → 谁做、多久、做成什么样都说不清。
18
+
19
+ ### WHAT · 交付了什么
20
+
21
+ 两个**可被新会话选择的 Agent preset** + 一个共享结构化服务:
22
+
23
+ | 交付物 | 位置 | 证据 |
24
+ |---|---|---|
25
+ | PM preset `master-pm` + 4 个工具 | `cordis.patch.yml`、`src/agents/pm-agent.ts` | `test/dual-agents.test.mjs`、`test/package-contract.test.mjs` |
26
+ | PJM preset `master-pjm` + 6 个工具 | 同上、`src/agents/pjm-agent.ts` | 同上 |
27
+ | 角色 Prompt(WHY/WHAT/六类;HOW/WHO/WHERE/WHEN/七类) | `src/agents/prompts.ts` | preset 注册断言、Prompt 文本断言 |
28
+ | 结构化契约与门禁 | `src/dual/schema.ts` | 10 条测试 |
29
+ | 文档渲染(PRD/盘点/项目/结案) | `src/dual/documents.ts` | 文档写入测试 |
30
+ | 共享服务(PRD/盘点/计划/进度/结案) | `src/plugins/dual-manager.ts` | `test/dual.test.mjs` + `test/dual-agents.test.mjs` |
31
+ | 智囊团只读面板 | `client.js`、`status().dual` | 面板与状态测试 |
32
+
33
+ **关键设计**:preset 决定"这个会话是谁",`ROLE_MATRIX` 决定"子插件在哪个角色装配",两者都不授予额外权限;
34
+ 智能体工具边界写在 preset 里(只读 fs + 提问/待办/交付,**无 shell、无写文件**)。
35
+
36
+ ### 六类盘点(房主范围,逐条带证据)
37
+
38
+ #### 1. 已完成(进度与证据)
39
+
40
+ | 项 | 进度 | 证据 |
41
+ |---|---|---|
42
+ | 成本优化系统层 + A/B 模块 + 报表/门禁/里程碑(3.3.0) | 100% | 449→462 测试;§10.2 门禁 G1–G7 全绿;真实 A/B −37.6% |
43
+ | 瓮城回流 contract 1.1.1 + 房主 relay 适配(3.3.1/3.3.2) | 100% | npm `1.1.1`;dsh-Fang 3.1.35 双进程实测 6 条回执、幂等重投通过 |
44
+ | 角色矩阵放开:母体全开 + 分身挂双主管(3.3.3) | 100% | `7e2c132`;7 条测试断言;`test:clean` 449/449 |
45
+ | PM/PJM 双智能体 + 共享服务 + 面板(3.3.4-dev) | 100%(代码) | `666f9bc`、`d1cf443`;`test:clean` 462/462 |
46
+ | 三方发布流水线(tag + Release + npm 各四次) | 100% | `v3.3.0`–`v3.3.3`,资产 sha256 与本地一致 |
47
+
48
+ #### 2. 未完成(原因与后续)
49
+
50
+ | 项 | 原因 | 后续 |
51
+ |---|---|---|
52
+ | 3.3.4 发布 | 未获发布授权 | 拿到授权后走同一套门禁 + 三方发布(估 30–45 分钟) |
53
+ | preset 在真实 Host 可选验证 | 守则 §二 禁止向任何 profile 安装 | 需一次**受限安装授权**,装到指定 profile 点选验证 |
54
+ | 面板卡片渲染验证 | 同上,且当前会话面板读的是已安装版本 | 与上一条合并做 |
55
+ | 智能体工具真实会话跑通 | 只有 fake ctx 级测试 | 同上 |
56
+
57
+ #### 3. 无法实现 / 受阻(卡点)
58
+
59
+ | 项 | 卡点 | 性质 |
60
+ |---|---|---|
61
+ | 宿主级 co-load / preset 点选 | 守则 §二:禁止把插件装进任何 profile | **策略限制**,非技术问题 |
62
+ | `vm_create/vm_exec` 那 27 个工具 | 房主仓库与本 Host 都不存在该工具面 | 依赖不存在,不是没写 |
63
+ | 房主的三层磁盘架构 / 蓝绿 / 快照回滚 | **那是瓮城 dsh-fang 的实现**,房主没有也不该有 | 跨仓库、跨项目 |
64
+
65
+ #### 4. 已计划(后续开发计划)
66
+
67
+ | 项 | 优先级 | 估时 |
68
+ |---|---|---|
69
+ | 3.3.4 三方发布 | P1 | 30–45 分钟 |
70
+ | 受限安装 + preset/面板验证 | P1(依赖授权) | 30 分钟 |
71
+ | PM → PJM 一键交接(PM 结案后生成 PRD 摘要供 PJM 建计划) | P2 | 2–3 小时 |
72
+ | 若确需真 VM:新增 vm-sandbox 适配层 | P3(依赖授权) | 3–5 天 |
73
+
74
+ #### 5. 相互矛盾 / 冲突(需你重新判断)
75
+
76
+ | # | 冲突 | 影响 | 我的处理 |
77
+ |---|---|---|---|
78
+ | A | 规格里"已完成:架构设计 100%、权限矩阵、UI 左厂右车、VM 27 工具映射、v3.0.8–v3.0.12" —— 这些是**瓮城**的成果,不是房主的 | 若照抄成房主完成度,就是伪造证据(PM 门禁本身禁止) | 已按房主真实仓库盘点;瓮城成果不计入房主 |
79
+ | B | "Agent = 后台调度官自动接管项目" vs "用户面板呼叫" | 自动接管会绕过用户决策 | 实现为 **preset 由用户选择**,不自动接管 |
80
+ | C | 旧文档"母体不接纳执行体" vs 3.3.3 已放开 | 文档与实现不一致 | 3.3.3 已改文档与测试,按你的选择执行 |
81
+ | D | 交付物要求"27 工具的 VM 沙箱插件" vs 房主只有 `fangForker` | 交付物清单无法满足 | PJM 派发走 `fangForker.spawn`,无 forker 即 fail-closed |
82
+
83
+ #### 6. 理解歧义(请确认是否理解错)
84
+
85
+ | # | 歧义 | 当前理解 |
86
+ |---|---|---|
87
+ | A | PM 是否应能改代码/跑测试? | **不能**:只读 + 文档 + 提问 |
88
+ | B | PJM 要真 VM 隔离,还是 `fangForker` 分身就够? | 当前按 `fangForker` 实现 |
89
+ | C | PM 结案后是否自动交给 PJM? | 不自动,人工选择(避免越权) |
90
+ | D | 这次是"发 3.3.4"还是"继续加功能"? | 代码已就绪,等你决定发布/验证/扩范围 |
91
+
92
+ ---
93
+
94
+ ## 第二部分 · 项目经理(PJM)
95
+
96
+ ### 一、项目会审
97
+
98
+ - **目标**:3.3.4 可发布、可验证;PM/PJM 在真实 Host 中可选择并可用。
99
+ - **范围**:发布 + 验证(不含 VM/三层架构移植,那属瓮城)。
100
+ - **依赖**:① 发布授权;② 受限安装授权(验证 preset);③ 若要做 VM 适配,需要范围与授权。
101
+ - **风险**:安装验证会改 profile;发布不可回滚;两者都需要你明确点头。
102
+
103
+ ### 二、项目计划(WBS)
104
+
105
+ | # | 步骤 | Owner | 场景 | 估时 | 依赖 | 验收 |
106
+ |---|---|---|---|---|---|---|
107
+ | P1 | 3.3.4 版本对齐 + CHANGELOG 收口 | 房主 Agent | 本仓库 | 20 分钟 | 发布授权 | 版本号/标题一致 |
108
+ | P2 | 门禁:check/build/test:clean + gate + milestones | 房主 Agent | 本仓库 | 15 分钟 | P1 | 462/462、G1–G7 全绿 |
109
+ | P3 | 三方发布:tag + Release + npm + 一致性核验 | 房主 Agent | 本仓库/registry | 30 分钟 | P2 | 资产 sha256 一致、latest 正确 |
110
+ | P4 | 受限安装到指定 profile | 房主 Agent | 指定 profile | 10 分钟 | **安装授权** | 插件行激活 |
111
+ | P5 | 点选 `master-pm`/`master-pjm`,确认工具可见 | 房主 Agent | 该 profile 新会话 | 15 分钟 | P4 | 工具清单与文档一致 |
112
+ | P6 | 面板智囊团卡片渲染验证 | 房主 Agent | 该 profile Web | 10 分钟 | P4 | 卡片与计数可见 |
113
+
114
+ ### 三、项目需求与内容
115
+
116
+ - 发布物:`@twinsearth/dsh-master@3.3.4`,含 `dist-host/agents/*`、`dist-host/dual/*`、`docs/AGENTS-PM-PJM.md`。
117
+ - 验证记录:预设可选择、工具可调用、结案门禁生效、文档写入 workspace。
118
+ - 文档:`docs/AGENTS-PM-PJM.md`(本文件之外的实现说明)。
119
+
120
+ ### 四、项目时间
121
+
122
+ - P1–P3:约 65 分钟(含 npm 传播等待)。
123
+ - P4–P6:约 35 分钟(依赖安装授权)。
124
+ - 合计:**约 1.5–2 小时**,不含 VM 适配(3–5 天,另议)。
125
+
126
+ ### 五、项目人员
127
+
128
+ | 角色 | 人 | 职责 |
129
+ |---|---|---|
130
+ | 实现/测试/发布 | 房主 Agent(本会话) | P1–P6 执行与证据 |
131
+ | 决策/授权 | 你 | 发布授权、安装授权、范围变更 |
132
+ | 需求方 | 你 | 验收标准与优先级 |
133
+
134
+ ### 六、测试、部署与交付验收
135
+
136
+ - **测试**:`npm run check`、`npm run build`、`npm run test:clean`(462/462)、§10.2 门禁、§14 里程碑。
137
+ - **部署**:npm public + GitHub Release;安装需单独授权,且只装指定 profile。
138
+ - **交付**:tarball + `.sha256` + `manifest.json`;`manifest.tagObject` 必须是 annotated tag。
139
+ - **验收**:三方产物 sha256 一致;CI 通过;preset 点选可用(若做 P4–P6)。
140
+
141
+ ### 七、项目结案
142
+
143
+ - **评审报告**:本文件 + `docs/CHANGELOG.md` 3.3.4 段 + `docs/AGENTS-PM-PJM.md`。
144
+ - **资料汇总**:设计文档、Prompt 源码、测试清单、发布 manifest、验证记录。
145
+ - **结案门禁**:P1–P3 全绿即可结案发布;P4–P6 未做则结案报告必须写明"host 级验证未做",不得写成已验证。
146
+
147
+ ---
148
+
149
+ ## 需要你做的决定
150
+
151
+ 1. 是否发布 **3.3.4**?
152
+ 2. 是否授权把 3.3.4 **装到某个 profile** 做 preset 点选验证(会改动该 profile)?
153
+ 3. 规格里的 VM 沙箱 / 三层架构 / 蓝绿升级,是否要**移植进房主**(这属于瓮城能力,需要明确范围)?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/dsh-master",
3
- "version": "3.3.3",
3
+ "version": "3.3.4",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -37,6 +37,8 @@
37
37
  "./contract": "./dist-host/contract/index.js",
38
38
  "./cost-optimizer": "./dist-host/plugins/cost-optimizer.js",
39
39
  "./dual-manager": "./dist-host/plugins/dual-manager.js",
40
+ "./pm-agent": "./dist-host/agents/pm-agent.js",
41
+ "./pjm-agent": "./dist-host/agents/pjm-agent.js",
40
42
  "./client": {
41
43
  "browser": "./client.js",
42
44
  "default": "./client.js"
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+
5
+ import { PJM_SYSTEM_PROMPT } from './prompts.ts'
6
+ import { dualService, dualUnavailable, id, installAgent, jsonTool, objectSchema, objects, string, strings } from './toolkit.ts'
7
+
8
+ export const name = 'dsh-master-pjm-agent'
9
+ export const inject = ['systemPrompt', 'tools']
10
+
11
+ /** PJM preset:项目计划、节点、隔离派发、结案;不替 PM 决定需求。 */
12
+ export function apply(ctx) {
13
+ const dual = () => dualService(ctx)
14
+ const tools = [
15
+ jsonTool('pjm_create_plan', '把已确认 PRD 拆成 WBS 与七类项目计划,返回 plan id;估时必须以分钟给出。', objectSchema({
16
+ prdId: id('PRD id'), title: string('项目计划标题'), how: string('实施路径'), where: string('执行场景'),
17
+ projectReview: strings('项目会审结论'), requirements: strings('项目需求与内容'),
18
+ steps: objects('WBS:id/title/prompt/owner/where/estimateMinutes/dependsOn/acceptance/kind'),
19
+ schedule: { type: 'object', description: 'startAt 与 milestones[](title/owner/estimateMinutes/dependsOn)', additionalProperties: true },
20
+ team: objects('人员:role/owner/responsibility'),
21
+ quality: { type: 'object', description: 'tests[] / deployment[] / acceptance[]', additionalProperties: true },
22
+ }, ['prdId', 'steps']), async (args) => dual() ? dual().pjm.plan(args.prdId, args) : dualUnavailable()),
23
+ jsonTool('pjm_dispatch_step', '通过 fangForker.spawn 在隔离分身中执行一个 WBS 步骤;不可用会 fail-closed,不在宿主本地执行。', objectSchema({
24
+ planId: id('计划 id'), stepId: id('步骤 id'),
25
+ }, ['planId', 'stepId']), async (args) => dual() ? await dual().pjm.dispatch(args.planId, { stepId: args.stepId }) : dualUnavailable()),
26
+ jsonTool('pjm_complete_step', '记录步骤完成/失败、证据与实际用时;不得把失败写成完成。', objectSchema({
27
+ planId: id('计划 id'), stepId: id('步骤 id'),
28
+ status: { type: 'string', enum: ['done', 'failed'] }, summary: string('结果摘要'),
29
+ evidence: strings('测试/文件/报告证据'), actualMinutes: { type: 'number', description: '实际用时(分钟)' },
30
+ }, ['planId', 'stepId', 'status']), async (args) => dual() ? dual().pjm.completeStep(args.planId, args.stepId, args) : dualUnavailable()),
31
+ jsonTool('pjm_project_status', '读取项目看板:完成/进行/待办/失败、估时、实际用时、是否可结案。', objectSchema({ planId: id('计划 id') }, ['planId']), async (args) => dual() ? dual().pjm.progress(args.planId) : dualUnavailable()),
32
+ jsonTool('pjm_write_project', '把项目计划与当前节点写成 workspace/docs/pjm 下的 Markdown。', objectSchema({ planId: id('计划 id') }, ['planId']), async (args, exec) => dual() ? await dual().pjm.writeProject(args.planId, { agent: exec.agent }) : dualUnavailable()),
33
+ jsonTool('pjm_close_project', '全部步骤完成后结案:写入总结、评审报告、验收与资料清单,并可生成结案文档。', objectSchema({
34
+ planId: id('计划 id'), summary: string('结案总结(必填)'), reviewReport: string('评审报告'),
35
+ acceptance: strings('验收记录'), artifacts: strings('项目资料路径'), write: { type: 'boolean', description: '是否同时写结案 Markdown,默认 true' },
36
+ }, ['planId', 'summary']), async (args, exec) => {
37
+ if (!dual()) return dualUnavailable()
38
+ const closed = dual().pjm.close(args.planId, args)
39
+ if (!closed.ok) return closed
40
+ if (args.write === false) return closed
41
+ const document = await dual().pjm.writeClosure(args.planId, { agent: exec.agent })
42
+ return { ...closed, document }
43
+ }),
44
+ ]
45
+ return installAgent(ctx, { prompt: PJM_SYSTEM_PROMPT, tools })
46
+ }
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+
5
+ import { PM_SYSTEM_PROMPT } from './prompts.ts'
6
+ import { dualService, dualUnavailable, id, installAgent, jsonTool, objectSchema, objects, string, strings } from './toolkit.ts'
7
+
8
+ export const name = 'dsh-master-pm-agent'
9
+ export const inject = ['systemPrompt', 'tools']
10
+
11
+ /** PM preset:只提供需求/盘点/文档工具,不提供任何执行或派生能力。 */
12
+ export function apply(ctx) {
13
+ const dual = () => dualService(ctx)
14
+ const tools = [
15
+ jsonTool('pm_create_prd', '创建结构化产品需求文档(WHY/WHAT/模块/约束/验收),返回 PRD id。', objectSchema({
16
+ title: string('需求标题'), goal: string('要解决的问题与目标'),
17
+ why: string('背景与价值'), what: strings('功能范围'), architecture: string('架构说明'),
18
+ outcomes: strings('预期结果'), modules: objects('模块:name/result/architecture/status'),
19
+ priority: { type: 'string', enum: ['low', 'normal', 'high', 'critical'] },
20
+ constraints: strings('约束'), acceptance: strings('验收标准'), nonGoals: strings('非目标'),
21
+ assumptions: strings('假设'), openQuestions: strings('待澄清问题'), requester: string('请求方'),
22
+ }, ['title', 'goal']), async (args) => dual() ? dual().pm.createPrd(args) : dualUnavailable()),
23
+ jsonTool('pm_create_assessment', '保存六类产品现状盘点。completed 每项必须附 evidence;其它类给原因、计划、冲突或歧义。', objectSchema({
24
+ prdId: id('可选:关联的 PRD id'), title: string('盘点标题'), why: string('目标'), what: strings('范围'),
25
+ completed: objects('已完成:title/progress/evidence[](证据必填)'),
26
+ unfinished: objects('未完成:title/reason/nextStep/progress'),
27
+ blocked: objects('受阻:title/reason/blocker/nextStep'),
28
+ planned: objects('已计划:title/plan/estimateMinutes/priority'),
29
+ conflicts: objects('冲突:title/question/options[{value,label,consequence}]'),
30
+ ambiguities: objects('歧义:title/question/currentInterpretation'),
31
+ }, ['title']), async (args) => dual() ? dual().pm.createAssessment(args) : dualUnavailable()),
32
+ jsonTool('pm_write_prd', '把 PRD(可带六类盘点)写成 workspace/docs/pm 下的 Markdown,返回文档路径。', objectSchema({
33
+ prdId: id('PRD id'), assessmentId: id('可选:关联的 Assessment id'),
34
+ }, ['prdId']), async (args, exec) => dual() ? await dual().pm.writePrd(args.prdId, { assessmentId: args.assessmentId, agent: exec.agent }) : dualUnavailable()),
35
+ jsonTool('pm_list_prds', '列出当前 Host 中的 PRD 与六类盘点索引。', objectSchema({}), async () => dual() ? ({ ok: true, prds: dual().pm.listPrds().items, assessments: dual().pm.listAssessments().items }) : dualUnavailable()),
36
+ ]
37
+ return installAgent(ctx, { prompt: PM_SYSTEM_PROMPT, tools })
38
+ }
@@ -0,0 +1,64 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+
4
+ export const PM_AGENT_ID = 'master-pm'
5
+ export const PJM_AGENT_ID = 'master-pjm'
6
+
7
+ export const PM_SYSTEM_PROMPT = String.raw`# Role
8
+ 你是 DSH-Master「房主」的产品经理 Agent(PM,preset: master-pm)。你把用户和开发者的大白话转换成可核对、可验收的产品需求与文档。
9
+
10
+ # Goals — WHY / WHAT
11
+ 1. WHY:说清解决什么问题、为什么值得做、达成什么目标。
12
+ 2. WHAT:说清功能、模块、架构、预期结果、约束、非目标和验收标准。
13
+ 3. 对现状做证据驱动的六类盘点:已完成、未完成、无法实现/受阻、已计划、冲突、歧义。
14
+
15
+ # Workflow
16
+ 1. 先识别目标、范围、约束和未决项;重大歧义或不可逆选择必须用 ask_user_question 提出一个具体决策点,给 2–4 个选项并标推荐项。不要为琐碎实现细节打断用户。
17
+ 2. 盘点前先用 read/grep/glob 读取真实代码、测试和文档。没有证据的“完成”不得写 100%,不得把设计稿当成已交付。
18
+ 3. 用 pm_create_prd 保存结构化 PRD;用 pm_create_assessment 保存六类盘点。每个 completed 项必须附文件、测试或报告证据。
19
+ 4. 用 pm_write_prd 生成 Markdown 文档;需要用户打开时用 present 交付。需要 PJM 执行时,只移交 PRD id、验收标准、未决问题,不代替 PJM 派工。
20
+
21
+ # Rules
22
+ - 不臆测、不伪造证据、不把“可以实现”写成“已经完成”。
23
+ - 冲突必须列出冲突双方、影响、可选方案和需要用户重新判断的问题。
24
+ - 歧义必须写出当前理解与待确认问题;用户未回答前不得把该理解当成事实。
25
+ - PM 不写生产代码、不跑 shell、不发布、不安装、不调度 VM/分身。
26
+ - 只写 PRD/盘点文档;不改需求范围外的文件。
27
+
28
+ # Output Format
29
+ 默认用简洁 Markdown 汇报,必须按任务需要包含:
30
+ - WHY & WHAT
31
+ - 六类现状矩阵:completed / unfinished / blocked / planned / conflicts / ambiguities
32
+ - 冲突与歧义的用户决策点
33
+ - 已保存的 PRD/Assessment id 与文档路径
34
+ 只输出已核实内容;未知、未验证、受限分别标明。`
35
+
36
+ export const PJM_SYSTEM_PROMPT = String.raw`# Role
37
+ 你是 DSH-Master「房主」的项目经理 Agent(PJM,preset: master-pjm)。你把已确认的 PRD 变成可执行、可追踪、可验收的项目计划,并按节点汇报直至结案。
38
+
39
+ # Goals — HOW / WHO / WHERE / WHEN
40
+ 1. HOW:把 PRD 拆成 WBS,每步写清 prompt、依赖、验收、估时。
41
+ 2. WHO:给每一步分配 owner 与责任;未分配必须写 unassigned,不得虚构人员。
42
+ 3. WHERE:明确执行场景(宿主、fork、workspace、DSH Web);脏任务只能在隔离执行体中运行。
43
+ 4. WHEN:给里程碑与估时;估时是计划,不是承诺工期。
44
+
45
+ # Workflow
46
+ 1. 项目会审:核对 PRD、验收标准、约束、未决问题;未决项未解决不得自行扩权。
47
+ 2. 用 pjm_create_plan 建立七类项目内容:项目会审、WBS、需求内容、时间、人员、测试部署验收、结案。
48
+ 3. 执行前申请隔离资源,通过 pjm_dispatch_step 走 fangForker.spawn;不可用就 fail-closed,绝不在母体/原身本地降级执行,不虚构 vm_create/vm_exec。
49
+ 4. 用 pjm_project_status 汇总节点,用 todo_write 跟踪当前工作;完成步骤时记录 summary/evidence/actualMinutes。
50
+ 5. 用 pjm_write_project 保存计划与节点报告。全部步骤完成后才能 pjm_close_project;结案必须有总结、验收记录、评审报告和资料清单,并交付文档。
51
+
52
+ # Rules
53
+ - 按时、按质、按量报告真实状态;失败、阻塞、未测量不得包装成完成。
54
+ - 不发布、不部署到生产、不修改 profile、不触碰凭据,除非用户对该动作另行明确授权。
55
+ - 不代替 PM 决定需求,不替用户回答选择题;阻塞时给出原因、影响、可选方案和所需决策。
56
+ - 不做无限轮询;节点汇报写完成/进行/待办/失败及下一步。
57
+
58
+ # Output Format
59
+ - 项目会审与范围
60
+ - WBS 看板(Todo / In Progress / Done / Failed)
61
+ - Owner、场景、估时、实际用时与依赖
62
+ - 测试/部署/交付/验收证据
63
+ - 结案:评审报告、验收记录、资料汇总
64
+ - 仍需人类决策的事项(如有)`
@@ -0,0 +1,64 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+
5
+ import { SERVICE, masterError, safeGet } from '../kernel.ts'
6
+
7
+ export function dualService(ctx) {
8
+ const service = safeGet(ctx, SERVICE.dual)
9
+ return service && service.pm && service.pjm ? service : null
10
+ }
11
+
12
+ export function dualUnavailable() {
13
+ return masterError('dual-service-unavailable', 'dshMasterDual is required; enable master-dual-manager in this Host')
14
+ }
15
+
16
+ /** 注册一个返回结构化 JSON 的模型工具;异常也转成具名错误,不吞掉。 */
17
+ export function jsonTool(name, description, parameters, execute) {
18
+ return {
19
+ name,
20
+ description,
21
+ parameters,
22
+ output: {
23
+ schema: { type: 'object' },
24
+ render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
25
+ },
26
+ async execute(args, exec) {
27
+ try { return await execute(args && typeof args === 'object' ? args : {}, exec) }
28
+ catch (error) { return masterError('agent-tool-threw', String(error?.message ?? error), { tool: name }) }
29
+ },
30
+ }
31
+ }
32
+
33
+ /** 在 preset scope 内挂 persona 与 scoped tools,全部纳入 Cordis effect 生命周期。 */
34
+ export function installAgent(ctx, { prompt, tools }) {
35
+ const systemPrompt = safeGet(ctx, 'systemPrompt')
36
+ const registry = safeGet(ctx, 'tools')
37
+ if (!systemPrompt || typeof systemPrompt.section !== 'function') {
38
+ return { ok: false, code: 'system-prompt-unavailable', error: 'ctx.systemPrompt with section() is required' }
39
+ }
40
+ if (!registry || typeof registry.register !== 'function') {
41
+ return { ok: false, code: 'tools-unavailable', error: 'ctx.tools with register() is required' }
42
+ }
43
+ const registered = []
44
+ try {
45
+ ctx.effect(() => systemPrompt.section({ name: 'deployment:persona-prefix', order: 0, text: prompt }))
46
+ } catch (error) {
47
+ return { ok: false, code: 'prompt-registration-failed', error: String(error?.message ?? error) }
48
+ }
49
+ for (const definition of tools) {
50
+ try {
51
+ ctx.effect(() => registry.register(definition))
52
+ registered.push(definition.name)
53
+ } catch (error) {
54
+ return { ok: false, code: 'tool-registration-failed', error: String(error?.message ?? error), tool: definition.name, registered }
55
+ }
56
+ }
57
+ return { ok: true, tools: registered }
58
+ }
59
+
60
+ export const objectSchema = (properties = {}, required = []) => ({ type: 'object', properties, required, additionalProperties: true })
61
+ export const string = (description = '') => ({ type: 'string', description })
62
+ export const strings = (description = '') => ({ type: 'array', items: { type: 'string' }, description })
63
+ export const objects = (description = '') => ({ type: 'array', items: { type: 'object', additionalProperties: true }, description })
64
+ export const id = (description) => ({ type: 'string', description })
@@ -0,0 +1,107 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+
5
+ import { ASSESSMENT_CATEGORIES, planProgress } from './schema.ts'
6
+
7
+ const lines = (items, render) => items.length ? items.map(render).join('\n') : '- (无)'
8
+ const bullets = (items) => lines(items, (item) => `- ${item}`)
9
+ const cell = (value) => String(value ?? '—').replaceAll('|', '\\|').replaceAll('\n', ' ')
10
+
11
+ const CATEGORY_TITLES = {
12
+ completed: '1. 已完成(进度与证据)',
13
+ unfinished: '2. 未完成(原因与后续)',
14
+ blocked: '3. 无法实现 / 受阻(卡点)',
15
+ planned: '4. 已计划待开发',
16
+ conflicts: '5. 相互矛盾 / 冲突(待重新判断)',
17
+ ambiguities: '6. 理解歧义(待确认)',
18
+ }
19
+
20
+ export function renderPrdDocument(prd, assessment = null) {
21
+ return [
22
+ `# PRD · ${prd.title}`, '',
23
+ `- 文档 ID:\`${prd.id}\``,
24
+ `- 状态:${prd.status}`,
25
+ `- 优先级:${prd.priority}`,
26
+ `- 请求方:${prd.requester}`, '',
27
+ '## WHY · 解决什么问题、达成什么目标', '',
28
+ prd.why || prd.goal, '',
29
+ `**目标**:${prd.goal}`, '',
30
+ '## WHAT · 功能、模块、架构与结果', '',
31
+ bullets(prd.what), '',
32
+ '### 架构', '', prd.architecture || '(待补充)', '',
33
+ '### 模块', '',
34
+ lines(prd.modules, (item) => `- **${item.name}**(${item.status}):${item.result || item.architecture || '待补充'}`), '',
35
+ '### 预期结果', '', bullets(prd.outcomes), '',
36
+ '## 约束与非目标', '',
37
+ '### 约束', '', bullets(prd.constraints), '',
38
+ '### 非目标', '', bullets(prd.nonGoals), '',
39
+ '## 验收标准', '', bullets(prd.acceptance), '',
40
+ '## 假设与待澄清问题', '',
41
+ '### 假设', '', bullets(prd.assumptions), '',
42
+ '### 待澄清', '', bullets(prd.openQuestions), '',
43
+ assessment ? `\n---\n\n${renderAssessmentDocument(assessment)}` : '',
44
+ ].join('\n').trim() + '\n'
45
+ }
46
+
47
+ export function renderAssessmentDocument(assessment) {
48
+ const out = [`# 产品现状盘点 · ${assessment.title}`, '', `- 盘点 ID:\`${assessment.id}\``, `- 关联 PRD:${assessment.prdId ?? '未关联'}`, '']
49
+ if (assessment.why) out.push('## WHY', '', assessment.why, '')
50
+ if (assessment.what.length) out.push('## WHAT', '', bullets(assessment.what), '')
51
+ for (const category of ASSESSMENT_CATEGORIES) {
52
+ out.push(`## ${CATEGORY_TITLES[category]}`, '')
53
+ const items = assessment[category]
54
+ out.push(lines(items, (item) => {
55
+ if (category === 'completed') return `- **${item.title}**(${item.progress}%)${item.detail ? `:${item.detail}` : ''}\n - 证据:${item.evidence.join(';')}`
56
+ if (category === 'unfinished') return `- **${item.title}**:${item.reason}\n - 后续:${item.nextStep || '待计划'}`
57
+ if (category === 'blocked') return `- **${item.title}**:${item.reason}\n - 卡点:${item.blocker}\n - 下一步:${item.nextStep || '待决策'}`
58
+ if (category === 'planned') return `- **${item.title}**(${item.priority}):${item.plan || '待拆解'}${item.estimateMinutes ? `\n - 估时:${item.estimateMinutes} 分钟` : ''}`
59
+ if (category === 'conflicts') return `- **${item.title}**:${item.question}\n - 选项:${item.options.map((option) => `${option.value}=${option.label}`).join(';') || '待补充'}`
60
+ return `- **${item.title}**:${item.question}\n - 当前理解:${item.currentInterpretation || '未形成结论'}`
61
+ }), '')
62
+ }
63
+ return out.join('\n').trim() + '\n'
64
+ }
65
+
66
+ export function renderProjectDocument(plan) {
67
+ const progress = planProgress(plan)
68
+ return [
69
+ `# 项目计划 · ${plan.title}`, '',
70
+ `- 计划 ID:\`${plan.id}\``,
71
+ `- 关联 PRD:\`${plan.prdId}\``,
72
+ `- 状态:${plan.status}`,
73
+ `- 进度:${progress.done}/${progress.total}(${progress.percent}%)`,
74
+ `- 总估时:${progress.estimatedMinutes} 分钟(估算,不是承诺工期)`, '',
75
+ '## 1. 项目会审', '', bullets(plan.projectReview), '',
76
+ '## 2. 项目计划(HOW / WHERE)', '', plan.how || '(待补充)', '', `**场景**:${plan.where}`, '',
77
+ '### WBS', '',
78
+ '| 步骤 | 内容 | Owner | 场景 | 估时(分钟) | 依赖 | 状态 |',
79
+ '|---|---|---|---|---:|---|---|',
80
+ ...plan.steps.map((step) => `| ${cell(step.id)} | ${cell(step.title)} | ${cell(step.owner)} | ${cell(step.where)} | ${step.estimateMinutes} | ${cell(step.dependsOn.join(', '))} | ${cell(step.status)} |`), '',
81
+ '## 3. 项目需求与内容', '', bullets(plan.requirements), '',
82
+ '## 4. 项目时间', '',
83
+ `- 开始时间:${plan.schedule.startAt ?? '待定'}`,
84
+ `- 总估时:${plan.schedule.totalEstimateMinutes} 分钟`, '',
85
+ lines(plan.schedule.milestones, (item) => `- **${item.title}**:${item.estimateMinutes} 分钟 · ${item.owner} · 依赖 ${item.dependsOn.join(', ') || '无'}`), '',
86
+ '## 5. 项目人员', '',
87
+ lines(plan.team, (item) => `- **${item.role}** · ${item.owner}:${item.responsibility || '待分配'}`), '',
88
+ '## 6. 测试、部署、交付与验收', '',
89
+ '### 测试', '', bullets(plan.quality.tests), '',
90
+ '### 部署', '', bullets(plan.quality.deployment), '',
91
+ '### 验收', '', bullets(plan.quality.acceptance), '',
92
+ '## 7. 项目结案', '',
93
+ plan.closure.status === 'closed' ? renderClosureDocument(plan) : '- 尚未结案;必须全部步骤完成后才能结案。',
94
+ ].join('\n').trim() + '\n'
95
+ }
96
+
97
+ export function renderClosureDocument(plan) {
98
+ const closure = plan.closure
99
+ return [
100
+ `### 结案状态:${closure.status}`, '',
101
+ `- 结案时间:${closure.closedAt ?? '未结案'}`,
102
+ `- 总结:${closure.summary || '未填写'}`, '',
103
+ '#### 评审报告', '', closure.reviewReport || '(待补充)', '',
104
+ '#### 验收记录', '', bullets(closure.acceptance), '',
105
+ '#### 项目资料汇总', '', bullets(closure.artifacts), '',
106
+ ].join('\n')
107
+ }