speccore 6.10.0 → 6.11.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.
Files changed (61) hide show
  1. package/.agents/skills/spec-ask/SKILL.md +18 -4
  2. package/README.md +10 -0
  3. package/dist/cli.js +9 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/analyze.d.ts +1 -0
  6. package/dist/commands/analyze.d.ts.map +1 -1
  7. package/dist/commands/analyze.js +37 -11
  8. package/dist/commands/analyze.js.map +1 -1
  9. package/dist/commands/change.d.ts.map +1 -1
  10. package/dist/commands/change.js +13 -4
  11. package/dist/commands/change.js.map +1 -1
  12. package/dist/commands/done.js +25 -5
  13. package/dist/commands/done.js.map +1 -1
  14. package/dist/commands/iteration/create.d.ts.map +1 -1
  15. package/dist/commands/iteration/create.js +15 -6
  16. package/dist/commands/iteration/create.js.map +1 -1
  17. package/dist/commands/iteration/split.js +195 -27
  18. package/dist/commands/iteration/split.js.map +1 -1
  19. package/dist/commands/knowledge.d.ts +6 -0
  20. package/dist/commands/knowledge.d.ts.map +1 -0
  21. package/dist/commands/knowledge.js +94 -0
  22. package/dist/commands/knowledge.js.map +1 -0
  23. package/dist/commands/sync-global.d.ts.map +1 -1
  24. package/dist/commands/sync-global.js +18 -0
  25. package/dist/commands/sync-global.js.map +1 -1
  26. package/dist/core/ask-engine.d.ts.map +1 -1
  27. package/dist/core/ask-engine.js +23 -2
  28. package/dist/core/ask-engine.js.map +1 -1
  29. package/dist/core/code-scanner.d.ts.map +1 -1
  30. package/dist/core/code-scanner.js +76 -5
  31. package/dist/core/code-scanner.js.map +1 -1
  32. package/dist/core/context-builder.js +8 -8
  33. package/dist/core/context-builder.js.map +1 -1
  34. package/dist/core/git-integration.d.ts.map +1 -1
  35. package/dist/core/git-integration.js +59 -31
  36. package/dist/core/git-integration.js.map +1 -1
  37. package/dist/core/intent-recognition.d.ts.map +1 -1
  38. package/dist/core/intent-recognition.js +21 -2
  39. package/dist/core/intent-recognition.js.map +1 -1
  40. package/dist/core/knowledge-graph.d.ts +11 -2
  41. package/dist/core/knowledge-graph.d.ts.map +1 -1
  42. package/dist/core/knowledge-graph.js +332 -21
  43. package/dist/core/knowledge-graph.js.map +1 -1
  44. package/dist/core/knowledge-visualizer.d.ts +19 -0
  45. package/dist/core/knowledge-visualizer.d.ts.map +1 -0
  46. package/dist/core/knowledge-visualizer.js +528 -0
  47. package/dist/core/knowledge-visualizer.js.map +1 -0
  48. package/dist/core/next-steps.js +6 -0
  49. package/dist/core/next-steps.js.map +1 -1
  50. package/dist/core/prompt-builder.d.ts.map +1 -1
  51. package/dist/core/prompt-builder.js +142 -2
  52. package/dist/core/prompt-builder.js.map +1 -1
  53. package/dist/core/spec-merger.d.ts +133 -0
  54. package/dist/core/spec-merger.d.ts.map +1 -0
  55. package/dist/core/spec-merger.js +735 -0
  56. package/dist/core/spec-merger.js.map +1 -0
  57. package/dist/core/unified-retrieval.d.ts +1 -0
  58. package/dist/core/unified-retrieval.d.ts.map +1 -1
  59. package/dist/core/unified-retrieval.js +15 -10
  60. package/dist/core/unified-retrieval.js.map +1 -1
  61. package/package.json +1 -1
@@ -883,6 +883,8 @@ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms
883
883
  const complexity = section._complexity || { estimatedHours: 2, priority: 'medium', complexity: 'medium', apiCount: 0, dbCount: 0, pageCount: 0, wordCount: 0 };
