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.
- package/.agents/skills/spec-ask/SKILL.md +18 -4
- package/README.md +10 -0
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +1 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +37 -11
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/change.d.ts.map +1 -1
- package/dist/commands/change.js +13 -4
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/done.js +25 -5
- package/dist/commands/done.js.map +1 -1
- package/dist/commands/iteration/create.d.ts.map +1 -1
- package/dist/commands/iteration/create.js +2 -5
- package/dist/commands/iteration/create.js.map +1 -1
- package/dist/commands/iteration/split.js +184 -27
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/knowledge.d.ts +6 -0
- package/dist/commands/knowledge.d.ts.map +1 -0
- package/dist/commands/knowledge.js +94 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/sync-global.d.ts.map +1 -1
- package/dist/commands/sync-global.js +18 -0
- package/dist/commands/sync-global.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +23 -2
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +76 -5
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/context-builder.js +8 -8
- package/dist/core/context-builder.js.map +1 -1
- package/dist/core/intent-recognition.d.ts.map +1 -1
- package/dist/core/intent-recognition.js +21 -2
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/knowledge-graph.d.ts +11 -2
- package/dist/core/knowledge-graph.d.ts.map +1 -1
- package/dist/core/knowledge-graph.js +332 -21
- package/dist/core/knowledge-graph.js.map +1 -1
- package/dist/core/knowledge-visualizer.d.ts +19 -0
- package/dist/core/knowledge-visualizer.d.ts.map +1 -0
- package/dist/core/knowledge-visualizer.js +528 -0
- package/dist/core/knowledge-visualizer.js.map +1 -0
- package/dist/core/next-steps.js +6 -0
- package/dist/core/next-steps.js.map +1 -1
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +142 -2
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/spec-merger.d.ts +133 -0
- package/dist/core/spec-merger.d.ts.map +1 -0
- package/dist/core/spec-merger.js +735 -0
- package/dist/core/spec-merger.js.map +1 -0
- package/dist/core/unified-retrieval.d.ts +1 -0
- package/dist/core/unified-retrieval.d.ts.map +1 -1
- package/dist/core/unified-retrieval.js +15 -10
- package/dist/core/unified-retrieval.js.map +1 -1
- 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
|
|
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
|
-
|
|
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
|
-
|
|
1074
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, '
|
|
1075
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, '
|
|
1076
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, '
|
|
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
|
-
|
|
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
|
-
<!--
|
|
1833
|
+
<!-- 从 analyze TECH.md 自动提取,若无内容则待补充 -->
|
|
1686
1834
|
|
|
1687
1835
|
## 组件清单
|
|
1688
1836
|
| 组件 | 路径 | 类型 | 状态 |
|
|
1689
1837
|
| :--- | :--- | :--- | :--- |
|
|
1690
|
-
| _
|
|
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,注意触控交互和移动适配' :
|