speccore 6.6.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 +24 -4
- package/README.md +10 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +10 -3
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/analyze.d.ts +3 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +178 -11
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/change.d.ts.map +1 -1
- package/dist/commands/change.js +16 -4
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/doc2spec.d.ts +1 -0
- package/dist/commands/doc2spec.d.ts.map +1 -1
- package/dist/commands/doc2spec.js +175 -0
- package/dist/commands/doc2spec.js.map +1 -1
- package/dist/commands/done.js +25 -5
- package/dist/commands/done.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +20 -6
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/help.js +32 -5
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/iteration/create.d.ts.map +1 -1
- package/dist/commands/iteration/create.js +40 -15
- package/dist/commands/iteration/create.js.map +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +309 -36
- 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/rag-index.d.ts +19 -0
- package/dist/commands/rag-index.d.ts.map +1 -0
- package/dist/commands/rag-index.js +272 -0
- package/dist/commands/rag-index.js.map +1 -0
- package/dist/commands/refresh.d.ts +21 -0
- package/dist/commands/refresh.d.ts.map +1 -0
- package/dist/commands/refresh.js +185 -0
- package/dist/commands/refresh.js.map +1 -0
- package/dist/commands/sync-global.d.ts.map +1 -1
- package/dist/commands/sync-global.js +59 -0
- package/dist/commands/sync-global.js.map +1 -1
- package/dist/core/ai-context-generator.d.ts +2 -0
- package/dist/core/ai-context-generator.d.ts.map +1 -1
- package/dist/core/ai-context-generator.js +8 -6
- package/dist/core/ai-context-generator.js.map +1 -1
- package/dist/core/analyze-engine.d.ts +19 -0
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +326 -9
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +134 -4
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/ask-host-ai.d.ts.map +1 -1
- package/dist/core/ask-host-ai.js +9 -6
- package/dist/core/ask-host-ai.js.map +1 -1
- package/dist/core/code-scanner.d.ts +24 -2
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +310 -9
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/context-builder.d.ts.map +1 -1
- package/dist/core/context-builder.js +28 -9
- package/dist/core/context-builder.js.map +1 -1
- package/dist/core/decay-detector.d.ts +2 -2
- package/dist/core/decay-detector.d.ts.map +1 -1
- package/dist/core/decay-detector.js +135 -6
- package/dist/core/decay-detector.js.map +1 -1
- package/dist/core/git-integration.d.ts +50 -2
- package/dist/core/git-integration.d.ts.map +1 -1
- package/dist/core/git-integration.js +217 -27
- package/dist/core/git-integration.js.map +1 -1
- package/dist/core/global-knowledge.d.ts +25 -0
- package/dist/core/global-knowledge.d.ts.map +1 -0
- package/dist/core/global-knowledge.js +228 -0
- package/dist/core/global-knowledge.js.map +1 -0
- package/dist/core/index-guard.d.ts +57 -0
- package/dist/core/index-guard.d.ts.map +1 -0
- package/dist/core/index-guard.js +202 -0
- package/dist/core/index-guard.js.map +1 -0
- package/dist/core/intent-cache.d.ts +3 -1
- package/dist/core/intent-cache.d.ts.map +1 -1
- package/dist/core/intent-cache.js +33 -3
- package/dist/core/intent-cache.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 +18 -3
- package/dist/core/knowledge-graph.d.ts.map +1 -1
- package/dist/core/knowledge-graph.js +539 -42
- 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/platform-registry.d.ts +5 -0
- package/dist/core/platform-registry.d.ts.map +1 -1
- package/dist/core/platform-registry.js +52 -0
- package/dist/core/platform-registry.js.map +1 -1
- package/dist/core/prompt-builder.d.ts +3 -2
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +361 -72
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/rag-engine.d.ts +152 -0
- package/dist/core/rag-engine.d.ts.map +1 -0
- package/dist/core/rag-engine.js +816 -0
- package/dist/core/rag-engine.js.map +1 -0
- 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 +141 -0
- package/dist/core/unified-retrieval.d.ts.map +1 -0
- package/dist/core/unified-retrieval.js +471 -0
- package/dist/core/unified-retrieval.js.map +1 -0
- package/package.json +1 -1
|
@@ -45,6 +45,7 @@ const next_steps_1 = require("../../core/next-steps");
|
|
|
45
45
|
const readline_1 = require("readline");
|
|
46
46
|
const prompt_builder_1 = require("../../core/prompt-builder");
|
|
47
47
|
const platform_registry_1 = require("../../core/platform-registry");
|
|
48
|
+
const index_guard_1 = require("../../core/index-guard");
|
|
48
49
|
/** 将名称转为目录安全的短 slug(2-4 词) */
|
|
49
50
|
function slugify(name) {
|
|
50
51
|
const cleaned = name
|
|
@@ -252,6 +253,12 @@ async function iterationSplitCommand(options) {
|
|
|
252
253
|
section._reqContent = task.reqContent;
|
|
253
254
|
if (task.techContent)
|
|
254
255
|
section._techContent = task.techContent;
|
|
256
|
+
if (task.sourceFile)
|
|
257
|
+
section._sourceFile = task.sourceFile;
|
|
258
|
+
if (task.functionalUnit)
|
|
259
|
+
section.functionalUnit = task.functionalUnit;
|
|
260
|
+
if (task.reason)
|
|
261
|
+
section._reason = task.reason;
|
|
255
262
|
if (taskScopePlatforms.length > 0)
|
|
256
263
|
section._scopePlatforms = taskScopePlatforms;
|
|
257
264
|
sections.push(section);
|
|
@@ -416,7 +423,7 @@ async function iterationSplitCommand(options) {
|
|
|
416
423
|
const taskTopic = sec._topic || slugify(sec.name);
|
|
417
424
|
const { id: taskId } = await (0, global_counters_1.nextTaskId)(sec.name, taskTopic);
|
|
418
425
|
sec._taskId = taskId;
|
|
419
|
-
await createTaskFromSection(iterDirFull, taskId, sec, allPlatforms, taskType);
|
|
426
|
+
await createTaskFromSection(iterDirFull, taskId, sec, allPlatforms, taskType, sections);
|
|
420
427
|
createdSections.push(sec);
|
|
421
428
|
logger_1.logger.info(` ✅ 创建: ${taskId} - [${taskType}] ${sec.name}`);
|
|
422
429
|
}
|
|
@@ -465,6 +472,8 @@ async function iterationSplitCommand(options) {
|
|
|
465
472
|
}
|
|
466
473
|
return;
|
|
467
474
|
}
|
|
475
|
+
// 命令前索引新鲜度检查(非阻塞)
|
|
476
|
+
await (0, index_guard_1.warnIfIndexStale)(process.cwd(), 'split', options.iteration);
|
|
468
477
|
const spinner = new logger_1.Spinner('Splitting requirements into tasks');
|
|
469
478
|
spinner.start();
|
|
470
479
|
try {
|
|
@@ -542,6 +551,36 @@ async function iterationSplitCommand(options) {
|
|
|
542
551
|
}
|
|
543
552
|
}
|
|
544
553
|
}
|
|
554
|
+
// 读取功能模块分析(020-specs/features/)
|
|
555
|
+
const iterFeaturesDir = (0, path_1.join)(specDir2, 'features');
|
|
556
|
+
if (await (0, fs_extra_1.pathExists)(iterFeaturesDir)) {
|
|
557
|
+
const featureEntries = await (0, fs_extra_1.readdir)(iterFeaturesDir, { withFileTypes: true });
|
|
558
|
+
for (const fe of featureEntries) {
|
|
559
|
+
if (!fe.name.startsWith('.') && fe.name.endsWith('.md')) {
|
|
560
|
+
const fp = (0, path_1.join)(iterFeaturesDir, fe.name);
|
|
561
|
+
const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
|
|
562
|
+
if (content.trim().length > 50 && !content.trim().match(/^#+\s*\u5f85\u586b\u5145|^<!--\s*AI-FILL/m)) {
|
|
563
|
+
specContents.push({ name: `features/${fe.name}`, content });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
// 读取类型文档分析(020-specs/{bugs,refactors,research}/)
|
|
569
|
+
for (const typeDir of ['bugs', 'refactors', 'research']) {
|
|
570
|
+
const typeDirPath = (0, path_1.join)(specDir2, typeDir);
|
|
571
|
+
if (!(await (0, fs_extra_1.pathExists)(typeDirPath)))
|
|
572
|
+
continue;
|
|
573
|
+
const typeEntries = await (0, fs_extra_1.readdir)(typeDirPath, { withFileTypes: true });
|
|
574
|
+
for (const te of typeEntries) {
|
|
575
|
+
if (!te.isFile() || !te.name.endsWith('.md'))
|
|
576
|
+
continue;
|
|
577
|
+
const fp = (0, path_1.join)(typeDirPath, te.name);
|
|
578
|
+
const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
|
|
579
|
+
if (content.trim().length > 50 && !content.trim().match(/^#+\s*\u5f85\u586b\u5145|^<!--\s*AI-FILL/m)) {
|
|
580
|
+
specContents.push({ name: `${typeDir}/${te.name}`, content });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
545
584
|
// 粒度推荐(基于 STAFFING 人数)
|
|
546
585
|
const staffing = readStaffing(iterationDir);
|
|
547
586
|
const teamSize = staffing ? staffing.length : 0;
|
|
@@ -620,7 +659,7 @@ async function iterationSplitCommand(options) {
|
|
|
620
659
|
}
|
|
621
660
|
for (const section of approved) {
|
|
622
661
|
const taskId = section._taskId;
|
|
623
|
-
await createTaskFromSection(iterationDir, taskId, section, platforms, section._taskType);
|
|
662
|
+
await createTaskFromSection(iterationDir, taskId, section, platforms, section._taskType, approved);
|
|
624
663
|
}
|
|
625
664
|
spinner.stop(`✅ 创建了 ${approved.length} 个任务`);
|
|
626
665
|
return;
|
|
@@ -680,7 +719,7 @@ async function iterationSplitCommand(options) {
|
|
|
680
719
|
break;
|
|
681
720
|
}
|
|
682
721
|
if (resp?.toLowerCase() === 'y' || resp === '') {
|
|
683
|
-
await createTaskFromSection(iterationDir, taskId, sections[i], platforms, sections[i]._taskType);
|
|
722
|
+
await createTaskFromSection(iterationDir, taskId, sections[i], platforms, sections[i]._taskType, sections);
|
|
684
723
|
created++;
|
|
685
724
|
logger_1.logger.info(` ✅ ${taskId}`);
|
|
686
725
|
}
|
|
@@ -698,7 +737,7 @@ async function iterationSplitCommand(options) {
|
|
|
698
737
|
// Default: create all
|
|
699
738
|
for (let i = 0; i < sections.length; i++) {
|
|
700
739
|
const taskId = sections[i]._taskId;
|
|
701
|
-
await createTaskFromSection(iterationDir, taskId, sections[i], platforms, sections[i]._taskType);
|
|
740
|
+
await createTaskFromSection(iterationDir, taskId, sections[i], platforms, sections[i]._taskType, sections);
|
|
702
741
|
}
|
|
703
742
|
await generateImpactGraph(iterationDir, sections, platforms);
|
|
704
743
|
await generateEnvExample(iterationDir, sections);
|
|
@@ -710,7 +749,7 @@ async function iterationSplitCommand(options) {
|
|
|
710
749
|
// Create tasks(使用预分配的 ID)
|
|
711
750
|
for (let i = 0; i < sections.length; i++) {
|
|
712
751
|
const taskId = sections[i]._taskId;
|
|
713
|
-
await createTaskFromSection(iterationDir, taskId, sections[i], platforms, sections[i]._taskType);
|
|
752
|
+
await createTaskFromSection(iterationDir, taskId, sections[i], platforms, sections[i]._taskType, sections);
|
|
714
753
|
}
|
|
715
754
|
// ── Generate impact graph + risk scores ──
|
|
716
755
|
await generateImpactGraph(iterationDir, sections, platforms);
|
|
@@ -837,13 +876,15 @@ function filterTemplateNoise(sections) {
|
|
|
837
876
|
return true;
|
|
838
877
|
});
|
|
839
878
|
}
|
|
840
|
-
async function createTaskFromSection(iterationDir, taskId, section, allPlatforms, taskType = 'feature') {
|
|
879
|
+
async function createTaskFromSection(iterationDir, taskId, section, allPlatforms, taskType = 'feature', allSections) {
|
|
841
880
|
// taskId 已含 slug(nextTaskId 返回 Task-NNN-slug),直接用
|
|
842
881
|
const taskDir = (0, path_1.join)(iterationDir, '030-tasks', taskType, taskId);
|
|
843
882
|
const taskPlatforms = section._scopePlatforms || (section.platform ? [section.platform] : allPlatforms);
|
|
844
883
|
const complexity = section._complexity || { estimatedHours: 2, priority: 'medium', complexity: 'medium', apiCount: 0, dbCount: 0, pageCount: 0, wordCount: 0 };
|
|
845
884
|
const owner = section._owner || '未分配';
|
|
846
885
|
const today = new Date().toISOString().split('T')[0];
|
|
886
|
+
// 加载迭代级 analyze 产出(020-specs/),用于填充任务级文件
|
|
887
|
+
const specContents = await loadSpecContents(iterationDir);
|
|
847
888
|
// 预生成所有端的子任务 ID(保证同一端在所有文件中 ID 一致)
|
|
848
889
|
const taskNum = taskId.replace(/^Task-/, '');
|
|
849
890
|
const subtaskIdMap = new Map();
|
|
@@ -868,6 +909,7 @@ ${taskId}/
|
|
|
868
909
|
├── README.md <-- 本文件
|
|
869
910
|
├── .meta/ <-- 任务元信息(type/status/owner/created-at)
|
|
870
911
|
├── _shared/ <-- 共享规格(所有端共用)
|
|
912
|
+
│ ├── CONTEXT.md <-- 任务上下文(来源追溯 + 原始描述 + 关联任务)
|
|
871
913
|
│ ├── REQ.md <-- 需求描述(API + 数据模型 + 业务规则)
|
|
872
914
|
│ ├── TECH.md <-- 技术方案(架构/接口设计/核心逻辑)
|
|
873
915
|
│ ├── SCHEMA.md <-- 数据库设计(如有)
|
|
@@ -898,6 +940,7 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
|
|
|
898
940
|
运行 \`speccore execute -t ${taskId} --platform {端}\` 时:
|
|
899
941
|
|
|
900
942
|
### 必读(自动嵌入)
|
|
943
|
+
- \`_shared/CONTEXT.md\` — 任务上下文(来源 + 关联)
|
|
901
944
|
- \`_shared/REQ.md\` — 共享需求
|
|
902
945
|
- \`_shared/TECH.md\` — 共享技术方案
|
|
903
946
|
- \`{端}/TASK.md\` — 该端子任务详情
|
|
@@ -930,10 +973,15 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
|
|
|
930
973
|
const apiLines = section.content.split('\n').filter(l => l.includes('| GET') || l.includes('| POST') || l.includes('| PUT') || l.includes('| DELETE') || l.includes('| PATCH'));
|
|
931
974
|
const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
|
|
932
975
|
const aiTechContent = section._techContent;
|
|
933
|
-
// TECH.md
|
|
976
|
+
// 从 analyze TECH.md 提取本任务相关内容
|
|
977
|
+
const specTechContent = extractTaskTechContent(specContents, section);
|
|
978
|
+
// TECH.md: 优先 AI 生成 → 回退 analyze 提取 → 回退模板
|
|
934
979
|
if (aiTechContent && aiTechContent.length > 50) {
|
|
935
980
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
|
|
936
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
|
+
}
|
|
937
985
|
else {
|
|
938
986
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'TECH.md'), `# ${section.name} - 技术方案
|
|
939
987
|
|
|
@@ -964,7 +1012,8 @@ ${apiDesc}
|
|
|
964
1012
|
`);
|
|
965
1013
|
}
|
|
966
1014
|
if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
|
|
967
|
-
|
|
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));
|
|
968
1017
|
}
|
|
969
1018
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'CHANGELOG.md'), `# ${section.name} - 变更记录
|
|
970
1019
|
|
|
@@ -1027,14 +1076,22 @@ ${apiDesc}
|
|
|
1027
1076
|
| :--- | :--- | :--- |
|
|
1028
1077
|
| ${today} | 创建子任务 | CLI |
|
|
1029
1078
|
`);
|
|
1030
|
-
//
|
|
1079
|
+
// 非 backend 端额外生成前端设计文档(从 analyze TECH.md 提取)
|
|
1031
1080
|
if (!isBackend) {
|
|
1032
|
-
|
|
1033
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, '
|
|
1034
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, '
|
|
1035
|
-
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));
|
|
1036
1086
|
}
|
|
1037
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
|
+
}
|
|
1038
1095
|
// ── 生成端注册表 _shared/PLATFORMS.md ──
|
|
1039
1096
|
const platformEntries = taskPlatforms.map((p) => ({
|
|
1040
1097
|
name: p,
|
|
@@ -1042,25 +1099,84 @@ ${apiDesc}
|
|
|
1042
1099
|
owner,
|
|
1043
1100
|
}));
|
|
1044
1101
|
await (0, platform_registry_1.generatePlatformsRegistry)(taskDir, taskId, platformEntries);
|
|
1045
|
-
// 确保至少有 backend 目录
|
|
1046
|
-
if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
|
|
1047
|
-
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'src'));
|
|
1048
|
-
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', 'tests'));
|
|
1049
|
-
}
|
|
1050
1102
|
// ── 5. 执行产出 99-artifacts/ ──
|
|
1051
1103
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '99-artifacts'));
|
|
1052
|
-
|
|
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));
|
|
1053
1109
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md'), generateReviewChecklist(section));
|
|
1054
1110
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPLOY.md'), generateDeployChecklist(section));
|
|
1055
1111
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ERROR_CODES.md'), generateErrorCodes(section));
|
|
1056
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'RISK.md'), generateRiskTemplate(section));
|
|
1057
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPS.md'), generateDepsTemplate(section));
|
|
1058
|
-
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));
|
|
1059
1115
|
const adr = generateAdr(section);
|
|
1060
1116
|
if (adr) {
|
|
1061
1117
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ADR.md'), adr);
|
|
1062
1118
|
}
|
|
1063
|
-
// ── 6.
|
|
1119
|
+
// ── 6. 任务上下文 CONTEXT.md ──
|
|
1120
|
+
const sourceFile = section._sourceFile || '';
|
|
1121
|
+
const topic = section._topic || slugify(section.name);
|
|
1122
|
+
// 推导 010-requirements 源路径
|
|
1123
|
+
const reqSourcePath = sourceFile
|
|
1124
|
+
? `010-requirements/${sourceFile}`
|
|
1125
|
+
: `010-requirements/${taskType === 'feature' ? 'features' : taskType === 'bugfix' ? 'bugs' : taskType === 'refactor' ? 'refactors' : 'research'}/${topic}.md`;
|
|
1126
|
+
// 推导 020-specs 分析文档路径
|
|
1127
|
+
const specSourcePath = sourceFile
|
|
1128
|
+
? `020-specs/${sourceFile}`
|
|
1129
|
+
: `020-specs/${taskType === 'feature' ? 'features' : taskType === 'bugfix' ? 'bugs' : taskType === 'refactor' ? 'refactors' : 'research'}/${topic}.md`;
|
|
1130
|
+
// 任务类型中文标签
|
|
1131
|
+
const typeLabels = { feature: '功能开发', bugfix: '缺陷修复', refactor: '重构优化', research: '技术调研' };
|
|
1132
|
+
const typeLabel = typeLabels[taskType] || taskType;
|
|
1133
|
+
// 收集同迭代的其它任务(用于关联关系)
|
|
1134
|
+
const relatedTasks = allSections
|
|
1135
|
+
? allSections
|
|
1136
|
+
.filter((s) => s !== section)
|
|
1137
|
+
.slice(0, 5)
|
|
1138
|
+
.map((s, idx) => {
|
|
1139
|
+
const sId = s._taskId || `Task-${String(allSections.indexOf(s) + 1).padStart(3, '0')}`;
|
|
1140
|
+
const sType = s._taskType || 'feature';
|
|
1141
|
+
return `- \`${sId}\` (${s.name}) — ${typeLabels[sType] || sType}`;
|
|
1142
|
+
})
|
|
1143
|
+
: [];
|
|
1144
|
+
// 提取原始描述摘要(从 REQ.md 内容截取前 500 字)
|
|
1145
|
+
const reqMdPath = (0, path_1.join)(taskDir, '_shared', 'REQ.md');
|
|
1146
|
+
let originalDesc = '';
|
|
1147
|
+
if (await (0, fs_extra_1.pathExists)(reqMdPath)) {
|
|
1148
|
+
const reqContent = await (0, fs_extra_1.readFile)(reqMdPath, 'utf-8');
|
|
1149
|
+
// 跳过标题行,取正文前 500 字
|
|
1150
|
+
const body = reqContent.replace(/^#[^\n]*\n/, '').trim();
|
|
1151
|
+
originalDesc = body.length > 500 ? body.slice(0, 500) + '...' : body;
|
|
1152
|
+
}
|
|
1153
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'CONTEXT.md'), `# 任务上下文
|
|
1154
|
+
|
|
1155
|
+
## 来源追溯
|
|
1156
|
+
|
|
1157
|
+
| 属性 | 值 |
|
|
1158
|
+
|:---|:---|
|
|
1159
|
+
| 任务类型 | ${typeLabel}(\`${taskType}\`) |
|
|
1160
|
+
| 需求文档 | \`${reqSourcePath}\` |
|
|
1161
|
+
| 分析文档 | \`${specSourcePath}\` |
|
|
1162
|
+
| 功能单元 | ${section.functionalUnit || section.name} |
|
|
1163
|
+
| 拆分原因 | ${section._reason || '—'} |
|
|
1164
|
+
|
|
1165
|
+
## 原始描述
|
|
1166
|
+
|
|
1167
|
+
${originalDesc || '> 待补充(执行 analyze 后自动生成)'}
|
|
1168
|
+
|
|
1169
|
+
## 关联任务
|
|
1170
|
+
|
|
1171
|
+
${relatedTasks.length > 0 ? relatedTasks.join('\n') : '> 暂无关联任务(split 时自动填充)'}
|
|
1172
|
+
|
|
1173
|
+
## 影响范围
|
|
1174
|
+
|
|
1175
|
+
| 端 | 影响说明 |
|
|
1176
|
+
|:---|:---|
|
|
1177
|
+
${taskPlatforms.map((p) => `| ${p} | 待补充 |`).join('\n')}
|
|
1178
|
+
`);
|
|
1179
|
+
// ── 7. 问题追踪 ──
|
|
1064
1180
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.issues.md'), `# ${section.name} - 问题追踪\n\n> 执行过程中发现的问题记录于此。\n\n`);
|
|
1065
1181
|
}
|
|
1066
1182
|
async function updateProjectGraph(iterationDir, sections) {
|
|
@@ -1084,7 +1200,10 @@ async function updateProjectGraph(iterationDir, sections) {
|
|
|
1084
1200
|
/**
|
|
1085
1201
|
* 根据需求内容自动生成测试用例框架
|
|
1086
1202
|
*/
|
|
1087
|
-
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
|
+
}
|
|
1088
1207
|
const name = section.name;
|
|
1089
1208
|
const content = section.content || '';
|
|
1090
1209
|
const isBackend = section.platform?.startsWith('后台') || false;
|
|
@@ -1324,8 +1443,11 @@ async function generateImpactGraph(iterationDir, sections, platforms) {
|
|
|
1324
1443
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(iterationDir, 'IMPACT.md'), impact);
|
|
1325
1444
|
logger_1.logger.info(`\nImpact analysis: ${iterationDir}/IMPACT.md`);
|
|
1326
1445
|
}
|
|
1327
|
-
function generateSchemaTemplate(section) {
|
|
1446
|
+
function generateSchemaTemplate(section, material) {
|
|
1328
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
|
+
}
|
|
1329
1451
|
return `# ${name} — Database Schema
|
|
1330
1452
|
|
|
1331
1453
|
> Auto-generated. Fill in DDL before development.
|
|
@@ -1563,30 +1685,157 @@ function generateAcceptanceCriteria(section) {
|
|
|
1563
1685
|
return acs;
|
|
1564
1686
|
}
|
|
1565
1687
|
// 风险评估
|
|
1566
|
-
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
|
+
}
|
|
1567
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`;
|
|
1568
1693
|
}
|
|
1569
1694
|
// 依赖清单
|
|
1570
|
-
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
|
+
}
|
|
1571
1699
|
return `# ${section.name} — 依赖清单\n\n## 上游依赖\n\n| 服务 | 版本 | 用途 | SLA |\n| :--- | :--- | :--- | :--- |\n| _待补充_ | — | — | — |\n\n## 下游影响\n\n| 服务 | 影响 | 通知 |\n| :--- | :--- | :--- |\n| _待补充_ | — | — |\n`;
|
|
1572
1700
|
}
|
|
1573
1701
|
// 监控指标
|
|
1574
|
-
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
|
+
}
|
|
1575
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`;
|
|
1576
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
|
+
}
|
|
1577
1823
|
// 前端专属:组件树
|
|
1578
|
-
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
|
+
}
|
|
1579
1828
|
return `# ${section.name} — 组件树 (${platform})
|
|
1580
1829
|
|
|
1581
1830
|
> split | ${new Date().toISOString().split('T')[0]}
|
|
1582
1831
|
|
|
1583
1832
|
## 页面结构
|
|
1584
|
-
<!--
|
|
1833
|
+
<!-- 从 analyze TECH.md 自动提取,若无内容则待补充 -->
|
|
1585
1834
|
|
|
1586
1835
|
## 组件清单
|
|
1587
1836
|
| 组件 | 路径 | 类型 | 状态 |
|
|
1588
1837
|
| :--- | :--- | :--- | :--- |
|
|
1589
|
-
| _
|
|
1838
|
+
| _待补充_ | — | — | — |
|
|
1590
1839
|
|
|
1591
1840
|
## 共享组件
|
|
1592
1841
|
| 组件 | 来源 | 用途 |
|
|
@@ -1595,7 +1844,10 @@ function generateComponentTree(section, platform) {
|
|
|
1595
1844
|
`;
|
|
1596
1845
|
}
|
|
1597
1846
|
// 前端专属:路由
|
|
1598
|
-
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
|
+
}
|
|
1599
1851
|
return `# ${section.name} — 路由设计 (${platform})
|
|
1600
1852
|
|
|
1601
1853
|
> split | ${new Date().toISOString().split('T')[0]}
|
|
@@ -1610,7 +1862,10 @@ function generateRoutesDoc(section, platform) {
|
|
|
1610
1862
|
`;
|
|
1611
1863
|
}
|
|
1612
1864
|
// 前端专属:状态管理
|
|
1613
|
-
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
|
+
}
|
|
1614
1869
|
return `# ${section.name} — 状态管理 (${platform})
|
|
1615
1870
|
|
|
1616
1871
|
> split | ${new Date().toISOString().split('T')[0]}
|
|
@@ -1630,7 +1885,10 @@ function generateStateDoc(section, platform) {
|
|
|
1630
1885
|
`;
|
|
1631
1886
|
}
|
|
1632
1887
|
// 前端专属:样式规范
|
|
1633
|
-
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
|
+
}
|
|
1634
1892
|
const isH5 = platform.includes('h5') || platform.includes('mobile');
|
|
1635
1893
|
const isMiniapp = platform.includes('miniapp') || platform.includes('小程序');
|
|
1636
1894
|
const styleCtx = isH5 ? '移动端 H5,注意触控交互和移动适配' :
|
|
@@ -1721,6 +1979,14 @@ function buildSplitPrompt(iteration, constitutionContent, reqContent, specConten
|
|
|
1721
1979
|
p += `- 低于当前粒度工时下限 → 合并到关联任务\n`;
|
|
1722
1980
|
p += `- 跨端功能 → 按端拆(后端 1 个 + 每个前端各 1 个)\n`;
|
|
1723
1981
|
p += `- 独立第三方集成(支付/短信/OSS)→ 独立任务\n\n`;
|
|
1982
|
+
// 类型文档 1:1 映射规则
|
|
1983
|
+
p += `### 类型文档拆分规则(bugs/refactors/research)\n`;
|
|
1984
|
+
p += `与 feature 不同,类型文档采用 **1:1 映射**——每个文档直接对应一个任务,不拆分不合并:\n`;
|
|
1985
|
+
p += `- \`bugs/xxx.md\` → 1 个 bugfix 任务(一个 bug 就是一个任务,不要把多个 bug 合并)\n`;
|
|
1986
|
+
p += `- \`refactors/xxx.md\` → 1 个 refactor 任务\n`;
|
|
1987
|
+
p += `- \`research/xxx.md\` → 1 个 research 任务\n`;
|
|
1988
|
+
p += `- \`features/xxx.md\` → 按功能单元拆分规则处理(可能 1~3 个 feature 任务)\n`;
|
|
1989
|
+
p += `- **sourceFile 必须填写**:类型文档任务的 sourceFile 就是该文档的相对路径(如 \`bugs/login-timeout.md\`)\n\n`;
|
|
1724
1990
|
p += `### 依赖关系\n`;
|
|
1725
1991
|
p += `- 基础模块(认证/数据库/配置)优先拆出,作为第一批任务\n`;
|
|
1726
1992
|
p += `- 依赖链深度 ≤ 3\n`;
|
|
@@ -1738,7 +2004,7 @@ function buildSplitPrompt(iteration, constitutionContent, reqContent, specConten
|
|
|
1738
2004
|
p += '```json\n';
|
|
1739
2005
|
p += `[\n {\n`;
|
|
1740
2006
|
p += ` "id": "Task-001",\n`;
|
|
1741
|
-
p += ` "functionalUnit": "
|
|
2007
|
+
p += ` "functionalUnit": "所属功能单元/影响域(必填!见下方类型规则)",\n`;
|
|
1742
2008
|
p += ` "name": "任务名称(中文)",\n`;
|
|
1743
2009
|
p += ` "topic": "english-slug-for-directory",\n`;
|
|
1744
2010
|
p += ` "type": "feature|bugfix|refactor|research",\n`;
|
|
@@ -1753,12 +2019,19 @@ function buildSplitPrompt(iteration, constitutionContent, reqContent, specConten
|
|
|
1753
2019
|
p += ` "acceptanceCriteria": ["AC1: ..."],\n`;
|
|
1754
2020
|
p += ` "risk": "low|medium|high",\n`;
|
|
1755
2021
|
p += ` "owner": "建议负责人",\n`;
|
|
2022
|
+
p += ` "sourceFile": "来源文档路径(如 bugs/login-timeout.md、features/user-auth.md)",\n`;
|
|
1756
2023
|
p += ` "reqContent": "需求描述内容(Markdown 格式,写入 REQ.md)",\n`;
|
|
1757
2024
|
p += ` "techContent": "技术方案内容(Markdown 格式,写入 TECH.md)"\n`;
|
|
1758
2025
|
p += ` }\n]\n`;
|
|
1759
2026
|
p += '```\n\n';
|
|
1760
|
-
p += `> **functionalUnit
|
|
2027
|
+
p += `> **functionalUnit 必须填写**:根据任务类型语义不同:\n`;
|
|
2028
|
+
p += `> - feature → 功能模块名(如:用户管理、订单系统、支付模块)\n`;
|
|
2029
|
+
p += `> - bugfix → 受影响组件/流程(如:登录流程、支付回调、数据同步)\n`;
|
|
2030
|
+
p += `> - refactor → 重构目标范围(如:数据库层、API网关、状态管理)\n`;
|
|
2031
|
+
p += `> - research → 研究主题(如:WebSocket方案、缓存策略)\n`;
|
|
2032
|
+
p += `> 同一模块/领域的任务填相同的值,用于粒度校验和任务分组\n`;
|
|
1761
2033
|
p += `> **topic** 必须是英文短横线格式(如 \`user-authentication\`、\`product-crud\`),用于生成任务目录名 Task-NNN-{topic}\n`;
|
|
2034
|
+
p += `> **sourceFile** 必须填写:该任务对应的 020-specs 源文档路径(如 \`bugs/login-timeout.md\`、\`features/user-auth.md\`、\`refactors/db-pool.md\`),用于在 CONTEXT.md 中生成来源追溯\n`;
|
|
1762
2035
|
p += `> **reqContent** 必须填写:该任务的需求描述(含业务规则、数据模型、接口定义),直接写入 REQ.md\n`;
|
|
1763
2036
|
p += `> **techContent** 必须填写:该任务的技术方案(含架构设计、核心逻辑、测试策略),直接写入 TECH.md\n`;
|
|
1764
2037
|
p += `> reqContent/techContent 是该任务的**子切面**,只包含该任务负责的部分,不是整个功能单元的内容\n\n`;
|