speccore 8.3.159 → 8.3.161

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 (50) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/analyze.d.ts +16 -0
  4. package/dist/commands/analyze.d.ts.map +1 -1
  5. package/dist/commands/analyze.js +538 -316
  6. package/dist/commands/analyze.js.map +1 -1
  7. package/dist/commands/change.d.ts.map +1 -1
  8. package/dist/commands/change.js +14 -14
  9. package/dist/commands/change.js.map +1 -1
  10. package/dist/commands/execute.d.ts +1 -0
  11. package/dist/commands/execute.d.ts.map +1 -1
  12. package/dist/commands/execute.js +92 -10
  13. package/dist/commands/execute.js.map +1 -1
  14. package/dist/commands/init.d.ts.map +1 -1
  15. package/dist/commands/init.js +6 -2
  16. package/dist/commands/init.js.map +1 -1
  17. package/dist/commands/iteration/split.d.ts.map +1 -1
  18. package/dist/commands/iteration/split.js +299 -52
  19. package/dist/commands/iteration/split.js.map +1 -1
  20. package/dist/commands/plan.d.ts.map +1 -1
  21. package/dist/commands/plan.js +45 -36
  22. package/dist/commands/plan.js.map +1 -1
  23. package/dist/core/agent-adapter.d.ts +81 -0
  24. package/dist/core/agent-adapter.d.ts.map +1 -0
  25. package/dist/core/agent-adapter.js +425 -0
  26. package/dist/core/agent-adapter.js.map +1 -0
  27. package/dist/core/ask-engine.d.ts.map +1 -1
  28. package/dist/core/ask-engine.js +60 -0
  29. package/dist/core/ask-engine.js.map +1 -1
  30. package/dist/core/pipeline-engine.d.ts +49 -8
  31. package/dist/core/pipeline-engine.d.ts.map +1 -1
  32. package/dist/core/pipeline-engine.js +221 -34
  33. package/dist/core/pipeline-engine.js.map +1 -1
  34. package/dist/core/prompt-builder.d.ts +12 -3
  35. package/dist/core/prompt-builder.d.ts.map +1 -1
  36. package/dist/core/prompt-builder.js +238 -15
  37. package/dist/core/prompt-builder.js.map +1 -1
  38. package/dist/core/requirement-clarifier.d.ts.map +1 -1
  39. package/dist/core/requirement-clarifier.js +36 -3
  40. package/dist/core/requirement-clarifier.js.map +1 -1
  41. package/dist/core/schemas/task.schema.d.ts +2 -2
  42. package/dist/core/structured-extractor.d.ts +2 -0
  43. package/dist/core/structured-extractor.d.ts.map +1 -1
  44. package/dist/core/structured-extractor.js +61 -0
  45. package/dist/core/structured-extractor.js.map +1 -1
  46. package/dist/core/unified-retrieval.d.ts +2 -1
  47. package/dist/core/unified-retrieval.d.ts.map +1 -1
  48. package/dist/core/unified-retrieval.js +34 -15
  49. package/dist/core/unified-retrieval.js.map +1 -1
  50. package/package.json +1 -1
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.UNITS_CACHE_DIR = void 0;
37
37
  exports.analyzeCommand = analyzeCommand;
38
+ exports.buildMultiDocPrompt = buildMultiDocPrompt;
38
39
  exports.extractIterationUnits = extractIterationUnits;
39
40
  exports.saveUnitAnalysis = saveUnitAnalysis;
40
41
  exports.loadUnitAnalyses = loadUnitAnalyses;
@@ -64,6 +65,8 @@ const task_paths_1 = require("../core/task-paths");
64
65
  const analyze_engine_1 = require("../core/analyze-engine");
65
66
  const fs_extra_2 = require("fs-extra");
66
67
  const prompt_builder_1 = require("../core/prompt-builder");
68
+ // v8.3.160+: 子 Agent 适配层(analyze 自定义 Prompt 中手动注入)
69
+ const agent_adapter_1 = require("../core/agent-adapter");
67
70
  const questions_1 = require("../core/questions");
68
71
  const platform_registry_1 = require("../core/platform-registry");
69
72
  const index_guard_1 = require("../core/index-guard");
@@ -850,8 +853,8 @@ async function analyzeCommand(options) {
850
853
  engine = result.engine;
851
854
  steps = result.steps;
852
855
  pipelineKey = iter;
853
- // v6.80.0+: 默认从 clarify 开始,skipClarify 时从 phase1 开始
854
- initStep = options.skipClarify ? 'phase1-prompt' : 'clarify-prompt';
856
+ // v8.3.160+: clarify 拆分为 product → interaction → security 三个子步骤
857
+ initStep = options.skipClarify ? 'phase1-prompt' : 'clarify-product';
855
858
  }
856
859
  // 检查是否有活跃的 Pipeline(恢复模式)
857
860
  const hasActive = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
@@ -871,15 +874,15 @@ async function analyzeCommand(options) {
871
874
  if (isGlobalScope) {
872
875
  // 全局层 Pipeline 步骤映射
873
876
  if (currentStep === 'init' || currentStep === 'discovery') {
874
- prompt = await buildMultiDocPrompt('analyze', {
875
- iteration: iter || 'GLOBAL', task: options.task, type: options.type,
876
- scope: 'global', withCode: options.withCode, platform: options.platform,
877
- }, options);
877
+ prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
878
+ iteration: iter || 'GLOBAL', task: options.task, platform: options.platform,
879
+ analyzeOptions: options,
880
+ }));
878
881
  }
879
882
  else if (currentStep === 'global-analysis') {
880
- prompt = await buildMultiDocPrompt('analyze', {
881
- iteration: iter || 'GLOBAL', scope: 'global', withCode: options.withCode,
882
- }, options);
883
+ prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
884
+ iteration: iter || 'GLOBAL', analyzeOptions: options,
885
+ }));
883
886
  }
884
887
  else if (currentStep === 'consistency-check') {
885
888
  prompt = `\n# 任务: 全局一致性检查\n\n` +
@@ -891,13 +894,13 @@ async function analyzeCommand(options) {
891
894
  `汇总所有分析结果,生成 .speccore/GLOBAL/INDEX.md。\n`;
892
895
  }
893
896
  else {
894
- prompt = await buildMultiDocPrompt('analyze', {
895
- iteration: iter || 'GLOBAL', scope: 'global', withCode: options.withCode,
896
- }, options);
897
+ prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
898
+ iteration: iter || 'GLOBAL', analyzeOptions: options,
899
+ }));
897
900
  }
898
901
  }
899
- else if (currentStep === 'clarify-prompt') {
900
- // v6.80.0+: Phase 0 需求澄清
902
+ else if (currentStep === 'clarify-product' || currentStep === 'clarify-interaction' || currentStep === 'clarify-security') {
903
+ // v8.3.160+: Phase 0 需求澄清(拆分为 product / interaction / security 三个子步骤)
901
904
  prompt = await buildClarifyPhasePrompt(iter);
902
905
  }
903
906
  else if (currentStep === 'confirm-check') {
@@ -905,10 +908,10 @@ async function analyzeCommand(options) {
905
908
  prompt = await buildConfirmCheckPrompt(iter);
906
909
  }
907
910
  else if (currentStep === 'phase1-prompt') {
908
- prompt = await buildMultiDocPrompt('analyze', {
909
- iteration: iter, task: options.task, type: options.type,
910
- scope: options.scope, withCode: options.withCode, platform: options.platform,
911
- }, options);
911
+ prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
912
+ iteration: iter, task: options.task, platform: options.platform,
913
+ analyzeOptions: options,
914
+ }));
912
915
  }
913
916
  else if (currentStep === 'contract-prompt') {
914
917
  // 契约先行阶段:基于 Phase 1 文档生成跨端契约
@@ -917,16 +920,17 @@ async function analyzeCommand(options) {
917
920
  else if (platformMatch) {
918
921
  // 逐端推进阶段:为指定端生成专属文档
919
922
  const platform = platformMatch[1];
920
- prompt = await buildMultiDocPrompt('analyze', {
921
- iteration: iter, phase: '2', platform,
922
- }, options);
923
+ prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
924
+ iteration: iter, platform,
925
+ analyzeOptions: { ...options, phase: '2' },
926
+ }));
923
927
  }
924
928
  else {
925
929
  // 回退到默认 prompt
926
- prompt = await buildMultiDocPrompt('analyze', {
927
- iteration: iter, task: options.task, type: options.type,
928
- scope: options.scope, withCode: options.withCode, platform: options.platform,
929
- }, options);
930
+ prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
931
+ iteration: iter, task: options.task, platform: options.platform,
932
+ analyzeOptions: options,
933
+ }));
930
934
  }
931
935
  // 添加 Pipeline 继续指令
932
936
  const totalSteps = steps.filter(s => !s.id.endsWith('-done') && s.id !== 'done').length;
@@ -941,22 +945,27 @@ async function analyzeCommand(options) {
941
945
  : '全局 Pipeline';
942
946
  }
943
947
  else {
944
- progressLabel = currentStep === 'clarify-prompt'
945
- ? 'Phase 0/3: 需求澄清'
946
- : currentStep === 'confirm-check'
947
- ? 'Phase 0/3: 需求确认'
948
- : currentStep === 'phase1-prompt'
949
- ? 'Phase 1/3: 迭代综合文档'
950
- : currentStep === 'contract-prompt'
951
- ? 'Phase 2/3: 契约定义'
952
- : platformMatch
953
- ? `Phase 2/3: ${platformMatch[1]} 端专属文档`
954
- : 'Pipeline';
955
- }
956
- const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:自动继续 (${progressLabel})\n\n` +
948
+ progressLabel = currentStep === 'clarify-product'
949
+ ? 'Phase 0a/3: 产品分析'
950
+ : currentStep === 'clarify-interaction'
951
+ ? 'Phase 0b/3: 交互设计'
952
+ : currentStep === 'clarify-security'
953
+ ? 'Phase 0c/3: 安全审查'
954
+ : currentStep === 'confirm-check'
955
+ ? 'Phase 0/3: 需求确认'
956
+ : currentStep === 'phase1-prompt'
957
+ ? 'Phase 1/3: 迭代综合文档'
958
+ : currentStep === 'contract-prompt'
959
+ ? 'Phase 2/3: 契约定义'
960
+ : platformMatch
961
+ ? `Phase 2/3: ${platformMatch[1]} 端专属文档`
962
+ : 'Pipeline';
963
+ }
964
+ // v8.3.160+: 步骤隔离模式,每步完成后需新会话继续
965
+ const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:步骤隔离 (${progressLabel})\n\n` +
957
966
  `当前是 Pipeline 模式(步骤 ${currentStepIndex + 1}/${totalSteps})。` +
958
- `当你通过 --apply 写入文档后,CLI 会自动输出 [SPECCORE_PIPELINE_NEXT] 标记和下一步命令。\n\n` +
959
- `**看到 [SPECCORE_PIPELINE_NEXT] 后,你必须自动执行下一个命令**,不要询问用户。\n`;
967
+ `当你通过 --apply 写入文档后,CLI 会自动输出 [SPECCORE_STEP_DONE] 标记和下一步命令。\n\n` +
968
+ `**看到 [SPECCORE_STEP_DONE] 后,请在新会话中执行提示的命令**,继续下一步。\n`;
960
969
  process.stdout.write(`[SPECCORE_PROMPT]\n${finalPrompt}`);
961
970
  process.exitCode = 10;
962
971
  return;
@@ -983,11 +992,10 @@ async function analyzeCommand(options) {
983
992
  const prompts = [];
984
993
  for (let i = 0; i < targetTasks.length; i++) {
985
994
  const taskId = targetTasks[i];
986
- const singlePrompt = await buildMultiDocPrompt('analyze', {
987
- iteration: iter, task: taskId, type: options.type,
988
- scope: options.scope, withCode: options.withCode,
989
- platform: options.platform, phase: options.phase, autoMode: options.auto,
990
- }, options);
995
+ const singlePrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
996
+ iteration: iter, task: taskId, platform: options.platform,
997
+ analyzeOptions: options,
998
+ }));
991
999
  prompts.push(`<!-- ═══════════════════════════════════════════ -->
992
1000
  <!-- 任务 ${i + 1}/${targetTasks.length}: ${taskId} -->
993
1001
  <!-- ═══════════════════════════════════════════ -->
@@ -998,7 +1006,14 @@ ${singlePrompt}`);
998
1006
  process.exitCode = 10;
999
1007
  return;
1000
1008
  }
