@openprd/cli 0.1.1 → 0.1.8

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 (137) hide show
  1. package/.openprd/README.md +43 -69
  2. package/.openprd/README_EN.md +84 -0
  3. package/.openprd/benchmarks/index.md +7 -0
  4. package/.openprd/benchmarks/sources.yaml +25 -3
  5. package/.openprd/discovery/config.json +16 -2
  6. package/.openprd/engagements/active/flows.md +19 -14
  7. package/.openprd/engagements/active/handoff.md +11 -4
  8. package/.openprd/engagements/active/prd.md +99 -71
  9. package/.openprd/engagements/active/review.html +4 -4
  10. package/.openprd/engagements/active/roles.md +9 -8
  11. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +4 -4
  12. package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +18 -0
  13. package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +18 -0
  14. package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +18 -0
  15. package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +18 -0
  16. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +78 -0
  17. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +129 -0
  18. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +41 -0
  19. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +14 -0
  20. package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +49 -0
  21. package/.openprd/knowledge/index.json +44 -4
  22. package/.openprd/reviews/v0001.html +195 -129
  23. package/.openprd/reviews/v0002.html +1150 -0
  24. package/.openprd/reviews/v0003.html +1150 -0
  25. package/.openprd/reviews/v0004.html +1150 -0
  26. package/.openprd/reviews/v0005.html +1150 -0
  27. package/.openprd/standards/config.json +12 -9
  28. package/.openprd/state/changes.json +17 -2
  29. package/.openprd/state/current.json +399 -63
  30. package/.openprd/state/release-ledger.json +344 -0
  31. package/.openprd/state/version-index.json +52 -0
  32. package/.openprd/state/versions/v0002.json +264 -0
  33. package/.openprd/state/versions/v0002.md +183 -0
  34. package/.openprd/state/versions/v0003.json +269 -0
  35. package/.openprd/state/versions/v0003.md +188 -0
  36. package/.openprd/state/versions/v0004.json +274 -0
  37. package/.openprd/state/versions/v0004.md +193 -0
  38. package/.openprd/state/versions/v0005.json +299 -0
  39. package/.openprd/state/versions/v0005.md +189 -0
  40. package/.openprd/templates/agent/intake.md +5 -4
  41. package/.openprd/templates/b2b/intake.md +5 -4
  42. package/.openprd/templates/base/intake.md +10 -4
  43. package/.openprd/templates/company/README.md +9 -7
  44. package/.openprd/templates/company/README_EN.md +12 -0
  45. package/.openprd/templates/consumer/intake.md +5 -4
  46. package/.openprd/templates/industry/README.md +12 -10
  47. package/.openprd/templates/industry/README_EN.md +18 -0
  48. package/.openprd/templates/project/README.md +11 -9
  49. package/.openprd/templates/project/README_EN.md +16 -0
  50. package/.openprd/templates/session/README.md +11 -9
  51. package/.openprd/templates/session/README_EN.md +16 -0
  52. package/AGENTS.md +12 -8
  53. package/README.md +399 -438
  54. package/README_CN.md +4 -578
  55. package/README_EN.md +850 -0
  56. package/docs/assets/openprd-requirement-routing-en.png +0 -0
  57. package/docs/assets/openprd-requirement-routing-en.svg +102 -0
  58. package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
  59. package/docs/assets/openprd-requirement-routing-zh.png +0 -0
  60. package/docs/assets/openprd-requirement-routing-zh.svg +102 -0
  61. package/package.json +6 -2
  62. package/scripts/dev-check-wrapup-copy.mjs +110 -0
  63. package/scripts/openprd-github-release-notes.mjs +99 -0
  64. package/scripts/quality-perf-check.mjs +203 -0
  65. package/skills/openprd-benchmark-router/SKILL.md +1 -0
  66. package/skills/openprd-benchmark-router/references/benchmark-sources.md +1 -0
  67. package/skills/openprd-benchmark-router/references/source-policy.md +2 -0
  68. package/skills/openprd-discovery-loop/SKILL.md +2 -2
  69. package/skills/openprd-harness/SKILL.md +46 -24
  70. package/skills/openprd-harness/references/workflow-gates.md +15 -0
  71. package/skills/openprd-quality/SKILL.md +10 -4
  72. package/skills/openprd-requirement-intake/SKILL.md +31 -20
  73. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -6
  74. package/skills/openprd-requirement-intake/references/routing-rubric.md +10 -2
  75. package/skills/openprd-router/SKILL.md +2 -2
  76. package/skills/openprd-shared/SKILL.md +51 -23
  77. package/skills/openprd-standards/SKILL.md +2 -1
  78. package/src/agent-integration.js +265 -65
  79. package/src/benchmark/constants.js +107 -0
  80. package/src/benchmark/operations.js +235 -0
  81. package/src/benchmark/registry.js +64 -0
  82. package/src/benchmark/render.js +115 -0
  83. package/src/benchmark/source.js +617 -0
  84. package/src/benchmark/storage.js +121 -0
  85. package/src/benchmark/verify.js +235 -0
  86. package/src/benchmark.js +50 -851
  87. package/src/change-summary.js +339 -0
  88. package/src/cli/args.js +67 -6
  89. package/src/cli/basic-print.js +365 -0
  90. package/src/cli/benchmark-print.js +91 -0
  91. package/src/cli/change-print.js +221 -0
  92. package/src/cli/doctor-print.js +268 -0
  93. package/src/cli/growth-print.js +176 -0
  94. package/src/cli/print.js +73 -1384
  95. package/src/cli/quality-print.js +284 -0
  96. package/src/cli/run-print.js +297 -0
  97. package/src/cli/shared-print.js +127 -0
  98. package/src/cli/workflow-print.js +195 -0
  99. package/src/codex-hook-runner-template.mjs +639 -117
  100. package/src/codex-runtime.js +324 -0
  101. package/src/dev-standards.js +178 -5
  102. package/src/diagram-core.js +5 -5
  103. package/src/discovery.js +2 -1
  104. package/src/execution-strategy.js +369 -0
  105. package/src/fleet.js +4 -0
  106. package/src/github-release.js +156 -0
  107. package/src/growth.js +311 -13
  108. package/src/html-artifact-utils.js +25 -0
  109. package/src/html-artifacts.js +157 -1596
  110. package/src/knowledge.js +1176 -75
  111. package/src/language-policy.js +2 -112
  112. package/src/learning-html-artifact.js +1031 -0
  113. package/src/learning-review.js +3 -2
  114. package/src/loop.js +280 -9
  115. package/src/openprd.js +341 -38
  116. package/src/openspec/change-validate.js +0 -9
  117. package/src/openspec/execute.js +79 -3
  118. package/src/openspec/generate.js +33 -20
  119. package/src/openspec/tasks.js +33 -2
  120. package/src/prd-core.js +10 -9
  121. package/src/product-type-copy.js +69 -0
  122. package/src/quality-html-artifact.js +108 -9
  123. package/src/quality-learning.js +30 -0
  124. package/src/quality-visual-review.js +237 -0
  125. package/src/quality.js +329 -43
  126. package/src/registry-hygiene.js +54 -0
  127. package/src/release-ledger.js +413 -0
  128. package/src/review-presentation.js +12 -6
  129. package/src/run-harness.js +722 -48
  130. package/src/session-binding.js +40 -3
  131. package/src/session-registry.js +159 -0
  132. package/src/standards.js +5 -3
  133. package/src/test-strategy.js +386 -0
  134. package/src/visual-compare.js +915 -34
  135. package/src/work-unit-migration.js +5 -1
  136. package/src/workspace-core.js +343 -19
  137. package/src/workspace-workflow.js +538 -134
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import os from 'node:os';
3
4
  import crypto from 'node:crypto';
4
5
  import { spawnSync } from 'node:child_process';
5
6
 
@@ -367,6 +368,21 @@ function sessionBindingPath(root, sessionId = null) {
367
368
  return path.join(sessionBindingDir(root), `${String(sessionId).replace(/[^A-Za-z0-9._-]/g, '_')}.json`);
368
369
  }
369
370
 
371
+ function openprdHome() {
372
+ return path.resolve(process.env.OPENPRD_HOME || path.join(os.homedir(), '.openprd'));
373
+ }
374
+
375
+ function sessionRegistryPath() {
376
+ return path.join(openprdHome(), 'registry', 'sessions.jsonl');
377
+ }
378
+
379
+ function sessionStatePath(root, sessionId = null) {
380
+ if (!sessionId) {
381
+ return null;
382
+ }
383
+ return path.join(harnessDir(root), 'session-states', `${String(sessionId).replace(/[^A-Za-z0-9._-]/g, '_')}.json`);
384
+ }
385
+
370
386
  function currentStatePath(root) {
371
387
  return path.join(root, '.openprd', 'state', 'current.json');
372
388
  }
@@ -407,7 +423,7 @@ function reviewPolicyAllowsSilentRecord(policy) {
407
423
  }
408
424
 
