speccore 8.3.253 → 8.3.255

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.
@@ -62,7 +62,6 @@ const task_utils_1 = require("../utils/task-utils");
62
62
  const cleanup_1 = require("./cleanup");
63
63
  const logger_1 = require("../utils/logger");
64
64
  const context_1 = require("../core/context");
65
- const task_paths_1 = require("../core/task-paths");
66
65
  const feature_source_1 = require("../core/feature-source");
67
66
  const analyze_engine_1 = require("../core/analyze-engine");
68
67
  const fs_extra_2 = require("fs-extra");
@@ -319,6 +318,10 @@ async function tryLlmGenerate(prompt, iter) {
319
318
  return false;
320
319
  }
321
320
  async function analyzeCommand(options) {
321
+ // v8.3.255+: 全局层分析已独立为 speccore global 命令,旧入口保留一版过渡
322
+ if (options.scope === 'global' && !options._fromGlobal) {
323
+ logger_1.logger.warn('⚠️ analyze --scope global 已废弃,请改用: speccore global analyze');
324
+ }
322
325
  // v6.97.0+ 修复:全局分析时统一设置 iteration 为 'GLOBAL',避免任何 fallback 到 getDefaultIteration()
323
326
  // 这是根治方案:后面所有 "options.iteration || await getDefaultIteration()" 都会命中 options.iteration
324
327
  // v8.3.102+: 向上查找项目根目录(支持在子目录执行)
@@ -1331,8 +1334,8 @@ async function analyzeCommand(options) {
1331
1334
  catch { /* 默认 host */ }
1332
1335
  // v8.3.225+: 方式 Y 调度循环 — SDK/llm 成功落盘后进程内 advance 继续下一步,直到 Pipeline 完成;
1333
1336
  // host 模式输出自包含 prompt 接力(exitCode 10,宿主会话内 --apply 后继续调用本命令即可断点续跑)
1334
- const scopeFlag = isGlobalScope ? '--scope global' : `-I ${iter}`;
1335
- const resumeCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
1337
+ const scopeFlag = isGlobalScope ? '' : `-I ${iter}`;
1338
+ const resumeCmd = isGlobalScope ? 'speccore global analyze --prompt --pipeline' : `speccore analyze --prompt --pipeline ${scopeFlag}`;
1336
1339
  const maxRounds = steps.length * 2 + 2; // 防异常死循环保护
1337
1340
  let round = 0;
1338
1341
  while (round < maxRounds) {
@@ -1486,31 +1489,9 @@ async function analyzeCommand(options) {
1486
1489
  // 跳过澄清(off 或 auto 判定 high):仍生成质量报告,供后续修正参考
1487
1490
  await generateQualityReport(iter);
1488
1491
  }
1489
- // v8.3.94+: 多子任务分析支持
1490
- const targetTasks = await resolveTargetTasks(options);
1491
- if (targetTasks.length > 1) {
1492
- logger_1.logger.info(`🔄 批量分析 ${targetTasks.length} 个子任务: ${targetTasks.join(', ')}`);
1493
- const prompts = [];
1494
- for (let i = 0; i < targetTasks.length; i++) {
1495
- const taskId = targetTasks[i];
1496
- const singlePrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
1497
- iteration: iter, task: taskId, platform: options.platform,
1498
- analyzeOptions: options,
1499
- }));
1500
- prompts.push(`<!-- ═══════════════════════════════════════════ -->
1501
- <!-- 任务 ${i + 1}/${targetTasks.length}: ${taskId} -->
1502
- <!-- ═══════════════════════════════════════════ -->
1503
- ${singlePrompt}`);
1504
- }
1505
- const mergedPrompt = `# 批量分析 ${targetTasks.length} 个子任务\n\n请分别为以下每个任务生成分析文档。每个任务之间用分隔线隔开。\n\n${prompts.join('\n\n---\n\n')}`;
1506
- process.stdout.write(`[SPECCORE_PROMPT]\n${mergedPrompt}`);
1507
- process.exitCode = 10;
1508
- return;
1509
- }
1510
1492
  // v8.3.160+: 统一使用 buildPrompt 入口
1511
1493
  const promptObj = await (0, prompt_builder_1.buildPrompt)('analyze', {
1512
1494
  iteration: iter,
1513
- task: options.task,
1514
1495
  platform: options.platform,
1515
1496
  analyzeOptions: options,
1516
1497
  });
@@ -1549,33 +1530,11 @@ ${singlePrompt}`);
1549
1530
  return;
1550
1531
  }
1551
1532
  const iterDir = isGlobalScope ? undefined : await (0, context_1.getIterationDir)(options.iteration);
1552
- // v8.3.94+: 多子任务 apply 检测
1553
- if (options.task && options.task.includes(',')) {
1554
- logger_1.logger.error('❌ --apply 模式下暂不支持多任务批量写入');
1555
- logger_1.logger.info(' 请逐个任务执行 apply:');
1556
- const taskIds = parseTaskIds(options.task);
1557
- for (const taskId of taskIds) {
1558
- logger_1.logger.info(` speccore analyze --apply '<JSON>' -I ${options.iteration} --task ${taskId}`);
1559
- }
1560
- return;
1561
- }
1562
- const isTaskLevel = !isGlobalScope && !!options.task;
1563
- let taskDir = null;
1564
- if (isTaskLevel) {
1565
- const taskId = options.task.startsWith('Task-') ? options.task : `Task-${options.task}`;
1566
- taskDir = await (0, task_paths_1.findTaskDir)((0, path_1.join)(iterDir, '030-tasks'), taskId);
1567
- if (!taskDir) {
1568
- logger_1.logger.error(`未找到任务: ${taskId}`);
1569
- return;
1570
- }
1571
- }
1572
1533
  // v8.3.237+: Pipeline 推进逻辑抽取为共享函数——JSON / [DOC:] 多文档通道写入后
1573
1534
  // 提前 return,此前会跳过尾部的 Pipeline 推进(bug: 只有单文件模式能推进),
1574
1535
  // 现将「RAG 刷新 + Pipeline 推进」抽为共享函数,三个写入通道成功后都调用。
1575
1536
  // 返回 true 表示存在活跃 Pipeline 并已处理(调用点应直接 return,不再输出后续提示)。
1576
1537
  const advancePipelineAfterApply = async () => {
1577
- if (isTaskLevel)
1578
- return false;
1579
1538
  // v8.3.238+: 步骤完成检查——计算当前步骤尚未填充的骨架清单(相对 020-specs/ 的路径)。
1580
1539
  // 单文档模式一次 --apply 通常只写 1~2 个文档,但一个步骤(如 phase1-prompt)有多个
1581
1540
  // 骨架待填充;此前写 1 个就推进,剩余骨架会错位到下一步骤。
@@ -1700,8 +1659,8 @@ ${singlePrompt}`);
1700
1659
  stepIds: stateBefore.steps || [stateBefore.currentStep],
1701
1660
  options,
1702
1661
  });
1703
- const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
1704
- const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
1662
+ const scopeFlag = isGlobalScope ? '' : `-I ${options.iteration}`;
1663
+ const nextCmd = isGlobalScope ? 'speccore global analyze --prompt --pipeline' : `speccore analyze --prompt --pipeline ${scopeFlag}`;
1705
1664
  const output = [
1706
1665
  `[SPECCORE_NEXT_STEP]`,
1707
1666
  `当前步骤 "${stateBefore.currentStep}" 尚未完成,请继续填充剩余骨架后再次 --apply。`,
@@ -1742,8 +1701,8 @@ ${singlePrompt}`);
1742
1701
  options,
1743
1702
  });
1744
1703
  // v8.3.225+: 方式 Y 输出标记 — 宿主会话内继续执行即可,不再强制新对话
1745
- const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
1746
- const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
1704
+ const scopeFlag = isGlobalScope ? '' : `-I ${options.iteration}`;
1705
+ const nextCmd = isGlobalScope ? 'speccore global analyze --prompt --pipeline' : `speccore analyze --prompt --pipeline ${scopeFlag}`;
1747
1706
  const output = [
1748
1707
  `[SPECCORE_STEP_DONE]`,
1749
1708
  `步骤 "${state.currentStep}" 已完成。`,
@@ -1809,7 +1768,7 @@ ${singlePrompt}`);
1809
1768
  logger_1.logger.info('');
1810
1769
  }
1811
1770
  // v8.3.190+: 需求澄清验证 — off 跳过门禁;auto/on 下质量不足且未澄清则拒绝写入
1812
- if (!isGlobalScope && !isTaskLevel && clarifyMode !== 'off') {
1771
+ if (!isGlobalScope && clarifyMode !== 'off') {
1813
1772
  const { hasValidClarifiedDocs } = await Promise.resolve().then(() => __importStar(require('../core/requirement-clarifier')));
1814
1773
  const hasClarifiedDocs = await hasValidClarifiedDocs(iterDir);
1815
1774
  if (!hasClarifiedDocs && clarifyBlocks.size === 0) {
@@ -1883,29 +1842,7 @@ ${singlePrompt}`);
1883
1842
  }
1884
1843
  }
1885
1844
  let count = 0;
1886
- if (isTaskLevel && taskDir) {
1887
- // 任务级:写 Task/00-specs/(v6.44.0+ 统一写入 00-specs/)
1888
- const targetSubDir = options.platform && !isTaskLevel ? options.platform : '00-specs';
1889
- const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
1890
- await (0, fs_extra_1.ensureDir)(taskSpecDir);
1891
- for (const [filename, content] of Object.entries(docs)) {
1892
- const fp = (0, path_1.join)(taskSpecDir, filename);
1893
- if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
1894
- logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
1895
- continue;
1896
- }
1897
- const bk = await (0, task_utils_1.backupWithTimestamp)(fp);
1898
- if (bk) {
1899
- backups.push(bk);
1900
- logger_1.logger.info(` 📦 ${filename} 已备份: ${(0, path_1.basename)(bk)}`);
1901
- }
1902
- await (0, fs_extra_1.writeFile)(fp, content);
1903
- count++;
1904
- }
1905
- const platformLabel = options.platform ? `/${options.platform}` : '';
1906
- logger_1.logger.success(`✅ ${count} 个 Spec 文档已写入 ${options.task}${platformLabel}/(任务级,迭代基线不变)`);
1907
- }
1908
- else if (isGlobalScope) {
1845
+ if (isGlobalScope) {
1909
1846
  // 全局级:写入 .speccore/GLOBAL/(与 platforms/ 同级,非迭代目录)
1910
1847
  const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
1911
1848
  await (0, fs_extra_1.ensureDir)(globalBaseDir);
@@ -1989,14 +1926,14 @@ ${singlePrompt}`);
1989
1926
  logger_1.logger.info(`\n💡 AI 请按以下步骤修复:`);
1990
1927
  logger_1.logger.info(` 1. 读取上述问题文档的当前内容`);
1991
1928
  logger_1.logger.info(` 2. 根据质量报告中的错误信息补充/修正内容`);
1992
- logger_1.logger.info(` 3. 用 speccore analyze --apply '{"文件路径":"修正后内容"}' --scope global 重新写入`);
1929
+ logger_1.logger.info(` 3. 用 speccore global analyze --apply '{"文件路径":"修正后内容"}' 重新写入`);
1993
1930
  process.stdout.write(`\n[SPECCORE_PROMPT]\n# 质量门禁修复任务\n\n以下全局分析文档未通过质量校验,请修复后重新写入:\n\n`);
1994
1931
  for (const r of criticalErrors.slice(0, 5)) {
1995
1932
  const fname = r.file.split('/').slice(-2).join('/');
1996
1933
  const errs = r.issues.filter(i => i.severity === 'error').map(i => `- ${i.message}`).join('\n');
1997
1934
  process.stdout.write(`## ${fname}\n${errs}\n\n`);
1998
1935
  }
1999
- process.stdout.write(`修复完成后重新执行:speccore analyze --scope global --layer 4\n`);
1936
+ process.stdout.write(`修复完成后重新执行:speccore global analyze --layer 4\n`);
2000
1937
  process.exitCode = 10;
2001
1938
  return;
2002
1939
  }
@@ -2384,28 +2321,7 @@ ${singlePrompt}`);
2384
2321
  try {
2385
2322
  const docs = JSON.parse(options.apply);
2386
2323
  let count = 0;
2387
- if (isTaskLevel && taskDir) {
2388
- const targetSubDir = options.platform && !isTaskLevel ? options.platform : '00-specs';
2389
- const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
2390
- await (0, fs_extra_1.ensureDir)(taskSpecDir);
2391
- for (const [filename, content] of Object.entries(docs)) {
2392
- const fp = (0, path_1.join)(taskSpecDir, filename);
2393
- if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
2394
- logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
2395
- continue;
2396
- }
2397
- const bk = await (0, task_utils_1.backupWithTimestamp)(fp);
2398
- if (bk) {
2399
- backups.push(bk);
2400
- logger_1.logger.info(` 📦 ${filename} 已备份: ${(0, path_1.basename)(bk)}`);
2401
- }
2402
- await (0, fs_extra_1.writeFile)(fp, content);
2403
- count++;
2404
- }
2405
- const platformLabel = options.platform ? `/${options.platform}` : '';
2406
- logger_1.logger.success(`✅ ${count} 个 Spec 文档已写入 ${options.task}${platformLabel}/(任务级,迭代基线不变)`);
2407
- }
2408
- else if (isGlobalScope) {
2324
+ if (isGlobalScope) {
2409
2325
  const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
2410
2326
  await (0, fs_extra_1.ensureDir)(globalBaseDir);
2411
2327
  const globalSet = new Set(spec_paths_2.GLOBAL_SPEC_FILES);
@@ -2613,27 +2529,7 @@ ${singlePrompt}`);
2613
2529
  // 注意:不能提前 return,否则会跳过后续的 Pipeline 推进逻辑(--apply 靠它推进 confirm-check)
2614
2530
  const hasClarifyMarkers = /\[CLARIFY:[^\]]+\]/.test(options.apply);
2615
2531
  // 单文件模式