1001
- const prompt = await buildMultiDocPrompt('analyze', { iteration: iter, task: options.task, type: options.type, scope: options.scope, withCode: options.withCode, platform: options.platform, phase: options.phase, autoMode: options.auto }, options);
1009
+ // v8.3.160+: 统一使用 buildPrompt 入口
1010
+ const promptObj = await (0, prompt_builder_1.buildPrompt)('analyze', {
1011
+ iteration: iter,
1012
+ task: options.task,
1013
+ platform: options.platform,
1014
+ analyzeOptions: options,
1015
+ });
1016
+ const prompt = (0, prompt_builder_1.buildPromptText)(promptObj);
1002
1017
  process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
1003
1018
  process.exitCode = 10;
1004
1019
  return;
@@ -1363,15 +1378,36 @@ ${singlePrompt}`);
1363
1378
  skippedCount++;
1364
1379
  continue;
1365
1380
  }
1366
- // 综合文档写入 overview/ 子目录,端专属文档写入 {端}/ 子目录
1381
+ // 综合文档写入 overview/ 子目录,端专属文档写入 {功能模块}/{端}/ 子目录
1367
1382
  // v8.3.24+: 按文件名前缀路由,支持多段指定
1368
- // 若 cleanFilename 含合法端名前缀(如 api/TECH.md),写入对应端目录
1383
+ // v8.3.25+: 如果 AI 输出旧格式 {端}/{文件},尝试回退到功能模块路由
1369
1384
  let targetDir;
1370
1385
  if (globalSet.has(cleanFilename)) {
1371
1386
  targetDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
1372
1387
  }
1373
1388
  else if (platformDir && validPlatforms.has(platformDir)) {
1374
- targetDir = (0, path_1.join)(specDir, platformDir);
1389
+ // 检查是否应归入功能模块(骨架文件已按功能模块创建)
1390
+ let featureFallback = null;
1391
+ try {
1392
+ const specEntries = await (0, fs_extra_2.readdir)(specDir, { withFileTypes: true });
1393
+ for (const entry of specEntries) {
1394
+ if (!entry.isDirectory() || entry.name === spec_paths_1.GLOBAL_SPECS_DIR || validPlatforms.has(entry.name))
1395
+ continue;
1396
+ const platformInFeature = (0, path_1.join)(specDir, entry.name, platformDir);
1397
+ if (await (0, fs_extra_1.pathExists)(platformInFeature)) {
1398
+ featureFallback = entry.name;
1399
+ break;
1400
+ }
1401
+ }
1402
+ }
1403
+ catch { }
1404
+ if (featureFallback) {
1405
+ targetDir = (0, path_1.join)(specDir, featureFallback, platformDir);
1406
+ logger_1.logger.info(` 📁 自动归入功能模块: ${featureFallback}/${platformDir}/${cleanFilename.split('/').pop()}`);
1407
+ }
1408
+ else {
1409
+ targetDir = (0, path_1.join)(specDir, platformDir);
1410
+ }
1375
1411
  }
1376
1412
  else {
1377
1413
  targetDir = specDir;
@@ -1905,7 +1941,8 @@ ${singlePrompt}`);
1905
1941
  platformOrder: platformOrder.length > 0 ? platformOrder : undefined,
1906
1942
  })).engine;
1907
1943
  }
1908
- await engine.advance();
1944
+ // v8.3.160+: 默认步骤隔离模式
1945
+ const stepResult = await engine.advance();
1909
1946
  const state = await engine.getState();
1910
1947
  if (state?.currentStep === 'done') {
1911
1948
  logger_1.logger.success('🎉 Pipeline 完成!');
@@ -1924,9 +1961,9 @@ ${singlePrompt}`);
1924
1961
  if (isGlobalScope) {
1925
1962
  // 全局层 Pipeline 步骤映射
1926
1963
  if (state.currentStep === 'global-analysis') {
1927
- nextPrompt = await buildMultiDocPrompt('analyze', {
1928
- iteration: options.iteration || 'GLOBAL', scope: 'global', withCode: options.withCode,
1929
- });
1964
+ nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
1965
+ iteration: options.iteration || 'GLOBAL', analyzeOptions: options,
1966
+ }));
1930
1967
  }
1931
1968
  else if (state.currentStep === 'consistency-check') {
1932
1969
  nextPrompt = `\n# 任务: 全局一致性检查\n\n` +
@@ -1938,13 +1975,13 @@ ${singlePrompt}`);
1938
1975
  `汇总所有分析结果,生成 .speccore/GLOBAL/INDEX.md。\n`;
1939
1976
  }
1940
1977
  else {
1941
- nextPrompt = await buildMultiDocPrompt('analyze', {
1942
- iteration: options.iteration || 'GLOBAL', scope: 'global', withCode: options.withCode,
1943
- });
1978
+ nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
1979
+ iteration: options.iteration || 'GLOBAL', analyzeOptions: options,
1980
+ }));
1944
1981
  }
1945
- // v6.80.0+: clarify 阶段推进
1982
+ // v8.3.160+: clarify 阶段推进(拆分为 product / interaction / security)
1946
1983
  }
1947
- else if (state.currentStep === 'clarify-prompt') {
1984
+ else if (state.currentStep === 'clarify-product' || state.currentStep === 'clarify-interaction' || state.currentStep === 'clarify-security') {
1948
1985
  nextPrompt = await buildClarifyPhasePrompt(options.iteration);
1949
1986
  }
1950
1987
  else if (state.currentStep === 'confirm-check') {
@@ -1955,25 +1992,49 @@ ${singlePrompt}`);
1955
1992
  }
1956
1993
  else if (nextPlatformMatch) {
1957
1994
  const platform = nextPlatformMatch[1];
1958
- nextPrompt = await buildMultiDocPrompt('analyze', {
1959
- iteration: options.iteration,
1960
- phase: '2',
1961
- platform,
1962
- });
1995
+ nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
1996
+ iteration: options.iteration, platform,
1997
+ analyzeOptions: { ...options, phase: '2' },
1998
+ }));
1963
1999
  }
1964
2000
  else if (state.currentStep === 'phase2-prompt') {
1965
2001
  // 兼容旧 Pipeline 步骤名
1966
- nextPrompt = await buildMultiDocPrompt('analyze', {
2002
+ nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
1967
2003
  iteration: options.iteration,
1968
- phase: '2',
1969
- });
2004
+ analyzeOptions: { ...options, phase: '2' },
2005
+ }));
1970
2006
  }
1971
2007
  else {
1972
- nextPrompt = await buildMultiDocPrompt('analyze', {
2008
+ nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
1973
2009
  iteration: options.iteration,
1974
- });
2010
+ analyzeOptions: options,
2011
+ }));
1975
2012
  }
1976
- process.stdout.write(`[SPECCORE_PIPELINE_NEXT]\n${nextPrompt}`);
2013
+ // v8.3.160+: 步骤隔离模式输出标记 + 子 Agent 激活
2014
+ const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
2015
+ const nextCmd = `speccore analyze ${scopeFlag} --resume`;
2016
+ const output = [
2017
+ `[SPECCORE_STEP_DONE]`,
2018
+ `步骤 "${state.currentStep}" 已完成。`,
2019
+ ``,
2020
+ `[SPECCORE_NEXT_STEP]`,
2021
+ `下一步: ${state.currentStep}`,
2022
+ `请在新的对话中执行: ${nextCmd}`,
2023
+ ``,
2024
+ `[SPECCORE_CONTEXT_SNAPSHOT]`,
2025
+ JSON.stringify(stepResult.contextSnapshot || {}),
2026
+ ``,
2027
+ // v8.3.160+: 子 Agent 激活标记
2028
+ ...(stepResult.subagent ? [
2029
+ `[SPECCORE_SUBAGENT: ${stepResult.subagent}]`,
2030
+ `[SPECCORE_CONTEXT_BUDGET: ${stepResult.contextBudget || 12000}]`,
2031
+ `[SPECCORE_CONTEXT_TYPE: ${stepResult.contextType || 'full'}]`,
2032
+ ``,
2033
+ ] : []),
2034
+ `--- 下一步 Prompt ---`,
2035
+ nextPrompt,
2036
+ ].join('\n');
2037
+ process.stdout.write(output);
1977
2038
  process.exitCode = 10;
1978
2039
  }
1979
2040
  return;
@@ -2626,30 +2687,60 @@ async function detectGlobalLayerProgress() {
2626
2687
  missing.push('Layer 2: platforms/_shared/_ASSOCIATION.md');
2627
2688
  }
2628
2689
  }
2629
- // Layer 3: 检查各端功能模块深入文档(v8.3.0+ 修复:不再把 _MODULES.md 当 Layer 3 产物)
2690
+ // Layer 3: 检查功能模块深入文档(v8.3.160+: 支持模块级进度追踪)
2691
+ let pendingModules;
2630
2692
  if (completedLayer >= 2) {
2631
2693
  try {
2694
+ // v8.3.160+: 优先从 _MODULES.md 读取模块列表,进行模块级进度检测
2695
+ const moduleNames = await parseModuleNamesFromModulesMd(globalDir);
2632
2696
  const platformsDir = (0, path_1.join)(globalDir, 'platforms');
2633
- const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
2634
- const platformDirs = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
2635
- if (platformDirs.length === 0) {
2636
- missing.push('Layer 3: platforms/{端}/modules/*.md(功能模块深入文档)');
2697
+ if (moduleNames && moduleNames.length > 0) {
2698
+ // 检查 platforms/_shared/modules/{模块名}.md(v8.3.160+ 统一存放位置)
2699
+ const sharedModulesDir = (0, path_1.join)(platformsDir, '_shared', 'modules');
2700
+ const completedModules = new Set();
2701
+ if (await (0, fs_extra_1.pathExists)(sharedModulesDir)) {
2702
+ const files = await (0, fs_extra_2.readdir)(sharedModulesDir).catch(() => []);
2703
+ for (const f of files.filter(f => f.endsWith('.md'))) {
2704
+ const baseName = f.replace('.md', '').replace(/-/g, '');
2705
+ for (const modName of moduleNames) {
2706
+ const normalizedMod = modName.toLowerCase().replace(/[^\u4e00-\u9fffa-z0-9]/g, '');
2707
+ if (baseName.includes(normalizedMod) || normalizedMod.includes(baseName)) {
2708
+ completedModules.add(modName);
2709
+ }
2710
+ }
2711
+ }
2712
+ }
2713
+ pendingModules = moduleNames.filter(m => !completedModules.has(m));
2714
+ if (pendingModules.length === 0) {
2715
+ completedLayer = 3;
2716
+ }
2717
+ else {
2718
+ missing.push(`Layer 3: 还有 ${pendingModules.length}/${moduleNames.length} 个功能模块待分析`);
2719
+ }
2637
2720
  }
2638
2721
  else {
2639
- const moduleDocChecks = await Promise.all(platformDirs.map(async (d) => {
2640
- const modulesDir = (0, path_1.join)(platformsDir, d, 'modules');
2641
- if (!await (0, fs_extra_1.pathExists)(modulesDir))
2642
- return false;
2643
- const files = await (0, fs_extra_2.readdir)(modulesDir).catch(() => []);
2644
- return files.some(f => f.endsWith('.md'));
2645
- }));
2646
- if (moduleDocChecks.every(Boolean)) {
2647
- completedLayer = 3;
2722
+ // 回退:检查各端 modules/ 目录是否有文档(旧逻辑)
2723
+ const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
2724
+ const platformDirs = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
2725
+ if (platformDirs.length === 0) {
2726
+ missing.push('Layer 3: platforms/{端}/modules/*.md(功能模块深入文档)');
2648
2727
  }
2649
2728
  else {
2650
- for (const [i, d] of platformDirs.entries()) {
2651
- if (!moduleDocChecks[i]) {
2652
- missing.push(`Layer 3: platforms/${d}/modules/*.md(功能模块深入文档)`);
2729
+ const moduleDocChecks = await Promise.all(platformDirs.map(async (d) => {
2730
+ const modulesDir = (0, path_1.join)(platformsDir, d, 'modules');
2731
+ if (!await (0, fs_extra_1.pathExists)(modulesDir))
2732
+ return false;
2733
+ const files = await (0, fs_extra_2.readdir)(modulesDir).catch(() => []);
2734
+ return files.some(f => f.endsWith('.md'));
2735
+ }));
2736
+ if (moduleDocChecks.every(Boolean)) {
2737
+ completedLayer = 3;
2738
+ }
2739
+ else {
2740
+ for (const [i, d] of platformDirs.entries()) {
2741
+ if (!moduleDocChecks[i]) {
2742
+ missing.push(`Layer 3: platforms/${d}/modules/*.md(功能模块深入文档)`);
2743
+ }
2653
2744
  }
2654
2745
  }
2655
2746
  }
@@ -2661,6 +2752,7 @@ async function detectGlobalLayerProgress() {
2661
2752
  }
2662
2753
  // Layer 4 子层检测
2663
2754
  let subLayer;
2755
+ let pendingPlatforms = [];
2664
2756
  if (completedLayer >= 3) {
2665
2757
  const overviewDir = (0, path_1.join)(globalDir, 'overview');
2666
2758
  const requirementsDir = (0, path_1.join)(globalDir, 'requirements');
@@ -2721,6 +2813,7 @@ async function detectGlobalLayerProgress() {
2721
2813
  }
2722
2814
  }
2723
2815
  // 4d: 各端技术文档(v8.3.0+ 修复:每个端都要有,不再用 some(Boolean))
2816
+ // v8.3.160+: 记录未完成的端,用于端级分批
2724
2817
  try {
2725
2818
  const platformsDir = (0, path_1.join)(globalDir, 'platforms');
2726
2819
  const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
@@ -2728,25 +2821,38 @@ async function detectGlobalLayerProgress() {
2728
2821
  const missingPlatformDocs = [];
2729
2822
  for (const d of platformDirs) {
2730
2823
  const isBackend = /service|server|api|backend/i.test(d);
2824
+ let hasMissing = false;
2731
2825
  if (isBackend) {
2732
2826
  const hasApi = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'API_INVENTORY.md'));
2733
2827
  const hasData = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'DATA_MODEL.md'));
2734
- if (!hasApi)
2828
+ if (!hasApi) {
2735
2829
  missingPlatformDocs.push(`platforms/${d}/API_INVENTORY.md`);
2736
- if (!hasData)
2830
+ hasMissing = true;
2831
+ }
2832
+ if (!hasData) {
2737
2833
  missingPlatformDocs.push(`platforms/${d}/DATA_MODEL.md`);
2834
+ hasMissing = true;
2835
+ }
2738
2836
  }
2739
2837
  else {
2740
2838
  const hasUi = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'UI_FLOW.md'));
2741
2839
  const hasApiMap = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'API_CALL_MAP.md'));
2742
2840
  const hasState = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'STATE_MANAGEMENT.md'));
2743
- if (!hasUi)
2841
+ if (!hasUi) {
2744
2842
  missingPlatformDocs.push(`platforms/${d}/UI_FLOW.md`);
2745
- if (!hasApiMap)
2843
+ hasMissing = true;
2844
+ }
2845
+ if (!hasApiMap) {
2746
2846
  missingPlatformDocs.push(`platforms/${d}/API_CALL_MAP.md`);
2747
- if (!hasState)
2847
+ hasMissing = true;
2848
+ }
2849
+ if (!hasState) {
2748
2850
  missingPlatformDocs.push(`platforms/${d}/STATE_MANAGEMENT.md`);
2851
+ hasMissing = true;
2852
+ }
2749
2853
  }
