@openprd/cli 0.1.8 → 0.1.10

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 (134) hide show
  1. package/.openprd/benchmarks/index.md +15 -1
  2. package/.openprd/config.yaml +10 -0
  3. package/.openprd/design/README.md +47 -0
  4. package/.openprd/design/README_EN.md +25 -0
  5. package/.openprd/design/active/asset-spec.md +15 -0
  6. package/.openprd/design/active/direction-plan.md +7 -0
  7. package/.openprd/design/active/facts-sheet.md +13 -0
  8. package/.openprd/design/active/image-preflight.md +9 -0
  9. package/.openprd/design/active/selected-direction.md +11 -0
  10. package/.openprd/design/anti-slop.md +16 -0
  11. package/.openprd/design/assets/dark-mesh.svg +14 -0
  12. package/.openprd/design/assets/paper-grid.svg +14 -0
  13. package/.openprd/design/assets/surface-presets.md +21 -0
  14. package/.openprd/design/assets/workbench-grid.svg +16 -0
  15. package/.openprd/design/checklists/ui-quality-gate.md +22 -0
  16. package/.openprd/design/components/component-catalog.md +42 -0
  17. package/.openprd/design/layouts/layout-catalog.json +45 -0
  18. package/.openprd/design/lenses/frontend-lenses.md +31 -0
  19. package/.openprd/design/recipes/content-experience.md +26 -0
  20. package/.openprd/design/recipes/operational-tool.md +25 -0
  21. package/.openprd/design/recipes/product-launch.md +26 -0
  22. package/.openprd/design/templates/README.md +36 -0
  23. package/.openprd/design/templates/README_EN.md +31 -0
  24. package/.openprd/design/templates/content-home.html +415 -0
  25. package/.openprd/design/templates/ops-dashboard.html +340 -0
  26. package/.openprd/design/templates/product-launch.html +360 -0
  27. package/.openprd/design/themes/theme-catalog.json +77 -0
  28. package/.openprd/engagements/active/prd.md +111 -100
  29. package/.openprd/schema/prd.schema.yaml +10 -0
  30. package/.openprd/templates/base/intake.md +4 -0
  31. package/.openprd/templates/base/prd.md +16 -6
  32. package/AGENTS.md +10 -7
  33. package/README.md +28 -10
  34. package/README_EN.md +23 -3
  35. package/package.json +41 -2
  36. package/scripts/openprd-codex-isolated-worker.mjs +799 -0
  37. package/skills/openprd-benchmark-router/SKILL.md +4 -0
  38. package/skills/openprd-frontend-design/SKILL.md +161 -0
  39. package/skills/openprd-frontend-design/references/design-asset-contract.md +34 -0
  40. package/skills/openprd-frontend-design/references/direction-engine.md +46 -0
  41. package/skills/openprd-harness/SKILL.md +38 -21
  42. package/skills/openprd-learning-review/SKILL.md +2 -1
  43. package/skills/openprd-learning-review/references/style-packs/xianxia-cultivation.prompt.md +1 -1
  44. package/skills/openprd-quality/SKILL.md +6 -4
  45. package/skills/openprd-requirement-intake/SKILL.md +27 -5
  46. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -3
  47. package/skills/openprd-requirement-intake/references/routing-rubric.md +9 -5
  48. package/skills/openprd-requirement-intake/references/startup-validation-lens.md +23 -0
  49. package/skills/openprd-router/SKILL.md +13 -5
  50. package/skills/openprd-shared/SKILL.md +38 -25
  51. package/src/agent-integration.js +126 -75
  52. package/src/brainstorm-artifacts.js +1657 -0
  53. package/src/brainstorm-presentation.js +223 -0
  54. package/src/brainstorm.js +603 -0
  55. package/src/cli/args.js +51 -3
  56. package/src/cli/doctor-print.js +34 -8
  57. package/src/cli/print.js +6 -0
  58. package/src/cli/quality-print.js +70 -0
  59. package/src/cli/run-print.js +43 -1
  60. package/src/cli/shared-print.js +33 -2
  61. package/src/cli/workflow-print.js +21 -0
  62. package/src/codex-hook-runner-template.mjs +858 -65
  63. package/src/design-starter-support.js +462 -0
  64. package/src/design-starter.js +1207 -0
  65. package/src/knowledge.js +377 -21
  66. package/src/learning-review.js +3 -3
  67. package/src/loop.js +839 -101
  68. package/src/openprd.js +86 -1
  69. package/src/openspec/change-validate.js +1 -0
  70. package/src/prd-core.js +18 -0
  71. package/src/quality-learning.js +50 -6
  72. package/src/quality.js +2 -2
  73. package/src/run-harness.js +191 -29
  74. package/src/self-update.js +301 -4
  75. package/src/standards.js +46 -5
  76. package/src/visual-prepare.js +940 -0
  77. package/src/workspace-core.js +137 -0
  78. package/src/workspace-workflow.js +190 -7
  79. package/.openprd/benchmarks/evidence/milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code.md +0 -14
  80. package/.openprd/benchmarks/evidence/nolanlawson-com-using-ai-to-write-better-code-more-slowly.md +0 -14
  81. package/.openprd/discovery/config.json +0 -35
  82. package/.openprd/engagements/active/flows.md +0 -35
  83. package/.openprd/engagements/active/handoff.md +0 -16
  84. package/.openprd/engagements/active/review.html +0 -61
  85. package/.openprd/engagements/active/roles.md +0 -22
  86. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +0 -23
  87. package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +0 -18
  88. package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +0 -18
  89. package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +0 -18
  90. package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +0 -18
  91. package/.openprd/exports/.gitkeep +0 -0
  92. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +0 -78
  93. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +0 -129
  94. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +0 -41
  95. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +0 -14
  96. package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +0 -49
  97. package/.openprd/knowledge/index.json +0 -47
  98. package/.openprd/reviews/v0001.html +0 -1322
  99. package/.openprd/reviews/v0002.html +0 -1150
  100. package/.openprd/reviews/v0003.html +0 -1150
  101. package/.openprd/reviews/v0004.html +0 -1150
  102. package/.openprd/reviews/v0005.html +0 -1150
  103. package/.openprd/sessions/.gitkeep +0 -0
  104. package/.openprd/state/changes.json +0 -27
  105. package/.openprd/state/current.json +0 -505
  106. package/.openprd/state/release-ledger.json +0 -344
  107. package/.openprd/state/version-index.json +0 -67
  108. package/.openprd/state/versions/.gitkeep +0 -0
  109. package/.openprd/state/versions/v0001.json +0 -121
  110. package/.openprd/state/versions/v0001.md +0 -161
  111. package/.openprd/state/versions/v0002.json +0 -264
  112. package/.openprd/state/versions/v0002.md +0 -183
  113. package/.openprd/state/versions/v0003.json +0 -269
  114. package/.openprd/state/versions/v0003.md +0 -188
  115. package/.openprd/state/versions/v0004.json +0 -274
  116. package/.openprd/state/versions/v0004.md +0 -193
  117. package/.openprd/state/versions/v0005.json +0 -299
  118. package/.openprd/state/versions/v0005.md +0 -189
  119. package/docs/assets/openprd-capability-overview-en.png +0 -0
  120. package/docs/assets/openprd-capability-overview-zh.png +0 -0
  121. package/docs/assets/openprd-learning-html.png +0 -0
  122. package/docs/assets/openprd-quality-html.png +0 -0
  123. package/docs/assets/openprd-requirement-routing-en.png +0 -0
  124. package/docs/assets/openprd-requirement-routing-en.svg +0 -102
  125. package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
  126. package/docs/assets/openprd-requirement-routing-zh.png +0 -0
  127. package/docs/assets/openprd-requirement-routing-zh.svg +0 -102
  128. package/docs/assets/openprd-review-html.png +0 -0
  129. package/docs/assets/openprd-scenario-overview.png +0 -0
  130. package/docs/assets/openprd-scenario-overview.svg +0 -114
  131. package/docs/assets/openprd-self-evolving-mechanisms-en.png +0 -0
  132. package/docs/assets/openprd-self-evolving-mechanisms-zh.png +0 -0
  133. package/docs/assets/openprd-visual-compare-case-study-en.png +0 -0
  134. package/docs/assets/openprd-visual-compare-case-study-zh.png +0 -0
