@openprd/cli 0.1.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 (154) hide show
  1. package/.openprd/README.md +82 -0
  2. package/.openprd/benchmarks/evidence/milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code.md +14 -0
  3. package/.openprd/benchmarks/evidence/nolanlawson-com-using-ai-to-write-better-code-more-slowly.md +14 -0
  4. package/.openprd/benchmarks/index.md +37 -0
  5. package/.openprd/benchmarks/sources.yaml +56 -0
  6. package/.openprd/config.yaml +50 -0
  7. package/.openprd/discovery/config.json +21 -0
  8. package/.openprd/engagements/active/flows.md +30 -0
  9. package/.openprd/engagements/active/handoff.md +9 -0
  10. package/.openprd/engagements/active/intake.md +15 -0
  11. package/.openprd/engagements/active/prd.md +161 -0
  12. package/.openprd/engagements/active/review.html +61 -0
  13. package/.openprd/engagements/active/roles.md +21 -0
  14. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +23 -0
  15. package/.openprd/exports/.gitkeep +0 -0
  16. package/.openprd/knowledge/index.json +7 -0
  17. package/.openprd/quality/config.json +229 -0
  18. package/.openprd/reviews/v0001.html +1256 -0
  19. package/.openprd/schema/diagram-architecture.schema.yaml +49 -0
  20. package/.openprd/schema/diagram-product-flow.schema.yaml +52 -0
  21. package/.openprd/schema/prd.schema.yaml +121 -0
  22. package/.openprd/sessions/.gitkeep +0 -0
  23. package/.openprd/standards/config.json +88 -0
  24. package/.openprd/standards/file-manual-template.md +28 -0
  25. package/.openprd/standards/folder-readme-template.md +28 -0
  26. package/.openprd/state/.gitkeep +0 -0
  27. package/.openprd/state/changes.json +12 -0
  28. package/.openprd/state/current.json +169 -0
  29. package/.openprd/state/version-index.json +15 -0
  30. package/.openprd/state/versions/.gitkeep +0 -0
  31. package/.openprd/state/versions/v0001.json +121 -0
  32. package/.openprd/state/versions/v0001.md +161 -0
  33. package/.openprd/templates/agent/intake.md +6 -0
  34. package/.openprd/templates/agent/prd.md +21 -0
  35. package/.openprd/templates/b2b/intake.md +6 -0
  36. package/.openprd/templates/b2b/prd.md +24 -0
  37. package/.openprd/templates/base/intake.md +18 -0
  38. package/.openprd/templates/base/prd.md +67 -0
  39. package/.openprd/templates/company/README.md +10 -0
  40. package/.openprd/templates/consumer/intake.md +6 -0
  41. package/.openprd/templates/consumer/prd.md +19 -0
  42. package/.openprd/templates/diagram/architecture.contract.json +53 -0
  43. package/.openprd/templates/diagram/product-flow.contract.json +76 -0
  44. package/.openprd/templates/industry/README.md +16 -0
  45. package/.openprd/templates/manifest.yaml +27 -0
  46. package/.openprd/templates/project/README.md +14 -0
  47. package/.openprd/templates/session/README.md +14 -0
  48. package/AGENTS.md +44 -0
  49. package/CONTRIBUTING.md +30 -0
  50. package/LICENSE +21 -0
  51. package/README.md +727 -0
  52. package/README_CN.md +583 -0
  53. package/SECURITY.md +23 -0
  54. package/bin/openprd.js +5 -0
  55. package/docs/assets/openprd-capability-overview-en.png +0 -0
  56. package/docs/assets/openprd-capability-overview-zh.png +0 -0
  57. package/docs/assets/openprd-learning-html.png +0 -0
  58. package/docs/assets/openprd-quality-html.png +0 -0
  59. package/docs/assets/openprd-review-html.png +0 -0
  60. package/docs/assets/openprd-scenario-overview.png +0 -0
  61. package/docs/assets/openprd-scenario-overview.svg +114 -0
  62. package/docs/assets/openprd-self-evolving-mechanisms-en.png +0 -0
  63. package/docs/assets/openprd-self-evolving-mechanisms-zh.png +0 -0
  64. package/docs/assets/openprd-visual-compare-case-study-en.png +0 -0
  65. package/docs/assets/openprd-visual-compare-case-study-zh.png +0 -0
  66. package/package.json +59 -0
  67. package/scripts/openprd-dev-check.mjs +5 -0
  68. package/scripts/openprd-review-presentation.mjs +82 -0
  69. package/skills/openprd-benchmark-router/SKILL.md +92 -0
  70. package/skills/openprd-benchmark-router/agents/openai.yaml +4 -0
  71. package/skills/openprd-benchmark-router/references/benchmark-sources.md +74 -0
  72. package/skills/openprd-benchmark-router/references/evaluation-lenses.md +66 -0
  73. package/skills/openprd-benchmark-router/references/source-policy.md +35 -0
  74. package/skills/openprd-diagram-review/SKILL.md +91 -0
  75. package/skills/openprd-diagram-review/agents/openai.yaml +4 -0
  76. package/skills/openprd-diagram-review/examples/architecture-zh.md +8 -0
  77. package/skills/openprd-diagram-review/examples/product-flow-zh.md +7 -0
  78. package/skills/openprd-diagram-review/references/cocoon-patterns.md +17 -0
  79. package/skills/openprd-diagram-review/references/diagram-contracts.md +126 -0
  80. package/skills/openprd-diagram-review/references/review-checklist.md +10 -0
  81. package/skills/openprd-discovery-loop/SKILL.md +196 -0
  82. package/skills/openprd-discovery-loop/agents/openai.yaml +3 -0
  83. package/skills/openprd-harness/SKILL.md +179 -0
  84. package/skills/openprd-harness/agents/openai.yaml +4 -0
  85. package/skills/openprd-harness/examples/full-workflow-zh.md +9 -0
  86. package/skills/openprd-harness/references/command-map.md +71 -0
  87. package/skills/openprd-harness/references/examples.md +26 -0
  88. package/skills/openprd-harness/references/usage-guide.md +335 -0
  89. package/skills/openprd-harness/references/workflow-gates.md +51 -0
  90. package/skills/openprd-learning-review/SKILL.md +75 -0
  91. package/skills/openprd-learning-review/agents/openai.yaml +4 -0
  92. package/skills/openprd-learning-review/references/content-contract.md +125 -0
  93. package/skills/openprd-learning-review/references/ebook-reader.md +46 -0
  94. package/skills/openprd-learning-review/references/evidence-manifest.md +55 -0
  95. package/skills/openprd-learning-review/references/genre-library.md +43 -0
  96. package/skills/openprd-learning-review/references/prompt-engineering.md +71 -0
  97. package/skills/openprd-learning-review/references/quality-rubric.md +28 -0
  98. package/skills/openprd-learning-review/references/retrieval-worked-example.md +40 -0
  99. package/skills/openprd-learning-review/references/style-packs/xianxia-cultivation.prompt.md +67 -0
  100. package/skills/openprd-quality/SKILL.md +101 -0
  101. package/skills/openprd-requirement-intake/SKILL.md +76 -0
  102. package/skills/openprd-requirement-intake/agents/openai.yaml +4 -0
  103. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +105 -0
  104. package/skills/openprd-requirement-intake/references/routing-rubric.md +64 -0
  105. package/skills/openprd-router/SKILL.md +40 -0
  106. package/skills/openprd-shared/SKILL.md +142 -0
  107. package/skills/openprd-shared/agents/openai.yaml +4 -0
  108. package/skills/openprd-shared/references/language-and-review.md +50 -0
  109. package/skills/openprd-shared/references/operating-rules.md +65 -0
  110. package/skills/openprd-shared/references/skill-architecture.md +70 -0
  111. package/skills/openprd-standards/SKILL.md +79 -0
  112. package/skills/openprd-standards/agents/openai.yaml +4 -0
  113. package/src/agent-integration.js +1717 -0
  114. package/src/benchmark.js +873 -0
  115. package/src/cli/args.js +460 -0
  116. package/src/cli/print.js +1423 -0
  117. package/src/codex-hook-runner-template.mjs +2422 -0
  118. package/src/dev-standards.js +372 -0
  119. package/src/diagram-core.js +1047 -0
  120. package/src/diagram-workspace.js +262 -0
  121. package/src/discovery.js +709 -0
  122. package/src/fleet.js +531 -0
  123. package/src/fs-utils.js +83 -0
  124. package/src/growth.js +545 -0
  125. package/src/html-artifacts.js +3803 -0
  126. package/src/knowledge.js +668 -0
  127. package/src/language-policy.js +142 -0
  128. package/src/learning-review.js +1655 -0
  129. package/src/loop.js +1290 -0
  130. package/src/openprd.js +1136 -0
  131. package/src/openspec/change-lifecycle.js +359 -0
  132. package/src/openspec/change-validate.js +248 -0
  133. package/src/openspec/constants.js +12 -0
  134. package/src/openspec/execute.js +300 -0
  135. package/src/openspec/generate.js +692 -0
  136. package/src/openspec/paths.js +111 -0
  137. package/src/openspec/tasks.js +352 -0
  138. package/src/prd-core.js +656 -0
  139. package/src/quality-html-artifact.js +1414 -0
  140. package/src/quality-learning.js +658 -0
  141. package/src/quality.js +1262 -0
  142. package/src/review-presentation.js +240 -0
  143. package/src/run-harness.js +1470 -0
  144. package/src/self-update.js +329 -0
  145. package/src/session-binding.js +140 -0
  146. package/src/source-inventory.js +224 -0
  147. package/src/standards.js +914 -0
  148. package/src/time.js +33 -0
  149. package/src/visual-compare.js +216 -0
  150. package/src/work-unit-migration.js +232 -0
  151. package/src/work-unit.js +88 -0
  152. package/src/workspace-core.js +1706 -0
  153. package/src/workspace-registry.js +162 -0
  154. package/src/workspace-workflow.js +1797 -0
