@openprd/cli 0.1.11 → 0.1.19

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 (147) hide show
  1. package/.openprd/changes/adaptive-project-framing/.openprd.yaml +2 -0
  2. package/.openprd/changes/adaptive-project-framing/design.md +47 -0
  3. package/.openprd/changes/adaptive-project-framing/proposal.md +35 -0
  4. package/.openprd/changes/adaptive-project-framing/specs/agent-requirements/spec.md +16 -0
  5. package/.openprd/changes/adaptive-project-framing/task-events.jsonl +20 -0
  6. package/.openprd/changes/adaptive-project-framing/tasks.md +340 -0
  7. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/.openprd.yaml +2 -0
  8. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/design.md +34 -0
  9. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/proposal.md +23 -0
  10. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/specs/agent-requirements/spec.md +16 -0
  11. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/task-events.jsonl +14 -0
  12. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/tasks.md +238 -0
  13. package/.openprd/changes/loop-isolated-worktree-commit/.openprd.yaml +2 -0
  14. package/.openprd/changes/loop-isolated-worktree-commit/design.md +53 -0
  15. package/.openprd/changes/loop-isolated-worktree-commit/proposal.md +38 -0
  16. package/.openprd/changes/loop-isolated-worktree-commit/specs/agent-requirements/spec.md +16 -0
  17. package/.openprd/changes/loop-isolated-worktree-commit/task-events.jsonl +22 -0
  18. package/.openprd/changes/loop-isolated-worktree-commit/tasks.md +357 -0
  19. package/.openprd/changes/openprd/.openprd.yaml +2 -0
  20. package/.openprd/changes/openprd/design.md +46 -0
  21. package/.openprd/changes/openprd/proposal.md +36 -0
  22. package/.openprd/changes/openprd/specs/agent-requirements/spec.md +16 -0
  23. package/.openprd/changes/openprd/task-events.jsonl +23 -0
  24. package/.openprd/changes/openprd/tasks.md +242 -0
  25. package/.openprd/changes/openprd-generated-change/.openprd.yaml +2 -0
  26. package/.openprd/changes/openprd-generated-change/design.md +50 -0
  27. package/.openprd/changes/openprd-generated-change/proposal.md +37 -0
  28. package/.openprd/changes/openprd-generated-change/specs/agent-requirements/spec.md +16 -0
  29. package/.openprd/changes/openprd-generated-change/task-events.jsonl +21 -0
  30. package/.openprd/changes/openprd-generated-change/tasks.md +357 -0
  31. package/.openprd/changes/pm-human-in-the-loop/.openprd.yaml +2 -0
  32. package/.openprd/changes/pm-human-in-the-loop/design.md +38 -0
  33. package/.openprd/changes/pm-human-in-the-loop/proposal.md +29 -0
  34. package/.openprd/changes/pm-human-in-the-loop/specs/agent-requirements/spec.md +16 -0
  35. package/.openprd/changes/pm-human-in-the-loop/task-events.jsonl +18 -0
  36. package/.openprd/changes/pm-human-in-the-loop/tasks.md +306 -0
  37. package/.openprd/changes/test-strategy-router/.openprd.yaml +2 -0
  38. package/.openprd/changes/test-strategy-router/design.md +65 -0
  39. package/.openprd/changes/test-strategy-router/proposal.md +45 -0
  40. package/.openprd/changes/test-strategy-router/specs/agent-requirements/spec.md +16 -0
  41. package/.openprd/changes/test-strategy-router/task-events.jsonl +1 -0
  42. package/.openprd/changes/test-strategy-router/tasks.md +132 -0
  43. package/.openprd/changes/verify-boundaries-and-historical-refresh/.openprd.yaml +2 -0
  44. package/.openprd/changes/verify-boundaries-and-historical-refresh/design.md +28 -0
  45. package/.openprd/changes/verify-boundaries-and-historical-refresh/proposal.md +23 -0
  46. package/.openprd/changes/verify-boundaries-and-historical-refresh/specs/agent-requirements/spec.md +16 -0
  47. package/.openprd/changes/verify-boundaries-and-historical-refresh/task-events.jsonl +11 -0
  48. package/.openprd/changes/verify-boundaries-and-historical-refresh/tasks.md +66 -0
  49. package/.openprd/changes/vibe-coding-dev-check/.openprd.yaml +2 -0
  50. package/.openprd/changes/vibe-coding-dev-check/design.md +37 -0
  51. package/.openprd/changes/vibe-coding-dev-check/proposal.md +28 -0
  52. package/.openprd/changes/vibe-coding-dev-check/specs/agent-requirements/spec.md +16 -0
  53. package/.openprd/changes/vibe-coding-dev-check/task-events.jsonl +18 -0
  54. package/.openprd/changes/vibe-coding-dev-check/tasks.md +306 -0
  55. package/.openprd/design/README.md +11 -6
  56. package/.openprd/design/README_EN.md +4 -1
  57. package/.openprd/design/active/asset-spec.md +6 -0
  58. package/.openprd/design/active/direction-plan.md +5 -5
  59. package/.openprd/design/active/selected-direction.md +2 -0
  60. package/.openprd/design/anti-slop.md +26 -1
  61. package/.openprd/design/assets/surface-presets.md +6 -0
  62. package/.openprd/design/checklists/ui-quality-gate.md +7 -0
  63. package/.openprd/design/lenses/frontend-lenses.md +28 -0
  64. package/.openprd/design/recipes/content-experience.md +8 -1
  65. package/.openprd/design/recipes/operational-tool.md +7 -0
  66. package/.openprd/design/recipes/product-launch.md +8 -1
  67. package/.openprd/design/templates/README.md +6 -3
  68. package/.openprd/design/templates/README_EN.md +6 -3
  69. package/.openprd/design/templates/ops-dashboard.html +8 -1
  70. package/.openprd/design/templates/product-launch.html +2 -1
  71. package/.openprd/design/themes/theme-catalog.json +23 -5
  72. package/.openprd/engagements/active/prd.md +137 -112
  73. package/.openprd/standards/config.json +7 -1
  74. package/AGENTS.md +13 -3
  75. package/README.md +127 -14
  76. package/README_EN.md +156 -13
  77. package/package.json +1 -1
  78. package/scripts/dev-check-wrapup-copy.mjs +46 -21
  79. package/skills/openprd-diagram-review/SKILL.md +27 -2
  80. package/skills/openprd-diagram-review/references/explanation-svg-patterns.md +131 -0
  81. package/skills/openprd-discovery-loop/SKILL.md +2 -2
  82. package/skills/openprd-frontend-design/SKILL.md +44 -20
  83. package/skills/openprd-frontend-design/references/design-asset-contract.md +6 -0
  84. package/skills/openprd-frontend-design/references/direction-engine.md +29 -4
  85. package/skills/openprd-harness/SKILL.md +22 -16
  86. package/skills/openprd-quality/SKILL.md +10 -8
  87. package/skills/openprd-requirement-intake/SKILL.md +2 -0
  88. package/skills/openprd-router/SKILL.md +2 -1
  89. package/skills/openprd-shared/SKILL.md +26 -16
  90. package/skills/openprd-standards/SKILL.md +4 -2
  91. package/src/agent-canonical-content.js +810 -0
  92. package/src/agent-integration.js +258 -777
  93. package/src/canvas-app-client-app.js +838 -0
  94. package/src/canvas-app-client-helpers.js +1138 -0
  95. package/src/canvas-app-styles.js +898 -0
  96. package/src/canvas-app.html.js +135 -0
  97. package/src/canvas-binding.js +414 -0
  98. package/src/canvas-codex-relay.js +430 -0
  99. package/src/canvas-i18n.js +282 -0
  100. package/src/canvas-scene.js +377 -0
  101. package/src/canvas-session-store.js +148 -0
  102. package/src/canvas-workspace.js +1264 -0
  103. package/src/cli/args.js +72 -4
  104. package/src/cli/basic-print.js +3 -0
  105. package/src/cli/canvas-print.js +106 -0
  106. package/src/cli/doctor-print.js +20 -0
  107. package/src/cli/print.js +2 -0
  108. package/src/cli/quality-commands.js +222 -0
  109. package/src/cli/quality-print.js +28 -2
  110. package/src/cli/run-print.js +5 -0
  111. package/src/cli/workflow-print.js +3 -0
  112. package/src/codex-app-server-relay.js +251 -0
  113. package/src/codex-hook-runner-template.mjs +978 -58
  114. package/src/design-starter-support.js +2 -2
  115. package/src/design-starter.js +39 -3
  116. package/src/dev-standards.js +125 -10
  117. package/src/diagram-core.js +6 -20
  118. package/src/discovery.js +2 -8
  119. package/src/docs-compact.js +125 -0
  120. package/src/execution-strategy.js +1 -1
  121. package/src/fleet.js +9 -3
  122. package/src/guide.js +227 -0
  123. package/src/html-artifacts.js +428 -7
  124. package/src/knowledge.js +45 -11
  125. package/src/language-policy.js +63 -3
  126. package/src/learning-review.js +1 -1
  127. package/src/loop.js +1 -1
  128. package/src/openprd.js +68 -131
  129. package/src/openspec/change-validate.js +2 -2
  130. package/src/openspec/constants.js +6 -7
  131. package/src/openspec/execute.js +2 -2
  132. package/src/openspec/generate.js +318 -102
  133. package/src/openspec/migration.js +168 -0
  134. package/src/openspec/paths.js +1 -22
  135. package/src/prd-core.js +1 -1
  136. package/src/quality-visual-review.js +257 -14
  137. package/src/quality.js +46 -8
  138. package/src/review-presentation.js +60 -0
  139. package/src/run-harness.js +41 -3
  140. package/src/session-evidence.js +363 -0
  141. package/src/standards.js +64 -0
  142. package/src/test-strategy.js +7 -4
  143. package/src/visual-compare-centering.js +622 -0
  144. package/src/visual-compare-core.js +1123 -0
  145. package/src/visual-compare.js +620 -698
  146. package/src/workspace-core.js +94 -4
  147. package/src/workspace-workflow.js +46 -1
