speccore 7.4.6 → 7.5.1

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.
@@ -1804,6 +1804,52 @@ async function injectGraphSummary(prompt) {
1804
1804
  return prompt;
1805
1805
  }
1806
1806
  }
1807
+ // ── v7.5.0+: 检测迭代级文档生成进度 ──
1808
+ // 检查 020-specs/overview/ 和 020-specs/{platform}/ 下哪些文档已存在
1809
+ async function detectIterationDocsStatus(iterDir) {
1810
+ const specsDir = (0, path_1.join)(iterDir, '020-specs');
1811
+ const overviewDir = (0, path_1.join)(specsDir, spec_paths_1.GLOBAL_SPECS_DIR);
1812
+ const PHASE1_DOCS = ['REQUIREMENT.md', 'ANALYSIS.md', 'TECH.md', 'DEPS.md', 'RISK.md', 'REVIEW.md', 'MONITOR.md', 'FUNCTION_MAP.md'];
1813
+ const PLATFORM_DOCS = ['TECH.md', 'TEST.md', 'UI_SPEC.md'];
1814
+ const existing = [];
1815
+ const missing = [];
1816
+ // Phase 1: 检查 overview/ 下的文档
1817
+ for (const doc of PHASE1_DOCS) {
1818
+ const fp = (0, path_1.join)(overviewDir, doc);
1819
+ if (await (0, fs_extra_1.pathExists)(fp)) {
1820
+ existing.push(`overview/${doc}`);
1821
+ }
1822
+ else {
1823
+ missing.push(`overview/${doc}`);
1824
+ }
1825
+ }
1826
+ const phase1Complete = missing.filter(m => m.startsWith('overview/')).length === 0;
1827
+ // Phase 2: 检查各端目录下的文档
1828
+ const platforms = await (0, spec_paths_1.parsePlatformList)();
1829
+ for (const platform of platforms) {
1830
+ const platformDir = (0, path_1.join)(specsDir, platform);
1831
+ if (await (0, fs_extra_1.pathExists)(platformDir)) {
1832
+ for (const doc of PLATFORM_DOCS) {
1833
+ const fp = (0, path_1.join)(platformDir, doc);
1834
+ if (await (0, fs_extra_1.pathExists)(fp)) {
1835
+ existing.push(`${platform}/${doc}`);
1836
+ }
1837
+ else {
1838
+ missing.push(`${platform}/${doc}`);
1839
+ }
1840
+ }
1841
+ }
1842
+ else {
1843
+ for (const doc of PLATFORM_DOCS) {
1844
+ missing.push(`${platform}/${doc}`);
1845
+ }
1846
+ }
1847
+ }
1848
+ const phase2Complete = missing.filter(m => !m.startsWith('overview/')).length === 0;
1849
+ // 下一个缺失的文档(Phase 1 优先)
1850
+ const nextDoc = missing.length > 0 ? missing[0] : null;
1851
+ return { existing, missing, nextDoc, phase1Complete, phase2Complete };
1852
+ }
1807
1853
  // ── v7.2.0+: 检测全局分析当前进度 ──
1808
1854
  // Layer 4 拆分为子层: 4a=产品文档, 4b=全局技术核心, 4c=全局技术扩展, 4d=各端技术
1809
1855
  async function detectGlobalLayerProgress() {
@@ -2668,70 +2714,151 @@ async function buildMultiDocPrompt(command, ctx, options) {
2668
2714
  }
2669
2715
  }
