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.
- 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 +538 -316
- 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 +92 -10
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +6 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +299 -52
- 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/agent-adapter.d.ts +81 -0
- package/dist/core/agent-adapter.d.ts.map +1 -0
- package/dist/core/agent-adapter.js +425 -0
- package/dist/core/agent-adapter.js.map +1 -0
- 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/pipeline-engine.d.ts +49 -8
- package/dist/core/pipeline-engine.d.ts.map +1 -1
- package/dist/core/pipeline-engine.js +221 -34
- package/dist/core/pipeline-engine.js.map +1 -1
- package/dist/core/prompt-builder.d.ts +12 -3
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +238 -15
- 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, // 单次迭代总任务数上限
|
|
@@ -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
|
-
|
|
397
|
+
// v8.3.160+: 步骤隔离模式,每步完成后需新会话继续
|
|
398
|
+
const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:步骤隔离\n\n` +
|
|
327
399
|
`当前是 Split Pipeline 模式。当你通过 --response 写入任务拆分结果后,` +
|
|
328
|
-
`CLI 会自动输出 [
|
|
329
|
-
`**看到 [
|
|
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
|
-
|
|
419
|
+
// v8.3.160+: 步骤隔离模式,每步完成后需新会话继续
|
|
420
|
+
const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:步骤隔离\n\n` +
|
|
348
421
|
`当前是 Split Pipeline 模式。当你通过 --response 写入任务拆分结果后,` +
|
|
349
|
-
`CLI 会自动输出 [
|
|
350
|
-
`**看到 [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
1441
|
-
|
|
1442
|
-
|
|
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 >
|
|
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
|
-
|
|
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
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
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 >
|
|
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 >
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 >
|
|
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
|
-
//
|
|
1523
|
-
|
|
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
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
p +=
|
|
2906
|
-
|
|
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 +=
|
|
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`;
|