speccore 6.10.0 → 6.11.0

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 (58) 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 +2 -5
  16. package/dist/commands/iteration/create.js.map +1 -1
  17. package/dist/commands/iteration/split.js +184 -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/intent-recognition.d.ts.map +1 -1
  35. package/dist/core/intent-recognition.js +21 -2
  36. package/dist/core/intent-recognition.js.map +1 -1
  37. package/dist/core/knowledge-graph.d.ts +11 -2
  38. package/dist/core/knowledge-graph.d.ts.map +1 -1
  39. package/dist/core/knowledge-graph.js +332 -21
  40. package/dist/core/knowledge-graph.js.map +1 -1
  41. package/dist/core/knowledge-visualizer.d.ts +19 -0
  42. package/dist/core/knowledge-visualizer.d.ts.map +1 -0
  43. package/dist/core/knowledge-visualizer.js +528 -0
  44. package/dist/core/knowledge-visualizer.js.map +1 -0
  45. package/dist/core/next-steps.js +6 -0
  46. package/dist/core/next-steps.js.map +1 -1
  47. package/dist/core/prompt-builder.d.ts.map +1 -1
  48. package/dist/core/prompt-builder.js +142 -2
  49. package/dist/core/prompt-builder.js.map +1 -1
  50. package/dist/core/spec-merger.d.ts +133 -0
  51. package/dist/core/spec-merger.d.ts.map +1 -0
  52. package/dist/core/spec-merger.js +735 -0
  53. package/dist/core/spec-merger.js.map +1 -0
  54. package/dist/core/unified-retrieval.d.ts +1 -0
  55. package/dist/core/unified-retrieval.d.ts.map +1 -1
  56. package/dist/core/unified-retrieval.js +15 -10
  57. package/dist/core/unified-retrieval.js.map +1 -1
  58. 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();
@@ -971,10 +973,15 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
971
973
  const apiLines = section.content.split('\n').filter(l => l.includes('| GET') || l.includes('| POST') || l.includes('| PUT') || l.includes('| DELETE') || l.includes('| PATCH'));
972
974
  const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
973
975
  const aiTechContent = section._techContent;
974
- // TECH.md: 优先用 AI 生成的实际内容,回退到模板
976
+ // 从 analyze TECH.md 提取本任务相关内容
977
+ const specTechContent = extractTaskTechContent(specContents, section);
978
+ // TECH.md: 优先 AI 生成 → 回退 analyze 提取 → 回退模板
975
979
  if (aiTechContent && aiTechContent.length > 50) {
976
980
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
977
981
  }
982
+ else if (specTechContent && specTechContent.length > 30) {
983
+ 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`);
984
+ }
978
985
  else {
979
986
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案
980
987
 
@@ -1005,7 +1012,8 @@ ${apiDesc}
1005
1012
  `);
1006
1013
  }
1007
1014
  if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
1008
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'SCHEMA.md'), generateSchemaTemplate(section));
1015
+ const schemaMaterial = extractRelevantSection(specContents['TECH.md'] || '', section.name, 'DDL 建表 表结构 索引 CREATE TABLE');
1016
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'SCHEMA.md'), generateSchemaTemplate(section, schemaMaterial));
1009
1017
  }
1010
1018
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'CHANGELOG.md'), `# ${section.name} - 变更记录
1011
1019
 
@@ -1068,14 +1076,22 @@ ${apiDesc}
1068
1076
  | :--- | :--- | :--- |
1069
1077
  | ${today} | 创建子任务 | CLI |
1070
1078
  `);
1071
- // 后端端额外生成组件/路由等前端文档
1079
+ // 非 backend 端额外生成前端设计文档(从 analyze TECH.md 提取)
1072
1080
  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));
1081
+ const feContent = extractTaskTechContent(specContents, section, platform);
1082
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platform, feContent));
1083
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'ROUTES.md'), generateRoutesDoc(section, platform, feContent));
1084
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STATE.md'), generateStateDoc(section, platform, feContent));
1085
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STYLE_GUIDE.md'), generateStyleGuide(section, platform, feContent));
1077
1086
  }
1078
1087
  }
1088
+ // 确保至少有 backend 目录(AI 未输出 backend 时自动补充)
1089
+ if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
1090
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'src'));
1091
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'tests'));
1092
+ taskPlatforms.push('backend');
1093
+ subtaskIdMap.set('backend', `${taskId}-backend-auto`);
1094
+ }
1079
1095
  // ── 生成端注册表 _shared/PLATFORMS.md ──
