speccore 8.3.160 → 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.
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +16 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +484 -292
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/change.d.ts.map +1 -1
- package/dist/commands/change.js +14 -14
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/execute.d.ts +1 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +25 -4
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +226 -43
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +45 -36
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +60 -0
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/prompt-builder.d.ts +8 -3
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +155 -5
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/requirement-clarifier.d.ts.map +1 -1
- package/dist/core/requirement-clarifier.js +36 -3
- package/dist/core/requirement-clarifier.js.map +1 -1
- package/dist/core/schemas/task.schema.d.ts +2 -2
- package/dist/core/structured-extractor.d.ts +2 -0
- package/dist/core/structured-extractor.d.ts.map +1 -1
- package/dist/core/structured-extractor.js +61 -0
- package/dist/core/structured-extractor.js.map +1 -1
- package/dist/core/unified-retrieval.d.ts +2 -1
- package/dist/core/unified-retrieval.d.ts.map +1 -1
- package/dist/core/unified-retrieval.js +34 -15
- package/dist/core/unified-retrieval.js.map +1 -1
- 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, // 单次迭代总任务数上限
|
|
@@ -467,9 +505,27 @@ async function iterationSplitCommand(options) {
|
|
|
467
505
|
const iterDir = (0, path_1.join)(iterDirFull, '030-tasks');
|
|
468
506
|
await (0, fs_extra_1.ensureDir)(iterDir);
|
|
469
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);
|
|
470
526
|
// 尝试解析 AI 返回的 JSON Task 列表
|
|
471
527
|
try {
|
|
472
|
-
const tasks = JSON.parse(
|
|
528
|
+
const tasks = JSON.parse(extracted.json);
|
|
473
529
|
if (Array.isArray(tasks)) {
|
|
474
530
|
const allPlatforms = await detectPlatforms(iterDirFull);
|
|
475
531
|
const sections = [];
|
|
@@ -738,6 +794,15 @@ async function iterationSplitCommand(options) {
|
|
|
738
794
|
outputAnalyzeTaskHints(iter, createdSections);
|
|
739
795
|
}
|
|
740
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
|
+
}
|
|
741
806
|
}
|
|
742
807
|
else {
|
|
743
808
|
logger_1.logger.warn('AI 返回格式非数组,将作为 Markdown 写入 REQUIREMENT.md');
|
|
@@ -882,6 +947,23 @@ async function iterationSplitCommand(options) {
|
|
|
882
947
|
if (await (0, fs_extra_1.pathExists)(reqPath2)) {
|
|
883
948
|
reqContent2 = await (0, fs_extra_1.readFile)(reqPath2, 'utf-8');
|
|
884
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
|
+
}
|
|
885
967
|
const specContents = [];
|
|
886
968
|
// v8.3.21+: 读取全局层文档(overview/ 下)
|
|
887
969
|
const overviewDir = (0, path_1.join)(specDir2, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
@@ -944,7 +1026,8 @@ async function iterationSplitCommand(options) {
|
|
|
944
1026
|
// v6.76.0+: 传入过滤条件,限制拆分范围;传入已有 Task 结构,支持增量拆分
|
|
945
1027
|
const modulesFilter = options.modules ? options.modules.split(',').map(m => m.trim()).filter(Boolean) : undefined;
|
|
946
1028
|
const platformsFilter = options.platforms ? options.platforms.split(',').map(p => p.trim()).filter(Boolean) : undefined;
|
|
947
|
-
|
|
1029
|
+
// v8.3.160+: 普通模式下默认使用 task-decomposer,full 上下文类型
|
|
1030
|
+
let splitPrompt = await buildSplitPrompt(iteration, constitutionContent, reqContent2, specContents, allPlatforms, modulesFilter, platformsFilter, existingTaskStructure, iterationDir, 'task-decomposer', 'full');
|
|
948
1031
|
// 注入全局上下文(INDEX + TOC 目录,AI 自主读取)
|
|
949
1032
|
const { loadGlobalContext, formatGlobalContext } = await Promise.resolve().then(() => __importStar(require('../../core/prompt-builder')));
|
|
950
1033
|
const globalCtx = await loadGlobalContext(process.cwd(), 'split');
|
|
@@ -1380,9 +1463,9 @@ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms
|
|
|
1380
1463
|
taskPlatforms.push(platform);
|
|
1381
1464
|
}
|
|
1382
1465
|
}
|
|
1383
|
-
//
|
|
1466
|
+
// v8.3.160+ 修复:如果 Spec 匹配失败,根据功能单元关键词推断端,不再回退到所有端
|
|
1384
1467
|
if (taskPlatforms.length === 0) {
|
|
1385
|
-
taskPlatforms = allPlatforms;
|
|
1468
|
+
taskPlatforms = inferPlatformsByKeywords(sectionFeatureName, allPlatforms);
|
|
1386
1469
|
}
|
|
1387
1470
|
}
|
|
1388
1471
|
const complexity = section._complexity || { estimatedHours: 8, priority: 'medium' };
|
|
@@ -1501,16 +1584,25 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
|
|
|
1501
1584
|
// ── 4. 核心规格写入 00-specs/(REQ/TECH/SCHEMA/CHANGELOG) ──
|
|
1502
1585
|
const acItems = generateAcceptanceCriteria(section);
|
|
1503
1586
|
const aiReqContent = section._reqContent;
|
|
1504
|
-
// v8.3.
|
|
1505
|
-
|
|
1506
|
-
|
|
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) {
|
|
1507
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`);
|
|
1508
1595
|
}
|
|
1509
|
-
else if (analyzeReqContent && analyzeReqContent.length >
|
|
1596
|
+
else if (analyzeReqContent && analyzeReqContent.length > 300) {
|
|
1510
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`);
|
|
1511
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
|
+
}
|
|
1512
1603
|
else {
|
|
1513
|
-
|
|
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`);
|
|
1514
1606
|
}
|
|
1515
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'));
|
|
1516
1608
|
const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
|
|
@@ -1518,29 +1610,41 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
|
|
|
1518
1610
|
// 从 analyze TECH.md 提取本任务相关内容
|
|
1519
1611
|
const specTechContent = extractTaskTechContent(specContents, section);
|
|
1520
1612
|
// v8.3.0+: 从 analyze ANALYSIS.md 补充功能分析内容
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
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) {
|
|
1524
1621
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
|
|
1525
1622
|
}
|
|
1526
|
-
else if (specTechContent && specTechContent.length >
|
|
1623
|
+
else if (specTechContent && specTechContent.length > 150) {
|
|
1527
1624
|
let techBody = `> 来源: analyze → TECH.md(自动提取)\n\n${specTechContent}`;
|
|
1528
1625
|
// 追加 ANALYSIS.md 功能分析(如有)
|
|
1529
|
-
if (analyzeAnalysisContent && analyzeAnalysisContent.length >
|
|
1626
|
+
if (analyzeAnalysisContent && analyzeAnalysisContent.length > 100) {
|
|
1530
1627
|
techBody += `\n\n---\n\n## 功能分析补充\n\n> 来源: analyze → ANALYSIS.md(自动提取)\n\n${analyzeAnalysisContent}`;
|
|
1531
1628
|
}
|
|
1532
1629
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n${techBody}\n`);
|
|
1533
1630
|
}
|
|
1534
1631
|
else {
|
|
1535
|
-
|
|
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} - 技术方案
|
|
1536
1640
|
|
|
1537
|
-
> ⚠️ 本文档由 split
|
|
1641
|
+
> ⚠️ 本文档由 split 自动生成框架,内容待 AI execute 阶段填充。
|
|
1642
|
+
> 参考:_shared/CONTEXT.md、00-specs/REQ.md、020-specs/overview/TECH.md
|
|
1538
1643
|
|
|
1539
1644
|
## 1. 方案概述
|
|
1540
1645
|
<!-- AI-FILL: 简述本任务的业务背景和技术目标 -->
|
|
1541
1646
|
|
|
1542
1647
|
## 2. 接口设计
|
|
1543
|
-
<!-- AI-FILL: 根据以下接口列表设计 Controller / Service 分层 -->
|
|
1544
1648
|
${apiDesc}
|
|
1545
1649
|
|
|
1546
1650
|
### 统一响应格式
|
|
@@ -1558,33 +1662,39 @@ ${apiDesc}
|
|
|
1558
1662
|
- 单元测试覆盖核心 Service 逻辑
|
|
1559
1663
|
- 接口测试覆盖正常/异常/边界
|
|
1560
1664
|
- 自动化测试通过后方可提 PR
|
|
1561
|
-
|
|
1562
|
-
## 6. 前端 UI 设计
|
|
1563
|
-
<!-- AI-FILL: 页面结构、组件清单、状态管理、路由设计 -->
|
|
1564
|
-
### 6.1 页面/路由
|
|
1565
|
-
<!-- AI-FILL: 路由表 -->
|
|
1566
|
-
|
|
1567
|
-
### 6.2 组件清单
|
|
1568
|
-
<!-- AI-FILL: 组件列表及职责 -->
|
|
1569
|
-
|
|
1570
|
-
### 6.3 状态管理
|
|
1571
|
-
<!-- AI-FILL: 状态字段及枚举值,需与后端保持一致 -->
|
|
1572
1665
|
`);
|
|
1666
|
+
}
|
|
1573
1667
|
}
|
|
1574
1668
|
// v7.4.0+: DEV_GUIDE.md 写入
|
|
1575
1669
|
// v8.3.0+: 三级回退 — AI split 生成 → analyze DEV_GUIDE.md 提取 → 结构化模板
|
|
1576
1670
|
const aiDevGuideContent = section._devGuideContent;
|
|
1577
1671
|
const analyzeDevGuideContent = extractTaskDevGuideContent(specContents, section, taskPlatforms);
|
|
1578
|
-
|
|
1672
|
+
// v8.3.160+: 提升阈值,确保内容质量(200/300 字符起用)
|
|
1673
|
+
if (aiDevGuideContent && aiDevGuideContent.length > 200) {
|
|
1579
1674
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'DEV_GUIDE.md'), `# ${section.name} - 开发者实现指南\n\n${aiDevGuideContent}\n`);
|
|
1580
1675
|
}
|
|
1581
|
-
else if (analyzeDevGuideContent && analyzeDevGuideContent.length >
|
|
1676
|
+
else if (analyzeDevGuideContent && analyzeDevGuideContent.length > 300) {
|
|
1582
1677
|
// v8.3.0+: 从 analyze DEV_GUIDE.md 提取本任务相关内容
|
|
1583
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`);
|
|
1584
1679
|
}
|
|
1585
1680
|
else {
|
|
1586
|
-
//
|
|
1587
|
-
|
|
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
|
+
}
|
|
1588
1698
|
}
|
|
1589
1699
|
if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
|
|
1590
1700
|
const schemaMaterial = extractRelevantSection(specContents['TECH.md'] || '', section.name, 'DDL 建表 表结构 索引 CREATE TABLE');
|
|
@@ -2929,9 +3039,22 @@ ${isH5 ? '移动端优先,适配 375/414/768' :
|
|
|
2929
3039
|
/**
|
|
2930
3040
|
* 构建完整的 AI 智能拆分 Prompt(含 SpecCore 理念 + 粒度规则 + 完整上下文)
|
|
2931
3041
|
*/
|
|
2932
|
-
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);
|
|
2933
3054
|
let p = `# SpecCore AI 智能拆分\n\n`;
|
|
2934
|
-
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';
|
|
2935
3058
|
// v6.76.0+: 拆分范围限制
|
|
2936
3059
|
if (modulesFilter?.length || platformsFilter?.length) {
|
|
2937
3060
|
p += `## 🎯 拆分范围限制(用户指定)\n\n`;
|
|
@@ -2961,16 +3084,37 @@ async function buildSplitPrompt(iteration, constitutionContent, reqContent, spec
|
|
|
2961
3084
|
p += `- 已有 Task 的 _shared/ 目录保持不变,新端目录平铺追加到任务根目录下(如 \`{端名}/{taskId}-{端名}/\`)\n`;
|
|
2962
3085
|
p += `- API 契约(_shared/API_CONTRACT.yaml)需要补充新端涉及的接口\n\n`;
|
|
2963
3086
|
}
|
|
2964
|
-
//
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
p +=
|
|
2970
|
-
|
|
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`;
|
|
2971
3107
|
for (const spec of specContents) {
|
|
2972
|
-
p +=
|
|
2973
|
-
}
|
|
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`;
|
|
2974
3118
|
// v6.69.3+: 注入标准端名列表
|
|
2975
3119
|
if (standardPlatforms.length > 0) {
|
|
2976
3120
|
p += `## 🖥️ 项目端列表(标准端名 — 权威来源)\n\n`;
|
|
@@ -3100,6 +3244,45 @@ async function buildSplitPrompt(iteration, constitutionContent, reqContent, spec
|
|
|
3100
3244
|
p += `- 每个任务必须有明确的 owner(对应 STAFFING 中的成员)\n`;
|
|
3101
3245
|
p += `- 高优先级任务排在前面\n`;
|
|
3102
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
|
+
}
|
|
3103
3286
|
// 输出格式
|
|
3104
3287
|
p += `## 📤 输出格式\n\n`;
|
|
3105
3288
|
p += `请输出 JSON 数组,每个 Task 包含:\n`;
|