speccore 8.3.159 → 8.3.161

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 (50) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/analyze.d.ts +16 -0
  4. package/dist/commands/analyze.d.ts.map +1 -1
  5. package/dist/commands/analyze.js +538 -316
  6. package/dist/commands/analyze.js.map +1 -1
  7. package/dist/commands/change.d.ts.map +1 -1
  8. package/dist/commands/change.js +14 -14
  9. package/dist/commands/change.js.map +1 -1
  10. package/dist/commands/execute.d.ts +1 -0
  11. package/dist/commands/execute.d.ts.map +1 -1
  12. package/dist/commands/execute.js +92 -10
  13. package/dist/commands/execute.js.map +1 -1
  14. package/dist/commands/init.d.ts.map +1 -1
  15. package/dist/commands/init.js +6 -2
  16. package/dist/commands/init.js.map +1 -1
  17. package/dist/commands/iteration/split.d.ts.map +1 -1
  18. package/dist/commands/iteration/split.js +299 -52
  19. package/dist/commands/iteration/split.js.map +1 -1
  20. package/dist/commands/plan.d.ts.map +1 -1
  21. package/dist/commands/plan.js +45 -36
  22. package/dist/commands/plan.js.map +1 -1
  23. package/dist/core/agent-adapter.d.ts +81 -0
  24. package/dist/core/agent-adapter.d.ts.map +1 -0
  25. package/dist/core/agent-adapter.js +425 -0
  26. package/dist/core/agent-adapter.js.map +1 -0
  27. package/dist/core/ask-engine.d.ts.map +1 -1
  28. package/dist/core/ask-engine.js +60 -0
  29. package/dist/core/ask-engine.js.map +1 -1
  30. package/dist/core/pipeline-engine.d.ts +49 -8
  31. package/dist/core/pipeline-engine.d.ts.map +1 -1
  32. package/dist/core/pipeline-engine.js +221 -34
  33. package/dist/core/pipeline-engine.js.map +1 -1
  34. package/dist/core/prompt-builder.d.ts +12 -3
  35. package/dist/core/prompt-builder.d.ts.map +1 -1
  36. package/dist/core/prompt-builder.js +238 -15
  37. package/dist/core/prompt-builder.js.map +1 -1
  38. package/dist/core/requirement-clarifier.d.ts.map +1 -1
  39. package/dist/core/requirement-clarifier.js +36 -3
  40. package/dist/core/requirement-clarifier.js.map +1 -1
  41. package/dist/core/schemas/task.schema.d.ts +2 -2
  42. package/dist/core/structured-extractor.d.ts +2 -0
  43. package/dist/core/structured-extractor.d.ts.map +1 -1
  44. package/dist/core/structured-extractor.js +61 -0
  45. package/dist/core/structured-extractor.js.map +1 -1
  46. package/dist/core/unified-retrieval.d.ts +2 -1
  47. package/dist/core/unified-retrieval.d.ts.map +1 -1
  48. package/dist/core/unified-retrieval.js +34 -15
  49. package/dist/core/unified-retrieval.js.map +1 -1
  50. package/package.json +1 -1
@@ -49,6 +49,7 @@ const spec_paths_1 = require("../../core/spec-paths");
49
49
  const spec_skeleton_1 = require("../../core/spec-skeleton");
50
50
  const questions_1 = require("../../core/questions");
51
51
  const pipeline_engine_1 = require("../../core/pipeline-engine");
52
+ const agent_adapter_1 = require("../../core/agent-adapter");
52
53
  const code_scanner_1 = require("../../core/code-scanner");
53
54
  const knowledge_graph_1 = require("../../core/knowledge-graph");
54
55
  const git_integration_1 = require("../../core/git-integration");
@@ -170,6 +171,43 @@ function generateSubtaskId(parentTaskId, platform) {
170
171
  // 因为每个任务每个端只有一个子任务,所以 {taskId}-{platform} 已经唯一
171
172
  return `Task-${parentTaskId}-${platform}`;
172
173
  }
174
+ /**
175
+ * v8.3.160+: 根据功能单元名称关键词推断涉及的端
176
+ * 当 Spec 匹配失败时,使用关键词推断替代回退到所有端
177
+ */
178
+ function inferPlatformsByKeywords(featureName, allPlatforms) {
179
+ const name = featureName.toLowerCase();
180
+ // 端分类规则:根据端名特征判断端类型
181
+ const backendPlatforms = allPlatforms.filter(p => /service|api|backend|server|core/i.test(p));
182
+ const adminPlatforms = allPlatforms.filter(p => /admin|web|pc|manage|dashboard|portal/i.test(p) && !/mobile|h5|mini|app/i.test(p));
183
+ const mobilePlatforms = allPlatforms.filter(p => /h5|mobile|app|mini|ios|android|wechat/i.test(p));
184
+ // 关键词 → 端类型映射
185
+ const keywordMap = [
186
+ // 纯后端功能
187
+ { keywords: ['接口', 'api', '服务', 'service', '数据表', 'entity', '数据库', 'db', '迁移', 'migration', '脚本', 'job', '定时', 'cron', '缓存', 'cache', '队列', 'queue'], platforms: backendPlatforms },
188
+ // 管理端功能
189
+ { keywords: ['报表', '统计', '管理', '配置', '审核', '审批', '后台', 'admin', 'dashboard', '运营', '数据看板', '权限分配', '角色管理'], platforms: [...backendPlatforms, ...adminPlatforms] },
190
+ // 移动端功能
191
+ { keywords: ['扫码', '定位', '地图', '推送', '通知', '分享', '支付', '下单', '预约', '签到', '打卡', '拍照', '扫码点餐'], platforms: [...backendPlatforms, ...mobilePlatforms] },
192
+ // 通用功能(所有端)
193
+ { keywords: ['登录', '认证', '注册', '鉴权', 'auth', 'login', 'logout', '用户', 'user', '个人中心', 'profile', '设置', 'setting'], platforms: allPlatforms },
194
+ // 前端展示功能
195
+ { keywords: ['页面', 'ui', '组件', '展示', '列表', '详情', '首页', '导航', '菜单', '主题', '样式', '布局'], platforms: [...adminPlatforms, ...mobilePlatforms] },
196
+ ];
197
+ // 匹配关键词
198
+ const matchedPlatforms = new Set();
199
+ for (const rule of keywordMap) {
200
+ if (rule.keywords.some(kw => name.includes(kw.toLowerCase()))) {
201
+ for (const p of rule.platforms)
202
+ matchedPlatforms.add(p);
203
+ }
204
+ }
205
+ // 如果没有匹配到任何关键词,回退到只包含后端端(最保守策略)
206
+ if (matchedPlatforms.size === 0) {
207
+ return backendPlatforms.length > 0 ? backendPlatforms : allPlatforms.slice(0, 1);
208
+ }
209
+ return Array.from(matchedPlatforms);
210
+ }
173
211
  /** 拆分约束常量(简化版:每个功能单元按涉及的端拆分,每端1个子任务) */