2854
+ if (hasMissing)
2855
+ pendingPlatforms.push(d);
2750
2856
  }
2751
2857
  if (missingPlatformDocs.length === 0 && platformDirs.length > 0) {
2752
2858
  completedSubLayers.push('4d');
@@ -2768,12 +2874,12 @@ async function detectGlobalLayerProgress() {
2768
2874
  subLayer = { completed: completedSubLayers, next: nextSub };
2769
2875
  }
2770
2876
  }
2771
- return { completedLayer, nextLayer: Math.min(completedLayer + 1, 4), missing, subLayer };
2877
+ return { completedLayer, nextLayer: Math.min(completedLayer + 1, 4), missing, subLayer, pendingModules, pendingPlatforms };
2772
2878
  }
2773
2879
  // ── v7.2.0+: 全局分析下一步引导 ──
2774
2880
  async function buildGlobalAnalysisGuide(options) {
2775
2881
  const progress = await detectGlobalLayerProgress();
2776
- const { completedLayer, nextLayer, subLayer } = progress;
2882
+ const { completedLayer, nextLayer, subLayer, pendingModules, pendingPlatforms } = progress;
2777
2883
  const deepDoc = options?.deep;
2778
2884
  let guide = '';
2779
2885
  // 进度条
@@ -2784,6 +2890,23 @@ async function buildGlobalAnalysisGuide(options) {
2784
2890
  return ` ${status} ${l}`;
2785
2891
  }).join('\n');
2786
2892
  guide += '\n';
