@openprd/cli 0.1.8 → 0.1.10

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 (134) hide show
  1. package/.openprd/benchmarks/index.md +15 -1
  2. package/.openprd/config.yaml +10 -0
  3. package/.openprd/design/README.md +47 -0
  4. package/.openprd/design/README_EN.md +25 -0
  5. package/.openprd/design/active/asset-spec.md +15 -0
  6. package/.openprd/design/active/direction-plan.md +7 -0
  7. package/.openprd/design/active/facts-sheet.md +13 -0
  8. package/.openprd/design/active/image-preflight.md +9 -0
  9. package/.openprd/design/active/selected-direction.md +11 -0
  10. package/.openprd/design/anti-slop.md +16 -0
  11. package/.openprd/design/assets/dark-mesh.svg +14 -0
  12. package/.openprd/design/assets/paper-grid.svg +14 -0
  13. package/.openprd/design/assets/surface-presets.md +21 -0
  14. package/.openprd/design/assets/workbench-grid.svg +16 -0
  15. package/.openprd/design/checklists/ui-quality-gate.md +22 -0
  16. package/.openprd/design/components/component-catalog.md +42 -0
  17. package/.openprd/design/layouts/layout-catalog.json +45 -0
  18. package/.openprd/design/lenses/frontend-lenses.md +31 -0
  19. package/.openprd/design/recipes/content-experience.md +26 -0
  20. package/.openprd/design/recipes/operational-tool.md +25 -0
  21. package/.openprd/design/recipes/product-launch.md +26 -0
  22. package/.openprd/design/templates/README.md +36 -0
  23. package/.openprd/design/templates/README_EN.md +31 -0
  24. package/.openprd/design/templates/content-home.html +415 -0
  25. package/.openprd/design/templates/ops-dashboard.html +340 -0
  26. package/.openprd/design/templates/product-launch.html +360 -0
  27. package/.openprd/design/themes/theme-catalog.json +77 -0
  28. package/.openprd/engagements/active/prd.md +111 -100
  29. package/.openprd/schema/prd.schema.yaml +10 -0
  30. package/.openprd/templates/base/intake.md +4 -0
  31. package/.openprd/templates/base/prd.md +16 -6
  32. package/AGENTS.md +10 -7
  33. package/README.md +28 -10
  34. package/README_EN.md +23 -3
  35. package/package.json +41 -2
  36. package/scripts/openprd-codex-isolated-worker.mjs +799 -0
  37. package/skills/openprd-benchmark-router/SKILL.md +4 -0
  38. package/skills/openprd-frontend-design/SKILL.md +161 -0
  39. package/skills/openprd-frontend-design/references/design-asset-contract.md +34 -0
  40. package/skills/openprd-frontend-design/references/direction-engine.md +46 -0
  41. package/skills/openprd-harness/SKILL.md +38 -21
  42. package/skills/openprd-learning-review/SKILL.md +2 -1
  43. package/skills/openprd-learning-review/references/style-packs/xianxia-cultivation.prompt.md +1 -1
  44. package/skills/openprd-quality/SKILL.md +6 -4
  45. package/skills/openprd-requirement-intake/SKILL.md +27 -5
  46. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -3
  47. package/skills/openprd-requirement-intake/references/routing-rubric.md +9 -5
  48. package/skills/openprd-requirement-intake/references/startup-validation-lens.md +23 -0
  49. package/skills/openprd-router/SKILL.md +13 -5
  50. package/skills/openprd-shared/SKILL.md +38 -25
  51. package/src/agent-integration.js +126 -75
  52. package/src/brainstorm-artifacts.js +1657 -0
  53. package/src/brainstorm-presentation.js +223 -0
  54. package/src/brainstorm.js +603 -0
  55. package/src/cli/args.js +51 -3
  56. package/src/cli/doctor-print.js +34 -8
  57. package/src/cli/print.js +6 -0
  58. package/src/cli/quality-print.js +70 -0
  59. package/src/cli/run-print.js +43 -1
  60. package/src/cli/shared-print.js +33 -2
  61. package/src/cli/workflow-print.js +21 -0
  62. package/src/codex-hook-runner-template.mjs +858 -65
  63. package/src/design-starter-support.js +462 -0
  64. package/src/design-starter.js +1207 -0
  65. package/src/knowledge.js +377 -21
  66. package/src/learning-review.js +3 -3
  67. package/src/loop.js +839 -101
  68. package/src/openprd.js +86 -1
  69. package/src/openspec/change-validate.js +1 -0
  70. package/src/prd-core.js +18 -0
  71. package/src/quality-learning.js +50 -6
  72. package/src/quality.js +2 -2
  73. package/src/run-harness.js +191 -29
  74. package/src/self-update.js +301 -4
  75. package/src/standards.js +46 -5
  76. package/src/visual-prepare.js +940 -0
  77. package/src/workspace-core.js +137 -0
  78. package/src/workspace-workflow.js +190 -7
  79. package/.openprd/benchmarks/evidence/milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code.md +0 -14
  80. package/.openprd/benchmarks/evidence/nolanlawson-com-using-ai-to-write-better-code-more-slowly.md +0 -14
  81. package/.openprd/discovery/config.json +0 -35
  82. package/.openprd/engagements/active/flows.md +0 -35
  83. package/.openprd/engagements/active/handoff.md +0 -16
  84. package/.openprd/engagements/active/review.html +0 -61
  85. package/.openprd/engagements/active/roles.md +0 -22
  86. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +0 -23
  87. package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +0 -18
  88. package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +0 -18
  89. package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +0 -18
  90. package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +0 -18
  91. package/.openprd/exports/.gitkeep +0 -0
  92. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +0 -78
  93. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +0 -129
  94. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +0 -41
  95. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +0 -14
  96. package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +0 -49
  97. package/.openprd/knowledge/index.json +0 -47
  98. package/.openprd/reviews/v0001.html +0 -1322
  99. package/.openprd/reviews/v0002.html +0 -1150
  100. package/.openprd/reviews/v0003.html +0 -1150
  101. package/.openprd/reviews/v0004.html +0 -1150
  102. package/.openprd/reviews/v0005.html +0 -1150
  103. package/.openprd/sessions/.gitkeep +0 -0
  104. package/.openprd/state/changes.json +0 -27
  105. package/.openprd/state/current.json +0 -505
  106. package/.openprd/state/release-ledger.json +0 -344
  107. package/.openprd/state/version-index.json +0 -67
  108. package/.openprd/state/versions/.gitkeep +0 -0
  109. package/.openprd/state/versions/v0001.json +0 -121
  110. package/.openprd/state/versions/v0001.md +0 -161
  111. package/.openprd/state/versions/v0002.json +0 -264
  112. package/.openprd/state/versions/v0002.md +0 -183
  113. package/.openprd/state/versions/v0003.json +0 -269
  114. package/.openprd/state/versions/v0003.md +0 -188
  115. package/.openprd/state/versions/v0004.json +0 -274
  116. package/.openprd/state/versions/v0004.md +0 -193
  117. package/.openprd/state/versions/v0005.json +0 -299
  118. package/.openprd/state/versions/v0005.md +0 -189
  119. package/docs/assets/openprd-capability-overview-en.png +0 -0
  120. package/docs/assets/openprd-capability-overview-zh.png +0 -0
  121. package/docs/assets/openprd-learning-html.png +0 -0
  122. package/docs/assets/openprd-quality-html.png +0 -0
  123. package/docs/assets/openprd-requirement-routing-en.png +0 -0
  124. package/docs/assets/openprd-requirement-routing-en.svg +0 -102
  125. package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
  126. package/docs/assets/openprd-requirement-routing-zh.png +0 -0
  127. package/docs/assets/openprd-requirement-routing-zh.svg +0 -102
  128. package/docs/assets/openprd-review-html.png +0 -0
  129. package/docs/assets/openprd-scenario-overview.png +0 -0
  130. package/docs/assets/openprd-scenario-overview.svg +0 -114
  131. package/docs/assets/openprd-self-evolving-mechanisms-en.png +0 -0
  132. package/docs/assets/openprd-self-evolving-mechanisms-zh.png +0 -0
  133. package/docs/assets/openprd-visual-compare-case-study-en.png +0 -0
  134. package/docs/assets/openprd-visual-compare-case-study-zh.png +0 -0