2616
- if (!hasClarifyMarkers && isTaskLevel && taskDir) {
2617
- // 任务级:写 Task/00-specs/(v6.44.0+)
2618
- const targetSubDir = '00-specs';
2619
- const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
2620
- await (0, fs_extra_1.ensureDir)(taskSpecDir);
2621
- const taskAnalysisPath = (0, path_1.join)(taskSpecDir, 'ANALYSIS.md');
2622
- if (await (0, task_utils_1.shouldOverwrite)(taskAnalysisPath, !!options.interactive)) {
2623
- const taskBackup = await (0, task_utils_1.backupWithTimestamp)(taskAnalysisPath);
2624
- if (taskBackup) {
2625
- backups.push(taskBackup);
2626
- logger_1.logger.info(` 📦 已备份: ${taskBackup.split('/').pop()}`);
2627
- }
2628
- await (0, fs_extra_1.writeFile)(taskAnalysisPath, options.apply);
2629
- const platformLabel = options.platform ? `/${options.platform}` : '';
2630
- logger_1.logger.success(`✅ ANALYSIS.md 已写入 ${options.task}${platformLabel}/`);
2631
- }
2632
- else {
2633
- logger_1.logger.info(` ⏭️ 用户取消覆盖`);
2634
- }
2635
- }
2636
- else if (!hasClarifyMarkers && isGlobalScope) {
2532
+ if (!hasClarifyMarkers && isGlobalScope) {
2637
2533
  // 全局级:写 .speccore/GLOBAL/ANALYSIS.md
2638
2534
  const globalDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
2639
2535
  await (0, fs_extra_1.ensureDir)(globalDir);
@@ -2670,26 +2566,6 @@ ${singlePrompt}`);
2670
2566
  logger_1.logger.info(` ⏭️ 用户取消覆盖`);
2671
2567
  }
2672
2568
  }
2673
- // ── --sync: 任务分析后局部回写 020-specs/ ──
2674
- if (options.sync && isTaskLevel && taskDir) {
2675
- logger_1.logger.info('');
2676
- logger_1.logger.info('🔄 局部回写: 将任务分析结果同步到 020-specs/ ...');
2677
- try {
2678
- const { syncTaskToSpecs } = await Promise.resolve().then(() => __importStar(require('../core/spec-merger')));
2679
- const iterDirPath = iterDir;
2680
- const taskName = options.task || '';
2681
- const mergeResult = await syncTaskToSpecs(iterDirPath, taskDir, taskName);
2682
- if (mergeResult.filesUpdated > 0) {
2683
- logger_1.logger.success(`✅ 局部回写完成: ${mergeResult.filesUpdated} 个 spec 文件已更新`);
2684
- }
2685
- else {
2686
- logger_1.logger.info(' ℹ️ 未找到匹配的 spec 文件,无需回写');
2687
- }
2688
- }
2689
- catch (e) {
2690
- logger_1.logger.debug('局部回写失败(非关键):', e);
2691
- }
2692
- }
2693
2569
  printBackupSummary();
2694
2570
  // v8.3.237+: RAG 刷新 + Pipeline 推进已抽取为 advancePipelineAfterApply(见 apply 分支开头),
2695
2571
  // 三个写入通道(JSON / [DOC:] / 单文件模式)统一调用,避免多文档通道提前 return 跳过推进。
@@ -2697,7 +2573,7 @@ ${singlePrompt}`);
2697
2573
  if (pipelineHandled)
2698
2574
  return;
2699
2575
  // ── v6.64.0+: Phase 1 完成后自动触发 Phase 2(仅多端项目,非 Pipeline 模式)──
2700
- if (!options.phase && !isTaskLevel && options.iteration) {
2576
+ if (!options.phase && options.iteration) {
2701
2577
  // 检查是否有多个端
2702
2578
  const platforms = await (0, spec_paths_2.parsePlatformList)();
2703
2579
  // 只有多端项目(≥2 个端)才需要分两阶段:
@@ -2735,8 +2611,8 @@ ${singlePrompt}`);
2735
2611
  const progress = await detectGlobalLayerProgress();
2736
2612
  if (progress.completedLayer < 4) {
2737
2613
  const nextCmd = progress.nextLayer === 4 && progress.subLayer
2738
- ? `speccore analyze --scope global --layer 4${options.withCode ? ' --with-code' : ''}`
2739
- : `speccore analyze --scope global --layer ${progress.nextLayer}${options.withCode ? ' --with-code' : ''}`;
2614
+ ? `speccore global analyze --layer 4${options.withCode ? ' --with-code' : ''}`
2615
+ : `speccore global analyze --layer ${progress.nextLayer}${options.withCode ? ' --with-code' : ''}`;
2740
2616
  process.stdout.write(`\n[SPECCORE_EXEC: ${nextCmd}]\n`);
2741
2617
  process.stdout.write(`> 全局分析 ${progress.completedLayer}/4 层完成,自动推进到下一层...\n`);
2742
2618
  process.exitCode = 10;
@@ -2767,7 +2643,7 @@ ${singlePrompt}`);
2767
2643
  }
2768
2644
  catch { /* ignore */ }
2769
2645
  }
2770
- 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, needsClarify ? { needsClarify, clarifyTargets } : undefined);
2646
+ const prompt = await buildMultiDocPrompt('analyze', { iteration: iter, scope: options.scope, withCode: options.withCode, platform: options.platform, phase: options.phase, autoMode: options.auto }, options, needsClarify ? { needsClarify, clarifyTargets } : undefined);
2771
2647
  await (0, ai_trace_1.recordAiTrace)(iter, 'prompt-out', { mode: 'analyze-main', scope: options.scope, promptLength: prompt.length, prompt });
2772
2648
  process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
2773
2649
  // v7.2.0+: 全局分析完成后输出下一步引导
@@ -2953,154 +2829,6 @@ async function generateIterationSpecDocs(iteration) {
2953
2829
  }
2954
2830
  logger_1.logger.info(`\n📄 Spec 文档: 新建 ${created} 个, 跳过 ${skipped} 个 (已存在) → ${specDir}/ (global/ + 根目录)`);
2955
2831
  }
2956
- /**
2957
- * 任务级文档补全 (原 perTaskAnalyze 逻辑)
2958
- */
2959
- async function enrichTaskDocs(iteration, taskId, reqFiles) {
2960
- const { readdirSync } = require('fs');
2961
- const iterDir = await (0, context_1.getIterationDir)(iteration);
2962
- if (!(await (0, fs_extra_1.pathExists)(iterDir)))
2963
- return;
2964
- const entries = readdirSync(iterDir, { withFileTypes: true });
2965
- const taskEntry = entries.find((e) => e.isDirectory() && e.name.startsWith(taskId));
2966
- // enrichTaskDocs continues, but taskEntry logic remains...
2967
- if (!taskEntry)
2968
- return;
2969
- if (!taskEntry) {
2970
- logger_1.logger.info(` ℹ️ 未找到任务目录 ${taskId},跳过文档补全`);
2971
- return;
2972
- }
2973
- const fullTaskDir = (0, path_1.join)(iterDir, taskEntry.name);
2974
- // _shared/ → 00-specs/
2975
- const specsDir = (await (0, fs_extra_1.pathExists)((0, path_1.join)(fullTaskDir, '_shared')))
2976
- ? (0, path_1.join)(fullTaskDir, '_shared')
2977
- : (0, path_1.join)(fullTaskDir, '00-specs');
2978
- if (!(await (0, fs_extra_1.pathExists)(specsDir)))
2979
- return;
2980
- let reqContent = '';
2981
- // 读取任务 REQ 或传入的需求文件
2982
- const taskReqPath = (0, path_1.join)(specsDir, 'REQ.md');
2983
- if (await (0, fs_extra_1.pathExists)(taskReqPath)) {
2984
- reqContent = await require('fs-extra').readFile(taskReqPath, 'utf-8');
2985
- }
2986
- else if (reqFiles.length > 0) {
2987
- for (const f of reqFiles) {
2988
- if (await (0, fs_extra_1.pathExists)(f))
2989
- reqContent += await require('fs-extra').readFile(f, 'utf-8') + '\n';
2990
- }
2991
- }
2992
- if (!reqContent)
2993
- return;
2994
- // 补全 TECH.md
2995
- const techPath = (0, path_1.join)(specsDir, 'TECH.md');
2996
- let techContent = '';
2997
- if (await (0, fs_extra_1.pathExists)(techPath)) {
2998
- techContent = await require('fs-extra').readFile(techPath, 'utf-8');
2999
- if (!techContent.includes('## 分析建议')) {
3000
- const items = [];
3001
- const apis = (reqContent.match(/\/api\/[a-zA-Z0-9\/-]+/g) || []).map((a) => a.trim());
3002
- if (apis.length > 0) {
3003
- items.push(`检测到 ${apis.length} 个 API:`);
3004
- for (const api of [...new Set(apis)])
3005
- items.push(` \`${api}\``);
3006
- }
3007
- if (reqContent.match(/数据库|表|DDL/))
3008
- items.push('涉及数据库变更,请补充 DDL');
3009
- if (reqContent.match(/权限|RBAC|鉴权/))
3010
- items.push('涉及权限控制,注意鉴权边界');
3011
- if (items.length > 0) {
3012
- techContent += `\n\n---\n\n## 分析建议\n\n> 自动生成\n\n${items.map(i => `- ${i}`).join('\n')}\n`;
3013
- await (0, fs_extra_1.writeFile)(techPath, techContent);
3014
- logger_1.logger.info(` 📄 更新 TECH.md`);
3015
- }
3016
- }
3017
- }
3018
- // 补全 TEST.md(v6.49.9+: 扫描平铺的端目录)
3019
- const testFiles = [];
3020
- const subtaskPaths = await getSubtaskDirs(fullTaskDir);
3021
- for (const stDir of subtaskPaths) {
3022
- const fp = (0, path_1.join)(stDir, 'TEST.md');
3023
- if (await (0, fs_extra_1.pathExists)(fp))
3024
- testFiles.push(fp);
3025
- }
3026
- if (testFiles.length === 0) {
3027
- const legacy = (0, path_1.join)(fullTaskDir, '99-artifacts', 'TEST.md');
3028
- if (await (0, fs_extra_1.pathExists)(legacy))
3029
- testFiles.push(legacy);
3030
- }
3031
- for (const testPath of testFiles) {
3032
- let testContent = await require('fs-extra').readFile(testPath, 'utf-8');
3033
- if (!testContent.includes('## 补充分析')) {
3034
- const items = [];
3035
- if (reqContent.includes('POST') || reqContent.includes('创建'))
3036
- items.push('[ ] 正常参数 + 异常参数测试');
3037
- if (reqContent.includes('GET') || reqContent.includes('查询'))
3038
- items.push('[ ] 分页 / 筛选 / 空结果测试');
3039
- if (reqContent.includes('DELETE') || reqContent.includes('删除'))
3040
- items.push('[ ] 删除确认 + 级联处理');
3041
- if (reqContent.includes('权限') || reqContent.includes('RBAC'))
3042
- items.push('[ ] 无权限访问 + 越权检测');
3043
- if (items.length > 0) {
3044
- testContent += `\n\n---\n\n## 补充分析\n${items.join('\n')}\n`;
3045
- await (0, fs_extra_1.writeFile)(testPath, testContent);
3046
- logger_1.logger.info(` 📄 更新 ${(0, path_1.relative)(fullTaskDir, testPath)}`);
3047
- }
3048
- }
3049
- }
3050
- // 补全 REVIEW.md(v6.49.9+: 扫描平铺的端目录)
3051
- const reviewFiles = [];
3052
- for (const stDir of subtaskPaths) {
3053
- const fp = (0, path_1.join)(stDir, 'REVIEW.md');
3054
- if (await (0, fs_extra_1.pathExists)(fp))
3055
- reviewFiles.push(fp);
3056
- }
3057
- if (reviewFiles.length === 0) {
3058
- const legacy = (0, path_1.join)(fullTaskDir, '99-artifacts', 'REVIEW.md');
3059
- if (await (0, fs_extra_1.pathExists)(legacy))
3060
- reviewFiles.push(legacy);
3061
- }
3062
- for (const reviewPath of reviewFiles) {
3063
- let reviewContent = await require('fs-extra').readFile(reviewPath, 'utf-8');
3064
- if (!reviewContent.includes('## 本任务专项检查')) {
3065
- const items = [];
3066
- if (reqContent.includes('POST') || reqContent.includes('创建'))
3067
- items.push('[ ] 参数校验 + 幂等性处理');
3068
- if (reqContent.includes('数据库') || reqContent.includes('表'))
3069
- items.push('[ ] 索引覆盖 + 迁移脚本可回滚');
3070
- if (reqContent.includes('权限') || reqContent.includes('RBAC'))
3071
- items.push('[ ] 鉴权注解/中间件正确配置');
3072
- if (items.length > 0) {
3073
- reviewContent += `\n\n---\n\n## 本任务专项检查\n${items.join('\n')}\n`;
3074
- await (0, fs_extra_1.writeFile)(reviewPath, reviewContent);
3075
- logger_1.logger.info(` 📄 更新 ${(0, path_1.relative)(fullTaskDir, reviewPath)}`);
3076
- }
3077
- }
3078
- }
3079
- // 创建缺失文件(v6.49.9+: 扫描平铺的端目录)
3080
- const subtaskDirs = subtaskPaths;
3081
- // 结构回退
3082
- if (subtaskDirs.length === 0) {
3083
- const legacyArtifacts = (0, path_1.join)(fullTaskDir, '99-artifacts');
3084
- if (await (0, fs_extra_1.pathExists)(legacyArtifacts))
3085
- subtaskDirs.push(legacyArtifacts);
3086
- else
3087
- subtaskDirs.push(fullTaskDir); // 最后回退到任务根
3088
- }
3089
- const templates = [
3090
- ['RISK.md', `# 风险评估\n\n> analyze | ${new Date().toISOString().split('T')[0]}\n\n## 风险矩阵\n| 风险 | 可能 | 影响 | 缓解 |\n| :--- | :--- | :--- | :--- |\n| 兼容性 | 中 | 高 | 版本号+测试 |\n\n## 回滚\n1. 触发: 线上错误率 > 1%\n2. 步骤: git revert → 重部署\n`],
3091
- ['DEPS.md', `# 依赖清单\n\n## 上游依赖\n| 服务 | 版本 | 用途 |\n| :--- | :--- | :--- |\n| _待补充_ | — | — |\n`],
3092
- ['MONITOR.md', `# 监控\n\n## 关键指标\n| 指标 | 阈值 | 级别 |\n| :--- | :--- | :--- |\n| 成功率 | <99.9% | P1 |\n| P99延迟 | >1000ms | P2 |\n`],
3093
- ];
3094
- for (const subtaskDir of subtaskDirs) {
3095
- for (const [filename, content] of templates) {
3096
- const fp = (0, path_1.join)(subtaskDir, filename);
3097
- if (!(await (0, fs_extra_1.pathExists)(fp))) {
3098
- await (0, fs_extra_1.writeFile)(fp, content);
3099
- logger_1.logger.info(` 📄 创建 ${(0, path_1.relative)(fullTaskDir, fp)}`);
3100
- }
3101
- }
3102
- }
3103
- }
3104
2832
  /**
3105
2833
  * 从 process.argv 手动解析选项 (Commander.js 偶发不传递部分选项)
3106
2834
  */