884
884
  const owner = section._owner || '未分配';
885
885
  const today = new Date().toISOString().split('T')[0];
886
+ // 加载迭代级 analyze 产出(020-specs/),用于填充任务级文件
887
+ const specContents = await loadSpecContents(iterationDir);
886
888
  // 预生成所有端的子任务 ID(保证同一端在所有文件中 ID 一致)
887
889
  const taskNum = taskId.replace(/^Task-/, '');
888
890
  const subtaskIdMap = new Map();
@@ -895,6 +897,17 @@ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms
895
897
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'status'), 'todo');
896
898
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'owner'), owner);
897
899
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'created-at'), today);
900
+ // ── 1a. Git 配置模板(默认继承迭代级配置) ──
901
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'git-config'), `# 任务级 Git 配置
902
+ # 以下配置覆盖迭代级 PROJECT_GRAPH.md,未配置项自动继承上一级。
903
+ # 修改时去掉注释符 #,填入具体值即可。
904
+
905
+ # 源分支: 继承迭代配置
906
+ # 分支前缀: 继承迭代配置
907
+ # 分支格式: 继承迭代配置
908
+ # 自动拉取: 继承迭代配置
909
+ # 远程名称: 继承迭代配置
910
+ `);
898
911
  // ── 2. 任务目录指引 ──