2670
2716
  else {
2671
- // 子层模式:每次只生成一个子层
2672
- prompt += `## 🌍 Layer 4: 全局汇总 — 子层 ${subLayerTarget}/4\n\n`;
2673
- prompt += `> ⚠️ **专注约束**: 你当前只执行 **子层 ${subLayerTarget}**,不要生成其他子层的文档。\n`;
2674
- prompt += `> 子层完成后执行: \`speccore analyze --scope global --layer 4\` 进入下一子层。\n\n`;
2717
+ // v7.5.0+: 子层内逐文档生成 — 检测当前子层中哪些文档已存在,每次只生成下一个缺失的
2718
+ const globalDir = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL');
2719
+ const overviewDir = (0, path_1.join)(globalDir, 'overview');
2720
+ const requirementsDir = (0, path_1.join)(globalDir, 'requirements');
2721
+ const platformsDir = (0, path_1.join)(globalDir, 'platforms');
2722
+ // 定义每个子层的预期文档清单(按优先级排序)
2723
+ const SUB_LAYER_DOCS = {
2724
+ '4a': ['requirements/REQUIREMENT.md'],
2725
+ '4b': ['overview/FUNCTION_MAP.md', 'overview/ARCHITECTURE.md', 'overview/API_CONTRACT.yaml', 'overview/INTERACTION_MAP.md'],
2726
+ '4c': ['overview/SECURITY_AUDIT.md', 'overview/PERFORMANCE_BASELINE.md', 'overview/DATA_FLOW.md', 'overview/DEPLOYMENT.md', 'overview/CONSISTENCY_CHECK.md'],
2727
+ '4d': [],
2728
+ };
2729
+ // 4a: 追加各前端端的需求文档
2730
+ if (subLayerTarget === '4a') {
2731
+ const allPlatforms = await (0, spec_paths_1.parsePlatformList)();
2732
+ const frontendPlatforms = allPlatforms.filter(p => {
2733
+ const pt = (async () => { try {
2734
+ const m = await (0, spec_paths_1.parsePlatformTypes)();
2735
+ return m.get(p);
2736
+ }
2737
+ catch {
2738
+ return '';
2739
+ } })();
2740
+ return true; // 保守策略:所有非后端端都可能需要 REQUIREMENT.md
2741
+ });
2742
+ for (const p of allPlatforms) {
2743
+ SUB_LAYER_DOCS['4a'].push(`requirements/${p}/REQUIREMENT.md`);
2744
+ }
2745
+ }
2746
+ // 4d: 动态构建各端文档清单
2747
+ if (subLayerTarget === '4d') {
2748
+ try {
2749
+ const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
2750
+ const platformNames = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
2751
+ for (const p of platformNames) {
2752
+ const isBackend = /service|server|api|backend/i.test(p);
2753
+ if (isBackend) {
2754
+ SUB_LAYER_DOCS['4d'].push(`platforms/${p}/API_INVENTORY.md`);
2755
+ SUB_LAYER_DOCS['4d'].push(`platforms/${p}/DATA_MODEL.md`);
2756
+ SUB_LAYER_DOCS['4d'].push(`platforms/${p}/BUSINESS_RULES.md`);
2757
+ }
2758
+ else {
2759
+ SUB_LAYER_DOCS['4d'].push(`platforms/${p}/UI_FLOW.md`);
2760
+ SUB_LAYER_DOCS['4d'].push(`platforms/${p}/API_CALL_MAP.md`);
2761
+ SUB_LAYER_DOCS['4d'].push(`platforms/${p}/STATE_MANAGEMENT.md`);
2762
+ }
2763
+ }
2764
+ }
2765
+ catch { }
2766
+ }
2767
+ // 检测当前子层中哪些文档已存在
2768
+ const expectedDocs = SUB_LAYER_DOCS[subLayerTarget] || [];
2769
+ const existingDocs = [];
2770
+ const missingDocs = [];
2771
+ for (const doc of expectedDocs) {
2772
+ const fullPath = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL', doc);
2773
+ if (await (0, fs_extra_1.pathExists)(fullPath)) {
2774
+ existingDocs.push(doc);
2775
+ }
2776
+ else {
2777
+ missingDocs.push(doc);
2778
+ }
2779
+ }
2780
+ const nextDoc = missingDocs.length > 0 ? missingDocs[0] : null;
2781
+ prompt += `## 🌍 Layer 4: 全局汇总 — 子层 ${subLayerTarget}/4(v7.5.0+ 逐文档模式)\n\n`;
2782
+ prompt += `> ⚠️ **专注约束**: 你当前只执行 **子层 ${subLayerTarget}** 中的 **下一个缺失文档**。\n`;
2783
+ prompt += `> 已存在: ${existingDocs.length}/${expectedDocs.length} 份文档\n`;
2784
+ if (missingDocs.length > 0) {
2785
+ prompt += `> 缺失: ${missingDocs.slice(0, 5).join('、')}${missingDocs.length > 5 ? '...' : ''}\n`;
2786
+ }
2787
+ prompt += `\n`;
2675
2788
  prompt += `**强制输入**: \n`;
2676
2789
  prompt += `- Read Layer 1 的所有 _INDEX.md\n`;
2677
2790
  prompt += `- Read Layer 2 的 _ASSOCIATION.md + _MODULES.md\n`;
2678
2791
  prompt += `- Read Layer 3 的功能模块深入文档\n\n`;
2679
- if (subLayerTarget === '4a') {
2680
- prompt += `**子层 4a: 产品视角文档(2-3 份)**\n`;
2681
- prompt += `> ⚠️ **REQUIREMENT.md 是必须生成的核心文档**,不可跳过!\n\n`;
2682
- prompt += `1. \`requirements/REQUIREMENT.md\` — 全局需求总纲(**必须生成**)\n`;
2683
- prompt += ` - 产品愿景、目标用户画像、核心场景地图\n`;
2684
- prompt += ` - 按业务场景组织:用户故事 操作流程 业务规则 → 边界条件 → 验收标准\n`;
2685
- prompt += ` - 功能优先级矩阵(P0/P1/P2)\n`;
2686
- prompt += ` - 必须从 Layer 3 的功能模块分析中提取真实内容,不要臆造\n`;
2687
- prompt += `2. 各前端端 \`requirements/{端}/REQUIREMENT.md\` 只生成已有前端端的需求\n`;
2688
- prompt += ` - 信息架构、用户旅程、页面清单、交互设计\n`;
2689
- prompt += ` - 从 Layer 1 的前端 _INDEX.md 提取页面/路由信息\n`;
2690
- prompt += ` - Layer 3 的模块分析提取交互流程\n`;
2691
- }
2692
- else if (subLayerTarget === '4b') {
2693
- prompt += `**子层 4b: 全局技术核心文档(3-4 份)**\n`;
2694
- prompt += `1. \`overview/FUNCTION_MAP.md\` — 功能单元 × 端映射表\n`;
2695
- prompt += ` - 从 Layer 2 的 _MODULES.md 提取功能模块\n`;
2696
- prompt += ` - 每个功能单元标注:涉及端、核心页面、核心接口、状态枚举\n`;
2697
- prompt += ` - ⚠️ **必须使用 Markdown 表格格式**,表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
2698
- prompt += ` - **禁止使用树形格式**(├── ...),split 无法解析树形格式\n`;
2699
- prompt += ` - 「涉及端」必须使用 CONSTITUTION.md 标准端名\n`;
2700
- prompt += `2. \`overview/ARCHITECTURE.md\` — 全局架构\n`;
2701
- prompt += ` - 服务拓扑(从 Layer 1 的后端 _INDEX.md 提取服务名和依赖)\n`;
2702
- prompt += ` - 数据流(从 Layer 3 的模块分析提取)\n`;
2703
- prompt += ` - 必须包含 Mermaid architecture diagram\n`;
2704
- prompt += `3. \`overview/API_CONTRACT.yaml\` — 全局接口契约\n`;
2705
- prompt += ` - 汇总所有后端端的 API_INVENTORY(从 Layer 1 提取)\n`;
2706
- prompt += ` - 标注 rate limit、幂等性、版本策略\n`;
2707
- prompt += `4. \`overview/INTERACTION_MAP.md\` — 跨端交互时序图\n`;
2708
- prompt += ` - Layer 3 的模块时序图汇总\n`;
2709
- prompt += ` - 必须包含 Mermaid sequenceDiagram\n`;
2710
- }
2711
- else if (subLayerTarget === '4c') {
2712
- prompt += `**子层 4c: 全局技术扩展文档(4-5 份)**\n`;
2713
- prompt += `1. \`overview/SECURITY_AUDIT.md\` 安全审计\n`;
2714
- prompt += `2. \`overview/PERFORMANCE_BASELINE.md\` — 性能基线\n`;
2715
- prompt += `3. \`overview/DATA_FLOW.md\` — 数据流与隐私\n`;
2716
- prompt += `4. \`overview/DEPLOYMENT.md\` — 部署运维\n`;
2717
- prompt += `5. \`overview/CONSISTENCY_CHECK.md\` 一致性校验\n`;
2718
- prompt += ` - 从 Layer 2 的关联分析提取不一致项\n`;
2792
+ if (nextDoc) {
2793
+ prompt += `## 🎯 本次只生成: \`${nextDoc}\`\n\n`;
2794
+ // 根据文档路径生成专属指令
2795
+ const docName = nextDoc.split('/').pop();
2796
+ const docDir = nextDoc.includes('/') ? nextDoc.split('/')[0] : '';
2797
+ if (docName === 'REQUIREMENT.md' && docDir === 'requirements') {
2798
+ prompt += `**全局需求总纲(产品视角)**\n`;
2799
+ prompt += `- 产品愿景、目标用户画像、核心场景地图\n`;
2800
+ prompt += `- 按业务场景组织:用户故事 操作流程 → 业务规则 → 边界条件 → 验收标准\n`;
2801
+ prompt += `- 功能优先级矩阵(P0/P1/P2)\n`;
2802
+ prompt += `- 功能模块清单表(含涉及端列)\n`;
2803
+ prompt += `- ⚠️ **以产品/用户视角撰写**,不写技术实现细节\n`;
2804
+ prompt += `- ⚠️ **必须从 Layer 3 的功能模块分析中提取真实内容,不要臆造**\n`;
2805
+ }
2806
+ else if (docName === 'REQUIREMENT.md') {
2807
+ prompt += `**${docDir} 端需求文档**\n`;
2808
+ prompt += `- 信息架构、用户旅程、页面清单、交互设计\n`;
2809
+ prompt += `- 从 Layer 1 的前端 _INDEX.md 提取页面/路由信息\n`;
2810
+ }
2811
+ else if (docName === 'FUNCTION_MAP.md') {
2812
+ prompt += `**功能单元 × 端映射表**\n`;
2813
+ prompt += `- 从 Layer 2 的 _MODULES.md 提取功能模块\n`;
2814
+ prompt += `- ⚠️ **必须使用 Markdown 表格格式**,表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
2815
+ prompt += `- **禁止使用树形格式**(├── ...),split 无法解析\n`;
2816
+ prompt += `- 「涉及端」必须使用 CONSTITUTION.md 标准端名\n`;
2817
+ }
2818
+ else if (docName === 'ARCHITECTURE.md') {
2819
+ prompt += `**全局架构文档**\n`;
2820
+ prompt += `- 服务拓扑、数据流、技术栈选型\n`;
2821
+ prompt += `- 必须包含 Mermaid architecture diagram\n`;
2822
+ }
2823
+ else if (docName === 'API_CONTRACT.yaml') {
2824
+ prompt += `**全局接口契约**\n`;
2825
+ prompt += `- 汇总所有后端端的 API 清单\n`;
2826
+ prompt += `- 标注 rate limit、幂等性、版本策略\n`;
2827
+ }
2828
+ else if (docName === 'INTERACTION_MAP.md') {
2829
+ prompt += `**跨端交互时序图**\n`;
2830
+ prompt += `- 每个核心场景一个 Mermaid sequenceDiagram\n`;
2831
+ prompt += `- 标注接口路径和契约引用\n`;
2832
+ }
2833
+ else if (['SECURITY_AUDIT.md', 'PERFORMANCE_BASELINE.md', 'DATA_FLOW.md', 'DEPLOYMENT.md', 'CONSISTENCY_CHECK.md'].includes(docName)) {
2834
+ prompt += `**${docName.replace('.md', '')}**\n`;
2835
+ prompt += `- 从 Layer 1-3 的产出中提取相关内容\n`;
2836
+ prompt += `- 必须包含真实数据,禁止占位符\n`;
2837
+ }
2838
+ else if (['API_INVENTORY.md', 'DATA_MODEL.md', 'BUSINESS_RULES.md', 'UI_FLOW.md', 'API_CALL_MAP.md', 'STATE_MANAGEMENT.md'].includes(docName)) {
2839
+ prompt += `**${docDir} 端 ${docName.replace('.md', '')}**\n`;
2840
+ prompt += `- 从 Layer 1 的 _INDEX.md 和 Layer 3 的模块分析提取\n`;
2841
+ prompt += `- 必须包含真实数据,禁止占位符\n`;
2842
+ }
2843
+ // 自动链式推进
2844
+ if (missingDocs.length > 1) {
2845
+ prompt += `\n## 🔗 自动链式推进\n\n`;
2846
+ prompt += `> 当前文档写入完成后,**立即输出以下标记**推进下一个文档:\n\n`;
2847
+ prompt += `[SPECCORE_EXEC: speccore analyze --scope global --layer 4${options?.withCode ? ' --with-code' : ''}]\n\n`;
2848
+ prompt += `> 💡 CLI 会自动检测下一个缺失文档并聚焦生成。重复直到子层 ${subLayerTarget} 完成,然后进入下一子层。\n`;
2849
+ }
2850
+ else {
2851
+ prompt += `\n> 🎉 这是子层 ${subLayerTarget} 的最后一份文档!写入后将自动进入下一子层。\n`;
2852
+ }
2719
2853
  }
2720
- else if (subLayerTarget === '4d') {
2721
- prompt += `**子层 4d: 各端技术文档**\n`;
2722
- prompt += `后端端(每端 9 项,但本次只生成核心 3 项,其余后续补充):\n`;
2723
- prompt += `1. \`API_INVENTORY.md\` — 从 Layer 1 的 _INDEX.md 提取完整接口清单\n`;
2724
- prompt += `2. \`DATA_MODEL.md\` — 从 Layer 1 的 Entity 目录 + Layer 3 的模块分析提取\n`;
2725
- prompt += `3. \`BUSINESS_RULES.md\` — 从 Layer 3 的模块分析提取业务规则\n`;
2726
- prompt += `前端端(每端 9 项,但本次只生成核心 3 项):\n`;
2727
- prompt += `1. \`UI_FLOW.md\` — 从 Layer 1 的路由 + Layer 3 的模块分析提取\n`;
2728
- prompt += `2. \`API_CALL_MAP.md\` — 从 Layer 1 的 API 调用提取\n`;
2729
- prompt += `3. \`STATE_MANAGEMENT.md\` — 从 Layer 1 的 store 目录提取\n`;
2854
+ else {
2855
+ prompt += `> 子层 ${subLayerTarget} 所有文档已存在,请执行下一子层。\n`;
2856
+ prompt += `\`speccore analyze --scope global --layer 4\`\n`;
2730
2857
  }
2731
2858
  prompt += `\n**质量要求**: \n`;
2732
- prompt += `- 禁止写"待导入"、"待补充"等占位符\n`;
2859
+ prompt += `- 禁止写“待导入”、“待补充”等占位符\n`;
2733
2860
  prompt += `- 每个表格必须有真实数据(从 Layer 1-3 提取)\n`;
2734
- prompt += `- 如果信息不足,明确标注"信息不足: 需要读取 xxx"\n`;
2861
+ prompt += `- 如果信息不足,明确标注“信息不足: 需要读取 xxx”\n`;
2735
2862
  }
2736
2863
  }
2737
2864
  // v7.4.5+: 用户模板接入 + 深度标准(引用模板而非笼统数字)
@@ -3088,6 +3215,34 @@ sequenceDiagram
3088
3215
  else if (ctx.phase === '2') {
3089
3216
  taskDocs = taskDocs.filter(([n]) => PLATFORM_DOCS.includes(n));
3090
3217
  }
3218
+ // v7.5.0+: 逐文档生成模式 — 检测已有文档,每次只生成下一个缺失的
3219
+ // 避免一次性生成 9+ 文档导致 AI 跳过或写错目录
3220
+ let perDocStatus = null;
3221
+ if (!isGlobal && !isTask && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
3222
+ try {
3223
+ const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
3224
+ if (iterDir) {
3225
+ perDocStatus = await detectIterationDocsStatus(iterDir);
3226
+ if (perDocStatus.missing.length > 0 && perDocStatus.nextDoc) {
3227
+ const nextDocName = perDocStatus.nextDoc.split('/').pop();
3228
+ const nextDocPlatform = perDocStatus.nextDoc.includes('/') ? perDocStatus.nextDoc.split('/')[0] : null;
3229
+ // 过滤 taskDocs 只保留下一个缺失的文档
3230
+ if (nextDocPlatform && nextDocPlatform !== spec_paths_1.GLOBAL_SPECS_DIR) {
3231
+ // Phase 2 文档(端专属)
3232
+ taskDocs = taskDocs.filter(([n]) => n === nextDocName);
3233
+ ctx.platform = nextDocPlatform;
3234
+ }
3235
+ else {
3236
+ // Phase 1 文档(overview)
3237
+ taskDocs = taskDocs.filter(([n]) => n === nextDocName);
3238
+ }
3239
+ }
3240
+ }
3241
+ }
3242
+ catch (e) {
3243
+ logger_1.logger.debug(`逐文档检测失败(非关键): ${e.message}`);
3244
+ }
3245
+ }
3091
3246
  // ── 任务级文档覆盖:00-specs/ 使用任务级文档集(v6.44.0+) ──
3092
3247
  if (isTask && !ctx.phase) {
3093
3248
  const TASK_DOCS = {
@@ -3616,6 +3771,28 @@ sequenceDiagram
3616
3771
  prompt += `请在生成综合文档后,**直接继续**生成各端专属文档(Phase 2)。\n`;
3617
3772
  prompt += `执行命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n\n`;
3618
3773
  }
3774
+ // v7.5.0+: 逐文档自动链式推进
3775
+ if (perDocStatus && perDocStatus.missing.length > 0) {
3776
+ const remainingAfterThis = perDocStatus.missing.length - 1;
3777
+ prompt += `\n## 🔗 逐文档自动链式推进(v7.5.0+)\n\n`;
3778
+ prompt += `> 当前为**单文档模式**:本次只生成 1 个文档,写入后立即推进下一个。\n`;
3779
+ prompt += `> 剩余缺失文档: ${perDocStatus.missing.length} 个(${perDocStatus.missing.slice(0, 5).join('、')}${perDocStatus.missing.length > 5 ? '...' : ''})\n\n`;
3780
+ if (remainingAfterThis > 0) {
3781
+ prompt += `**当前文档写入完成后,立即输出以下标记推进下一个文档**:\n\n`;
3782
+ prompt += `[SPECCORE_EXEC: speccore analyze --prompt -I ${iter}${options?.auto ? ' --auto' : ''}${options?.withCode ? ' --with-code' : ''}]\n\n`;
3783
+ prompt += `> 💡 CLI 会自动检测下一个缺失的文档并生成对应的 prompt。重复执行直到所有文档生成完毕。\n`;
3784
+ }
3785
+ else {
3786
+ prompt += `> 🎉 这是最后一个缺失文档!写入后所有迭代级分析文档将完整。\n`;
3787
+ const platforms = await (0, spec_paths_1.parsePlatformList)();
3788
+ if (platforms.length >= 2 && perDocStatus.phase2Complete === false) {
3789
+ prompt += `> 下一步: 生成各端专属文档 → \`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
3790
+ }
3791
+ else {
3792
+ prompt += `> 下一步: 运行 \`speccore split -I ${iter}\` 进行任务拆分\n`;
3793
+ }
3794
+ }
3795
+ }
3619
3796
  // ── v6.52.0+: 图谱 RAG 上下文注入(analyze 阶段也检索项目关联内容)──
3620
3797
  if (!isTask && ctx.phase !== '2') {
3621
3798
  try {