1080
1096
  const platformEntries = taskPlatforms.map((p) => ({
1081
1097
  name: p,
@@ -1083,20 +1099,19 @@ ${apiDesc}
1083
1099
  owner,
1084
1100
  }));
1085
1101
  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
1102
  // ── 5. 执行产出 99-artifacts/ ──
1092
1103
  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));
1104
+ const testMaterial = extractRelevantSection(specContents['TEST.md'] || '', section.name);
1105
+ const riskMaterial = extractRelevantSection(specContents['RISK.md'] || '', section.name);
1106
+ const depsMaterial = extractRelevantSection(specContents['DEPS.md'] || '', section.name);
1107
+ const monitorMaterial = extractRelevantSection(specContents['MONITOR.md'] || '', section.name);
1108
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'TEST.md'), generateTestOutline(section, testMaterial));
1094
1109
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md'), generateReviewChecklist(section));
1095
1110
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPLOY.md'), generateDeployChecklist(section));
1096
1111
  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));
1112
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'RISK.md'), generateRiskTemplate(section, riskMaterial));
1113
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPS.md'), generateDepsTemplate(section, depsMaterial));
1114
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'MONITOR.md'), generateMonitorTemplate(section, monitorMaterial));
1100
1115
  const adr = generateAdr(section);
1101
1116
  if (adr) {
1102
1117
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ADR.md'), adr);
@@ -1185,7 +1200,10 @@ async function updateProjectGraph(iterationDir, sections) {
1185
1200
  /**
1186
1201
  * 根据需求内容自动生成测试用例框架
1187
1202
  */
1188
- function generateTestOutline(section) {
1203
+ function generateTestOutline(section, material) {
1204
+ if (material && material.trim().length > 30) {
1205
+ return `# ${section.name} — 测试用例\n\n> 来源: analyze → TEST.md(自动提取)\n\n${material.trim()}\n`;
1206
+ }
1189
1207
  const name = section.name;
1190
1208
  const content = section.content || '';
1191
1209
  const isBackend = section.platform?.startsWith('后台') || false;
@@ -1425,8 +1443,11 @@ async function generateImpactGraph(iterationDir, sections, platforms) {
1425
1443
  await (0, fs_extra_1.writeFile)((0, path_1.join)(iterationDir, 'IMPACT.md'), impact);
1426
1444
  logger_1.logger.info(`\nImpact analysis: ${iterationDir}/IMPACT.md`);
1427
1445
  }
1428
- function generateSchemaTemplate(section) {
1446
+ function generateSchemaTemplate(section, material) {
1429
1447
  const name = section.name;
1448
+ if (material && material.trim().length > 30) {
1449
+ return `# ${name} — Database Schema\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1450
+ }
1430
1451
  return `# ${name} — Database Schema
1431
1452
 
1432
1453
  > Auto-generated. Fill in DDL before development.
@@ -1664,30 +1685,157 @@ function generateAcceptanceCriteria(section) {
1664
1685
  return acs;
1665
1686
  }
1666
1687
  // 风险评估
1667
- function generateRiskTemplate(section) {
1688
+ function generateRiskTemplate(section, material) {
1689
+ if (material && material.trim().length > 30) {
1690
+ return `# ${section.name} — 风险评估\n\n> 来源: analyze → RISK.md(自动提取)\n\n${material.trim()}\n`;
1691
+ }
1668
1692
  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
1693
  }
1670
1694
  // 依赖清单
1671
- function generateDepsTemplate(section) {
1695
+ function generateDepsTemplate(section, material) {
1696
+ if (material && material.trim().length > 30) {
1697
+ return `# ${section.name} — 依赖清单\n\n> 来源: analyze → DEPS.md(自动提取)\n\n${material.trim()}\n`;
1698
+ }
1672
1699
  return `# ${section.name} — 依赖清单\n\n## 上游依赖\n\n| 服务 | 版本 | 用途 | SLA |\n| :--- | :--- | :--- | :--- |\n| _待补充_ | — | — | — |\n\n## 下游影响\n\n| 服务 | 影响 | 通知 |\n| :--- | :--- | :--- |\n| _待补充_ | — | — |\n`;
1673
1700
  }
1674
1701
  // 监控指标
1675
- function generateMonitorTemplate(section) {
1702
+ function generateMonitorTemplate(section, material) {
1703
+ if (material && material.trim().length > 30) {
1704
+ return `# ${section.name} — 监控\n\n> 来源: analyze → MONITOR.md(自动提取)\n\n${material.trim()}\n`;
1705
+ }
1676
1706
  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
1707
  }
1708
+ // ═══════════════════════════════════════════════
1709
+ // 从 analyze 产出中提取任务相关内容
1710
+ // ═══════════════════════════════════════════════
1711
+ /** 加载迭代级 020-specs/ 文档内容 */
1712
+ async function loadSpecContents(iterationDir) {
1713
+ const specs = {};
1714
+ const specDir = (0, path_1.join)(iterationDir, '020-specs');
1715
+ if (!(await (0, fs_extra_1.pathExists)(specDir)))
1716
+ return specs;
1717
+ for (const f of ['TECH.md', 'TEST.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'ANALYSIS.md', 'REQUIREMENT.md']) {
1718
+ const fp = (0, path_1.join)(specDir, f);
1719
+ if (await (0, fs_extra_1.pathExists)(fp)) {
1720
+ const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
1721
+ if (content.trim().length > 50 && !content.trim().match(/^#+\s*待填充|^<!--\s*AI-FILL/m)) {
1722
+ specs[f] = content;
1723
+ }
1724
+ }
1725
+ }
1726
+ return specs;
1727
+ }
1728
+ /** 从完整文档中提取与任务名相关的段落 */
1729
+ function extractRelevantSection(fullContent, taskName, sectionHint) {
1730
+ if (!fullContent || !taskName)
1731
+ return '';
1732
+ const lines = fullContent.split('\n');
1733
+ const nameKeywords = new Set();
1734
+ for (const m of taskName.matchAll(/[a-zA-Z]+/g)) {
1735
+ if (m[0].length > 1)
1736
+ nameKeywords.add(m[0].toLowerCase());
1737
+ }
1738
+ for (const m of taskName.matchAll(/[\u4e00-\u9fff]+/g)) {
1739
+ if (m[0].length >= 2)
1740
+ nameKeywords.add(m[0]);
1741
+ }
1742
+ if (sectionHint) {
1743
+ for (const m of sectionHint.matchAll(/[a-zA-Z]+/g)) {
1744
+ if (m[0].length > 1)
1745
+ nameKeywords.add(m[0].toLowerCase());
1746
+ }
1747
+ for (const m of sectionHint.matchAll(/[\u4e00-\u9fff]+/g)) {
1748
+ if (m[0].length >= 2)
1749
+ nameKeywords.add(m[0]);
1750
+ }
1751
+ }
1752
+ if (nameKeywords.size === 0)
1753
+ return '';
1754
+ const kwArray = [...nameKeywords];
1755
+ // 按 Markdown 标题拆分,查找包含关键词的完整段落
1756
+ const sections = [];
1757
+ let cur = { heading: '', level: 0, content: [], headingLine: '' };
1758
+ for (const line of lines) {
1759
+ const hm = line.match(/^(#{1,4})\s+(.+)/);
1760
+ if (hm) {
1761
+ if (cur.heading || cur.content.length > 0)
1762
+ sections.push({ heading: cur.heading, level: cur.level, content: cur.content.join('\n'), headingLine: cur.headingLine });
1763
+ cur = { heading: hm[2].trim(), level: hm[1].length, content: [], headingLine: line };
1764
+ }
1765
+ else {
1766
+ cur.content.push(line);
1767
+ }
1768
+ }
1769
+ if (cur.heading || cur.content.length > 0)
1770
+ sections.push({ heading: cur.heading, level: cur.level, content: cur.content.join('\n'), headingLine: cur.headingLine });
1771
+ // 匹配:标题或内容包含关键词
1772
+ const matched = [];
1773
+ for (const sec of sections) {
1774
+ const text = (sec.heading + ' ' + sec.content).toLowerCase();
1775
+ const hit = kwArray.some(kw => text.includes(kw));
1776
+ if (hit && (sec.heading || sec.content.trim())) {
1777
+ const prefix = '#'.repeat(Math.max(sec.level + 1, 2));
1778
+ matched.push(`${prefix} ${sec.heading}\n${sec.content.trim()}`);
1779
+ }
1780
+ }
1781
+ if (matched.length > 0)
1782
+ return matched.join('\n\n');
1783
+ // 回退:提取包含关键词的连续行
1784
+ const fallback = [];
1785
+ for (const line of lines) {
1786
+ const lower = line.toLowerCase();
1787
+ if (kwArray.some(kw => lower.includes(kw)))
1788
+ fallback.push(line);
1789
+ }
1790
+ return fallback.length > 0 ? fallback.join('\n') : '';
1791
+ }
1792
+ /** 从 TECH.md 提取前端平台相关内容 */
1793
+ function extractFrontendContent(techContent, taskName, platform) {
1794
+ if (!techContent)
1795
+ return '';
1796
+ // 先提取该前端平台相关的顶层段落(如 "H5移动端"、"后台管理端")
1797
+ const platformLabels = {
1798
+ 'h5': ['H5', 'h5', '移动端', 'mobile'],
1799
+ 'admin': ['admin', '后台', '管理端', 'Admin'],
1800
+ 'web': ['Web', 'web', '前端', '桌面'],
1801
+ 'app': ['App', 'app', '客户端'],
1802
+ 'miniapp': ['小程序', 'miniapp'],
1803
+ };
1804
+ const platformKws = platformLabels[platform] || [platform];
1805
+ const platformSection = extractRelevantSection(techContent, taskName, platformKws.join(' '));
1806
+ if (platformSection)
1807
+ return platformSection;
1808
+ // 回退:用任务名匹配
1809
+ return extractRelevantSection(techContent, taskName);
1810
+ }
1811
+ /** 从 specContents 提取任务级 TECH 内容 */
1812
+ function extractTaskTechContent(specContents, section, platform) {
1813
+ const techMd = specContents['TECH.md'];
1814
+ if (!techMd)
1815
+ return '';
1816
+ if (platform && platform !== 'backend') {
1817
+ return extractFrontendContent(techMd, section.name, platform);
1818
+ }
1819
+ // 后端/共享:提取技术方案、架构、接口设计、数据模型等
1820
+ const techSection = extractRelevantSection(techMd, section.name, '技术方案 架构 接口设计 数据模型 模块设计');
1821
+ return techSection;
1822
+ }
1678
1823
  // 前端专属:组件树
1679
- function generateComponentTree(section, platform) {
1824
+ function generateComponentTree(section, platform, material) {
1825
+ if (material && material.trim().length > 30) {
1826
+ return `# ${section.name} — 组件树 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1827
+ }
1680
1828
  return `# ${section.name} — 组件树 (${platform})
1681
1829
 
1682
1830
  > split | ${new Date().toISOString().split('T')[0]}
1683
1831
 
1684
1832
  ## 页面结构
1685
- <!-- AI-FILL: 根据需求描述页面的组件层级 -->
1833
+ <!-- 从 analyze TECH.md 自动提取,若无内容则待补充 -->
1686
1834
 
1687
1835
  ## 组件清单
1688
1836
  | 组件 | 路径 | 类型 | 状态 |
1689
1837
  | :--- | :--- | :--- | :--- |
1690
- | _待AI分析_ | — | — | — |
1838
+ | _待补充_ | — | — | — |
1691
1839
 
1692
1840
  ## 共享组件
1693
1841
  | 组件 | 来源 | 用途 |
@@ -1696,7 +1844,10 @@ function generateComponentTree(section, platform) {
1696
1844
  `;
1697
1845
  }
1698
1846
  // 前端专属:路由
1699
- function generateRoutesDoc(section, platform) {
1847
+ function generateRoutesDoc(section, platform, material) {
1848
+ if (material && material.trim().length > 30) {
1849
+ return `# ${section.name} — 路由设计 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1850
+ }
1700
1851
  return `# ${section.name} — 路由设计 (${platform})
1701
1852
 
1702
1853
  > split | ${new Date().toISOString().split('T')[0]}
@@ -1711,7 +1862,10 @@ function generateRoutesDoc(section, platform) {
1711
1862
  `;
1712
1863
  }
1713
1864
  // 前端专属:状态管理
1714
- function generateStateDoc(section, platform) {
1865
+ function generateStateDoc(section, platform, material) {
1866
+ if (material && material.trim().length > 30) {
1867
+ return `# ${section.name} — 状态管理 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1868
+ }
1715
1869
  return `# ${section.name} — 状态管理 (${platform})
1716
1870
 
1717
1871
  > split | ${new Date().toISOString().split('T')[0]}
@@ -1731,7 +1885,10 @@ function generateStateDoc(section, platform) {
1731
1885
  `;
1732
1886
  }
1733
1887
  // 前端专属:样式规范
1734
- function generateStyleGuide(section, platform) {
1888
+ function generateStyleGuide(section, platform, material) {
1889
+ if (material && material.trim().length > 30) {
1890
+ return `# ${section.name} — 样式规范 (${platform})\n\n> 来源: analyze → TECH.md(自动提取)\n\n${material.trim()}\n`;
1891
+ }
1735
1892
  const isH5 = platform.includes('h5') || platform.includes('mobile');
1736
1893
  const isMiniapp = platform.includes('miniapp') || platform.includes('小程序');
1737
1894
  const styleCtx = isH5 ? '移动端 H5,注意触控交互和移动适配' :