899
912
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'README.md'), `# ${section.name}
900
913
 
@@ -971,10 +984,15 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
971
984
  const apiLines = section.content.split('\n').filter(l => l.includes('| GET') || l.includes('| POST') || l.includes('| PUT') || l.includes('| DELETE') || l.includes('| PATCH'));
972
985
  const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
973
986
  const aiTechContent = section._techContent;
974
- // TECH.md: 优先用 AI 生成的实际内容,回退到模板
987
+ // 从 analyze TECH.md 提取本任务相关内容
988
+ const specTechContent = extractTaskTechContent(specContents, section);
989
+ // TECH.md: 优先 AI 生成 → 回退 analyze 提取 → 回退模板
975
990
  if (aiTechContent && aiTechContent.length > 50) {
976
991
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
977
992
  }
993
+ else if (specTechContent && specTechContent.length > 30) {
994
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案\n\n> 来源: analyze → TECH.md(自动提取)\n\n${specTechContent}\n`);
995
+ }
978
996
  else {
979
997
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案
980
998
 
@@ -1005,7 +1023,8 @@ ${apiDesc}
1005
1023
  `);
1006
1024
  }
1007
1025
  if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
1008
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'SCHEMA.md'), generateSchemaTemplate(section));
1026
+ const schemaMaterial = extractRelevantSection(specContents['TECH.md'] || '', section.name, 'DDL 建表 表结构 索引 CREATE TABLE');
1027
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'SCHEMA.md'), generateSchemaTemplate(section, schemaMaterial));
1009
1028
  }
1010
1029
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'CHANGELOG.md'), `# ${section.name} - 变更记录
1011
1030
 
@@ -1068,14 +1087,22 @@ ${apiDesc}
1068
1087
  | :--- | :--- | :--- |
1069
1088
  | ${today} | 创建子任务 | CLI |
1070
1089
  `);
1071
- // 后端端额外生成组件/路由等前端文档
1090
+ // 非 backend 端额外生成前端设计文档(从 analyze TECH.md 提取)
1072
1091
  if (!isBackend) {
1073
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platform));
1074
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'ROUTES.md'), generateRoutesDoc(section, platform));
1075
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STATE.md'), generateStateDoc(section, platform));
1076
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STYLE_GUIDE.md'), generateStyleGuide(section, platform));
1092
+ const feContent = extractTaskTechContent(specContents, section, platform);
1093
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platform, feContent));
1094
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'ROUTES.md'), generateRoutesDoc(section, platform, feContent));
1095
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STATE.md'), generateStateDoc(section, platform, feContent));
1096
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STYLE_GUIDE.md'), generateStyleGuide(section, platform, feContent));
1077
1097
  }
1078
1098
  }
1099
+ // 确保至少有 backend 目录(AI 未输出 backend 时自动补充)
1100
+ if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
1101
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'src'));
1102
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'tests'));
1103
+ taskPlatforms.push('backend');
1104
+ subtaskIdMap.set('backend', `${taskId}-backend-auto`);
1105
+ }
1079
1106
  // ── 生成端注册表 _shared/PLATFORMS.md ──
1080
1107
  const platformEntries = taskPlatforms.map((p) => ({
1081
1108
  name: p,
@@ -1083,20 +1110,19 @@ ${apiDesc}
1083
1110
  owner,
1084
1111
  }));
1085
1112
  await (0, platform_registry_1.generatePlatformsRegistry)(taskDir, taskId, platformEntries);
1086
- // 确保至少有 backend 目录
1087
- if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
1088
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'src'));
1089
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'tests'));
1090
- }
1091
1113
  // ── 5. 执行产出 99-artifacts/ ──
1092
1114
  await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '99-artifacts'));
1093
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'TEST.md'), generateTestOutline(section));
1115
+ const testMaterial = extractRelevantSection(specContents['TEST.md'] || '', section.name);
1116
+ const riskMaterial = extractRelevantSection(specContents['RISK.md'] || '', section.name);
1117
+ const depsMaterial = extractRelevantSection(specContents['DEPS.md'] || '', section.name);
1118
+ const monitorMaterial = extractRelevantSection(specContents['MONITOR.md'] || '', section.name);
1119
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'TEST.md'), generateTestOutline(section, testMaterial));
1094
1120
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md'), generateReviewChecklist(section));
1095
1121
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPLOY.md'), generateDeployChecklist(section));
1096
1122
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ERROR_CODES.md'), generateErrorCodes(section));
1097
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'RISK.md'), generateRiskTemplate(section));
1098
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPS.md'), generateDepsTemplate(section));
1099
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'MONITOR.md'), generateMonitorTemplate(section));
1123
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'RISK.md'), generateRiskTemplate(section, riskMaterial));
1124
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPS.md'), generateDepsTemplate(section, depsMaterial));
1125
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'MONITOR.md'), generateMonitorTemplate(section, monitorMaterial));
1100
1126
  const adr = generateAdr(section);
1101
1127
  if (adr) {
1102
1128
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ADR.md'), adr);
@@ -1185,7 +1211,10 @@ async function updateProjectGraph(iterationDir, sections) {
1185
1211
  /**
1186
1212
  * 根据需求内容自动生成测试用例框架
1187
1213
  */
1188
- function generateTestOutline(section) {
1214
+ function generateTestOutline(section, material) {
1215
+ if (material && material.trim().length > 30) {
1216
+ return `# ${section.name} — 测试用例\n\n> 来源: analyze → TEST.md(自动提取)\n\n${material.trim()}\n`;
1217
+ }
1189
1218
  const name = section.name;
1190
1219
  const content = section.content || '';
1191
1220
  const isBackend = section.platform?.startsWith('后台') || false;
@@ -1425,8 +1454,11 @@ async function generateImpactGraph(iterationDir, sections, platforms) {
1425
1454
  await (0, fs_extra_1.writeFile)((0, path_1.join)(iterationDir, 'IMPACT.md'), impact);
1426
1455
  logger_1.logger.info(`\nImpact analysis: ${iterationDir}/IMPACT.md`);
1427
1456
  }