@@ -1,11 +1,13 @@
1
- export const OPENPRD_CONTENT_LOCALE = 'zh-CN';
1
+ export const OPENPRD_FALLBACK_LOCALE = 'zh-CN';
2
+ export const OPENPRD_CONTENT_LOCALE = OPENPRD_FALLBACK_LOCALE;
2
3
 
3
4
  export const OPENPRD_LANGUAGE_POLICY =
4
- 'OpenPrd 默认用简体中文生成用户可见内容以及 Agent 产出的 spec、tasks 和说明文案;PRD、OpenPrd、OpenSpec、API、SDK、CLI、TypeScript、JSON、HTTP、WebSocket、字段 key、命令名、品牌名、产品名和协议名等必要专有名词按原文保留。';
5
+ 'OpenPrd 用户可见内容以及 Agent 产出的 spec、tasks 和说明文案应跟随用户当前主语言;无法判断时才使用简体中文兜底。PRD、OpenPrd、OpenSpec、API、SDK、CLI、TypeScript、JSON、HTTP、WebSocket、字段 key、命令名、品牌名、产品名和协议名等必要专有名词按原文保留。';
5
6
 
6
7
  export const TBD_ZH = '待补充';
7
8
 
8
9
  const CJK_RE = /[\u3400-\u9fff]/;