174
212
  const SPLIT_RULES = {
175
213
  maxTasksPerIteration: 20, // 单次迭代总任务数上限
@@ -309,6 +347,7 @@ async function iterationSplitCommand(options) {
309
347
  if (hasPipeline) {
310
348
  const { createSplitPipeline } = await Promise.resolve().then(() => __importStar(require('../../core/pipeline-engine')));
311
349
  const { engine } = await createSplitPipeline(iter);
350
+ // v8.3.160+: 默认步骤隔离模式
312
351
  const result = await engine.advance();
313
352
  if (result.isComplete) {
314
353
  logger_1.logger.success('🎉 Split Pipeline 完成!');
@@ -319,14 +358,47 @@ async function iterationSplitCommand(options) {
319
358
  logger_1.logger.info('');
320
359
  logger_1.logger.info(`🔄 Split Pipeline 推进到: ${result.nextStepName || result.nextStepId}`);
321
360
  logger_1.logger.info('');
361
+ // v8.3.160+: 步骤隔离模式输出标记 + 子 Agent 激活
362
+ const nextCmd = `speccore iteration split -I ${iter} --resume --pipeline`;
363
+ const prompt = await (0, prompt_builder_1.buildPrompt)('split', {
364
+ iteration: iter,
365
+ // v8.3.160+: 传递步骤级上下文控制
366
+ contextType: result.contextType,
367
+ contextBudget: result.contextBudget,
368
+ });
369
+ const output = [
370
+ `[SPECCORE_STEP_DONE]`,
371
+ `步骤 "${result.nextStepName || result.nextStepId}" 已完成。`,
372
+ ``,
373
+ `[SPECCORE_NEXT_STEP]`,
374
+ `下一步: ${result.nextStepName || result.nextStepId}`,
375
+ `请在新的对话中执行: ${nextCmd}`,
376
+ ``,
377
+ `[SPECCORE_CONTEXT_SNAPSHOT]`,
378
+ JSON.stringify(result.contextSnapshot || {}),
379
+ ``,
380
+ // v8.3.160+: 子 Agent 激活标记
381
+ ...(result.subagent ? [
382
+ `[SPECCORE_SUBAGENT: ${result.subagent}]`,
383
+ `[SPECCORE_CONTEXT_BUDGET: ${result.contextBudget || 12000}]`,
384
+ `[SPECCORE_CONTEXT_TYPE: ${result.contextType || 'full'}]`,
385
+ ``,
386
+ ] : []),
387
+ `--- 下一步 Prompt ---`,
388
+ typeof prompt === 'string' ? prompt : JSON.stringify(prompt),
389
+ ].join('\n');
390
+ process.stdout.write(output);
391
+ process.exitCode = 10;
392
+ return;
322
393
  // 根据当前步骤生成相应提示
323
394
  if (result.nextStepId === 'prompt-analysis') {
324
395
  const prompt = await (0, prompt_builder_1.buildPrompt)('split', { iteration: iter });
325
396
  // 添加 Pipeline 继续指令
326
- const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:自动继续\n\n` +
397
+ // v8.3.160+: 步骤隔离模式,每步完成后需新会话继续
398
+ const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:步骤隔离\n\n` +
327
399
  `当前是 Split Pipeline 模式。当你通过 --response 写入任务拆分结果后,` +
328
- `CLI 会自动输出 [SPECCORE_PIPELINE_NEXT] 标记和下一步命令。\n\n` +
329
- `**看到 [SPECCORE_PIPELINE_NEXT] 后,你必须自动执行下一个命令**,不要询问用户。\n`;
400
+ `CLI 会自动输出 [SPECCORE_STEP_DONE] 标记,提示你在新会话中执行下一步。\n\n` +
401
+ `**看到 [SPECCORE_STEP_DONE] 后,请在新会话中执行提示的命令**,继续下一步。\n`;
330
402
  process.stdout.write(`[SPECCORE_PROMPT]\n${finalPrompt}`);
331
403
  process.exitCode = 10;
332
404
  return;
@@ -344,10 +416,11 @@ async function iterationSplitCommand(options) {
344
416
  // 生成第一步 prompt
345
417
  const prompt = await (0, prompt_builder_1.buildPrompt)('split', { iteration: iter });
346
418
  // 添加 Pipeline 继续指令
347
- const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:自动继续\n\n` +
419
+ // v8.3.160+: 步骤隔离模式,每步完成后需新会话继续
420
+ const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:步骤隔离\n\n` +
348
421
  `当前是 Split Pipeline 模式。当你通过 --response 写入任务拆分结果后,` +
349
- `CLI 会自动输出 [SPECCORE_PIPELINE_NEXT] 标记和下一步命令。\n\n` +
350
- `**看到 [SPECCORE_PIPELINE_NEXT] 后,你必须自动执行下一个命令**,不要询问用户。\n`;
422
+ `CLI 会自动输出 [SPECCORE_STEP_DONE] 标记,提示你在新会话中执行下一步。\n\n` +
423
+ `**看到 [SPECCORE_STEP_DONE] 后,请在新会话中执行提示的命令**,继续下一步。\n`;
351
424
  process.stdout.write(`[SPECCORE_PROMPT]\n${finalPrompt}`);
352
425
  process.exitCode = 10;
353
426
  return;
@@ -372,7 +445,8 @@ async function iterationSplitCommand(options) {
372
445
  if (hasPipeline) {
373
446
  const { createSplitPipeline } = await Promise.resolve().then(() => __importStar(require('../../core/pipeline-engine')));
374
447
  const { engine } = await createSplitPipeline(iter);
375
- await engine.advance();
448
+ // v8.3.160+: 默认步骤隔离模式
449
+ const stepResult = await engine.advance();
376
450
  const state = await engine.getState();
377
451
  if (state?.currentStep === 'done') {
378
452
  logger_1.logger.success('🎉 Split Pipeline 完成!');
@@ -389,10 +463,38 @@ async function iterationSplitCommand(options) {
389
463
  nextPrompt = `任务拆分已完成,正在创建任务目录结构...\n\n请继续执行后续操作。`;
390
464
  }
391
465
  else {
392
- const promptResult = await (0, prompt_builder_1.buildPrompt)('split', { iteration: iter });
466
+ // v8.3.160+: 传递步骤级上下文控制,确保 Pipeline 配置的 budget/type 生效
467
+ const promptResult = await (0, prompt_builder_1.buildPrompt)('split', {
468
+ iteration: iter,
469
+ contextType: stepResult.contextType,
470
+ contextBudget: stepResult.contextBudget,
471
+ });
393
472
  nextPrompt = typeof promptResult === 'string' ? promptResult : JSON.stringify(promptResult);
394
473
  }
395
- process.stdout.write(`[SPECCORE_PIPELINE_NEXT]\n${nextPrompt}`);
474
+ // v8.3.160+: 步骤隔离模式输出标记 + 子 Agent 激活
475
+ const nextCmd = `speccore iteration split -I ${iter} --resume --pipeline`;
476
+ const output = [
477
+ `[SPECCORE_STEP_DONE]`,
478
+ `步骤 "${state.currentStep}" 已完成。`,
479
+ ``,
480
+ `[SPECCORE_NEXT_STEP]`,
481
+ `下一步: ${state.currentStep}`,
482
+ `请在新的对话中执行: ${nextCmd}`,
483
+ ``,
484
+ `[SPECCORE_CONTEXT_SNAPSHOT]`,
485
+ JSON.stringify(stepResult.contextSnapshot || {}),
486
+ ``,
487
+ // v8.3.160+: 子 Agent 激活标记
488
+ ...(stepResult.subagent ? [
489
+ `[SPECCORE_SUBAGENT: ${stepResult.subagent}]`,
490
+ `[SPECCORE_CONTEXT_BUDGET: ${stepResult.contextBudget || 12000}]`,
491
+ `[SPECCORE_CONTEXT_TYPE: ${stepResult.contextType || 'full'}]`,
492
+ ``,
493
+ ] : []),
494
+ `--- 下一步 Prompt ---`,
495
+ nextPrompt,
496
+ ].join('\n');
497
+ process.stdout.write(output);
396
498
  process.exitCode = 10;
397
499
  }
398
500
  return;
@@ -403,9 +505,27 @@ async function iterationSplitCommand(options) {
403
505
  const iterDir = (0, path_1.join)(iterDirFull, '030-tasks');
404
506
  await (0, fs_extra_1.ensureDir)(iterDir);
405
507
  const backups = [];
508
+ // v8.3.160+: 智能提取 JSON(支持 JSON 后附带 [PENDING] / [SPECCORE_EXEC] 标记)
509
+ function extractJsonArray(text) {
510
+ const result = { json: text };
511
+ // 提取 [PENDING: ...]
512
+ const pendingMatch = text.match(/\[PENDING:\s*([^\]]+)\]/);
513
+ if (pendingMatch)
514
+ result.pending = pendingMatch[1].trim();
515
+ // 提取 [SPECCORE_EXEC: ...]
516
+ const execMatch = text.match(/\[SPECCORE_EXEC:\s*([^\]]+)\]/);
517
+ if (execMatch)
518
+ result.exec = execMatch[1].trim();
519
+ // 提取 JSON 数组(从第一个 [ 到最后一个 ])
520
+ const jsonMatch = text.match(/(\[[\s\S]*\])/);
521
+ if (jsonMatch)
522
+ result.json = jsonMatch[1];
523
+ return result;
524
+ }
525
+ const extracted = extractJsonArray(options.response);
406
526
  // 尝试解析 AI 返回的 JSON Task 列表
407
527
  try {
408
- const tasks = JSON.parse(options.response);
528
+ const tasks = JSON.parse(extracted.json);
409
529
  if (Array.isArray(tasks)) {
410
530
  const allPlatforms = await detectPlatforms(iterDirFull);
411
531
  const sections = [];
@@ -674,6 +794,15 @@ async function iterationSplitCommand(options) {
674
794
  outputAnalyzeTaskHints(iter, createdSections);
675
795
  }
676
796
  logger_1.logger.success(`✅ 创建了 ${createdSections.length}/${sections.length} 个任务(${sections.length - createdSections.length} 个跳过)`);
797
+ // v8.3.160+: 检测是否有待拆分的功能模块
798
+ if (extracted.pending) {
799
+ logger_1.logger.info('');
800
+ logger_1.logger.info(`📦 还有未拆分的功能模块: ${extracted.pending}`);
801
+ logger_1.logger.info(` 继续拆分: speccore iteration split --prompt -I ${iter}`);
802
+ if (extracted.exec) {
803
+ process.stdout.write(`\n[SPECCORE_EXEC: ${extracted.exec}]\n`);
804
+ }
805
+ }
677
806
  }
678
807
  else {
679
808
  logger_1.logger.warn('AI 返回格式非数组,将作为 Markdown 写入 REQUIREMENT.md');
@@ -818,6 +947,23 @@ async function iterationSplitCommand(options) {
818
947
  if (await (0, fs_extra_1.pathExists)(reqPath2)) {
819
948
  reqContent2 = await (0, fs_extra_1.readFile)(reqPath2, 'utf-8');
820
949
  }
950
+ // v8.3.160+: 如果全局 REQUIREMENT.md 为空/骨架,从各功能模块 overview/REQUIREMENT.md 聚合
951
+ if (!reqContent2 || reqContent2.trim().length < 200 || reqContent2.includes('<!-- SPEC-SKELETON -->')) {
952
+ const featureReqParts = [];
953
+ const features = await (0, spec_paths_1.parseFeatureList)(iterationDir);
954
+ for (const feature of features) {
955
+ const featureReqPath = (0, path_1.join)(specDir2, feature, 'overview', 'REQUIREMENT.md');
956
+ if (await (0, fs_extra_1.pathExists)(featureReqPath)) {
957
+ const content = await (0, fs_extra_1.readFile)(featureReqPath, 'utf-8');
958
+ if (content.trim().length > 100 && !content.includes('<!-- SPEC-SKELETON -->')) {
959
+ featureReqParts.push(`# ${feature}\n\n${content.trim()}`);
960
+ }
961
+ }
962
+ }
963
+ if (featureReqParts.length > 0) {
964
+ reqContent2 = featureReqParts.join('\n\n---\n\n');
965
+ }
966
+ }
821
967
  const specContents = [];
822
968
  // v8.3.21+: 读取全局层文档(overview/ 下)
823
969
  const overviewDir = (0, path_1.join)(specDir2, spec_paths_1.GLOBAL_SPECS_DIR);
@@ -880,7 +1026,8 @@ async function iterationSplitCommand(options) {
880
1026
  // v6.76.0+: 传入过滤条件,限制拆分范围;传入已有 Task 结构,支持增量拆分
881
1027
  const modulesFilter = options.modules ? options.modules.split(',').map(m => m.trim()).filter(Boolean) : undefined;
882
1028
  const platformsFilter = options.platforms ? options.platforms.split(',').map(p => p.trim()).filter(Boolean) : undefined;
883
- let splitPrompt = await buildSplitPrompt(iteration, constitutionContent, reqContent2, specContents, allPlatforms, modulesFilter, platformsFilter, existingTaskStructure, iterationDir);
1029
+ // v8.3.160+: 普通模式下默认使用 task-decomposer,full 上下文类型
1030
+ let splitPrompt = await buildSplitPrompt(iteration, constitutionContent, reqContent2, specContents, allPlatforms, modulesFilter, platformsFilter, existingTaskStructure, iterationDir, 'task-decomposer', 'full');
884
1031
  // 注入全局上下文(INDEX + TOC 目录,AI 自主读取)
885
1032
  const { loadGlobalContext, formatGlobalContext } = await Promise.resolve().then(() => __importStar(require('../../core/prompt-builder')));
886
1033
  const globalCtx = await loadGlobalContext(process.cwd(), 'split');
@@ -1316,9 +1463,9 @@ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms
1316
1463
  taskPlatforms.push(platform);
1317
1464
  }
1318
1465
  }
1319
- // 如果都没检测到,回退到所有端
1466
+ // v8.3.160+ 修复:如果 Spec 匹配失败,根据功能单元关键词推断端,不再回退到所有端
1320
1467
  if (taskPlatforms.length === 0) {
1321
- taskPlatforms = allPlatforms;
1468
+ taskPlatforms = inferPlatformsByKeywords(sectionFeatureName, allPlatforms);
1322
1469
  }
1323
1470
  }
1324
1471
  const complexity = section._complexity || { estimatedHours: 8, priority: 'medium' };
@@ -1437,16 +1584,25 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
1437
1584
  // ── 4. 核心规格写入 00-specs/(REQ/TECH/SCHEMA/CHANGELOG) ──
1438
1585
  const acItems = generateAcceptanceCriteria(section);
1439
1586
  const aiReqContent = section._reqContent;
1440
- // v8.3.0+: REQ.md 三级回退 — AI split 生成 → analyze REQUIREMENT.md 提取 → section.content
1441
- const analyzeReqContent = extractRelevantSection(specContents['REQUIREMENT.md'] || '', section.name, '需求 功能 业务规则 异常场景 验收标准');
1442
- if (aiReqContent && aiReqContent.length > 50) {
1587
+ // v8.3.160+: 提升阈值,确保内容质量(200/300 字符起用)
1588
+ // v8.3.160+: 优先从功能模块级 overview/REQUIREMENT.md 提取,回退全局 REQUIREMENT.md
1589
+ const bestReqMatch = findBestSpecMatch(specContents, section.name, undefined, 'REQUIREMENT.md');
1590
+ const analyzeReqContent = bestReqMatch && bestReqMatch.score >= 20
1591
+ ? extractRelevantSection(specContents[bestReqMatch.key], section.name, '需求 功能 业务规则 异常场景 验收标准')
1592
+ : extractRelevantSection(specContents['REQUIREMENT.md'] || '', section.name, '需求 功能 业务规则 异常场景 验收标准');
1593
+ if (aiReqContent && aiReqContent.length > 200) {
1443
1594
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n${aiReqContent}\n\n## 验收标准\n\n${acItems}\n`);
1444
1595
  }
1445
- else if (analyzeReqContent && analyzeReqContent.length > 100) {
1596
+ else if (analyzeReqContent && analyzeReqContent.length > 300) {
1446
1597
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n> 来源: analyze → REQUIREMENT.md(自动提取)\n\n${analyzeReqContent}\n\n## 验收标准\n\n${acItems}\n`);
1447
1598
  }
1599
+ else if (section.content && section.content.trim().length > 200) {
1600
+ // v8.3.160+: 第三级回退 — section.content 有实质内容时直接使用
1601
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n> 来源: split 阶段 section 原始内容\n\n${section.content.trim()}\n\n## 验收标准\n\n${acItems}\n`);
1602
+ }
1448
1603
  else {
1449
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n## 需求描述\n\n${section.content}\n\n## 验收标准\n\n${acItems}\n`);
1604
+ // 最终回退:精简骨架 + section.content(即使很短也放入,方便 AI execute 阶段参考)
1605
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n## 需求描述\n\n${section.content || '(待补充,请根据功能单元描述填充)'}\n\n## 验收标准\n\n${acItems}\n`);
1450
1606
  }
1451
1607
  const apiLines = section.content.split(/\r?\n/).filter(l => l.includes('| GET') || l.includes('| POST') || l.includes('| PUT') || l.includes('| DELETE') || l.includes('| PATCH'));
1452
1608
  const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
@@ -1454,29 +1610,41 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
1454
1610
  // 从 analyze TECH.md 提取本任务相关内容
1455
1611
  const specTechContent = extractTaskTechContent(specContents, section);
1456
1612
  // v8.3.0+: 从 analyze ANALYSIS.md 补充功能分析内容
1457
- const analyzeAnalysisContent = extractRelevantSection(specContents['ANALYSIS.md'] || '', section.name, '功能分析 业务流程 数据流 决策逻辑 业务规则');
1458
- // TECH.md: 优先 AI 生成 → 回退 analyze 提取 → 回退模板
1459
- if (aiTechContent && aiTechContent.length > 50) {
1613
+ // v8.3.160+: 优先从功能模块级 overview/ANALYSIS.md 提取,回退全局 ANALYSIS.md
1614
+ const bestAnalysisMatch = findBestSpecMatch(specContents, section.name, undefined, 'ANALYSIS.md');
1615
+ const analyzeAnalysisContent = bestAnalysisMatch && bestAnalysisMatch.score >= 20
1616
+ ? extractRelevantSection(specContents[bestAnalysisMatch.key], section.name, '功能分析 业务流程 数据流 决策逻辑 业务规则')
1617
+ : extractRelevantSection(specContents['ANALYSIS.md'] || '', section.name, '功能分析 业务流程 数据流 决策逻辑 业务规则');
1618
+ // v8.3.160+: 提升阈值,确保内容质量(200/150 字符起用)
1619
+ // TECH.md: 优先 AI 生成 → 回退 analyze 提取 → 回退 section 内容提取 → 回退模板
1620
+ if (aiTechContent && aiTechContent.length > 200) {
1460
1621
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
1461
1622
  }
1462
- else if (specTechContent && specTechContent.length > 30) {
1623
+ else if (specTechContent && specTechContent.length > 150) {
1463
1624
  let techBody = `> 来源: analyze → TECH.md(自动提取)\n\n${specTechContent}`;
1464
1625
  // 追加 ANALYSIS.md 功能分析(如有)
1465
- if (analyzeAnalysisContent && analyzeAnalysisContent.length > 50) {
1626
+ if (analyzeAnalysisContent && analyzeAnalysisContent.length > 100) {
1466
1627
  techBody += `\n\n---\n\n## 功能分析补充\n\n> 来源: analyze → ANALYSIS.md(自动提取)\n\n${analyzeAnalysisContent}`;
1467
1628
  }
1468
1629
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n${techBody}\n`);
1469
1630
  }
1470
1631
  else {
1471
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案
1632
+ // v8.3.160+: 第三级回退 — 从 section.content 提取技术相关内容
1633
+ const sectionTechExtract = extractRelevantSection(section.content, section.name, '接口 数据 模型 字段 表结构 技术方案 架构 流程');
1634
+ if (sectionTechExtract && sectionTechExtract.length > 100) {
1635
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n> 来源: split 阶段 section 原始内容(技术相关段落自动提取)\n\n${sectionTechExtract}\n\n## 接口设计\n${apiDesc}\n`);
1636
+ }
1637
+ else {
1638
+ // 最终回退:精简骨架模板(保留接口信息,减少 AI-FILL 占位符数量)
1639
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案
1472
1640
 
1473
- > ⚠️ 本文档由 split 自动生成框架,AI 执行时会自动填充。
1641
+ > ⚠️ 本文档由 split 自动生成框架,内容待 AI execute 阶段填充。
1642
+ > 参考:_shared/CONTEXT.md、00-specs/REQ.md、020-specs/overview/TECH.md
1474
1643
 
1475
1644
  ## 1. 方案概述
1476
1645
  <!-- AI-FILL: 简述本任务的业务背景和技术目标 -->
1477
1646
 
1478
1647
  ## 2. 接口设计
1479
- <!-- AI-FILL: 根据以下接口列表设计 Controller / Service 分层 -->
1480
1648
  ${apiDesc}
1481
1649
 
1482
1650
  ### 统一响应格式
@@ -1494,33 +1662,39 @@ ${apiDesc}
1494
1662
  - 单元测试覆盖核心 Service 逻辑
1495
1663
  - 接口测试覆盖正常/异常/边界
1496
1664
  - 自动化测试通过后方可提 PR
1497
-
1498
- ## 6. 前端 UI 设计
1499
- <!-- AI-FILL: 页面结构、组件清单、状态管理、路由设计 -->
1500
- ### 6.1 页面/路由
1501
- <!-- AI-FILL: 路由表 -->
1502
-
1503
- ### 6.2 组件清单
1504
- <!-- AI-FILL: 组件列表及职责 -->
1505
-
1506
- ### 6.3 状态管理
1507
- <!-- AI-FILL: 状态字段及枚举值,需与后端保持一致 -->
1508
1665
  `);
1666
+ }
1509
1667
  }
1510
1668
  // v7.4.0+: DEV_GUIDE.md 写入
1511
1669
  // v8.3.0+: 三级回退 — AI split 生成 → analyze DEV_GUIDE.md 提取 → 结构化模板
1512
1670
  const aiDevGuideContent = section._devGuideContent;
1513
1671
  const analyzeDevGuideContent = extractTaskDevGuideContent(specContents, section, taskPlatforms);
1514
- if (aiDevGuideContent && aiDevGuideContent.length > 50) {
1672
+ // v8.3.160+: 提升阈值,确保内容质量(200/300 字符起用)
1673
+ if (aiDevGuideContent && aiDevGuideContent.length > 200) {
1515
1674
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'DEV_GUIDE.md'), `# ${section.name} - 开发者实现指南\n\n${aiDevGuideContent}\n`);
1516
1675
  }
1517
- else if (analyzeDevGuideContent && analyzeDevGuideContent.length > 100) {
1676
+ else if (analyzeDevGuideContent && analyzeDevGuideContent.length > 300) {
1518
1677
  // v8.3.0+: 从 analyze DEV_GUIDE.md 提取本任务相关内容
1519
1678
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'DEV_GUIDE.md'), `# ${section.name} - 开发者实现指南\n\n> 任务: ${taskId} | ${section.name}\n> 本文档面向开发者,提供可执行的实现指导。\n> 来源: analyze → DEV_GUIDE.md(自动提取)\n\n${analyzeDevGuideContent}\n\n## 全局开发指南引用\n- 环境/规范/联调 → ../../../overview/DEV_GUIDE.md\n`);
1520
1679
  }
1521
1680
  else {
1522
- // 回退:结构化骨架模板(AI execute 阶段填充)
1523
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'DEV_GUIDE.md'), `# ${section.name} - 开发者实现指南\n\n> 任务: ${taskId} | ${section.name}\n> 本文档面向开发者,提供可执行的实现指导。\n\n## 1. 本任务改造范围\n\n<!-- AI-FILL: execute 阶段根据 REQ.md 和 TECH.md 填充 -->\n| 类型 | 文件/目录 | 说明 |\n| :--- | :--- | :--- |\n| 新增 | | |\n| 修改 | | |\n| 删除 | | |\n\n## 2. 实施步骤(按依赖排序)\n\n<!-- AI-FILL: Step-by-step 开发步骤,具体到文件/函数级 -->\n- **Step 1**: ...(为什么先做,依赖什么)\n- **Step 2**: ...(依赖 Step 1 的什么产出)\n- ...\n\n## 3. 关键代码指引\n\n<!-- AI-FILL: 核心逻辑的伪代码或改造前后对比 -->\n\n### 3.1 核心改造点 A\n- **文件**: \`xxx.ts\`\n- **改造**: ...\n- **代码示例**:\n \`\`\`typescript\n // 改造后\n \`\`\`\n\n## 4. 接口契约\n\n<!-- AI-FILL: 本任务涉及的前后接口对照 -->\n| 接口 | 路径 | 后端实现 | 前端调用 | 状态 |\n| :--- | :--- | :--- | :--- | :--- |\n\n## 5. 每步验证\n\n<!-- AI-FILL: 每步改完怎么验证 -->\n| 步骤 | 验证命令/操作 | 通过标准 |\n| :--- | :--- | :--- |\n\n## 6. 回滚方案\n\n<!-- AI-FILL: 改坏了怎么回退 -->\n- 数据库: ...\n- 代码: ...\n\n## 7. 已知坑点\n\n<!-- AI-FILL: 常见坑点及解决方式 -->\n- ⚠️ **坑 1**: ...(解决方式)\n- ⚠️ **坑 2**: ...(解决方式)\n\n## 全局开发指南引用\n- 环境/规范/联调 → ../../../overview/DEV_GUIDE.md\n`);
1681
+ // v8.3.160+: 第三级回退 — 从 section.content 提取开发相关内容
1682
+ const sectionDevExtract = extractRelevantSection(section.content, section.name, '步骤 实现 改造 文件 函数 类 接口 验证 测试');
1683
+ if (sectionDevExtract && sectionDevExtract.length > 100) {
1684
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'DEV_GUIDE.md'), `# ${section.name} - 开发者实现指南\n\n> 任务: ${taskId} | ${section.name}
1685
+ > 本文档面向开发者,提供可执行的实现指导。
1686
+ > 来源: split 阶段 section 原始内容(开发相关段落自动提取)
1687
+
1688
+ ${sectionDevExtract}
1689
+
1690
+ ## 全局开发指南引用
1691
+ - 环境/规范/联调 → ../../../overview/DEV_GUIDE.md
1692
+ `);
1693
+ }
1694
+ else {
1695
+ // 最终回退:结构化骨架模板(AI execute 阶段填充)
1696
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'DEV_GUIDE.md'), `# ${section.name} - 开发者实现指南\n\n> 任务: ${taskId} | ${section.name}\n> 本文档面向开发者,提供可执行的实现指导。\n\n## 1. 本任务改造范围\n\n<!-- AI-FILL: execute 阶段根据 REQ.md 和 TECH.md 填充 -->\n| 类型 | 文件/目录 | 说明 |\n| :--- | :--- | :--- |\n| 新增 | | |\n| 修改 | | |\n| 删除 | | |\n\n## 2. 实施步骤(按依赖排序)\n\n<!-- AI-FILL: Step-by-step 开发步骤,具体到文件/函数级 -->\n- **Step 1**: ...(为什么先做,依赖什么)\n- **Step 2**: ...(依赖 Step 1 的什么产出)\n- ...\n\n## 3. 关键代码指引\n\n<!-- AI-FILL: 核心逻辑的伪代码或改造前后对比 -->\n\n### 3.1 核心改造点 A\n- **文件**: \`xxx.ts\`\n- **改造**: ...\n- **代码示例**:\n \`\`\`typescript\n // 改造后\n \`\`\`\n\n## 4. 接口契约\n\n<!-- AI-FILL: 本任务涉及的前后接口对照 -->\n| 接口 | 路径 | 后端实现 | 前端调用 | 状态 |\n| :--- | :--- | :--- | :--- | :--- |\n\n## 5. 每步验证\n\n<!-- AI-FILL: 每步改完怎么验证 -->\n| 步骤 | 验证命令/操作 | 通过标准 |\n| :--- | :--- | :--- |\n\n## 6. 回滚方案\n\n<!-- AI-FILL: 改坏了怎么回退 -->\n- 数据库: ...\n- 代码: ...\n\n## 7. 已知坑点\n\n<!-- AI-FILL: 常见坑点及解决方式 -->\n- ⚠️ **坑 1**: ...(解决方式)\n- ⚠️ **坑 2**: ...(解决方式)\n\n## 全局开发指南引用\n- 环境/规范/联调 → ../../../overview/DEV_GUIDE.md\n`);
1697
+ }
1524
1698
  }
1525
1699
  if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
1526
1700
  const schemaMaterial = extractRelevantSection(specContents['TECH.md'] || '', section.name, 'DDL 建表 表结构 索引 CREATE TABLE');
@@ -2865,9 +3039,22 @@ ${isH5 ? '移动端优先,适配 375/414/768' :
2865
3039
  /**
2866
3040
  * 构建完整的 AI 智能拆分 Prompt(含 SpecCore 理念 + 粒度规则 + 完整上下文)
2867
3041
  */
2868
- async function buildSplitPrompt(iteration, constitutionContent, reqContent, specContents, standardPlatforms, modulesFilter, platformsFilter, existingTasks, iterationDir) {
3042
+ async function buildSplitPrompt(iteration, constitutionContent, reqContent, specContents, standardPlatforms, modulesFilter, platformsFilter, existingTasks, iterationDir,
3043
+ // v8.3.160+: 子 Agent 角色和上下文类型,用于动态调整 Prompt
3044
+ subagent, contextType) {
3045
+ // v8.3.160+: 接入 agent-adapter,注入子 Agent 角色定义和上下文裁剪策略
3046
+ const agentCtx = {
3047
+ subagent: subagent || 'task-decomposer',
3048
+ iteration,
3049
+ contextBudget: contextType === 'incremental' ? 5000 : 8000,
3050
+ contextType: contextType || 'full',
3051
+ cwd: iterationDir || process.cwd(),
3052
+ };
3053
+ const agentContextText = await agent_adapter_1.defaultAdapter.prepareContext(agentCtx);
2869
3054
  let p = `# SpecCore AI 智能拆分\n\n`;
2870
- p += `> 迭代: ${iteration} | 生成: ${new Date().toISOString().split('T')[0]}\n\n`;
3055
+ p += `> 迭代: ${iteration} | 子 Agent: ${agentCtx.subagent} | 生成: ${new Date().toISOString().split('T')[0]}\n\n`;
3056
+ // 注入子 Agent 上下文(角色定义 + 上下文裁剪策略)
3057
+ p += agentContextText + '\n\n';
2871
3058
  // v6.76.0+: 拆分范围限制
2872
3059
  if (modulesFilter?.length || platformsFilter?.length) {
2873
3060
  p += `## 🎯 拆分范围限制(用户指定)\n\n`;
@@ -2897,16 +3084,37 @@ async function buildSplitPrompt(iteration, constitutionContent, reqContent, spec
2897
3084
  p += `- 已有 Task 的 _shared/ 目录保持不变,新端目录平铺追加到任务根目录下(如 \`{端名}/{taskId}-{端名}/\`)\n`;
2898
3085
  p += `- API 契约(_shared/API_CONTRACT.yaml)需要补充新端涉及的接口\n\n`;
2899
3086
  }
2900
- // 技术宪法
2901
- if (constitutionContent) {
2902
- p += `## 📜 技术宪法 (CONSTITUTION.md)\n\n${constitutionContent.slice(0, 3000)}\n\n---\n\n`;
2903
- }
2904
- // 需求原文
2905
- p += `## 📋 需求原文 (REQUIREMENT.md)\n\n${reqContent.slice(0, 5000) || '_未找到_'}\n\n---\n\n`;
2906
- // 全部 Spec 文档
3087
+ // v8.3.160+: 路径引用模式 —— 不再把文档全文塞进 Prompt,让 AI 通过 Read 工具按需读取
3088
+ p += `## 📂 文档清单(请按需 Read)\n\n`;
3089
+ p += `> 💡 **路径引用模式**:以下文档已就绪,请根据当前任务需要选择性 Read,不要一次性加载所有文件。\n\n`;
3090
+ p += `### 必读文档\n\n`;
3091
+ p += `1. \`CONSTITUTION.md\` — 技术宪法(端列表、命名规范、技术栈)\n`;
3092
+ p += `2. \`010-requirements/INDEX.md\` — 需求全貌索引\n`;
3093
+ p += `3. \`010-requirements/converted/*.md\` — 转换后的需求文档\n`;
3094
+ p += `4. \`020-specs/overview/REQUIREMENT.md\` — 黄金需求文档\n`;
3095
+ p += `5. \`020-specs/overview/TECH.md\` — 全局技术方案\n`;
3096
+ p += `6. \`020-specs/overview/ANALYSIS.md\` — 功能分析\n`;
3097
+ p += `7. \`020-specs/overview/FUNCTION_MAP.md\` — 功能单元与端的映射(拆分核心依据)\n`;
3098
+ if (await (0, fs_extra_1.pathExists)((0, path_1.join)(iterationDir || '', '020-specs', 'overview', 'INTERACTION_MAP.md'))) {
3099
+ p += `8. \`020-specs/overview/INTERACTION_MAP.md\` — 跨端交互时序\n`;
3100
+ }
3101
+ if (await (0, fs_extra_1.pathExists)((0, path_1.join)(iterationDir || '', '020-specs', 'overview', 'DEV_GUIDE.md'))) {
3102
+ p += `9. \`020-specs/overview/DEV_GUIDE.md\` — 开发指南\n`;
3103
+ }
3104
+ p += `\n`;
3105
+ p += `### 按功能模块读取(020-specs/{功能模块}/)\n\n`;
3106
+ p += `对于每个功能模块,按需 Read:\n`;
2907
3107
  for (const spec of specContents) {
2908
- p += `## 📜 ${spec.name}\n\n${spec.content.slice(0, 3000)}\n\n---\n\n`;
2909
- }
3108
+ p += `- \`020-specs/${spec.name}\`\n`;
3109
+ }
3110
+ p += `\n`;
3111
+ p += `> 📖 **读取策略**:\n`;
3112
+ p += `> - 先 Read FUNCTION_MAP.md 了解功能单元划分和涉及端\n`;
3113
+ p += `> - 按功能单元逐个处理,每次只 Read 当前单元相关的文档\n`;
3114
+ p += `> - reqContent 从 REQUIREMENT.md 和对应功能模块的需求文档提取\n`;
3115
+ p += `> - techContent 从 TECH.md 和对应功能模块的技术规格提取\n`;
3116
+ p += `> - devGuideContent 从 DEV_GUIDE.md 提取\n\n`;
3117
+ p += `---\n\n`;
2910
3118
  // v6.69.3+: 注入标准端名列表
2911
3119
  if (standardPlatforms.length > 0) {
2912
3120
  p += `## 🖥️ 项目端列表(标准端名 — 权威来源)\n\n`;
@@ -3036,6 +3244,45 @@ async function buildSplitPrompt(iteration, constitutionContent, reqContent, spec
3036
3244
  p += `- 每个任务必须有明确的 owner(对应 STAFFING 中的成员)\n`;
3037
3245
  p += `- 高优先级任务排在前面\n`;
3038
3246
  p += `- 没有实质性功能内容的章节(如背景、概述、架构、术语等)不能作为拆分依据\n\n`;
3247
+ // v8.3.160+: 根据 subagent 动态调整本步骤任务指令
3248
+ if (subagent === 'dependency-analyst') {
3249
+ p += `## 🔗 本步骤任务:依赖关系分析\n\n`;
3250
+ p += `你是 **依赖关系分析师**。你的职责是分析任务间的依赖关系,不需要做拆分或工时估算。\n\n`;
3251
+ p += `**输入**:上一步生成的任务列表(含 id、name、functionalUnit、scope)\n`;
3252
+ p += `**输出**:为每个任务补充 \`dependencies\` 字段,标注该任务依赖哪些其他任务(填写 task id)\n\n`;
3253
+ p += `### 依赖判定规则\n\n`;
3254
+ p += `- **数据依赖**:Task-B 需要 Task-A 创建的数据表/实体 → B 依赖 A\n`;
3255
+ p += `- **API 依赖**:Task-B 调用 Task-A 提供的接口 → B 依赖 A\n`;
3256
+ p += `- **状态依赖**:Task-B 需要 Task-A 完成后的某个业务状态 → B 依赖 A\n`;
3257
+ p += `- **配置依赖**:Task-B 需要 Task-A 完成后的配置/环境 → B 依赖 A\n`;
3258
+ p += `- 依赖链深度 ≤ 3,发现循环依赖必须标注并建议解耦方案\n`;
3259
+ p += `- 同层级任务之间尽量无依赖,可并行执行\n\n`;
3260
+ }
3261
+ else if (subagent === 'effort-estimator') {
3262
+ p += `## ⏱️ 本步骤任务:工时估算与排期\n\n`;
3263
+ p += `你是 **工时估算专家**。你的职责是为每个任务估算工时并确定优先级,不需要重新拆分。\n\n`;
3264
+ p += `**输入**:带依赖关系的任务列表(含 id、name、scope、dependencies)\n`;
3265
+ p += `**输出**:为每个任务补充 \`estimatedHours\`、\`priority\`、\`owner\` 字段\n\n`;
3266
+ p += `### 估算规则\n\n`;
3267
+ p += `- 单个任务工时控制在 **2h-8h** 之间\n`;
3268
+ p += `- 超过 8h 必须拆分为多个任务(返回修改建议,不要硬估)\n`;
3269
+ p += `- 低于 2h 建议合并到关联任务\n`;
3270
+ p += `- **高优先级**:基础模块(认证/数据库/配置)、被多个任务依赖的任务\n`;
3271
+ p += `- **中优先级**:核心业务功能\n`;
3272
+ p += `- **低优先级**:独立第三方集成、纯 UI 调整\n`;
3273
+ p += `- 考虑技术风险和不确定性,预留 20% 缓冲\n\n`;
3274
+ }
3275
+ else {
3276
+ // task-decomposer 默认
3277
+ p += `## 📋 本步骤任务:任务拆分与内容生成\n\n`;
3278
+ p += `你是 **任务拆分专家**。你的职责是将功能模块拆分为原子级开发任务,并为每个任务生成完整的内容。\n\n`;
3279
+ p += `**核心要求**:\n`;
3280
+ p += `- 按 FUNCTION_MAP.md 中的功能单元拆分\n`;
3281
+ p += `- 每个功能单元涉及的端,各生成一个子任务(scope 只包含该端)\n`;
3282
+ p += `- 为每个任务生成完整的 reqContent、techContent、devGuideContent\n`;
3283
+ p += `- **内容必须从 020-specs/ 对应文档中读取并提取**,禁止输出模板化占位符\n`;
3284
+ p += `- 如果某个文档内容不足,标注「基于现有信息推断」而非留空\n\n`;
3285
+ }
3039
3286
  // 输出格式
3040
3287
  p += `## 📤 输出格式\n\n`;
3041
3288
  p += `请输出 JSON 数组,每个 Task 包含:\n`;