@openprd/cli 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/.openprd/benchmarks/index.md +16 -0
  2. package/.openprd/benchmarks/sources.yaml +53 -1
  3. package/.openprd/config.yaml +10 -0
  4. package/.openprd/design/README.md +47 -0
  5. package/.openprd/design/README_EN.md +25 -0
  6. package/.openprd/design/active/asset-spec.md +15 -0
  7. package/.openprd/design/active/direction-plan.md +7 -0
  8. package/.openprd/design/active/facts-sheet.md +13 -0
  9. package/.openprd/design/active/image-preflight.md +9 -0
  10. package/.openprd/design/active/selected-direction.md +11 -0
  11. package/.openprd/design/anti-slop.md +16 -0
  12. package/.openprd/design/assets/dark-mesh.svg +14 -0
  13. package/.openprd/design/assets/paper-grid.svg +14 -0
  14. package/.openprd/design/assets/surface-presets.md +21 -0
  15. package/.openprd/design/assets/workbench-grid.svg +16 -0
  16. package/.openprd/design/checklists/ui-quality-gate.md +22 -0
  17. package/.openprd/design/components/component-catalog.md +42 -0
  18. package/.openprd/design/layouts/layout-catalog.json +45 -0
  19. package/.openprd/design/lenses/frontend-lenses.md +31 -0
  20. package/.openprd/design/recipes/content-experience.md +26 -0
  21. package/.openprd/design/recipes/operational-tool.md +25 -0
  22. package/.openprd/design/recipes/product-launch.md +26 -0
  23. package/.openprd/design/templates/README.md +36 -0
  24. package/.openprd/design/templates/README_EN.md +31 -0
  25. package/.openprd/design/templates/content-home.html +415 -0
  26. package/.openprd/design/templates/ops-dashboard.html +340 -0
  27. package/.openprd/design/templates/product-launch.html +360 -0
  28. package/.openprd/design/themes/theme-catalog.json +77 -0
  29. package/.openprd/engagements/active/prd.md +111 -100
  30. package/.openprd/schema/prd.schema.yaml +25 -0
  31. package/.openprd/templates/base/intake.md +17 -0
  32. package/.openprd/templates/base/prd.md +31 -6
  33. package/AGENTS.md +10 -7
  34. package/README.md +26 -28
  35. package/README_EN.md +3 -3
  36. package/package.json +41 -2
  37. package/scripts/openprd-codex-isolated-worker.mjs +799 -0
  38. package/skills/openprd-benchmark-router/SKILL.md +5 -0
  39. package/skills/openprd-frontend-design/SKILL.md +161 -0
  40. package/skills/openprd-frontend-design/references/design-asset-contract.md +34 -0
  41. package/skills/openprd-frontend-design/references/direction-engine.md +46 -0
  42. package/skills/openprd-harness/SKILL.md +37 -20
  43. package/skills/openprd-learning-review/SKILL.md +2 -1
  44. package/skills/openprd-learning-review/references/style-packs/xianxia-cultivation.prompt.md +1 -1
  45. package/skills/openprd-quality/SKILL.md +5 -3
  46. package/skills/openprd-requirement-intake/SKILL.md +27 -5
  47. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -3
  48. package/skills/openprd-requirement-intake/references/routing-rubric.md +9 -5
  49. package/skills/openprd-requirement-intake/references/startup-validation-lens.md +108 -0
  50. package/skills/openprd-router/SKILL.md +13 -5
  51. package/skills/openprd-shared/SKILL.md +38 -25
  52. package/src/agent-integration.js +120 -69
  53. package/src/brainstorm-artifacts.js +1805 -0
  54. package/src/brainstorm-presentation.js +233 -0
  55. package/src/brainstorm.js +766 -0
  56. package/src/cli/args.js +51 -3
  57. package/src/cli/doctor-print.js +34 -8
  58. package/src/cli/print.js +6 -0
  59. package/src/cli/quality-print.js +79 -3
  60. package/src/cli/run-print.js +43 -1
  61. package/src/cli/shared-print.js +33 -2
  62. package/src/cli/workflow-print.js +21 -0
  63. package/src/codex-hook-runner-template.mjs +838 -58
  64. package/src/design-starter-support.js +462 -0
  65. package/src/design-starter.js +1207 -0
  66. package/src/diagram-core.js +384 -151
  67. package/src/html-artifacts.js +403 -50
  68. package/src/knowledge.js +291 -32
  69. package/src/learning-review.js +3 -3
  70. package/src/loop.js +839 -101
  71. package/src/openprd.js +86 -1
  72. package/src/openspec/change-validate.js +1 -0
  73. package/src/prd-core.js +119 -2
  74. package/src/quality-html-artifact.js +30 -5
  75. package/src/quality-learning.js +50 -6
  76. package/src/quality.js +22 -12
  77. package/src/run-harness.js +191 -29
  78. package/src/self-update.js +301 -4
  79. package/src/standards.js +46 -5
  80. package/src/visual-prepare.js +940 -0
  81. package/src/workspace-core.js +175 -0
  82. package/src/workspace-workflow.js +307 -7
  83. package/.openprd/benchmarks/evidence/milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code.md +0 -14
  84. package/.openprd/benchmarks/evidence/nolanlawson-com-using-ai-to-write-better-code-more-slowly.md +0 -14
  85. package/.openprd/discovery/config.json +0 -35
  86. package/.openprd/engagements/active/flows.md +0 -35
  87. package/.openprd/engagements/active/handoff.md +0 -16
  88. package/.openprd/engagements/active/review.html +0 -61
  89. package/.openprd/engagements/active/roles.md +0 -22
  90. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +0 -23
  91. package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +0 -18
  92. package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +0 -18
  93. package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +0 -18
  94. package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +0 -18
  95. package/.openprd/exports/.gitkeep +0 -0
  96. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +0 -78
  97. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +0 -129
  98. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +0 -41
  99. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +0 -14
  100. package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +0 -49
  101. package/.openprd/knowledge/index.json +0 -47
  102. package/.openprd/reviews/v0001.html +0 -1322
  103. package/.openprd/reviews/v0002.html +0 -1150
  104. package/.openprd/reviews/v0003.html +0 -1150
  105. package/.openprd/reviews/v0004.html +0 -1150
  106. package/.openprd/reviews/v0005.html +0 -1150
  107. package/.openprd/sessions/.gitkeep +0 -0
  108. package/.openprd/state/changes.json +0 -27
  109. package/.openprd/state/current.json +0 -505
  110. package/.openprd/state/release-ledger.json +0 -387
  111. package/.openprd/state/version-index.json +0 -67
  112. package/.openprd/state/versions/.gitkeep +0 -0
  113. package/.openprd/state/versions/v0001.json +0 -121
  114. package/.openprd/state/versions/v0001.md +0 -161
  115. package/.openprd/state/versions/v0002.json +0 -264
  116. package/.openprd/state/versions/v0002.md +0 -183
  117. package/.openprd/state/versions/v0003.json +0 -269
  118. package/.openprd/state/versions/v0003.md +0 -188
  119. package/.openprd/state/versions/v0004.json +0 -274
  120. package/.openprd/state/versions/v0004.md +0 -193
  121. package/.openprd/state/versions/v0005.json +0 -299
  122. package/.openprd/state/versions/v0005.md +0 -189
  123. package/docs/assets/openprd-capability-overview-en.png +0 -0
  124. package/docs/assets/openprd-capability-overview-zh.png +0 -0
  125. package/docs/assets/openprd-learning-html.png +0 -0
  126. package/docs/assets/openprd-quality-html.png +0 -0
  127. package/docs/assets/openprd-requirement-routing-en.png +0 -0
  128. package/docs/assets/openprd-requirement-routing-en.svg +0 -102
  129. package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
  130. package/docs/assets/openprd-requirement-routing-zh.png +0 -0
  131. package/docs/assets/openprd-requirement-routing-zh.svg +0 -102
  132. package/docs/assets/openprd-review-html.png +0 -0
  133. package/docs/assets/openprd-scenario-overview.png +0 -0
  134. package/docs/assets/openprd-scenario-overview.svg +0 -114
  135. package/docs/assets/openprd-self-evolving-mechanisms-en.png +0 -0
  136. package/docs/assets/openprd-self-evolving-mechanisms-zh.png +0 -0
  137. package/docs/assets/openprd-visual-compare-case-study-en.png +0 -0
  138. package/docs/assets/openprd-visual-compare-case-study-zh.png +0 -0
