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.
Files changed (121) hide show
  1. package/.agents/skills/spec-ask/SKILL.md +24 -4
  2. package/README.md +10 -0
  3. package/dist/cli.js +18 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/about.d.ts.map +1 -1
  6. package/dist/commands/about.js +10 -3
  7. package/dist/commands/about.js.map +1 -1
  8. package/dist/commands/analyze.d.ts +3 -0
  9. package/dist/commands/analyze.d.ts.map +1 -1
  10. package/dist/commands/analyze.js +178 -11
  11. package/dist/commands/analyze.js.map +1 -1
  12. package/dist/commands/change.d.ts.map +1 -1
  13. package/dist/commands/change.js +16 -4
  14. package/dist/commands/change.js.map +1 -1
  15. package/dist/commands/doc2spec.d.ts +1 -0
  16. package/dist/commands/doc2spec.d.ts.map +1 -1
  17. package/dist/commands/doc2spec.js +175 -0
  18. package/dist/commands/doc2spec.js.map +1 -1
  19. package/dist/commands/done.js +25 -5
  20. package/dist/commands/done.js.map +1 -1
  21. package/dist/commands/execute.d.ts.map +1 -1
  22. package/dist/commands/execute.js +20 -6
  23. package/dist/commands/execute.js.map +1 -1
  24. package/dist/commands/help.js +32 -5
  25. package/dist/commands/help.js.map +1 -1
  26. package/dist/commands/iteration/create.d.ts.map +1 -1
  27. package/dist/commands/iteration/create.js +40 -15
  28. package/dist/commands/iteration/create.js.map +1 -1
  29. package/dist/commands/iteration/split.d.ts.map +1 -1
  30. package/dist/commands/iteration/split.js +309 -36
  31. package/dist/commands/iteration/split.js.map +1 -1
  32. package/dist/commands/knowledge.d.ts +6 -0
  33. package/dist/commands/knowledge.d.ts.map +1 -0
  34. package/dist/commands/knowledge.js +94 -0
  35. package/dist/commands/knowledge.js.map +1 -0
  36. package/dist/commands/rag-index.d.ts +19 -0
  37. package/dist/commands/rag-index.d.ts.map +1 -0
  38. package/dist/commands/rag-index.js +272 -0
  39. package/dist/commands/rag-index.js.map +1 -0
  40. package/dist/commands/refresh.d.ts +21 -0
  41. package/dist/commands/refresh.d.ts.map +1 -0
  42. package/dist/commands/refresh.js +185 -0
  43. package/dist/commands/refresh.js.map +1 -0
  44. package/dist/commands/sync-global.d.ts.map +1 -1
  45. package/dist/commands/sync-global.js +59 -0
  46. package/dist/commands/sync-global.js.map +1 -1
  47. package/dist/core/ai-context-generator.d.ts +2 -0
  48. package/dist/core/ai-context-generator.d.ts.map +1 -1
  49. package/dist/core/ai-context-generator.js +8 -6
  50. package/dist/core/ai-context-generator.js.map +1 -1
  51. package/dist/core/analyze-engine.d.ts +19 -0
  52. package/dist/core/analyze-engine.d.ts.map +1 -1
  53. package/dist/core/analyze-engine.js +326 -9
  54. package/dist/core/analyze-engine.js.map +1 -1
  55. package/dist/core/ask-engine.d.ts.map +1 -1
  56. package/dist/core/ask-engine.js +134 -4
  57. package/dist/core/ask-engine.js.map +1 -1
  58. package/dist/core/ask-host-ai.d.ts.map +1 -1
  59. package/dist/core/ask-host-ai.js +9 -6
  60. package/dist/core/ask-host-ai.js.map +1 -1
  61. package/dist/core/code-scanner.d.ts +24 -2
  62. package/dist/core/code-scanner.d.ts.map +1 -1
  63. package/dist/core/code-scanner.js +310 -9
  64. package/dist/core/code-scanner.js.map +1 -1
  65. package/dist/core/context-builder.d.ts.map +1 -1
  66. package/dist/core/context-builder.js +28 -9
  67. package/dist/core/context-builder.js.map +1 -1
  68. package/dist/core/decay-detector.d.ts +2 -2
  69. package/dist/core/decay-detector.d.ts.map +1 -1
  70. package/dist/core/decay-detector.js +135 -6
  71. package/dist/core/decay-detector.js.map +1 -1
  72. package/dist/core/git-integration.d.ts +50 -2
  73. package/dist/core/git-integration.d.ts.map +1 -1
  74. package/dist/core/git-integration.js +217 -27
  75. package/dist/core/git-integration.js.map +1 -1
  76. package/dist/core/global-knowledge.d.ts +25 -0
  77. package/dist/core/global-knowledge.d.ts.map +1 -0
  78. package/dist/core/global-knowledge.js +228 -0
  79. package/dist/core/global-knowledge.js.map +1 -0
  80. package/dist/core/index-guard.d.ts +57 -0
  81. package/dist/core/index-guard.d.ts.map +1 -0
  82. package/dist/core/index-guard.js +202 -0
  83. package/dist/core/index-guard.js.map +1 -0
  84. package/dist/core/intent-cache.d.ts +3 -1
  85. package/dist/core/intent-cache.d.ts.map +1 -1
  86. package/dist/core/intent-cache.js +33 -3
  87. package/dist/core/intent-cache.js.map +1 -1
  88. package/dist/core/intent-recognition.d.ts.map +1 -1
  89. package/dist/core/intent-recognition.js +21 -2
  90. package/dist/core/intent-recognition.js.map +1 -1
  91. package/dist/core/knowledge-graph.d.ts +18 -3
  92. package/dist/core/knowledge-graph.d.ts.map +1 -1
  93. package/dist/core/knowledge-graph.js +539 -42
  94. package/dist/core/knowledge-graph.js.map +1 -1
  95. package/dist/core/knowledge-visualizer.d.ts +19 -0
  96. package/dist/core/knowledge-visualizer.d.ts.map +1 -0
  97. package/dist/core/knowledge-visualizer.js +528 -0
  98. package/dist/core/knowledge-visualizer.js.map +1 -0
  99. package/dist/core/next-steps.js +6 -0
  100. package/dist/core/next-steps.js.map +1 -1
  101. package/dist/core/platform-registry.d.ts +5 -0
  102. package/dist/core/platform-registry.d.ts.map +1 -1
  103. package/dist/core/platform-registry.js +52 -0
  104. package/dist/core/platform-registry.js.map +1 -1
  105. package/dist/core/prompt-builder.d.ts +3 -2
  106. package/dist/core/prompt-builder.d.ts.map +1 -1
  107. package/dist/core/prompt-builder.js +361 -72
  108. package/dist/core/prompt-builder.js.map +1 -1
  109. package/dist/core/rag-engine.d.ts +152 -0
  110. package/dist/core/rag-engine.d.ts.map +1 -0
  111. package/dist/core/rag-engine.js +816 -0
  112. package/dist/core/rag-engine.js.map +1 -0
  113. package/dist/core/spec-merger.d.ts +133 -0
  114. package/dist/core/spec-merger.d.ts.map +1 -0
  115. package/dist/core/spec-merger.js +735 -0
  116. package/dist/core/spec-merger.js.map +1 -0
  117. package/dist/core/unified-retrieval.d.ts +141 -0
  118. package/dist/core/unified-retrieval.d.ts.map +1 -0
  119. package/dist/core/unified-retrieval.js +471 -0
  120. package/dist/core/unified-retrieval.js.map +1 -0
  121. 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: 优先用 AI 生成的实际内容,回退到模板
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
- 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));
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
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platform));
1033
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'ROUTES.md'), generateRoutesDoc(section, platform));
1034
- await (0, fs_extra_1.writeFile)((0, path_1.join)(platformDir, 'STATE.md'), generateStateDoc(section, platform));
1035
- 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));
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
- 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));
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
- <!-- AI-FILL: 根据需求描述页面的组件层级 -->
1833
+ <!-- 从 analyze TECH.md 自动提取,若无内容则待补充 -->
1585
1834
 
1586
1835
  ## 组件清单
1587
1836
  | 组件 | 路径 | 类型 | 状态 |
1588
1837
  | :--- | :--- | :--- | :--- |
1589
- | _待AI分析_ | — | — | — |
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": "所属功能单元(必填!如:用户管理、订单系统、支付模块)",\n`;
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 必须填写**:功能单元名称,不是章节名。同一功能模块的任务填相同的值。\n`;
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`;