@@ -0,0 +1,223 @@
1
+ /*
2
+ * 核心功能
3
+ * 校验并写入脑暴模式的展示文案,让 brainstorm.html 使用稳定的 presentation 契约渲染。
4
+ *
5
+ * 输入
6
+ * 接收当前项目脑暴状态和 Agent 生成的 presentation JSON。
7
+ *
8
+ * 输出
9
+ * 返回校验结果,在通过时写回 brainstormPresentation 与 meta,并重渲染脑暴页面。
10
+ *
11
+ * 定位
12
+ * 位于脑暴模式展示治理层,类似 review-presentation,但作用对象是 brainstorm.json。
13
+ *
14
+ * 依赖
15
+ * 依赖 brainstorm-artifacts 的契约与反馈逻辑,以及 brainstorm.js 的状态加载和重渲染能力。
16
+ *
17
+ * 维护规则
18
+ * 修改 presentation 契约时,必须同步维护模板、反馈信息、写回元数据和测试。
19
+ */
20
+ import fs from 'node:fs/promises';
21
+ import crypto from 'node:crypto';
22
+ import path from 'node:path';
23
+ import {
24
+ BRAINSTORM_PRESENTATION_CONTRACT,
25
+ buildBrainstormPresentationFeedback,
26
+ } from './brainstorm-artifacts.js';
27
+ import { loadBrainstormState, renderBrainstormWorkspaceArtifacts } from './brainstorm.js';
28
+
29
+ export const BRAINSTORM_PRESENTATION_TEMPLATE = {
30
+ hero: {
31
+ summary: '120 字内说明这次到底在讨论什么、为什么现在值得先想清楚。',
32
+ direction: '40 字内说明目前更建议怎么做。',
33
+ confidence: '28 字内说明还差什么确认或先停条件。',
34
+ },
35
+ visualScenes: [
36
+ {
37
+ type: 'focus-strip',
38
+ title: '一眼看懂这次讨论',
39
+ subtitle: '把核心诉求、目标、当前做法和推荐方向串起来。',
40
+ items: [
41
+ { label: '核心诉求', title: '先说清要解决什么', detail: '先把这次真正要解决的问题讲明白。', tone: 'function' },
42
+ { label: '目标结果', title: '先定义第一版结果', detail: '先想清楚第一版最想换来的结果。', tone: 'flow' },
43
+ { label: '当前做法', title: '先回到现状', detail: '先说清现在主要靠什么办法在解决。', tone: 'guardrail' },
44
+ { label: '推荐方向', title: '先收敛第一版做法', detail: '先缩小范围,再决定怎么进入 PRD。', tone: 'success' },
45
+ ],
46
+ },
47
+ {
48
+ type: 'validation-ladder',
49
+ title: '先验证什么',
50
+ subtitle: '把关键前提、低成本验证、过关标准和止损线摆出来。',
51
+ items: [
52
+ { label: '关键前提', title: '先确认什么必须为真', detail: '优先挑最影响方向判断的一条前提。', tone: 'risk' },
53
+ { label: '先怎么验', title: '先做最低成本验证', detail: '不要先写大方案,先定一个最便宜的验证动作。', tone: 'map' },
54
+ { label: '什么算过', title: '先定义通过标准', detail: '先说清看到什么结果,才算值得继续做。', tone: 'success' },
55
+ { label: '什么先停', title: '提前约定止损线', detail: '先想清楚什么情况出现时,就先暂停。', tone: 'guardrail' },
56
+ ],
57
+ },
58
+ ],
59
+ panels: {
60
+ userSignals: [
61
+ { summary: '真实场景', detail: '先抓最近一次真实案例,不要泛泛而谈。' },
62
+ { summary: '现在怎么做', detail: '先说清现在主要靠什么办法在解决。' },
63
+ { summary: '为什么现在做', detail: '先说清这次为什么是现在,不是以后。' },
64
+ ],
65
+ marketSignals: [
66
+ { summary: '推荐方向', detail: '先说清当前更建议怎么做,为什么先走这条路。' },
67
+ { summary: '备选方向', detail: '至少给一条备选路,方便用户比较取舍。' },
68
+ ],
69
+ validationLoop: [
70
+ { summary: '先找谁验', detail: '先补第一批最容易触达、最可能给真实反馈的人。' },
71
+ { summary: '当前替代', detail: '先说清用户现在主要靠什么办法在解决。' },
72
+ { summary: '手工路径', detail: '先补不做完整产品时也能交付价值的手工路径。' },
73
+ ],
74
+ businessViability: [
75
+ { summary: '承诺信号', detail: '先定义什么真实承诺最能证明值得继续。' },
76
+ { summary: '最低成本验证', detail: '优先写最便宜的验证动作,不要一上来做全套方案。' },
77
+ { summary: '先活下来', detail: '先说清验证阶段怎样控制成本、时间和交付方式。' },
78
+ ],
79
+ reuseOpportunities: [
80
+ { summary: '现有基础', detail: '指出现在已经有什么能直接借,不要默认从零开始。' },
81
+ { summary: '关键参与方', detail: '补谁会拍板、谁会受影响、谁需要一起参与。' },
82
+ ],
83
+ risks: [
84
+ { summary: '关键前提', detail: '把这件事要成立必须为真的条件摆出来。' },
85
+ { summary: '先怎么验证', detail: '优先写最低成本验证动作,不要只写开放问题。' },
86
+ { summary: '止损线', detail: '先说清什么情况下先停,避免投入失控。' },
87
+ ],
88
+ },
89
+ };
90
+
91
+ export function buildBrainstormPresentationTemplatePayload() {
92
+ return {
93
+ intent: 'Agent 先按这个模板写 brainstormPresentation,再用本脚本校验;页面重点不只是整理需求,还要把当前替代方案、推荐方向、关键前提、验证动作和止损线说清楚。除了正文卡片,还可以用 visualScenes 生成更灵活的可视化区域,但仍然要走统一契约,不直接手写任意 SVG。',
94
+ presentationTemplate: BRAINSTORM_PRESENTATION_TEMPLATE,
95
+ presentationContract: BRAINSTORM_PRESENTATION_CONTRACT,
96
+ };
97
+ }
98
+
99
+ export async function brainstormPresentationWorkspace(projectRoot, options = {}) {
100
+ const resolvedProjectRoot = path.resolve(projectRoot);
101
+ const { ws, record } = await loadBrainstormState(resolvedProjectRoot);
102
+ let presentationSource = record.brainstormPresentation ? 'state' : null;
103
+
104
+ if (options.presentationPath) {
105
+ const presentationPath = path.resolve(options.presentationPath);
106
+ record.brainstormPresentation = normalizeBrainstormPresentationInput(await readJson(presentationPath));
107
+ delete record.brainstormPresentationMeta;
108
+ presentationSource = presentationPath;
109
+ }
110
+
111
+ if (options.write && !options.presentationPath) {
112
+ throw new Error('--write 需要配合 --presentation,避免误写没有更新的展示文案。');
113
+ }
114
+
115
+ const feedback = buildBrainstormPresentationFeedback(record);
116
+ if (options.write && options.presentationPath && feedback.violations.length === 0) {
117
+ record.brainstormPresentationMeta = buildBrainstormPresentationMeta({
118
+ presentation: record.brainstormPresentation,
119
+ feedback,
120
+ source: presentationSource,
121
+ });
122
+ }
123
+
124
+ const gate = getBrainstormPresentationGate(record, feedback);
125
+ const result = {
126
+ ok: gate.ok,
127
+ artifactId: record.artifactId,
128
+ title: record.title,
129
+ topic: record.topic,
130
+ statePath: ws.paths.activeBrainstormState,
131
+ presentationSource,
132
+ presentationContract: feedback.contract,
133
+ presentationFeedback: feedback.violations,
134
+ brainstormPresentationGate: gate,
135
+ };
136
+
137
+ if (options.write) {
138
+ if (!gate.ok) {
139
+ result.writeBlocked = true;
140
+ return result;
141
+ }
142
+ await fs.writeFile(ws.paths.activeBrainstormState, `${JSON.stringify(record, null, 2)}\n`);
143
+ result.written = ws.paths.activeBrainstormState;
144
+ const rendered = await renderBrainstormWorkspaceArtifacts(resolvedProjectRoot, record, { open: false });
145
+ result.htmlPath = rendered.htmlPath;
146
+ result.bundleHtmlPath = rendered.bundleHtmlPath;
147
+ result.markdownPath = rendered.markdownPath;
148
+ result.patchPath = rendered.patchPath;
149
+ }
150
+
151
+ return result;
152
+ }
153
+
154
+ export function getBrainstormPresentationGate(record, feedback = buildBrainstormPresentationFeedback(record)) {
155
+ const errors = [];
156
+ const presentation = record?.brainstormPresentation;
157
+ const meta = record?.brainstormPresentationMeta;
158
+ if (!presentation || typeof presentation !== 'object' || Array.isArray(presentation)) {
159
+ errors.push('缺少已由脚本写入的 brainstormPresentation。先运行 openprd brainstorm-presentation . --template,再填写 presentation JSON。');
160
+ }
161
+ if (feedback.violations.length > 0) {
162
+ errors.push(`brainstormPresentation 仍有 ${feedback.violations.length} 个超限或格式问题。请按 presentationFeedback 中的 jsonPath 重写后再写入。`);
163
+ }
164
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta)) {
165
+ errors.push('缺少 brainstormPresentationMeta。必须通过 openprd brainstorm-presentation --presentation <json> --write --fail-on-violation 写入,不能手工改状态文件。');
166
+ } else {
167
+ const expectedPresentationHash = hashStableJson(presentation ?? null);
168
+ const expectedViolationsHash = hashStableJson(feedback.violations);
169
+ if (meta.presentationHash !== expectedPresentationHash) {
170
+ errors.push('brainstormPresentationMeta.presentationHash 与当前 brainstormPresentation 不一致,请重新运行 brainstorm-presentation 写入。');
171
+ }
172
+ if (meta.violationsHash !== expectedViolationsHash) {
173
+ errors.push('brainstormPresentationMeta.violationsHash 与当前校验结果不一致,请重新运行 brainstorm-presentation 写入。');
174
+ }
175
+ if (!meta.validatedAt) {
176
+ errors.push('brainstormPresentationMeta.validatedAt 缺失,请重新运行 brainstorm-presentation 写入。');
177
+ }
178
+ }
179
+ return {
180
+ ok: errors.length === 0,
181
+ errors,
182
+ violations: feedback.violations,
183
+ requiredCommand: 'openprd brainstorm-presentation . --presentation <brainstorm-presentation.json> --write --fail-on-violation',
184
+ };
185
+ }
186
+
187
+ function buildBrainstormPresentationMeta({ presentation, feedback, source }) {
188
+ return {
189
+ validatedAt: new Date().toISOString(),
190
+ source: source ?? 'state',
191
+ presentationHash: hashStableJson(presentation ?? null),
192
+ violationsHash: hashStableJson(feedback.violations),
193
+ validator: 'openprd brainstorm-presentation',
194
+ };
195
+ }
196
+
197
+ function hashStableJson(value) {
198
+ return crypto.createHash('sha256').update(stableStringify(value)).digest('hex');
199
+ }
200
+
201
+ function stableStringify(value) {
202
+ if (Array.isArray(value)) {
203
+ return `[${value.map(stableStringify).join(',')}]`;
204
+ }
205
+ if (value && typeof value === 'object') {
206
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
207
+ }
208
+ return JSON.stringify(value);
209
+ }
210
+
211
+ async function readJson(filePath) {
212
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
213
+ }
214
+
215
+ function normalizeBrainstormPresentationInput(value) {
216
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
217
+ throw new Error('presentation JSON 必须是对象。');
218
+ }
219
+ if (value.brainstormPresentation && typeof value.brainstormPresentation === 'object' && !Array.isArray(value.brainstormPresentation)) {
220
+ return value.brainstormPresentation;
221
+ }
222
+ return value;
223
+ }