@@ -3108,7 +2836,6 @@ function parseArgv(options) {
3108
2836
  const argv = process.argv;
3109
2837
  const strFlags = [
3110
2838
  [['--iteration', '-i', '-I'], (v) => { options.iteration = v; }],
3111
- [['--task', '-t'], (v) => { options.task = v; }],
3112
2839
  [['--platform'], (v) => { options.platform = v; }],
3113
2840
  [['--scope'], (v) => { options.scope = v; }],
3114
2841
  [['--src', '--source'], (v) => { options.source = v; }],
@@ -3573,7 +3300,7 @@ async function buildGlobalAnalysisGuide(options) {
3573
3300
  if (completedLayer === 4 && (!subLayer || subLayer.completed.length === 4)) {
3574
3301
  guide += '\n🎉 全局分析全部完成!\n';
3575
3302
  guide += ' 所有文档已生成在 .speccore/GLOBAL/ 目录\n';
3576
- guide += ' 如需补充某份文档: speccore analyze --scope global --layer 4 --deep <文档名>\n';
3303
+ guide += ' 如需补充某份文档: speccore global analyze --layer 4 --deep <文档名>\n';
3577
3304
  return guide;
3578
3305
  }
3579
3306
  // 下一步命令
@@ -3584,15 +3311,15 @@ async function buildGlobalAnalysisGuide(options) {
3584
3311
  if (!hasOutline) {
3585
3312
  guide += ` 1. 将 AI 输出的大纲保存到: ${outlinePath}\n`;
3586
3313
  guide += ` 2. 审核/修改大纲\n`;
3587
- guide += ` 3. 再次执行: speccore analyze --scope global --layer 4 --deep ${deepDoc} --iterative\n`;
3314
+ guide += ` 3. 再次执行: speccore global analyze --layer 4 --deep ${deepDoc} --iterative\n`;
3588
3315
  }
3589
3316
  else {
3590
3317
  guide += ` 1. 将 AI 输出的本节内容追加到文档\n`;
3591
- guide += ` 2. 继续下一节: speccore analyze --scope global --layer 4 --deep ${deepDoc} --iterative\n`;
3318
+ guide += ` 2. 继续下一节: speccore global analyze --layer 4 --deep ${deepDoc} --iterative\n`;
3592
3319
  }
3593
3320
  }
3594
3321
  else if (deepDoc) {
3595
- guide += ` speccore analyze --scope global --layer 4 --deep ${deepDoc}\n`;
3322
+ guide += ` speccore global analyze --layer 4 --deep ${deepDoc}\n`;
3596
3323
  guide += ` (如需迭代式补全: 加 --iterative 参数)\n`;
3597
3324
  }
3598
3325
  else if (nextLayer === 4 && subLayer) {
@@ -3602,19 +3329,19 @@ async function buildGlobalAnalysisGuide(options) {
3602
3329
  '4c': '全局技术扩展文档(overview/)',
3603
3330
  '4d': '各端技术文档(platforms/)',
3604
3331
  };
3605
- guide += ` speccore analyze --scope global --layer 4\n`;
3332
+ guide += ` speccore global analyze --layer 4\n`;
3606
3333
  guide += ` (即将生成: ${subNames[subLayer.next] || subLayer.next})\n`;
3607
3334
  }
3608
3335
  else {
3609
- guide += ` speccore analyze --scope global --layer ${nextLayer}\n`;
3336
+ guide += ` speccore global analyze --layer ${nextLayer}\n`;
3610
3337
  }
3611
3338
  // 快捷命令提示
3612
3339
  guide += '\n💡 快捷命令:\n';
3613
3340
  guide += ` 查看进度: speccore status\n`;
3614
3341
  if (completedLayer >= 3) {
3615
- guide += ` 深度分析单文档: speccore analyze --scope global --layer 4 --deep ARCHITECTURE.md\n`;
3342
+ guide += ` 深度分析单文档: speccore global analyze --layer 4 --deep ARCHITECTURE.md\n`;
3616
3343
  }
3617
- guide += ` 全量重新分析: speccore analyze --scope global --with-code\n`;
3344
+ guide += ` 全量重新分析: speccore global analyze --with-code\n`;
3618
3345
  return guide;
3619
3346
  }
3620
3347
  // ── v7.4.5+: Layer 3 功能模块代码上下文预提取 ──
@@ -3728,10 +3455,7 @@ async function buildLayer3ModuleContext(projectRoot) {
3728
3455
  // v8.3.160+: 改为 export,供 prompt-builder.ts 统一调用
3729
3456
  async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3730
3457
  const iter = ctx.iteration || '当前迭代';
3731
- const task = ctx.task ? ` — ${ctx.task}` : '';
3732
- const taskType = ctx.type || 'feature';
3733
3458
  const now = new Date().toISOString().split('T')[0];
3734
- const isTask = ctx.scope === 'task' || !!ctx.task;
3735
3459
  const isGlobal = ctx.scope === 'global';
3736
3460
  const autoMode = ctx.autoMode || false;
3737
3461
  // v8.3.0+: 从 clarifyCtx 提取需求澄清状态
@@ -3854,7 +3578,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3854
3578
  // v7.2.0+: 分层专注指令 — 强烈约束 AI 只执行当前 layer
3855
3579
  prompt += `## 🎯 当前执行层级: Layer ${targetLayer}/4 — ${layerMeta.role}\n\n`;
3856
3580
  prompt += `> ⚠️ **重要约束**: 你当前只需要完成 **Layer ${targetLayer}** 的工作。不要提前做后续层的内容。\n`;
3857
- prompt += `> 每层完成后通过 \`speccore analyze --scope global --layer ${targetLayer + 1 <= 4 ? targetLayer + 1 : 4}\` 进入下一层。\n\n`;
3581
+ prompt += `> 每层完成后通过 \`speccore global analyze --layer ${targetLayer + 1 <= 4 ? targetLayer + 1 : 4}\` 进入下一层。\n\n`;
3858
3582
  prompt += `| 层级 | 角色 | 核心任务 | 产出物 |\n`;
3859
3583
  prompt += `| :--- | :--- | :--- | :--- |\n`;
3860
3584
  prompt += `| 1 | 代码索引专家 | 扫描源码结构,提取索引 | platforms/{端}/_INDEX.md |\n`;
@@ -3901,7 +3625,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3901
3625
  prompt += `**分批策略(v8.3.160+)**:\n`;
3902
3626
  prompt += `- 如端数量 ≤ 5:一次生成所有端的 \`_INDEX.md\`\n`;
3903
3627
  prompt += `- 如端数量 > 5:一次处理 3-5 个端,剩余端在输出末尾标注 \`[PENDING: {端名列表}]\`,供续批处理\n`;
3904
- prompt += `- 续批命令:\`speccore analyze --scope global --layer 1 --resume\`\n`;
3628
+ prompt += `- 续批命令:\`speccore global analyze --layer 1 --resume\`\n`;
3905
3629
  prompt += `**最小内容标准(v8.3.61+)**:\n`;
3906
3630
  prompt += `- 每个 \_INDEX.md ≥ 80 行(维度少可接受 50+,但严禁 < 30 行)\n`;
3907
3631
  prompt += `- 必须包含:端名、技术栈、目录结构概览、各维度清单表格\n`;
@@ -3994,7 +3718,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
3994
3718
  prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的分析文档\n`;
3995
3719
  prompt += `- 如功能模块数量 > 2:一次处理 1-2 个模块,确保每个模块分析深入、不空洞\n`;
3996
3720
  prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
3997
- prompt += `- 续批命令:\`speccore analyze --scope global --layer 3 --resume\`\n`;
3721
+ prompt += `- 续批命令:\`speccore global analyze --layer 3 --resume\`\n`;
3998
3722
  prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(≥100 行、有 Mermaid 图、有具体代码引用)\n\n`;
3999
3723
  prompt += `**每个功能模块输出**:\n`;
4000
3724
  prompt += `- 后端端:该功能模块相关的 API 详细设计、数据模型、业务规则、安全策略\n`;
@@ -5046,7 +4770,7 @@ sequenceDiagram
5046
4770
  bugfix: ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'DEV_GUIDE.md'],
5047
4771
  research: ['ANALYSIS.md', 'DEV_GUIDE.md'],
5048
4772
  };
5049
- const includeDocs = isTask ? (DOC_MATRIX[taskType] || DOC_MATRIX['feature']) : DOC_MATRIX['feature'];
4773
+ const includeDocs = DOC_MATRIX['feature'];
5050
4774
  // ── v6.61.0+: 恢复 Phase 1/Phase 2 分步逻辑,但 CLI 自动触发 Phase 2 ──
5051
4775
  // v8.3.160+: 改为功能模块优先 — 全局只保留索引文档,每个功能模块有自己的综合文档
5052
4776
  // Phase 1: 全局索引(FUNCTION_MAP.md/INTERACTION_MAP.md/PLATFORMS.md) + 各功能模块 overview/
@@ -5094,7 +4818,7 @@ sequenceDiagram
5094
4818
  // 替代 v7.5.0 的 detectIterationDocsStatus(基于文件是否存在)
5095
4819
  let perDocStatus = null;
5096
4820
  let lastDocQuality = null;
5097
- if (!isGlobal && !isTask && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
4821
+ if (!isGlobal && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
5098
4822
  try {
5099
4823
  const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
5100
4824
  if (iterDir) {
@@ -5211,39 +4935,6 @@ sequenceDiagram
5211
4935
  logger_1.logger.debug(`骨架进度检测失败(非关键): ${e.message}`);
5212
4936
  }
5213
4937
  }
5214
- // ── 任务级文档覆盖:00-specs/ 使用任务级文档集(v6.44.0+) ──
5215
- if (isTask && !ctx.phase) {
5216
- const TASK_DOCS = {
5217
- feature: ['REQ.md', 'TECH.md', 'TASK.md', 'SCHEMA.md'],
5218
- refactor: ['REQ.md', 'TECH.md', 'TASK.md'],
5219
- bugfix: ['REQ.md', 'TECH.md'],
5220
- research: ['REQ.md'],
5221
- };
5222
- const taskDocNames = TASK_DOCS[taskType] || TASK_DOCS['feature'];
5223
- taskDocs = taskDocs.filter(([n]) => taskDocNames.includes(n));
5224
- // 补充 TASK.md 和 SCHEMA.md(不在全局 docs 数组中)
5225
- if (taskDocNames.includes('TASK.md') && !taskDocs.find(([n]) => n === 'TASK.md')) {
5226
- taskDocs.push(['TASK.md', '']);
5227
- }
5228
- if (taskDocNames.includes('SCHEMA.md') && !taskDocs.find(([n]) => n === 'SCHEMA.md')) {
5229
- taskDocs.push(['SCHEMA.md', '']);
5230
- }
5231
- // 任务级模板覆盖(v6.44.0+)
5232
- for (const doc of taskDocs) {
5233
- if (doc[0] === 'REQ.md') {
5234
- doc[1] = `# \u672c\u4efb\u52a1\u9700\u6c42\u89c4\u683c\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u6839\u636e split \u4ea7\u51fa\u7684\u9700\u6c42\u5207\u7247\uff0c\u7ed3\u5408\u5168\u5c40\u4e0a\u4e0b\u6587\uff0c\u91cd\u65b0\u7ec4\u7ec7\u672c\u4efb\u52a1\u7684\u9700\u6c42\u89c4\u683c\uff1a\n- \u660e\u786e\u672c\u4efb\u52a1\u7684\u9a8c\u6536\u6807\u51c6\uff08\u53ef\u6d4b\u8bd5\u7684\u3001\u5177\u4f53\u7684\uff09\n- \u7ec6\u5316\u4e1a\u52a1\u89c4\u5219\u548c\u8fb9\u754c\u6761\u4ef6\n- \u5217\u51fa\u5f02\u5e38\u573a\u666f\u548c\u5904\u7406\u65b9\u5f0f\n- \u6807\u6ce8\u4e0e\u5176\u4ed6 Task \u7684\u4f9d\u8d56\u5173\u7cfb\n`;
5235
- }
5236
- else if (doc[0] === 'TECH.md') {
5237
- doc[1] = `# \u672c\u4efb\u52a1\u6280\u672f\u65b9\u6848\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u57fa\u4e8e overview/TECH.md \u7684\u6574\u4f53\u67b6\u6784\uff0c\u7ec6\u5316\u5230\u51fd\u6570/\u63a5\u53e3\u7ea7\u522b\uff1a\n- \u5177\u4f53\u7684\u63a5\u53e3\u5b9a\u4e49\uff08\u8def\u5f84/\u53c2\u6570/\u54cd\u5e94\uff09\n- \u6570\u636e\u6a21\u578b\u8bbe\u8ba1\uff08Entity/DTO/VO \u5b57\u6bb5\u6620\u5c04\uff09\n- \u6838\u5fc3\u4e1a\u52a1\u903b\u8f91\u7684\u4f2a\u4ee3\u7801\u6216\u6d41\u7a0b\u63cf\u8ff0\n- \u524d\u7aef\u7ec4\u4ef6\u62c6\u5206\u548c\u72b6\u6001\u8bbe\u8ba1\n- \u5fc5\u987b\u4e0e overview/TECH.md \u7684\u6574\u4f53\u67b6\u6784\u4fdd\u6301\u4e00\u81f4\n`;
5238
- }
5239
- else if (doc[0] === 'TASK.md') {
5240
- doc[1] = `# \u5b9e\u65bd\u8ba1\u5212\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u6839\u636e\u672c\u4efb\u52a1\u7684\u9700\u6c42\u548c\u6280\u672f\u65b9\u6848\uff0c\u5236\u5b9a\u5177\u4f53\u5b9e\u65bd\u6b65\u9aa4\uff1a\n- \u6309\u5f00\u53d1\u987a\u5e8f\u5217\u51fa\u5177\u4f53\u6b65\u9aa4\n- \u6bcf\u4e2a\u6b65\u9aa4\u6709\u660e\u786e\u7684\u5b8c\u6210\u6807\u51c6\n- \u6807\u6ce8\u6b65\u9aa4\u95f4\u7684\u4f9d\u8d56\u5173\u7cfb\n- \u4f30\u7b97\u6bcf\u6b65\u7684\u5de5\u4f5c\u91cf\n`;
5241
- }
5242
- else if (doc[0] === 'SCHEMA.md') {
5243
- doc[1] = `# \u6570\u636e\u6a21\u578b\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u68b3\u7406\u672c\u4efb\u52a1\u6d89\u53ca\u7684\u6570\u636e\u6a21\u578b\u53d8\u66f4\uff1a\n- \u5b8c\u6574\u7684\u5b57\u6bb5\u5b9a\u4e49\uff08\u540d\u79f0/\u7c7b\u578b/\u7ea6\u675f/\u8bf4\u660e\uff09\n- \u7d22\u5f15\u8bbe\u8ba1\n- DDL \u8bed\u53e5\uff08\u5982\u9002\u7528\uff09\n- \u5b57\u6bb5\u2192API \u54cd\u5e94\u5b57\u6bb5\u7684\u6620\u5c04\u5173\u7cfb\n`;
5244
- }
5245
- }
5246
- }
5247
4938
  // v7.2.0+: 细粒度分析 — 只生成指定文档的指定功能