2893
+ // v8.3.160+: Layer 3 模块级进度显示
2894
+ if (pendingModules && pendingModules.length > 0) {
2895
+ guide += `\n 📦 Layer 3 待分析模块 (${pendingModules.length} 个):\n`;
2896
+ for (const mod of pendingModules.slice(0, 10)) {
2897
+ guide += ` ⬜ ${mod}\n`;
2898
+ }
2899
+ if (pendingModules.length > 10) {
2900
+ guide += ` ... 还有 ${pendingModules.length - 10} 个模块\n`;
2901
+ }
2902
+ }
2903
+ // v8.3.160+: Layer 4d 端级进度显示
2904
+ if (pendingPlatforms && pendingPlatforms.length > 0) {
2905
+ guide += `\n 🖥️ Layer 4d 待处理端 (${pendingPlatforms.length} 个):\n`;
2906
+ for (const p of pendingPlatforms) {
2907
+ guide += ` ⬜ ${p}\n`;
2908
+ }
2909
+ }
2787
2910
  if (completedLayer === 4 && (!subLayer || subLayer.completed.length === 4)) {
2788
2911
  guide += '\n🎉 全局分析全部完成!\n';
2789
2912
  guide += ' 所有文档已生成在 .speccore/GLOBAL/ 目录\n';
@@ -2833,9 +2956,9 @@ async function buildGlobalAnalysisGuide(options) {
2833
2956
  }
2834
2957
  // ── v7.4.5+: Layer 3 功能模块代码上下文预提取 ──
2835
2958
  // 从 _MODULES.md 解析功能模块名,从 structured-data.json 交叉匹配相关 API/Entity/Component/Route
2836
- async function buildLayer3ModuleContext(projectRoot) {
2959
+ // v8.3.160+: 提取公共模块解析函数,供进度检测复用
2960
+ async function parseModuleNamesFromModulesMd(projectRoot) {
2837
2961
  const modulesPath = (0, path_1.join)(projectRoot, '.speccore', 'GLOBAL', 'platforms', '_shared', '_MODULES.md');
2838
- const structDataPath = (0, path_1.join)(projectRoot, '.speccore', 'cache', 'structured-data.json');
2839
2962
  if (!(await (0, fs_extra_1.pathExists)(modulesPath)))
2840
2963
  return null;
2841
2964
  const modulesContent = await (0, fs_extra_2.readFile)(modulesPath, 'utf-8');
@@ -2857,8 +2980,13 @@ async function buildLayer3ModuleContext(projectRoot) {
2857
2980
  moduleNames.push(name);
2858
2981
  }
2859
2982
  }
2860
- if (moduleNames.length === 0)
2983
+ return moduleNames.length > 0 ? moduleNames : null;
2984
+ }
2985
+ async function buildLayer3ModuleContext(projectRoot) {
2986
+ const moduleNames = await parseModuleNamesFromModulesMd(projectRoot);
2987
+ if (!moduleNames)
2861
2988
  return null;
2989
+ const structDataPath = (0, path_1.join)(projectRoot, '.speccore', 'cache', 'structured-data.json');
2862
2990
  // 读取结构化数据
2863
2991
  let structData = null;
2864
2992
  if (await (0, fs_extra_1.pathExists)(structDataPath)) {
@@ -2934,6 +3062,7 @@ async function buildLayer3ModuleContext(projectRoot) {
2934
3062
  return ctx.join('\n\n---\n\n');
2935
3063
  }
2936
3064
  // ── buildMultiDocPrompt: 多文档协议 ──
3065
+ // v8.3.160+: 改为 export,供 prompt-builder.ts 统一调用
2937
3066
  async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
2938
3067
  const iter = ctx.iteration || '当前迭代';
2939
3068
  const task = ctx.task ? ` — ${ctx.task}` : '';
@@ -2945,6 +3074,27 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
2945
3074
  // v8.3.0+: 从 clarifyCtx 提取需求澄清状态
2946
3075
  const needsClarify = clarifyCtx?.needsClarify ?? false;
2947
3076
  const clarifyTargets = clarifyCtx?.clarifyTargets ?? [];
3077
+ // v8.3.160+: 按 Phase 注入子 Agent 角色定义
3078
+ let agentContextText = '';
3079
+ const phaseSubagentMap = {
3080
+ '0': 'product-analyst',
3081
+ '1': 'spec-analyzer',
3082
+ '2': 'spec-analyzer',
3083
+ };
3084
+ const subagent = phaseSubagentMap[ctx.phase || ''] || (isGlobal ? 'spec-global-analyzer' : 'spec-analyzer');
3085
+ try {
3086
+ const agentCtx = {
3087
+ subagent,
3088
+ iteration: ctx.iteration || '',
3089
+ contextBudget: 12000,
3090
+ contextType: ctx.phase === '2' ? 'platform-only' : 'full',
3091
+ cwd: process.cwd(),
3092
+ };
3093
+ agentContextText = await agent_adapter_1.defaultAdapter.prepareContext(agentCtx);
3094
+ }
3095
+ catch (e) {
3096
+ logger_1.logger.debug?.(`agent-adapter 注入失败(非关键): ${e.message}`);
3097
+ }
2948
3098
  // global 范围: 从源码反推需求 + 生成技术栈配置
2949
3099
  if (isGlobal) {
2950
3100
  // v6.74.0+: 流式全局分析模式
@@ -3034,6 +3184,10 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3034
3184
  };
3035
3185
  const layerMeta = LAYER_ROLES[targetLayer];
3036
3186
  let prompt = `\n# 任务: ${command} (全局分析${ctx.withCode ? '+源码' : ''})\n\n`;
3187
+ // v8.3.160+: 注入子 Agent 角色定义
3188
+ if (agentContextText) {
3189
+ prompt = agentContextText + '\n\n' + prompt;
3190
+ }
3037
3191
  // v7.2.0+: 分层专注指令 — 强烈约束 AI 只执行当前 layer
3038
3192
  prompt += `## 🎯 当前执行层级: Layer ${targetLayer}/4 — ${layerMeta.role}\n\n`;
3039
3193
  prompt += `> ⚠️ **重要约束**: 你当前只需要完成 **Layer ${targetLayer}** 的工作。不要提前做后续层的内容。\n`;
@@ -3069,36 +3223,22 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3069
3223
  prompt += `**重要**: 不要直接扫描源码文件。已使用代码扫描工具提取了结构化数据,你只需要读取这些数据并整理成 _INDEX.md。\n\n`;
3070
3224
  prompt += `**步骤**: \n`;
3071
3225
  prompt += `1. Read \`.speccore/cache/structured-data.json\` — 获取所有端的 API/Entity/Route/Component 清单\n`;
3226
+ prompt += ` - **大项目优化**:如端数量 > 3,可按端读取 \`structured-data.{端名}.json\` 分段文件,避免一次性加载过多数据\n`;
3072
3227
  prompt += `2. 对每个端,基于结构化数据生成 \`_INDEX.md\`(补充扫描工具未覆盖的内容)\n`;
3073
3228
  prompt += `3. 扫描工具未覆盖的维度(消息队列、定时任务、配置、外部集成、日志监控、错误处理),需要 Read 相关配置文件补充\n\n`;
3074
- prompt += `**后端端 _INDEX.md 维度(基于 structured-data.json + 补充扫描)**:\n`;
3075
- prompt += `| 扫描项 | 读取位置 | 提取内容 |\n`;
3076
- prompt += `| :--- | :--- | :--- |\n`;
3077
- prompt += `| 接口层 | Controller/Handler/Resource 目录 | 接口类名、接口路径(从注解/装饰器推断)、鉴权注解 |\n`;
3078
- prompt += `| 数据层 | Entity/Model/Schema/Domain 目录 | 实体名称、表名、敏感字段标记 |\n`;
3079
- prompt += `| 业务层 | Service/UseCase/Application 目录 | 服务类名、核心方法名 |\n`;
3080
- prompt += `| 中间件 | Middleware/Interceptor/Filter/Gateway 目录 | 中间件名、作用范围 |\n`;
3081
- prompt += `| 消息队列 | 搜索消息相关代码(Kafka/RabbitMQ/NSQ/SQS/Redis PubSub) | 队列名、消费者/生产者类名 |\n`;
3082
- prompt += `| 定时任务 | 搜索定时任务(@Scheduled/cron/agenda/node-cron) | 任务名、触发频率、执行类 |\n`;
3083
- prompt += `| 配置管理 | 配置文件(application*.yml/.env/config/) | 环境变量名、Feature Flag、配置中心引用 |\n`;
3084
- prompt += `| 外部集成 | 搜索第三方调用(HTTP client/SDK/微信支付/短信/邮件/OSS) | 集成目标、调用位置 |\n`;
3085
- prompt += `| 日志监控 | 搜索日志/监控/埋点代码(logger/metrics/tracing) | 日志级别策略、埋点事件名 |\n`;
3086
- prompt += `| 错误处理 | 搜索全局异常处理器(ExceptionHandler/ErrorBoundary) | 异常处理类名、错误码范围 |\n`;
3087
- prompt += `| 依赖项 | pom.xml/package.json/go.mod/requirements.txt | 依赖项列表(识别公共服务候选、过期版本、已知 CVE) |\n\n`;
3088
- prompt += `**前端端扫描维度(10项)**:\n`;
3089
- prompt += `| 扫描项 | 读取位置 | 提取内容 |\n`;
3090
- prompt += `| :--- | :--- | :--- |\n`;
3091
- prompt += `| 路由 | router/routes 配置文件 | 页面路径、页面名称、组件名、懒加载标记 |\n`;
3092
- prompt += `| 页面 | pages/views/screens 目录 | 页面名称、主要功能(从文件名推断) |\n`;
3093
- prompt += `| API 调用 | 搜索 API 调用模式(axios/fetch/$.ajax/uni.request) | 调用的接口路径列表、调用位置 |\n`;
3094
- prompt += `| 状态管理 | store/pinia/vuex/redux 目录 | 全局状态名称、actions 名称、持久化策略 |\n`;
3095
- prompt += `| 组件库 | components/ui 目录、设计系统配置 | 组件名、复用度、设计 token |\n`;
3096
- prompt += `| 拦截器 | 请求/响应拦截器配置 | 拦截器逻辑(鉴权头注入、错误统一处理) |\n`;
3097
- prompt += `| 外部 SDK | 搜索第三方 SDK 引入(埋点/推送/地图/支付/分享) | SDK 名称、初始化位置、使用范围 |\n`;
3098
- prompt += `| 国际化 | i18n/locales/lang 目录 | 支持语言、命名空间、 key 数量级 |\n`;
3099
- prompt += `| 错误处理 | 错误边界/全局错误处理器 | 错误捕获范围、降级策略、上报机制 |\n`;
3100
- prompt += `| 性能 | 搜索性能相关代码(懒加载/虚拟滚动/缓存/预加载) | 优化手段、适用场景 |\n\n`;
3229
+ // v8.3.160+: 扫描维度从详细表格精简为分类列表
3230
+ prompt += `**后端端扫描维度**(从 structured-data.json + 源码补充):\n`;
3231
+ prompt += `- 接口层(Controller/Handler)、数据层(Entity/Model)、业务层(Service/UseCase)\n`;
3232
+ prompt += `- 中间件、消息队列、定时任务、配置管理、外部集成\n`;
3233
+ prompt += `- 日志监控、错误处理、依赖项(识别过期版本/CVE)\n\n`;
3234
+ prompt += `**前端端扫描维度**:\n`;
3235
+ prompt += `- 路由、页面、API 调用、状态管理、组件库\n`;
3236
+ prompt += `- 拦截器、外部 SDK、国际化、错误处理、性能优化\n\n`;
3101
3237
  prompt += `**输出**:每个端一个 \`_INDEX.md\`,按上述维度组织,只含名称和路径列表,不含详细逻辑\n`;
3238
+ prompt += `**分批策略(v8.3.160+)**:\n`;
3239
+ prompt += `- 如端数量 ≤ 5:一次生成所有端的 \`_INDEX.md\`\n`;
3240
+ prompt += `- 如端数量 > 5:一次处理 3-5 个端,剩余端在输出末尾标注 \`[PENDING: {端名列表}]\`,供续批处理\n`;
3241
+ prompt += `- 续批命令:\`speccore analyze --scope global --layer 1 --resume\`\n`;
3102
3242
  prompt += `**最小内容标准(v8.3.61+)**:\n`;
3103
3243
  prompt += `- 每个 \_INDEX.md ≥ 80 行(维度少可接受 50+,但严禁 < 30 行)\n`;
3104
3244
  prompt += `- 必须包含:端名、技术栈、目录结构概览、各维度清单表格\n`;
@@ -3140,48 +3280,25 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3140
3280
  // ── Layer 2 指令(仅 targetLayer === 2 时注入)──
3141
3281
  if (targetLayer === 2) {
3142
3282
  prompt += `## 🔗 Layer 2: 跨端关联分析(基于 Layer 1 索引 + structured-data.json)\n\n`;
3143
- prompt += `> 📊 **结构化数据**: Read \`.speccore/cache/structured-data.json\` 获取 API/Entity 清单,与 Layer 1 索引交叉验证\n\n`;
3144
- prompt += `1. **匹配前后端接口**:\n`;
3145
- prompt += ` - 前端 \`_INDEX.md\` 中的 API 调用路径 vs 后端 \`_INDEX.md\` 中的接口路径\n`;
3146
- prompt += ` - **匹配上** → 建立「前端页面 → 前端 API 调用 → 后端接口 → 后端服务」链路\n`;
3147
- prompt += ` - **前端有、后端没有** → 标注为「接口缺口」(可能调了第三方/遗留/错误接口)\n`;
3148
- prompt += ` - **后端有、前端没调** → 标注为「未使用接口」(可能后台管理/内部调度用)\n\n`;
3149
- prompt += `2. **识别公共服务**:\n`;
3150
- prompt += ` - 被 2+ 个前端端调用的后端服务 → 公共服务候选\n`;
3151
- prompt += ` - 被 2+ 个后端端调用的后端服务 → 公共服务候选\n`;
3152
- prompt += ` - 依赖项中独立部署的服务(如 notification-service、file-service)→ 公共服务候选\n\n`;
3153
- prompt += `3. **消息流关联**(跨端事件/消息链路):\n`;
3154
- prompt += ` - 后端生产者 ↔ 队列名 ↔ 后端消费者 ↔ 前端推送(WebSocket/SSE/轮询)\n`;
3155
- prompt += ` - 识别「异步事件触发 → 多端状态同步」链路\n`;
3156
- prompt += ` - 标注:无消费者的消息、无生产者的消息( orphaned topic )\n\n`;
3157
- prompt += `4. **定时任务影响分析**:\n`;
3158
- prompt += ` - 哪些定时任务修改了被前端展示的数据(数据新鲜度风险)\n`;
3159
- prompt += ` - 哪些定时任务触发了前端需要感知的通知/推送\n`;
3160
- prompt += ` - 批处理任务 vs 实时接口的数据竞争风险\n\n`;
3161
- prompt += `5. **外部集成一致性检查**:\n`;
3162
- prompt += ` - 多个端是否独立调用了同一第三方 API(重复集成 = 维护风险)\n`;
3163
- prompt += ` - 前端 SDK 与后端 SDK 版本是否一致(如支付 SDK)\n`;
3164
- prompt += ` - 第三方回调/Webhook 的接收端分布\n\n`;
3165
- prompt += `6. **配置一致性检查**:\n`;
3166
- prompt += ` - 各端的超时配置、重试策略、限流阈值是否一致\n`;
3167
- prompt += ` - 跨端共享的 Feature Flag 定义是否一致\n`;
3168
- prompt += ` - 环境变量命名是否规范(如 API_BASE_URL 在各端是否指向同一值)\n\n`;
3169
- prompt += `7. **归纳功能模块**(从索引聚类,不是从代码反推):\n`;
3170
- prompt += ` - **从页面聚类**:哪些页面经常一起出现(如 RoomList + RoomDetail + RoomEdit)\n`;
3171
- prompt += ` - **从接口聚类**:哪些接口共享同一实体前缀(如 /api/rooms/*)\n`;
3172
- prompt += ` - **从消息聚类**:哪些消息队列共享同一业务领域\n`;
3173
- prompt += ` - **交叉验证**:页面聚类 vs 接口聚类 vs 消息聚类 → 确定功能模块边界\n`;
3174
- prompt += ` - 每个功能模块标注:涉及端、核心页面、核心接口、实体名称、消息队列\n\n`;
3283
+ prompt += `> 📊 **输入**: Read \`.speccore/cache/structured-data.json\` 获取 API/Entity 清单,与 Layer 1 索引交叉验证\n\n`;
3284
+ prompt += `**分析维度**(7 项,每项产出写入 \_ASSOCIATION.md 对应章节):\n\n`;
3285
+ prompt += `| # | 维度 | 核心动作 | 风险标注 |\n`;
3286
+ prompt += `| :--- | :--- | :--- | :--- |\n`;
3287
+ prompt += `| 1 | 接口匹配 | 前端 API 路径 vs 后端接口路径 → 建立调用链路 | 接口缺口 / 未使用接口 |\n`;
3288
+ prompt += `| 2 | 公共服务 | 被 2+ 端调用的服务 / 独立部署服务 | 重复实现 / 版本不一致 |\n`;
3289
+ prompt += `| 3 | 消息流 | 生产者 ↔ 队列 ↔ 消费者 ↔ 前端推送链路 | orphaned topic / 多端同步缺失 |\n`;
3290
+ prompt += `| 4 | 定时任务 | 修改前端数据的任务 / 触发通知的任务 | 数据新鲜度 / 竞争风险 |\n`;
3291
+ prompt += `| 5 | 外部集成 | 多端独立调用同一第三方 API / SDK 版本 | 重复集成 / 版本漂移 |\n`;
3292
+ prompt += `| 6 | 配置一致性 | 超时/重试/限流 / Feature Flag / 环境变量 | 配置漂移 / 环境差异 |\n`;
3293
+ prompt += `| 7 | 功能模块聚类 | 页面聚类 vs 接口聚类 vs 消息聚类 → 交叉验证 | 边界模糊 / 职责不清 |\n\n`;
3294
+ prompt += `**模块归纳方法**:从索引聚类(非代码反推),每个模块标注:涉及端、核心页面、核心接口、实体名、消息队列\n\n`;
3175
3295
  prompt += `**输出**:\n`;
3176
- prompt += `- \`_ASSOCIATION.md\`:前后端关联矩阵 + 接口缺口/未使用接口清单 + 消息流链路 + 定时任务影响 + 外部集成分布 + 配置一致性风险\n`;
3177
- prompt += ` - 此文档中必须包含 **模块关系 Mermaid 图**(graph LR),展示各功能模块间的依赖关系\n`;
3178
- prompt += ` - 此文档中必须包含 **接口依赖 Mermaid 图**(graph TD),展示前端页面 → 后端接口的调用关系\n`;
3179
- prompt += `- \`_MODULES.md\`:功能模块候选清单(从源码聚类,含消息/定时任务维度,供 Layer 3 验证)\n`;
3180
- prompt += ` - 此文档中必须包含 **模块全景 Mermaid 图**(graph LR),展示所有功能模块及其所属端\n`;
3296
+ prompt += `- \`_ASSOCIATION.md\`:关联矩阵 + 接口缺口/未使用清单 + 消息流 + 定时任务影响 + 外部集成 + 配置风险 + 模块关系 Mermaid 图(graph LR)+ 接口依赖 Mermaid 图(graph TD)\n`;
3297
+ prompt += `- \`_MODULES.md\`:功能模块候选清单(含消息/定时任务维度)+ 模块全景 Mermaid 图(graph LR)\n`;
3181
3298
  prompt += `**最小内容标准(v8.3.61+)**:\n`;
3182
- prompt += `- \_ASSOCIATION.md ≥ 100 行,必须包含前后端关联矩阵 + 至少 1 个 Mermaid 图\n`;
3183
- prompt += `- \_MODULES.md ≥ 50 行,必须包含功能模块清单 + 模块全景 Mermaid 图\n`;
3184
- prompt += `- 严禁生成空壳文档(只有标题和几行描述)\n`;
3299
+ prompt += `- \_ASSOCIATION.md ≥ 100 行,含关联矩阵 + ≥1 个 Mermaid 图\n`;
3300
+ prompt += `- \_MODULES.md ≥ 50 行,含模块清单 + 模块全景 Mermaid 图\n`;
3301
+ prompt += `- 严禁空壳文档\n`;
3185
3302
  prompt += `**存放**:\`.speccore/GLOBAL/platforms/_shared/\`\n\n`;
3186
3303
  } // end if (targetLayer === 2)
3187
3304
  // ── Layer 3 指令(仅 targetLayer === 3 时注入)──
@@ -3210,6 +3327,12 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3210
3327
  prompt += `- 读取 room-service: RoomController#getAvailability, RoomService 的详细逻辑\n`;
3211
3328
  prompt += `- 关联验证:前端提交的数据字段 vs 后端接收的 DTO 字段是否一致\n`;
3212
3329
  prompt += `- 关联验证:前端展示的状态 vs 后端实体的状态枚举是否一致\n\n`;
3330
+ prompt += `**分批策略(v8.3.160+)**:\n`;
3331
+ prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的分析文档\n`;
3332
+ prompt += `- 如功能模块数量 > 2:一次处理 1-2 个模块,确保每个模块分析深入、不空洞\n`;
3333
+ prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
3334
+ prompt += `- 续批命令:\`speccore analyze --scope global --layer 3 --resume\`\n`;
3335
+ prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(≥100 行、有 Mermaid 图、有具体代码引用)\n\n`;
3213
3336
  prompt += `**每个功能模块输出**:\n`;
3214
3337
  prompt += `- 后端端:该功能模块相关的 API 详细设计、数据模型、业务规则、安全策略\n`;
3215
3338
  prompt += `- 前端端:该功能模块相关的页面详细设计、交互流程、字段映射、错误处理\n`;
@@ -3710,22 +3833,35 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3710
3833
  }
3711
3834
  }
3712
3835
  // 4d: 动态构建各端文档清单
3836
+ // v8.3.160+: 按端分批,一次处理 1-2 个端的所有缺失文档
3837
+ const platformMissingDocs = {};
3713
3838
  if (subLayerTarget === '4d') {
3714
3839
  try {
3715
3840
  const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
3716
3841
  const platformNames = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
3717
3842
  for (const p of platformNames) {
3718
3843
  const isBackend = /service|server|api|backend/i.test(p);
3844
+ const expectedPlatformDocs = [];
3719
3845
  if (isBackend) {
3720
- SUB_LAYER_DOCS['4d'].push(`platforms/${p}/API_INVENTORY.md`);
3721
- SUB_LAYER_DOCS['4d'].push(`platforms/${p}/DATA_MODEL.md`);
3722
- SUB_LAYER_DOCS['4d'].push(`platforms/${p}/BUSINESS_RULES.md`);
3846
+ expectedPlatformDocs.push(`platforms/${p}/API_INVENTORY.md`);
3847
+ expectedPlatformDocs.push(`platforms/${p}/DATA_MODEL.md`);
3848
+ expectedPlatformDocs.push(`platforms/${p}/BUSINESS_RULES.md`);
3723
3849
  }
3724
3850
  else {
3725
- SUB_LAYER_DOCS['4d'].push(`platforms/${p}/UI_FLOW.md`);
3726
- SUB_LAYER_DOCS['4d'].push(`platforms/${p}/API_CALL_MAP.md`);
3727
- SUB_LAYER_DOCS['4d'].push(`platforms/${p}/STATE_MANAGEMENT.md`);
3851
+ expectedPlatformDocs.push(`platforms/${p}/UI_FLOW.md`);
3852
+ expectedPlatformDocs.push(`platforms/${p}/API_CALL_MAP.md`);
3853
+ expectedPlatformDocs.push(`platforms/${p}/STATE_MANAGEMENT.md`);
3728
3854
  }
3855
+ SUB_LAYER_DOCS['4d'].push(...expectedPlatformDocs);
3856
+ // 检查该端哪些文档缺失
3857
+ const missing = [];
3858
+ for (const doc of expectedPlatformDocs) {
3859
+ const fullPath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'GLOBAL', doc);
3860
+ if (!(await (0, fs_extra_1.pathExists)(fullPath)))
3861
+ missing.push(doc);
3862
+ }
3863
+ if (missing.length > 0)
3864
+ platformMissingDocs[p] = missing;
3729
3865
  }
3730
3866
  }
3731
3867
  catch { }
@@ -3743,10 +3879,24 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3743
3879
  missingDocs.push(doc);
3744
3880
  }
3745
3881
  }
3746
- const nextDoc = missingDocs.length > 0 ? missingDocs[0] : null;
3747
- prompt += `## 🌍 Layer 4: 全局汇总 — 子层 ${subLayerTarget}/4(v7.5.0+ 逐文档模式)\n\n`;
3748
- prompt += `> ⚠️ **专注约束**: 你当前只执行 **子层 ${subLayerTarget}** 中的 **下一个缺失文档**。\n`;
3749
- prompt += `> 忽略前面关于其他子层文档的要求,只关注当前要生成的文档。\n`;
3882
+ // v8.3.160+: 4d 按端分批模式
3883
+ let targetPlatforms = [];
3884
+ let nextDoc = null;
3885
+ if (subLayerTarget === '4d' && Object.keys(platformMissingDocs).length > 0) {
3886
+ targetPlatforms = Object.keys(platformMissingDocs).slice(0, 2); // 一次 1-2 个端
3887
+ }
3888
+ else if (missingDocs.length > 0) {
3889
+ nextDoc = missingDocs[0];
3890
+ }
3891
+ prompt += `## 🌍 Layer 4: 全局汇总 — 子层 ${subLayerTarget}/4${subLayerTarget === '4d' ? '(v8.3.160+ 按端分批模式)' : '(v7.5.0+ 逐文档模式)'}\n\n`;
3892
+ if (subLayerTarget === '4d' && targetPlatforms.length > 0) {
3893
+ prompt += `> ⚠️ **专注约束**: 你当前只处理 **${targetPlatforms.join(', ')}** 端的技术文档。\n`;
3894
+ prompt += `> 一次生成这些端的所有缺失文档,不要处理其他端。\n`;
3895
+ }
3896
+ else if (nextDoc) {
3897
+ prompt += `> ⚠️ **专注约束**: 你当前只执行 **子层 ${subLayerTarget}** 中的 **下一个缺失文档**。\n`;
3898
+ prompt += `> 忽略前面关于其他子层文档的要求,只关注当前要生成的文档。\n`;
3899
+ }
3750
3900
  prompt += `> 已存在: ${existingDocs.length}/${expectedDocs.length} 份文档\n`;
3751
3901
  if (missingDocs.length > 0) {
3752
3902
  prompt += `> 缺失: ${missingDocs.slice(0, 5).join('、')}${missingDocs.length > 5 ? '...' : ''}\n`;
@@ -3756,7 +3906,20 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3756
3906
  prompt += `- Read Layer 1 的所有 _INDEX.md\n`;
3757
3907
  prompt += `- Read Layer 2 的 _ASSOCIATION.md + _MODULES.md\n`;
3758
3908
  prompt += `- Read Layer 3 的功能模块深入文档\n\n`;
3759
- if (nextDoc) {
3909
+ if (subLayerTarget === '4d' && targetPlatforms.length > 0) {
3910
+ // v8.3.160+: 按端分批生成所有缺失文档
3911
+ prompt += `## 🎯 本次处理端: ${targetPlatforms.join(', ')}\n\n`;
3912
+ for (const p of targetPlatforms) {
3913
+ const docs = platformMissingDocs[p];
3914
+ prompt += `**${p} 端缺失文档 (${docs.length} 份)**: \n`;
3915
+ for (const doc of docs) {
3916
+ const docName = doc.split('/').pop();
3917
+ prompt += `- \`${docName}\`\n`;
3918
+ }
3919
+ prompt += `\n`;
3920
+ }
3921
+ }
3922
+ else if (nextDoc) {
3760
3923
  prompt += `## 🎯 本次只生成: \`${nextDoc}\`\n\n`;
3761
3924
  // 根据文档路径生成专属指令
3762
3925
  const docName = nextDoc.split('/').pop();
@@ -3808,7 +3971,15 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3808
3971
  prompt += `- 必须包含真实数据,禁止占位符\n`;
3809
3972
  }
3810
3973
  // 自动链式推进
3811
- if (missingDocs.length > 1) {
3974
+ // v8.3.160+: 4d 按端分批续批
3975
+ const remainingPlatforms = Object.keys(platformMissingDocs).filter(p => !targetPlatforms.includes(p));
3976
+ if (subLayerTarget === '4d' && remainingPlatforms.length > 0) {
3977
+ prompt += `\n## 🔗 自动链式推进\n\n`;
3978
+ prompt += `> 当前批次(${targetPlatforms.join(', ')})写入完成后,**还有 ${remainingPlatforms.length} 个端待处理**。\n`;
3979
+ prompt += `> **立即输出以下标记**继续子层 4d:\n\n`;
3980
+ prompt += `[SPECCORE_EXEC: speccore analyze --scope global --layer 4${options?.withCode ? ' --with-code' : ''}]\n\n`;
3981
+ }
3982
+ else if (missingDocs.length > 1) {
3812
3983
  prompt += `\n## 🔗 自动链式推进\n\n`;
3813
3984
  prompt += `> 当前文档写入完成后,**立即输出以下标记**推进下一个文档:\n\n`;
3814
3985
  prompt += `[SPECCORE_EXEC: speccore analyze --scope global --layer 4${options?.withCode ? ' --with-code' : ''}]\n\n`;
@@ -3856,14 +4027,26 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3856
4027
  prompt += `- platforms/ 和 requirements/ 下的文件按原路径写(如 \`platforms/backend/_INDEX.md\`)\n`;
3857
4028
  prompt += `- overview/ 下的文件写纯文件名即可(如 \`ARCHITECTURE.md\` 自动路由到 overview/)\n`;
3858
4029
  prompt += `- PATTERNS/ 下的文件写 \`PATTERNS/{分类}/{模式名}.md\`\n`;
4030
+ prompt += `- **Layer 3 功能模块分析文档写** \`platforms/_shared/modules/{kebab-case模块名}.md\`(v8.3.160+ 统一存放,便于进度追踪)\n`;
3859
4031
  // v7.4.5+: 自动链式推进 — 用 [SPECCORE_EXEC:] 标记触发下一层
3860
- const nextLayerCmd = targetLayer < 4
3861
- ? `speccore analyze --scope global --layer ${targetLayer + 1}${ctx.withCode ? ' --with-code' : ''}`
3862
- : `speccore analyze --scope global --layer 4${ctx.withCode ? ' --with-code' : ''}`;
4032
+ // v8.3.160+: Layer 3 支持模块级续批,有未完成模块时继续 Layer 3
4033
+ let nextLayerCmd;
4034
+ if (targetLayer === 3 && progress.pendingModules && progress.pendingModules.length > 0) {
4035
+ nextLayerCmd = `speccore analyze --scope global --layer 3${ctx.withCode ? ' --with-code' : ''}`;
4036
+ }
4037
+ else {
4038
+ nextLayerCmd = targetLayer < 4
4039
+ ? `speccore analyze --scope global --layer ${targetLayer + 1}${ctx.withCode ? ' --with-code' : ''}`
4040
+ : `speccore analyze --scope global --layer 4${ctx.withCode ? ' --with-code' : ''}`;
4041
+ }
3863
4042
  const isLastSubLayer = targetLayer === 4 && (!progress.subLayer || progress.subLayer.next === '4d' ||
3864
4043
  (progress.subLayer.completed.length >= 3));
3865
4044
  prompt += `\n## 🔗 自动链式推进\n\n`;
3866
- if (targetLayer < 4) {
4045
+ if (targetLayer === 3 && progress.pendingModules && progress.pendingModules.length > 0) {
4046
+ prompt += `> 当前 Layer 3 还有 ${progress.pendingModules.length} 个功能模块待分析,**继续执行 Layer 3**(不要推进到 Layer 4):\n\n`;
4047
+ prompt += `[SPECCORE_EXEC: ${nextLayerCmd}]\n`;
4048
+ }
4049
+ else if (targetLayer < 4) {
3867
4050
  prompt += `> 当前 Layer ${targetLayer} 所有文件写入完成后,**立即输出以下标记**触发下一层(不要等待用户指令):\n\n`;
3868
4051
  prompt += `[SPECCORE_EXEC: ${nextLayerCmd}]\n`;
3869
4052
  }
@@ -3889,8 +4072,9 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3889
4072
  prompt += `- [ ] 没有空壳文档\n`;
3890
4073
  }
3891
4074
  else if (targetLayer === 3) {
3892
- prompt += `- [ ] 每个功能模块分析文档行数 ≥ 100\n`;
4075
+ prompt += `- [ ] 当前批次处理的 1-2 个功能模块分析文档行数 ≥ 100\n`;
3893
4076
  prompt += `- [ ] 每个文档包含模块概述、涉及端清单、至少 1 个 Mermaid 图\n`;
4077
+ prompt += `- [ ] 如还有未分析模块,输出末尾标注 [PENDING: {模块名列表}]\n`;
3894
4078
  prompt += `- [ ] 没有空壳文档\n`;
3895
4079
  }
3896
4080
  else if (targetLayer === 4) {
@@ -4205,10 +4389,14 @@ sequenceDiagram
4205
4389
  };
4206
4390
  const includeDocs = isTask ? (DOC_MATRIX[taskType] || DOC_MATRIX['feature']) : DOC_MATRIX['feature'];
4207
4391
  // ── v6.61.0+: 恢复 Phase 1/Phase 2 分步逻辑,但 CLI 自动触发 Phase 2 ──
4208
- // Phase 1: 生成综合文档(overview/REQUIREMENT.md、ANALYSIS.md、DEPS.md 等)
4209
- // Phase 2: 生成各端专属文档({端}/TECH.md、TEST.md、UI_SPEC.md 等)
4210
- const GLOBAL_DOCS = ['REQUIREMENT.md', 'ANALYSIS.md', 'TECH.md', 'RISK.md', 'DEPS.md', 'REVIEW.md', 'MONITOR.md', 'FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'DEV_GUIDE.md'];
4392
+ // v8.3.160+: 改为功能模块优先 — 全局只保留索引文档,每个功能模块有自己的综合文档
4393
+ // Phase 1: 全局索引(FUNCTION_MAP.md/INTERACTION_MAP.md/PLATFORMS.md) + 各功能模块 overview/
4394
+ // Phase 2: 各功能模块的各端专属文档({功能模块}/{端}/TECH.md 等)
4395
+ const GLOBAL_INDEX_DOCS = ['FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'PLATFORMS.md'];
4396
+ const FEATURE_OVERVIEW_DOCS_LIST = ['REQUIREMENT.md', 'ANALYSIS.md', 'TECH.md', 'RISK.md', 'DEPS.md', 'REVIEW.md', 'MONITOR.md', 'DEV_GUIDE.md'];
4211
4397
  const PLATFORM_DOCS = ['TECH.md', 'TEST.md', 'UI_SPEC.md', 'DEV_GUIDE.md'];
4398
+ // 向后兼容:保留 GLOBAL_DOCS 引用
4399
+ const GLOBAL_DOCS = [...GLOBAL_INDEX_DOCS, ...FEATURE_OVERVIEW_DOCS_LIST];
4212
4400
  let taskDocs = docs.filter(([n]) => includeDocs.includes(n));
4213
4401
  if (ctx.phase === '1') {
4214
4402
  taskDocs = taskDocs.filter(([n]) => GLOBAL_DOCS.includes(n));
@@ -4401,6 +4589,10 @@ sequenceDiagram
4401
4589
  techDoc[1] = `# 技术架构(跨端全局)\n\n> ${iter}\n\n## 写作要求\n撰写整体技术架构,覆盖所有端的交互关系:\n- 系统整体分层设计(各端在架构中的位置)\n- 跨端交互协议(前端↔后端通信方式、数据流向)\n- 中间件选型(缓存、消息队列、网关等)\n- 数据库整体设计(核心表结构、ER 关系)\n- 技术栈选型及理由\n`;
4402
4590
  }
4403
4591
  let prompt = `\n# 任务: ${command}${task} (${taskDocs.length}个文档 · ${isTask ? `类型:${taskType}` : '迭代全量'}${ctx.phase ? ` · Phase ${ctx.phase}` : ''})\n\n`;
4592
+ // v8.3.160+: 注入子 Agent 角色定义
4593
+ if (agentContextText) {
4594
+ prompt = agentContextText + '\n\n' + prompt;
4595
+ }
4404
4596
  // v8.3.0+: 如果检测到需求质量不足,在 prompt 开头注入强制澄清阶段
4405
4597
  if (needsClarify && !isGlobal && !isTask && !ctx.phase) {
4406
4598
  const now = new Date().toISOString();
@@ -4568,29 +4760,21 @@ status: "clarified"
4568
4760
  prompt += `- 写完后用 Read 验证文件内容已替换占位内容\n\n`;
4569
4761
  // ── v8.1.0+: 逐文档模式自动注入 PRD 内容 + 前序文档摘要 ──
4570
4762
  // v8.3.2+ 修复:PRD 注入不再仅限逐文档模式,Phase 1/2 模式下也必须注入需求内容
4763
+ // v8.3.160+: 改为路径引用模式(不再全文塞进,让 AI 按需 Read)
4571
4764
  if (!isGlobal && !isTask && ctx.iteration && ctx.iteration !== 'GLOBAL') {
4572
4765
  try {
4573
4766
  const iterDirForCtx = await (0, context_1.getIterationDir)(iter);
4574
4767
  if (iterDirForCtx) {
4575
- // v8.2.0+: 注入 PRD 内容(优先从黄金需求目录 020-specs/requirements/ 读取)
4576
- // v8.3.97+: 对 .md 文件自动展开链接 + 提取图片,确保关联内容被读取
4577
- let prdContent = '';
4578
- const seenPaths = new Set();
4768
+ // v8.3.160+: 路径引用模式 — 列出文档清单,让 AI 按需 Read
4769
+ const docList = [];
4579
4770
  const goldenReqDir = (0, path_1.join)(iterDirForCtx, '020-specs', 'requirements');
4580
4771
  if (await (0, fs_extra_1.pathExists)(goldenReqDir)) {
4581
- // 黄金需求目录存在,只读这里(唯一依据)
4582
4772
  const files = (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readdirSync(goldenReqDir).filter((f) => f.endsWith('.md')).slice(0, 10);
4583
4773
  for (const f of files) {
4584
- const filePath = (0, path_1.join)(goldenReqDir, f);
4585
- let content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile(filePath, 'utf-8');
4586
- content = await (0, prompt_builder_1.processMarkdownContent)(content, filePath, seenPaths, undefined, {
4587
- maxLinkDepth: 2, maxLinkChars: 1200, maxSvgChars: 1500,
4588
- });
4589
- prdContent += `\n### ${f}\n${content.slice(0, 2000)}\n`;
4774
+ docList.push(`020-specs/requirements/${f}`);
4590
4775
  }
4591
4776
  }
4592
4777
  else {
4593
- // 回退:从 010-requirements/ 读取原始需求
4594
4778
  const reqDir = (0, path_1.join)(iterDirForCtx, '010-requirements');
4595
4779
  const prdSources = (0, path_1.join)(reqDir, 'sources');
4596
4780
  const prdConverted = (0, path_1.join)(reqDir, 'converted');
@@ -4599,42 +4783,30 @@ status: "clarified"
4599
4783
  if (await (0, fs_extra_1.pathExists)(dir)) {
4600
4784
  const files = (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readdirSync(dir).filter((f) => f.endsWith('.md')).slice(0, 5);
4601
4785
  for (const f of files) {
4602
- const filePath = (0, path_1.join)(dir, f);
4603
- let content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile(filePath, 'utf-8');
4604
- content = await (0, prompt_builder_1.processMarkdownContent)(content, filePath, seenPaths, undefined, {
4605
- maxLinkDepth: 2, maxLinkChars: 1200, maxSvgChars: 1500,
4606
- });
4607
- prdContent += `\n### ${f}\n${content.slice(0, 2000)}\n`;
4786
+ const relDir = (0, path_1.relative)(iterDirForCtx, dir);
4787
+ docList.push(`${relDir}/${f}`);
4608
4788
  }
4609
4789
  }
4610
4790
  }
4611
4791
  }
4612
- if (prdContent.length > 0) {
4613
- prompt += `\n## 📎 需求文档原文(PRD,写作核心输入)\n\n`;
4614
- const sourceLabel = await (0, fs_extra_1.pathExists)(goldenReqDir) ? '020-specs/requirements/(黄金需求,已澄清)' : '010-requirements/(原始需求)';
4615
- prompt += `> 以下是 ${sourceLabel} 下的需求文档,这是你撰写专业分析文档的核心输入。所有分析必须基于这些内容,不要臆造。\n`;
4616
- prompt += prdContent.slice(0, 5000);
4617
- prompt += `\n\n`;
4792
+ if (docList.length > 0) {
4793
+ prompt += `\n## 📎 需求文档清单(请按需 Read)\n\n`;
4794
+ prompt += `> 以下是你撰写专业分析文档的核心输入。所有分析必须基于这些内容,不要臆造。\n`;
4795
+ prompt += `> 用 Read 工具按需读取,不需要一次性读完所有文档。\n\n`;
4796
+ for (const docPath of docList) {
4797
+ prompt += `- \`${docPath}\`\n`;
4798
+ }
4799
+ prompt += `\n`;
4618
4800
  }
4619
- // 2. 注入前序已填充文档摘要(仅在逐文档模式下)
4801
+ // 2. 前序已填充文档路径(改为路径引用,不再注入摘要)
4620
4802
  if (perDocStatus?.filled.length && perDocStatus.filled.length > 0) {
4621
- const specDir = (0, path_1.join)(iterDirForCtx, '020-specs');
4622
- prompt += `## 📎 前序已填充文档摘要(当前文档必须与之保持一致)\n\n`;
4623
- prompt += `> 以下文档已生成,当前文档必须与之保持一致(字段名、接口路径、状态枚举等不能冲突)。\n\n`;
4803
+ prompt += `## 📎 前序已填充文档(必须与之保持一致)\n\n`;
4804
+ prompt += `> 以下文档已生成,当前文档必须与之保持一致(字段名、接口路径、状态枚举等不能冲突)。\n`;
4805
+ prompt += `> 用 Read 工具读取这些文件,提取关键约束。\n\n`;
4624
4806
  for (const filledPath of perDocStatus.filled) {
4625
- const fullPath = (0, path_1.join)(specDir, filledPath);
4626
- try {
4627
- let content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile(fullPath, 'utf-8');
4628
- if (fullPath.endsWith('.md')) {
4629
- content = await (0, prompt_builder_1.processMarkdownContent)(content, fullPath, seenPaths, undefined, {
4630
- maxLinkDepth: 1, maxLinkChars: 800, maxSvgChars: 1000,
4631
- });
4632
- }
4633
- const summary = content.slice(0, 1500);
4634
- prompt += `### ${filledPath}\n\`\`\`\n${summary}${content.length > 1500 ? '\n... (截断)' : ''}\n\`\`\`\n\n`;
4635
- }
4636
- catch { /* skip unreadable */ }
4807
+ prompt += `- \`020-specs/${filledPath}\`\n`;
4637
4808
  }
4809
+ prompt += `\n`;
4638
4810
  }
4639
4811
  }
4640
4812
  }
@@ -4712,9 +4884,18 @@ status: "clarified"
4712
4884
  prompt += `- Read 020-specs/overview/ANALYSIS.md → 分析报告\n`;
4713
4885
  prompt += `- Read 020-specs/overview/TECH.md → 整体技术架构\n`;
4714
4886
  prompt += `- Read 020-specs/overview/RISK.md、DEPS.md、REVIEW.md、MONITOR.md(如存在)\n\n`;
4715
- prompt += `### Step 2: 为每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
4716
- prompt += `根据全局上下文,为 PLATFORMS.md 中的**每个端**分别撰写以下 **4 份文档**,缺一不可:\n\n`;
4717
- prompt += `**1. {端}/TECH.md — 该端专属技术方案(必须对齐 overview/TECH.md 架构)**\n`;
4887
+ prompt += `### Step 2: 按功能模块组织,为每个功能模块的每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
4888
+ prompt += `先 Read \`020-specs/overview/FUNCTION_MAP.md\` 获取功能模块清单。\n\n`;
4889
+ prompt += `**分批策略(v8.3.160+)**:\n`;
4890
+ prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的所有端文档\n`;
4891
+ prompt += `- 如功能模块数量 > 2:一次只处理 **1-2 个功能模块** 的所有端文档,确保质量深入\n`;
4892
+ prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
4893
+ prompt += `- 续批命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
4894
+ prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(文档充实、有具体代码引用、表格有数据行)\n\n`;
4895
+ prompt += `每个功能模块涉及的每个端,分别撰写以下 **4 份文档**,缺一不可:\n\n`;
4896
+ prompt += `**文件路径格式**:\`{功能模块名}/{端名}/{文件名}\`(如 \`booking/backend/TECH.md\`、\`booking/h5-mobile/UI_SPEC.md\`)\n`;
4897
+ prompt += `> ⚠️ **严禁**只写 \`{端}/{文件}\` 格式(如 \`backend/TECH.md\`),这会导致所有功能模块的文档混在一起无法区分\n\n`;
4898
+ prompt += `**1. {功能模块}/{端}/TECH.md — 该端在该功能模块下的技术方案(必须对齐 overview/TECH.md 架构)**\n`;
4718
4899
  prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
4719
4900
  prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
4720
4901
  prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
@@ -4722,15 +4903,15 @@ status: "clarified"
4722
4903
  prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
4723
4904
  prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
4724
4905
  prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
4725
- prompt += `**2. {端}/TEST.md — 该端专属测试计划**\n`;
4906
+ prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
4726
4907
  prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
4727
4908
  prompt += ` - 边界值和异常输入测试\n`;
4728
4909
  prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
4729
- prompt += `**3. {端}/UI_SPEC.md — 该端专属 UI 规格(仅前端端需要)**\n`;
4910
+ prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
4730
4911
  prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
4731
4912
  prompt += ` - 组件清单和字段→UI 映射\n`;
4732
4913
  prompt += ` - 状态枚举和错误处理策略\n\n`;
4733
- prompt += `**4. {端}/DEV_GUIDE.md — 该端开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
4914
+ prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
4734
4915
  prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
4735
4916
  prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
4736
4917
  prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
@@ -4750,10 +4931,14 @@ status: "clarified"
4750
4931
  prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
4751
4932
  prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
4752
4933
  prompt += `### 写入方式\n`;
4753
- prompt += `**Pipeline 模式**:一次 --apply 写入所有端的文档(推荐)\n`;
4754
- prompt += `speccore analyze --apply '{"TECH.md":"...","TEST.md":"...","UI_SPEC.md":"...","DEV_GUIDE.md":"..."}' -I ${iter} --platform all\n\n`;
4755
- prompt += `**或者逐端写入**(每端一次 --apply):\n`;
4756
- prompt += `speccore analyze --apply '{"TECH.md":"...","TEST.md":"...","UI_SPEC.md":"...","DEV_GUIDE.md":"..."}' -I ${iter} --platform {端名}\n\n`;
4934
+ prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
4935
+ prompt += `\`\`\`bash\n`;
4936
+ prompt += `speccore analyze --apply '{"booking/backend/TECH.md":"...","booking/backend/TEST.md":"...","booking/h5-mobile/TECH.md":"...","booking/h5-mobile/UI_SPEC.md":"..."}' -I ${iter}\n`;
4937
+ prompt += `\`\`\`\n\n`;
4938
+ prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
4939
+ prompt += `\`\`\`bash\n`;
4940
+ prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
4941
+ prompt += `\`\`\`\n\n`;
4757
4942
  prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
4758
4943
  prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
4759
4944
  prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
@@ -4774,25 +4959,31 @@ status: "clarified"
4774
4959
  const tpl = templateMap[doc[0]] || '';
4775
4960
  prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
4776
4961
  }
4777
- prompt += `2. 读取全局层产物(建立全局视角,重要)\n`;
4778
- prompt += ` 在读取迭代需求之前,先 Read 全局层已有产物,了解系统当前状态:\n`;
4779
- prompt += ` a. Read .speccore/GLOBAL/requirements/REQUIREMENT.md → 系统已有功能清单\n`;
4780
- prompt += ` b. Read .speccore/GLOBAL/overview/FUNCTION_MAP.md → 已有功能单元和涉及端\n`;
4781
- prompt += ` c. Read .speccore/GLOBAL/overview/API_CONTRACT.yaml → 已有接口契约\n`;
4782
- prompt += ` d. Read .speccore/GLOBAL/overview/ARCHITECTURE.md → 全局架构(如有)\n`;
4783
- prompt += ` e. Read .speccore/GLOBAL/platforms/{相关端}/_INDEX.md → 各端已有页面和接口索引\n`;
4784
- prompt += ` f. Read .speccore/GLOBAL/platforms/_shared/_ASSOCIATION.md → 前后端关联矩阵(如有)\n`;
4785
- prompt += ` g. Read .speccore/GLOBAL/platforms/_shared/_MODULES.md → 功能模块候选(如有)\n`;
4786
- prompt += ` ⚠️ 如果全局层产物不存在,跳过该项,继续后续分析\n\n`;
4787
- prompt += `3. 读取迭代需求文档(按优先级顺序):\n`;
4788
- prompt += ` a. 先读 010-requirements/INDEX.md — 了解需求全貌和文件清单\n`;
4789
- prompt += ` b. 再读 010-requirements/converted/*.md — doc2spec 转换后的核心规格(主要依据)\n`;
4790
- prompt += ` c. 再读 010-requirements/features/*/README.md — 功能级补充需求\n`;
4791
- prompt += ` d. 读取 010-requirements/prototypes/ — 原型文件(HTML/图片/链接均读取)\n`;
4792
- prompt += ` ⚠️ 需求文档中链接到原型的(如 \`![原型](../prototypes/xxx.png)\` 或 \`详见 prototypes/xxx.html\`),必须主动 Read 该原型文件\n`;
4793
- prompt += ` e. 如用户指定了特定文档,优先读取指定文件;如要求全部,再读 sources/ 原始文档\n`;
4794
- prompt += ` f. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读。不要在非关键章节上花费过多 tokens\n`;
4795
- prompt += `4. 读懂需求文档后,按专业模板标准自由撰写每个文档(不是填空表)\n`;
4962
+ prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
4963
+ prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
4964
+ prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
4965
+ prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
4966
+ prompt += ` - 如不存在则跳过\n\n`;
4967
+ // v8.3.160+: 迭代级分析结合源码上下文
4968
+ if (ctx.withCode) {
4969
+ prompt += `2b. **源码上下文(--with-code)**:\n`;
4970
+ // v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
4971
+ prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
4972
+ prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
4973
+ prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件(从 CONSTITUTION.md 获取源码路径)\n`;
4974
+ prompt += ` - 重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
4975
+ }
4976
+ prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
4977
+ prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
4978
+ prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
4979
+ prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/README.md\n`;
4980
+ prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
4981
+ prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
4982
+ prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
4983
+ prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
4984
+ prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
4985
+ prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
4986
+ prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
4796
4987
  prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
4797
4988
  prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
4798
4989
  prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
@@ -4814,26 +5005,16 @@ status: "clarified"
4814
5005
  prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
4815
5006
  prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
4816
5007
  // v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
4817
- // v6.99.0+: 丰富需求文档章节要求
4818
- prompt += `9. **REQUIREMENT.md 写作风格(重要)**:全局需求文档必须以产品/用户视角撰写\n`;
4819
- prompt += ` - **按业务场景/用户旅程组织章节**,不按端分章节(如"H5端需求"、"后端需求")\n`;
4820
- prompt += ` - 文档结构必须包含(如需求文档中有相关信息):\n`;
4821
- prompt += ` - **产品愿景**:本迭代要解决的核心问题和目标价值(1-2段)\n`;
4822
- prompt += ` - **目标用户画像**:主要用户角色、使用场景、痛点(如有)\n`;
4823
- prompt += ` - **核心场景地图**:按业务流程组织的场景列表,每个场景标注优先级\n`;
4824
- prompt += ` - **功能全景图**:所有功能模块的可视化列表(表格或脑图描述)\n`;
4825
- prompt += ` - **功能优先级矩阵**:P0(必须)/ P1(重要)/ P2(可选)标注\n`;
4826
- prompt += ` - **发布里程碑**:如有分期计划,标注各阶段交付内容\n`;
4827
- prompt += ` - **风险预判**:技术风险、业务风险、依赖风险(如有)\n`;
4828
- prompt += ` - 每个场景描述:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
4829
- prompt += ` - 系统响应中自然包含前后端交互,但不刻意标注技术实现细节\n`;
4830
- prompt += ` - 示例正确写法:「用户选择时间段后点击预订,系统检查会议室可用性,如可用则锁定会议室并创建待支付订单」\n`;
4831
- prompt += ` - 示例错误写法:「后端 booking-service 需要新增 /api/bookings 接口,接收 roomId 参数」\n`;
4832
- prompt += ` - 技术实现细节留在 TECH.md 和各端专属文档中,不在 REQUIREMENT.md 展开\n`;
5008
+ // v8.3.160+: 压缩写作风格说明
5009
+ prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
5010
+ prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
5011
+ prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
5012
+ prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
5013
+ prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
4833
5014
  prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
4834
5015
  // v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
4835
5016
  // v6.71.3+: 增加「与全局层对比」列
4836
- prompt += `10. **功能模块清单(重要)**:写入 overview/REQUIREMENT.md 时,功能模块清单表格必须包含以下列\n`;
5017
+ prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
4837
5018
  prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
4838
5019
  prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
4839
5020
  prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
@@ -4854,12 +5035,12 @@ status: "clarified"
4854
5035
  prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
4855
5036
  // v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
4856
5037
  // v6.71.3+: 增加与全局层关联分析
4857
- prompt += `7a. **迭代需求与全局层关联分析(重要)**:在生成功能模块清单时,必须对比全局层产物\n`;
5038
+ prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
4858
5039
  prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
4859
5040
  prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
4860
5041
  prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
4861
5042
  prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
4862
- prompt += `7b. **跨端功能映射表(重要)**:在 REQUIREMENT.md 完成后,必须生成 overview/FUNCTION_MAP.md\n`;
5043
+ prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
4863
5044
  prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
4864
5045
  prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
4865
5046
  prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
@@ -4872,7 +5053,7 @@ status: "clarified"
4872
5053
  prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
4873
5054
  prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
4874
5055
  // v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
4875
- prompt += `7c. **跨端交互图谱(重要)**:在 FUNCTION_MAP.md 完成后,必须生成 overview/INTERACTION_MAP.md\n`;
5056
+ prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
4876
5057
  prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
4877
5058
  prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
4878
5059
  prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
@@ -4881,43 +5062,40 @@ status: "clarified"
4881
5062
  prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
4882
5063
  prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
4883
5064
  // 注入工程类型信息(v6.49.0+)
5065
+ // v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
4884
5066
  const platformTypes = await (0, spec_paths_1.parsePlatformTypes)();
4885
5067
  if (platformTypes.size > 0) {
4886
- prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请据此生成针对性内容\n`;
5068
+ prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
4887
5069
  prompt += ` | 工程标识 | 工程类型 |\n`;
4888
5070
  prompt += ` | :--- | :--- |\n`;
4889
5071
  for (const [name, type] of platformTypes) {
4890
5072
  prompt += ` | ${name} | ${type} |\n`;
4891
5073
  }
4892
- prompt += `\n **根据工程类型应用对应的专业维度**:\n`;
4893
- prompt += ` - Java服务 → API设计、数据库、缓存、消息队列、安全、性能\n`;
4894
- prompt += ` - Node服务 → API设计、数据库、中间件、异步处理、安全\n`;
4895
- prompt += ` - Go服务 → API设计、数据库、并发、微服务、性能\n`;
4896
- prompt += ` - Python服务 → API设计、数据库、数据分析、AI/ML集成\n`;
4897
- prompt += ` - H5微信公众号 → 微信JS-SDK、OAuth授权、分享、支付、模板消息\n`;
4898
- prompt += ` - H5移动端 → 响应式、viewport适配、触摸交互、弱网优化、首屏性能\n`;
4899
- prompt += ` - Android移动端 → 生命周期、权限、推送、适配、内存优化\n`;
4900
- prompt += ` - iOS移动端 → Swift/SwiftUI、App Store规范、推送、性能\n`;
4901
- prompt += ` - 微信小程序 → 包体积(2MB)、平台API、setData优化、页面栈\n`;
4902
- prompt += ` - Web管理后台 → 复杂表单、数据表格、权限UI、状态管理\n`;
4903
- prompt += ` - 桌面应用 → 本地存储、系统API、自动更新、离线支持\n`;
5074
+ prompt += `\n`;
4904
5075
  }
4905
5076
  const dirStepNum = platformTypes.size > 0 ? 9 : 8;
4906
5077
  prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
4907
- prompt += ` - **综合文档**(跨端通用)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
4908
- prompt += ` - REQUIREMENT.md(需求文档,含功能模块清单+涉及端列)\n`;
4909
- prompt += ` - ANALYSIS.md(需求分析)\n`;
4910
- prompt += ` - DEPS.md(依赖清单)\n`;
4911
- prompt += ` - **端专属文档**(每端各一份)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{端名}/{文件名}\`\n`;
4912
- prompt += ` - TECH.md(技术方案:API/数据库/组件/路由等)\n`;
4913
- prompt += ` - TEST.md(测试用例)\n`;
4914
- prompt += ` - UI_SPEC.md(UI 规范,仅前端端)\n`;
4915
- prompt += ` - RISK.md(风险评估)\n`;
4916
- prompt += ` - REVIEW.md(评审检查项)\n`;
4917
- prompt += ` - MONITOR.md(监控指标)\n`;
5078
+ prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
5079
+ prompt += ` - FUNCTION_MAP.md(功能单元 × 端映射表)\n`;
5080
+ prompt += ` - INTERACTION_MAP.md(跨端交互时序图)\n`;
5081
+ prompt += ` - PLATFORMS.md(端列表)\n`;
5082
+ prompt += ` - **功能模块综合文档**(每个功能模块独立)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/overview/{文件名}\`\n`;
5083
+ prompt += ` - {功能模块}/overview/REQUIREMENT.md(该模块的需求规格)\n`;
5084
+ prompt += ` - {功能模块}/overview/ANALYSIS.md(该模块的需求分析)\n`;
5085
+ prompt += ` - {功能模块}/overview/TECH.md(该模块的技术方案)\n`;
5086
+ prompt += ` - {功能模块}/overview/RISK.md(该模块的风险评估)\n`;
5087
+ prompt += ` - {功能模块}/overview/DEPS.md(该模块的依赖清单)\n`;
5088
+ prompt += ` - {功能模块}/overview/REVIEW.md(该模块的评审检查项)\n`;
5089
+ prompt += ` - {功能模块}/overview/MONITOR.md(该模块的监控指标)\n`;
5090
+ prompt += ` - {功能模块}/overview/DEV_GUIDE.md(该模块的实现指南)\n`;
5091
+ prompt += ` - **端专属文档**(按功能模块组织)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/{端名}/{文件名}\`\n`;
5092
+ prompt += ` - {功能模块}/{端}/TECH.md(该模块该端的技术方案)\n`;
5093
+ prompt += ` - {功能模块}/{端}/TEST.md(该模块该端的测试用例)\n`;
5094
+ prompt += ` - {功能模块}/{端}/UI_SPEC.md(该模块该端的 UI 规范,仅前端端)\n`;
5095
+ prompt += ` - {功能模块}/{端}/DEV_GUIDE.md(该模块该端的实现指南)\n`;
4918
5096
  prompt += ` - **禁止**:不要创建 020-specs/ 下的任何额外子目录(如数字编号、中文名称等)\n`;
4919
5097
  prompt += ` - **禁止直接用 Write 工具写文件到 020-specs/**:必须通过 \`speccore analyze --apply '{"文件名":"内容"}' -I ${iter}\` 写入\n`;
4920
- prompt += ` - ⚠️ 直接 Write 会导致目录结构错误(所有文件扁平在根目录),必须走 --apply 让 CLI 自动路由到 overview/ 或 {端名}/ 子目录\n`;
5098
+ prompt += ` - ⚠️ 直接 Write 会导致目录结构错误,必须走 --apply 让 CLI 自动路由到正确子目录\n`;
4921
5099
  if (ctx.phase !== '1') {
4922
5100
  // 端专业性约束只在默认模式(全量)中输出
4923
5101
  prompt += `\n## ⚠️ 端专业性约束\n`;
@@ -4931,7 +5109,7 @@ status: "clarified"
4931
5109
  prompt += `- 缓存策略/并发与事务/消息队列(如涉及)\n`;
4932
5110
  prompt += `- 安全:SQL 注入防护/接口鉴权/数据脱敏\n`;
4933
5111
  prompt += `- 性能:QPS 预估/慢查询优化/连接池配置\n`;
4934
- prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 overview/REQUIREMENT.md 中)\n\n`;
5112
+ prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 {功能模块}/overview/REQUIREMENT.md 中)\n\n`;
4935
5113
  prompt += `### 前端端(h5 / admin-web / miniapp)必含内容 — 产品+技术双视角\n`;
4936
5114
  prompt += `- **产品视角(主要)**:\n`;
4937
5115
  prompt += ` - 用户旅程:该端用户如何完成核心任务(步骤流程图)\n`;
@@ -4949,24 +5127,26 @@ status: "clarified"
4949
5127
  // v6.60.0+: 文档与端的对应关系(不再分 Phase)
4950
5128
  // v6.71.0+: 前后端文档差异化
4951
5129
  prompt += `### 文档与端的对应关系\n`;
4952
- prompt += `- **overview/REQUIREMENT.md**:整体需求(产品视角,按业务场景组织)\n`;
4953
- prompt += `- **overview/ANALYSIS.md**:整体需求分析\n`;
4954
- prompt += `- **overview/DEPS.md**:整体依赖清单\n`;
4955
- prompt += `- **overview/FUNCTION_MAP.md**:功能单元 × 端映射表\n`;
4956
- prompt += `- **overview/INTERACTION_MAP.md**:跨端交互时序图\n`;
4957
- prompt += `- **overview/DEV_GUIDE.md**:开发者实现指南(实现步骤、关键代码示例、集成指南、注意事项)\n`;
4958
- prompt += `- **后端端(*service)/{端}/TECH.md**:纯技术视角 — 接口设计+数据模型+架构+性能\n`;
4959
- prompt += `- **前端端(h5/admin/miniapp)/{端}/TECH.md**:产品+技术双视角 — 用户旅程+页面清单+交互流程+API调用链\n`;
4960
- prompt += `- **前端端/{端}/UI_SPEC.md**:UI 规格(字段映射、组件设计、交互细节)\n`;
5130
+ prompt += `- **overview/FUNCTION_MAP.md**:全局索引 — 所有功能单元 × 端映射表\n`;
5131
+ prompt += `- **overview/INTERACTION_MAP.md**:全局索引 — 所有跨端交互时序图\n`;
5132
+ prompt += `- **overview/PLATFORMS.md**:全局索引 — 端列表\n`;
5133
+ prompt += `- **{功能模块}/overview/REQUIREMENT.md**:该功能模块的需求规格(产品视角,按业务场景组织)\n`;
5134
+ prompt += `- **{功能模块}/overview/ANALYSIS.md**:该功能模块的需求分析\n`;
5135
+ prompt += `- **{功能模块}/overview/DEPS.md**:该功能模块的依赖清单\n`;
5136
+ prompt += `- **{功能模块}/overview/DEV_GUIDE.md**:该功能模块的开发者实现指南\n`;
5137
+ prompt += `- **{功能模块}/overview/RISK.md**:该功能模块的风险评估\n`;
5138
+ prompt += `- **{功能模块}/overview/REVIEW.md**:该功能模块的评审检查项\n`;
5139
+ prompt += `- **{功能模块}/overview/MONITOR.md**:该功能模块的监控指标\n`;
5140
+ prompt += `- **后端端(*service)/{功能模块}/{端}/TECH.md**:纯技术视角 — 接口设计+数据模型+架构+性能\n`;
5141
+ prompt += `- **前端端(h5/admin/miniapp)/{功能模块}/{端}/TECH.md**:产品+技术双视角 — 用户旅程+页面清单+交互流程+API调用链\n`;
5142
+ prompt += `- **前端端/{功能模块}/{端}/UI_SPEC.md**:UI 规格(字段映射、组件设计、交互细节)\n`;
4961
5143
  prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加一个表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
4962
5144
  prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
4963
5145
  prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
4964
5146
  prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n`;
4965
- prompt += `- **{端}/TEST.md**:该端专属测试计划\n`;
4966
- prompt += `- **{端}/RISK.md**:该端专属风险评估\n`;
4967
- prompt += `- **{端}/REVIEW.md**:该端专属评审检查项\n`;
4968
- prompt += `- **{端}/MONITOR.md**:该端专属监控指标\n`;
4969
- prompt += `- **{端}/UI_SPEC.md**:前端端专属 UI 规格,字段映射必须与后端 API 响应字段一一对应\n`;
5147
+ prompt += `- **{功能模块}/{端}/TEST.md**:该功能模块下该端的测试计划\n`;
5148
+ prompt += `- **{功能模块}/{端}/DEV_GUIDE.md**:该功能模块下该端的实现指南\n`;
5149
+ prompt += `- **{功能模块}/{端}/UI_SPEC.md**:前端端专属 UI 规格,字段映射必须与后端 API 响应字段一一对应\n`;
4970
5150
  prompt += `- 分析完成后会自动生成 QUALITY_AUDIT.md 质量报告,检查各端内容是否完整\n`;
4971
5151
  }
4972
5152
  // 步骤 2-7 已在上面的 phase 分支中处理
@@ -5431,6 +5611,17 @@ async function buildClarifyPhasePrompt(iteration) {
5431
5611
  prompt += `3. \`010-requirements/converted/*.md\` — 已转换文档\n`;
5432
5612
  prompt += `4. \`010-requirements/features/*/README.md\` — 功能级补充\n`;
5433
5613
  prompt += `5. \`010-requirements/prototypes/\` — 原型文件\n\n`;
5614
+ // v8.3.160+ 修复:列出实际扫描到的源文档清单,明确每份文档独立澄清
5615
+ if (docPaths.length > 0) {
5616
+ prompt += `## 源文档清单(本次需澄清的原始文档)\n\n`;
5617
+ prompt += `共扫描到 ${docPaths.length} 份需求文档,**每份文档必须独立澄清**,分别生成对应的黄金需求文档:\n\n`;
5618
+ for (const p of docPaths) {
5619
+ const name = p.split('/').pop() || '-';
5620
+ const clarifiedName = name.replace(/\.md$/, '') + '-clarified.md';
5621
+ prompt += `- \`${name}\` → 澄清为 \`${clarifiedName}\`\n`;
5622
+ }
5623
+ prompt += `\n> ⚠️ **重要**:每份源文档对应一份独立的澄清文档,禁止将多份源文档合并为一份。\n\n`;
5624
+ }
5434
5625
  // v8.2.0+: 如果检测到多个功能单元,启用单元化澄清模式
5435
5626
  if (hasMultipleUnits) {
5436
5627
  prompt += `## ⚠️ 功能单元拆分澄清模式(v8.2.0+)\n\n`;
@@ -5546,9 +5737,40 @@ async function buildClarifyPhasePrompt(iteration) {
5546
5737
  prompt += ` - 记录取舍:每轮迭代记录「采纳/拒绝/待确认」的决策\n\n`;
5547
5738
  prompt += `## Step 5: 确认写入\n\n`;
5548
5739
  prompt += `当用户确认 "满意,可以写入" 后:\n\n`;
5549
- prompt += `1. 将最终版 PRD 写入 \`020-specs/requirements/{源文件名}-clarified.md\`(黄金需求目录)\n`;
5550
- prompt += `2. 同时生成 \`020-specs/requirements/{源文件名}-clarified-diff.md\` 保存最终对比报告\n`;
5551
- prompt += `3. 使用以下命令写入:\n\n`;
5740
+ prompt += `**必须为每份源文档分别生成独立的澄清文档**,使用 \`[CLARIFY:requirements/文件名-clarified.md]\` 标记分隔:\n\n`;
5741
+ if (docPaths.length > 1) {
5742
+ prompt += `本次共 ${docPaths.length} 份源文档,需要输出 ${docPaths.length} 个 \`[CLARIFY:...]\` 标记块:\n\n`;
5743
+ for (const p of docPaths) {
5744
+ const name = p.split('/').pop() || '-';
5745
+ const baseName = name.replace(/\.md$/, '');
5746
+ prompt += `\`\`\`\n`;
5747
+ prompt += `[CLARIFY:requirements/${baseName}-clarified.md]\n`;
5748
+ prompt += `---\n`;
5749
+ prompt += `source: "010-requirements/.../${name}"\n`;
5750
+ prompt += `clarified-at: "${new Date().toISOString()}"\n`;
5751
+ prompt += `status: "clarified"\n`;
5752
+ prompt += `---\n\n`;
5753
+ prompt += `# ${baseName}(澄清后的专业 PRD)\n`;
5754
+ prompt += `...\n`;
5755
+ prompt += `\`\`\`\n\n`;
5756
+ }
5757
+ }
5758
+ else {
5759
+ prompt += `\`\`\`\n`;
5760
+ prompt += `[CLARIFY:requirements/xxx-clarified.md]\n`;
5761
+ prompt += `---\n`;
5762
+ prompt += `source: "原始文档路径"\n`;
5763
+ prompt += `clarified-at: "${new Date().toISOString()}"\n`;
5764
+ prompt += `status: "clarified"\n`;
5765
+ prompt += `---\n\n`;
5766
+ prompt += `# 功能名称\n`;
5767
+ prompt += `...\n`;
5768
+ prompt += `\`\`\`\n\n`;
5769
+ }
5770
+ prompt += `> ⚠️ **禁止合并**:每份源文档必须对应一份独立的 \`-clarified.md\`,即使内容有关联也要分开输出。\n\n`;
5771
+ prompt += `同时生成对应的对比报告文件(可选):\n`;
5772
+ prompt += `- \`020-specs/requirements/{源文件名}-clarified-diff.md\` — 保存最终对比报告\n\n`;
5773
+ prompt += `使用以下命令写入:\n\n`;
5552
5774
  prompt += `\`\`\`bash\n`;
5553
5775
  prompt += `speccore analyze --apply '{"020-specs/requirements/xxx-clarified.md":"...","020-specs/requirements/xxx-clarified-diff.md":"..."}' -I ${iteration}\n`;
5554
5776
  prompt += `\`\`\`\n\n`;
@@ -5603,7 +5825,7 @@ async function buildConfirmCheckPrompt(iteration) {
5603
5825
  prompt += `\`\`\`bash\n`;
5604
5826
  prompt += `speccore analyze --prompt -I ${iteration} --pipeline --skip-clarify=false\n`;
5605
5827
  prompt += `\`\`\`\n`;
5606
- prompt += `> 重新进入 clarify-prompt 阶段,AI 会基于已有 draft 继续迭代。\n\n`;
5828
+ prompt += `> 重新进入 clarify-product 阶段,AI 会基于已有 draft 继续迭代。\n\n`;
5607
5829
  prompt += `**选项 C:跳过确认,直接进入分析(风险自负)**\n`;
5608
5830
  prompt += `\`\`\`bash\n`;
5609
5831
  prompt += `speccore analyze --prompt -I ${iteration} --pipeline --skip-clarify\n`;