@@ -0,0 +1,262 @@
1
+ import path from 'node:path';
2
+ import { spawn } from 'node:child_process';
3
+ import { buildPrdSnapshot, formatVersionId } from './prd-core.js';
4
+ import { buildDiagramArtifact, renderDiagramArtifactFromModel, renderDiagramMermaidFromModel, validateDiagramContract, validateDiagramLanguage } from './diagram-core.js';
5
+ import { exists, readJson, writeJson, writeText } from './fs-utils.js';
6
+ import { appendDecision, appendProgress, appendWorkflowEvent, loadLatestVersionSnapshot, loadWorkspace, readVersionIndex, resolveActiveTemplatePack, resolveCurrentProductType } from './workspace-core.js';
7
+
8
+ async function openInBrowser(filePath) {
9
+ const platform = process.platform;
10
+ const command = platform === 'darwin'
11
+ ? 'open'
12
+ : platform === 'win32'
13
+ ? 'cmd'
14
+ : 'xdg-open';
15
+ const args = platform === 'win32'
16
+ ? ['/c', 'start', '', filePath]
17
+ : [filePath];
18
+
19
+ const child = spawn(command, args, {
20
+ detached: true,
21
+ stdio: 'ignore',
22
+ });
23
+ child.unref();
24
+ }
25
+
26
+ function resolveDiagramPaths(ws, type = 'architecture') {
27
+ if (type === 'product-flow') {
28
+ return {
29
+ htmlPath: ws.paths.activeProductFlowDiagramHtml,
30
+ jsonPath: ws.paths.activeProductFlowDiagramJson,
31
+ mermaidPath: ws.paths.activeProductFlowDiagramMermaid,
32
+ label: 'product flow',
33
+ };
34
+ }
35
+
36
+ return {
37
+ htmlPath: ws.paths.activeArchitectureDiagramHtml,
38
+ jsonPath: ws.paths.activeArchitectureDiagramJson,
39
+ mermaidPath: ws.paths.activeArchitectureDiagramMermaid,
40
+ label: 'architecture',
41
+ };
42
+ }
43
+
44
+ async function readDiagramModel(filePath) {
45
+ if (!(await exists(filePath))) {
46
+ return null;
47
+ }
48
+ return readJson(filePath).catch(() => null);
49
+ }
50
+
51
+ function normalizeDiagramReviewStatus(status) {
52
+ return ['pending-confirmation', 'confirmed', 'needs-revision'].includes(status)
53
+ ? status
54
+ : 'pending-confirmation';
55
+ }
56
+
57
+ function assessDiagramComplexity(snapshot) {
58
+ const sections = snapshot?.sections ?? {};
59
+ const flowScore = [
60
+ (sections.scenarios?.primaryFlows ?? []).length > 0,
61
+ (sections.scenarios?.edgeCases ?? []).length > 0,
62
+ (sections.scenarios?.failureModes ?? []).length > 0,
63
+ (sections.goals?.acceptanceGoals ?? []).length > 0,
64
+ ].filter(Boolean).length;
65
+
66
+ const architectureScore = [
67
+ (sections.constraints?.dependencies ?? []).length > 0,
68
+ (sections.constraints?.technical ?? []).length > 0,
69
+ (sections.constraints?.compliance ?? []).length > 0,
70
+ (sections.requirements?.nonFunctional ?? []).length > 0,
71
+ snapshot?.productType === 'agent',
72
+ snapshot?.productType === 'b2b',
73
+ ].filter(Boolean).length;
74
+
75
+ if (flowScore === 0 && architectureScore === 0) {
76
+ return { needed: false, preferredType: 'architecture', reason: null };
77
+ }
78
+
79
+ const preferredType = flowScore >= architectureScore ? 'product-flow' : 'architecture';
80
+ return {
81
+ needed: Math.max(flowScore, architectureScore) >= 2,
82
+ preferredType,
83
+ reason: preferredType === 'product-flow'
84
+ ? 'The user journey and failure/decision paths would benefit from a visual confirmation.'
85
+ : 'The system shape, boundaries, or dependencies would benefit from a visual confirmation.',
86
+ };
87
+ }
88
+
89
+ async function getDiagramReviewState(ws, snapshot) {
90
+ const architecture = await readDiagramModel(ws.paths.activeArchitectureDiagramJson);
91
+ const productFlow = await readDiagramModel(ws.paths.activeProductFlowDiagramJson);
92
+ const complexity = assessDiagramComplexity(snapshot);
93
+ const preferred = complexity.preferredType;
94
+ const target = preferred === 'product-flow' ? productFlow : architecture;
95
+ const targetStatus = normalizeDiagramReviewStatus(target?.metadata?.reviewStatus);
96
+ const targetExists = Boolean(target);
97
+
98
+ const shouldGateFreeze = complexity.needed && (!targetExists || targetStatus !== 'confirmed');
99
+ let reason = complexity.reason;
100
+ if (shouldGateFreeze && targetExists && targetStatus === 'needs-revision') {
101
+ reason = `The ${preferred} diagram exists but is marked needs-revision and should be updated before freeze.`;
102
+ } else if (shouldGateFreeze && targetExists && targetStatus === 'pending-confirmation') {
103
+ reason = `The ${preferred} diagram exists but is still pending confirmation before freeze.`;
104
+ } else if (shouldGateFreeze && !targetExists) {
105
+ reason = `A ${preferred} diagram has not been generated yet and should be reviewed before freeze.`;
106
+ }
107
+
108
+ return {
109
+ preferredType: preferred,
110
+ needed: complexity.needed,
111
+ shouldGateFreeze,
112
+ reason,
113
+ architecture: {
114
+ exists: Boolean(architecture),
115
+ reviewStatus: normalizeDiagramReviewStatus(architecture?.metadata?.reviewStatus),
116
+ },
117
+ productFlow: {
118
+ exists: Boolean(productFlow),
119
+ reviewStatus: normalizeDiagramReviewStatus(productFlow?.metadata?.reviewStatus),
120
+ },
121
+ };
122
+ }
123
+
124
+ async function diagramWorkspace(projectRoot, options = {}) {
125
+ const ws = await loadWorkspace(projectRoot);
126
+ if (!(await exists(ws.workspaceRoot))) {
127
+ throw new Error(`Missing workspace: ${ws.workspaceRoot}`);
128
+ }
129
+
130
+ const type = options.type ?? 'architecture';
131
+ if (!['architecture', 'product-flow'].includes(type)) {
132
+ throw new Error(`Unsupported diagram type: ${type}`);
133
+ }
134
+ if (options.mark && !['pending-confirmation', 'confirmed', 'needs-revision'].includes(options.mark)) {
135
+ throw new Error(`Unsupported diagram review status: ${options.mark}`);
136
+ }
137
+
138
+ const { htmlPath, jsonPath, mermaidPath, label } = resolveDiagramPaths(ws, type);
139
+ if (options.mark && !options.input && await exists(jsonPath)) {
140
+ const model = await readJson(jsonPath);
141
+ model.metadata = {
142
+ ...(model.metadata ?? {}),
143
+ reviewStatus: options.mark,
144
+ };
145
+ const languageValidation = validateDiagramLanguage(model);
146
+ if (!languageValidation.valid) {
147
+ throw new Error(`Invalid ${type} diagram language:\n- ${languageValidation.errors.join('\n- ')}`);
148
+ }
149
+ const html = renderDiagramArtifactFromModel(type, model);
150
+ const mermaid = renderDiagramMermaidFromModel(type, model);
151
+ await writeJson(jsonPath, model);
152
+ await writeText(htmlPath, html);
153
+ await writeText(mermaidPath, `${mermaid}\n`);
154
+ await appendWorkflowEvent(ws, 'diagram_marked', {
155
+ diagramType: type,
156
+ reviewStatus: options.mark,
157
+ htmlPath,
158
+ mermaidPath,
159
+ });
160
+ await appendDecision(ws, [
161
+ `Marked ${label} diagram as ${options.mark}.`,
162
+ ]);
163
+ if (options.open) {
164
+ await openInBrowser(htmlPath);
165
+ }
166
+ return {
167
+ ws,
168
+ snapshot: null,
169
+ type,
170
+ model,
171
+ inputPath: null,
172
+ htmlPath,
173
+ jsonPath,
174
+ mermaidPath,
175
+ opened: Boolean(options.open),
176
+ marked: options.mark,
177
+ };
178
+ }
179
+
180
+ const versionIndex = await readVersionIndex(ws);
181
+ const latestVersion = versionIndex.length > 0 ? await loadLatestVersionSnapshot(ws) : null;
182
+ const snapshot = latestVersion?.snapshot ?? buildPrdSnapshot(ws, {
183
+ ...ws.data.currentState,
184
+ versionNumber: ws.data.currentState?.prdVersion ?? (versionIndex.at(-1)?.versionNumber ?? 0),
185
+ versionId: ws.data.currentState?.prdVersion > 0
186
+ ? formatVersionId(ws.data.currentState.prdVersion)
187
+ : (versionIndex.at(-1)?.versionId ?? 'v0000'),
188
+ productType: resolveCurrentProductType(ws),
189
+ templatePack: resolveActiveTemplatePack(ws),
190
+ status: ws.data.currentState?.status ?? 'draft',
191
+ });
192
+
193
+ const contract = options.input
194
+ ? await readJson(path.resolve(options.input))
195
+ : null;
196
+ if (contract) {
197
+ const schema = type === 'product-flow'
198
+ ? ws.data.diagramProductFlowSchema
199
+ : ws.data.diagramArchitectureSchema;
200
+ const validation = validateDiagramContract(contract, schema);
201
+ if (!validation.valid) {
202
+ throw new Error(`Invalid ${type} diagram contract:\n- ${validation.errors.join('\n- ')}`);
203
+ }
204
+ }
205
+ const artifact = buildDiagramArtifact(snapshot, { type, contract });
206
+ if (options.mark) {
207
+ artifact.model.metadata = {
208
+ ...(artifact.model.metadata ?? {}),
209
+ reviewStatus: options.mark,
210
+ };
211
+ artifact.html = renderDiagramArtifactFromModel(type, artifact.model);
212
+ }
213
+ const languageValidation = validateDiagramLanguage(artifact.model);
214
+ if (!languageValidation.valid) {
215
+ throw new Error(`Invalid ${type} diagram language:\n- ${languageValidation.errors.join('\n- ')}`);
216
+ }
217
+
218
+ const mermaid = renderDiagramMermaidFromModel(type, artifact.model);
219
+ await writeJson(jsonPath, artifact.model);
220
+ await writeText(htmlPath, artifact.html);
221
+ await writeText(mermaidPath, `${mermaid}\n`);
222
+ await appendWorkflowEvent(ws, 'diagram_generated', {
223
+ versionId: snapshot.versionId,
224
+ productType: snapshot.productType,
225
+ diagramType: type,
226
+ inputPath: options.input ? path.resolve(options.input) : null,
227
+ htmlPath,
228
+ mermaidPath,
229
+ });
230
+ await appendProgress(ws, [
231
+ `已为 ${snapshot.title} 生成 ${label} 图表产物。`,
232
+ `HTML: ${htmlPath}`,
233
+ `Mermaid: ${mermaidPath}`,
234
+ ...(options.input ? [`Input contract: ${path.resolve(options.input)}`] : []),
235
+ ]);
236
+ await appendDecision(ws, [
237
+ `已为 ${snapshot.title} 创建 ${label} 图表评审产物。`,
238
+ type === 'product-flow'
239
+ ? '请在需求定稿前使用该产物确认步骤、决策点和恢复路径。'
240
+ : '请在需求定稿前使用该产物确认组件、边界和缺失系统。',
241
+ ]);
242
+
243
+ if (options.open) {
244
+ await openInBrowser(htmlPath);
245
+ }
246
+
247
+ return {
248
+ ws,
249
+ snapshot,
250
+ type,
251
+ model: artifact.model,
252
+ inputPath: options.input ? path.resolve(options.input) : null,
253
+ htmlPath,
254
+ jsonPath,
255
+ mermaidPath,
256
+ opened: Boolean(options.open),
257
+ marked: options.mark ?? null,
258
+ };
259
+ }
260
+
261
+
262
+ export { diagramWorkspace, getDiagramReviewState };