package/src/openprd.js CHANGED
@@ -33,7 +33,7 @@ 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, printCaptureResult, printClarifyResult, printClassifyResult, 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 } from './cli/print.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';
37
37
  import { cjoin, exists, readJson, writeJson, writeText, writeYaml } from './fs-utils.js';
38
38
  import { diagramWorkspace } from './diagram-workspace.js';
39
39
  import { createOpenSpecDiscoveryWorkspace } from './discovery.js';
@@ -61,11 +61,15 @@ import {
61
61
  rejectGrowthCandidateWorkspace,
62
62
  reviewGrowthWorkspace,
63
63
  } from './growth.js';
64
+ import { brainstormWorkspace } from './brainstorm.js';
65
+ import { buildBrainstormPresentationTemplatePayload, brainstormPresentationWorkspace } from './brainstorm-presentation.js';
66
+ import { designStarterWorkspace } from './design-starter.js';
64
67
  import { buildReviewPresentationTemplatePayload, reviewPresentationWorkspace } from './review-presentation.js';
65
68
  import { analyzeWorkspaceRegistryHygiene } from './registry-hygiene.js';
66
69
  import { syncSessionBindingFromChange } from './session-binding.js';
67
70
  import { readSessionRegistry } from './session-registry.js';
68
71
  import { visualCompareWorkspace } from './visual-compare.js';