5248
4939
  if (options?.docName && options?.featureName && !isGlobal) {
5249
4940
  const targetDoc = options.docName.endsWith('.md') ? options.docName : `${options.docName}.md`;
@@ -5257,8 +4948,8 @@ sequenceDiagram
5257
4948
  }
5258
4949
  }
5259
4950
  // ── 用户自定义模板集成(v6.45.0+)──
5260
- const templateLevel = isGlobal ? 'global' : (isTask ? 'task' : 'iteration');
5261
- const userTemplates = await loadUserTemplates(templateLevel, isTask ? taskType : undefined, ctx.platform);
4951
+ const templateLevel = isGlobal ? 'global' : 'iteration';
4952
+ const userTemplates = await loadUserTemplates(templateLevel, undefined, ctx.platform);
5262
4953
  const hasUserTemplates = userTemplates.size > 0;
5263
4954
  if (hasUserTemplates) {
5264
4955
  // 用户模板覆盖/追加:用户放了什么文档就生成什么文档
@@ -5281,13 +4972,13 @@ sequenceDiagram
5281
4972
  techDoc[1] = `# 技术架构(跨端全局)\n\n> ${iter}\n\n## 写作要求\n撰写整体技术架构,覆盖所有端的交互关系:\n- 系统整体分层设计(各端在架构中的位置)\n- 跨端交互协议(前端↔后端通信方式、数据流向)\n- 中间件选型(缓存、消息队列、网关等)\n- 数据库整体设计(核心表结构、ER 关系)\n- 技术栈选型及理由\n`;
5282
4973
  }
5283
4974
  const featureLabel = ctx.feature ? ` · 功能模块:${ctx.feature}` : '';
5284
- let prompt = `\n# 任务: ${command}${task} (${taskDocs.length}个文档 · ${isTask ? `类型:${taskType}` : '迭代全量'}${ctx.phase ? ` · Phase ${ctx.phase}` : ''}${featureLabel})\n\n`;
4975
+ let prompt = `\n# 任务: ${command} (${taskDocs.length}个文档 · 迭代全量${ctx.phase ? ` · Phase ${ctx.phase}` : ''}${featureLabel})\n\n`;
5285
4976
  // v8.3.160+: 注入子 Agent 角色定义
5286
4977
  if (agentContextText) {
5287
4978
  prompt = agentContextText + '\n\n' + prompt;
5288
4979
  }
5289
4980
  // v8.3.0+: 如果检测到需求质量不足,在 prompt 开头注入强制澄清阶段
5290
- if (needsClarify && !isGlobal && !isTask && !ctx.phase) {
4981
+ if (needsClarify && !isGlobal && !ctx.phase) {
5291
4982
  const now = new Date().toISOString();
5292
4983
  prompt += `## 🚨 Phase 0: 需求澄清(强制前置 — 未完成则禁止进入 Phase 1)\n\n`;
5293
4984
  prompt += `检测到以下需求文档质量不足,**必须先完成专业化整理,才能继续技术分析**:\n\n`;
@@ -5382,7 +5073,7 @@ status: "clarified"
5382
5073
  logger_1.logger.warn(` ⚠️ 代码关联注入失败: ${e.message}`);
5383
5074
  }
5384
5075
  }