409
425
  function resolveRequirementApprovalPolicy(prompt, intent = {}) {
410
- const suppressExtraConfirmation = Boolean(intent?.noReviewRequested || intent?.noConfirmationRequested);
426
+ const suppressExtraConfirmation = Boolean(intent?.noConfirmationRequested);
411
427
  const explicitExecution = Boolean(intent?.explicitExecution);
412
428
  return normalizeRequirementApprovalPolicy({
413
429
  mode: 'decision-points',
@@ -445,6 +461,47 @@ function readSessionBinding(root, sessionId = null) {
445
461
  return readJsonSync(sessionBindingPath(root, sessionId), null);
446
462
  }
447
463
 
464
+ function appendJsonLine(filePath, value) {
465
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
466
+ fs.appendFileSync(filePath, JSON.stringify(value) + '\n');
467
+ }
468
+
469
+ function writeSessionRegistryEntry(root, sessionId, patch = {}) {
470
+ if (!sessionId) {
471
+ return null;
472
+ }
473
+ const workspaceRoot = path.resolve(root);
474
+ const recordedAt = patch.recordedAt || now();
475
+ const entry = {
476
+ version: 1,
477
+ sessionId,
478
+ workspaceRoot,
479
+ realpath: workspaceRoot,
480
+ laneKind: patch.laneKind || 'requirement',
481
+ tool: patch.tool || 'codex',
482
+ threadId: patch.threadId || null,
483
+ changeId: patch.changeId || null,
484
+ taskHandle: patch.taskHandle || null,
485
+ workUnitId: patch.workUnitId || null,
486
+ versionId: patch.versionId || null,
487
+ digest: patch.digest || null,
488
+ title: patch.title || null,
489
+ targetRoot: patch.targetRoot ? path.resolve(patch.targetRoot) : null,
490
+ promptPreview: patch.promptPreview || null,
491
+ reviewStatus: patch.reviewStatus || null,
492
+ gateStatus: patch.gateStatus || null,
493
+ gateActive: patch.gateActive === true,
494
+ statePath: patch.statePath || sessionStatePath(root, sessionId),
495
+ bindingPath: patch.bindingPath || sessionBindingPath(root, sessionId),
496
+ firstRegisteredAt: patch.firstRegisteredAt || recordedAt,
497
+ lastRegisteredAt: recordedAt,
498
+ lastUpdatedAt: patch.updatedAt || recordedAt,
499
+ recordedAt,
500
+ };
501
+ appendJsonLine(sessionRegistryPath(), entry);
502
+ return entry;
503
+ }
504
+
448
505
  function writeSessionBinding(root, sessionId, patch = {}) {
449
506
  if (!sessionId) {
450
507
  return null;
@@ -460,6 +517,25 @@ function writeSessionBinding(root, sessionId, patch = {}) {
460
517
  updatedAt: patch.updatedAt ?? now(),
461
518
  };
462
519
  writeJsonSync(filePath, next);
520
+ writeSessionRegistryEntry(root, sessionId, {
521
+ laneKind: patch.laneKind ?? previous?.laneKind ?? 'requirement',
522
+ tool: patch.tool ?? previous?.tool ?? 'codex',
523
+ threadId: patch.threadId ?? previous?.threadId ?? null,
524
+ changeId: patch.changeId ?? next.changeId ?? null,
525
+ taskHandle: patch.taskHandle ?? next.taskHandle ?? null,
526
+ workUnitId: patch.workUnitId ?? next.workUnitId ?? null,
527
+ versionId: patch.versionId ?? next.versionId ?? null,
528
+ digest: patch.digest ?? next.digest ?? null,
529
+ title: patch.title ?? next.title ?? null,
530
+ targetRoot: patch.targetRoot ?? next.targetRoot ?? null,
531
+ promptPreview: patch.promptPreview ?? next.promptPreview ?? null,
532
+ reviewStatus: patch.reviewStatus ?? next.reviewStatus ?? null,
533
+ gateStatus: patch.gateStatus ?? next.gateStatus ?? null,
534
+ gateActive: patch.gateActive ?? next.gateActive ?? false,
535
+ bindingPath: filePath,
536
+ statePath: sessionStatePath(root, sessionId),
537
+ updatedAt: next.updatedAt,
538
+ });
463
539
  return next;
464
540
  }
465
541
 
@@ -676,7 +752,7 @@ function changeRequirementSummary(root, changeId) {
676
752
  }
677
753
 
678
754
  function runOpenPrdContext(cwd, prompt = null) {
679
- const args = ['run', '.', '--context', '--json'];
755
+ const args = ['run', '.', '--context', '--json', '--hook-inject'];
680
756
  if (String(prompt || '').trim()) {
681
757
  args.push('--message', String(prompt).trim());
682
758
  }
@@ -705,43 +781,103 @@ function runOpenPrdContext(cwd, prompt = null) {
705
781
  };
706
782
  }
707
783
 
784
+ function knowledgeSkillContextLines(knowledgeSkills) {
785
+ const matched = Array.isArray(knowledgeSkills?.matched) ? knowledgeSkills.matched : [];
786
+ if (matched.length === 0) {
787
+ return [];
788
+ }
789
+ const lines = [
790
+ `项目级 Skill: 自动命中 ${matched.length} 个,并已加入当前上下文`,
791
+ ];
792
+ for (const skill of matched.slice(0, 3)) {
793
+ lines.push(`- ${skill.skillName}: ${skill.matchSummary || '命中当前上下文'}`);
794
+ if (skill.description) {
795
+ lines.push(` 说明: ${skill.description}`);
796
+ }
797
+ if (Array.isArray(skill.touchedFiles) && skill.touchedFiles.length > 0) {
798
+ lines.push(` 相关文件: ${skill.touchedFiles.slice(0, 4).join(';')}`);
799
+ }
800
+ if (skill.adoption) {
801
+ lines.push(` 复用指标: 命中 ${skill.adoption.hitCount || 0} / 引用 ${skill.adoption.referencedCount || 0} / 注入 ${skill.adoption.injectedCount || 0}`);
802
+ }
803
+ }
804
+ return lines;
805
+ }
806
+
708
807
  function renderRunContextText(result) {
709
808
  const lines = [
710
- 'OpenPrd 运行上下文',
711
- '项目: ' + result.projectRoot,
712
- '验证: ' + (result.validation?.valid ? '通过' : '失败'),
809
+ '当前进展参考',
810
+ '当前项目: ' + result.projectRoot,
811
+ '基础检查: ' + (result.validation?.valid ? '通过' : '失败'),
713
812
  ];
813
+ if (result.activeRequirementGate) {
814
+ const gateStatus = result.activeRequirementGate.status ?? 'active';
815
+ const gateSuffix = result.activeRequirementGate.relevance === 'background' ? '(仅背景提醒)' : '';
816
+ lines.push('当前处理阶段: ' + gateStatus + gateSuffix);
817
+ }
714
818
  if (result.activeChange) {
715
- lines.push('激活变更: ' + result.activeChange);
819
+ const label = result.recommendation?.type === 'requirement-intake' ? '历史聚焦事项' : '当前聚焦事项';
820
+ lines.push(label + ': ' + result.activeChange);
716
821
  }
717
822
  if (result.lane?.summary) {
718
- lines.push('执行流: ' + result.lane.summary);
823
+ lines.push('当前处理路径: ' + result.lane.summary);
719
824
  }
720
825
  if (result.taskSummary) {
721
- lines.push('任务: ' + result.taskSummary.completed + '/' + result.taskSummary.total + ' 完成,' + result.taskSummary.pending + ' 待处理,' + result.taskSummary.blocked + ' 阻塞');
826
+ lines.push('后续任务进度: ' + result.taskSummary.completed + '/' + result.taskSummary.total + ' 完成,' + result.taskSummary.pending + ' 待处理,' + result.taskSummary.blocked + ' 阻塞');
827
+ if (result.taskSummary.implementation) {
828
+ lines.push('待落地任务: ' + result.taskSummary.implementation.completed + '/' + result.taskSummary.implementation.total + ' 完成,' + result.taskSummary.implementation.pending + ' 待处理');
829
+ }
722
830
  }
723
831
  if (result.discovery) {
724
- lines.push('持续发现: ' + result.discovery.runId + ' 已覆盖 ' + result.discovery.summary.covered + '/' + result.discovery.summary.total + ',待处理 ' + result.discovery.summary.pending);
832
+ lines.push('调研进度: ' + result.discovery.runId + ' 已覆盖 ' + result.discovery.summary.covered + '/' + result.discovery.summary.total + ',待处理 ' + result.discovery.summary.pending);
725
833
  }
834
+ lines.push(...knowledgeSkillContextLines(result.knowledgeSkills));
835
+ lines.push('对外表达: 面向用户时,请优先说“本次调整”“后续任务”“继续落地”“完成后检查”这类人话,不要直接复述内部编号、命令、路径、版本号或流程术语。');
726
836
  const recommendation = result.recommendation || {};
727
- lines.push('下一步类型: ' + recommendation.type);
728
- lines.push('下一步: ' + recommendation.title);
729
- lines.push('原因: ' + recommendation.reason);
730
- lines.push('建议只读命令: ' + recommendation.command);
837
+ lines.push('建议下一步: ' + recommendation.title);
838
+ lines.push('这样安排的原因: ' + recommendation.reason);
731
839
  if (recommendation.preparationCommand || recommendation.executionCommand || recommendation.commitCommand) {
732
- lines.push('执行门槛: 仅当用户当前明确要求开发、实现、继续任务、深度调研、深度对标、复刻落地或提交时使用;规划、梳理、分析、审查类请求保持只读。');
840
+ lines.push('开始动手前提: 只有在用户明确要求继续落地、实现、修复、深挖或提交时,才继续往下做;如果还缺这一步,就先用人话说明范围和影响。');
841
+ }
842
+ const checklist = recommendation.executionConfirmationChecklist;
843
+ if (checklist?.required) {
844
+ lines.push((checklist.title || '开始动手前先确认这些') + ':');
845
+ if (checklist.objective) {
846
+ lines.push('- 这次要做什么: ' + checklist.objective);
847
+ }
848
+ if (checklist.scope?.length > 0) {
849
+ lines.push('- 这次范围: ' + checklist.scope.join(';'));
850
+ }
851
+ if (checklist.implementationItems?.length > 0) {
852
+ lines.push('- 我会这样推进: ' + checklist.implementationItems.join(';'));
853
+ }
854
+ if (checklist.outOfScope?.length > 0) {
855
+ lines.push('- 这次先不做: ' + checklist.outOfScope.join(';'));
856
+ }
857
+ if (checklist.verification?.length > 0) {
858
+ lines.push('- 完成后会检查: ' + checklist.verification.join(';'));
859
+ }
860
+ if (checklist.risks?.length > 0) {
861
+ lines.push('- 需要提前知道: ' + checklist.risks.join(';'));
862
+ }
863
+ if (checklist.confirmationPrompt) {
864
+ lines.push('- 如果要我现在继续: ' + checklist.confirmationPrompt);
865
+ }
733
866
  }
734
867
  if (recommendation.preparationCommand) {
735
- lines.push('准备命令: ' + recommendation.preparationCommand);
868
+ lines.push('内部准备参考: ' + recommendation.preparationCommand);
736
869
  }
737
870
  if (recommendation.executionCommand) {
738
- lines.push('执行命令: ' + recommendation.executionCommand);
871
+ lines.push('内部执行参考: ' + recommendation.executionCommand);
739
872
  }
740
873
  if (recommendation.commitCommand) {
741
- lines.push('提交命令: ' + recommendation.commitCommand);
874
+ lines.push('内部提交参考: ' + recommendation.commitCommand);
875
+ }
876
+ if (recommendation.loop?.worktreeRecommended) {
877
+ lines.push('环境建议: 最好放到单独环境里继续,避免和别的事项串线。');
742
878
  }
743
- lines.push('验证命令: ' + recommendation.verifyCommand);
744
- lines.push('状态文件: ' + (result.files?.runState || '.openprd/harness/run-state.json'));
879
+ lines.push('内部检查参考: ' + recommendation.verifyCommand);
880
+ lines.push('内部状态参考: ' + (result.files?.runState || '.openprd/harness/run-state.json'));
745
881
  return lines.filter(Boolean).join('\n');
746
882
  }
747
883
 
@@ -751,32 +887,31 @@ function analyzePromptIntent(prompt) {
751
887
  const continuationSessionId = text.match(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i)?.[0] ?? null;
752
888
  const continuationTaskHandle = text.match(/\b[a-z0-9._-]+:T\d{3}\.\d{2}:[a-z0-9._-]+\b/i)?.[0] ?? null;
753
889
  const continuationWorkUnitId = text.match(/\bwu-[a-z0-9._-]+\b/i)?.[0] ?? null;
754
- const continuationRequest = /(继续(这个|这条|当前)?(对话|任务|会话|记录|历史)?|续做|接着做|继续执行|继续推进)/i.test(text)
755
- || Boolean(continuationSessionId || continuationTaskHandle || continuationWorkUnitId);
890
+ const promptReviewCommand = parseReviewMarkCommand(text);
891
+ const continuationVerbMatched = /(?:(?:继续|续做|接着做|继续执行|继续推进)(?:这个|这条|当前)?\s*(?:对话|任务|会话|记录|历史|Codex\s*任务)|(?:对话|任务|会话|记录|历史|Codex\s*任务).{0,6}(?:继续|续做|接着做|继续执行|继续推进)|^(?:继续|续做|接着做|继续执行|继续推进)\s*(?::|:))/i.test(text);
892
+ const continuationRequest = continuationVerbMatched
893
+ || Boolean(
894
+ continuationTaskHandle
895
+ || (continuationWorkUnitId && !promptReviewCommand)
896
+ );
756
897
  const githubRepoPattern = /(?:https?:\/\/)?github\.com\/[^\s/]+\/[^\s/#?]+|(?:^|[\s(])[\w.-]+\/[\w.-]+(?=$|[\s)#?])/i;
757
898
  const internalOpenPrdExecution = /^#\s*OpenPrd\s+长程单任务执行会话/m.test(text)
758
899
  || /模式:\s*loop-run\b/i.test(text)
759
900
  || /模式:\s*loop-finish\b/i.test(text);
760
901
  // These signals only decide when to inject/open the requirement-intake lane.
761
- // The L0/L1/L2 decision itself belongs to openprd-requirement-intake, not to
762
- // this matcher.
763
- const requirementRoutingSignals = [
902
+ // The matcher should stay conservative: only likely L2 requests open the
903
+ // heavy gate; L0/L1 stay lightweight unless the user expands the scope.
904
+ const requirementRoutingSignalsStrong = [
764
905
  /新增/,
765
906
  /增加/,
766
907
  /新建/,
767
908
  /我希望/,
768
- /用户反馈/,
769
909
  /需求/,
770
- /功能/,
771
910
  /模块/,
772
- /页面/,
773
- /界面/,
774
- /视觉/,
775
911
  /入口/,
776
912
  /流程/,
777
913
  /编排/,
778
914
  /一站式/,
779
- /体验/,
780
915
  /信息架构/,
781
916
  /团队搭建/,
782
917
  /agent\s*市场/i,
@@ -786,6 +921,34 @@ function analyzePromptIntent(prompt) {
786
921
  /workflow/i,
787
922
  /wizard/i,
788
923
  ];
924
+ const requirementRoutingSignalsWeak = [
925
+ /用户反馈/,
926
+ /功能/,
927
+ /页面/,
928
+ /界面/,
929
+ /视觉/,
930
+ /体验/,
931
+ ];
932
+ const requirementChangeIntentSignals = [
933
+ /新增/,
934
+ /增加/,
935
+ /新建/,
936
+ /改(动|版|造|进|一下)?/,
937
+ /优化/,
938
+ /调整/,
939
+ /重做/,
940
+ /重构/,
941
+ /放到/,
942
+ /移到/,
943
+ /移动到/,
944
+ /切到/,
945
+ /切换到/,
946
+ /替换(成|为)?/,
947
+ /串联/,
948
+ /接入/,
949
+ /实现/,
950
+ /落地/,
951
+ ];
789
952
  const tinyEditPatterns = [
790
953
  /加(一个|个)?空格/,
791
954
  /增加(一个|个)?空格/,
@@ -802,15 +965,77 @@ function analyzePromptIntent(prompt) {
802
965
  /按钮|文案|颜色|圆角|位置|间距|字号|图标|标题|空格|标点|label|copy/i,
803
966
  /从.+(改到|移到|移动到|换到|变成|改成|改为).+/,
804
967
  ];
805
- const complexScopePatterns = [
806
- /新增|新建|模块|流程|编排|一站式|权限|审批|团队|agent\s*市场|AI/i,
968
+ const l2StructuralScopePatterns = [
969
+ /模块/,
970
+ /入口/,
971
+ /流程/,
972
+ /导入流/,
973
+ /编排/,
974
+ /workflow/i,
975
+ /wizard/i,
976
+ ];
977
+ const l2StrategicScopePatterns = [
978
+ /一站式/,
979
+ /团队搭建/,
980
+ /agent\s*市场/i,
981
+ /skill\s*library/i,
982
+ /cli\s*库/i,
983
+ /权限/,
984
+ /审批/,
985
+ /计费/,
986
+ /账号/,
987
+ /第三方/,
988
+ /云服务/,
989
+ /迁移/,
990
+ /跨系统/,
991
+ /(AI|模型).{0,8}(接入|集成|编排)/i,
992
+ ];
993
+ const structuralExpansionIntensityPatterns = [
994
+ /串联/,
995
+ /一站式/,
996
+ /整体/,
997
+ /全链路/,
998
+ /端到端/,
999
+ /体系/,
1000
+ /平台/,
1001
+ /多个/,
1002
+ ];
1003
+ const featurePlanningPatterns = [
1004
+ /需求/,
1005
+ /方案/,
1006
+ /规划/,
1007
+ /产品/,
1008
+ ];
1009
+ const capabilityCreationPatterns = [
1010
+ /新增/,
1011
+ /增加/,
1012
+ /新建/,
1013
+ /做(一个|个)?/,
1014
+ /支持/,
1015
+ /提供/,
1016
+ /搭建/,
1017
+ /引入/,
1018
+ /接入/,
1019
+ /集成/,
1020
+ ];
1021
+ const crossSystemRiskPatterns = [
1022
+ /支付|登录|注册|账号|权限|订单|回调|风控|退款|计费|同步|迁移|数据库|schema|协议|网关|跨系统|第三方|云服务|OSS|CDN|MCP|SDK|CLI/i,
1023
+ ];
1024
+ const l1OptimizationPatterns = [
1025
+ /(优化|调整|改版|重做|重构|增强|补齐|梳理|统一|整理)/,
1026
+ ];
1027
+ const l0AdjustmentPatterns = [
1028
+ /(修复|修一下|改一下|调一下|换成|改成|去掉|补一个|补一下)/,
807
1029
  ];
808
1030
  const explicitExecutionPatterns = [
809
1031
  /直接(帮我|给我)?(改|做|实现|落地|修|修复|处理|解决)/,
810
1032
  /如果.{0,24}(定位|确认|找到).{0,12}(原因|根因).{0,24}直接.{0,12}(帮我|给我)?(修|修复|改|处理|解决)/,
811
1033
  /开始(改|做|实现|开发|落地)/,
1034
+ /继续(改|做|实现|开发|落地|修|修复|处理|解决)/,
812
1035
  /请(直接)?(实现|落地|修改|修复|处理|解决)/,
813
1036
  /可以(执行|落地|实现|开发)/,
1037
+ /去(改|做|实现|开发|落地|修|修复|处理|解决)吧/,
1038
+ /那你去(改|做|实现|开发|落地|修|修复|处理|解决)吧/,
814
1039
  ];
815
1040
  const implementationConfirmationPatterns = [
816
1041
  /确认.*(执行|落地|实现|继续|开发|修复|修改|处理|解决|改)/,
@@ -832,7 +1057,12 @@ function analyzePromptIntent(prompt) {
832
1057
  /别再确认/,
833
1058
  /不需要再跟我确认/,
834
1059
  ];
835
- const promptReviewCommand = parseReviewMarkCommand(text);
1060
+ const reviewContinuationPatterns = [
1061
+ /认可方案并继续/,
1062
+ /认可并继续/,
1063
+ /继续当前\s*openprd\s*下一步/i,
1064
+ /按当前\s*openprd\s*下一步继续/i,
1065
+ ];
836
1066
  const reviewConfirmPatterns = [
837
1067
  /认可方案/,
838
1068
  /确认(?:当前|这个|这版|该)?(?:PRD|评审稿|评审页|review|需求稿|版本)/i,
@@ -844,22 +1074,38 @@ function analyzePromptIntent(prompt) {
844
1074
  ];
845
1075
  const readOnlyPatterns = [
846
1076
  /看看/,
1077
+ /看下(一下)?/,
1078
+ /你看/,
847
1079
  /规划/,
848
1080
  /分析/,
1081
+ /先分析(一下)?/,
849
1082
  /梳理/,
850
1083
  /评估/,
851
1084
  /怎么改/,
852
1085
  /预计动哪些文件/,
1086
+ /看(看)?(一下)?(原因|问题|风险|情况)/,
1087
+ /什么原因/,
1088
+ /怎么看/,
1089
+ /你觉得/,
1090
+ /可行吗/,
1091
+ /有没有可能/,
1092
+ /会有什么问题/,
1093
+ /值不值得/,
853
1094
  /review/i,
854
1095
  /explain/i,
855
1096
  ];
856
1097
  const bugfixOrDiagnostic = /诊断包|报错|错误|异常|崩溃|bug|问题|排查|定位|根因|复现|日志|故障/i.test(text)
857
1098
  || /失败.{0,20}(原因|根因|排查|定位|修|修复|处理|解决)|(?:原因|根因|排查|定位).{0,20}失败/.test(text);
858
- const simpleConcrete = text.length <= 80
859
- && simpleConcretePatterns.some((pattern) => pattern.test(text))
860
- && !complexScopePatterns.some((pattern) => pattern.test(text));
861
- const explicitExecution = internalOpenPrdExecution || continuationRequest || explicitExecutionPatterns.some((pattern) => pattern.test(text));
862
- const implementationConfirmation = implementationConfirmationPatterns.some((pattern) => pattern.test(text));
1099
+ const tinyEdit = tinyEditPatterns.some((pattern) => pattern.test(text));
1100
+ const crossSystemRiskMatched = crossSystemRiskPatterns.some((pattern) => pattern.test(text));
1101
+ const localUiScopeMatched = /(按钮|文案|颜色|圆角|位置|间距|字号|图标|标题|空格|标点|label|copy|toast|placeholder|样式|页面|界面|布局|信息架构|导航|列表|详情页|设置页)/i.test(text);
1102
+ const reviewContinuationRequested = reviewContinuationPatterns.some((pattern) => pattern.test(text));
1103
+ const explicitExecution = internalOpenPrdExecution
1104
+ || continuationVerbMatched
1105
+ || reviewContinuationRequested
1106
+ || explicitExecutionPatterns.some((pattern) => pattern.test(text));
1107
+ const implementationConfirmation = reviewContinuationRequested
1108
+ || implementationConfirmationPatterns.some((pattern) => pattern.test(text));
863
1109
  const noReviewRequested = noReviewRequestedPatterns.some((pattern) => pattern.test(text));
864
1110
  const noConfirmationRequested = noConfirmationRequestedPatterns.some((pattern) => pattern.test(text));
865
1111
  const reviewDecision = promptReviewCommand?.mark
@@ -873,8 +1119,38 @@ function analyzePromptIntent(prompt) {
873
1119
  const visualMockupRequest = imageGenerationTerms.test(text)
874
1120
  && imageGenerationAction.test(text)
875
1121
  && !codeVisualArtifactRequested;
1122
+ const largeUiChangeRequest = /(界面|页面|视觉|样式|UI|前端体验|布局|信息架构|主视觉|效果图|视觉稿|mockup|设计方向|设计预览)/i.test(text)
1123
+ && /(大|较大|比较大|明显|重做|重构|改版|优化|重新设计|设计方向|三种|3种|方案|效果图|先看样子|确认方向|体验优化|产品内)/i.test(text);
876
1124
  const visualReview = /效果图|实现截图|视觉对比|视觉评审|对标效果图|复刻/i.test(text);
877
1125
  const directBugfixExecution = explicitExecution && bugfixOrDiagnostic;
1126
+ const newFeatureVerbMatched = /(新增|增加|新建)/.test(text);
1127
+ const capabilityCreationMatched = capabilityCreationPatterns.some((pattern) => pattern.test(text));
1128
+ const featurePlanningMatched = featurePlanningPatterns.some((pattern) => pattern.test(text));
1129
+ const structuralL2ScopeMatchCount = l2StructuralScopePatterns.filter((pattern) => pattern.test(text)).length;
1130
+ const strategicL2ScopeMatched = l2StrategicScopePatterns.some((pattern) => pattern.test(text));
1131
+ const structuralExpansionMatched = structuralL2ScopeMatchCount >= 2
1132
+ || (structuralL2ScopeMatchCount >= 1
1133
+ && (structuralExpansionIntensityPatterns.some((pattern) => pattern.test(text)) || (capabilityCreationMatched && !localUiScopeMatched)));
1134
+ const l2ScopeMatched = strategicL2ScopeMatched || structuralExpansionMatched;
1135
+ const simpleConcrete = text.length <= 80
1136
+ && simpleConcretePatterns.some((pattern) => pattern.test(text))
1137
+ && !l2ScopeMatched;
1138
+ const requirementSignalMatched = requirementRoutingSignalsStrong.some((pattern) => pattern.test(text))
1139
+ || (requirementRoutingSignalsWeak.some((pattern) => pattern.test(text))
1140
+ && requirementChangeIntentSignals.some((pattern) => pattern.test(text)));
1141
+ const l2FeatureExpansionMatched = !localUiScopeMatched
1142
+ && (
1143
+ (capabilityCreationMatched && (strategicL2ScopeMatched || structuralExpansionMatched))
1144
+ || (newFeatureVerbMatched && text.length > 18)
1145
+ );
1146
+ const l2PlanningRequestMatched = !readOnly
1147
+ && featurePlanningMatched
1148
+ && requirementChangeIntentSignals.some((pattern) => pattern.test(text))
1149
+ && (strategicL2ScopeMatched || structuralExpansionMatched);
1150
+ const l1OptimizationMatched = l1OptimizationPatterns.some((pattern) => pattern.test(text))
1151
+ && /(页面|界面|视觉|样式|布局|信息架构|交互|体验|流程|入口|导航|表单|设置页|列表|详情页)/i.test(text);
1152
+ const l0AdjustmentMatched = l0AdjustmentPatterns.some((pattern) => pattern.test(text))
1153
+ && /(按钮|文案|颜色|圆角|位置|间距|字号|图标|标题|空格|标点|label|copy|toast|placeholder|样式|一处)/i.test(text);
878
1154
  const publicRepoResearchRequest = githubRepoPattern.test(text)
879
1155
  && /(github|仓库|repo|项目|参考|对标|复刻|review|学习|架构|模块|流程|构建|测试|扩展点)/i.test(text);
880
1156
  const externalTechResearchRequest = /(第三方|library|framework|sdk|api|mcp|cli|依赖|包|版本|迁移|弃用|官方文档|参数|返回值|生命周期)/i.test(text)
@@ -883,19 +1159,41 @@ function analyzePromptIntent(prompt) {
883
1159
  || (/(^|[^a-z])(skill|skills)([^a-z]|$)/i.test(text) && /(创建|修改|优化|重构|合并|拆分|更新|工作流|workflow|流程|路由|router|提示词|规则)/i.test(text))
884
1160
  || (/AGENTS\.md/i.test(text) && /(创建|修改|优化|精简|收薄|重构|更新)/i.test(text));
885
1161
  const secretsRequest = /(api\s*key|token|secret|credential|password|凭证|密钥|密码|账号信息|第三方服务凭证|个人信息|登录信息)/i.test(text);
886
- const weappValidationRequest = /(微信小程序|miniprogram|weapp|微信开发者工具|weapp-dev-mcp)/i.test(text);
1162
+ const weappMention = /(微信小程序|miniprogram|weapp|微信开发者工具|weapp-dev-mcp)/i.test(text);
1163
+ const weappValidationAction = /(测试|验证|实测|复现|截图|日志|抓日志|抓包|网络请求|network|运行态|开发者工具自动化|从\s*0\s*到\s*1|冷启动|重开|重新打开|全流程)/i.test(text);
1164
+ const weappValidationRequest = /weapp-dev-mcp/i.test(text)
1165
+ || (weappMention && weappValidationAction);
887
1166
  const browserSafetyRequest = /(computer use|browser use|浏览器|browser|网页|页面|窗口|标签页|tab|profile)/i.test(text)
888
1167
  && /(点击|输入|提交|登录|注销|退出|支付|关闭|send|submit|type|click|switch account|切换账号)/i.test(text);
889
1168
  const productCopyRequest = /(文案|copy|错误文案|空状态|成功提示|按钮文案|提示语|toast|placeholder|设置项文案|国际化|i18n|locales|translations|localizable)/i.test(text);
890
- const requiresIntake = !internalOpenPrdExecution
891
- && requirementRoutingSignals.some((pattern) => pattern.test(text))
892
- && !tinyEditPatterns.some((pattern) => pattern.test(text))
893
- && !simpleConcrete
894
- && !visualMockupRequest
895
- && !directBugfixExecution
896
- && !(readOnly && !explicitExecution);
1169
+ const l2RequirementCandidate = (bugfixOrDiagnostic && crossSystemRiskMatched)
1170
+ || l2FeatureExpansionMatched
1171
+ || l2PlanningRequestMatched;
1172
+ const requirementTier = !internalOpenPrdExecution
1173
+ ? (l2RequirementCandidate
1174
+ && !tinyEdit
1175
+ && !simpleConcrete
1176
+ && !visualMockupRequest
1177
+ && !(readOnly && !explicitExecution)
1178
+ ? 'l2'
1179
+ : (!visualMockupRequest
1180
+ && (!readOnly || explicitExecution)
1181
+ && (tinyEdit
1182
+ || simpleConcrete
1183
+ || l0AdjustmentMatched
1184
+ || (directBugfixExecution && !crossSystemRiskMatched)
1185
+ || (bugfixOrDiagnostic && !crossSystemRiskMatched))
1186
+ ? 'l0'
1187
+ : (!visualMockupRequest
1188
+ && !readOnly
1189
+ && (largeUiChangeRequest || l1OptimizationMatched || requirementSignalMatched)
1190
+ ? 'l1'
1191
+ : null)))
1192
+ : null;
1193
+ const requiresIntake = requirementTier === 'l2';
897
1194
  return {
898
1195
  promptText: text,
1196
+ requirementTier,
899
1197
  requiresIntake,
900
1198
  explicitExecution,
901
1199
  confirmation,
@@ -904,9 +1202,11 @@ function analyzePromptIntent(prompt) {
904
1202
  noConfirmationRequested,
905
1203
  reviewDecision,
906
1204
  reviewCommand: promptReviewCommand,
1205
+ reviewContinuationRequested,
907
1206
  readOnly,
908
1207
  simpleConcrete,
909
1208
  visualMockupRequest,
1209
+ largeUiChangeRequest,
910
1210
  continuationRequest,
911
1211
  continuationSessionId,
912
1212
  continuationTaskHandle,
@@ -919,10 +1219,12 @@ function analyzePromptIntent(prompt) {
919
1219
  browserSafetyRequest,
920
1220
  productCopyRequest,
921
1221
  shouldInject: requiresIntake
1222
+ || requirementTier === 'l1'
922
1223
  || explicitExecution
923
1224
  || confirmation
924
1225
  || readOnly
925
1226
  || visualMockupRequest
1227
+ || largeUiChangeRequest
926
1228
  || continuationRequest
927
1229
  || visualReview
928
1230
  || publicRepoResearchRequest
@@ -988,7 +1290,7 @@ function openRequirementGate(root, prompt, intent, sessionId = null) {
988
1290
  openedAt: current?.openedAt || now(),
989
1291
  updatedAt: now(),
990
1292
  promptPreview: preview(prompt, 500),
991
- reason: 'requirement-intake routing candidate',
1293
+ reason: 'likely-l2 requirement flow',
992
1294
  requiredFlow: ['requirement-intake', 'clarify', 'capture', 'synthesize', 'review', 'change-generate', 'tasks', 'implementation'],
993
1295
  intakeMode: detectRequirementIntakeMode(prompt),
994
1296
  intent,
@@ -1132,7 +1434,7 @@ function openWeappGate(root, prompt, sessionId = null) {
1132
1434
  return writeNamedGate(root, 'weapp', {
1133
1435
  version: 1,
1134
1436
  active: true,
1135
- status: 'needs-weapp-mcp-validation',
1437
+ status: 'needs-weapp-runtime-validation',
1136
1438
  openedAt: now(),
1137
1439
  updatedAt: now(),
1138
1440
  promptPreview: preview(prompt, 500),
@@ -1174,27 +1476,34 @@ function evaluateRequirementGateProgress(root, sessionId = null) {
1174
1476
  const taskSummary = activeChange ? readLocalTaskSummary(root, activeChange) : null;
1175
1477
  const hasTaskBreakdown = Boolean(activeChange && Number(taskSummary?.total ?? 0) > 0);
1176
1478
  const approvalPolicy = requirementApprovalPolicy(gate);
1479
+ const clarificationConfirmed = requirementWritePathExplicitlyAuthorized(gate);
1177
1480
  let nextStep = 'implementation-ready';
1178
- let reason = 'PRD 评审与任务拆解已就绪;如果当前需求原本就明确要求实现,可直接继续执行,否则等待一句明确的执行指令。';
1481
+ let reason = '这版需求和后续任务都已经准备好了;如果用户原本就明确要继续做,就直接往下推进,否则再补一句清楚的人话授权。';
1179
1482
  if (!review.versionId) {
1180
- nextStep = 'prd-review-required';
1181
- reason = '当前还没有本轮最新 PRD 评审产物,先 synthesize 出稳定 review artifact,再等待用户评审。';
1483
+ nextStep = clarificationConfirmed ? 'prd-synthesis-required' : 'clarification-confirmation-required';
1484
+ reason = clarificationConfirmed
1485
+ ? (
1486
+ reviewPolicyAllowsSilentRecord(approvalPolicy)
1487
+ ? '用户已明确表示不需要再停下来确认,本轮可以直接整理需求事实、生成这版确认稿,并继续后面的整理步骤。'
1488
+ : '当前需求摘要已经确认,下一步把已确认内容整理成可确认的需求稿。'
1489
+ )
1490
+ : '当前还缺需求摘要确认。先在对话里按“需求判断 / 需求理解 / 功能范围 / 技术方案”整理结构化摘要,其中“功能范围”和“技术方案”优先用 Markdown 表格;用户没确认前,不要直接把核心需求写成既定事实。';
1182
1491
  } else if (review.status === 'needs-revision') {
1183
1492
  nextStep = 'prd-review-required';
1184
- reason = '当前 PRD review artifact 已标记为需要调整,不能继续生成 change 或实现。';
1493
+ reason = '当前这版需求确认稿已经被标记为需要调整,先改完再继续后面的整理或实现。';
1185
1494
  } else if (review.status !== 'confirmed') {
1186
1495
  nextStep = reviewPolicyAllowsSilentRecord(approvalPolicy)
1187
1496
  ? 'review-recording-required'
1188
1497
  : 'prd-review-required';
1189
1498
  reason = reviewPolicyAllowsSilentRecord(approvalPolicy)
1190
- ? '当前稳定 PRD 评审稿已经生成。由于用户一开始已明确要求直接做且不再额外评审/确认,本轮可以直接记录这版稳定 review artifact,再继续 change 和 tasks。'
1191
- : '当前 PRD review artifact 还没有被用户确认,不能把实现授权当成 review 确认。';
1499
+ ? '这版需求确认稿已经生成。由于用户已经明确表示不需要再停下来确认,本轮可以直接记录这次确认结果,再继续整理本次调整和后续任务。'
1500
+ : '这版需求确认稿还没有得到用户认可,不能把“请帮我实现/继续实现”当成已经确认这版内容。';
1192
1501
  } else if (!activeChange) {
1193
1502
  nextStep = 'change-generation-required';
1194
- reason = 'PRD 评审已确认,下一步先生成 OpenPrd change。';
1503
+ reason = '这版需求已经确认,下一步先整理本次调整范围。';
1195
1504
  } else if (!hasTaskBreakdown) {
1196
1505
  nextStep = 'task-breakdown-required';
1197
- reason = 'OpenPrd change 已存在,但还缺任务拆解,不能直接进入实现。';
1506
+ reason = '本次调整范围已经立起来了,但还没拆成可直接执行的后续任务。';
1198
1507
  }
1199
1508
  return {
1200
1509
  runContext: parsed,
@@ -1225,6 +1534,11 @@ function reviewActionAuthorizationFor(intent, progress, prompt) {
1225
1534
  versionId: review.versionId,
1226
1535
  digest: review.digest,
1227
1536
  workUnitId: review.workUnitId,
1537
+ continueAfterReview: Boolean(
1538
+ intent?.reviewContinuationRequested
1539
+ || intent?.implementationConfirmation
1540
+ || intent?.explicitExecution
1541
+ ),
1228
1542
  promptPreview: preview(prompt, 500),
1229
1543
  grantedAt: now(),
1230
1544
  source: 'explicit-user-review-decision',
@@ -1253,11 +1567,16 @@ function silentReviewActionAuthorizationFor(gate, progress, prompt) {
1253
1567
 
1254
1568
  function holdRequirementGate(root, prompt, progress, sessionId = null, extra = {}) {
1255
1569
  const reviewActionAuthorization = extra.reviewActionAuthorization ?? null;
1256
- return updateRequirementGate(root, {
1570
+ const patch = {
1257
1571
  status: extra.status ?? progress.nextStep,
1258
1572
  confirmationPreview: preview(prompt, 500),
1259
1573
  reviewActionAuthorization,
1260
- }, sessionId);
1574
+ };
1575
+ if (extra.clarificationConfirmedAt) {
1576
+ patch.clarificationConfirmedAt = extra.clarificationConfirmedAt;
1577
+ patch.clarificationConfirmationPreview = extra.clarificationConfirmationPreview ?? preview(prompt, 500);
1578
+ }
1579
+ return updateRequirementGate(root, patch, sessionId);
1261
1580
  }
1262
1581
 
1263
1582
  function isImplementationAdvanceIntent(intent) {
@@ -1336,6 +1655,28 @@ function isMutationPayload(payload, risk) {
1336
1655
  || /\*\*\* Begin Patch/.test(text);
1337
1656
  }
1338
1657
 
1658
+ function captureSourceFromCommand(command) {
1659
+ return readCliFlagValue(command, '--source');
1660
+ }
1661
+
1662
+ function isNonSemanticCaptureCommand(command) {
1663
+ return /openprd\s+capture\b/i.test(command)
1664
+ && captureSourceFromCommand(command) === 'agent-normalized';
1665
+ }
1666
+
1667
+ function gateHasClarificationConfirmation(gate) {
1668
+ return Boolean(gate?.clarificationConfirmedAt || gate?.status === 'clarification-confirmed');
1669
+ }
1670
+
1671
+ function requirementWritePathExplicitlyAuthorized(gate) {
1672
+ return gateHasClarificationConfirmation(gate)
1673
+ || reviewPolicyAllowsSilentRecord(requirementApprovalPolicy(gate));
1674
+ }
1675
+
1676
+ function isRequirementWritePathMutation(command) {
1677
+ return /openprd\s+(capture|classify|synthesize|diagram)\b/i.test(command);
1678
+ }
1679
+
1339
1680
  function isAllowedDuringRequirementGate(root, payload, gate, sessionId = null) {
1340
1681
  const text = payloadText(payload);
1341
1682
  const command = commandText(payload);
@@ -1346,12 +1687,7 @@ function isAllowedDuringRequirementGate(root, payload, gate, sessionId = null) {
1346
1687
  /openprd\s+run\s+\.\s+--context\b/i,
1347
1688
  /openprd\s+run\s+\.\s+--verify\b/i,
1348
1689
  /openprd\s+clarify\b/i,
1349
- /openprd\s+capture\b/i,
1350
- /openprd\s+classify\b/i,
1351
1690
  /openprd\s+interview\b/i,
1352
- /openprd\s+synthesize\b/i,
1353
- /openprd\s+diagram\b/i,
1354
- /openprd\s+review-presentation\b/i,
1355
1691
  /openprd\s+standards\s+.*--verify/i,
1356
1692
  /openprd\s+quality\s+.*--verify/i,
1357
1693
  /openprd\s+doctor\b/i,
@@ -1359,6 +1695,15 @@ function isAllowedDuringRequirementGate(root, payload, gate, sessionId = null) {
1359
1695
  if (alwaysAllowed.some((pattern) => pattern.test(text))) {
1360
1696
  return true;
1361
1697
  }
1698
+ if (/openprd\s+capture\b/i.test(command)) {
1699
+ return isNonSemanticCaptureCommand(command) || requirementWritePathExplicitlyAuthorized(gate);
1700
+ }
1701
+ if (/openprd\s+(classify|synthesize|diagram)\b/i.test(command)) {
1702
+ return requirementWritePathExplicitlyAuthorized(gate);
1703
+ }
1704
+ if (/openprd\s+review-presentation\b/i.test(command)) {
1705
+ return /--template\b/i.test(command) || Boolean(progress.review.versionId);
1706
+ }
1362
1707
  if (/openprd\s+review\b/i.test(command)) {
1363
1708
  if (!/--mark\b/i.test(command)) {
1364
1709
  return true;
@@ -1458,7 +1803,7 @@ function applyWeappToolSignal(root, payload, sessionId = null) {
1458
1803
  const satisfied = validationSignals.ensureConnection && validationSignals.runtimeAction;
1459
1804
  if (satisfied) {
1460
1805
  return closeWeappGate(root, sessionId, {
1461
- status: 'validated-through-weapp-mcp',
1806
+ status: 'validated-through-weapp-runtime-evidence',
1462
1807
  validationSignals,
1463
1808
  validatedAt: now(),
1464
1809
  });
@@ -1523,8 +1868,10 @@ function weappGateMessage(gate) {
1523
1868
  }
1524
1869
  return [
1525
1870
  'OpenPrd 微信小程序验证门禁: active。',
1526
- '当前任务涉及微信小程序测试、验证、截图、日志、网络请求、微信开发者工具自动化,或可能影响小程序运行态的代码修改。',
1527
- '请先使用 `weapp-dev-mcp` skill,并通过本地 `weapp-dev-mcp` MCP 完成运行态验证;未通过本地 MCP 实际验证时,不要宣称“小程序已验证”。',
1871
+ '只有当用户明确要求小程序实测、复现、截图、抓日志/网络、从 0 到 1 走流程,或当前改动高风险到必须依赖运行态证据时,才升级到本地小程序运行态验证。',
1872
+ '一旦进入小程序运行态验证,默认沿用当前小程序运行态或开发者工具会话连续验证,不要为了验证自动重开应用;只有用户明确要求从 0 1、冷启动、重开或重新打开时,才从头启动。',
1873
+ '优先使用当前环境已配置的小程序本地验证能力;如果当前客户端没有相应工具,不要假定已经安装,也不要把缺少工具本身当成任务失败。',
1874
+ '未拿到本地运行态证据前,不要宣称“小程序已验证”。',
1528
1875
  ].join('\n');
1529
1876
  }
1530
1877
 
@@ -1559,12 +1906,44 @@ function composeHookContext(root, intent = null, gate = null, progress = null, s
1559
1906
  weappGateMessage(readNamedGate(root, 'weapp', sessionId)),
1560
1907
  browserSafetyMessage(intent),
1561
1908
  productCopyMessage(intent),
1909
+ largeUiVisualDirectionMessage(intent),
1562
1910
  ].filter(Boolean).join('\n');
1563
1911
  }
1564
1912
 
1565
1913
  function runOpenPrd(args, cwd) {
1566
- const command = process.env.OPENPRD_CLI || 'openprd';
1567
- const result = spawnSync(command, args, {
1914
+ const configuredCommand = String(process.env.OPENPRD_CLI || 'openprd').trim() || 'openprd';
1915
+ const jsEntry = /\.(?:c|m)?js$/i.test(configuredCommand);
1916
+ let command = configuredCommand;
1917
+ let commandArgs = args;
1918
+ if (jsEntry) {
1919
+ const candidates = [];
1920
+ const addCollapsedPackageCandidate = (candidatePath) => {
1921
+ if (!candidatePath) {
1922
+ return;
1923
+ }
1924
+ const normalized = path.resolve(candidatePath);
1925
+ const binDir = path.dirname(normalized);
1926
+ const packageDir = path.dirname(binDir);
1927
+ const packageName = path.basename(packageDir);
1928
+ if (path.basename(binDir) === 'bin' && packageName === 'openprd') {
1929
+ candidates.push(path.join(path.dirname(packageDir), 'bin', 'openprd.js'));
1930
+ }
1931
+ };
1932
+ if (path.isAbsolute(configuredCommand)) {
1933
+ candidates.push(configuredCommand);
1934
+ addCollapsedPackageCandidate(configuredCommand);
1935
+ } else {
1936
+ candidates.push(path.resolve(cwd, configuredCommand));
1937
+ candidates.push(path.resolve(configuredCommand));
1938
+ addCollapsedPackageCandidate(path.resolve(cwd, configuredCommand));
1939
+ addCollapsedPackageCandidate(path.resolve(configuredCommand));
1940
+ }
1941
+ candidates.push(path.join(cwd, 'bin', 'openprd.js'));
1942
+ const resolvedEntry = candidates.find((candidate) => candidate && fs.existsSync(candidate));
1943
+ command = process.execPath;
1944
+ commandArgs = [resolvedEntry || configuredCommand, ...args];
1945
+ }
1946
+ const result = spawnSync(command, commandArgs, {
1568
1947
  cwd,
1569
1948
  encoding: 'utf8',
1570
1949
  timeout: 15000,
@@ -1590,6 +1969,34 @@ function parseJsonOutput(text) {
1590
1969
  }
1591
1970
  }
1592
1971
 
1972
+ function devCheckWrapUpMessage(root, turnState) {
1973
+ const files = Array.isArray(turnState?.touchedFiles)
1974
+ ? [...new Set(turnState.touchedFiles)].filter(Boolean)
1975
+ : [];
1976
+ if (files.length === 0) {
1977
+ return null;
1978
+ }
1979
+ const result = runOpenPrd(['dev-check', '.', ...files, '--json'], root);
1980
+ if (!result.ok && !result.stdout) {
1981
+ return [
1982
+ 'OpenPrd 收工回顾:本轮有 touched code files,但 dev-check 未能完成。',
1983
+ '最终回复里请说明无法生成大文件审查表,并列出失败原因。',
1984
+ result.stderr ? `错误: ${result.stderr}` : null,
1985
+ ].filter(Boolean).join('\n');
1986
+ }
1987
+ const parsed = parseJsonOutput(result.stdout);
1988
+ if (!parsed?.wrapUp?.required || !parsed.wrapUp.markdownTable) {
1989
+ return null;
1990
+ }
1991
+ return [
1992
+ 'OpenPrd 后续建议:本轮有改动对象需要主动说明。',
1993
+ '最终回复必须直接复用下面的 Markdown 表格,按 🔴 → 🟠 → 🟡 的顺序帮助产品或业务理解影响对象、本次处理结果和后续建议;不要只用工具名或一段话带过。',
1994
+ '如果你改写了“预警原因 / 本次处理结果 / 后续建议”,先用 `node scripts/dev-check-wrapup-copy.mjs --validate` 校验每格不超过 20 字;若报错,按提示缩短后重试。',
1995
+ '不要把“关注程度”列改写成纯 emoji;必须保留例如“🟠 中风险|建议优先关注”这类完整标签。',
1996
+ parsed.wrapUp.markdownBlock ?? parsed.wrapUp.markdownTable,
1997
+ ].join('\n');
1998
+ }
1999
+
1593
2000
  function shouldRunDoctorForHighRisk(payload) {
1594
2001
  const text = commandText(payload) || payloadText(payload);
1595
2002
  return /(git\s+(commit|push)\b|npm\s+publish|pnpm\s+publish|yarn\s+npm\s+publish|gh\s+release|openprd\s+(freeze|handoff|setup|update|fleet|doctor)\b|openprd\s+change\s+.*--(apply|archive)\b|release|publish)/i.test(text);
@@ -1608,7 +2015,8 @@ function summarizeRunVerifyCheck(parsed, fallbackText = '') {
1608
2015
  return `run-verify: taskReady=no${failedTaskChecks.length ? ` (${failedTaskChecks.join(', ')})` : ''}`;
1609
2016
  }
1610
2017
  if (readiness.workspaceReady === false) {
1611
- return `run-verify: taskReady=yes, workspaceReady=no${workspaceWarnings.length ? ` (${workspaceWarnings[0]})` : ''}`;
2018
+ const workspaceDetail = parsed.workspaceAttention?.summary ?? workspaceWarnings[0] ?? null;
2019
+ return `run-verify: taskReady=yes, workspaceReady=no${workspaceDetail ? ` (${workspaceDetail})` : ''}`;
1612
2020
  }
1613
2021
  return 'run-verify: taskReady=yes, workspaceReady=yes';
1614
2022
  }
@@ -1655,7 +2063,11 @@ function buildGateFailureEnvelope(result) {
1655
2063
  if (runCheck?.workspaceReady === false) {
1656
2064
  return {
1657
2065
  kind: 'workspace-debt',
1658
- details: [runCheck.summary, ...(runCheck.warnings ?? [])].filter(Boolean),
2066
+ details: [
2067
+ runCheck.summary,
2068
+ runCheck.workspaceAttention?.detail ?? null,
2069
+ ...(runCheck.warnings ?? []),
2070
+ ].filter(Boolean),
1659
2071
  repair: 'Repair path: resolve the workspace-level debt from openprd run . --verify or openprd quality . --verify, then retry this high-risk action.',
1660
2072
  };
1661
2073
  }
@@ -1732,40 +2144,81 @@ function requirementGateMessage(intent, gate) {
1732
2144
  'The user is asking for an image asset such as a cover image, poster, illustration, icon, sticker, visual mockup, or effect image, not code implementation.',
1733
2145
  'For logo, icon, avatar, badge, and similar development assets, default to a standalone asset: full-frame single subject with no extra UI frame, card shell, device mockup, or presentation container unless the user explicitly asked for one.',
1734
2146
  'Do not create temporary HTML/SVG/CSS files for this image unless the user explicitly requested that format.',
1735
- 'Use Codex native Image 2 to generate the image; keep implementation, PRD review, and visual-compare for later explicit confirmation.',
2147
+ 'Use `imagegen`, which is Codex native Image 2, to generate the image; keep implementation, PRD review, and visual-compare for later explicit confirmation.',
1736
2148
  ].join('\n');
1737
2149
  }
1738
2150
  const status = gateBlocksImplementation ? 'active' : 'opened';
1739
2151
  return [
1740
2152
  'OpenPrd requirement intake gate: ' + status + '.',
1741
- 'This prompt matched the requirement-intake safety lane. Do not decide from fixed keywords; first use $openprd-requirement-intake to classify L0/L1/L2 by impact, unknowns, decision cost, and validation cost.',
1742
- 'If the intake decision is L2, do not edit implementation files yet and proceed through PRD/review/change/tasks with the appropriate base/consumer/b2b/agent PRD lens.',
2153
+ 'This prompt looks like a likely 新功能/新流程方案 (L2), so the heavy requirement-intake lane is active. Do not decide from fixed keywords; first use $openprd-requirement-intake to classify the user-visible requirement type by impact, unknowns, decision cost, and validation cost.',
2154
+ 'Keep this mapping visible for internal review: 快速修正=L0, 现有功能优化=L1, 新功能/新流程方案=L2.',
2155
+ 'L0 and L1 stay on lightweight paths and should not be forced through formal PRD/review/change/tasks unless the scope expands.',
2156
+ 'If the requirement type is 新功能/新流程方案 (L2), do not edit implementation files yet and proceed through PRD/review/change/tasks with the appropriate PRD scene lens: 通用场景、面向个人消费者场景、面向企业服务场景,或以 Agent 为主要使用场景。 Keep raw enum values such as base / consumer / b2b / agent for internal commands or records only; do not surface them to the user unless truly necessary.',
1743
2157
  reviewPolicyAllowsSilentRecord(approvalPolicy)
1744
- ? 'Decision-point policy: clarify the requirement, capture user answers, synthesize the PRD, record the exact current stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.'
1745
- : 'Decision-point policy: clarify the requirement, capture user answers, synthesize the PRD, wait for a human decision on the stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.',
2158
+ ? 'Decision-point policy: because the user explicitly said there is no need for any confirmation stop, you may skip the requirement-summary confirmation stop, write back the requirement facts, synthesize the PRD, record the exact current stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.'
2159
+ : 'Decision-point policy: first output a short structured requirement summary in chat with 需求判断 / 需求理解 / 功能范围 / 技术方案, where 功能范围 and 技术方案 should prefer Markdown tables; wait for the user to confirm that summary, then write back confirmed facts, synthesize the PRD, wait for a human decision on the stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.',
1746
2160
  reviewPolicyAllowsSilentRecord(approvalPolicy)
1747
- ? 'This lane is in silent-record mode because the user upfront asked to implement without another review stop. You may record only the exact current version, digest, and work unit.'
1748
- : 'Review-artifact confirmation and implementation authorization are different gates: do not treat "可以开做" as permission to run openprd review --mark confirmed.',
2161
+ ? 'This lane is in silent-record mode only because the user explicitly said there is no need for any further review or confirmation stop. Plain "请帮我实现" is not enough; you may record only the exact current version, digest, and work unit.'
2162
+ : 'Requirement-summary confirmation, review-artifact confirmation, and implementation authorization are different gates: do not treat "可以开做", "继续实现", plain "请帮我实现", or "不需要评审" as permission to skip them.',
1749
2163
  'If the original request already asked to implement, execution can continue once the active approval policy and tasks are ready; otherwise wait for a clear execution request.',
1750
- 'Recommended next action: write a short Intake Decision in chat, then for L2 run openprd clarify ., summarize target, scope, out-of-scope, and acceptance in chat, then ask for confirmation. Do not open a clarification HTML page; the formal HTML review happens after synthesize/review.',
2164
+ 'Recommended next action: write a short 需求类型判断 in chat, and by default merge the route into the label as 需求类型:新功能/新流程方案(L2); only add a separate 内部路由码 when internal debugging truly benefits. Then run openprd clarify ., summarize the requirement in chat using 需求判断 / 需求理解 / 功能范围 / 技术方案, prefer Markdown tables for 功能范围 and 技术方案, ask for confirmation, and only after that write back confirmed facts. Do not open a clarification HTML page; the formal HTML review happens after synthesize/review.',
1751
2165
  ].join('\n');
1752
2166
  }
1753
2167
 
2168
+ function lightweightRequirementMessage(intent) {
2169
+ if (intent?.requirementTier === 'l0') {
2170
+ return [
2171
+ 'OpenPrd 轻量需求路径: 当前更接近快速修正 (L0)。',
2172
+ '先在 chat 用短格式写出“需求类型 / 理由 / 推荐下一步”,并默认写成“需求类型:快速修正(L0)”;只有内部排障确实需要时,才额外单列“内部路由码”。',
2173
+ '直接处理并事后说明即可,不打开正式 PRD/review/change/tasks。',
2174
+ '优先做最小足够验证,并用 1-2 句说明本轮特别需要强化的测试点;默认不要求正式测试报告。',
2175
+ '如果过程中暴露出跨系统依赖、支付/账号/权限/回调等高风险因素,再升级到 L2 重流程。',
2176
+ ].join('\n');
2177
+ }
2178
+ if (intent?.requirementTier === 'l1') {
2179
+ return [
2180
+ 'OpenPrd 轻量需求路径: 当前更接近现有功能优化 (L1)。',
2181
+ '先在 chat 用短格式写出“需求类型 / 理由 / 推荐下一步”,并默认写成“需求类型:现有功能优化(L1)”,再给 3-5 行 mini-plan;只有内部排障确实需要时,才额外单列“内部路由码”。',
2182
+ '先在对话里给 3-5 行 mini-plan,至少写清目标、范围内、范围外和验证方式。',
2183
+ '默认不要打开正式 PRD/review/change/tasks;只有在 mini-plan 暴露新决策缺口、跨系统风险或范围升级时,才提升到 L2 重流程。',
2184
+ '验证采用最小足够组合即可,重点说明需要强化测试的地方;默认不要求正式测试报告。',
2185
+ intent?.largeUiChangeRequest
2186
+ ? '如果这是大界面改动,mini-plan 之后先做 3 方向视觉方案评审,再进入实现。'
2187
+ : '',
2188
+ ].filter(Boolean).join('\n');
2189
+ }
2190
+ return null;
2191
+ }
2192
+
1754
2193
  function visualMockupMessage(intent) {
1755
2194
  if (!intent?.visualMockupRequest) {
1756
2195
  return null;
1757
2196
  }
1758
2197
  return [
1759
2198
  '当前用户要的是图片内容生成,例如图片、封面图、配图、海报、插画、图标、贴纸、头像、banner、主视觉/KV、运营图、效果图、视觉稿或 mockup。',
1760
- '默认直接调用 Codex 原生 Image 2 生成图片;除非用户明确指定 HTML、SVG、CSS、Canvas、代码稿或可编辑矢量/source artifact,不要改用临时 HTML/SVG/CSS 再截图。',
2199
+ '默认直接调用 `imagegen`,也就是 Codex 原生 Image 2,来生成图片;除非用户明确指定 HTML、SVG、CSS、Canvas、代码稿或可编辑矢量/source artifact,不要改用临时 HTML/SVG/CSS 再截图。',
1761
2200
  '对 logo、icon、avatar、badge 等开发素材,如果用户没有明确要求 mockup、场景图、设备框、卡片承载、名片/包装展示或参考界面复刻,默认按独立素材输出(standalone asset)处理:使用全画布单主体,不额外添加 UI frame、卡片、设备壳、名片、桌面陈列、手持实拍或其他展示容器。',
1762
2201
  '只有当用户明确要求 mockup、场景化效果图、容器化呈现,或参考图本身就包含这些承载结构时,才生成对应的容器或场景。',
1763
- 'OpenPrd review.html 只用于需求评审,visual-compare 只用于实现阶段已有参考图后的实现截图对比。',
2202
+ '只有在实际发生 `imagegen` 调用后,才能汇报生图结果、失败或限流;未调用 `imagegen` 前,不要声称“生图限流”或“生图失败”。',
2203
+ 'OpenPrd review.html 只用于需求评审,visual-compare 只用于实现阶段视觉证据:已有参考图时做效果图/实现截图对比,无参考图但改动界面时做修改前/修改后自检;局部细节优先补局部焦点证据板,并行优化方向优先补并行实验证据板。',
2204
+ ].join('\n');
2205
+ }
2206
+
2207
+ function largeUiVisualDirectionMessage(intent) {
2208
+ if (!intent?.largeUiChangeRequest) {
2209
+ return null;
2210
+ }
2211
+ return [
2212
+ 'OpenPrd 大界面改动视觉方案评审:',
2213
+ '位置: 需求分流之后、PRD 定稿或实现开工之前;它不同于 review.html,也不同于实现后的 visual-compare。',
2214
+ '判断: 会明显改变信息架构、核心布局、主视觉、关键路径、组件层级/密度,或用户需要先选设计方向时触发。',
2215
+ '步骤: 用 Codex Computer Use 进入产品内对应功能并截当前真实界面;基于截图调用 `imagegen`(Codex 原生 Image 2)做图生图,至少生成 3 个不同设计思想方向;把效果图横向拼成一张大图,每张左上角标注 1/2/3,并保存到 .openprd/harness/visual-reviews/。',
2216
+ '交互: 把横向大图展示给用户评审确认;用户确认方向前,不进入大 UI 实现,也不要声称界面方案已定。',
1764
2217
  ].join('\n');
1765
2218
  }
1766
2219
 
1767
2220
  function codexConfirmationReplyRule() {
1768
- return 'Codex UI 规则: 只有当前 approval policy 仍然需要人类对稳定 review artifact 做决定时,才在 final answer 里停下来请求确认;如果当前 lane 已进入 silent-record 策略,就继续记录精确 review artifact 并推进,不要为了同一个需求再额外停顿。';
2221
+ return 'Codex 回复规则: 只有当前真的还缺人来拍板时,才在 final answer 里停下来请求确认。只要用户已经明确说了“认可并继续”,或已经确认了 mini-plan、范围边界、正式产品边界,就直接沿着已确认路径继续,不要再写“如果你认可”“确认的话我就继续”这类二次索取确认的话。面向用户时,不要直接抛 lane、change、tasks、review artifact、work unit、digest、worker shard、write-scope、worktree、内部版本号、命令或文件路径这些内部词;统一改说“需求确认稿”“本次调整”“后续任务”“继续落地”“完成后检查”。如果后面真的还需要额外授权,再用人话把会做什么、不会做什么、完成后怎么检查说清楚。如果当前仍在 L2 的首轮澄清或需求摘要确认阶段,不要写成“你回我一句我就开始实现”;只能承诺“我先整理需求摘要给你确认,确认后再继续”。如果当前 lane 已进入 silent-record 策略,只能说明用户已经明确说了“不需要进行任何确认”;单纯的“请帮我实现/继续实现”不能把 lane 切到 silent-record。';
1769
2222
  }
1770
2223
 
1771
2224
  function confirmationGateMessage(gate) {
@@ -1778,32 +2231,40 @@ function confirmationGateMessage(gate) {
1778
2231
  return [
1779
2232
  intro,
1780
2233
  'Implementation may proceed only within the confirmed scope, with docs/basic, file manuals, folder README docs, standards verification, and OpenPrd run verification kept up to date. For backend, script, agent, tooling, service, or data-processing changes, keep CLI and API surface review current in docs/basic/backend-structure.md.',
1781
- 'For UI or visual work with an existing reference image, capture the implemented UI and run openprd visual-compare . --reference <effect-image> --actual <implementation-screenshot>; inspect the generated JPG before claiming the visual work is complete.',
2234
+ 'For UI or visual work with an existing reference image, capture the implemented UI and run openprd visual-compare . --reference <effect-image> --actual <implementation-screenshot>; if local detail matters more than the whole screen, add openprd visual-compare . --board <focus-board.json> so the agent can review numbered zoom regions. When there is no reference image, capture the before screenshot first, implement, capture the after screenshot from the same entry, viewport, account, and data state, then run openprd visual-compare . --before <before-screenshot> --after <after-screenshot>; if the agent explored multiple optimization directions, add openprd visual-compare . --board <parallel-board.json> and inspect expected changes plus unintended drift before claiming completion.',
1782
2235
  ].join('\n');
1783
2236
  }
1784
2237
 
1785
2238
  function currentRequirementStatusLine(gate, progress) {
1786
2239
  if (gate?.status === 'review-confirmation-authorized') {
1787
- return '用户刚刚确认了当前稳定 PRD 评审稿;本回合只允许记录这一个版本的 review 状态,不能把它直接扩展成实现确认。';
2240
+ return gate?.reviewActionAuthorization?.continueAfterReview
2241
+ ? '用户刚刚已经确认这版需求,并且明确表示继续。先记录这次确认结果,然后直接接着整理本次调整和后续任务;只有后面真的还缺额外授权时,再用人话说明影响和下一步。'
2242
+ : '用户刚刚已经确认这版需求;本回合只允许记录这次确认结果,不能把它直接扩展成开工授权。';
1788
2243
  }
1789
2244
  if (gate?.status === 'review-recording-authorized') {
1790
- return '当前稳定 PRD 评审稿已经按 silent-record 策略授权记录;只允许写回这一个版本,随后继续 change 和 tasks。';
2245
+ return '这版需求确认稿已经按免再次确认的规则授权记录;只记录这一次确认结果,然后继续整理本次调整和后续任务。';
1791
2246
  }
1792
2247
  switch (progress?.nextStep) {
2248
+ case 'clarification-confirmation-required':
2249
+ return '当前卡点: 先让用户确认当前需求摘要;在此之前不要把核心需求写成既定事实,也不要直接往后推进。';
2250
+ case 'prd-synthesis-required':
2251
+ return reviewPolicyAllowsSilentRecord(requirementApprovalPolicy(gate))
2252
+ ? '当前卡点: 用户已明确表示不需要再停下来确认,可以直接整理已确认内容,并生成这版需求确认稿。'
2253
+ : '当前卡点: 当前需求摘要已确认,下一步把已确认内容整理成这版需求确认稿。';
1793
2254
  case 'prd-review-required':
1794
2255
  return progress?.review?.versionId
1795
- ? '当前卡点: 先等用户确认当前稳定 PRD 评审稿;不要把“继续做”或“开落地吧”当成 review 确认。'
1796
- : '当前卡点: 先 synthesize 出本轮稳定 PRD 评审稿,再等待用户评审。';
2256
+ ? '当前卡点: 先等用户确认这版需求确认稿;不要把“继续做”“开落地吧”或单纯的“请帮我实现/继续实现”当成已经认可这版内容。'
2257
+ : '当前卡点: 先整理出这版需求确认稿,再等待用户确认。';
1797
2258
  case 'review-recording-required':
1798
- return '当前卡点: 稳定 PRD 评审稿已经生成;按当前 approval policy 可直接记录这版 review artifact,不需要再额外追问用户。';
2259
+ return '当前卡点: 这版需求确认稿已经生成,而且用户已明确表示不需要再停下来确认;可以先记录这次确认结果,再继续后面的整理。';
1799
2260
  case 'change-generation-required':
1800
- return '当前卡点: PRD 评审已确认,下一步先生成 OpenPrd change。';
2261
+ return '当前卡点: 这版需求已经确认,下一步先整理本次调整范围;这是当前流程的直接延续,不需要再重复索取同一轮确认。';
1801
2262
  case 'task-breakdown-required':
1802
- return '当前卡点: change 已存在,但还缺任务拆解,不能直接进入实现。';
2263
+ return '当前卡点: 本次调整范围已经立起来了,下一步把它拆成可直接执行的后续任务。';
1803
2264
  case 'implementation-ready':
1804
- return '当前卡点: review 已确认且 tasks 已就绪;如果当前需求原本就明确要求实现,可直接进入实现,否则等待一句明确的执行指令。';
2265
+ return '当前卡点: 这版需求和后续任务都准备好了。如果用户一开始就明确要继续做,就直接进入实现;否则再用人话说明这次会做什么、不会做什么、完成后怎么检查。若用户刚刚确认的是 L1 范围边界或 mini-plan,承接话术要写成“已确认,我按这个继续”,不要再写成像二次索取确认的句子。';
1805
2266
  default:
1806
- return '当前卡点: 继续按“澄清 -> 评审 -> change -> tasks -> 实现”的顺序推进。';
2267
+ return '当前卡点: 继续按“澄清需求 -> 确认需求 -> 整理本次调整 -> 拆分后续任务 -> 开始实现”的顺序推进。';
1807
2268
  }
1808
2269
  }
1809
2270
 
@@ -1816,20 +2277,29 @@ function currentRequirementMessage(intent, gate, progress) {
1816
2277
  const lines = [
1817
2278
  'OpenPrd 当前需求入口',
1818
2279
  gateStatus,
1819
- '当前输入命中了需求入口安全通道。不要按固定关键词判断 L2;先用 $openprd-requirement-intake 按影响面、未知数、决策成本和验证成本做语义分流。',
1820
- '如果分流结果是 L2,本轮只围绕这个新需求推进 PRD/review/change/tasks,并选择 base/consumer/b2b/agent PRD lens,不自动继续历史 active change。',
2280
+ '对外表达要求: 面向用户不要直接复述 PRD、review artifact、change、tasks、lane、approval policy、work unit、digest、worker shard、write-scope、worktree、内部版本号、命令或文件路径;改说“需求确认稿”“本次调整”“后续任务”“继续落地”“完成后检查”。',
2281
+ '当前输入已被判定为可能的新功能/新流程方案(L2),因此进入重流程需求入口。不要按固定关键词判断;先用 $openprd-requirement-intake 按影响面、未知数、决策成本和验证成本判断用户可见需求类型。',
2282
+ '内部审查保留固定对照:快速修正=L0,现有功能优化=L1,新功能/新流程方案=L2。',
2283
+ '如果用户刚刚已经确认了现有功能优化(L1)的 mini-plan、范围边界或正式产品边界,下一句要明确写成“已确认,我按这个继续/收口/落地”;不要只写一个“确认”,更不要写成“确认,我们就按这个……”这种容易让用户误以为还要再表态的句子。',
2284
+ '如果需求类型是新功能/新流程方案(L2),本轮只围绕这个新需求推进 PRD/review/change/tasks,并选择通用场景 / 面向个人消费者场景 / 面向企业服务场景 / 以 Agent 为主要使用场景的 PRD 视角,不自动继续历史 active change。对用户复述时不要直接把 consumer / b2b / agent 当展示词;这些枚举值只用于内部记录和命令。',
1821
2285
  prompt ? '本轮需求: ' + prompt : '',
1822
2286
  gate?.intakeMode === 'deep-reflection'
1823
2287
  ? '需求入口: 先运行需求自省,再输出对话内澄清摘要或简短清单。'
1824
2288
  : '需求入口: 先做轻量项目映射,再确认影响点和验收方式。',
1825
2289
  reviewPolicyAllowsSilentRecord(approvalPolicy)
1826
- ? '当前 approval policy: decision-points / silent-record。保留稳定 review artifact,但在版本、digest、work unit 精确匹配时不再额外停下来追问用户。'
1827
- : '当前 approval policy: decision-points / human-review。稳定 review artifact 仍需要一次明确的人类决策。',
2290
+ ? '当前 approval policy: decision-points / silent-record。之所以进入 silent-record,是因为用户已经明确表示不需要进行任何确认;单纯的“请帮我实现/继续实现”或“不要评审”都不够,仍然只能记录版本、digest、work unit 精确匹配的 artifact。'
2291
+ : '当前 approval policy: decision-points / human-review。当前 lane 仍需要一次 requirement 摘要确认和一次稳定 review artifact 的明确人类决策;单纯的“请帮我实现/继续实现”不算这两次决策。',
2292
+ intent?.reviewContinuationRequested
2293
+ ? '这条消息同时表达了“确认当前稳定评审稿并继续当前 OpenPrd 下一步”的意图:先记录精确 review artifact,再继续当前 lane;如果 review 后 tasks 已就绪但还需要执行授权,立刻展示执行确认清单,不要停在“如果你要我继续”。'
2294
+ : '',
1828
2295
  currentRequirementStatusLine(gate, progress),
1829
2296
  reviewPolicyAllowsSilentRecord(approvalPolicy)
1830
- ? 'Decision-point order: clarify the requirement, capture user answers, synthesize the PRD, record the exact stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.'
1831
- : 'Decision-point order: clarify the requirement, capture user answers, synthesize the PRD, wait for a human review decision on the stable artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.',
1832
- 'Recommended next action: 先在 chat 输出 Intake Decision;若为 L2,再运行 openprd clarify .,并在十句话左右回答 target、scope、out-of-scope、acceptance 后请求确认。Do not open clarification HTML; use review.html only after synthesize/review.',
2297
+ ? 'Decision-point order: because the user explicitly waived any confirmation stop, you may skip requirement-summary confirmation, write back requirement facts, synthesize the PRD, record the exact stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.'
2298
+ : 'Decision-point order: clarify the requirement, summarize it in chat using 需求判断 / 需求理解 / 功能范围 / 技术方案, prefer Markdown tables for 功能范围 and 技术方案, wait for the user to confirm that requirement summary, write back only confirmed facts, synthesize the PRD, wait for a human review decision on the stable artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.',
2299
+ intent?.largeUiChangeRequest
2300
+ ? 'Large UI direction gate: before PRD freeze or implementation, capture the current in-product screen with Codex Computer Use, generate at least three Image 2 directions, combine them into one horizontal numbered contact sheet, and wait for the user to choose a direction.'
2301
+ : '',
2302
+ 'Recommended next action: 先在 chat 输出“需求类型判断”,默认把路由码并进“需求类型:新功能/新流程方案(L2)”这类标签里;只有内部排障确实需要时,才额外写“内部路由码”。若为新功能/新流程方案(L2),再运行 openprd clarify .,并按“需求判断 / 需求理解 / 功能范围 / 技术方案”给出十句话左右的结构化摘要,其中“功能范围”和“技术方案”优先用 Markdown 表格;请求确认后再写回 requirement 事实并继续 classify/synthesize,不要把这一步表述成“确认后直接开始实现”。Do not open clarification HTML; use review.html only after synthesize/review.',
1833
2303
  ];
1834
2304
  if (isImplementationAdvanceIntent(intent)) {
1835
2305
  lines.splice(2, 1, gate?.active
@@ -1840,6 +2310,10 @@ function currentRequirementMessage(intent, gate, progress) {
1840
2310
  return lines.filter(Boolean).join('\n');
1841
2311
  }
1842
2312
 
2313
+ function requirementRoutingSummary() {
2314
+ return '需求类型由 $openprd-requirement-intake 按影响面、未知数、决策成本和验证成本判断:快速修正(L0)直接处理并事后说明,不打开正式 PRD/review/change/tasks;现有功能优化(L1)先给对话内 mini-plan,默认不生成正式 PRD/change/tasks;新功能/新流程方案(L2)才进入 requirement intake 与 PRD/review/change/tasks,并选择通用场景 / 面向个人消费者场景 / 面向企业服务场景 / 以 Agent 为主要使用场景的 PRD 视角。对用户复述时不要直接把 consumer / b2b / agent 当展示词;这些枚举值只用于内部记录和命令。单纯的“请帮我实现/继续实现”只表示有执行意图,不表示跳过 requirement 摘要确认或 review;只有用户明确表示不需要进行任何确认时,才允许静默走完整 requirement write path。';
2315
+ }
2316
+
1843
2317
  function historicalRequirementReminder(root, runContext, intent, gate) {
1844
2318
  const activeChange = runContext?.activeChange;
1845
2319
  if (!activeChange || (!intent?.requiresIntake && !(intent?.confirmation && gate?.promptPreview))) {
@@ -1864,6 +2338,11 @@ function historicalRequirementReminder(root, runContext, intent, gate) {
1864
2338
  ].filter(Boolean).join('\n');
1865
2339
  }
1866
2340
 
2341
+ function knowledgeSkillReminder(runContext) {
2342
+ const lines = knowledgeSkillContextLines(runContext?.knowledgeSkills);
2343
+ return lines.length > 0 ? lines.join('\n') : null;
2344
+ }
2345
+
1867
2346
  function contextMessage(cwd, intent = null, gate = null, progress = null) {
1868
2347
  const run = progress?.runContext
1869
2348
  ? { ok: true, parsed: progress.runContext, stdout: renderRunContextText(progress.runContext) }
@@ -1876,14 +2355,18 @@ function contextMessage(cwd, intent = null, gate = null, progress = null) {
1876
2355
  return [
1877
2356
  currentRequirementMessage(intent, gate, effectiveProgress),
1878
2357
  historicalRequirementReminder(cwd, run.parsed, intent, gate),
2358
+ knowledgeSkillReminder(run.parsed),
1879
2359
  'OpenPrd 上下文只是建议,不是自动执行指令。请先判断用户当前意图。',
2360
+ lightweightRequirementMessage(intent),
1880
2361
  visualMockupMessage(intent),
1881
- '需求复杂度由 $openprd-requirement-intake 按影响面、未知数、决策成本和验证成本判断:L0 直接处理并事后说明,L1 给对话内 mini-plan,L2 先走 PRD/review/change/tasks 并选择 base/consumer/b2b/agent PRD lens。只有在用户原始意图已明确要求实现,或后续明确发出执行指令时,才进入实现。',
2362
+ largeUiVisualDirectionMessage(intent),
2363
+ requirementRoutingSummary(),
1882
2364
  '如果用户只是要求看看、规划、分析、审查、解释影响或列出文件,请保持只读并基于证据回答;不要运行 OpenPrd loop、任务推进、discovery 推进、commit 或其他写入命令。',
1883
2365
  '只有当用户当前明确要求开发、实现、修复、继续任务、深度调研、对标复刻或提交时,才运行 openprd loop --run、openprd tasks --advance、openprd discovery --advance、commit/push 等执行命令。',
1884
- '代码修改完成后、最终回复前,针对本轮实际 touched code files 运行 openprd dev-check . <file...>;attention 需说明局部职责,warning 需判断本轮是否扩大职责,扩大则先重构/拆分/解耦并复查,窄修暂不拆时说明原因和后续拆分建议。',
1885
- '涉及界面、页面、视觉、样式或前端体验,且已经有效果图/设计稿/用户给图并进入实现阶段时,阶段性完成后必须截图并运行 openprd visual-compare . --reference <效果图> --actual <实现截图>;默认输出 JPG .openprd/harness/visual-reviews/。查看合成图后继续对标,直到没有明显视觉差异。',
1886
- '发现可沉淀项时不要中途打断任务:工具识别补全和减少重复打扰的高置信低风险项可自动补齐;用户偏好、项目协作规矩和 OpenPrd 默认行为先记录为候选,收工时运行 openprd grow . --review 集中确认。',
2366
+ '代码修改完成后、最终回复前,针对本轮实际 touched code files 运行 openprd dev-check . <file...>;若出现需要关注的文件,最终回复必须以 **后续建议** 为标题,直接复用 dev-check 生成的 Markdown 表格,列出影响对象、关注程度、规模信号、预警原因、本次处理结果和后续建议,并按 🔴 → 🟠 → 🟡 排序;不要把“关注程度”列改写成纯 emoji,必须保留例如“🟠 中风险|建议优先关注”这类完整标签;如果你改写了“预警原因 / 本次处理结果 / 后续建议”,先用 `node scripts/dev-check-wrapup-copy.mjs --validate` 校验每格不超过 20 字;若报错,按提示缩短后重试。',
2367
+ '大界面改动进入实现前,先用 Codex Computer Use 截取产品内当前功能截图,再用 `imagegen`(Codex 原生 Image 2)基于截图生成至少 3 个设计方向,横向拼接为一张带 1/2/3 序号的大图给用户确认;未确认方向前不要进入大 UI 实现。',
2368
+ '涉及界面、页面、视觉、样式或前端体验,且已经有效果图/设计稿/用户给图并进入实现阶段时,阶段性完成后必须截图并运行 openprd visual-compare . --reference <效果图> --actual <实现截图>;如果这次重点在局部细节,再补一份 openprd visual-compare . --board <focus-board.json>。没有明确参考图但改动界面时,动手前先截修改前截图,完成后用同一入口、视口、账号和数据状态截修改后截图,并运行 openprd visual-compare . --before <修改前截图> --after <修改后截图>;如果并行试了多个优化方向,再补一份 openprd visual-compare . --board <parallel-board.json>;默认输出 JPG 到 .openprd/harness/visual-reviews/。查看合成图后继续对标或自检,直到没有明显视觉差异或意外漂移。',
2369
+ '发现可沉淀项时不要中途打断任务:代码扩展识别这类白名单工具补全会自动应用并记录;用户偏好、项目协作规矩和 OpenPrd 默认行为先记录为候选,收工时运行 openprd grow . --review 集中确认。',
1887
2370
  '维护 OpenPrd 本身且涉及配置类能力时,先判断是否应纳入 openprd grow;高置信可成长默认纳入,不确定则主动询问用户。',
1888
2371
  '涉及后端、脚本、Agent、工具链、服务或数据处理变更时,把 CLI 与 API 视为同级接入面:同步检查命令入口、参数、输出契约、help/doctor/dry-run/status 与接口协议、返回结构、身份边界是否受影响,并更新 docs/basic/backend-structure.md 或明确写不适用原因。',
1889
2372
  '声明实现就绪前,先运行 openprd standards . --verify 和 openprd run . --verify。',
@@ -1892,14 +2375,18 @@ function contextMessage(cwd, intent = null, gate = null, progress = null) {
1892
2375
  return [
1893
2376
  run.stdout,
1894
2377
  gateMessage,
2378
+ knowledgeSkillReminder(run.parsed),
1895
2379
  'OpenPrd 上下文只是建议,不是自动执行指令。请先判断用户当前意图。',
2380
+ lightweightRequirementMessage(intent),
1896
2381
  visualMockupMessage(intent),
1897
- '需求复杂度由 $openprd-requirement-intake 按影响面、未知数、决策成本和验证成本判断:L0 直接处理并事后说明,L1 给对话内 mini-plan,L2 先走 PRD/review/change/tasks 并选择 base/consumer/b2b/agent PRD lens。只有在用户原始意图已明确要求实现,或后续明确发出执行指令时,才进入实现。',
2382
+ largeUiVisualDirectionMessage(intent),
2383
+ requirementRoutingSummary(),
1898
2384
  '如果用户只是要求看看、规划、分析、审查、解释影响或列出文件,请保持只读并基于证据回答;不要运行 OpenPrd loop、任务推进、discovery 推进、commit 或其他写入命令。',
1899
2385
  '只有当用户当前明确要求开发、实现、修复、继续任务、深度调研、对标复刻或提交时,才运行 openprd loop --run、openprd tasks --advance、openprd discovery --advance、commit/push 等执行命令。',
1900
- '代码修改完成后、最终回复前,针对本轮实际 touched code files 运行 openprd dev-check . <file...>;attention 需说明局部职责,warning 需判断本轮是否扩大职责,扩大则先重构/拆分/解耦并复查,窄修暂不拆时说明原因和后续拆分建议。',
1901
- '涉及界面、页面、视觉、样式或前端体验,且已经有效果图/设计稿/用户给图并进入实现阶段时,阶段性完成后必须截图并运行 openprd visual-compare . --reference <效果图> --actual <实现截图>;默认输出 JPG .openprd/harness/visual-reviews/。查看合成图后继续对标,直到没有明显视觉差异。',
1902
- '发现可沉淀项时不要中途打断任务:工具识别补全和减少重复打扰的高置信低风险项可自动补齐;用户偏好、项目协作规矩和 OpenPrd 默认行为先记录为候选,收工时运行 openprd grow . --review 集中确认。',
2386
+ '代码修改完成后、最终回复前,针对本轮实际 touched code files 运行 openprd dev-check . <file...>;若出现需要关注的文件,最终回复必须以 **后续建议** 为标题,直接复用 dev-check 生成的 Markdown 表格,列出影响对象、关注程度、规模信号、预警原因、本次处理结果和后续建议,并按 🔴 → 🟠 → 🟡 排序;不要把“关注程度”列改写成纯 emoji,必须保留例如“🟠 中风险|建议优先关注”这类完整标签;如果你改写了“预警原因 / 本次处理结果 / 后续建议”,先用 `node scripts/dev-check-wrapup-copy.mjs --validate` 校验每格不超过 20 字;若报错,按提示缩短后重试。',
2387
+ '大界面改动进入实现前,先用 Codex Computer Use 截取产品内当前功能截图,再用 `imagegen`(Codex 原生 Image 2)基于截图生成至少 3 个设计方向,横向拼接为一张带 1/2/3 序号的大图给用户确认;未确认方向前不要进入大 UI 实现。',
2388
+ '涉及界面、页面、视觉、样式或前端体验,且已经有效果图/设计稿/用户给图并进入实现阶段时,阶段性完成后必须截图并运行 openprd visual-compare . --reference <效果图> --actual <实现截图>;如果这次重点在局部细节,再补一份 openprd visual-compare . --board <focus-board.json>。没有明确参考图但改动界面时,动手前先截修改前截图,完成后用同一入口、视口、账号和数据状态截修改后截图,并运行 openprd visual-compare . --before <修改前截图> --after <修改后截图>;如果并行试了多个优化方向,再补一份 openprd visual-compare . --board <parallel-board.json>;默认输出 JPG 到 .openprd/harness/visual-reviews/。查看合成图后继续对标或自检,直到没有明显视觉差异或意外漂移。',
2389
+ '发现可沉淀项时不要中途打断任务:代码扩展识别这类白名单工具补全会自动应用并记录;用户偏好、项目协作规矩和 OpenPrd 默认行为先记录为候选,收工时运行 openprd grow . --review 集中确认。',
1903
2390
  '维护 OpenPrd 本身且涉及配置类能力时,先判断是否应纳入 openprd grow;高置信可成长默认纳入,不确定则主动询问用户。',
1904
2391
  '涉及后端、脚本、Agent、工具链、服务或数据处理变更时,把 CLI 与 API 视为同级接入面:同步检查命令入口、参数、输出契约、help/doctor/dry-run/status 与接口协议、返回结构、身份边界是否受影响,并更新 docs/basic/backend-structure.md 或明确写不适用原因。',
1905
2392
  '声明实现就绪前,先运行 openprd standards . --verify 和 openprd run . --verify。',
@@ -1915,11 +2402,13 @@ function contextMessage(cwd, intent = null, gate = null, progress = null) {
1915
2402
  status.ok ? status.stdout : '',
1916
2403
  next.ok ? next.stdout : '',
1917
2404
  gateMessage,
2405
+ lightweightRequirementMessage(intent),
1918
2406
  visualMockupMessage(intent),
1919
- '需求复杂度由 $openprd-requirement-intake 按影响面、未知数、决策成本和验证成本判断:L0 直接处理并事后说明,L1 给对话内 mini-plan,L2 先走 PRD/review/change/tasks 并选择 base/consumer/b2b/agent PRD lens。只有在用户原始意图已明确要求实现,或后续明确发出执行指令时,才进入实现。',
2407
+ largeUiVisualDirectionMessage(intent),
2408
+ requirementRoutingSummary(),
1920
2409
  'OpenPrd 下一步只是建议。规划、分析、审查类请求保持只读;只有用户当前明确要求开发、深度调研、对标复刻或继续任务时才执行。',
1921
- '代码修改完成后、最终回复前,针对本轮实际 touched code files 运行 openprd dev-check . <file...>;attention 需说明局部职责,warning 需判断本轮是否扩大职责,扩大则先重构/拆分/解耦并复查,窄修暂不拆时说明原因和后续拆分建议。',
1922
- '发现可沉淀项时不要中途打断任务:工具识别补全和减少重复打扰的高置信低风险项可自动补齐;用户偏好、项目协作规矩和 OpenPrd 默认行为先记录为候选,收工时运行 openprd grow . --review 集中确认。',
2410
+ '代码修改完成后、最终回复前,针对本轮实际 touched code files 运行 openprd dev-check . <file...>;若出现需要关注的文件,最终回复必须以 **后续建议** 为标题,直接复用 dev-check 生成的 Markdown 表格,列出影响对象、关注程度、规模信号、预警原因、本次处理结果和后续建议,并按 🔴 → 🟠 → 🟡 排序;不要把“关注程度”列改写成纯 emoji,必须保留例如“🟠 中风险|建议优先关注”这类完整标签;如果你改写了“预警原因 / 本次处理结果 / 后续建议”,先用 `node scripts/dev-check-wrapup-copy.mjs --validate` 校验每格不超过 20 字;若报错,按提示缩短后重试。',
2411
+ '发现可沉淀项时不要中途打断任务:代码扩展识别这类白名单工具补全会自动应用并记录;用户偏好、项目协作规矩和 OpenPrd 默认行为先记录为候选,收工时运行 openprd grow . --review 集中确认。',
1923
2412
  '维护 OpenPrd 本身且涉及配置类能力时,先判断是否应纳入 openprd grow;高置信可成长默认纳入,不确定则主动询问用户。',
1924
2413
  '涉及后端、脚本、Agent、工具链、服务或数据处理变更时,把 CLI 与 API 视为同级接入面,并同步更新 docs/basic/backend-structure.md 或明确写不适用原因。',
1925
2414
  '声明就绪前请验证 docs/basic 标准。',
@@ -1932,8 +2421,13 @@ function shouldInjectOpenPrdContext(payload) {
1932
2421
  return false;
1933
2422
  }
1934
2423
  const intent = analyzePromptIntent(prompt);
2424
+ const hasProjectPathReference = /(?:^|[\s`"'(])(?:src|app|lib|server|scripts|test|tests|docs|skills|openprd|openspec)\/[^\s`"'()]+/i.test(prompt);
1935
2425
  if (
1936
2426
  intent.simpleConcrete
2427
+ && !intent.visualMockupRequest
2428
+ && !intent.largeUiChangeRequest
2429
+ && !intent.continuationRequest
2430
+ && !hasProjectPathReference
1937
2431
  && !intent.productCopyRequest
1938
2432
  && !intent.secretsRequest
1939
2433
  && !intent.weappValidationRequest
@@ -2042,6 +2536,7 @@ function runGateChecks(cwd, payload, risk) {
2042
2536
  taskReady: runTaskReady,
2043
2537
  workspaceReady: runWorkspaceReady,
2044
2538
  summary: summarizeRunVerifyCheck(runParsed, run.stdout || run.stderr),
2539
+ workspaceAttention: runParsed?.workspaceAttention ?? null,
2045
2540
  warnings: Array.isArray(runParsed?.warnings) ? runParsed.warnings : [],
2046
2541
  errors: Array.isArray(runParsed?.errors) ? runParsed.errors : [],
2047
2542
  details: [
@@ -2182,6 +2677,18 @@ function handle(eventName, cwd, payload) {
2182
2677
  progress = null;
2183
2678
  }
2184
2679
  if (isBlockingRequirementGate(gate)) {
2680
+ if ((intent.confirmation || shortAffirmative) && progress?.nextStep === 'clarification-confirmation-required') {
2681
+ gate = holdRequirementGate(root, prompt, progress, sessionId, {
2682
+ status: 'clarification-confirmed',
2683
+ clarificationConfirmedAt: now(),
2684
+ clarificationConfirmationPreview: preview(prompt, 500),
2685
+ });
2686
+ progress = evaluateRequirementGateProgress(root, sessionId);
2687
+ appendEvent(root, { ...baseEvent, outcome: 'requirement-gate-clarification-confirmed' });
2688
+ recordRunHook(root, baseEvent, 'requirement-gate-clarification-confirmed');
2689
+ updateHookState(root, baseEvent);
2690
+ return allowHook(composeHookContext(root, intent, gate, progress, sessionId));
2691
+ }
2185
2692
  if (intent.reviewDecision) {
2186
2693
  const authorization = reviewActionAuthorizationFor(intent, progress, prompt);
2187
2694
  gate = holdRequirementGate(root, prompt, progress, sessionId, {
@@ -2279,6 +2786,7 @@ function handle(eventName, cwd, payload) {
2279
2786
  const reviewMark = parseReviewMarkCommand(commandText(payload));
2280
2787
  const approvalPolicy = requirementApprovalPolicy(gate);
2281
2788
  const silentRecord = reviewPolicyAllowsSilentRecord(approvalPolicy);
2789
+ const writePathMutation = isRequirementWritePathMutation(commandText(payload));
2282
2790
  const reason = reviewMark
2283
2791
  ? [
2284
2792
  silentRecord
@@ -2292,19 +2800,23 @@ function handle(eventName, cwd, payload) {
2292
2800
  : 'Current review artifact has not been synthesized yet. Run openprd synthesize . --open first.',
2293
2801
  silentRecord
2294
2802
  ? 'Do not mark any stale or different review artifact; only the exact current artifact is allowed.'
2295
- : 'Implementation approval and review confirmation are different gates; do not treat "可以开做" or similar wording as permission to run openprd review --mark confirmed.',
2803
+ : 'Implementation approval and review confirmation are different gates; do not treat "可以开做", plain "请帮我实现/继续实现", or similar wording as permission to run openprd review --mark confirmed.',
2296
2804
  ].filter(Boolean).join('\n')
2297
2805
  : [
2298
2806
  'OpenPrd blocked a mutating action because the requirement gate is still active.',
2299
2807
  progress?.reason || 'The requirement still needs PRD review, change generation, or task preparation.',
2300
2808
  progress?.nextStep === 'implementation-ready'
2301
2809
  ? 'Do not edit implementation files until the user clearly asks to execute this reviewed requirement.'
2810
+ : writePathMutation && progress?.nextStep === 'clarification-confirmation-required'
2811
+ ? 'Do not write requirement facts, classify, or synthesize yet. First summarize the requirement in chat using 需求判断 / 需求理解 / 功能范围 / 技术方案, prefer Markdown tables for 功能范围 and 技术方案, wait for the user to confirm that summary, then continue the requirement write path.'
2812
+ : writePathMutation && progress?.nextStep === 'prd-synthesis-required'
2813
+ ? 'You may continue the requirement write path only within the confirmed summary: write back confirmed facts, classify if needed, synthesize the PRD, then proceed to review/change/tasks.'
2302
2814
  : progress?.nextStep === 'review-recording-required'
2303
2815
  ? 'Do not edit implementation files yet. First record the exact current stable review artifact, then generate change and tasks.'
2304
2816
  : 'Do not edit implementation files until the active approval policy is satisfied and the OpenPrd change has generated tasks.',
2305
2817
  silentRecord
2306
- ? 'Decision-point order: clarify the requirement, capture user answers, synthesize the PRD, record the exact stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.'
2307
- : 'Decision-point order: clarify the requirement, capture user answers, synthesize the PRD, wait for a human review decision on the stable artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.',
2818
+ ? 'Decision-point order: because the user explicitly waived any confirmation stop, you may skip requirement-summary confirmation, write back requirement facts, synthesize the PRD, record the exact stable review artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.'
2819
+ : 'Decision-point order: clarify the requirement, summarize it in chat using 需求判断 / 需求理解 / 功能范围 / 技术方案, prefer Markdown tables for 功能范围 and 技术方案, wait for the user to confirm that requirement summary, write back only confirmed facts, synthesize the PRD, wait for a human review decision on the stable artifact, generate the OpenPrd change, prepare the task breakdown, then implement within the confirmed scope.',
2308
2820
  ].join('\n');
2309
2821
  appendEvent(root, { ...baseEvent, outcome: 'blocked-requirement-intake' });
2310
2822
  recordRunHook(root, baseEvent, 'blocked-requirement-intake');
@@ -2333,7 +2845,7 @@ function handle(eventName, cwd, payload) {
2333
2845
  recordRunHook(root, baseEvent, 'allowed-medium-risk');
2334
2846
  updateHookState(root, baseEvent);
2335
2847
  recordTouchedFiles(root, payload);
2336
- return allowHook('OpenPrd 检测到写入动作。本轮写入完成后、最终回复前,请针对实际 touched code files 运行 openprd dev-check . <file...>;如出现 warning,判断本轮是否扩大职责,扩大则先重构/拆分/解耦并复查,窄修暂不拆时说明原因和后续拆分建议;如涉及界面视觉且已有参考效果图并进入实现阶段,阶段性完成后运行 openprd visual-compare . --reference <效果图> --actual <实现截图> 并查看 JPG 对比图;发现可沉淀项时不要中途打断任务,工具识别补全和减少重复打扰的高置信低风险项可自动补齐,用户偏好、项目协作规矩和 OpenPrd 默认行为留到收工时用 openprd grow . --review 集中确认;维护 OpenPrd 本身且涉及配置类能力时,先判断是否应纳入 openprd grow;声明就绪前,请同步维护 docs/basic、文件说明书、文件夹 README,以及相关 OpenPrd change/task 状态;如果涉及后端、脚本、Agent、工具链、服务或数据处理变更,还要把 CLI 与 API 视为同级接入面并更新 docs/basic/backend-structure.md。');
2848
+ return allowHook('OpenPrd 检测到写入动作。本轮写入完成后、最终回复前,请针对实际 touched code files 运行 openprd dev-check . <file...>;如出现需要关注的文件,最终回复必须以 **后续建议** 为标题,直接复用 dev-check 生成的 Markdown 表格,说明影响对象、关注程度、规模信号、预警原因、本次处理结果和后续建议,并按 🔴 → 🟠 → 🟡 排序;不要把“关注程度”列改写成纯 emoji,必须保留例如“🟠 中风险|建议优先关注”这类完整标签;如果你改写了“预警原因 / 本次处理结果 / 后续建议”,先用 `node scripts/dev-check-wrapup-copy.mjs --validate` 校验每格不超过 20 字;若报错,按提示缩短后重试;如涉及界面视觉且已有参考效果图并进入实现阶段,阶段性完成后运行 openprd visual-compare . --reference <效果图> --actual <实现截图> 并查看 JPG 对比图;若局部细节更重要,再补 openprd visual-compare . --board <focus-board.json>;如无参考图但改动界面,确认已先截修改前截图,并在完成后运行 openprd visual-compare . --before <修改前截图> --after <修改后截图> 查看 JPG 自检图;若并行试了多个优化方向,再补 openprd visual-compare . --board <parallel-board.json>;发现可沉淀项时不要中途打断任务,代码扩展识别这类白名单工具补全会自动应用并记录,用户偏好、项目协作规矩和 OpenPrd 默认行为留到收工时用 openprd grow . --review 集中确认;维护 OpenPrd 本身且涉及配置类能力时,先判断是否应纳入 openprd grow;声明就绪前,请同步维护 docs/basic、文件说明书、文件夹 README,以及相关 OpenPrd change/task 状态;如果涉及后端、脚本、Agent、工具链、服务或数据处理变更,还要把 CLI 与 API 视为同级接入面并更新 docs/basic/backend-structure.md。');
2337
2849
  }
2338
2850
  return allowHook();
2339
2851
  }
@@ -2363,13 +2875,23 @@ function handle(eventName, cwd, payload) {
2363
2875
  const turnState = readTurnState(root);
2364
2876
  const stopIntent = analyzePromptIntent(turnState.prompt || '');
2365
2877
  const weappGate = readNamedGate(root, 'weapp', sessionId);
2366
- if (weappGate?.active && (stopIntent.weappValidationRequest || (Array.isArray(turnState.touchedFiles) && turnState.touchedFiles.length > 0))) {
2878
+ if (weappGate?.active && stopIntent.weappValidationRequest) {
2367
2879
  return allowHook([
2368
- 'OpenPrd 在本轮收工回顾里发现微信小程序验证仍未完成。',
2369
- '如果这次任务涉及微信小程序测试、验证、截图、日志、网络请求、微信开发者工具自动化,或修改了可能影响运行态的代码,请先用 `weapp-dev-mcp` skill 和本地 `weapp-dev-mcp` MCP 做实际验证。',
2370
- '在没有本地 MCP 验证证据前,不要宣称“小程序已验证”。',
2880
+ 'OpenPrd 在本轮收工回顾里发现小程序运行态验证仍未完成。',
2881
+ '如果这次任务是用户明确要求的小程序实测、复现、截图、抓日志/网络,或你已经承诺提供运行态证据,请补齐本地运行态验证;补齐时默认沿用当前小程序运行态或开发者工具会话连续验证,不要为了验证自动重开应用;否则不要把普通代码改动默认升级成小程序实测。',
2882
+ '如果当前环境没有可用的小程序本地验证工具,请明确说明未完成运行态验证,不要假定工具已安装。',
2371
2883
  ].join('\n'));
2372
2884
  }
2885
+ if (stopIntent.visualMockupRequest && (!Array.isArray(turnState.touchedFiles) || turnState.touchedFiles.length === 0)) {
2886
+ return allowHook([
2887
+ 'OpenPrd 生图事实对齐提醒:如果这轮要汇报图片结果、失败或限流,请确保它来自一次实际的 `imagegen` 调用。',
2888
+ '未实际调用 `imagegen` 前,不要声称“生图限流”“生图失败”或“已经生成图片结果”;若本轮还没调用,请如实说明仍未开始或尚未完成生图。',
2889
+ ].join('\n'));
2890
+ }
2891
+ const devCheckMessage = devCheckWrapUpMessage(root, turnState);
2892
+ if (devCheckMessage) {
2893
+ return allowHook(devCheckMessage);
2894
+ }
2373
2895
  if (Array.isArray(turnState.touchedFiles) && turnState.touchedFiles.length > 0) {
2374
2896
  const review = runOpenPrd(['quality', '.', '--learn', '--review', '--from', '.openprd/harness/turn-state.json', '--json'], root);
2375
2897
  if (review.ok) {