72
+ import { visualPrepareWorkspace } from './visual-prepare.js';
69
73
  import { captureWorkspace, clarifyWorkspace, classifyWorkspace, computeWorkspaceGuidance, diffWorkspace, historyWorkspace, interviewWorkspace, nextWorkspace, playgroundWorkspace, reviewWorkspace, synthesizeWorkspace } from './workspace-workflow.js';
70
74
  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';
71
75
  import { readWorkspaceRegistry } from './workspace-registry.js';
@@ -784,8 +788,10 @@ export async function main(argv = process.argv.slice(2)) {
784
788
 
785
789
  if (command === 'self-update') {
786
790
  const result = await selfUpdateWorkspace({
791
+ check: flags.check,
787
792
  dryRun: flags.dryRun,
788
793
  json: flags.json,
794
+ openprdHome: flags.openprdHome,
789
795
  });
790
796
  printSelfUpdateResult(result, flags.json);
791
797
  return result.ok ? 0 : 1;
@@ -866,6 +872,9 @@ export async function main(argv = process.argv.slice(2)) {
866
872
  evidence: flags.evidence,
867
873
  notes: flags.notes,
868
874
  repairAgent: flags.repairAgent,
875
+ worktree: flags.worktree,
876
+ branch: flags.branch,
877
+ allowDirtyMain: flags.allowDirtyMain,
869
878
  };
870
879
  if (flags.init) {
871
880
  result = await initLoopWorkspace(projectPath, options);
@@ -944,6 +953,20 @@ export async function main(argv = process.argv.slice(2)) {
944
953
  return result.ok ? 0 : 1;
945
954
  }
946
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,
962
+ 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
+
947
970
  if (command === 'visual-compare') {
948
971
  const result = await visualCompareWorkspace(projectPath, {
949
972
  reference: flags.reference,
@@ -962,6 +985,22 @@ export async function main(argv = process.argv.slice(2)) {
962
985
  return result.ok ? 0 : 1;
963
986
  }
964
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,
999
+ });
1000
+ printDesignStarterResult(result, flags.json);
1001
+ return result.ok ? 0 : 1;
1002
+ }
1003
+
965
1004
  if (command === 'grow') {
966
1005
  const firstArgIsProjectPath = !flags.path && positionals.length > 0 && await isDirectoryPath(positionals[0]);
967
1006
  const growthProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[0] : process.cwd()));
@@ -1073,6 +1112,16 @@ export async function main(argv = process.argv.slice(2)) {
1073
1112
  return 0;
1074
1113
  }
1075
1114
 
1115
+ if (command === 'brainstorm') {
1116
+ const result = await brainstormWorkspace(projectPath, {
1117
+ topic: flags.topic,
1118
+ open: flags.open || !flags.json,
1119
+ json: flags.json,
1120
+ });
1121
+ printBrainstormResult(result, flags.json);
1122
+ return 0;
1123
+ }
1124
+
1076
1125
  if (command === 'learn') {
1077
1126
  if (flags.enable || flags.disable) {
1078
1127
  const enabled = flags.enable ? true : false;
@@ -1163,6 +1212,38 @@ export async function main(argv = process.argv.slice(2)) {
1163
1212
  return flags.failOnViolation && !result.ok ? 1 : 0;
1164
1213
  }
1165
1214
 
1215
+ if (command === 'brainstorm-presentation') {
1216
+ if (flags.template) {
1217
+ console.log(JSON.stringify(buildBrainstormPresentationTemplatePayload(), null, 2));
1218
+ return 0;
1219
+ }
1220
+ const result = await brainstormPresentationWorkspace(projectPath, {
1221
+ presentationPath: flags.presentation,
1222
+ write: flags.write,
1223
+ });
1224
+ if (flags.json) {
1225
+ console.log(JSON.stringify(result, null, 2));
1226
+ } else {
1227
+ console.log(`展示文案校验: ${result.ok ? '通过' : '需要重写'}`);
1228
+ console.log(`主题: ${result.topic}`);
1229
+ console.log(`超限或格式问题: ${result.presentationFeedback.length}`);
1230
+ if (result.presentationFeedback.length > 0) {
1231
+ for (const item of result.presentationFeedback.slice(0, 6)) {
1232
+ const pathHint = item.jsonPath ? `${item.jsonPath}: ` : '';
1233
+ const sizeHint = item.maxChars ? ` 当前 ${item.currentChars} 字,限制 ${item.maxChars} 字。` : '';
1234
+ console.log(`- ${item.area} / ${item.target}: ${pathHint}${item.action}${sizeHint}`);
1235
+ }
1236
+ }
1237
+ if (result.written) {
1238
+ console.log(`已写入: ${result.written}`);
1239
+ }
1240
+ if (result.htmlPath) {
1241
+ console.log(`已更新脑暴页面: ${result.htmlPath}`);
1242
+ }
1243
+ }
1244
+ return flags.failOnViolation && !result.ok ? 1 : 0;
1245
+ }
1246
+
1166
1247
  if (command === 'diagram') {
1167
1248
  const result = await diagramWorkspace(projectPath, { open: flags.open, type: flags.type, input: flags.input, mark: flags.mark });
1168
1249
  printDiagramResult(result, flags.json);
@@ -1384,6 +1465,7 @@ export {
1384
1465
  clarifyWorkspace,
1385
1466
  captureWorkspace,
1386
1467
  playgroundWorkspace,
1468
+ brainstormWorkspace,
1387
1469
  generateLearningReviewWorkspace,
1388
1470
  setLearningReviewModeWorkspace,
1389
1471
  synthesizeWorkspace,
@@ -1392,6 +1474,7 @@ export {
1392
1474
  historyWorkspace,
1393
1475
  releaseWorkspace,
1394
1476
  reviewWorkspace,
1477
+ brainstormPresentationWorkspace,
1395
1478
  reviewPresentationWorkspace,
1396
1479
  freezeWorkspace,
1397
1480
  handoffWorkspace,
@@ -1409,6 +1492,7 @@ export {
1409
1492
  archiveOpenPrdChangeWorkspace,
1410
1493
  listAcceptedSpecsWorkspace,
1411
1494
  diagramWorkspace,
1495
+ designStarterWorkspace,
1412
1496
  classifyWorkspace,
1413
1497
  interviewWorkspace,
1414
1498
  loadWorkspace,
@@ -1423,6 +1507,7 @@ export {
1423
1507
  rejectKnowledgeCandidate,
1424
1508
  archiveKnowledgeCandidate,
1425
1509
  restoreKnowledgeCandidate,
1510
+ visualPrepareWorkspace,
1426
1511
  visualCompareWorkspace,
1427
1512
  checkDevelopmentStandardsWorkspace,
1428
1513
  initGrowthWorkspace,
@@ -210,6 +210,7 @@ export async function validateOpenSpecChangeWorkspace(projectRoot, options = {})
210
210
  optional: !(await exists(cjoin(projectRoot, '.openprd'))),
211
211
  sourceManuals: options.sourceManuals,
212
212
  docsContent: options.docsContent,
213
+ folderManualModuleName: options.folderManualModuleName,
213
214
  });
214
215
  if (!standards.skipped) {
215
216
  errors.push(...standards.errors);
package/src/prd-core.js CHANGED
@@ -239,6 +239,15 @@ export function buildPrdSnapshot(ws, options = {}) {
239
239
  successMetrics: normalizeArray(pickValue(options.successMetrics, state.successMetrics)),
240
240
  acceptanceGoals: normalizeArray(pickValue(options.acceptanceGoals, state.acceptanceGoals)),
241
241
  },
242
+ validation: {
243
+ community: normalizeArray(pickValue(options.community, state.community)),
244
+ seedUsers: normalizeArray(pickValue(options.seedUsers, state.seedUsers)),
245
+ currentAlternative: pickValue(options.currentAlternative, state.currentAlternative, state.asIs),
246
+ manualPath: normalizeArray(pickValue(options.manualPath, state.manualPath)),
247
+ commitmentSignals: normalizeArray(pickValue(options.commitmentSignals, state.commitmentSignals)),
248
+ firstValidationStep: pickValue(options.firstValidationStep, state.firstValidationStep, state.nextStep),
249
+ defaultAlivePlan: normalizeArray(pickValue(options.defaultAlivePlan, state.defaultAlivePlan)),
250
+ },
242
251
  scope: {
243
252
  inScope: normalizeArray(pickValue(options.inScope, state.inScope)),
244
253
  outOfScope: normalizeArray(pickValue(options.outOfScope, state.outOfScope)),
@@ -367,6 +376,15 @@ export function renderPrdMarkdown(snapshot) {
367
376
  ['成功指标', sections.goals.successMetrics],
368
377
  ['验收目标', sections.goals.acceptanceGoals],
369
378
  ]),
379
+ renderSection('验证与创业闭环', [
380
+ ['可触达社区', sections.validation.community],
381
+ ['第一批种子用户', sections.validation.seedUsers],
382
+ ['当前替代方案', sections.validation.currentAlternative],
383
+ ['手工交付路径', sections.validation.manualPath],
384
+ ['承诺信号', sections.validation.commitmentSignals],
385
+ ['首个低成本验证', sections.validation.firstValidationStep],
386
+ ['先活下来方案', sections.validation.defaultAlivePlan],
387
+ ]),
370
388
  renderSection('范围与非目标', [
371
389
  ['范围内', sections.scope.inScope],
372
390
  ['范围外', sections.scope.outOfScope],
@@ -575,9 +575,24 @@ function renderList(items, fallback) {
575
575
  return list.map((item) => `- ${item}`).join('\n')
576
576
  }
577
577
 
578
- function renderEvidenceSources(evidenceSources) {
578
+ function describeEvidenceSource(source, options = {}) {
579
+ if (!options.genericPaths) {
580
+ return `${source.kind}: \`${source.path}\``;
581
+ }
582
+ const genericLabel = {
583
+ 'quality-report': '当前项目里最新一轮相关的质量报告与 HTML 评估页',
584
+ 'diagnostic-bundle': '当前任务对应的诊断目录',
585
+ 'diagnostic-report': '当前任务对应的 diagnostic-report',
586
+ 'runtime-events': '当前任务对应的 runtime-events',
587
+ timeline: '当前任务对应的 timeline',
588
+ 'root-cause-candidates': '当前任务对应的 root-cause-candidates',
589
+ }[source.kind] ?? '当前任务对应的相关证据';
590
+ return `${source.kind}: ${genericLabel}`;
591
+ }
592
+
593
+ function renderEvidenceSources(evidenceSources, options = {}) {
579
594
  return renderList(
580
- evidenceSources.map((source) => `${source.kind}: \`${source.path}\``),
595
+ evidenceSources.map((source) => describeEvidenceSource(source, options)),
581
596
  '当前来源没有显式证据清单,后续应补齐标准诊断目录。',
582
597
  )
583
598
  }
@@ -608,6 +623,18 @@ function renderCorrelationSection({ correlationFields, extraContextFields, missi
608
623
  }
609
624
 
610
625
  export function renderExperienceSkill({ skillName, source }) {
626
+ const stableProblemSummary = source.kind === 'quality-report'
627
+ ? (
628
+ source.attentionGates.length > 0
629
+ ? `当前任务的质量收口与门禁闭环(${source.attentionGates.slice(0, 4).join(', ')})`
630
+ : '当前任务的质量收口与门禁闭环'
631
+ )
632
+ : (
633
+ source.rootCauseCandidates[0]?.title
634
+ ?? source.eventNames[0]
635
+ ?? source.symptoms[0]
636
+ ?? '当前任务对应的问题症状'
637
+ );
611
638
  const triggers = source.kind === 'quality-report'
612
639
  ? [
613
640
  '某项任务改动了前端、后端、agent 工作流或错误处理行为。',
@@ -626,9 +653,9 @@ export function renderExperienceSkill({ skillName, source }) {
626
653
  : '适用于已经拿到 runtime-events、timeline、root-cause-candidates 或 diagnostic-report 的排查任务。',
627
654
  ]
628
655
  const typicalInputs = [
629
- source.title ? `问题摘要: ${source.title}` : null,
656
+ stableProblemSummary ? `问题摘要: ${stableProblemSummary}` : null,
630
657
  source.evidenceSources.length > 0
631
- ? `可直接使用的证据: ${source.evidenceSources.slice(0, 4).map((item) => `${item.kind}:${item.path}`).join(';')}`
658
+ ? `可直接使用的证据类型: ${source.evidenceSources.slice(0, 4).map((item) => item.kind).join(';')}`
632
659
  : null,
633
660
  source.eventNames.length > 0 ? `关键事件: ${source.eventNames.join(' -> ')}` : null,
634
661
  ]
@@ -637,10 +664,23 @@ export function renderExperienceSkill({ skillName, source }) {
637
664
  source.verificationSteps.length > 0 ? `最小验证链路: ${source.verificationSteps[0]}` : null,
638
665
  '可复用的项目级 knowledge skill 或后续 candidate / incident / pattern 记录。',
639
666
  ]
667
+ const useWhen = source.kind === 'quality-report'
668
+ ? 'Use when the current task is entering quality closeout and you should reuse a proven evidence and gate-check path instead of rebuilding the checklist from scratch.'
669
+ : 'Use when the current task overlaps this verified diagnosis pattern, symptoms, or touched files and you should reuse proven evidence before starting from scratch.'
670
+ const antiPatterns = [
671
+ source.kind === 'quality-report'
672
+ ? '如果当前任务还没进入验证或收尾阶段,不要把这条经验当成通用实现模板直接套用。'
673
+ : '如果当前问题没有出现相似症状、关键事件或证据入口,不要只因为改到相似文件就直接套用这条经验。',
674
+ '如果只是文件名、路径或个别词相似,但当前目标、阶段或验证方式不同,不要直接照搬。',
675
+ source.evidenceSources.length > 0
676
+ ? '如果本轮已经拿到更新的现场证据,先核对新证据,再决定是否复用这条经验。'
677
+ : null,
678
+ ]
640
679
 
641
680
  return `---
642
681
  name: ${skillName}
643
- description: 由 OpenPrd 从 ${source.kind} 自动沉淀的项目级排查经验。目标是在相似问题再次出现时优先复用现有诊断证据,而不是临时补日志。
682
+ description: ${useWhen}
683
+ use_when: ${useWhen}
644
684
  ---
645
685
 
646
686
  # ${skillName}
@@ -663,7 +703,11 @@ ${renderList(typicalOutputs, '至少产出可复用结论、验证链路和后
663
703
 
664
704
  ## 先看哪些证据
665
705
 
666
- ${renderEvidenceSources(source.evidenceSources)}
706
+ ${renderEvidenceSources(source.evidenceSources, { genericPaths: true })}
707
+
708
+ ## 不要直接套用
709
+
710
+ ${renderList(antiPatterns, '如果只是表面相似,但目标和验证方式不一致,不要直接套用。')}
667
711
 
668
712
  ## 关联字段
669
713
 
package/src/quality.js CHANGED
@@ -1019,7 +1019,7 @@ function detectKnowledge({ config, knowledgeFiles, candidateState, knowledgeInde
1019
1019
  warnings.push('本次已经达到可交付状态,但还没有自动生成 knowledge candidate;收工前至少保留一条可审查的项目经验草案。');
1020
1020
  }
1021
1021
  if (config.knowledge.enabled && skills.length > 0 && adoption.totals.referenced === 0) {
1022
- warnings.push('项目级经验 skill 已产出,但还没有任何 run-context 引用记录;优先接入自动命中与注入链路。');
1022
+ warnings.push('项目级经验 skill 已产出,但还没有任何 run-context 引用记录;优先接入候选召回与判断注入链路。');
1023
1023
  }
1024
1024
  return {
1025
1025
  status: !config.knowledge.enabled || hasReusableArtifact ? 'pass' : 'needs-attention',
@@ -1444,7 +1444,7 @@ export async function learnQualityWorkspace(projectRoot, options = {}) {
1444
1444
  };
1445
1445
  }
1446
1446
  const source = resolved.source;
1447
- const { incidentId, patternId, skillName } = deriveKnowledgeNames(source);
1447
+ const { incidentId, patternId, skillName } = deriveKnowledgeNames(source, { stablePattern: true });
1448
1448
  const incidentPath = qualityPath(projectRoot, cjoin(KNOWLEDGE_DIR, 'incidents', `${incidentId}.json`));
1449
1449
  const patternPath = qualityPath(projectRoot, cjoin(KNOWLEDGE_DIR, 'patterns', `${patternId}.json`));
1450
1450
  const skillDir = qualityPath(projectRoot, cjoin(KNOWLEDGE_DIR, 'skills', skillName));