@openprd/cli 0.1.9 → 0.1.11

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 (138) hide show
  1. package/.openprd/benchmarks/index.md +16 -0
  2. package/.openprd/benchmarks/sources.yaml +53 -1
  3. package/.openprd/config.yaml +10 -0
  4. package/.openprd/design/README.md +47 -0
  5. package/.openprd/design/README_EN.md +25 -0
  6. package/.openprd/design/active/asset-spec.md +15 -0
  7. package/.openprd/design/active/direction-plan.md +7 -0
  8. package/.openprd/design/active/facts-sheet.md +13 -0
  9. package/.openprd/design/active/image-preflight.md +9 -0
  10. package/.openprd/design/active/selected-direction.md +11 -0
  11. package/.openprd/design/anti-slop.md +16 -0
  12. package/.openprd/design/assets/dark-mesh.svg +14 -0
  13. package/.openprd/design/assets/paper-grid.svg +14 -0
  14. package/.openprd/design/assets/surface-presets.md +21 -0
  15. package/.openprd/design/assets/workbench-grid.svg +16 -0
  16. package/.openprd/design/checklists/ui-quality-gate.md +22 -0
  17. package/.openprd/design/components/component-catalog.md +42 -0
  18. package/.openprd/design/layouts/layout-catalog.json +45 -0
  19. package/.openprd/design/lenses/frontend-lenses.md +31 -0
  20. package/.openprd/design/recipes/content-experience.md +26 -0
  21. package/.openprd/design/recipes/operational-tool.md +25 -0
  22. package/.openprd/design/recipes/product-launch.md +26 -0
  23. package/.openprd/design/templates/README.md +36 -0
  24. package/.openprd/design/templates/README_EN.md +31 -0
  25. package/.openprd/design/templates/content-home.html +415 -0
  26. package/.openprd/design/templates/ops-dashboard.html +340 -0
  27. package/.openprd/design/templates/product-launch.html +360 -0
  28. package/.openprd/design/themes/theme-catalog.json +77 -0
  29. package/.openprd/engagements/active/prd.md +111 -100
  30. package/.openprd/schema/prd.schema.yaml +25 -0
  31. package/.openprd/templates/base/intake.md +17 -0
  32. package/.openprd/templates/base/prd.md +31 -6
  33. package/AGENTS.md +10 -7
  34. package/README.md +26 -28
  35. package/README_EN.md +3 -3
  36. package/package.json +41 -2
  37. package/scripts/openprd-codex-isolated-worker.mjs +799 -0
  38. package/skills/openprd-benchmark-router/SKILL.md +5 -0
  39. package/skills/openprd-frontend-design/SKILL.md +161 -0
  40. package/skills/openprd-frontend-design/references/design-asset-contract.md +34 -0
  41. package/skills/openprd-frontend-design/references/direction-engine.md +46 -0
  42. package/skills/openprd-harness/SKILL.md +37 -20
  43. package/skills/openprd-learning-review/SKILL.md +2 -1
  44. package/skills/openprd-learning-review/references/style-packs/xianxia-cultivation.prompt.md +1 -1
  45. package/skills/openprd-quality/SKILL.md +5 -3
  46. package/skills/openprd-requirement-intake/SKILL.md +27 -5
  47. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -3
  48. package/skills/openprd-requirement-intake/references/routing-rubric.md +9 -5
  49. package/skills/openprd-requirement-intake/references/startup-validation-lens.md +108 -0
  50. package/skills/openprd-router/SKILL.md +13 -5
  51. package/skills/openprd-shared/SKILL.md +38 -25
  52. package/src/agent-integration.js +120 -69
  53. package/src/brainstorm-artifacts.js +1805 -0
  54. package/src/brainstorm-presentation.js +233 -0
  55. package/src/brainstorm.js +766 -0
  56. package/src/cli/args.js +51 -3
  57. package/src/cli/doctor-print.js +34 -8
  58. package/src/cli/print.js +6 -0
  59. package/src/cli/quality-print.js +79 -3
  60. package/src/cli/run-print.js +43 -1
  61. package/src/cli/shared-print.js +33 -2
  62. package/src/cli/workflow-print.js +21 -0
  63. package/src/codex-hook-runner-template.mjs +838 -58
  64. package/src/design-starter-support.js +462 -0
  65. package/src/design-starter.js +1207 -0
  66. package/src/diagram-core.js +384 -151
  67. package/src/html-artifacts.js +403 -50
  68. package/src/knowledge.js +291 -32
  69. package/src/learning-review.js +3 -3
  70. package/src/loop.js +839 -101
  71. package/src/openprd.js +86 -1
  72. package/src/openspec/change-validate.js +1 -0
  73. package/src/prd-core.js +119 -2
  74. package/src/quality-html-artifact.js +30 -5
  75. package/src/quality-learning.js +50 -6
  76. package/src/quality.js +22 -12
  77. package/src/run-harness.js +191 -29
  78. package/src/self-update.js +301 -4
  79. package/src/standards.js +46 -5
  80. package/src/visual-prepare.js +940 -0
  81. package/src/workspace-core.js +175 -0
  82. package/src/workspace-workflow.js +307 -7
  83. package/.openprd/benchmarks/evidence/milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code.md +0 -14
  84. package/.openprd/benchmarks/evidence/nolanlawson-com-using-ai-to-write-better-code-more-slowly.md +0 -14
  85. package/.openprd/discovery/config.json +0 -35
  86. package/.openprd/engagements/active/flows.md +0 -35
  87. package/.openprd/engagements/active/handoff.md +0 -16
  88. package/.openprd/engagements/active/review.html +0 -61
  89. package/.openprd/engagements/active/roles.md +0 -22
  90. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +0 -23
  91. package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +0 -18
  92. package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +0 -18
  93. package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +0 -18
  94. package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +0 -18
  95. package/.openprd/exports/.gitkeep +0 -0
  96. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +0 -78
  97. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +0 -129
  98. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +0 -41
  99. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +0 -14
  100. package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +0 -49
  101. package/.openprd/knowledge/index.json +0 -47
  102. package/.openprd/reviews/v0001.html +0 -1322
  103. package/.openprd/reviews/v0002.html +0 -1150
  104. package/.openprd/reviews/v0003.html +0 -1150
  105. package/.openprd/reviews/v0004.html +0 -1150
  106. package/.openprd/reviews/v0005.html +0 -1150
  107. package/.openprd/sessions/.gitkeep +0 -0
  108. package/.openprd/state/changes.json +0 -27
  109. package/.openprd/state/current.json +0 -505
  110. package/.openprd/state/release-ledger.json +0 -387
  111. package/.openprd/state/version-index.json +0 -67
  112. package/.openprd/state/versions/.gitkeep +0 -0
  113. package/.openprd/state/versions/v0001.json +0 -121
  114. package/.openprd/state/versions/v0001.md +0 -161
  115. package/.openprd/state/versions/v0002.json +0 -264
  116. package/.openprd/state/versions/v0002.md +0 -183
  117. package/.openprd/state/versions/v0003.json +0 -269
  118. package/.openprd/state/versions/v0003.md +0 -188
  119. package/.openprd/state/versions/v0004.json +0 -274
  120. package/.openprd/state/versions/v0004.md +0 -193
  121. package/.openprd/state/versions/v0005.json +0 -299
  122. package/.openprd/state/versions/v0005.md +0 -189
  123. package/docs/assets/openprd-capability-overview-en.png +0 -0
  124. package/docs/assets/openprd-capability-overview-zh.png +0 -0
  125. package/docs/assets/openprd-learning-html.png +0 -0
  126. package/docs/assets/openprd-quality-html.png +0 -0
  127. package/docs/assets/openprd-requirement-routing-en.png +0 -0
  128. package/docs/assets/openprd-requirement-routing-en.svg +0 -102
  129. package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
  130. package/docs/assets/openprd-requirement-routing-zh.png +0 -0
  131. package/docs/assets/openprd-requirement-routing-zh.svg +0 -102
  132. package/docs/assets/openprd-review-html.png +0 -0
  133. package/docs/assets/openprd-scenario-overview.png +0 -0
  134. package/docs/assets/openprd-scenario-overview.svg +0 -114
  135. package/docs/assets/openprd-self-evolving-mechanisms-en.png +0 -0
  136. package/docs/assets/openprd-self-evolving-mechanisms-zh.png +0 -0
  137. package/docs/assets/openprd-visual-compare-case-study-en.png +0 -0
  138. package/docs/assets/openprd-visual-compare-case-study-zh.png +0 -0