10
+ const LATIN_WORD_RE = /[A-Za-z][A-Za-z0-9+_.-]*/g;
9
11
 
10
12
  export function scalarZh(value, fallback = TBD_ZH) {
11
13
  if (value === null || value === undefined) {
@@ -19,14 +21,72 @@ export function hasSimplifiedChinese(value) {
19
21
  return CJK_RE.test(String(value ?? ''));
20
22
  }
21
23
 
24
+ export function englishWords(value) {
25
+ return String(value ?? '').match(LATIN_WORD_RE) ?? [];
26
+ }
27
+
28
+ export function isChineseLocale(locale) {
29
+ return normalizeOutputLocale(locale).startsWith('zh');
30
+ }
31
+
32
+ export function normalizeOutputLocale(locale, fallback = OPENPRD_FALLBACK_LOCALE) {
33
+ const text = String(locale ?? '').trim();
34
+ if (!text) {
35
+ return fallback;
36
+ }
37
+ const lower = text.toLowerCase();
38
+ if (lower === 'cn' || lower.startsWith('zh')) {
39
+ return 'zh-CN';
40
+ }
41
+ if (lower.startsWith('en')) {
42
+ return 'en';
43
+ }
44
+ return text;
45
+ }
46
+
47
+ export function detectPrimaryLanguage(values, fallback = OPENPRD_FALLBACK_LOCALE) {
48
+ const text = (Array.isArray(values) ? values : [values])
49
+ .map((value) => String(value ?? ''))
50
+ .join('\n');
51
+ const cjkCount = (text.match(CJK_RE) ?? []).length;
52
+ const latinCount = englishWords(text).length;
53
+ if (cjkCount > 0) {
54
+ return 'zh-CN';
55
+ }
56
+ if (latinCount >= 4) {
57
+ return 'en';
58
+ }
59
+ return normalizeOutputLocale(fallback);
60
+ }
61
+
62
+ export function isEnglishHeavyText(value) {
63
+ const text = String(value ?? '').trim();
64
+ if (!text || hasSimplifiedChinese(text)) {
65
+ return false;
66
+ }
67
+ return englishWords(text).length >= 4;
68
+ }
69
+
22
70
  export function preferSimplifiedChinese(value, fallback = TBD_ZH) {
23
71
  const text = scalarZh(value, '');
24
72
  return hasSimplifiedChinese(text) ? text : fallback;
25
73
  }
26
74
 
75
+ export function preferUserLanguage(value, locale = OPENPRD_FALLBACK_LOCALE, fallbacks = {}) {
76
+ const text = scalarZh(value, '');
77
+ if (text) {
78
+ return text;
79
+ }
80
+ const normalizedLocale = normalizeOutputLocale(locale);
81
+ if (!isChineseLocale(normalizedLocale) && fallbacks.en) {
82
+ return fallbacks.en;
83
+ }
84
+ return fallbacks.zh ?? fallbacks.default ?? TBD_ZH;
85
+ }
86
+
27
87
  export function languagePolicyLines() {
28
88
  return [
29
- '> 语言规则:默认用简体中文生成 PRD、spectasks 和用户可见说明;除 PRD、OpenPrd、OpenSpec、API、SDK、CLI、TypeScript、JSON、HTTP、WebSocket、字段 key、命令名、品牌名、产品名和协议名等必要专有名词外,其余内容优先写成简体中文。',
89
+ '> 语言规则:用户可见说明、PRD、spectasks 跟随用户当前主语言;无法判断时才使用简体中文兜底。PRD、OpenPrd、OpenSpec、API、SDK、CLI、TypeScript、JSON、HTTP、WebSocket、字段 key、命令名、品牌名、产品名和协议名等必要专有名词保留原文。',
30
90
  '',
31
91
  ];
32
92
  }
@@ -622,7 +622,7 @@ async function buildEvidenceManifest(ws, snapshot, options = {}) {
622
622
  ];
623
623
 
624
624
  if (changeId) {
625
- const changeRoot = path.join(ws.projectRoot, 'openprd', 'changes', changeId);
625
+ const changeRoot = path.join(ws.projectRoot, '.openprd', 'changes', changeId);
626
626
  candidateDefs.push(
627
627
  makeEvidenceCandidate({
628
628
  id: 'change-proposal',
package/src/loop.js CHANGED
@@ -550,7 +550,7 @@ async function syncLoopIsolationArtifacts(sourceRoot, targetRoot, options = {})
550
550
  LOOP_STATE,
551
551
  LOOP_BOOTSTRAP,
552
552
  RELEASE_LEDGER,
553
- options.change ? path.join('openprd', 'changes', options.change) : null,
553
+ options.change ? path.join('.openprd', 'changes', options.change) : null,
554
554
  ].filter(Boolean);
555
555
  for (const relativePath of relativePaths) {
556
556
  if (await copyRelativePath(sourceRoot, targetRoot, relativePath, { force: Boolean(options.force) })) {
package/src/openprd.js CHANGED
@@ -33,7 +33,8 @@ import { doctorOpenPrdAgentIntegration, setupOpenPrdAgentIntegration, updateOpen
33
33
  import { finishLoopWorkspace, initLoopWorkspace, nextLoopWorkspace, planLoopWorkspace, promptLoopWorkspace, runLoopWorkspace, statusLoopWorkspace, verifyLoopWorkspace } from './loop.js';
34
34
  import { timestamp } from './time.js';
35
35
  import { parseCommandArgs, usage } from './cli/args.js';
36
- import { printAcceptedSpecsResult, printAgentIntegrationResult, printBenchmarkResult, printBrainstormResult, printCaptureResult, printClarifyResult, printClassifyResult, printDesignStarterResult, printDevelopmentStandardsResult, printDiagramResult, printDiffResult, printDoctorResult, printFleetResult, printFreezeResult, printGrowthResult, printHandoffResult, printHistoryResult, printInitResult, printInterviewResult, printKnowledgeResult, printLearningResult, printLoopResult, printNextResult, printOpenPrdChangeActionResult, printOpenPrdChangesResult, printOpenSpecChangeValidationResult, printOpenSpecDiscoveryResult, printOpenSpecGenerateResult, printOpenSpecTaskResult, printPlaygroundResult, printQualityResult, printReleaseResult, printReviewResult, printRunResult, printSelfUpdateResult, printStandardsResult, printStatus, printSynthesizeResult, printUpgradeResult, printValidation, printVisualCompareResult, printVisualPrepareResult } from './cli/print.js';
36
+ import { runQualityCommand } from './cli/quality-commands.js';
37
+ import { printAcceptedSpecsResult, printAgentIntegrationResult, printBenchmarkResult, printBrainstormResult, printCanvasResult, printCaptureResult, printClarifyResult, printClassifyResult, printDesignStarterResult, printDevelopmentStandardsResult, printDiagramResult, printDiffResult, printDoctorResult, printFleetResult, printFreezeResult, printGrowthResult, printHandoffResult, printHistoryResult, printInitResult, printInterviewResult, printKnowledgeResult, printLearningResult, printLoopResult, printNextResult, printOpenPrdChangeActionResult, printOpenPrdChangesResult, printOpenSpecChangeValidationResult, printOpenSpecDiscoveryResult, printOpenSpecGenerateResult, printOpenSpecTaskResult, printPlaygroundResult, printQualityResult, printReleaseResult, printReviewResult, printRunResult, printSelfUpdateResult, printStandardsResult, printStatus, printSynthesizeResult, printUpgradeResult, printValidation, printVisualCompareResult, printVisualPrepareResult } from './cli/print.js';
37
38
  import { cjoin, exists, readJson, writeJson, writeText, writeYaml } from './fs-utils.js';
38
39
  import { diagramWorkspace } from './diagram-workspace.js';
39
40
  import { createOpenSpecDiscoveryWorkspace } from './discovery.js';
@@ -64,6 +65,7 @@ import {
64
65
  import { brainstormWorkspace } from './brainstorm.js';
65
66
  import { buildBrainstormPresentationTemplatePayload, brainstormPresentationWorkspace } from './brainstorm-presentation.js';
66
67
  import { designStarterWorkspace } from './design-starter.js';
68
+ import { canvasWorkspace } from './canvas-workspace.js';
67
69
  import { buildReviewPresentationTemplatePayload, reviewPresentationWorkspace } from './review-presentation.js';
68
70
  import { analyzeWorkspaceRegistryHygiene } from './registry-hygiene.js';
69
71
  import { syncSessionBindingFromChange } from './session-binding.js';
@@ -74,6 +76,22 @@ import { captureWorkspace, clarifyWorkspace, classifyWorkspace, computeWorkspace
74
76
  import { appendDecision, appendProgress, appendVerification, appendWorkflowEvent, buildCurrentStateSnapshot, buildWorkflowTaskGraph, computeWorkspaceDigest, CORE_TEMPLATE_FILES, ensureWorkspaceSkeleton, isSupportedProductType, loadCurrentLaneSnapshot, loadLatestVersionSnapshot, loadWorkspace, migrateWorkspaceSkeleton, normalizeVersionId, persistWorkspaceCurrentState, readVersionIndex, resolveActiveTemplatePack, resolveCurrentProductType, validateWorkspace } from './workspace-core.js';
75
77
  import { readWorkspaceRegistry } from './workspace-registry.js';
76
78
 
79
+ function buildInitTemplatePackGuidance(templatePack, options = {}) {
80
+ const label = formatTemplatePackDisplay(templatePack, { fallback: '通用产品或工程场景' });
81
+ if (options.explicit) {
82
+ return {
83
+ source: 'explicit',
84
+ templatePack,
85
+ message: `已按指定场景模板使用${label}起步;后续需求澄清或场景分类时,仍可按用户确认调整产品场景。`,
86
+ };
87
+ }
88
+ return {
89
+ source: 'default',
90
+ templatePack,
91
+ message: `未指定场景模板,已使用${label}起步;后续需求澄清或场景分类时,可再锁定为个人消费者、企业服务或 Agent 场景。`,
92
+ };
93
+ }
94
+
77
95
  async function initWorkspace(projectRoot, options) {
78
96
  const ws = await ensureWorkspaceSkeleton(projectRoot, options);
79
97
  const workspace = await loadWorkspace(projectRoot);
@@ -131,12 +149,16 @@ async function initWorkspace(projectRoot, options) {
131
149
  templatePack: currentState.templatePack,
132
150
  projectRoot,
133
151
  });
152
+ const templatePackGuidance = buildInitTemplatePackGuidance(currentState.templatePack, {
153
+ explicit: Boolean(options.templatePack),
154
+ });
134
155
  await appendProgress(workspace, [
135
156
  `已初始化工作区: ${workspace.workspaceRoot}。`,
136
157
  `场景模板: ${formatTemplatePackDisplay(currentState.templatePack, { fallback: '待确认' })}。`,
158
+ templatePackGuidance.message,
137
159
  ]);
138
160
 
139
- return { ws: workspace, created: ws.created, currentState, standards, quality, growth, agentIntegration };
161
+ return { ws: workspace, created: ws.created, currentState, templatePackGuidance, standards, quality, growth, agentIntegration };
140
162
  }
141
163
 
142
164
  async function setupAgentIntegrationWorkspace(projectRoot, options = {}) {
@@ -171,7 +193,16 @@ async function setupAgentIntegrationWorkspace(projectRoot, options = {}) {
171
193
  platform: options.platform,
172
194
  hookProfile: options.hookProfile,
173
195
  });
174
- return { ...agentIntegration, initialized: false, migration, standards, quality, growth };
196
+ return {
197
+ ...agentIntegration,
198
+ ok: agentIntegration.ok && migration.ok,
199
+ initialized: false,
200
+ migration,
201
+ standards,
202
+ quality,
203
+ growth,
204
+ errors: [...(agentIntegration.errors ?? []), ...(migration.errors ?? [])],
205
+ };
175
206
  }
176
207
 
177
208
  async function updateAgentIntegrationWorkspace(projectRoot, options = {}) {
@@ -189,7 +220,15 @@ async function updateAgentIntegrationWorkspace(projectRoot, options = {}) {
189
220
  platform: options.platform,
190
221
  hookProfile: options.hookProfile,
191
222
  });
192
- return { ...agentIntegration, migration, standards, quality, growth };
223
+ return {
224
+ ...agentIntegration,
225
+ ok: agentIntegration.ok && migration.ok,
226
+ migration,
227
+ standards,
228
+ quality,
229
+ growth,
230
+ errors: [...(agentIntegration.errors ?? []), ...(migration.errors ?? [])],
231
+ };
193
232
  }
194
233
 
195
234
  async function doctorWorkspace(projectRoot, options = {}) {
@@ -897,138 +936,34 @@ export async function main(argv = process.argv.slice(2)) {
897
936
  return result.ok ? 0 : 1;
898
937
  }
899
938
 
900
- if (command === 'standards') {
901
- const result = flags.init
902
- ? await initStandardsWorkspace(projectPath, { force: flags.force })
903
- : flags.classifyExternal !== null
904
- ? await classifyExternalReferenceWorkspace(projectPath, { externalReference: flags.classifyExternal })
905
- : await checkStandardsWorkspace(projectPath);
906
- printStandardsResult(result, flags.json);
907
- return result.ok ? 0 : 1;
908
- }
909
-
910
- if (command === 'quality') {
911
- const result = await qualityWorkspace(projectPath, {
912
- init: flags.init,
913
- verify: flags.verify,
914
- report: flags.report,
915
- html: flags.html,
916
- learn: Boolean(flags.learn),
917
- review: flags.review,
918
- from: flags.from,
919
- force: flags.force,
920
- });
921
- printQualityResult(result, flags.json);
922
- return result.ok ? 0 : 1;
923
- }
924
-
925
- if (command === 'knowledge') {
926
- const subcommand = positionals[0] ?? 'candidates';
927
- const firstArgIsProjectPath = !flags.path && positionals.length > 1 && await isDirectoryPath(positionals[1]);
928
- const knowledgeProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[1] : process.cwd()));
929
- let result;
930
- if (subcommand === 'candidates' || subcommand === 'list') {
931
- result = await listKnowledgeCandidates(knowledgeProjectPath, {
932
- status: flags.status ?? 'pending-review',
933
- });
934
- } else if (subcommand === 'reject') {
935
- result = await rejectKnowledgeCandidate(knowledgeProjectPath, {
936
- id: flags.id ?? positionals[1] ?? null,
937
- reason: flags.reason ?? flags.notes,
938
- });
939
- } else if (subcommand === 'archive') {
940
- result = await archiveKnowledgeCandidate(knowledgeProjectPath, {
941
- id: flags.id ?? positionals[1] ?? null,
942
- reason: flags.reason ?? flags.notes,
943
- });
944
- } else if (subcommand === 'restore') {
945
- result = await restoreKnowledgeCandidate(knowledgeProjectPath, {
946
- id: flags.id ?? positionals[1] ?? null,
947
- });
948
- } else {
949
- console.log('Usage: openprd knowledge <candidates|reject|archive|restore> [path-or-id] [--status <pending-review|all|rejected|archived|promoted|merged>] [--id <candidate-id>] [--reason <text>] [--json]');
950
- return 1;
951
- }
952
- printKnowledgeResult(result, flags.json);
953
- return result.ok ? 0 : 1;
954
- }
955
-
956
- if (command === 'visual-prepare') {
957
- const result = await visualPrepareWorkspace(projectPath, {
958
- reference: flags.reference,
959
- grid: flags.grid,
960
- boxes: flags.boxes,
961
- include: flags.include,
939
+ if (command === 'canvas') {
940
+ const result = await canvasWorkspace(projectPath, {
941
+ thread: flags.thread,
942
+ threadTitle: flags.threadTitle,
943
+ conversationTitle: flags.conversationTitle,
944
+ session: flags.session,
945
+ sessionTitle: flags.sessionTitle,
946
+ host: flags.host,
947
+ port: flags.port,
948
+ daemon: flags.daemon,
949
+ stop: flags.stop,
950
+ bridgeOutbox: flags.bridgeOutbox,
951
+ bridgeSent: flags.bridgeSent,
952
+ bridgeFailed: flags.bridgeFailed,
953
+ claim: flags.claim,
962
954
  id: flags.id,
963
- title: flags.title,
964
- out: flags.out,
965
- });
966
- printVisualPrepareResult(result, flags.json);
967
- return result.ok ? 0 : 1;
968
- }
969
-
970
- if (command === 'visual-compare') {
971
- const result = await visualCompareWorkspace(projectPath, {
972
- reference: flags.reference,
973
- actual: flags.actual,
974
- before: flags.before,
975
- after: flags.after,
976
- board: flags.board,
977
- out: flags.out,
978
- format: flags.format,
979
- quality: flags.quality,
980
- maxPanelWidth: flags.maxPanelWidth,
981
- referenceLabel: flags.referenceLabel,
982
- actualLabel: flags.actualLabel,
983
- });
984
- printVisualCompareResult(result, flags.json);
985
- return result.ok ? 0 : 1;
986
- }
987
-
988
- if (command === 'design-starter') {
989
- const result = await designStarterWorkspace(projectPath, {
990
- starter: flags.starter,
991
- out: flags.out,
992
- title: flags.title,
993
- brief: flags.brief,
994
- sections: flags.sections,
995
- noExternalFacts: flags.noExternalFacts,
996
- noBrandAssets: flags.noBrandAssets,
997
- noRealImages: flags.noRealImages,
998
- force: flags.force,
955
+ reason: flags.reason,
956
+ open: flags.open,
957
+ openTarget: flags.openTarget,
958
+ dryRun: flags.dryRun,
999
959
  });
1000
- printDesignStarterResult(result, flags.json);
960
+ printCanvasResult(result, flags.json);
1001
961
  return result.ok ? 0 : 1;
1002
962
  }
1003
963
 
1004
- if (command === 'grow') {
1005
- const firstArgIsProjectPath = !flags.path && positionals.length > 0 && await isDirectoryPath(positionals[0]);
1006
- const growthProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[0] : process.cwd()));
1007
- let result;
1008
- if (flags.init) {
1009
- result = await initGrowthWorkspace(growthProjectPath);
1010
- } else if (flags.apply) {
1011
- result = await applyGrowthCandidateWorkspace(growthProjectPath, { id: flags.id });
1012
- } else if (flags.reject) {
1013
- result = await rejectGrowthCandidateWorkspace(growthProjectPath, { id: flags.id, notes: flags.notes });
1014
- } else if (flags.check) {
1015
- result = await checkGrowthWorkspace(growthProjectPath);
1016
- } else {
1017
- result = await reviewGrowthWorkspace(growthProjectPath);
1018
- }
1019
- printGrowthResult(result, flags.json);
1020
- return result.ok ? 0 : 1;
1021
- }
1022
-
1023
- if (command === 'dev-check') {
1024
- const firstArgIsProjectPath = !flags.path && positionals.length > 1 && await isDirectoryPath(positionals[0]);
1025
- const devProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[0] : process.cwd()));
1026
- const files = flags.path
1027
- ? positionals
1028
- : (firstArgIsProjectPath ? positionals.slice(1) : positionals);
1029
- const result = await checkDevelopmentStandardsWorkspace(devProjectPath, { files });
1030
- printDevelopmentStandardsResult(result, flags.json);
1031
- return result.ok ? 0 : 1;
964
+ const qualityCommandExit = await runQualityCommand(command, { projectPath, flags, positionals });
965
+ if (qualityCommandExit !== null) {
966
+ return qualityCommandExit;
1032
967
  }
1033
968
 
1034
969
  if (command === 'benchmark') {
@@ -1171,6 +1106,7 @@ export async function main(argv = process.argv.slice(2)) {
1171
1106
  version: flags.version,
1172
1107
  digest: flags.digest,
1173
1108
  workUnit: flags.workUnit,
1109
+ direction: flags.direction,
1174
1110
  });
1175
1111
  printReviewResult(result, flags.json);
1176
1112
  return result.ok ? 0 : 1;
@@ -1454,6 +1390,7 @@ export {
1454
1390
  fleetWorkspace,
1455
1391
  backfillWorkUnitsWorkspace,
1456
1392
  runWorkspace,
1393
+ canvasWorkspace,
1457
1394
  initLoopWorkspace,
1458
1395
  planLoopWorkspace,
1459
1396
  statusLoopWorkspace,
@@ -47,7 +47,7 @@ export async function resolveOpenSpecChangeId(projectRoot, requestedChange) {
47
47
  return changeDirs[0];
48
48
  }
49
49
  if (changeDirs.length === 0) {
50
- throw new Error('openprd/changes 下没有找到 OpenPrd change。');
50
+ throw new Error('.openprd/changes 下没有找到 OpenPrd change。');
51
51
  }
52
52
  throw new Error(`找到多个 OpenPrd change;请传入 --change <id>。已找到: ${changeDirs.join(', ')}`);
53
53
  }
@@ -60,7 +60,7 @@ function extractProposalCapabilities(text) {
60
60
  return capabilities;
61
61
  }
62
62
 
63
- const SPEC_SECTION_HEADING_RE = /^##\s+(?:(ADDED|MODIFIED|REMOVED)\s+Requirements|(?:新增|修改|移除)需求)\s*$/gim;
63
+ const SPEC_SECTION_HEADING_RE = /^##\s+(?:(ADDED|MODIFIED|REMOVED|NEW|UPDATED|CHANGED)\s+Requirements|(?:新增|修改|移除)需求)\s*$/gim;
64
64
  const REQUIREMENT_HEADING_RE = /^###\s+(?:Requirement|需求)[::]\s*(.+)$/gm;
65
65
  const SCENARIO_HEADING_RE = /^####\s+(?:Scenario|场景)[::]\s*(.+)$/gm;
66
66
  const WHEN_STEP_RE = /^-\s+\*\*(?:WHEN|当)\*\*/im;
@@ -1,12 +1,11 @@
1
1
  export const OPENPRD_DISCOVERY_DIR = ['.openprd', 'discovery'];
2
2
  export const OPENPRD_DISCOVERY_CONFIG_PATH = ['.openprd', 'discovery', 'config.json'];
3
- export const LEGACY_OPENSPEC_DISCOVERY_DIR = ['.openspec', 'discovery'];
4
- export const LEGACY_OPENSPEC_DISCOVERY_CONFIG_PATH = ['.openspec', 'discovery', 'config.json'];
5
- export const OPENSPEC_DISCOVERY_CONFIG_PATH = LEGACY_OPENSPEC_DISCOVERY_CONFIG_PATH;
6
- export const OPENPRD_CHANGE_ROOT = ['openprd', 'changes'];
7
- export const LEGACY_OPENSPEC_CHANGE_ROOT = ['openspec', 'changes'];
8
- export const OPENPRD_ACCEPTED_SPEC_ROOT = ['openprd', 'specs'];
9
- export const OPENPRD_ARCHIVE_CHANGE_ROOT = ['openprd', 'archive', 'changes'];
3
+ export const OPENPRD_CHANGE_ROOT = ['.openprd', 'changes'];
4
+ export const LEGACY_OPENPRD_CHANGE_ROOT = ['openprd', 'changes'];
5
+ export const OPENPRD_ACCEPTED_SPEC_ROOT = ['.openprd', 'specs'];
6
+ export const LEGACY_OPENPRD_ACCEPTED_SPEC_ROOT = ['openprd', 'specs'];
7
+ export const OPENPRD_ARCHIVE_CHANGE_ROOT = ['.openprd', 'archive', 'changes'];
8
+ export const LEGACY_OPENPRD_ARCHIVE_CHANGE_ROOT = ['openprd', 'archive', 'changes'];
10
9
  export const OPENSPEC_TASK_MAX_ITEMS_PER_FILE = 25;
11
10
  export const OPENSPEC_TASK_FILE_PATTERN = /^tasks(?:-\d{3})?\.md$/;
12
11
  export const OPENSPEC_TASK_ID_PATTERN = /^T\d{3}\.\d+$/;
@@ -96,9 +96,9 @@ function resolveTaskSelection(state, options = {}) {
96
96
 
97
97
  if (!task) {
98
98
  if (requestedId) {
99
- throw new Error(`Unknown OpenSpec task: ${requestedId}`);
99
+ throw new Error(`Unknown OpenPrd task: ${requestedId}`);
100
100
  }
101
- throw new Error('No ready OpenSpec task is available.');
101
+ throw new Error('No ready OpenPrd task is available.');
102
102
  }
103
103
 
104
104
  return task;