@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
package/src/openprd.js ADDED
@@ -0,0 +1,1136 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { analyzePrdSnapshot, buildPrdSnapshot, formatVersionId } from './prd-core.js';
4
+ import { validateOpenSpecChangeWorkspace } from './openspec/change-validate.js';
5
+ import { generateOpenSpecChangeWorkspace as writeOpenSpecChangeWorkspace } from './openspec/generate.js';
6
+ import { advanceOpenSpecTaskWorkspace, listOpenSpecTaskWorkspace, verifyOpenSpecTaskWorkspace } from './openspec/execute.js';
7
+ import { activateOpenPrdChangeWorkspace, applyOpenPrdChangeWorkspace, archiveOpenPrdChangeWorkspace, closeOpenPrdChangeWorkspace, listAcceptedSpecsWorkspace, listOpenPrdChangesWorkspace } from './openspec/change-lifecycle.js';
8
+ import { checkStandardsWorkspace, classifyExternalReferenceWorkspace, initStandardsWorkspace } from './standards.js';
9
+ import { doctorOpenPrdAgentIntegration, setupOpenPrdAgentIntegration, updateOpenPrdAgentIntegration } from './agent-integration.js';
10
+ import { finishLoopWorkspace, initLoopWorkspace, nextLoopWorkspace, planLoopWorkspace, promptLoopWorkspace, runLoopWorkspace, statusLoopWorkspace, verifyLoopWorkspace } from './loop.js';
11
+ import { timestamp } from './time.js';
12
+ import { parseCommandArgs, usage } from './cli/args.js';
13
+ import { printAcceptedSpecsResult, printAgentIntegrationResult, printBenchmarkResult, printCaptureResult, printClarifyResult, printClassifyResult, printDevelopmentStandardsResult, printDiagramResult, printDiffResult, printDoctorResult, printFleetResult, printFreezeResult, printGrowthResult, printHandoffResult, printHistoryResult, printInitResult, printInterviewResult, printLearningResult, printLoopResult, printNextResult, printOpenPrdChangeActionResult, printOpenPrdChangesResult, printOpenSpecChangeValidationResult, printOpenSpecDiscoveryResult, printOpenSpecGenerateResult, printOpenSpecTaskResult, printPlaygroundResult, printQualityResult, printReviewResult, printRunResult, printSelfUpdateResult, printStandardsResult, printStatus, printSynthesizeResult, printUpgradeResult, printValidation, printVisualCompareResult } from './cli/print.js';
14
+ import { cjoin, exists, writeJson, writeText, writeYaml } from './fs-utils.js';
15
+ import { diagramWorkspace } from './diagram-workspace.js';
16
+ import { createOpenSpecDiscoveryWorkspace } from './discovery.js';
17
+ import { createFleetWorkspace } from './fleet.js';
18
+ import { selfUpdateWorkspace, upgradeWorkspace } from './self-update.js';
19
+ import { backfillWorkUnitsWorkspace } from './work-unit-migration.js';
20
+ import { generateLearningReviewWorkspace, setLearningReviewModeWorkspace } from './learning-review.js';
21
+ import { addBenchmarkWorkspace, approveBenchmarkWorkspace, benchmarkWorkspace, listBenchmarkWorkspace, verifyBenchmarkWorkspace } from './benchmark.js';
22
+ import { initQualityWorkspace, qualityWorkspace, verifyQualityWorkspace, learnQualityWorkspace } from './quality.js';
23
+ import { createRunWorkspace } from './run-harness.js';
24
+ import { checkDevelopmentStandardsWorkspace } from './dev-standards.js';
25
+ import { applyGrowthCandidateWorkspace, checkGrowthWorkspace, initGrowthWorkspace, rejectGrowthCandidateWorkspace, reviewGrowthWorkspace } from './growth.js';
26
+ import { buildReviewPresentationTemplatePayload, reviewPresentationWorkspace } from './review-presentation.js';
27
+ import { syncSessionBindingFromChange } from './session-binding.js';
28
+ import { visualCompareWorkspace } from './visual-compare.js';
29
+ import { captureWorkspace, clarifyWorkspace, classifyWorkspace, computeWorkspaceGuidance, diffWorkspace, historyWorkspace, interviewWorkspace, nextWorkspace, playgroundWorkspace, reviewWorkspace, synthesizeWorkspace } from './workspace-workflow.js';
30
+ import { appendDecision, appendProgress, appendVerification, appendWorkflowEvent, buildWorkflowTaskGraph, computeWorkspaceDigest, CORE_TEMPLATE_FILES, ensureWorkspaceSkeleton, isSupportedProductType, loadLatestVersionSnapshot, loadWorkspace, migrateWorkspaceSkeleton, normalizeVersionId, readVersionIndex, resolveActiveTemplatePack, resolveCurrentProductType, validateWorkspace } from './workspace-core.js';
31
+
32
+ async function initWorkspace(projectRoot, options) {
33
+ const ws = await ensureWorkspaceSkeleton(projectRoot, options);
34
+ const workspace = await loadWorkspace(projectRoot);
35
+ const standards = await initStandardsWorkspace(projectRoot, { force: Boolean(options.force) });
36
+ const quality = await initQualityWorkspace(projectRoot, { force: Boolean(options.force) });
37
+ const growth = await initGrowthWorkspace(projectRoot);
38
+ const agentIntegration = await setupOpenPrdAgentIntegration(projectRoot, {
39
+ tools: options.tools ?? 'all',
40
+ force: Boolean(options.force),
41
+ action: 'init',
42
+ enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
43
+ codexHome: options.codexHome,
44
+ openprdHome: options.openprdHome,
45
+ hookProfile: options.hookProfile,
46
+ });
47
+ const config = workspace.data.config ?? {};
48
+ if (options.templatePack) {
49
+ config.activeTemplatePack = options.templatePack;
50
+ }
51
+ if (!config.activeTemplatePack) {
52
+ config.activeTemplatePack = 'base';
53
+ }
54
+ config.learningReview = {
55
+ enabled: config.learningReview?.enabled !== false,
56
+ autoOpen: config.learningReview?.autoOpen !== false,
57
+ defaultGenre: config.learningReview?.defaultGenre ?? 'internet-product',
58
+ sourceScope: config.learningReview?.sourceScope ?? 'workspace',
59
+ ...config.learningReview,
60
+ };
61
+ config.quality = {
62
+ enabled: config.quality?.enabled !== false,
63
+ command: config.quality?.command ?? 'openprd quality . --verify',
64
+ reportFormat: config.quality?.reportFormat ?? 'html',
65
+ ...config.quality,
66
+ };
67
+ await writeYaml(workspace.paths.config, config);
68
+
69
+ const currentState = {
70
+ version: 1,
71
+ status: 'initialized',
72
+ activeEngagement: 'active',
73
+ prdVersion: 0,
74
+ productType: isSupportedProductType(config.activeTemplatePack) ? config.activeTemplatePack : null,
75
+ templatePack: config.activeTemplatePack,
76
+ captureMeta: {},
77
+ projectRoot,
78
+ workspaceRoot: workspace.workspaceRoot,
79
+ createdAt: timestamp(),
80
+ };
81
+ await writeJson(workspace.paths.currentState, currentState);
82
+ await writeJson(workspace.paths.taskGraph, buildWorkflowTaskGraph(currentState));
83
+ await appendWorkflowEvent(workspace, 'initialized', {
84
+ templatePack: currentState.templatePack,
85
+ projectRoot,
86
+ });
87
+ await appendProgress(workspace, [
88
+ `已初始化工作区: ${workspace.workspaceRoot}。`,
89
+ `模板包: ${currentState.templatePack}。`,
90
+ ]);
91
+
92
+ return { ws: workspace, created: ws.created, currentState, standards, quality, growth, agentIntegration };
93
+ }
94
+
95
+ async function setupAgentIntegrationWorkspace(projectRoot, options = {}) {
96
+ if (!(await exists(cjoin(projectRoot, '.openprd')))) {
97
+ const initResult = await initWorkspace(projectRoot, options);
98
+ return {
99
+ ...initResult.agentIntegration,
100
+ initialized: true,
101
+ standards: initResult.standards,
102
+ quality: initResult.quality,
103
+ init: {
104
+ workspaceRoot: initResult.ws.workspaceRoot,
105
+ created: initResult.created,
106
+ templatePack: initResult.currentState.templatePack,
107
+ },
108
+ growth: initResult.growth,
109
+ };
110
+ }
111
+
112
+ const migration = await migrateWorkspaceSkeleton(projectRoot, { recordEvent: true });
113
+ const standards = await initStandardsWorkspace(projectRoot, { force: Boolean(options.force) });
114
+ const quality = await initQualityWorkspace(projectRoot, { force: false });
115
+ const growth = await initGrowthWorkspace(projectRoot);
116
+ const agentIntegration = await setupOpenPrdAgentIntegration(projectRoot, {
117
+ tools: options.tools ?? 'all',
118
+ force: Boolean(options.force),
119
+ action: 'setup',
120
+ enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
121
+ codexHome: options.codexHome,
122
+ openprdHome: options.openprdHome,
123
+ hookProfile: options.hookProfile,
124
+ });
125
+ return { ...agentIntegration, initialized: false, migration, standards, quality, growth };
126
+ }
127
+
128
+ async function updateAgentIntegrationWorkspace(projectRoot, options = {}) {
129
+ const migration = await migrateWorkspaceSkeleton(projectRoot, { recordEvent: true });
130
+ const standards = await initStandardsWorkspace(projectRoot, { force: false });
131
+ const quality = await initQualityWorkspace(projectRoot, { force: false });
132
+ const growth = await initGrowthWorkspace(projectRoot);
133
+ const agentIntegration = await updateOpenPrdAgentIntegration(projectRoot, {
134
+ tools: options.tools ?? 'all',
135
+ force: Boolean(options.force),
136
+ enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
137
+ codexHome: options.codexHome,
138
+ openprdHome: options.openprdHome,
139
+ hookProfile: options.hookProfile,
140
+ });
141
+ return { ...agentIntegration, migration, standards, quality, growth };
142
+ }
143
+
144
+ async function doctorWorkspace(projectRoot, options = {}) {
145
+ const agentIntegration = await doctorOpenPrdAgentIntegration(projectRoot, {
146
+ tools: options.tools ?? 'all',
147
+ enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
148
+ codexHome: options.codexHome,
149
+ hookProfile: options.hookProfile,
150
+ });
151
+ const standards = await checkStandardsWorkspace(projectRoot).catch((error) => ({
152
+ ok: false,
153
+ errors: [error instanceof Error ? error.message : String(error)],
154
+ warnings: [],
155
+ checks: [],
156
+ docsRoot: path.join('docs', 'basic'),
157
+ }));
158
+ const validation = await validateWorkspace(projectRoot)
159
+ .then(({ report }) => report)
160
+ .catch((error) => ({
161
+ valid: false,
162
+ errors: [error instanceof Error ? error.message : String(error)],
163
+ warnings: [],
164
+ checks: [],
165
+ }));
166
+
167
+ return {
168
+ ok: agentIntegration.ok && standards.ok && validation.valid,
169
+ action: 'doctor',
170
+ projectRoot,
171
+ tools: agentIntegration.tools,
172
+ agentIntegration,
173
+ standards,
174
+ validation,
175
+ errors: [
176
+ ...agentIntegration.errors,
177
+ ...(standards.errors ?? []).map((error) => `standards: ${error}`),
178
+ ...(validation.errors ?? []).map((error) => `validate: ${error}`),
179
+ ],
180
+ };
181
+ }
182
+
183
+ async function readCliPackageInfo() {
184
+ const raw = await fs.readFile(new URL('../package.json', import.meta.url), 'utf8');
185
+ const packageJson = JSON.parse(raw);
186
+ return {
187
+ name: packageJson.name ?? '@openprd/cli',
188
+ version: packageJson.version ?? '0.0.0',
189
+ };
190
+ }
191
+
192
+ const {
193
+ openspecDiscoveryWorkspace,
194
+ resumeOpenSpecDiscoveryWorkspace,
195
+ verifyOpenSpecDiscoveryWorkspace,
196
+ } = createOpenSpecDiscoveryWorkspace({
197
+ appendProgress,
198
+ appendWorkflowEvent,
199
+ checkStandardsWorkspace,
200
+ loadLatestVersionSnapshot,
201
+ loadWorkspace,
202
+ readVersionIndex,
203
+ resolveActiveTemplatePack,
204
+ resolveCurrentProductType,
205
+ validateOpenSpecChangeWorkspace,
206
+ });
207
+
208
+ const runWorkspace = createRunWorkspace({
209
+ checkStandardsWorkspace,
210
+ listOpenPrdChangesWorkspace,
211
+ listOpenSpecTaskWorkspace,
212
+ nextWorkspace,
213
+ resumeOpenSpecDiscoveryWorkspace,
214
+ validateOpenSpecChangeWorkspace,
215
+ validateWorkspace,
216
+ verifyOpenSpecDiscoveryWorkspace,
217
+ verifyQualityWorkspace,
218
+ });
219
+
220
+ const fleetWorkspace = createFleetWorkspace({ setupAgentIntegrationWorkspace, updateAgentIntegrationWorkspace, doctorWorkspace, backfillWorkUnitsWorkspace });
221
+
222
+ async function freezeWorkspace(projectRoot) {
223
+ const validation = await validateWorkspace(projectRoot);
224
+ const { report } = validation;
225
+ if (!report.valid) {
226
+ await appendWorkflowEvent(validation.ws, 'freeze_failed', {
227
+ errors: report.errors,
228
+ warnings: report.warnings,
229
+ });
230
+ await appendVerification(validation.ws, [
231
+ 'Freeze 验证失败。',
232
+ ...report.errors.map((error) => `错误: ${error}`),
233
+ ...report.warnings.map((warning) => `警告: ${warning}`),
234
+ ]);
235
+ return { ok: false, report, ws: validation.ws };
236
+ }
237
+
238
+ let ws = validation.ws;
239
+ let latest = await loadLatestVersionSnapshot(ws);
240
+ if (!latest) {
241
+ const synthesized = await synthesizeWorkspace(projectRoot, {});
242
+ ws = synthesized.ws;
243
+ latest = { indexEntry: synthesized.indexEntry, snapshot: synthesized.snapshot };
244
+ }
245
+
246
+ const guidance = await computeWorkspaceGuidance(ws, { questionLimit: 5 });
247
+ if (guidance.nextAction !== 'freeze' && (ws.data.currentState?.status ?? null) !== 'frozen') {
248
+ const gateReport = {
249
+ ...report,
250
+ valid: false,
251
+ errors: [
252
+ ...(report.errors ?? []),
253
+ `Freeze blocked by ${guidance.gates.currentGate}: ${guidance.reason}`,
254
+ ],
255
+ };
256
+ await appendWorkflowEvent(ws, 'freeze_failed', {
257
+ errors: gateReport.errors,
258
+ warnings: gateReport.warnings,
259
+ gate: guidance.gates.currentGate,
260
+ nextAction: guidance.nextAction,
261
+ });
262
+ await appendVerification(ws, [
263
+ 'Freeze 门禁失败。',
264
+ `当前门禁: ${guidance.gates.currentGate}`,
265
+ `原因: ${guidance.reason}`,
266
+ `建议命令: ${guidance.suggestedCommand}`,
267
+ ]);
268
+ return { ok: false, report: gateReport, ws, guidance };
269
+ }
270
+
271
+ const digest = await computeWorkspaceDigest(ws);
272
+ const snapshot = {
273
+ version: 1,
274
+ frozenAt: timestamp(),
275
+ projectRoot: ws.projectRoot,
276
+ workspaceRoot: ws.workspaceRoot,
277
+ schema: ws.data.schema?.name ?? null,
278
+ templatePack: resolveActiveTemplatePack(ws),
279
+ productTypes: ws.data.config?.supportedProductTypes ?? [],
280
+ prdVersion: latest.snapshot.versionNumber,
281
+ latestVersionId: latest.snapshot.versionId,
282
+ digest,
283
+ status: 'frozen',
284
+ };
285
+ await writeJson(ws.paths.freezeState, snapshot);
286
+ await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(null));
287
+
288
+ const currentState = {
289
+ ...(ws.data.currentState ?? {}),
290
+ status: 'frozen',
291
+ prdVersion: latest.snapshot.versionNumber,
292
+ latestVersionId: latest.snapshot.versionId,
293
+ templatePack: resolveActiveTemplatePack(ws),
294
+ frozenAt: snapshot.frozenAt,
295
+ digest,
296
+ };
297
+ await writeJson(ws.paths.currentState, currentState);
298
+ await appendWorkflowEvent(ws, 'frozen', {
299
+ versionId: snapshot.latestVersionId,
300
+ digest,
301
+ });
302
+ await appendVerification(ws, [
303
+ 'Freeze 验证通过。',
304
+ `版本: ${snapshot.latestVersionId}`,
305
+ `Digest: ${digest}`,
306
+ `PRD 版本: ${snapshot.prdVersion}`,
307
+ ]);
308
+ await appendProgress(ws, [
309
+ `已 freeze PRD 版本 ${snapshot.latestVersionId}。`,
310
+ `Digest: ${digest}`,
311
+ ]);
312
+ await appendDecision(ws, [
313
+ `已 freeze 版本 ${snapshot.latestVersionId}。`,
314
+ `已准备好交接给 ${resolveActiveTemplatePack(ws) === 'base' ? '下游执行方' : '执行系统'}。`,
315
+ ]);
316
+ await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(currentState));
317
+
318
+ return { ok: true, ws, report, snapshot, latest };
319
+ }
320
+
321
+ async function handoffWorkspace(projectRoot, target) {
322
+ const freeze = await freezeWorkspace(projectRoot);
323
+ if (!freeze.ok) {
324
+ return freeze;
325
+ }
326
+
327
+ const { ws, snapshot } = freeze;
328
+ const exportDir = cjoin(ws.paths.exportsDir, target);
329
+ await fs.mkdir(exportDir, { recursive: true });
330
+
331
+ const handoff = {
332
+ version: 1,
333
+ versionId: snapshot.latestVersionId,
334
+ versionNumber: snapshot.prdVersion,
335
+ target,
336
+ generatedAt: timestamp(),
337
+ workspaceRoot: ws.workspaceRoot,
338
+ projectRoot: ws.projectRoot,
339
+ schema: ws.data.schema?.name ?? null,
340
+ templatePack: resolveActiveTemplatePack(ws),
341
+ productTypes: ws.data.config?.supportedProductTypes ?? [],
342
+ productType: resolveCurrentProductType(ws),
343
+ digest: snapshot.digest,
344
+ sourceFiles: [
345
+ ...CORE_TEMPLATE_FILES,
346
+ ...((await exists(ws.paths.activeArchitectureDiagramHtml)) ? ['engagements/active/architecture-diagram.html'] : []),
347
+ ...((await exists(ws.paths.activeArchitectureDiagramJson)) ? ['engagements/active/architecture-diagram.json'] : []),
348
+ ...((await exists(ws.paths.activeArchitectureDiagramMermaid)) ? ['engagements/active/architecture-diagram.mmd'] : []),
349
+ ...((await exists(ws.paths.activeProductFlowDiagramHtml)) ? ['engagements/active/product-flow-diagram.html'] : []),
350
+ ...((await exists(ws.paths.activeProductFlowDiagramJson)) ? ['engagements/active/product-flow-diagram.json'] : []),
351
+ ...((await exists(ws.paths.activeProductFlowDiagramMermaid)) ? ['engagements/active/product-flow-diagram.mmd'] : []),
352
+ ],
353
+ nextStep: target === 'openprd' || target === 'openspec'
354
+ ? 'Generate an OpenPrd change and continue with specs/design/tasks.'
355
+ : 'Consume the handoff bundle in the downstream system.',
356
+ };
357
+
358
+ await writeJson(cjoin(exportDir, 'handoff.json'), handoff);
359
+ await writeText(cjoin(exportDir, 'handoff.md'), `# 交接\n\n- 目标: ${target}\n- 版本: ${handoff.versionId}\n- Schema: ${handoff.schema}\n- 模板包: ${handoff.templatePack}\n- Digest: ${handoff.digest}\n- 下一步: ${handoff.nextStep}\n`);
360
+ if (await exists(ws.paths.activeArchitectureDiagramHtml)) {
361
+ await fs.copyFile(ws.paths.activeArchitectureDiagramHtml, cjoin(exportDir, 'architecture-diagram.html'));
362
+ }
363
+ if (await exists(ws.paths.activeArchitectureDiagramJson)) {
364
+ await fs.copyFile(ws.paths.activeArchitectureDiagramJson, cjoin(exportDir, 'architecture-diagram.json'));
365
+ }
366
+ if (await exists(ws.paths.activeArchitectureDiagramMermaid)) {
367
+ await fs.copyFile(ws.paths.activeArchitectureDiagramMermaid, cjoin(exportDir, 'architecture-diagram.mmd'));
368
+ }
369
+ if (await exists(ws.paths.activeProductFlowDiagramHtml)) {
370
+ await fs.copyFile(ws.paths.activeProductFlowDiagramHtml, cjoin(exportDir, 'product-flow-diagram.html'));
371
+ }
372
+ if (await exists(ws.paths.activeProductFlowDiagramJson)) {
373
+ await fs.copyFile(ws.paths.activeProductFlowDiagramJson, cjoin(exportDir, 'product-flow-diagram.json'));
374
+ }
375
+ if (await exists(ws.paths.activeProductFlowDiagramMermaid)) {
376
+ await fs.copyFile(ws.paths.activeProductFlowDiagramMermaid, cjoin(exportDir, 'product-flow-diagram.mmd'));
377
+ }
378
+ await appendWorkflowEvent(ws, 'handoff', {
379
+ target,
380
+ versionId: handoff.versionId,
381
+ });
382
+ await appendProgress(ws, [
383
+ `已生成面向 ${target} 的交接包。`,
384
+ `版本: ${handoff.versionId}`,
385
+ ]);
386
+ await appendDecision(ws, [
387
+ `交接目标已设置为 ${target}。`,
388
+ `版本 ${handoff.versionId} 已导出到 ${exportDir}。`,
389
+ ]);
390
+
391
+ const currentState = {
392
+ ...(ws.data.currentState ?? {}),
393
+ status: 'handed_off',
394
+ handedOffAt: handoff.generatedAt,
395
+ handoffTarget: target,
396
+ };
397
+ await writeJson(ws.paths.currentState, currentState);
398
+ await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(currentState));
399
+
400
+ return { ok: true, ws, report: freeze.report, snapshot, handoff, exportDir };
401
+ }
402
+
403
+ function reviewConfirmationCommand(snapshot) {
404
+ if (!snapshot?.versionId || !snapshot?.digest) {
405
+ return 'openprd review . --open,然后 openprd review . --mark confirmed';
406
+ }
407
+ let command = `openprd review . --open,然后 openprd review . --mark confirmed --version ${snapshot.versionId} --digest ${snapshot.digest}`;
408
+ if (snapshot.workUnitId) {
409
+ command = `${command} --work-unit ${snapshot.workUnitId}`;
410
+ }
411
+ return command;
412
+ }
413
+
414
+ function assertPrdReviewConfirmedForChange(currentState, snapshot) {
415
+ if (['frozen', 'handed_off'].includes(snapshot?.status)) {
416
+ return;
417
+ }
418
+ const review = currentState?.reviewStatus ?? null;
419
+ const confirmed = review?.versionId === snapshot?.versionId && review?.status === 'confirmed';
420
+ if (confirmed) {
421
+ return;
422
+ }
423
+ throw new Error([
424
+ '生成 OpenPrd change 前需要先确认最新 PRD review.html。',
425
+ `当前版本: ${snapshot?.versionId ?? '未知'}。`,
426
+ `当前评审状态: ${review?.status ?? 'missing'}。`,
427
+ `建议: ${reviewConfirmationCommand(snapshot)}。`,
428
+ ].join('\n'));
429
+ }
430
+
431
+ async function generateOpenSpecChangeWorkspace(projectRoot, options = {}) {
432
+ const ws = await loadWorkspace(projectRoot);
433
+ const versionIndex = await readVersionIndex(ws);
434
+ const latest = await loadLatestVersionSnapshot(ws);
435
+ const currentState = ws.data.currentState ?? {};
436
+ const snapshot = latest?.snapshot ?? buildPrdSnapshot(ws, {
437
+ ...currentState,
438
+ versionNumber: currentState.prdVersion ?? (versionIndex.at(-1)?.versionNumber ?? 0),
439
+ versionId: currentState.prdVersion > 0
440
+ ? formatVersionId(currentState.prdVersion)
441
+ : (versionIndex.at(-1)?.versionId ?? 'v0000'),
442
+ productType: resolveCurrentProductType(ws),
443
+ templatePack: resolveActiveTemplatePack(ws),
444
+ });
445
+ assertPrdReviewConfirmedForChange(currentState, snapshot);
446
+ const analysis = analyzePrdSnapshot(snapshot);
447
+ const result = await writeOpenSpecChangeWorkspace(projectRoot, {
448
+ ...options,
449
+ snapshot,
450
+ analysis,
451
+ });
452
+ const validation = await validateOpenSpecChangeWorkspace(projectRoot, { change: result.changeId });
453
+ await activateOpenPrdChangeWorkspace(projectRoot, { change: result.changeId });
454
+
455
+ await appendWorkflowEvent(ws, 'openprd_change_generated', {
456
+ changeId: result.changeId,
457
+ taskCount: result.taskCount,
458
+ valid: validation.valid,
459
+ });
460
+ await appendProgress(ws, [
461
+ `已生成 OpenPrd change ${result.changeId}。`,
462
+ `任务数: ${result.taskCount}。`,
463
+ `验证: ${validation.valid ? '通过' : '失败'}。`,
464
+ ]);
465
+ await syncSessionBindingFromChange(projectRoot, result.changeId, {
466
+ title: snapshot.title ?? null,
467
+ versionId: snapshot.versionId ?? null,
468
+ digest: snapshot.digest ?? null,
469
+ workUnitId: snapshot.workUnitId ?? null,
470
+ targetRoot: snapshot.targetRoot ?? null,
471
+ reviewStatus: currentState.reviewStatus?.status ?? null,
472
+ reviewPath: currentState.reviewStatus?.reviewPath ?? currentState.reviewStatus?.stableArtifact ?? null,
473
+ activeReviewPath: currentState.reviewStatus?.entryPath ?? currentState.reviewStatus?.artifact ?? null,
474
+ });
475
+
476
+ return {
477
+ ...result,
478
+ ws,
479
+ snapshot,
480
+ analysis,
481
+ validation,
482
+ ok: result.ok && validation.ok,
483
+ };
484
+ }
485
+
486
+ async function isDirectoryPath(candidate) {
487
+ const stat = await fs.stat(path.resolve(candidate)).catch(() => null);
488
+ return Boolean(stat?.isDirectory());
489
+ }
490
+
491
+ export async function main(argv = process.argv.slice(2)) {
492
+ const [command = 'help', ...rest] = argv;
493
+
494
+ if (command === 'version' || command === '--version' || command === '-v') {
495
+ const packageInfo = await readCliPackageInfo();
496
+ console.log(packageInfo.version);
497
+ return 0;
498
+ }
499
+
500
+ if (command === 'help' || command === '--help' || command === '-h') {
501
+ console.log(usage());
502
+ return 0;
503
+ }
504
+
505
+ const { flags, positionals } = parseCommandArgs(rest);
506
+ if (positionals.includes('--help') || positionals.includes('-h')) {
507
+ console.log(usage());
508
+ return 0;
509
+ }
510
+ const projectPath = path.resolve(flags.path ?? positionals[0] ?? process.cwd());
511
+
512
+ try {
513
+ if (command === 'init') {
514
+ const result = await initWorkspace(projectPath, {
515
+ force: flags.force,
516
+ templatePack: flags.templatePack,
517
+ tools: flags.tools,
518
+ hookProfile: flags.hookProfile,
519
+ enableUserCodexConfig: true,
520
+ });
521
+ printInitResult(result, flags.json);
522
+ return 0;
523
+ }
524
+
525
+ if (command === 'setup') {
526
+ const result = await setupAgentIntegrationWorkspace(projectPath, {
527
+ force: flags.force,
528
+ templatePack: flags.templatePack,
529
+ tools: flags.tools,
530
+ hookProfile: flags.hookProfile,
531
+ enableUserCodexConfig: true,
532
+ });
533
+ printAgentIntegrationResult(result, flags.json);
534
+ return result.ok ? 0 : 1;
535
+ }
536
+
537
+ if (command === 'update') {
538
+ const result = await updateAgentIntegrationWorkspace(projectPath, {
539
+ force: flags.force,
540
+ tools: flags.tools,
541
+ hookProfile: flags.hookProfile,
542
+ enableUserCodexConfig: true,
543
+ });
544
+ printAgentIntegrationResult(result, flags.json);
545
+ return result.ok ? 0 : 1;
546
+ }
547
+
548
+ if (command === 'self-update') {
549
+ const result = await selfUpdateWorkspace({
550
+ dryRun: flags.dryRun,
551
+ json: flags.json,
552
+ });
553
+ printSelfUpdateResult(result, flags.json);
554
+ return result.ok ? 0 : 1;
555
+ }
556
+
557
+ if (command === 'upgrade') {
558
+ const result = await upgradeWorkspace(projectPath, {
559
+ dryRun: flags.dryRun,
560
+ json: flags.json,
561
+ fleet: flags.fleet,
562
+ tools: flags.tools,
563
+ hookProfile: flags.hookProfile,
564
+ force: flags.force,
565
+ maxDepth: flags.maxDepth,
566
+ include: flags.include,
567
+ exclude: flags.exclude,
568
+ report: flags.report,
569
+ });
570
+ printUpgradeResult(result, flags.json);
571
+ return result.ok ? 0 : 1;
572
+ }
573
+
574
+ if (command === 'doctor') {
575
+ const result = await doctorWorkspace(projectPath, { tools: flags.tools, hookProfile: flags.hookProfile, enableUserCodexConfig: true });
576
+ printDoctorResult(result, flags.json);
577
+ return result.ok ? 0 : 1;
578
+ }
579
+
580
+ if (command === 'fleet') {
581
+ const result = await fleetWorkspace(projectPath, {
582
+ tools: flags.tools,
583
+ hookProfile: flags.hookProfile,
584
+ force: flags.force,
585
+ dryRun: flags.dryRun,
586
+ updateOpenprd: flags.updateOpenprd,
587
+ backfillWorkUnits: flags.backfillWorkUnits,
588
+ syncRegistry: flags.syncRegistry,
589
+ setupMissing: flags.setupMissing,
590
+ doctor: flags.doctor,
591
+ maxDepth: flags.maxDepth,
592
+ include: flags.include,
593
+ exclude: flags.exclude,
594
+ report: flags.report,
595
+ enableUserCodexConfig: true,
596
+ openprdHome: flags.openprdHome,
597
+ });
598
+ printFleetResult(result, flags.json);
599
+ return result.ok ? 0 : 1;
600
+ }
601
+
602
+ if (command === 'run') {
603
+ const result = await runWorkspace(projectPath, {
604
+ context: flags.context,
605
+ verify: flags.verify,
606
+ recordHook: flags.recordHook,
607
+ event: flags.event,
608
+ risk: flags.risk,
609
+ outcome: flags.outcome,
610
+ preview: flags.preview,
611
+ learn: flags.learn,
612
+ message: flags.message,
613
+ });
614
+ printRunResult(result, flags.json);
615
+ return result.ok ? 0 : 1;
616
+ }
617
+
618
+ if (command === 'loop') {
619
+ let result;
620
+ const options = {
621
+ change: flags.change,
622
+ item: flags.item,
623
+ agent: flags.agent,
624
+ agentCommand: flags.agentCommand,
625
+ dryRun: flags.dryRun,
626
+ commit: flags.commit,
627
+ message: flags.message,
628
+ evidence: flags.evidence,
629
+ notes: flags.notes,
630
+ };
631
+ if (flags.init) {
632
+ result = await initLoopWorkspace(projectPath, options);
633
+ } else if (flags.plan) {
634
+ result = await planLoopWorkspace(projectPath, options);
635
+ } else if (flags.prompt) {
636
+ result = await promptLoopWorkspace(projectPath, options);
637
+ } else if (flags.loopRun) {
638
+ result = await runLoopWorkspace(projectPath, options);
639
+ } else if (flags.verify) {
640
+ result = await verifyLoopWorkspace(projectPath, options);
641
+ } else if (flags.finish) {
642
+ result = await finishLoopWorkspace(projectPath, options);
643
+ } else if (flags.next) {
644
+ result = await nextLoopWorkspace(projectPath, options);
645
+ } else {
646
+ result = await statusLoopWorkspace(projectPath);
647
+ }
648
+ printLoopResult(result, flags.json);
649
+ return result.ok ? 0 : 1;
650
+ }
651
+
652
+ if (command === 'standards') {
653
+ const result = flags.init
654
+ ? await initStandardsWorkspace(projectPath, { force: flags.force })
655
+ : flags.classifyExternal !== null
656
+ ? await classifyExternalReferenceWorkspace(projectPath, { externalReference: flags.classifyExternal })
657
+ : await checkStandardsWorkspace(projectPath);
658
+ printStandardsResult(result, flags.json);
659
+ return result.ok ? 0 : 1;
660
+ }
661
+
662
+ if (command === 'quality') {
663
+ const result = await qualityWorkspace(projectPath, {
664
+ init: flags.init,
665
+ verify: flags.verify,
666
+ report: flags.report,
667
+ html: flags.html,
668
+ learn: Boolean(flags.learn),
669
+ review: flags.review,
670
+ from: flags.from,
671
+ force: flags.force,
672
+ });
673
+ printQualityResult(result, flags.json);
674
+ return result.ok ? 0 : 1;
675
+ }
676
+
677
+ if (command === 'visual-compare') {
678
+ const result = await visualCompareWorkspace(projectPath, {
679
+ reference: flags.reference,
680
+ actual: flags.actual,
681
+ out: flags.out,
682
+ format: flags.format,
683
+ quality: flags.quality,
684
+ maxPanelWidth: flags.maxPanelWidth,
685
+ referenceLabel: flags.referenceLabel,
686
+ actualLabel: flags.actualLabel,
687
+ });
688
+ printVisualCompareResult(result, flags.json);
689
+ return result.ok ? 0 : 1;
690
+ }
691
+
692
+ if (command === 'grow') {
693
+ const firstArgIsProjectPath = !flags.path && positionals.length > 0 && await isDirectoryPath(positionals[0]);
694
+ const growthProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[0] : process.cwd()));
695
+ let result;
696
+ if (flags.init) {
697
+ result = await initGrowthWorkspace(growthProjectPath);
698
+ } else if (flags.apply) {
699
+ result = await applyGrowthCandidateWorkspace(growthProjectPath, { id: flags.id });
700
+ } else if (flags.reject) {
701
+ result = await rejectGrowthCandidateWorkspace(growthProjectPath, { id: flags.id, notes: flags.notes });
702
+ } else if (flags.check) {
703
+ result = await checkGrowthWorkspace(growthProjectPath);
704
+ } else {
705
+ result = await reviewGrowthWorkspace(growthProjectPath);
706
+ }
707
+ printGrowthResult(result, flags.json);
708
+ return result.ok ? 0 : 1;
709
+ }
710
+
711
+ if (command === 'dev-check') {
712
+ const firstArgIsProjectPath = !flags.path && positionals.length > 1 && await isDirectoryPath(positionals[0]);
713
+ const devProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[0] : process.cwd()));
714
+ const files = flags.path
715
+ ? positionals
716
+ : (firstArgIsProjectPath ? positionals.slice(1) : positionals);
717
+ const result = await checkDevelopmentStandardsWorkspace(devProjectPath, { files });
718
+ printDevelopmentStandardsResult(result, flags.json);
719
+ return result.ok ? 0 : 1;
720
+ }
721
+
722
+ if (command === 'benchmark') {
723
+ const subcommand = positionals[0] ?? 'list';
724
+ const benchmarkProjectPath = path.resolve(
725
+ flags.path
726
+ ?? ((subcommand === 'list' || subcommand === 'verify') ? positionals[1] : null)
727
+ ?? process.cwd(),
728
+ );
729
+ const target = positionals[1] ?? null;
730
+ let result;
731
+ if (subcommand === 'add') {
732
+ result = await addBenchmarkWorkspace(benchmarkProjectPath, {
733
+ source: target ?? flags.source,
734
+ notes: flags.notes,
735
+ });
736
+ } else if (subcommand === 'approve') {
737
+ result = await approveBenchmarkWorkspace(benchmarkProjectPath, {
738
+ id: flags.id ?? target,
739
+ });
740
+ } else if (subcommand === 'verify') {
741
+ result = await verifyBenchmarkWorkspace(benchmarkProjectPath);
742
+ } else if (subcommand === 'list') {
743
+ result = await listBenchmarkWorkspace(benchmarkProjectPath);
744
+ } else {
745
+ console.log('Usage: openprd benchmark <add|list|approve|verify> [target-or-id] [path-for-list-or-verify] [--path <project>] [--notes <text>] [--id <benchmark-id>]');
746
+ return 1;
747
+ }
748
+ printBenchmarkResult(result, flags.json);
749
+ return result.ok ? 0 : 1;
750
+ }
751
+
752
+ if (command === 'classify') {
753
+ const productType = flags.productType ?? positionals[1] ?? positionals[0];
754
+ if (!productType) {
755
+ console.log('Usage: openprd classify [path] <consumer|b2b|agent>');
756
+ return 1;
757
+ }
758
+ const result = await classifyWorkspace(projectPath, productType);
759
+ printClassifyResult(result, flags.json);
760
+ return 0;
761
+ }
762
+
763
+ if (command === 'clarify') {
764
+ const result = await clarifyWorkspace(projectPath, { open: flags.open || !flags.json, mode: flags.mode });
765
+ printClarifyResult(result, flags.json);
766
+ return 0;
767
+ }
768
+
769
+ if (command === 'capture') {
770
+ const result = await captureWorkspace(projectPath, {
771
+ field: flags.field,
772
+ value: flags.value,
773
+ jsonFile: flags.jsonFile,
774
+ artifactMarkdown: flags.artifactMarkdown,
775
+ source: flags.source,
776
+ append: flags.append,
777
+ });
778
+ printCaptureResult(result, flags.json);
779
+ return 0;
780
+ }
781
+
782
+ if (command === 'interview') {
783
+ const requestedType = flags.productType ?? positionals[1] ?? null;
784
+ const result = await interviewWorkspace(projectPath, requestedType);
785
+ printInterviewResult(result, flags.json);
786
+ return 0;
787
+ }
788
+
789
+ if (command === 'playground') {
790
+ const result = await playgroundWorkspace(projectPath, { open: flags.open });
791
+ printPlaygroundResult(result, flags.json);
792
+ return 0;
793
+ }
794
+
795
+ if (command === 'learn') {
796
+ if (flags.enable || flags.disable) {
797
+ const enabled = flags.enable ? true : false;
798
+ if (flags.enable && flags.disable) {
799
+ throw new Error('Cannot use --enable and --disable together.');
800
+ }
801
+ const result = await setLearningReviewModeWorkspace(projectPath, enabled);
802
+ printLearningResult(result, flags.json);
803
+ return result.ok ? 0 : 1;
804
+ }
805
+
806
+ const result = await generateLearningReviewWorkspace(projectPath, {
807
+ topic: flags.topic,
808
+ genre: flags.genre,
809
+ style: flags.style,
810
+ sourceScope: flags.source,
811
+ contentJson: flags.contentJson,
812
+ open: flags.open || !flags.json,
813
+ trigger: 'manual',
814
+ respectConfig: false,
815
+ });
816
+ printLearningResult(result, flags.json);
817
+ return result.ok ? 0 : 1;
818
+ }
819
+
820
+ if (command === 'synthesize') {
821
+ const result = await synthesizeWorkspace(projectPath, {
822
+ title: flags.title,
823
+ owner: flags.owner,
824
+ problemStatement: flags.problem,
825
+ whyNow: flags.whyNow,
826
+ evidence: flags.evidence,
827
+ productType: flags.productType,
828
+ workUnit: flags.workUnit,
829
+ targetRoot: flags.targetRoot,
830
+ open: flags.open || !flags.json,
831
+ });
832
+ printSynthesizeResult(result, flags.json);
833
+ return 0;
834
+ }
835
+
836
+ if (command === 'review') {
837
+ const result = await reviewWorkspace(projectPath, {
838
+ open: flags.open,
839
+ mark: flags.mark,
840
+ notes: flags.notes,
841
+ version: flags.version,
842
+ digest: flags.digest,
843
+ workUnit: flags.workUnit,
844
+ });
845
+ printReviewResult(result, flags.json);
846
+ return result.ok ? 0 : 1;
847
+ }
848
+
849
+ if (command === 'review-presentation') {
850
+ if (flags.template) {
851
+ console.log(JSON.stringify(buildReviewPresentationTemplatePayload(), null, 2));
852
+ return 0;
853
+ }
854
+ const result = await reviewPresentationWorkspace(projectPath, {
855
+ version: flags.version,
856
+ presentationPath: flags.presentation,
857
+ write: flags.write,
858
+ });
859
+ if (flags.json) {
860
+ console.log(JSON.stringify(result, null, 2));
861
+ } else {
862
+ console.log(`展示文案校验: ${result.ok ? '通过' : '需要重写'}`);
863
+ console.log(`版本: ${result.versionId}`);
864
+ console.log(`超限或格式问题: ${result.presentationFeedback.length}`);
865
+ if (result.presentationFeedback.length > 0) {
866
+ for (const item of result.presentationFeedback.slice(0, 6)) {
867
+ const pathHint = item.jsonPath ? `${item.jsonPath}: ` : '';
868
+ const sizeHint = item.maxChars ? ` 当前 ${item.currentChars} 字,限制 ${item.maxChars} 字。` : '';
869
+ console.log(`- ${item.area} / ${item.target}: ${pathHint}${item.action}${sizeHint}`);
870
+ }
871
+ }
872
+ if (result.written) {
873
+ console.log(`已写入: ${result.written}`);
874
+ }
875
+ if (result.reviewPath) {
876
+ console.log(`已生成评审面板: ${result.reviewPath}`);
877
+ }
878
+ if (result.reviewEntryPath) {
879
+ console.log(`已更新固定入口: ${result.reviewEntryPath}`);
880
+ }
881
+ }
882
+ return flags.failOnViolation && !result.ok ? 1 : 0;
883
+ }
884
+
885
+ if (command === 'diagram') {
886
+ const result = await diagramWorkspace(projectPath, { open: flags.open, type: flags.type, input: flags.input, mark: flags.mark });
887
+ printDiagramResult(result, flags.json);
888
+ return 0;
889
+ }
890
+
891
+ if (command === 'next') {
892
+ const result = await nextWorkspace(projectPath);
893
+ printNextResult(result, flags.json);
894
+ return 0;
895
+ }
896
+
897
+ if (command === 'diff') {
898
+ const result = await diffWorkspace(projectPath, { from: flags.from, to: flags.to });
899
+ printDiffResult(result, flags.json);
900
+ return 0;
901
+ }
902
+
903
+ if (command === 'history') {
904
+ const result = await historyWorkspace(projectPath);
905
+ printHistoryResult(result, flags.json);
906
+ return 0;
907
+ }
908
+
909
+ if (command === 'validate') {
910
+ const { report } = await validateWorkspace(projectPath);
911
+ printValidation(report, flags.json);
912
+ return report.valid ? 0 : 1;
913
+ }
914
+
915
+ if (command === 'status') {
916
+ const { report, ws } = await validateWorkspace(projectPath);
917
+ const guidance = await computeWorkspaceGuidance(ws, { questionLimit: 5 });
918
+ printStatus(ws, report, guidance, flags.json);
919
+ return report.valid ? 0 : 1;
920
+ }
921
+
922
+ if (command === 'freeze') {
923
+ const result = await freezeWorkspace(projectPath);
924
+ if (!result.ok) {
925
+ printValidation(result.report, flags.json);
926
+ return 1;
927
+ }
928
+ printFreezeResult(result, flags.json);
929
+ return 0;
930
+ }
931
+
932
+ if (command === 'handoff') {
933
+ const result = await handoffWorkspace(projectPath, flags.target);
934
+ if (!result.ok) {
935
+ printValidation(result.report, flags.json);
936
+ return 1;
937
+ }
938
+ printHandoffResult(result, flags.json);
939
+ return 0;
940
+ }
941
+
942
+ if (command === 'changes') {
943
+ const result = await listOpenPrdChangesWorkspace(projectPath);
944
+ printOpenPrdChangesResult(result, flags.json);
945
+ return result.ok ? 0 : 1;
946
+ }
947
+
948
+ if (command === 'specs') {
949
+ const result = await listAcceptedSpecsWorkspace(projectPath);
950
+ printAcceptedSpecsResult(result, flags.json);
951
+ return result.ok ? 0 : 1;
952
+ }
953
+
954
+ if (command === 'change') {
955
+ if (flags.generate) {
956
+ const result = await generateOpenSpecChangeWorkspace(projectPath, {
957
+ change: flags.change ?? positionals[1] ?? null,
958
+ force: flags.force,
959
+ });
960
+ printOpenSpecGenerateResult(result, flags.json);
961
+ return result.ok ? 0 : 1;
962
+ }
963
+ if (flags.validate) {
964
+ const result = await validateOpenSpecChangeWorkspace(projectPath, {
965
+ change: flags.change ?? positionals[1] ?? null,
966
+ });
967
+ printOpenSpecChangeValidationResult(result, flags.json);
968
+ return result.ok ? 0 : 1;
969
+ }
970
+ if (flags.apply) {
971
+ const result = await applyOpenPrdChangeWorkspace(projectPath, {
972
+ change: flags.change ?? positionals[1] ?? null,
973
+ force: flags.force,
974
+ });
975
+ printOpenPrdChangeActionResult(result, flags.json);
976
+ return result.ok ? 0 : 1;
977
+ }
978
+ if (flags.archive) {
979
+ const result = await archiveOpenPrdChangeWorkspace(projectPath, {
980
+ change: flags.change ?? positionals[1] ?? null,
981
+ force: flags.force,
982
+ keep: flags.keep,
983
+ });
984
+ printOpenPrdChangeActionResult(result, flags.json);
985
+ return result.ok ? 0 : 1;
986
+ }
987
+ if (flags.activate) {
988
+ const result = await activateOpenPrdChangeWorkspace(projectPath, {
989
+ change: flags.change ?? positionals[1] ?? null,
990
+ });
991
+ printOpenPrdChangeActionResult(result, flags.json);
992
+ return result.ok ? 0 : 1;
993
+ }
994
+ if (flags.close) {
995
+ const result = await closeOpenPrdChangeWorkspace(projectPath, {
996
+ change: flags.change ?? positionals[1] ?? null,
997
+ notes: flags.notes,
998
+ });
999
+ printOpenPrdChangeActionResult(result, flags.json);
1000
+ return result.ok ? 0 : 1;
1001
+ }
1002
+ const result = await listOpenPrdChangesWorkspace(projectPath);
1003
+ printOpenPrdChangesResult(result, flags.json);
1004
+ return result.ok ? 0 : 1;
1005
+ }
1006
+
1007
+ if (command === 'tasks' || command === 'openspec-tasks') {
1008
+ const taskOptions = {
1009
+ change: flags.change ?? null,
1010
+ item: flags.item ?? flags.id ?? positionals[1] ?? null,
1011
+ verify: flags.verify,
1012
+ evidence: flags.evidence,
1013
+ notes: flags.notes,
1014
+ };
1015
+ const result = flags.advance
1016
+ ? await advanceOpenSpecTaskWorkspace(projectPath, taskOptions)
1017
+ : flags.verify
1018
+ ? await verifyOpenSpecTaskWorkspace(projectPath, taskOptions)
1019
+ : await listOpenSpecTaskWorkspace(projectPath, taskOptions);
1020
+ printOpenSpecTaskResult(result, flags.json);
1021
+ return result.ok ? 0 : 1;
1022
+ }
1023
+
1024
+ if (command === 'openspec-generate') {
1025
+ const result = await generateOpenSpecChangeWorkspace(projectPath, {
1026
+ change: flags.change ?? positionals[1] ?? null,
1027
+ force: flags.force,
1028
+ });
1029
+ printOpenSpecGenerateResult(result, flags.json);
1030
+ return result.ok ? 0 : 1;
1031
+ }
1032
+
1033
+ if (command === 'openspec-validate') {
1034
+ const result = await validateOpenSpecChangeWorkspace(projectPath, {
1035
+ change: flags.change ?? positionals[1] ?? null,
1036
+ });
1037
+ printOpenSpecChangeValidationResult(result, flags.json);
1038
+ return result.ok ? 0 : 1;
1039
+ }
1040
+
1041
+ if (command === 'discovery' || command === 'openspec-discovery') {
1042
+ const result = await openspecDiscoveryWorkspace(projectPath, {
1043
+ mode: flags.mode,
1044
+ reference: flags.reference,
1045
+ maxIterations: flags.maxIterations,
1046
+ resume: flags.resume,
1047
+ advance: flags.advance,
1048
+ verify: flags.verify,
1049
+ item: flags.item,
1050
+ status: flags.status,
1051
+ claim: flags.claim,
1052
+ evidence: flags.evidence,
1053
+ notes: flags.notes,
1054
+ confidence: flags.confidence,
1055
+ source: flags.source,
1056
+ });
1057
+ printOpenSpecDiscoveryResult(result, flags.json);
1058
+ return result.ok ? 0 : 1;
1059
+ }
1060
+
1061
+ console.log(usage());
1062
+ return 1;
1063
+ } catch (error) {
1064
+ console.error(error instanceof Error ? error.message : String(error));
1065
+ return 1;
1066
+ }
1067
+ }
1068
+
1069
+ export {
1070
+ validateWorkspace,
1071
+ initWorkspace,
1072
+ setupAgentIntegrationWorkspace,
1073
+ updateAgentIntegrationWorkspace,
1074
+ selfUpdateWorkspace,
1075
+ upgradeWorkspace,
1076
+ doctorWorkspace,
1077
+ fleetWorkspace,
1078
+ backfillWorkUnitsWorkspace,
1079
+ runWorkspace,
1080
+ initLoopWorkspace,
1081
+ planLoopWorkspace,
1082
+ statusLoopWorkspace,
1083
+ nextLoopWorkspace,
1084
+ promptLoopWorkspace,
1085
+ runLoopWorkspace,
1086
+ verifyLoopWorkspace,
1087
+ finishLoopWorkspace,
1088
+ clarifyWorkspace,
1089
+ captureWorkspace,
1090
+ playgroundWorkspace,
1091
+ generateLearningReviewWorkspace,
1092
+ setLearningReviewModeWorkspace,
1093
+ synthesizeWorkspace,
1094
+ nextWorkspace,
1095
+ diffWorkspace,
1096
+ historyWorkspace,
1097
+ reviewWorkspace,
1098
+ reviewPresentationWorkspace,
1099
+ freezeWorkspace,
1100
+ handoffWorkspace,
1101
+ generateOpenSpecChangeWorkspace,
1102
+ validateOpenSpecChangeWorkspace,
1103
+ openspecDiscoveryWorkspace,
1104
+ listOpenSpecTaskWorkspace,
1105
+ advanceOpenSpecTaskWorkspace,
1106
+ verifyOpenSpecTaskWorkspace,
1107
+ listOpenPrdChangesWorkspace,
1108
+ activateOpenPrdChangeWorkspace,
1109
+ closeOpenPrdChangeWorkspace,
1110
+ applyOpenPrdChangeWorkspace,
1111
+ archiveOpenPrdChangeWorkspace,
1112
+ listAcceptedSpecsWorkspace,
1113
+ diagramWorkspace,
1114
+ classifyWorkspace,
1115
+ interviewWorkspace,
1116
+ loadWorkspace,
1117
+ initStandardsWorkspace,
1118
+ checkStandardsWorkspace,
1119
+ classifyExternalReferenceWorkspace,
1120
+ initQualityWorkspace,
1121
+ verifyQualityWorkspace,
1122
+ learnQualityWorkspace,
1123
+ qualityWorkspace,
1124
+ visualCompareWorkspace,
1125
+ checkDevelopmentStandardsWorkspace,
1126
+ initGrowthWorkspace,
1127
+ checkGrowthWorkspace,
1128
+ reviewGrowthWorkspace,
1129
+ applyGrowthCandidateWorkspace,
1130
+ rejectGrowthCandidateWorkspace,
1131
+ benchmarkWorkspace,
1132
+ addBenchmarkWorkspace,
1133
+ listBenchmarkWorkspace,
1134
+ approveBenchmarkWorkspace,
1135
+ verifyBenchmarkWorkspace,
1136
+ };