@@ -0,0 +1,233 @@
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
+ { summary: '手工作战卡', detail: '把触发条件、步骤、工具、耗时和交接点先写清楚。' },
74
+ ],
75
+ businessViability: [
76
+ { summary: '承诺信号', detail: '先定义什么真实承诺最能证明值得继续。' },
77
+ { summary: '付费证明', detail: '先补有没有 10 个样本、3/10 愿意付费或更强交易信号。' },
78
+ { summary: '最低成本验证', detail: '优先写最便宜的验证动作,不要一上来做全套方案。' },
79
+ { summary: '先怎么收费', detail: '先给一个从第一个客户开始就成立的收费假设。' },
80
+ { summary: '增长纪律', detail: '先约束什么时候不该 launch、什么时候别急着花钱或扩团队。' },
81
+ ],
82
+ minimalistReview: [
83
+ { summary: '更小的执行', detail: '先判断能不能继续用 spreadsheet、表单或 no-code 保持小;如果必须开始做产品,也只自动化最重复的一步并先压成 forms / lists / CRUD 骨架,不要先为假想中的未来客户造复杂能力。' },
84
+ { summary: '产品化门槛', detail: '先写清达到什么条件才允许继续产品化或加功能。' },
85
+ { summary: '是否可逆', detail: '先判断验证结果一般时,这条路是不是容易回退,避免先做重招聘、长期绑定或重平台化。' },
86
+ { summary: '客户真问题', detail: '先看这更像客户痛点,还是团队自己的技术冲动。' },
87
+ { summary: '价值观一致', detail: '先确认这条路是否符合团队想坚持的做法,以及它是不是你愿意长期住进去的业务形态。' },
88
+ ],
89
+ reuseOpportunities: [
90
+ { summary: '现有基础', detail: '指出现在已经有什么能直接借,不要默认从零开始。' },
91
+ { summary: '关键参与方', detail: '补谁会拍板、谁会受影响、谁需要一起参与。' },
92
+ ],
93
+ risks: [
94
+ { summary: '关键前提', detail: '把这件事要成立必须为真的条件摆出来。' },
95
+ { summary: '先怎么验证', detail: '优先写最低成本验证动作,不要只写开放问题。' },
96
+ { summary: '止损线', detail: '先说清什么情况下先停,避免投入失控。' },
97
+ ],
98
+ },
99
+ };
100
+
101
+ export function buildBrainstormPresentationTemplatePayload() {
102
+ return {
103
+ intent: 'Agent 先按这个模板写 brainstormPresentation,再用本脚本校验;页面重点不只是整理需求,还要把当前替代方案、社区契合、痛点证据、手工作战卡、一件事 MVP、最小工具桥接、首批成交、产品化门槛、增长纪律、关键前提、验证动作和止损线说清楚。除了正文卡片,还可以用 visualScenes 生成更灵活的可视化区域,但仍然要走统一契约,不直接手写任意 SVG。',
104
+ presentationTemplate: BRAINSTORM_PRESENTATION_TEMPLATE,
105
+ presentationContract: BRAINSTORM_PRESENTATION_CONTRACT,
106
+ };
107
+ }
108
+
109
+ export async function brainstormPresentationWorkspace(projectRoot, options = {}) {
110
+ const resolvedProjectRoot = path.resolve(projectRoot);
111
+ const { ws, record } = await loadBrainstormState(resolvedProjectRoot);
112
+ let presentationSource = record.brainstormPresentation ? 'state' : null;
113
+
114
+ if (options.presentationPath) {
115
+ const presentationPath = path.resolve(options.presentationPath);
116
+ record.brainstormPresentation = normalizeBrainstormPresentationInput(await readJson(presentationPath));
117
+ delete record.brainstormPresentationMeta;
118
+ presentationSource = presentationPath;
119
+ }
120
+
121
+ if (options.write && !options.presentationPath) {
122
+ throw new Error('--write 需要配合 --presentation,避免误写没有更新的展示文案。');
123
+ }
124
+
125
+ const feedback = buildBrainstormPresentationFeedback(record);
126
+ if (options.write && options.presentationPath && feedback.violations.length === 0) {
127
+ record.brainstormPresentationMeta = buildBrainstormPresentationMeta({
128
+ presentation: record.brainstormPresentation,
129
+ feedback,
130
+ source: presentationSource,
131
+ });
132
+ }
133
+
134
+ const gate = getBrainstormPresentationGate(record, feedback);
135
+ const result = {
136
+ ok: gate.ok,
137
+ artifactId: record.artifactId,
138
+ title: record.title,
139
+ topic: record.topic,
140
+ statePath: ws.paths.activeBrainstormState,
141
+ presentationSource,
142
+ presentationContract: feedback.contract,
143
+ presentationFeedback: feedback.violations,
144
+ brainstormPresentationGate: gate,
145
+ };
146
+
147
+ if (options.write) {
148
+ if (!gate.ok) {
149
+ result.writeBlocked = true;
150
+ return result;
151
+ }
152
+ await fs.writeFile(ws.paths.activeBrainstormState, `${JSON.stringify(record, null, 2)}\n`);
153
+ result.written = ws.paths.activeBrainstormState;
154
+ const rendered = await renderBrainstormWorkspaceArtifacts(resolvedProjectRoot, record, { open: false });
155
+ result.htmlPath = rendered.htmlPath;
156
+ result.bundleHtmlPath = rendered.bundleHtmlPath;
157
+ result.markdownPath = rendered.markdownPath;
158
+ result.patchPath = rendered.patchPath;
159
+ }
160
+
161
+ return result;
162
+ }
163
+
164
+ export function getBrainstormPresentationGate(record, feedback = buildBrainstormPresentationFeedback(record)) {
165
+ const errors = [];
166
+ const presentation = record?.brainstormPresentation;
167
+ const meta = record?.brainstormPresentationMeta;
168
+ if (!presentation || typeof presentation !== 'object' || Array.isArray(presentation)) {
169
+ errors.push('缺少已由脚本写入的 brainstormPresentation。先运行 openprd brainstorm-presentation . --template,再填写 presentation JSON。');
170
+ }
171
+ if (feedback.violations.length > 0) {
172
+ errors.push(`brainstormPresentation 仍有 ${feedback.violations.length} 个超限或格式问题。请按 presentationFeedback 中的 jsonPath 重写后再写入。`);
173
+ }
174
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta)) {
175
+ errors.push('缺少 brainstormPresentationMeta。必须通过 openprd brainstorm-presentation --presentation <json> --write --fail-on-violation 写入,不能手工改状态文件。');
176
+ } else {
177
+ const expectedPresentationHash = hashStableJson(presentation ?? null);
178
+ const expectedViolationsHash = hashStableJson(feedback.violations);
179
+ if (meta.presentationHash !== expectedPresentationHash) {
180
+ errors.push('brainstormPresentationMeta.presentationHash 与当前 brainstormPresentation 不一致,请重新运行 brainstorm-presentation 写入。');
181
+ }
182
+ if (meta.violationsHash !== expectedViolationsHash) {
183
+ errors.push('brainstormPresentationMeta.violationsHash 与当前校验结果不一致,请重新运行 brainstorm-presentation 写入。');
184
+ }
185
+ if (!meta.validatedAt) {
186
+ errors.push('brainstormPresentationMeta.validatedAt 缺失,请重新运行 brainstorm-presentation 写入。');
187
+ }
188
+ }
189
+ return {
190
+ ok: errors.length === 0,
191
+ errors,
192
+ violations: feedback.violations,
193
+ requiredCommand: 'openprd brainstorm-presentation . --presentation <brainstorm-presentation.json> --write --fail-on-violation',
194
+ };
195
+ }
196
+
197
+ function buildBrainstormPresentationMeta({ presentation, feedback, source }) {
198
+ return {
199
+ validatedAt: new Date().toISOString(),
200
+ source: source ?? 'state',
201
+ presentationHash: hashStableJson(presentation ?? null),
202
+ violationsHash: hashStableJson(feedback.violations),
203
+ validator: 'openprd brainstorm-presentation',
204
+ };
205
+ }
206
+
207
+ function hashStableJson(value) {
208
+ return crypto.createHash('sha256').update(stableStringify(value)).digest('hex');
209
+ }
210
+
211
+ function stableStringify(value) {
212
+ if (Array.isArray(value)) {
213
+ return `[${value.map(stableStringify).join(',')}]`;
214
+ }
215
+ if (value && typeof value === 'object') {
216
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
217
+ }
218
+ return JSON.stringify(value);
219
+ }
220
+
221
+ async function readJson(filePath) {
222
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
223
+ }
224
+
225
+ function normalizeBrainstormPresentationInput(value) {
226
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
227
+ throw new Error('presentation JSON 必须是对象。');
228
+ }
229
+ if (value.brainstormPresentation && typeof value.brainstormPresentation === 'object' && !Array.isArray(value.brainstormPresentation)) {
230
+ return value.brainstormPresentation;
231
+ }
232
+ return value;
233
+ }