package/src/cli/args.js CHANGED
@@ -19,7 +19,7 @@
19
19
  */
20
20
  function parseCommandArgs(argv) {
21
21
  const args = [...argv];
22
- const flags = { json: false, force: false, fix: false, open: false, append: false, init: false, check: false, review: false, reject: false, resume: false, advance: false, verify: false, evidenceRequired: false, next: false, generate: false, validate: false, apply: false, archive: false, activate: false, close: false, keep: false, write: false, dryRun: false, repairAgent: false, fleet: false, updateOpenprd: false, backfillWorkUnits: false, syncRegistry: false, setupMissing: false, doctor: false, context: false, recordHook: false, hookInject: false, plan: false, prompt: false, loopRun: false, finish: false, commit: false, html: false, template: false, failOnViolation: false, mark: null, type: 'architecture', mode: 'auto', input: null, field: null, value: null, set: null, jsonFile: null, artifactMarkdown: null, contentJson: null, presentation: null, source: null, reference: null, actual: null, before: null, after: null, board: null, out: null, format: null, quality: null, maxPanelWidth: null, referenceLabel: null, actualLabel: null, classifyExternal: null, maxIterations: null, maxDepth: null, include: null, exclude: null, report: null, item: null, id: null, status: null, claim: null, notes: null, reason: null, confidence: null, threshold: null, change: null, tools: 'all', hookProfile: null, templatePack: null, target: 'openprd', targetRoot: null, path: null, productType: null, title: null, owner: null, problem: null, whyNow: null, evidence: null, from: null, to: null, version: null, digest: null, workUnit: null, event: null, risk: null, outcome: null, preview: null, learn: null, genre: null, style: null, topic: null, enable: false, disable: false, agent: 'codex', agentCommand: null, message: null };
22
+ const flags = { json: false, force: false, fix: false, open: false, append: false, init: false, check: false, review: false, reject: false, resume: false, advance: false, verify: false, evidenceRequired: false, next: false, generate: false, validate: false, apply: false, archive: false, activate: false, close: false, keep: false, write: false, dryRun: false, repairAgent: false, allowDirtyMain: false, fleet: false, updateOpenprd: false, backfillWorkUnits: false, syncRegistry: false, setupMissing: false, doctor: false, context: false, recordHook: false, hookInject: false, plan: false, prompt: false, loopRun: false, finish: false, commit: false, html: false, template: false, failOnViolation: false, noExternalFacts: false, noBrandAssets: false, noRealImages: false, mark: null, type: 'architecture', mode: 'auto', input: null, field: null, value: null, set: null, jsonFile: null, artifactMarkdown: null, contentJson: null, presentation: null, source: null, reference: null, actual: null, before: null, after: null, board: null, grid: null, boxes: null, out: null, format: null, quality: null, maxPanelWidth: null, referenceLabel: null, actualLabel: null, classifyExternal: null, maxIterations: null, maxDepth: null, include: null, exclude: null, report: null, item: null, id: null, status: null, claim: null, notes: null, reason: null, confidence: null, threshold: null, change: null, starter: null, sections: null, brief: null, tools: 'all', hookProfile: null, templatePack: null, target: 'openprd', targetRoot: null, path: null, productType: null, title: null, owner: null, problem: null, whyNow: null, evidence: null, from: null, to: null, version: null, digest: null, workUnit: null, event: null, risk: null, outcome: null, preview: null, learn: null, genre: null, style: null, topic: null, enable: false, disable: false, agent: 'codex', agentCommand: null, worktree: null, branch: null, message: null };
23
23
  const positionals = [];
24
24
 
25
25
  while (args.length > 0) {
@@ -120,6 +120,10 @@ function parseCommandArgs(argv) {
120
120
  flags.repairAgent = true;
121
121
  continue;
122
122
  }
123
+ if (arg === '--allow-dirty-main') {
124
+ flags.allowDirtyMain = true;
125
+ continue;
126
+ }
123
127
  if (arg === '--fleet') {
124
128
  flags.fleet = true;
125
129
  continue;
@@ -220,6 +224,14 @@ function parseCommandArgs(argv) {
220
224
  flags.agentCommand = args.shift() ?? null;
221
225
  continue;
222
226
  }
227
+ if (arg === '--worktree') {
228
+ flags.worktree = args.shift() ?? null;
229
+ continue;
230
+ }
231
+ if (arg === '--branch') {
232
+ flags.branch = args.shift() ?? null;
233
+ continue;
234
+ }
223
235
  if (arg === '--product-type' || arg === '-P') {
224
236
  flags.productType = args.shift() ?? null;
225
237
  continue;
@@ -288,6 +300,14 @@ function parseCommandArgs(argv) {
288
300
  flags.board = args.shift() ?? null;
289
301
  continue;
290
302
  }
303
+ if (arg === '--grid') {
304
+ flags.grid = args.shift() ?? null;
305
+ continue;
306
+ }
307
+ if (arg === '--boxes') {
308
+ flags.boxes = args.shift() ?? null;
309
+ continue;
310
+ }
291
311
  if (arg === '--out') {
292
312
  flags.out = args.shift() ?? null;
293
313
  continue;
@@ -372,6 +392,30 @@ function parseCommandArgs(argv) {
372
392
  flags.change = args.shift() ?? null;
373
393
  continue;
374
394
  }
395
+ if (arg === '--starter') {
396
+ flags.starter = args.shift() ?? null;
397
+ continue;
398
+ }
399
+ if (arg === '--brief') {
400
+ flags.brief = args.shift() ?? null;
401
+ continue;
402
+ }
403
+ if (arg === '--sections') {
404
+ flags.sections = args.shift() ?? null;
405
+ continue;
406
+ }
407
+ if (arg === '--no-external-facts') {
408
+ flags.noExternalFacts = true;
409
+ continue;
410
+ }
411
+ if (arg === '--no-brand-assets') {
412
+ flags.noBrandAssets = true;
413
+ continue;
414
+ }
415
+ if (arg === '--no-real-images') {
416
+ flags.noRealImages = true;
417
+ continue;
418
+ }
375
419
  if (arg === '--title') {
376
420
  flags.title = args.shift() ?? null;
377
421
  continue;
@@ -478,26 +522,30 @@ function usage() {
478
522
  ' openprd init [path] [--template-pack <base|consumer|b2b|agent>] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--force]',
479
523
  ' openprd setup [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--force] [--json]',
480
524
  ' openprd update [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--force] [--json]',
481
- ' openprd self-update [--dry-run] [--json]',
525
+ ' openprd self-update [--check] [--dry-run] [--json]',
482
526
  ' openprd upgrade [path] [--fleet] [--dry-run] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--max-depth <n>] [--include <csv>] [--exclude <csv>] [--report <file>] [--force] [--json]',
483
527
  ' openprd doctor [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--fix] [--json]',
484
528
  ' openprd fleet <root> [--dry-run|--doctor|--update-openprd|--backfill-work-units|--sync-registry|--setup-missing] [--hook-profile <lite|guarded|full>] [--max-depth <n>] [--include <csv>] [--exclude <csv>] [--report <file>] [--json]',
485
529
  ' openprd run [path] [--context|--verify|--record-hook --event <name> --risk <level> --outcome <text> --preview <text>] [--message <text>] [--json]',
486
- ' openprd loop [path] [--init|--plan|--next|--prompt|--run|--verify|--finish] [--change <id>] [--item <task-id-or-handle>] [--agent <codex|claude>] [--agent-command <cmd>] [--repair-agent] [--commit] [--dry-run] [--message <text>] [--json]',
530
+ ' openprd loop [path] [--init|--plan|--next|--prompt|--run|--verify|--finish] [--change <id>] [--item <task-id-or-handle>] [--agent <codex|claude>] [--agent-command <cmd>] [--repair-agent] [--worktree <path>] [--branch <name>] [--allow-dirty-main] [--commit] [--dry-run] [--message <text>] [--json]',
487
531
  ' openprd classify [path] <consumer|b2b|agent>',
488
532
  ' openprd clarify [path] [--mode <auto|inline|inline-with-checklist>] [--json]',
489
533
  ' openprd capture [path] (--field <section.path> --value <text|json> | --json-file <answers.json> | --artifact-markdown <artifact.md>) [--source <user-confirmed|project-derived|agent-inferred|agent-normalized>] [--append] [--json]',
490
534
  ' openprd interview [path] [--product-type <consumer|b2b|agent>]',
491
535
  ' openprd playground [path] [--open] [--json]',
536
+ ' openprd brainstorm [path] [--topic <text>] [--open] [--json]',
492
537
  ' openprd learn [path] [--topic <text>] [--genre <internet-product|scientific|fairy-tale|web-novel|xianxia>] [--style <substyle>] [--source <workspace|docs|loop|all>] [--content-json <file>] [--open] [--enable|--disable] [--json]',
493
538
  ' openprd quality [path] [--init|--verify|--report --html|--learn [--review] --from <report-id-or-json-or-diagnostics-or-turn-state>] [--force] [--json]',
494
539
  ' openprd knowledge <candidates|reject|archive|restore> [path-or-id] [--status <pending-review|all|rejected|archived|promoted|merged>] [--id <candidate-id>] [--reason <text>] [--json]',
540
+ ' openprd visual-prepare [path] --reference <effect-image> ((--grid <columns>x<rows>) | (--boxes <plan.json>)) [--include <csv>] [--id <reference-set-id>] [--title <text>] [--out <dir>] [--json]',
495
541
  ' openprd visual-compare [path] ((--reference <effect-image> --actual <screenshot-image>) | (--before <before-screenshot> --after <after-screenshot>) | --board <board.json>) [--out <file.jpg>] [--format <jpg|png|webp>] [--quality <1..100>] [--max-panel-width <px>] [--json]',
542
+ ' openprd design-starter [path] [--starter <content-home|product-launch|ops-dashboard>] [--out <index.html>] [--title <name>] [--brief <text>] [--sections <a|b|c>] [--no-external-facts] [--no-brand-assets] [--no-real-images] [--force] [--json]',
496
543
  ' openprd dev-check [path] <file...> [--json]',
497
544
  ' openprd grow [path] [--review|--apply --id <candidate-id>|--reject --id <candidate-id>|--init|--check] [--notes <text>] [--json]',
498
545
  ' openprd benchmark <add|observe|list|approve|verify> [target-or-id] [path-for-list-or-verify] [--path <project>] [--notes <text>] [--threshold <n>] [--id <benchmark-id>] [--json]',
499
546
  ' openprd synthesize [path] [--title <text>] [--owner <text>] [--problem <text>] [--why-now <text>] [--work-unit <id>] [--target-root <path>] [--open] [--json]',
500
547
  ' openprd review [path] [--open] [--mark <pending-confirmation|confirmed|needs-revision>] [--version <id>] [--digest <sha256>] [--work-unit <id>] [--notes <text>] [--json]',
548
+ ' openprd brainstorm-presentation [path] [--template] [--presentation <json>] [--write] [--fail-on-violation] [--json]',
501
549
  ' openprd review-presentation [path] [--template] [--version <id>] [--presentation <json>] [--write] [--fail-on-violation] [--json]',
502
550
  ' openprd diagram [path] [--type <architecture|product-flow>] [--input <contract.json>] [--mark <pending-confirmation|confirmed|needs-revision>] [--open] [--json]',
503
551
  ' openprd diff [path] [--from <version>] [--to <version>]',
@@ -200,17 +200,32 @@ function printSelfUpdateResult(result, json) {
200
200
  return;
201
201
  }
202
202
 
203
- const status = result.dryRun
204
- ? '预演'
205
- : result.ok
206
- ? '完成'
207
- : result.skipped
208
- ? '已跳过'
209
- : '失败';
203
+ const status = result.checkOnly
204
+ ? result.ok ? '检查完成' : '检查失败'
205
+ : result.dryRun
206
+ ? '预演'
207
+ : result.ok
208
+ ? '完成'
209
+ : result.skipped
210
+ ? '已跳过'
211
+ : '失败';
210
212
  console.log(`OpenPrd self-update: ${status}`);
211
213
  console.log(`当前版本: ${result.package?.version ?? 'unknown'}`);
214
+ if (result.publishedVersion) {
215
+ console.log(`已发布版本: ${result.publishedVersion}`);
216
+ }
217
+ if (result.comparison === 'behind') {
218
+ console.log('版本关系: 当前安装版本落后于 npm 已发布版本');
219
+ } else if (result.comparison === 'same') {
220
+ console.log('版本关系: 当前安装版本已经是 npm 最新版本');
221
+ } else if (result.comparison === 'ahead') {
222
+ console.log('版本关系: 当前安装版本高于 npm 已发布版本');
223
+ }
212
224
  console.log(`安装源: ${result.source}`);
213
- console.log(`计划命令: ${result.installCommand.display}`);
225
+ if (result.versionCheck?.command?.display) {
226
+ console.log(`检查命令: ${result.versionCheck.command.display}`);
227
+ }
228
+ console.log(`计划命令: ${result.installCommand?.display ?? 'N/A'}`);
214
229
  if (result.localCheckout) {
215
230
  console.log('运行环境: 本地源码 checkout');
216
231
  }
@@ -223,6 +238,17 @@ function printSelfUpdateResult(result, json) {
223
238
  if (result.resolvedExecutable?.executable) {
224
239
  console.log(`OpenPrd 可执行文件: ${result.resolvedExecutable.executable}`);
225
240
  }
241
+ if (result.installedVersion?.version) {
242
+ console.log(`安装后版本: ${result.installedVersion.version}`);
243
+ }
244
+ if ((result.refreshCandidates?.total ?? 0) > 0) {
245
+ console.log(`旧项目刷新候选: ${result.refreshCandidates.total}`);
246
+ for (const project of result.refreshCandidates.projects ?? []) {
247
+ console.log(`- ${project.workspaceRoot} (${project.currentVersion} -> ${project.targetVersion}; ${project.note})`);
248
+ }
249
+ } else if (result.refreshCandidates?.ok && result.checkOnly) {
250
+ console.log('旧项目刷新候选: 0');
251
+ }
226
252
  for (const action of result.nextActions ?? []) {
227
253
  console.log(`下一步: ${action}`);
228
254
  }
package/src/cli/print.js CHANGED
@@ -48,9 +48,11 @@ import {
48
48
  } from './doctor-print.js';
49
49
  import { printGrowthResult } from './growth-print.js';
50
50
  import {
51
+ printDesignStarterResult,
51
52
  printDevelopmentStandardsResult,
52
53
  printKnowledgeResult,
53
54
  printQualityResult,
55
+ printVisualPrepareResult,
54
56
  printStandardsResult,
55
57
  printVisualCompareResult,
56
58
  } from './quality-print.js';
@@ -59,6 +61,7 @@ import {
59
61
  printRunResult,
60
62
  } from './run-print.js';
61
63
  import {
64
+ printBrainstormResult,
62
65
  printDiagramResult,
63
66
  printDiffResult,
64
67
  printFreezeResult,
@@ -80,6 +83,7 @@ export {
80
83
  printLearningResult,
81
84
  printReleaseResult,
82
85
  printSynthesizeResult,
86
+ printBrainstormResult,
83
87
  printReviewResult,
84
88
  printHistoryResult,
85
89
  printDiffResult,
@@ -94,9 +98,11 @@ export {
94
98
  printLoopResult,
95
99
  printStandardsResult,
96
100
  printDevelopmentStandardsResult,
101
+ printDesignStarterResult,
97
102
  printGrowthResult,
98
103
  printQualityResult,
99
104
  printKnowledgeResult,
105
+ printVisualPrepareResult,
100
106
  printVisualCompareResult,
101
107
  printFreezeResult,
102
108
  printDiagramResult,
@@ -204,20 +204,26 @@ function printKnowledgeResult(result, json) {
204
204
  const counts = result.counts ?? {};
205
205
  console.log(`OpenPrd knowledge candidates: ${result.candidates.length} 个 (${result.status})`);
206
206
  console.log(`统计: pending ${counts.pending ?? 0}, promoted ${counts.promoted ?? 0}, rejected ${counts.rejected ?? 0}, archived ${counts.archived ?? 0}, reviewed ${counts.reviewed ?? 0}, total ${counts.total ?? 0}`);
207
- for (const candidate of result.candidates) {
208
- console.log(`- ${candidate.candidateId}: ${candidate.title ?? candidate.candidateId}`);
209
- console.log(` 状态: ${candidate.status}`);
207
+ const ordered = [...result.candidates].sort((left, right) => (right.occurrences ?? 1) - (left.occurrences ?? 1));
208
+ for (const candidate of ordered) {
209
+ const occurrences = candidate.occurrences ?? 1;
210
+ const repeatNote = occurrences > 1 ? `(同类经验已重复出现 ${occurrences} 次,值得优先保留)` : '';
211
+ console.log(`- ${candidate.title ?? candidate.candidateId}${repeatNote}`);
212
+ console.log(` 编号: ${candidate.candidateId} · 状态: ${candidate.status}`);
210
213
  console.log(` 候选: ${candidate.path ?? candidate.files?.candidate}`);
211
214
  if (candidate.draftSkillPath ?? candidate.files?.draftSkill) {
212
215
  console.log(` 草案 Skill: ${candidate.draftSkillPath ?? candidate.files?.draftSkill}`);
213
216
  }
214
217
  if (candidate.pending) {
218
+ console.log(` 保留: openprd quality . --learn --review --from ${candidate.files?.candidateDir ?? candidate.candidateId}`);
215
219
  console.log(` 拒绝: openprd knowledge reject --id ${candidate.candidateId} --reason <原因>`);
216
220
  console.log(` 归档: openprd knowledge archive --id ${candidate.candidateId} --reason <原因>`);
217
221
  }
218
222
  }
219
223
  if (result.candidates.length === 0) {
220
224
  console.log('没有匹配的 knowledge candidate。');
225
+ } else if (ordered.some((candidate) => candidate.pending)) {
226
+ console.log('给用户的确认话术: 只要回复「保留第 N 条」或「忽略第 N 条」,Agent 就会执行对应命令,不需要手动操作。');
221
227
  }
222
228
  return;
223
229
  }
@@ -275,10 +281,80 @@ function printVisualCompareResult(result, json) {
275
281
  }
276
282
  }
277
283
 
284
+ function printVisualPrepareResult(result, json) {
285
+ if (json) {
286
+ console.log(JSON.stringify(result, null, 2));
287
+ return;
288
+ }
289
+
290
+ const modeLabel = result.mode === 'grid' ? '规则网格切片' : '手工裁剪计划';
291
+ console.log('OpenPrd visual prepare: 已生成');
292
+ console.log(`reference-set: ${result.referenceSetPath}`);
293
+ console.log(`输出目录: ${result.outputDir}`);
294
+ console.log(`模式: ${modeLabel}`);
295
+ console.log(`参考对象: ${result.itemCount}`);
296
+ if (result.source?.metadata) {
297
+ console.log(`参考整板: ${result.source.metadata.width}x${result.source.metadata.height}`);
298
+ }
299
+ console.log(`Contact sheet: ${result.contactSheetPath}`);
300
+ console.log(`Focus 模板: ${result.focusBoardTemplatePath}`);
301
+ console.log(`Parallel 模板: ${result.parallelBoardTemplatePath}`);
302
+ console.log(`对比计划: ${result.comparePlanPath}`);
303
+ for (const action of result.nextActions ?? []) {
304
+ console.log(`- 下一步: ${action}`);
305
+ }
306
+ }
307
+
308
+ function printDesignStarterResult(result, json) {
309
+ if (json) {
310
+ console.log(JSON.stringify(result, null, 2));
311
+ return;
312
+ }
313
+
314
+ if (!result.ok) {
315
+ console.log('OpenPrd design starter: 失败');
316
+ for (const error of result.errors ?? []) {
317
+ console.log(`- ${error}`);
318
+ }
319
+ return;
320
+ }
321
+
322
+ console.log('OpenPrd design starter: 已生成');
323
+ console.log(`模板: ${result.starterId} (${result.relativeTemplatePath})`);
324
+ console.log(`输出: ${result.relativeOutputPath}`);
325
+ if (result.defaults?.lens || result.defaults?.theme || result.defaults?.layout) {
326
+ console.log(`默认组合: lens=${result.defaults.lens ?? '待补'} theme=${result.defaults.theme ?? '待补'} layout=${result.defaults.layout ?? '待补'}`);
327
+ }
328
+ if (result.overwritten) {
329
+ console.log('覆盖: 是');
330
+ }
331
+ if (result.hydrated) {
332
+ console.log('active design contracts: 已同步写实');
333
+ if (Array.isArray(result.docsHydrated) && result.docsHydrated.length > 0) {
334
+ console.log(`docs/basic: 已同步写实 (${result.docsHydrated.length} 份)`);
335
+ }
336
+ if (result.sourceManualFilled) {
337
+ console.log(`文件说明书: 已补到 ${result.relativeOutputPath}`);
338
+ }
339
+ if (result.folderManualCreated && result.folderManualPath) {
340
+ console.log(`文件夹 README: 已补到 ${result.folderManualPath}`);
341
+ }
342
+ console.log(`页面主题: ${result.title ?? result.brief ?? '已补'}`);
343
+ if (Array.isArray(result.sections) && result.sections.length > 0) {
344
+ console.log(`模块: ${result.sections.join(' / ')}`);
345
+ }
346
+ console.log('下一步: 直接在生成的入口文件上继续细化结构和样式,不要回到 pending 合同;禁止删除这个入口文件后另起新稿。如果题目更像导览、旅行、展览或馆藏内容页,保持真实图片路线,不要再把它改回 `--no-real-images`。');
347
+ return;
348
+ }
349
+ console.log('下一步: 立即替换入口文件里的标题、CTA 和所有占位文案,并同步把 active design contracts 写实;只有确认当前页不依赖外部事实、品牌素材或真实图片时,才直接写清无依赖。若题目更像导览、旅行、展览或馆藏内容页,先不要急着加 `--no-real-images`。后续必须继续在生成的入口文件上改,禁止删除后重起。');
350
+ }
351
+
278
352
  export {
279
353
  printStandardsResult,
280
354
  printDevelopmentStandardsResult,
281
355
  printQualityResult,
282
356
  printKnowledgeResult,
357
+ printVisualPrepareResult,
283
358
  printVisualCompareResult,
359
+ printDesignStarterResult,
284
360
  };
@@ -21,6 +21,20 @@ import { labelExecutionMode } from '../execution-strategy.js';
21
21
  import { printCodexRuntimeResult } from './doctor-print.js';
22
22
  import { printKnowledgeReview, printKnowledgeSkillMatches } from './shared-print.js';
23
23
 
24
+ function printLoopWorkspaceMeta(result, fallbackProjectRoot = null) {
25
+ const workspacePath = result.workspace?.path ?? result.commit?.worktreePath ?? fallbackProjectRoot;
26
+ const branch = result.workspace?.branch ?? result.commit?.branch ?? null;
27
+ if (workspacePath) {
28
+ console.log(`工作区: ${workspacePath}`);
29
+ }
30
+ if (branch) {
31
+ console.log(`分支: ${branch}`);
32
+ }
33
+ if (result.workspace?.created) {
34
+ console.log('隔离 worktree: 已创建');
35
+ }
36
+ }
37
+
24
38
  function printExecutionConfirmationChecklist(checklist) {
25
39
  if (!checklist?.required) {
26
40
  return;
@@ -152,6 +166,31 @@ function printRunResult(result, json) {
152
166
  console.log(`协作建议: ${result.recommendation.parallelPlan.summary}`);
153
167
  }
154
168
  console.log(`这样安排的原因: ${result.recommendation.reason}`);
169
+ if (
170
+ result.recommendation.command
171
+ && !result.recommendation.preparationCommand
172
+ && !result.recommendation.executionCommand
173
+ && !result.recommendation.commitCommand
174
+ ) {
175
+ console.log(`内部下一步参考: ${result.recommendation.command}`);
176
+ }
177
+ if (
178
+ result.recommendation.nextAction === 'brainstorm'
179
+ || result.recommendation.title === '先进入脑暴模式收敛方向'
180
+ || result.recommendation.command === 'openprd brainstorm . --open'
181
+ ) {
182
+ console.log('当前回复目标: 先进入脑暴模式,把核心诉求、目标结果、当前替代方案、推荐方向和验证重点整理成脑暴页;不要只停在 requirement 摘要。');
183
+ } else if (result.recommendation.nextAction === 'lightweight-l1') {
184
+ console.log('当前回复目标: 先用 3-5 行 mini-plan 收一下目标、范围内、范围外和验证方式;如果用户已经明确不需要确认,就按这个轻量路径继续实现,不要再回到 clarify-user。空白工作区先补 `.openprd/design/active/`,再从 `.openprd/design/templates/` 选最近模板;若页面主题和模块范围已经明确,优先把它们带进 `openprd design-starter --brief/--sections`,让 starter 一次落合同和首版内容。只有确认这个页面本来就不依赖外部事实、品牌素材或真实图片时,才在 active design artifacts 写清无依赖;如果题目更像旅游、导览、展览、博物馆、城市或自然观察内容页,先不要急着加 `--no-real-images`。后续必须继续在生成的入口文件上补丁修改;即使结构要大改,也不要删除 `index.html` 后重起。');
185
+ } else if (result.recommendation.nextAction === 'lightweight-l0') {
186
+ console.log('当前回复目标: 直接处理这次局部改动,做最小足够验证,并在完成后用人话说明本次调整。');
187
+ } else if (
188
+ result.recommendation.nextAction === 'clarify-user'
189
+ || result.recommendation.title === 'clarify-user'
190
+ || result.recommendation.title === '继续本轮需求入口澄清'
191
+ ) {
192
+ console.log('当前回复目标: 先在对话里输出 requirement 摘要或只追问 1 个最高价值澄清点;不要承诺“按默认方案直接实现”,也不要把“请帮我实现/继续实现”当成跳过 requirement 摘要确认的依据。');
193
+ }
155
194
  if (result.recommendation.preparationCommand || result.recommendation.executionCommand || result.recommendation.commitCommand) {
156
195
  console.log('开始动手前提: 只有在用户明确要求继续落地、实现、修复、深挖或提交时,才继续往下做;如果还缺这一步,就先用人话说明范围和影响。');
157
196
  }
@@ -165,7 +204,7 @@ function printRunResult(result, json) {
165
204
  if (result.recommendation.commitCommand) {
166
205
  console.log(`内部提交参考: ${result.recommendation.commitCommand}`);
167
206
  }
168
- if (result.recommendation.loop?.worktreeRecommended) {
207
+ if (result.recommendation.loop?.worktreeRecommended || result.recommendation.isolation?.worktreeRecommended) {
169
208
  console.log('环境建议: 最好放到单独环境里继续,避免和别的事项串线。');
170
209
  }
171
210
  console.log(`内部检查参考: ${result.recommendation.verifyCommand}`);
@@ -184,6 +223,7 @@ function printLoopResult(result, json) {
184
223
  console.log(`任务: ${result.task.id} ${result.task.title}`);
185
224
  if (result.task.taskHandle) console.log(`任务句柄: ${result.task.taskHandle}`);
186
225
  }
226
+ printLoopWorkspaceMeta(result, result.projectRoot);
187
227
  if (result.promptPath) {
188
228
  console.log(`提示词: ${result.promptPath}`);
189
229
  }
@@ -200,6 +240,7 @@ function printLoopResult(result, json) {
200
240
  console.log(`OpenPrd loop 运行: ${result.ok ? '通过' : '失败'}${result.dryRun ? ' (dry-run)' : ''}`);
201
241
  if (result.task) console.log(`任务: ${result.task.id} ${result.task.title}`);
202
242
  if (result.task?.taskHandle) console.log(`任务句柄: ${result.task.taskHandle}`);
243
+ printLoopWorkspaceMeta(result, result.projectRoot);
203
244
  if (result.promptPath) console.log(`提示词: ${result.promptPath}`);
204
245
  if (result.invocation?.display) console.log(`执行: ${result.invocation.display}`);
205
246
  if (result.codexRuntime || result.preflight) {
@@ -233,6 +274,7 @@ function printLoopResult(result, json) {
233
274
  console.log(`OpenPrd loop finish: ${result.ok ? '通过' : '失败'}`);
234
275
  if (result.task) console.log(`任务: ${result.task.id} ${result.task.title}`);
235
276
  if (result.task?.taskHandle) console.log(`任务句柄: ${result.task.taskHandle}`);
277
+ printLoopWorkspaceMeta(result, result.projectRoot);
236
278
  if (result.commit) console.log(`提交: ${result.commit.skipped ? '跳过' : result.commit.sha}`);
237
279
  if (result.projectRelease?.version) {
238
280
  console.log(`项目版本: ${result.projectRelease.version}`);
@@ -29,6 +29,12 @@ function printKnowledgeReview(knowledgeReview) {
29
29
  console.log(`项目经验回顾: 失败 (${knowledgeReview.errors?.[0] ?? 'unknown'})`);
30
30
  return;
31
31
  }
32
+ const userFacingMessage = String(knowledgeReview.userFacingExperience?.message ?? '').trim();
33
+ if (userFacingMessage) {
34
+ console.log('项目经验回顾:');
35
+ console.log(userFacingMessage);
36
+ return;
37
+ }
32
38
  console.log(`项目经验草案: ${knowledgeReview.candidateId}`);
33
39
  if (knowledgeReview.summary) {
34
40
  console.log(`摘要: ${knowledgeReview.summary}`);
@@ -51,18 +57,43 @@ function knowledgeAdoptionLabel(adoption = {}) {
51
57
  return `命中 ${adoption.hitCount ?? 0} / 引用 ${adoption.referencedCount ?? 0} / 注入 ${adoption.injectedCount ?? 0}`;
52
58
  }
53
59
 
60
+ function formatUseWhenLabel(value) {
61
+ return String(value ?? '').trim().replace(/^use when\b[::]?\s*/i, '').trim();
62
+ }
63
+
54
64
  function printKnowledgeSkillMatches(knowledgeSkills) {
55
65
  const matched = Array.isArray(knowledgeSkills?.matched) ? knowledgeSkills.matched : [];
56
66
  if (matched.length === 0) {
57
67
  return;
58
68
  }
59
69
  const summary = knowledgeSkills?.summary ?? {};
60
- console.log(`项目级 Skill: 命中 ${matched.length} 个${summary.hookInjected ? ',已自动注入当前上下文' : ''}`);
70
+ const mandatoryCheck = knowledgeSkills?.mandatoryCheck ?? null;
71
+ if (mandatoryCheck?.required) {
72
+ console.log(`项目级经验候选: 找到 ${matched.length} 条${summary.hookInjected ? ',已加入当前上下文供判断' : ''}`);
73
+ console.log(`${mandatoryCheck.title}: ${mandatoryCheck.summary}`);
74
+ for (const instruction of (mandatoryCheck.instructions ?? []).slice(0, 3)) {
75
+ console.log(`- ${instruction}`);
76
+ }
77
+ if (Array.isArray(mandatoryCheck.focusSignals) && mandatoryCheck.focusSignals.length > 0) {
78
+ console.log(`当前判断线索: ${mandatoryCheck.focusSignals.join(';')}`);
79
+ }
80
+ } else {
81
+ console.log(`项目级 Skill: 命中 ${matched.length} 个${summary.hookInjected ? ',已自动注入当前上下文' : ''}`);
82
+ }
61
83
  for (const skill of matched.slice(0, 3)) {
62
84
  console.log(`- ${skill.skillName}: ${skill.matchSummary ?? '命中当前上下文'}`);
63
- if (skill.description) {
85
+ const useWhen = formatUseWhenLabel(skill.useWhen ?? skill.description);
86
+ if (useWhen) {
87
+ console.log(` 适用时机: ${useWhen}`);
88
+ } else if (skill.description) {
64
89
  console.log(` 说明: ${skill.description}`);
65
90
  }
91
+ if (Array.isArray(skill.reviewFirst) && skill.reviewFirst.length > 0) {
92
+ console.log(` 先看: ${skill.reviewFirst.slice(0, 3).join(';')}`);
93
+ }
94
+ if (Array.isArray(skill.antiPatterns) && skill.antiPatterns.length > 0) {
95
+ console.log(` 不要直接套用: ${skill.antiPatterns.slice(0, 2).join(';')}`);
96
+ }
66
97
  if (Array.isArray(skill.touchedFiles) && skill.touchedFiles.length > 0) {
67
98
  console.log(` 相关文件: ${skill.touchedFiles.slice(0, 4).join(';')}`);
68
99
  }
@@ -76,6 +76,22 @@ function printReviewResult(result, json) {
76
76
  }
77
77
  }
78
78
 
79
+ function printBrainstormResult(result, json) {
80
+ if (json) {
81
+ console.log(JSON.stringify(result, null, 2));
82
+ return;
83
+ }
84
+
85
+ console.log('脑暴工作台已生成');
86
+ console.log(`主题: ${result.record.topic}`);
87
+ console.log(`推荐方向: ${result.record.summary?.recommendedDirection ?? '待补充'}`);
88
+ console.log(`脑暴页面: ${result.htmlPath}`);
89
+ console.log(`Markdown 数据源: ${result.markdownPath}`);
90
+ console.log(`Capture Patch: ${result.patchPath}`);
91
+ console.log(`状态文件: ${result.statePath}`);
92
+ console.log(`已自动打开: ${result.opened ? '是' : '否'}`);
93
+ }
94
+
79
95
  function printHistoryResult(result, json) {
80
96
  if (json) {
81
97
  console.log(JSON.stringify(result, null, 2));
@@ -121,6 +137,10 @@ function printNextResult(result, json) {
121
137
  if (taskGraph?.nextReadyNode) {
122
138
  console.log(`下一个就绪节点: ${taskGraph.nextReadyNode}`);
123
139
  }
140
+ if (result.brainstormSuggestion?.recommended) {
141
+ console.log(`脑暴模式建议: ${result.brainstormSuggestion.reason}`);
142
+ console.log(`脑暴命令: ${result.brainstormSuggestion.suggestedCommand}`);
143
+ }
124
144
  if (result.diagramState?.needed) {
125
145
  console.log(`图表门禁: ${result.diagramState.shouldGateFreeze ? '激活' : '已满足'}`);
126
146
  console.log(`建议图表: ${result.diagramState.preferredType}`);
@@ -184,6 +204,7 @@ function printHandoffResult(result, json) {
184
204
  }
185
205
 
186
206
  export {
207
+ printBrainstormResult,
187
208
  printSynthesizeResult,
188
209
  printReviewResult,
189
210
  printHistoryResult,