5385
- if (!isGlobal && !isTask && ctx.iteration && ctx.iteration !== 'GLOBAL') {
5076
+ if (!isGlobal && ctx.iteration && ctx.iteration !== 'GLOBAL') {
5386
5077
  try {
5387
5078
  const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
5388
5079
  const reportPath = (0, path_1.join)(iterDir, '010-requirements', 'CLARIFY_REPORT.md');
@@ -5409,7 +5100,7 @@ status: "clarified"
5409
5100
  catch { /* 忽略质量上下文注入失败 */ }
5410
5101
  }
5411
5102
  // ── v6.67.0+: 在 prompt 开头就明确完整工作流程 ──
5412
- if (!ctx.phase && !isTask) {
5103
+ if (!ctx.phase) {
5413
5104
  const platforms = await (0, spec_paths_2.parsePlatformList)();
5414
5105
  if (platforms.length >= 2) {
5415
5106
  prompt += `## 📋 完整工作流程(两阶段分析)\n\n`;
@@ -5454,7 +5145,7 @@ status: "clarified"
5454
5145
  // ── v8.1.0+: 逐文档模式自动注入 PRD 内容 + 前序文档摘要 ──
5455
5146
  // v8.3.2+ 修复:PRD 注入不再仅限逐文档模式,Phase 1/2 模式下也必须注入需求内容
5456
5147
  // v8.3.160+: 改为路径引用模式(不再全文塞进,让 AI 按需 Read)
5457
- if (!isGlobal && !isTask && ctx.iteration && ctx.iteration !== 'GLOBAL') {
5148
+ if (!isGlobal && ctx.iteration && ctx.iteration !== 'GLOBAL') {
5458
5149
  try {
5459
5150
  const iterDirForCtx = await (0, context_1.getIterationDir)(iter);
5460
5151
  if (iterDirForCtx) {
@@ -5550,7 +5241,7 @@ status: "clarified"
5550
5241
  prompt += `- 分析过程中,始终把迭代名称当作透明信息处理,不做任何功能推断\n\n`;
5551
5242
  prompt += `## 分析范围说明\n`;
5552
5243
  // v8.3.167+: 功能模块级分析专属指令
5553
- if (ctx.feature && !isTask) {
5244
+ if (ctx.feature) {
5554
5245
  prompt += `- 当前是**功能模块级分析**,只分析「**${ctx.feature}**」功能模块\n`;
5555
5246
  prompt += `- **只生成该功能模块的 overview/ 文档**,不要分析其他功能模块\n`;
5556
5247
  prompt += `- 文档写入路径格式:\`${ctx.feature}/overview/{文件名}\`(如 \`${ctx.feature}/overview/REQUIREMENT.md\`)\n`;
@@ -5558,345 +5249,300 @@ status: "clarified"
5558
5249
  prompt += `- 分析前请先 Read 全局索引文档,确保本模块内容与全局文档一致\n`;
5559
5250
  prompt += `- 如果需求文档中该模块的描述不完整,标注"文档未充分描述",不要自行脑补\n\n`;
5560
5251
  }
5561
- if (isTask) {
5562
- prompt += `- 当前是**任务级分析**,类型为 \`${taskType}\`,只需产出 ${taskDocs.length} 个文档:${taskDocs.map(([n]) => n).join('、')}\n`;
5563
- prompt += `- bugfix: 聚焦根因分析和修复验证;research: 聚焦技术调研;review: 聚焦代码审查\n`;
5564
- prompt += `- feature/refactor: 全量分析(功能、接口、数据、规则)\n`;
5565
- prompt += `- **双层解耦**:分析结果写入 Task/00-specs/,不覆盖 020-specs/(迭代基线)\n`;
5566
- // ── 任务级深度分析指令(v6.44.0+)──
5567
- prompt += `\n## 🧠 任务级深度分析\n\n`;
5568
- prompt += `当前是对 **单个 Task** 的深度分析。split 已在 00-specs/ 中生成了基础内容(机械提取),你需要 Read 这些内容 + 全局上下文,重新生成**任务级深度分析**。\n\n`;
5569
- prompt += `### Step 1: 读取已有任务文档(split 产出)\n`;
5570
- prompt += `- Read ${ctx.task}/00-specs/REQ.md → 本任务的需求切片\n`;
5571
- prompt += `- Read ${ctx.task}/00-specs/TECH.md → 本任务的技术方案框架\n`;
5572
- prompt += `- Read ${ctx.task}/00-specs/TASK.md(如存在)→ 已有的实施计划\n`;
5573
- prompt += `- Read ${ctx.task}/00-specs/SCHEMA.md(如存在)→ 已有的数据模型\n`;
5574
- prompt += `- Read ${ctx.task}/_shared/API_CONTRACT.yaml(如存在)→ 共享 API 契约\n\n`;
5575
- prompt += `### Step 2: 读取全局上下文(作为参考)\n`;
5576
- prompt += `- Read .speccore/CONSTITUTION.md → 项目配置\n`;
5577
- prompt += `- Read 020-specs/PLATFORMS.md → 端列表\n`;
5578
- prompt += `- Read 020-specs/overview/REQUIREMENT.md → 迭代综合需求规格\n`;
5579
- prompt += `- Read 020-specs/overview/TECH.md → 迭代综合技术架构\n`;
5580
- prompt += `- Read 020-specs/overview/ANALYSIS.md → 迭代综合分析报告\n`;
5581
- prompt += `- Read 020-specs/{功能模块}/{本任务端名}/TECH.md → 该端专属技术方案\n\n`;
5582
- prompt += `### Step 3: 撰写任务级深度分析文档\n\n`;
5583
- prompt += `**REQ.md** — 本任务的需求规格(不是 overview/REQUIREMENT.md 的复制):\n`;
5584
- prompt += `- 明确本任务的验收标准(可测试的、具体的)\n`;
5585
- prompt += `- 细化业务规则和边界条件\n`;
5586
- prompt += `- 列出本任务涉及的异常场景\n\n`;
5587
- prompt += `**TECH.md** — 本任务的实现方案(基于全局架构,细化到函数/接口级):\n`;
5588
- prompt += `- 具体的接口定义(路径/参数/响应)\n`;
5589
- prompt += `- 数据模型设计(Entity/DTO/VO 字段映射)\n`;
5590
- prompt += `- 核心业务逻辑的伪代码或流程描述\n`;
5591
- prompt += `- 前端组件拆分和状态设计\n`;
5592
- prompt += `- 必须与 overview/TECH.md 的整体架构保持一致\n\n`;
5593
- prompt += `**TASK.md** — 本任务的实施步骤:\n`;
5594
- prompt += `- 按开发顺序列出具体步骤\n`;
5595
- prompt += `- 每个步骤有明确的完成标准\n`;
5596
- prompt += `- 标注步骤间的依赖关系\n\n`;
5597
- prompt += `**SCHEMA.md** — 本任务涉及的数据模型:\n`;
5598
- prompt += `- 完整的字段定义(名称/类型/约束/说明)\n`;
5599
- prompt += `- 索引设计\n`;
5600
- prompt += `- DDL 语句(如适用)\n\n`;
5252
+ prompt += `- 当前是**迭代级分析**,需产出全部 8 个文档,覆盖需求→技术→测试→评审→风险→依赖→监控→UI规格\n`;
5253
+ if (ctx.platform) {
5254
+ prompt += `- **只分析 ${ctx.platform} 端**:从 CONSTITUTION.md 读取端列表,但只生成 ${ctx.platform} 端的专属文档\n`;
5255
+ prompt += `- 在 020-specs/{功能模块}/${ctx.platform}/ 下写入该端专属文档(ANALYSIS.md、TECH.md、TEST.md 等)\n`;
5256
+ prompt += `- 迭代综合文档写入 020-specs/overview/(REQUIREMENT.md、DEPS.md、RISK.md 等)\n`;
5257
+ prompt += `- **不要生成**其他端的子目录和文档\n`;
5258
+ }
5259
+ // ── v6.61.0+: 阶段专属指令(Phase 2)──
5260
+ if (ctx.phase === '2') {
5261
+ prompt += `## 要求\n\n`;
5262
+ prompt += `### Step 1: 读取全局上下文(Phase 1 产出)\n`;
5263
+ prompt += `依次 Read 以下文件,建立全局技术架构认知:\n`;
5264
+ prompt += `- Read .speccore/CONSTITUTION.md\n`;
5265
+ prompt += `- Read 020-specs/PLATFORMS.md → 获取端列表\n`;
5266
+ prompt += `- Read 020-specs/overview/REQUIREMENT.md → 需求规格\n`;
5267
+ prompt += `- Read 020-specs/overview/ANALYSIS.md → 分析报告\n`;
5268
+ prompt += `- Read 020-specs/overview/TECH.md → 整体技术架构\n`;
5269
+ prompt += `- Read 020-specs/overview/RISK.md、DEPS.md、REVIEW.md、MONITOR.md(如存在)\n\n`;
5270
+ prompt += `### Step 2: 按功能模块组织,为每个功能模块的每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
5271
+ prompt += `先 Read \`020-specs/overview/FUNCTION_MAP.md\` 获取功能模块清单。\n\n`;
5272
+ prompt += `**分批策略(v8.3.160+)**:\n`;
5273
+ prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的所有端文档\n`;
5274
+ prompt += `- 如功能模块数量 > 2:一次只处理 **1-2 个功能模块** 的所有端文档,确保质量深入\n`;
5275
+ prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
5276
+ prompt += `- 续批命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
5277
+ prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(文档充实、有具体代码引用、表格有数据行)\n\n`;
5278
+ prompt += `每个功能模块涉及的每个端,分别撰写以下 **4 份文档**,缺一不可:\n\n`;
5279
+ prompt += `**文件路径格式**:\`{功能模块名}/{端名}/{文件名}\`(如 \`booking/backend/TECH.md\`、\`booking/h5-mobile/UI_SPEC.md\`)\n`;
5280
+ prompt += `> ⚠️ **严禁**只写 \`{端}/{文件}\` 格式(如 \`backend/TECH.md\`),这会导致所有功能模块的文档混在一起无法区分\n\n`;
5281
+ prompt += `**1. {功能模块}/{端}/TECH.md — 该端在该功能模块下的技术方案(必须对齐 overview/TECH.md 架构)**\n`;
5282
+ prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
5283
+ prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
5284
+ prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
5285
+ prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
5286
+ prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
5287
+ prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
5288
+ prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
5289
+ prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
5290
+ prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
5291
+ prompt += ` - 边界值和异常输入测试\n`;
5292
+ prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
5293
+ prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
5294
+ prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
5295
+ prompt += ` - 组件清单和字段→UI 映射\n`;
5296
+ prompt += ` - 状态枚举和错误处理策略\n\n`;
5297
+ prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
5298
+ prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
5299
+ prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
5300
+ prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
5301
+ prompt += ` - **代码级指引**:改哪几行、改什么、改造后的关键代码片段\n`;
5302
+ prompt += ` - **与后端联调**:接口状态、联调顺序、Mock 方案,表格表头:| # | 接口 | 后端状态 | 联调顺序 | Mock 方案 |\n`;
5303
+ prompt += ` - **验证方式**:本端特有的验证命令/操作和通过标准,表格表头:| # | 验证项 | 命令/操作 | 通过标准 |\n`;
5304
+ prompt += ` - **本端坑点**:已知陷阱及解决方式\n`;
5305
+ prompt += ` - **回滚方案**:代码回滚方式、配置回滚方式\n\n`;
5306
+ prompt += `### Step 3: 一致性检查\n`;
5307
+ prompt += `- 各端 TECH.md 的技术选型必须与 overview/TECH.md 一致\n`;
5308
+ prompt += `- UI_SPEC.md 的字段映射必须与后端 API 响应字段一一对应\n`;
5309
+ prompt += `- TEST.md 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
5310
+ prompt += `### Step 3: 自检 — 确保所有文档已实质填充\n`;
5311
+ prompt += `生成完成后,逐个检查每个文档:\n`;
5312
+ prompt += `- 如果文档中仍含有 \`<!-- SPEC-SKELETON -->\` 标记或空表格 → **必须重新填充**\n`;
5313
+ prompt += `- 如果「改造范围」表格只有表头没有数据行 → **必须补充具体文件路径**\n`;
5314
+ prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
5315
+ prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
5601
5316
  prompt += `### 写入方式\n`;
5602
- prompt += `speccore analyze --apply '{"REQ.md":"...","TECH.md":"...","TASK.md":"...","SCHEMA.md":"..."}' -I ${iter} --task ${ctx.task || 'Task-NNN'}\n\n`;
5317
+ prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
5318
+ prompt += `\`\`\`bash\n`;
5319
+ 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`;
5320
+ prompt += `\`\`\`\n\n`;
5321
+ prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
5322
+ prompt += `\`\`\`bash\n`;
5323
+ prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
5324
+ prompt += `\`\`\`\n\n`;
5325
+ prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
5326
+ prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
5327
+ prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
5328
+ prompt += `- **禁止输出占位符** — 不允许写「待填充」、「TODO」、「...」、「xxx」等占位内容\n`;
5329
+ prompt += `- **DEV_GUIDE.md 最低标准**:改造范围表格 ≥3 行、实施步骤 ≥3 步、验证方式表格 ≥3 行、坑点 ≥2 条\n`;
5330
+ prompt += `- **如果 token 不足**:优先保证 DEV_GUIDE.md 完整,其他文档可精简但不可空\n`;
5331
+ prompt += `- **自检必做**:写入前用 Read 工具检查每个文档,确认不含 \`<!-- SPEC-SKELETON -->\` 且所有表格有数据行\n\n`;
5603
5332
  }
5604
5333
  else {
5605
- prompt += `- 当前是**迭代级分析**,需产出全部 8 个文档,覆盖需求→技术→测试→评审→风险→依赖→监控→UI规格\n`;
5606
- if (ctx.platform) {
5607
- prompt += `- **只分析 ${ctx.platform} 端**:从 CONSTITUTION.md 读取端列表,但只生成 ${ctx.platform} 端的专属文档\n`;
5608
- prompt += `- 在 020-specs/{功能模块}/${ctx.platform}/ 下写入该端专属文档(ANALYSIS.md、TECH.md、TEST.md 等)\n`;
5609
- prompt += `- 迭代综合文档写入 020-specs/overview/(REQUIREMENT.md、DEPS.md、RISK.md 等)\n`;
5610
- prompt += `- **不要生成**其他端的子目录和文档\n`;
5611
- }
5612
- // ── v6.61.0+: 阶段专属指令(Phase 2)──
5613
- if (ctx.phase === '2') {
5614
- prompt += `## 要求\n\n`;
5615
- prompt += `### Step 1: 读取全局上下文(Phase 1 产出)\n`;
5616
- prompt += `依次 Read 以下文件,建立全局技术架构认知:\n`;
5617
- prompt += `- Read .speccore/CONSTITUTION.md\n`;
5618
- prompt += `- Read 020-specs/PLATFORMS.md → 获取端列表\n`;
5619
- prompt += `- Read 020-specs/overview/REQUIREMENT.md → 需求规格\n`;
5620
- prompt += `- Read 020-specs/overview/ANALYSIS.md → 分析报告\n`;
5621
- prompt += `- Read 020-specs/overview/TECH.md → 整体技术架构\n`;
5622
- prompt += `- Read 020-specs/overview/RISK.md、DEPS.md、REVIEW.md、MONITOR.md(如存在)\n\n`;
5623
- prompt += `### Step 2: 按功能模块组织,为每个功能模块的每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
5624
- prompt += `先 Read \`020-specs/overview/FUNCTION_MAP.md\` 获取功能模块清单。\n\n`;
5625
- prompt += `**分批策略(v8.3.160+)**:\n`;
5626
- prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的所有端文档\n`;
5627
- prompt += `- 如功能模块数量 > 2:一次只处理 **1-2 个功能模块** 的所有端文档,确保质量深入\n`;
5628
- prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
5629
- prompt += `- 续批命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
5630
- prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(文档充实、有具体代码引用、表格有数据行)\n\n`;
5631
- prompt += `每个功能模块涉及的每个端,分别撰写以下 **4 份文档**,缺一不可:\n\n`;
5632
- prompt += `**文件路径格式**:\`{功能模块名}/{端名}/{文件名}\`(如 \`booking/backend/TECH.md\`、\`booking/h5-mobile/UI_SPEC.md\`)\n`;
5633
- prompt += `> ⚠️ **严禁**只写 \`{端}/{文件}\` 格式(如 \`backend/TECH.md\`),这会导致所有功能模块的文档混在一起无法区分\n\n`;
5634
- prompt += `**1. {功能模块}/{端}/TECH.md — 该端在该功能模块下的技术方案(必须对齐 overview/TECH.md 架构)**\n`;
5635
- prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
5636
- prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
5637
- prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
5638
- prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
5639
- prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
5640
- prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
5641
- prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
5642
- prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
5643
- prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
5644
- prompt += ` - 边界值和异常输入测试\n`;
5645
- prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
5646
- prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
5647
- prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
5648
- prompt += ` - 组件清单和字段→UI 映射\n`;
5649
- prompt += ` - 状态枚举和错误处理策略\n\n`;
5650
- prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
5651
- prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
5652
- prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
5653
- prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
5654
- prompt += ` - **代码级指引**:改哪几行、改什么、改造后的关键代码片段\n`;
5655
- prompt += ` - **与后端联调**:接口状态、联调顺序、Mock 方案,表格表头:| # | 接口 | 后端状态 | 联调顺序 | Mock 方案 |\n`;
5656
- prompt += ` - **验证方式**:本端特有的验证命令/操作和通过标准,表格表头:| # | 验证项 | 命令/操作 | 通过标准 |\n`;
5657
- prompt += ` - **本端坑点**:已知陷阱及解决方式\n`;
5658
- prompt += ` - **回滚方案**:代码回滚方式、配置回滚方式\n\n`;
5659
- prompt += `### Step 3: 一致性检查\n`;
5660
- prompt += `- 各端 TECH.md 的技术选型必须与 overview/TECH.md 一致\n`;
5661
- prompt += `- UI_SPEC.md 的字段映射必须与后端 API 响应字段一一对应\n`;
5662
- prompt += `- TEST.md 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
5663
- prompt += `### Step 3: 自检 — 确保所有文档已实质填充\n`;
5664
- prompt += `生成完成后,逐个检查每个文档:\n`;
5665
- prompt += `- 如果文档中仍含有 \`<!-- SPEC-SKELETON -->\` 标记或空表格 → **必须重新填充**\n`;
5666
- prompt += `- 如果「改造范围」表格只有表头没有数据行 → **必须补充具体文件路径**\n`;
5667
- prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
5668
- prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
5669
- prompt += `### 写入方式\n`;
5670
- prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
5671
- prompt += `\`\`\`bash\n`;
5672
- 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`;
5673
- prompt += `\`\`\`\n\n`;
5674
- prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
5675
- prompt += `\`\`\`bash\n`;
5676
- prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
5677
- prompt += `\`\`\`\n\n`;
5678
- prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
5679
- prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
5680
- prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
5681
- prompt += `- **禁止输出占位符** — 不允许写「待填充」、「TODO」、「...」、「xxx」等占位内容\n`;
5682
- prompt += `- **DEV_GUIDE.md 最低标准**:改造范围表格 ≥3 行、实施步骤 ≥3 步、验证方式表格 ≥3 行、坑点 ≥2 条\n`;
5683
- prompt += `- **如果 token 不足**:优先保证 DEV_GUIDE.md 完整,其他文档可精简但不可空\n`;
5684
- prompt += `- **自检必做**:写入前用 Read 工具检查每个文档,确认不含 \`<!-- SPEC-SKELETON -->\` 且所有表格有数据行\n\n`;
5685
- }
5686
- else {
5687
- // v6.61.0+: 一次性生成所有文档(global/ + {端}/)
5688
- prompt += `## 要求\n1. Read .speccore/PATTERNS/TEMPLATES/specs/ 下的专业模板(如目录不存在或为空,用你的专业知识自由撰写,绝不允许产出一行垃圾)\n`;
5689
- const templateMap = {
5690
- 'ANALYSIS.md': 'ANALYSIS-template.md', 'TECH.md': 'TECH-template.md', 'TEST.md': 'TEST-template.md',
5691
- 'REVIEW.md': 'REVIEW-template.md', 'RISK.md': 'RISK-template.md', 'DEPS.md': 'DEPS-template.md',
5692
- 'MONITOR.md': 'MONITOR-template.md', 'UI_SPEC.md': 'UI_SPEC-template.md'
5693
- };
5694
- for (const doc of taskDocs) {
5695
- const tpl = templateMap[doc[0]] || '';
5696
- prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
5697
- }
5698
- prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
5699
- prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
5700
- prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
5701
- prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
5702
- prompt += ` - 如不存在则跳过\n\n`;
5703
- // v8.3.160+: 迭代级分析结合源码上下文
5704
- if (ctx.withCode) {
5705
- prompt += `2b. **源码上下文(--with-code)**:\n`;
5706
- // v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
5707
- prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
5708
- prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
5709
- // v8.3.248+: 直接注入各端工程路径表(此前仅提示 AI「从 CONSTITUTION.md 获取源码路径」,
5710
- // AI 需自行翻表定位;现在直接给出,减少一步间接查找、杜绝瞎猜)
5711
- try {
5712
- const projectInfoMap = await (0, spec_paths_1.parseProjectInfo)();
5713
- if (projectInfoMap.size > 0) {
5714
- prompt += ` - **各端工程路径**(结合源码分析时按此定位,CONSTITUTION.md 工程表权威):\n`;
5715
- for (const [identifier, info] of projectInfoMap) {
5716
- prompt += ` - \`${identifier}\`(${info.projectType || '-'})→ \`${info.srcPath}\`\n`;
5717
- }
5334
+ // v6.61.0+: 一次性生成所有文档(global/ + {端}/)
5335
+ prompt += `## 要求\n1. Read .speccore/PATTERNS/TEMPLATES/specs/ 下的专业模板(如目录不存在或为空,用你的专业知识自由撰写,绝不允许产出一行垃圾)\n`;
5336
+ const templateMap = {
5337
+ 'ANALYSIS.md': 'ANALYSIS-template.md', 'TECH.md': 'TECH-template.md', 'TEST.md': 'TEST-template.md',
5338
+ 'REVIEW.md': 'REVIEW-template.md', 'RISK.md': 'RISK-template.md', 'DEPS.md': 'DEPS-template.md',
5339
+ 'MONITOR.md': 'MONITOR-template.md', 'UI_SPEC.md': 'UI_SPEC-template.md'
5340
+ };
5341
+ for (const doc of taskDocs) {
5342
+ const tpl = templateMap[doc[0]] || '';
5343
+ prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
5344
+ }
5345
+ prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
5346
+ prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
5347
+ prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
5348
+ prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
5349
+ prompt += ` - 如不存在则跳过\n\n`;
5350
+ // v8.3.160+: 迭代级分析结合源码上下文
5351
+ if (ctx.withCode) {
5352
+ prompt += `2b. **源码上下文(--with-code)**:\n`;
5353
+ // v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
5354
+ prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
5355
+ prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
5356
+ // v8.3.248+: 直接注入各端工程路径表(此前仅提示 AI「从 CONSTITUTION.md 获取源码路径」,
5357
+ // AI 需自行翻表定位;现在直接给出,减少一步间接查找、杜绝瞎猜)
5358
+ try {
5359
+ const projectInfoMap = await (0, spec_paths_1.parseProjectInfo)();
5360
+ if (projectInfoMap.size > 0) {
5361
+ prompt += ` - **各端工程路径**(结合源码分析时按此定位,CONSTITUTION.md 工程表权威):\n`;
5362
+ for (const [identifier, info] of projectInfoMap) {
5363
+ prompt += ` - \`${identifier}\`(${info.projectType || '-'})→ \`${info.srcPath}\`\n`;
5718
5364
  }
5719
5365
  }
5720
- catch { /* 路径解析失败不阻断分析 */ }
5721
- prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件,重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
5722
- }
5723
- prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
5724
- prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
5725
- prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
5726
- prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/SOURCE.md(旧项目为 README.md)\n`;
5727
- prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
5728
- prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
5729
- prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
5730
- prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
5731
- prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
5732
- prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
5733
- prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
5734
- prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
5735
- prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
5736
- prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
5737
- prompt += ` - **严禁推断**:不要从一句话推断出整个功能模块,只写文档中明确描述的内容\n`;
5738
- prompt += ` - **边界处理**:如果文档对某功能描述不完整,标注"文档未充分描述",不要自行脑补完整方案\n`;
5739
- prompt += ` - **交叉验证**:每写一个功能点,回头检查需求文档中是否有对应描述,没有则删除\n`;
5740
- prompt += `6. 每个文档都要具体内容(禁止"待填充")\n`;
5741
- prompt += `7. **辅助文档强制要求(v8.3.1+ 修复空模板问题)**:以下文档常被 AI 遗漏或敷衍,必须同等重视:\n`;
5742
- prompt += ` - **FUNCTION_MAP.md**:功能单元 × 端映射表,必须用 Markdown 表格,每行一个功能单元,不允许合并。表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
5743
- prompt += ` - **INTERACTION_MAP.md**:每个功能单元一个 Mermaid sequenceDiagram,展示跨端交互时序,箭头标注接口路径\n`;
5744
- prompt += ` - **DEV_GUIDE.md(迭代级)**:全局实施指导,必须包含:分支策略、改造范围清单(具体到文件路径)、改造顺序与依赖、代码级指引(含改造前后对比)、接口契约对照表、每步验证方式、回滚方案、常见坑点\n`;
5745
- prompt += ` - **MONITOR.md**:必须按 Fatal/Critical/Warning/Info 四级定义告警规则,不能只列指标名称\n`;
5746
- prompt += ` - **REVIEW.md**:安全检查必须逐接口列出鉴权需求,不能笼统写"需要鉴权"\n`;
5747
- prompt += ` ⛔ **自检规则**:生成完成后检查每个文档,如果仍含 \`<!-- SPEC-SKELETON -->\` 或空表格只有表头 → 必须重新填充\n`;
5748
- prompt += `8. **端发现(重要)**:先确定项目有哪些端,再按端组织文档\n`;
5749
- prompt += ` - 第 1 步:Read .speccore/CONSTITUTION.md\n`;
5750
- prompt += ` - 第 2 步:从「## 端列表」章节提取端名(这是全局权威来源)\n`;
5751
- prompt += ` - 第 3 步:如果没有「端列表」章节,从「对应需求端」列提取\n`;
5752
- prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
5753
- prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
5754
- // v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
5755
- // v8.3.160+: 压缩写作风格说明
5756
- prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
5757
- prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
5758
- prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
5759
- prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
5760
- prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
5761
- prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
5762
- // v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
5763
- // v6.71.3+: 增加「与全局层对比」列
5764
- prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
5765
- prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
5766
- prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
5767
- prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
5768
- prompt += ` - 「不涉及」= 只是提到、调用已有接口、纯展示 → 不标注\n`;
5769
- prompt += ` - 端名必须与 CONSTITUTION.md「端列表」中的标准端名完全匹配\n`;
5770
- prompt += ` - 「全局对比」:该功能模块与全局层已有功能的关系(必须标注)\n`;
5771
- prompt += ` - 「新增」:全局层不存在,本迭代全新开发\n`;
5772
- prompt += ` - 「扩展」:全局层已有基础功能,本迭代增加新字段/新接口/新页面\n`;
5773
- prompt += ` - 「重构」:全局层已有,本迭代修改实现方式(不新增功能)\n`;
5774
- prompt += ` - 「复用」:全局层已有,本迭代直接使用(无需开发)\n`;
5775
- prompt += ` - 示例:「扩展:增加会议室设备管理」或「新增」\n`;
5776
- prompt += ` - 「来源」:该功能模块在需求文档中的具体位置,用 Markdown 链接格式\n`;
5777
- prompt += ` - 格式:[文档名](相对路径#章节锚点),如 [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#3-2-会议室管理)\n`;
5778
- prompt += ` - 如果模块来自 converted 文档:[xxx需求](../010-requirements/converted/xxx.md#相关章节)\n`;
5779
- prompt += ` - 如果来自 features 目录:[xxx功能](../010-requirements/features/xxx/SOURCE.md)\n`;
5780
- prompt += ` - 目的是让阅读者能直接点击跳转到原始需求位置\n`;
5781
- prompt += ` - 示例:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展:增加设备管理 | [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#2-1) | 会议室 CRUD、设备管理 |\n`;
5782
- prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
5783
- // v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
5784
- // v6.71.3+: 增加与全局层关联分析
5785
- prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
5786
- prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
5787
- prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
5788
- prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
5789
- prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
5790
- prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
5791
- prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
5792
- prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
5793
- prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
5794
- prompt += ` - 「涉及端」标注所有需要新开发工作的端(标准端名,逗号分隔)\n`;
5795
- prompt += ` - 「全局对比」标注与全局层的关系(新增/扩展/重构/复用)\n`;
5796
- prompt += ` - 「共享能力」标注跨任务共享的通用组件(如"审批引擎"、"消息通知"),无则填"无"\n`;
5797
- prompt += ` - 「依赖任务」标注当前迭代内该功能单元依赖的其他功能单元编号\n`;
5798
- prompt += ` - 「说明」描述跨端交互关系(如"admin-web 调用 booking-service 的 /api/rooms 接口")\n`;
5799
- prompt += ` - **示例**:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展 | 无 | 无 | admin-web 调用 room-service CRUD 接口 |\n`;
5800
- prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
5801
- prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
5802
- // v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
5803
- prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
5804
- prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
5805
- prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
5806
- prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
5807
- prompt += ` - 箭头标注接口路径,标注 [contract] 表示接口在 API_CONTRACT.yaml 中有定义\n`;
5808
- prompt += ` - 序列图后附「接口契约索引」表格:步骤号、接口、消费者端、提供者端\n`;
5809
- prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
5810
- prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
5811
- // 注入工程类型信息(v6.49.0+)
5812
- // v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
5813
- const platformTypes = await (0, spec_paths_2.parsePlatformTypes)();
5814
- if (platformTypes.size > 0) {
5815
- prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
5816
- prompt += ` | 工程标识 | 工程类型 |\n`;
5817
- prompt += ` | :--- | :--- |\n`;
5818
- for (const [name, type] of platformTypes) {
5819
- prompt += ` | ${name} | ${type} |\n`;
5820
- }
5821
- prompt += `\n`;
5822
5366
  }
5823
- // v8.3.182+: 显式文档路由表
5824
- prompt += `## 📋 文档路由表(v8.3.182+)\n\n`;
5825
- prompt += `> 以下表格明确每类文档的写入路径。索引类放 overview/,内容类放 {feature}/overview/,端专属放 {feature}/{端}/。\n\n`;
5826
- prompt += `| 文档名 | 路由目标 | 文档类型 | 说明 |\n`;
5827
- prompt += `| :--- | :--- | :--- | :--- |\n`;
5828
- prompt += `| FUNCTION_MAP.md | 020-specs/overview/ | 索引类 | 功能单元 × 端映射表(全局) |\n`;
5829
- prompt += `| INTERACTION_MAP.md | 020-specs/overview/ | 索引类 | 跨端交互时序图(全局) |\n`;
5830
- prompt += `| PLATFORMS.md | 020-specs/overview/ | 索引类 | 端列表(全局) |\n`;
5831
- prompt += `| CLARIFY.md | 020-specs/overview/ | 索引类 | 全局澄清(跨功能共性问题) |\n`;
5832
- prompt += `| API_CONTRACT.yaml | 020-specs/overview/ | 索引类 | 跨功能接口契约(全局) |\n`;
5833
- prompt += `| ARCHITECTURE.md | 020-specs/overview/ | 索引类 | 迭代全局架构(服务拓扑/数据流) |\n`;
5834
- prompt += `| REQUIREMENT.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求规格 |\n`;
5835
- prompt += `| ANALYSIS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求分析 |\n`;
5836
- prompt += `| TECH.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的技术方案(Phase 1) |\n`;
5837
- prompt += `| RISK.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的风险评估 |\n`;
5838
- prompt += `| DEPS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的依赖清单 |\n`;
5839
- prompt += `| REVIEW.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的评审检查项 |\n`;
5840
- prompt += `| MONITOR.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的监控指标 |\n`;
5841
- prompt += `| DEV_GUIDE.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的实现指南(Phase 1) |\n`;
5842
- prompt += `| TECH.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的技术方案(Phase 2) |\n`;
5843
- prompt += `| TEST.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的测试用例(Phase 2) |\n`;
5844
- prompt += `| UI_SPEC.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的 UI 规范(Phase 2) |\n`;
5845
- prompt += `| DEV_GUIDE.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的实现指南(Phase 2) |\n\n`;
5846
- prompt += `> **重要规则**:\n`;
5847
- prompt += `> - 索引类文档(FUNCTION_MAP.md / INTERACTION_MAP.md / PLATFORMS.md / CLARIFY.md / API_CONTRACT.yaml)**必须**写入 020-specs/overview/,严禁写入 {feature}/overview/\n`;
5848
- prompt += `> - 内容类文档(REQUIREMENT.md / ANALYSIS.md / TECH.md / RISK.md / DEPS.md / REVIEW.md / MONITOR.md / DEV_GUIDE.md)**必须**写入 020-specs/{feature}/overview/,严禁写入 020-specs/overview/\n`;
5849
- prompt += `> - ANALYSIS.md 属于内容类,严禁放在 020-specs/overview/ 下\n`;
5850
- prompt += `> - 全局 overview/REQUIREMENT.md 由 split 阶段从各功能模块 {feature}/overview/REQUIREMENT.md 自动聚合生成,AI 不要直接写入(会被拦截)\n\n`;
5851
- const dirStepNum = platformTypes.size > 0 ? 10 : 9;
5852
- prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
5853
- prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
5854
- prompt += ` - FUNCTION_MAP.md(功能单元 × 端映射表)\n`;
5855
- prompt += ` - INTERACTION_MAP.md(跨端交互时序图)\n`;
5856
- prompt += ` - PLATFORMS.md(端列表)\n`;
5857
- prompt += ` - **功能模块综合文档**(每个功能模块独立)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/overview/{文件名}\`\n`;
5858
- prompt += ` - {功能模块}/overview/REQUIREMENT.md(该模块的需求规格)\n`;
5859
- prompt += ` - {功能模块}/overview/ANALYSIS.md(该模块的需求分析)\n`;
5860
- prompt += ` - {功能模块}/overview/TECH.md(该模块的技术方案)\n`;
5861
- prompt += ` - {功能模块}/overview/RISK.md(该模块的风险评估)\n`;
5862
- prompt += ` - {功能模块}/overview/DEPS.md(该模块的依赖清单)\n`;
5863
- prompt += ` - {功能模块}/overview/REVIEW.md(该模块的评审检查项)\n`;
5864
- prompt += ` - {功能模块}/overview/MONITOR.md(该模块的监控指标)\n`;
5865
- prompt += ` - {功能模块}/overview/DEV_GUIDE.md(该模块的实现指南)\n`;
5866
- prompt += ` - **端专属文档**(按功能模块组织)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/{端名}/{文件名}\`\n`;
5867
- prompt += ` - {功能模块}/{端}/TECH.md(该模块该端的技术方案)\n`;
5868
- prompt += ` - {功能模块}/{端}/TEST.md(该模块该端的测试用例)\n`;
5869
- prompt += ` - {功能模块}/{端}/UI_SPEC.md(该模块该端的 UI 规范,仅前端端)\n`;
5870
- prompt += ` - {功能模块}/{端}/DEV_GUIDE.md(该模块该端的实现指南)\n`;
5871
- // v8.3.171+: 简化路径约束(骨架已预创建,CLI 掌控路径)
5872
- prompt += ` - 文件已预创建,用 Write 工具覆盖已有路径即可,不要创建新文件或新目录\n`;
5873
- if (ctx.phase !== '1') {
5874
- // 端专业性约束只在默认模式(全量)中输出
5875
- prompt += `\n## ⚠️ 端专业性约束\n`;
5876
- prompt += `CONSTITUTION.md 中配置了多个端,每个端的文档必须有该端专属内容。\n`;
5877
- prompt += `**先识别端类型,再应用对应的专业维度**:\n\n`;
5878
- // v6.71.0+: 前后端文档差异化
5879
- prompt += `### 后端服务(*service)必含内容 — 技术视角\n`;
5880
- prompt += `- API 接口定义(路径/方法/参数/响应字段/状态码/错误码)\n`;
5881
- prompt += `- 数据库表结构(字段/类型/索引/约束)\n`;
5882
- prompt += `- 业务规则(含边界条件和异常流)\n`;
5883
- prompt += `- 缓存策略/并发与事务/消息队列(如涉及)\n`;
5884
- prompt += `- 安全:SQL 注入防护/接口鉴权/数据脱敏\n`;
5885
- prompt += `- 性能:QPS 预估/慢查询优化/连接池配置\n`;
5886
- prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 {功能模块}/overview/REQUIREMENT.md 中)\n\n`;
5887
- prompt += `### 前端端(h5 / admin-web / miniapp)必含内容 — 产品+技术双视角\n`;
5888
- prompt += `- **产品视角(主要)**:\n`;
5889
- prompt += ` - 用户旅程:该端用户如何完成核心任务(步骤流程图)\n`;
5890
- prompt += ` - 页面清单:所有页面名称、路径、核心功能、入口位置\n`;
5891
- prompt += ` - 交互设计:关键操作流程、状态变化、异常提示方式\n`;
5892
- prompt += ` - 字段展示:每个页面展示哪些字段、字段来源(后端哪个接口)\n`;
5893
- prompt += ` - 权限控制:哪些页面/按钮需要权限、权限粒度\n`;
5894
- prompt += `- **技术视角(辅助)**:\n`;
5895
- prompt += ` - 页面路由表 + 组件清单\n`;
5896
- prompt += ` - API 调用清单:前端调用了哪些后端接口(路径+方法+用途)\n`;
5897
- prompt += ` - 状态管理:全局状态设计、与后端数据同步策略\n`;
5898
- prompt += ` - 适配/性能/安全等前端专项(按端类型选择)\n\n`;
5367
+ catch { /* 路径解析失败不阻断分析 */ }
5368
+ prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件,重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
5369
+ }
5370
+ prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
5371
+ prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
5372
+ prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
5373
+ prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/SOURCE.md(旧项目为 README.md)\n`;
5374
+ prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
5375
+ prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
5376
+ prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
5377
+ prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
5378
+ prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
5379
+ prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
5380
+ prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
5381
+ prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
5382
+ prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
5383
+ prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
5384
+ prompt += ` - **严禁推断**:不要从一句话推断出整个功能模块,只写文档中明确描述的内容\n`;
5385
+ prompt += ` - **边界处理**:如果文档对某功能描述不完整,标注"文档未充分描述",不要自行脑补完整方案\n`;
5386
+ prompt += ` - **交叉验证**:每写一个功能点,回头检查需求文档中是否有对应描述,没有则删除\n`;
5387
+ prompt += `6. 每个文档都要具体内容(禁止"待填充")\n`;
5388
+ prompt += `7. **辅助文档强制要求(v8.3.1+ 修复空模板问题)**:以下文档常被 AI 遗漏或敷衍,必须同等重视:\n`;
5389
+ prompt += ` - **FUNCTION_MAP.md**:功能单元 × 端映射表,必须用 Markdown 表格,每行一个功能单元,不允许合并。表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
5390
+ prompt += ` - **INTERACTION_MAP.md**:每个功能单元一个 Mermaid sequenceDiagram,展示跨端交互时序,箭头标注接口路径\n`;
5391
+ prompt += ` - **DEV_GUIDE.md(迭代级)**:全局实施指导,必须包含:分支策略、改造范围清单(具体到文件路径)、改造顺序与依赖、代码级指引(含改造前后对比)、接口契约对照表、每步验证方式、回滚方案、常见坑点\n`;
5392
+ prompt += ` - **MONITOR.md**:必须按 Fatal/Critical/Warning/Info 四级定义告警规则,不能只列指标名称\n`;
5393
+ prompt += ` - **REVIEW.md**:安全检查必须逐接口列出鉴权需求,不能笼统写"需要鉴权"\n`;
5394
+ prompt += ` ⛔ **自检规则**:生成完成后检查每个文档,如果仍含 \`<!-- SPEC-SKELETON -->\` 或空表格只有表头 → 必须重新填充\n`;
5395
+ prompt += `8. **端发现(重要)**:先确定项目有哪些端,再按端组织文档\n`;
5396
+ prompt += ` - 第 1 步:Read .speccore/CONSTITUTION.md\n`;
5397
+ prompt += ` - 第 2 步:从「## 端列表」章节提取端名(这是全局权威来源)\n`;
5398
+ prompt += ` - 第 3 步:如果没有「端列表」章节,从「对应需求端」列提取\n`;
5399
+ prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
5400
+ prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
5401
+ // v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
5402
+ // v8.3.160+: 压缩写作风格说明
5403
+ prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
5404
+ prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
5405
+ prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
5406
+ prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
5407
+ prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
5408
+ prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
5409
+ // v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
5410
+ // v6.71.3+: 增加「与全局层对比」列
5411
+ prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
5412
+ prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
5413
+ prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
5414
+ prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
5415
+ prompt += ` - 「不涉及」= 只是提到、调用已有接口、纯展示 → 不标注\n`;
5416
+ prompt += ` - 端名必须与 CONSTITUTION.md「端列表」中的标准端名完全匹配\n`;
5417
+ prompt += ` - 「全局对比」:该功能模块与全局层已有功能的关系(必须标注)\n`;
5418
+ prompt += ` - 「新增」:全局层不存在,本迭代全新开发\n`;
5419
+ prompt += ` - 「扩展」:全局层已有基础功能,本迭代增加新字段/新接口/新页面\n`;
5420
+ prompt += ` - 「重构」:全局层已有,本迭代修改实现方式(不新增功能)\n`;
5421
+ prompt += ` - 「复用」:全局层已有,本迭代直接使用(无需开发)\n`;
5422
+ prompt += ` - 示例:「扩展:增加会议室设备管理」或「新增」\n`;
5423
+ prompt += ` - 「来源」:该功能模块在需求文档中的具体位置,用 Markdown 链接格式\n`;
5424
+ prompt += ` - 格式:[文档名](相对路径#章节锚点),如 [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#3-2-会议室管理)\n`;
5425
+ prompt += ` - 如果模块来自 converted 文档:[xxx需求](../010-requirements/converted/xxx.md#相关章节)\n`;
5426
+ prompt += ` - 如果来自 features 目录:[xxx功能](../010-requirements/features/xxx/SOURCE.md)\n`;
5427
+ prompt += ` - 目的是让阅读者能直接点击跳转到原始需求位置\n`;
5428
+ prompt += ` - 示例:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展:增加设备管理 | [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#2-1) | 会议室 CRUD、设备管理 |\n`;
5429
+ prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
5430
+ // v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
5431
+ // v6.71.3+: 增加与全局层关联分析
5432
+ prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
5433
+ prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
5434
+ prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
5435
+ prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
5436
+ prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
5437
+ prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
5438
+ prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
5439
+ prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
5440
+ prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
5441
+ prompt += ` - 「涉及端」标注所有需要新开发工作的端(标准端名,逗号分隔)\n`;
5442
+ prompt += ` - 「全局对比」标注与全局层的关系(新增/扩展/重构/复用)\n`;
5443
+ prompt += ` - 「共享能力」标注跨任务共享的通用组件(如"审批引擎"、"消息通知"),无则填"无"\n`;
5444
+ prompt += ` - 「依赖任务」标注当前迭代内该功能单元依赖的其他功能单元编号\n`;
5445
+ prompt += ` - 「说明」描述跨端交互关系(如"admin-web 调用 booking-service 的 /api/rooms 接口")\n`;
5446
+ prompt += ` - **示例**:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展 | 无 | 无 | admin-web 调用 room-service CRUD 接口 |\n`;
5447
+ prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
5448
+ prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
5449
+ // v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
5450
+ prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
5451
+ prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
5452
+ prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
5453
+ prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
5454
+ prompt += ` - 箭头标注接口路径,标注 [contract] 表示接口在 API_CONTRACT.yaml 中有定义\n`;
5455
+ prompt += ` - 序列图后附「接口契约索引」表格:步骤号、接口、消费者端、提供者端\n`;
5456
+ prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
5457
+ prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
5458
+ // 注入工程类型信息(v6.49.0+)
5459
+ // v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
5460
+ const platformTypes = await (0, spec_paths_2.parsePlatformTypes)();
5461
+ if (platformTypes.size > 0) {
5462
+ prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
5463
+ prompt += ` | 工程标识 | 工程类型 |\n`;
5464
+ prompt += ` | :--- | :--- |\n`;
5465
+ for (const [name, type] of platformTypes) {
5466
+ prompt += ` | ${name} | ${type} |\n`;
5899
5467
  }
5468
+ prompt += `\n`;
5469
+ }
5470
+ // v8.3.182+: 显式文档路由表
5471
+ prompt += `## 📋 文档路由表(v8.3.182+)\n\n`;
5472
+ prompt += `> 以下表格明确每类文档的写入路径。索引类放 overview/,内容类放 {feature}/overview/,端专属放 {feature}/{端}/。\n\n`;
5473
+ prompt += `| 文档名 | 路由目标 | 文档类型 | 说明 |\n`;
5474
+ prompt += `| :--- | :--- | :--- | :--- |\n`;
5475
+ prompt += `| FUNCTION_MAP.md | 020-specs/overview/ | 索引类 | 功能单元 × 端映射表(全局) |\n`;
5476
+ prompt += `| INTERACTION_MAP.md | 020-specs/overview/ | 索引类 | 跨端交互时序图(全局) |\n`;
5477
+ prompt += `| PLATFORMS.md | 020-specs/overview/ | 索引类 | 端列表(全局) |\n`;
5478
+ prompt += `| CLARIFY.md | 020-specs/overview/ | 索引类 | 全局澄清(跨功能共性问题) |\n`;
5479
+ prompt += `| API_CONTRACT.yaml | 020-specs/overview/ | 索引类 | 跨功能接口契约(全局) |\n`;
5480
+ prompt += `| ARCHITECTURE.md | 020-specs/overview/ | 索引类 | 迭代全局架构(服务拓扑/数据流) |\n`;
5481
+ prompt += `| REQUIREMENT.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求规格 |\n`;
5482
+ prompt += `| ANALYSIS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求分析 |\n`;
5483
+ prompt += `| TECH.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的技术方案(Phase 1) |\n`;
5484
+ prompt += `| RISK.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的风险评估 |\n`;
5485
+ prompt += `| DEPS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的依赖清单 |\n`;
5486
+ prompt += `| REVIEW.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的评审检查项 |\n`;
5487
+ prompt += `| MONITOR.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的监控指标 |\n`;
5488
+ prompt += `| DEV_GUIDE.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的实现指南(Phase 1) |\n`;
5489
+ prompt += `| TECH.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的技术方案(Phase 2) |\n`;
5490
+ prompt += `| TEST.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的测试用例(Phase 2) |\n`;
5491
+ prompt += `| UI_SPEC.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的 UI 规范(Phase 2) |\n`;
5492
+ prompt += `| DEV_GUIDE.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的实现指南(Phase 2) |\n\n`;
5493
+ prompt += `> **重要规则**:\n`;
5494
+ prompt += `> - 索引类文档(FUNCTION_MAP.md / INTERACTION_MAP.md / PLATFORMS.md / CLARIFY.md / API_CONTRACT.yaml)**必须**写入 020-specs/overview/,严禁写入 {feature}/overview/\n`;
5495
+ prompt += `> - 内容类文档(REQUIREMENT.md / ANALYSIS.md / TECH.md / RISK.md / DEPS.md / REVIEW.md / MONITOR.md / DEV_GUIDE.md)**必须**写入 020-specs/{feature}/overview/,严禁写入 020-specs/overview/\n`;
5496
+ prompt += `> - ANALYSIS.md 属于内容类,严禁放在 020-specs/overview/ 下\n`;
5497
+ prompt += `> - 全局 overview/REQUIREMENT.md 由 split 阶段从各功能模块 {feature}/overview/REQUIREMENT.md 自动聚合生成,AI 不要直接写入(会被拦截)\n\n`;
5498
+ const dirStepNum = platformTypes.size > 0 ? 10 : 9;
5499
+ prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
5500
+ prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
5501
+ prompt += ` - FUNCTION_MAP.md(功能单元 × 端映射表)\n`;
5502
+ prompt += ` - INTERACTION_MAP.md(跨端交互时序图)\n`;
5503
+ prompt += ` - PLATFORMS.md(端列表)\n`;
5504
+ prompt += ` - **功能模块综合文档**(每个功能模块独立)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/overview/{文件名}\`\n`;
5505
+ prompt += ` - {功能模块}/overview/REQUIREMENT.md(该模块的需求规格)\n`;
5506
+ prompt += ` - {功能模块}/overview/ANALYSIS.md(该模块的需求分析)\n`;
5507
+ prompt += ` - {功能模块}/overview/TECH.md(该模块的技术方案)\n`;
5508
+ prompt += ` - {功能模块}/overview/RISK.md(该模块的风险评估)\n`;
5509
+ prompt += ` - {功能模块}/overview/DEPS.md(该模块的依赖清单)\n`;
5510
+ prompt += ` - {功能模块}/overview/REVIEW.md(该模块的评审检查项)\n`;
5511
+ prompt += ` - {功能模块}/overview/MONITOR.md(该模块的监控指标)\n`;
5512
+ prompt += ` - {功能模块}/overview/DEV_GUIDE.md(该模块的实现指南)\n`;
5513
+ prompt += ` - **端专属文档**(按功能模块组织)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/{端名}/{文件名}\`\n`;
5514
+ prompt += ` - {功能模块}/{端}/TECH.md(该模块该端的技术方案)\n`;
5515
+ prompt += ` - {功能模块}/{端}/TEST.md(该模块该端的测试用例)\n`;
5516
+ prompt += ` - {功能模块}/{端}/UI_SPEC.md(该模块该端的 UI 规范,仅前端端)\n`;
5517
+ prompt += ` - {功能模块}/{端}/DEV_GUIDE.md(该模块该端的实现指南)\n`;
5518
+ // v8.3.171+: 简化路径约束(骨架已预创建,CLI 掌控路径)
5519
+ prompt += ` - 文件已预创建,用 Write 工具覆盖已有路径即可,不要创建新文件或新目录\n`;
5520
+ if (ctx.phase !== '1') {
5521
+ // 端专业性约束只在默认模式(全量)中输出
5522
+ prompt += `\n## ⚠️ 端专业性约束\n`;
5523
+ prompt += `CONSTITUTION.md 中配置了多个端,每个端的文档必须有该端专属内容。\n`;
5524
+ prompt += `**先识别端类型,再应用对应的专业维度**:\n\n`;
5525
+ // v6.71.0+: 前后端文档差异化
5526
+ prompt += `### 后端服务(*service)必含内容 — 技术视角\n`;
5527
+ prompt += `- API 接口定义(路径/方法/参数/响应字段/状态码/错误码)\n`;
5528
+ prompt += `- 数据库表结构(字段/类型/索引/约束)\n`;
5529
+ prompt += `- 业务规则(含边界条件和异常流)\n`;
5530
+ prompt += `- 缓存策略/并发与事务/消息队列(如涉及)\n`;
5531
+ prompt += `- 安全:SQL 注入防护/接口鉴权/数据脱敏\n`;
5532
+ prompt += `- 性能:QPS 预估/慢查询优化/连接池配置\n`;
5533
+ prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 {功能模块}/overview/REQUIREMENT.md 中)\n\n`;
5534
+ prompt += `### 前端端(h5 / admin-web / miniapp)必含内容 — 产品+技术双视角\n`;
5535
+ prompt += `- **产品视角(主要)**:\n`;
5536
+ prompt += ` - 用户旅程:该端用户如何完成核心任务(步骤流程图)\n`;
5537
+ prompt += ` - 页面清单:所有页面名称、路径、核心功能、入口位置\n`;
5538
+ prompt += ` - 交互设计:关键操作流程、状态变化、异常提示方式\n`;
5539
+ prompt += ` - 字段展示:每个页面展示哪些字段、字段来源(后端哪个接口)\n`;
5540
+ prompt += ` - 权限控制:哪些页面/按钮需要权限、权限粒度\n`;
5541
+ prompt += `- **技术视角(辅助)**:\n`;
5542
+ prompt += ` - 页面路由表 + 组件清单\n`;
5543
+ prompt += ` - API 调用清单:前端调用了哪些后端接口(路径+方法+用途)\n`;
5544
+ prompt += ` - 状态管理:全局状态设计、与后端数据同步策略\n`;
5545
+ prompt += ` - 适配/性能/安全等前端专项(按端类型选择)\n\n`;
5900
5546
  }
5901
5547
  // v6.60.0+: 文档与端的对应关系(不再分 Phase)
5902
5548
  // v6.71.0+: 前后端文档差异化
@@ -5924,16 +5570,13 @@ status: "clarified"
5924
5570
  prompt += `- 分析完成后会自动生成 QUALITY_AUDIT.md 质量报告,检查各端内容是否完整\n`;
5925
5571
  }
5926
5572
  // 步骤 2-7 已在上面的 phase 分支中处理
5927
- const taskFlag = isTask && ctx.task ? ` --task ${ctx.task}` : '';
5928
5573
  const platformFlag = ctx.platform ? ` --platform ${ctx.platform}` : '';
5929
5574
  // ── 链式生成指令(v6.45.0+)──
5930
- if (hasUserTemplates || isTask) {
5575
+ if (hasUserTemplates) {
5931
5576
  prompt += `\n## 🔗 链式生成(重要)\n\n`;
5932
5577
  prompt += `请按以下顺序**逐个生成**文档,每个文档生成后立即 --apply 写入,然后 Read 自己的产出再生成下一个:\n\n`;
5933
5578
  // 定义生成顺序
5934
- const chainOrder = isTask
5935
- ? ['REQ.md', 'TECH.md', 'SCHEMA.md', 'TASK.md']
5936
- : ['REQUIREMENT.md', 'FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'API_CONTRACT.yaml', 'ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'UI_SPEC.md', 'DEV_GUIDE.md'];
5579
+ const chainOrder = ['REQUIREMENT.md', 'FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'API_CONTRACT.yaml', 'ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'UI_SPEC.md', 'DEV_GUIDE.md'];
5937
5580
  const orderedDocs = taskDocs.filter(([n]) => chainOrder.includes(n));
5938
5581
  const customDocs = taskDocs.filter(([n]) => !chainOrder.includes(n));
5939
5582
  const ordered = [...orderedDocs.sort((a, b) => chainOrder.indexOf(a[0]) - chainOrder.indexOf(b[0])), ...customDocs];
@@ -5947,19 +5590,19 @@ status: "clarified"
5947
5590
  }
5948
5591
  prompt += `\n每个文档用单独的 --apply 调用写入:\n`;
5949
5592
  for (const doc of ordered) {
5950
- prompt += `speccore analyze --apply '{"${doc[0]}":"..."}' -I ${iter}${taskFlag}${platformFlag}\n`;
5593
+ prompt += `speccore analyze --apply '{"${doc[0]}":"..."}' -I ${iter}${platformFlag}\n`;
5951
5594
  }
5952
5595
  prompt += `\n`;
5953
5596
  }
5954
5597
  else if (ctx.phase !== '2') {
5955
5598
  // 传统模式:一次性写入
5956
- prompt += `7. 写入: speccore analyze --apply '{${taskDocs.map(([n]) => `"${n}":"..."`).join(',')}}' -I ${iter}${taskFlag}${platformFlag}\n\n`;
5599
+ prompt += `7. 写入: speccore analyze --apply '{${taskDocs.map(([n]) => `"${n}":"..."`).join(',')}}' -I ${iter}${platformFlag}\n\n`;
5957
5600
  }
5958
5601
  // v6.60.0+: 移除两阶段分析提示,一次性生成所有文档
5959
5602
  prompt += '\n' + (0, questions_1.buildAutoModeInstruction)('analyze', iter) + '\n';
5960
5603
  // ── v6.65.0+: Phase 1 完成后主动询问用户是否继续 Phase 2 ──
5961
5604
  // v6.70.0+: 自动模式下跳过确认,AI 直接推断执行
5962
- if (!ctx.phase && !isTask && !autoMode) {
5605
+ if (!ctx.phase && !autoMode) {
5963
5606
  prompt += `\n## ⚠️ 重要:Phase 1 完成后的下一步\n\n`;
5964
5607
  prompt += `当你通过 --apply 写入所有综合文档后,CLI 会检测到项目有多个端(≥2 个端)。\n`;
5965
5608
  prompt += `**此时你需要主动询问用户**:\n\n`;
@@ -5970,7 +5613,7 @@ status: "clarified"
5970
5613
  prompt += `\`speccore analyze --prompt -I ${iter} --phase 2\`\n\n`;
5971
5614
  prompt += `**不要等待 CLI 的提示信息**,CLI 的输出你可能看不到。你需要主动询问用户。\n\n`;
5972
5615
  }
5973
- if (autoMode && !ctx.phase && !isTask) {
5616
+ if (autoMode && !ctx.phase) {
5974
5617
  prompt += `\n## 🤖 自动模式说明\n\n`;
5975
5618
  prompt += `当前处于自动模式(--auto),不需要人工确认。\n`;
5976
5619
  prompt += `请在生成综合文档后,**直接继续**生成各端专属文档(Phase 2)。\n`;
@@ -6007,7 +5650,7 @@ status: "clarified"
6007
5650
  }
6008
5651
  }
6009
5652
  // ── v6.52.0+: 图谱 RAG 上下文注入(analyze 阶段也检索项目关联内容)──
6010
- if (!isTask && ctx.phase !== '2') {
5653
+ if (ctx.phase !== '2') {
6011
5654
  try {
6012
5655
  const ragResult = await (0, unified_retrieval_1.unifiedSearch)(((0, task_utils_1.findProjectRoot)() || process.cwd()), {
6013
5656
  query: ctx.iteration || '',
@@ -6252,26 +5895,6 @@ async function buildContractFirstPrompt(iteration) {
6252
5895
  prompt += `- 契约应**精确且完整**,避免后续各端分析时出现接口不一致\n\n`;
6253
5896
  return await injectGraphSummary(prompt);
6254
5897
  }
6255
- // ── v6.49.9+: 扫描平铺的端目录,返回所有子任务目录路径 ──
6256
- async function getSubtaskDirs(taskDir) {
6257
- const result = [];
6258
- const platformList = await (0, spec_paths_2.parsePlatformList)();
6259
- for (const platform of platformList) {
6260
- const platDir = (0, path_1.join)(taskDir, platform);
6261
- if (!(await (0, fs_extra_1.pathExists)(platDir)))
6262
- continue;
6263
- try {
6264
- const entries = await (0, fs_extra_2.readdir)(platDir, { withFileTypes: true });
6265
- for (const entry of entries) {
6266
- if (entry.isDirectory() && !entry.name.startsWith('.')) {
6267
- result.push((0, path_1.join)(platDir, entry.name));
6268
- }
6269
- }
6270
- }
6271
- catch { /* ignore */ }
6272
- }
6273
- return result;
6274
- }
6275
5898
  // ═══════════════════════════════════════════════════════════
6276
5899
  // ═══════════════════════════════════════════════════════════
6277
5900
  /**
@@ -7007,7 +6630,7 @@ async function buildPipelineStepPrompt(args) {
7007
6630
  // 全局层 Pipeline 步骤映射
7008
6631
  if (currentStep === 'init' || currentStep === 'discovery') {
7009
6632
  prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
7010
- iteration: iter, task: options.task, platform: options.platform,
6633
+ iteration: iter, platform: options.platform,
7011
6634
  analyzeOptions: options,
7012
6635
  }));
7013
6636
  }
@@ -7040,7 +6663,7 @@ async function buildPipelineStepPrompt(args) {
7040
6663
  }
7041
6664
  else if (currentStep === 'phase1-prompt') {
7042
6665
  prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
7043
- iteration: iter, task: options.task, platform: options.platform,
6666
+ iteration: iter, platform: options.platform,
7044
6667
  analyzeOptions: options,
7045
6668
  }));
7046
6669
  }
@@ -7074,7 +6697,7 @@ async function buildPipelineStepPrompt(args) {
7074
6697
  else {
7075
6698
  // 回退到默认 prompt
7076
6699
  prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
7077
- iteration: iter, task: options.task, platform: options.platform,
6700
+ iteration: iter, platform: options.platform,
7078
6701
  analyzeOptions: options,
7079
6702
  }));
7080
6703
  }
@@ -7302,133 +6925,6 @@ async function buildAndSaveQualityReport(iterDir, iter, units) {
7302
6925
  logger_1.logger.info(` 📊 质量报告已落盘: 020-specs/QUALITY_AUDIT.md(${okCount} 成功 / ${failCount} 失败,平均 ${avgScore} 分)`);
7303
6926
  return reportPath;
7304
6927
  }
7305
- // ═══════════════════════════════════════════════════════════
7306
- // 多子任务分析支持(v8.3.94+)
7307
- // ═══════════════════════════════════════════════════════════
7308
- /** 解析逗号分隔的任务 ID */
7309
- function parseTaskIds(taskOpt) {
7310
- return taskOpt
7311
- .split(',')
7312
- .map(t => t.trim())
7313
- .filter(Boolean)
7314
- .map(t => t.startsWith('Task-') ? t : `Task-${t}`);
7315
- }
7316
- /**
7317
- * 根据过滤条件扫描迭代目录下的任务
7318
- * 支持: status:doing | owner:张三 | type:feature | platform:web | keyword:auth
7319
- */
7320
- async function filterTasks(iterDir, filter) {
7321
- const tasksDir = (0, path_1.join)(iterDir, '030-tasks');
7322
- if (!await (0, fs_extra_1.pathExists)(tasksDir))
7323
- return [];
7324
- const entries = await (0, fs_extra_2.readdir)(tasksDir, { withFileTypes: true });
7325
- const taskDirs = entries.filter(e => e.isDirectory() && e.name.startsWith('Task-')).map(e => e.name);
7326
- if (taskDirs.length === 0)
7327
- return [];
7328
- const matched = [];
7329
- for (const taskId of taskDirs) {
7330
- const taskDir = (0, path_1.join)(tasksDir, taskId);
7331
- const metaDir = (0, path_1.join)(taskDir, '.meta');
7332
- // 解析过滤条件
7333
- const filterLower = filter.toLowerCase();
7334
- // 1. 状态过滤: status:doing
7335
- if (filterLower.startsWith('status:')) {
7336
- const statusFile = (0, path_1.join)(metaDir, 'status');
7337
- if (await (0, fs_extra_1.pathExists)(statusFile)) {
7338
- const status = (await (0, fs_extra_2.readFile)(statusFile, 'utf-8')).trim().toLowerCase();
7339
- if (status === filterLower.slice(7).trim())
7340
- matched.push(taskId);
7341
- }
7342
- continue;
7343
- }
7344
- // 2. 负责人过滤: owner:张三
7345
- if (filterLower.startsWith('owner:')) {
7346
- const ownerFile = (0, path_1.join)(metaDir, 'owner');
7347
- if (await (0, fs_extra_1.pathExists)(ownerFile)) {
7348
- const owner = (await (0, fs_extra_2.readFile)(ownerFile, 'utf-8')).trim();
7349
- if (owner.includes(filter.slice(6).trim()))
7350
- matched.push(taskId);
7351
- }
7352
- continue;
7353
- }
7354
- // 3. 类型过滤: type:feature
7355
- if (filterLower.startsWith('type:')) {
7356
- const typeFile = (0, path_1.join)(metaDir, 'type');
7357
- if (await (0, fs_extra_1.pathExists)(typeFile)) {
7358
- const type = (await (0, fs_extra_2.readFile)(typeFile, 'utf-8')).trim().toLowerCase();
7359
- if (type === filterLower.slice(5).trim())
7360
- matched.push(taskId);
7361
- }
7362
- continue;
7363
- }
7364
- // 4. 端过滤: platform:web
7365
- if (filterLower.startsWith('platform:')) {
7366
- const targetPlat = filterLower.slice(9).trim();
7367
- try {
7368
- const subEntries = await (0, fs_extra_2.readdir)(taskDir, { withFileTypes: true });
7369
- const hasPlatform = subEntries.some(e => e.isDirectory() &&
7370
- !e.name.startsWith('.') &&
7371
- e.name !== '_shared' &&
7372
- e.name !== '00-specs' &&
7373
- e.name.toLowerCase().includes(targetPlat));
7374
- if (hasPlatform)
7375
- matched.push(taskId);
7376
- }
7377
- catch { /* ignore */ }
7378
- continue;
7379
- }
7380
- // 5. 关键词过滤: keyword:auth(或无前缀的默认行为)
7381
- const keyword = filterLower.startsWith('keyword:') ? filterLower.slice(8).trim() : filterLower;
7382
- // 检查任务目录名、feature 名、TASK.md 内容
7383
- let hasKeyword = taskId.toLowerCase().includes(keyword);
7384
- if (!hasKeyword) {
7385
- const featureFile = (0, path_1.join)(metaDir, 'feature');
7386
- if (await (0, fs_extra_1.pathExists)(featureFile)) {
7387
- const feature = (await (0, fs_extra_2.readFile)(featureFile, 'utf-8')).trim().toLowerCase();
7388
- if (feature.includes(keyword))
7389
- hasKeyword = true;
7390
- }
7391
- }
7392
- if (!hasKeyword) {
7393
- const taskMd = (0, path_1.join)(taskDir, '00-specs', 'REQ.md');
7394
- if (await (0, fs_extra_1.pathExists)(taskMd)) {
7395
- const content = (await (0, fs_extra_2.readFile)(taskMd, 'utf-8')).toLowerCase();
7396
- if (content.includes(keyword))
7397
- hasKeyword = true;
7398
- }
7399
- }
7400
- if (hasKeyword)
7401
- matched.push(taskId);
7402
- }
7403
- return matched;
7404
- }
7405
- /**
7406
- * 综合解析目标任务列表
7407
- * - 优先使用 options.task(支持逗号分隔)
7408
- * - 其次使用 options.filter 自动发现
7409
- */
7410
- async function resolveTargetTasks(options) {
7411
- // 1. 直接指定多个任务
7412
- if (options.task && options.task.includes(',')) {
7413
- return parseTaskIds(options.task);
7414
- }
7415
- // 2. 单任务
7416
- if (options.task) {
7417
- const taskId = options.task.startsWith('Task-') ? options.task : `Task-${options.task}`;
7418
- return [taskId];
7419
- }
7420
- // 3. 通过 filter 自动发现
7421
- if (options.filter && options.iteration) {
7422
- const iterDir = await (0, context_1.getIterationDir)(options.iteration);
7423
- const matched = await filterTasks(iterDir, options.filter);
7424
- if (matched.length > 0) {
7425
- logger_1.logger.info(`🔍 filter "${options.filter}" 匹配到 ${matched.length} 个任务: ${matched.join(', ')}`);
7426
- return matched;
7427
- }
7428
- logger_1.logger.warn(`⚠️ filter "${options.filter}" 未匹配到任何任务`);
7429
- }
7430
- return [];
7431
- }
7432
6928
  // ═══════════════════════════════════════════════════════════════════════════════
7433
6929
  // v8.3.177+: 总览生成辅助函数
7434
6930
  // ═══════════════════════════════════════════════════════════════════════════════