1428
- function generateSchemaTemplate(section) {
1457
+ function generateSchemaTemplate(section, material) {
1429
1458
  const name = section.name;
1459
+ if (material && material.trim().length > 30) {
1460
+ return `# ${name} — Database Schema\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1461
+ }
1430
1462
  return `# ${name} — Database Schema
1431
1463
 
1432
1464
  > Auto-generated. Fill in DDL before development.
@@ -1664,30 +1696,157 @@ function generateAcceptanceCriteria(section) {
1664
1696
  return acs;
1665
1697
  }
1666
1698
  // 风险评估
1667
- function generateRiskTemplate(section) {
1699
+ function generateRiskTemplate(section, material) {
1700
+ if (material && material.trim().length > 30) {
1701
+ return `# ${section.name} — 风险评估\n\n> 来源: analyze → RISK.md(自动提取)\n\n${material.trim()}\n`;
1702
+ }
1668
1703
  return `# ${section.name} — 风险评估\n\n> split | ${new Date().toISOString().split('T')[0]}\n\n## 风险矩阵\n\n| 风险 | 可能 | 影响 | 缓解 |\n| :--- | :--- | :--- | :--- |\n| 兼容性 | 中 | 高 | 版本号+测试 |\n| 性能 | 低 | 中 | 压测+索引 |\n| 依赖故障 | 低 | 高 | 降级方案 |\n\n## 回滚\n\n1. 触发: 线上错误率 > 1%\n2. 步骤: git revert → 重部署\n3. 验证: 冒烟测试 + 监控\n`;
1669
1704
  }
1670
1705
  // 依赖清单
1671
- function generateDepsTemplate(section) {
1706
+ function generateDepsTemplate(section, material) {
1707
+ if (material && material.trim().length > 30) {
1708
+ return `# ${section.name} — 依赖清单\n\n> 来源: analyze → DEPS.md(自动提取)\n\n${material.trim()}\n`;
1709
+ }
1672
1710
  return `# ${section.name} — 依赖清单\n\n## 上游依赖\n\n| 服务 | 版本 | 用途 | SLA |\n| :--- | :--- | :--- | :--- |\n| _待补充_ | — | — | — |\n\n## 下游影响\n\n| 服务 | 影响 | 通知 |\n| :--- | :--- | :--- |\n| _待补充_ | — | — |\n`;
1673
1711
  }
1674
1712
  // 监控指标
1675
- function generateMonitorTemplate(section) {
1713
+ function generateMonitorTemplate(section, material) {
1714
+ if (material && material.trim().length > 30) {
1715
+ return `# ${section.name} — 监控\n\n> 来源: analyze → MONITOR.md(自动提取)\n\n${material.trim()}\n`;
1716
+ }
1676
1717
  return `# ${section.name} — 监控\n\n## 关键指标\n\n| 指标 | 阈值 | 级别 |\n| :--- | :--- | :--- |\n| 成功率 | <99.9% | P1 |\n| P99延迟 | >1000ms | P2 |\n| 错误率 | >0.1% | P0 |\n\n## 关键日志\n\n- 请求入口 (traceId)\n- 业务异常 (上下文)\n- 外部调用 (耗时)\n`;
1677
1718
  }
1719
+ // ═══════════════════════════════════════════════
1720
+ // 从 analyze 产出中提取任务相关内容
1721
+ // ═══════════════════════════════════════════════
1722
+ /** 加载迭代级 020-specs/ 文档内容 */
1723
+ async function loadSpecContents(iterationDir) {
1724
+ const specs = {};
1725
+ const specDir = (0, path_1.join)(iterationDir, '020-specs');
1726
+ if (!(await (0, fs_extra_1.pathExists)(specDir)))
1727
+ return specs;
1728
+ for (const f of ['TECH.md', 'TEST.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'ANALYSIS.md', 'REQUIREMENT.md']) {
1729
+ const fp = (0, path_1.join)(specDir, f);
1730
+ if (await (0, fs_extra_1.pathExists)(fp)) {
1731
+ const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
1732
+ if (content.trim().length > 50 && !content.trim().match(/^#+\s*待填充|^<!--\s*AI-FILL/m)) {
1733
+ specs[f] = content;
1734
+ }
1735
+ }
1736
+ }
1737
+ return specs;
1738
+ }
1739
+ /** 从完整文档中提取与任务名相关的段落 */
1740
+ function extractRelevantSection(fullContent, taskName, sectionHint) {
1741
+ if (!fullContent || !taskName)
1742
+ return '';
1743
+ const lines = fullContent.split('\n');
1744
+ const nameKeywords = new Set();
1745
+ for (const m of taskName.matchAll(/[a-zA-Z]+/g)) {
1746
+ if (m[0].length > 1)
1747
+ nameKeywords.add(m[0].toLowerCase());
1748
+ }
1749
+ for (const m of taskName.matchAll(/[\u4e00-\u9fff]+/g)) {
1750
+ if (m[0].length >= 2)
1751
+ nameKeywords.add(m[0]);
1752
+ }
1753
+ if (sectionHint) {
1754
+ for (const m of sectionHint.matchAll(/[a-zA-Z]+/g)) {
1755
+ if (m[0].length > 1)
1756
+ nameKeywords.add(m[0].toLowerCase());
1757
+ }
1758
+ for (const m of sectionHint.matchAll(/[\u4e00-\u9fff]+/g)) {
1759
+ if (m[0].length >= 2)
1760
+ nameKeywords.add(m[0]);
1761
+ }
1762
+ }
1763
+ if (nameKeywords.size === 0)
1764
+ return '';
1765
+ const kwArray = [...nameKeywords];
1766
+ // 按 Markdown 标题拆分,查找包含关键词的完整段落
1767
+ const sections = [];
1768
+ let cur = { heading: '', level: 0, content: [], headingLine: '' };
1769
+ for (const line of lines) {
1770
+ const hm = line.match(/^(#{1,4})\s+(.+)/);
1771
+ if (hm) {
1772
+ if (cur.heading || cur.content.length > 0)
1773
+ sections.push({ heading: cur.heading, level: cur.level, content: cur.content.join('\n'), headingLine: cur.headingLine });
1774
+ cur = { heading: hm[2].trim(), level: hm[1].length, content: [], headingLine: line };
1775
+ }
1776
+ else {
1777
+ cur.content.push(line);
1778
+ }
1779
+ }
1780
+ if (cur.heading || cur.content.length > 0)
1781
+ sections.push({ heading: cur.heading, level: cur.level, content: cur.content.join('\n'), headingLine: cur.headingLine });
1782
+ // 匹配:标题或内容包含关键词
1783
+ const matched = [];
1784
+ for (const sec of sections) {
1785
+ const text = (sec.heading + ' ' + sec.content).toLowerCase();
1786
+ const hit = kwArray.some(kw => text.includes(kw));
1787
+ if (hit && (sec.heading || sec.content.trim())) {
1788
+ const prefix = '#'.repeat(Math.max(sec.level + 1, 2));
1789
+ matched.push(`${prefix} ${sec.heading}\n${sec.content.trim()}`);
1790
+ }
1791
+ }
1792
+ if (matched.length > 0)
1793
+ return matched.join('\n\n');
1794
+ // 回退:提取包含关键词的连续行
1795
+ const fallback = [];
1796
+ for (const line of lines) {
1797
+ const lower = line.toLowerCase();
1798
+ if (kwArray.some(kw => lower.includes(kw)))
1799
+ fallback.push(line);
1800
+ }
1801
+ return fallback.length > 0 ? fallback.join('\n') : '';
1802
+ }
1803
+ /** 从 TECH.md 提取前端平台相关内容 */
1804
+ function extractFrontendContent(techContent, taskName, platform) {
1805
+ if (!techContent)
1806
+ return '';
1807
+ // 先提取该前端平台相关的顶层段落(如 "H5移动端"、"后台管理端")
1808
+ const platformLabels = {
1809
+ 'h5': ['H5', 'h5', '移动端', 'mobile'],
1810
+ 'admin': ['admin', '后台', '管理端', 'Admin'],
1811
+ 'web': ['Web', 'web', '前端', '桌面'],
1812
+ 'app': ['App', 'app', '客户端'],
1813
+ 'miniapp': ['小程序', 'miniapp'],
1814
+ };
1815
+ const platformKws = platformLabels[platform] || [platform];
1816
+ const platformSection = extractRelevantSection(techContent, taskName, platformKws.join(' '));
1817
+ if (platformSection)
1818
+ return platformSection;
1819
+ // 回退:用任务名匹配
1820
+ return extractRelevantSection(techContent, taskName);
1821
+ }
1822
+ /** 从 specContents 提取任务级 TECH 内容 */
1823
+ function extractTaskTechContent(specContents, section, platform) {
1824
+ const techMd = specContents['TECH.md'];
1825
+ if (!techMd)
1826
+ return '';
1827
+ if (platform && platform !== 'backend') {
1828
+ return extractFrontendContent(techMd, section.name, platform);
1829
+ }
1830
+ // 后端/共享:提取技术方案、架构、接口设计、数据模型等
1831
+ const techSection = extractRelevantSection(techMd, section.name, '技术方案 架构 接口设计 数据模型 模块设计');
1832
+ return techSection;
1833
+ }
1678
1834
  // 前端专属:组件树
1679
- function generateComponentTree(section, platform) {
1835
+ function generateComponentTree(section, platform, material) {
1836
+ if (material && material.trim().length > 30) {
1837
+ return `# ${section.name} — 组件树 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1838
+ }
1680
1839
  return `# ${section.name} — 组件树 (${platform})
1681
1840
 
1682
1841
  > split | ${new Date().toISOString().split('T')[0]}
1683
1842
 
1684
1843
  ## 页面结构
1685
- <!-- AI-FILL: 根据需求描述页面的组件层级 -->
1844
+ <!-- 从 analyze TECH.md 自动提取,若无内容则待补充 -->
1686
1845
 
1687
1846
  ## 组件清单
1688
1847
  | 组件 | 路径 | 类型 | 状态 |
1689
1848
  | :--- | :--- | :--- | :--- |
1690
- | _待AI分析_ | — | — | — |
1849
+ | _待补充_ | — | — | — |
1691
1850
 
1692
1851
  ## 共享组件
1693
1852
  | 组件 | 来源 | 用途 |
@@ -1696,7 +1855,10 @@ function generateComponentTree(section, platform) {
1696
1855
  `;
1697
1856
  }
1698
1857
  // 前端专属:路由
1699
- function generateRoutesDoc(section, platform) {
1858
+ function generateRoutesDoc(section, platform, material) {
1859
+ if (material && material.trim().length > 30) {
1860
+ return `# ${section.name} — 路由设计 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1861
+ }
1700
1862
  return `# ${section.name} — 路由设计 (${platform})
1701
1863
 
1702
1864
  > split | ${new Date().toISOString().split('T')[0]}
@@ -1711,7 +1873,10 @@ function generateRoutesDoc(section, platform) {
1711
1873
  `;
1712
1874
  }
1713
1875
  // 前端专属:状态管理
1714
- function generateStateDoc(section, platform) {
1876
+ function generateStateDoc(section, platform, material) {
1877
+ if (material && material.trim().length > 30) {
1878
+ return `# ${section.name} — 状态管理 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1879
+ }
1715
1880
  return `# ${section.name} — 状态管理 (${platform})
1716
1881
 
1717
1882
  > split | ${new Date().toISOString().split('T')[0]}
@@ -1731,7 +1896,10 @@ function generateStateDoc(section, platform) {
1731
1896
  `;
1732
1897
  }
1733
1898
  // 前端专属:样式规范
1734
- function generateStyleGuide(section, platform) {
1899
+ function generateStyleGuide(section, platform, material) {
1900
+ if (material && material.trim().length > 30) {
1901
+ return `# ${section.name} — 样式规范 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1902
+ }
1735
1903
  const isH5 = platform.includes('h5') || platform.includes('mobile');
1736
1904
  const isMiniapp = platform.includes('miniapp') || platform.includes('小程序');
1737
1905
  const styleCtx = isH5 ? '移动端 H5,注意触控交互和移动适配' :