openmatrix 0.2.23 → 0.2.25

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.
@@ -5,14 +5,8 @@ const answer_mapper_js_1 = require("./answer-mapper.js");
5
5
  /**
6
6
  * TaskPlanner - 任务拆解器
7
7
  *
8
- * 增强版特性:
9
- * 1. 更细粒度的任务拆分 (每个目标拆分为设计+实现+测试)
10
- * 2. 测试任务配对 (每个开发任务自动生成对应测试任务)
11
- * 3. 验收标准注入 (从用户回答中提取)
12
- * 4. 用户上下文注入 (将用户回答注入任务描述)
13
- * 5. 依赖关系分析 (自动分析任务间依赖)
14
- * 6. 并行执行 (独立任务互不依赖)
15
- * 7. 质量级别感知 (根据配置调整测试覆盖率)
8
+ * Plan 原文作为原始上下文透传给 AI Agent,由 AI 自行理解和提取
9
+ * 技术栈、数据模型、依赖关系等信息。CLI 层不做语义解析。
16
10
  */
17
11
  class TaskPlanner {
18
12
  userAnswers;
@@ -28,666 +22,21 @@ class TaskPlanner {
28
22
  this.userAnswers = answers;
29
23
  }
30
24
  /**
31
- * 解析 plan 文本,提取模块、技术栈、数据模型等结构化信息
25
+ * 任务拆解 — 唯一路径:按目标拆分,plan 作为原始上下文透传
32
26
  */
33
- parsePlan(planText) {
34
- const modules = [];
35
- const techStack = [];
36
- // 1. 提取技术栈
37
- const techStackMatch = planText.match(/(?:技术栈|Technology)[\s\S]*?((?:- .+\n?)+)/i);
38
- if (techStackMatch) {
39
- techStackMatch[1].split('\n').forEach(line => {
40
- const trimmed = line.replace(/^- /, '').trim();
41
- if (trimmed)
42
- techStack.push(trimmed);
43
- });
44
- }
45
- // 2. 提取模块定义
46
- // 模式1: "N领域模块: A、B、C..." 或 "N个领域模块: A, B, C"
47
- const moduleListMatch = planText.match(/(\d+)\s*(?:个)?领域模块\s*[::]\s*(.+)/i);
48
- if (moduleListMatch) {
49
- const moduleNames = moduleListMatch[2]
50
- .split(/[,,、]/)
51
- .map(s => s.trim().replace(/域$/, ''))
52
- .filter(s => s.length > 0 && s.length < 30);
53
- // 从 plan 中的 "数据模型" 部分提取表信息
54
- // 通用方式:查找以 "- " 开头的表名/实体名列表,不在模块列表中
55
- const moduleNamesSet = new Set(moduleNames.map(n => n.toLowerCase()));
56
- const allTables = [];
57
- const tableSections = planText.match(/(?:数据模型|database|tables?|schema|实体|模型)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/gi);
58
- if (tableSections) {
59
- for (const section of tableSections) {
60
- const tableLines = section.split('\n')
61
- .map(l => l.replace(/^[-*]\s*/, '').trim())
62
- .filter(l => l.length > 0 && l.length < 50);
63
- for (const t of tableLines) {
64
- // 跳过看起来像模块名的条目
65
- const tLower = t.toLowerCase();
66
- if (!moduleNamesSet.has(tLower) && !t.includes(':') && !t.includes('—')) {
67
- allTables.push(t);
68
- }
69
- }
70
- }
71
- }
72
- for (const modName of moduleNames) {
73
- // 通用方式:从 plan 中查找该模块相关的表(包含模块名的行附近)
74
- const modLower = modName.toLowerCase();
75
- const modTables = allTables.filter(t => t.toLowerCase().includes(modLower) ||
76
- modLower.includes(t.split(/[_\s]/)[0]?.toLowerCase() || ''));
77
- modules.push({
78
- name: modName,
79
- description: `${modName}模块`,
80
- tables: modTables,
81
- type: 'domain',
82
- dependsOn: [],
83
- complexity: this.estimateModuleComplexity(modName, modTables)
84
- });
85
- }
86
- // 分析模块间依赖
87
- this.analyzeModuleDependencies(modules, planText);
88
- }
89
- // 模式2: 架构设计部分的编号列表 "1. 用户域:描述"
90
- if (modules.length === 0) {
91
- const archMatch = planText.match(/架构设计[\s\S]*?((?:\d+\.\s*.+(?:\n|$))+)/i);
92
- if (archMatch) {
93
- const lines = archMatch[1].split('\n').filter(l => l.trim());
94
- for (const line of lines) {
95
- const modMatch = line.match(/\d+\.\s*(.+?)[::\s](.*)/);
96
- if (modMatch) {
97
- const name = modMatch[1].replace(/域$/, '').trim();
98
- modules.push({
99
- name,
100
- description: modMatch[2].trim(),
101
- tables: [],
102
- type: 'domain',
103
- dependsOn: [],
104
- complexity: this.estimateModuleComplexity(name, [])
105
- });
106
- }
107
- }
108
- this.analyzeModuleDependencies(modules, planText);
109
- }
110
- }
111
- // 模式3: 英文格式 "N modules: A, B, C" 或 "N components: A, B, C"
112
- if (modules.length === 0) {
113
- const enModuleMatch = planText.match(/(\d+)\s*(?:modules?|components?|domains?|features?)\s*[::]\s*(.+)/i);
114
- if (enModuleMatch) {
115
- const moduleNames = enModuleMatch[2]
116
- .split(/[,,、]/)
117
- .map(s => s.trim())
118
- .filter(s => s.length > 0 && s.length < 30);
119
- for (const modName of moduleNames) {
120
- modules.push({
121
- name: modName,
122
- description: `${modName} module`,
123
- tables: [],
124
- type: 'domain',
125
- dependsOn: [],
126
- complexity: this.estimateModuleComplexity(modName, [])
127
- });
128
- }
129
- this.analyzeModuleDependencies(modules, planText);
130
- }
131
- }
132
- // 模式4: Markdown 标题下的列表 "## Modules\n- A\n- B" 或 "## Architecture\n1. A\n2. B"
133
- if (modules.length === 0) {
134
- const mdPatterns = [
135
- /##\s*(?:模块|Modules?|架构|Architecture|领域|Domains?|功能|Features?)[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
136
- /##\s*(?:实现|Implementation|开发|Development)[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
137
- ];
138
- for (const pattern of mdPatterns) {
139
- const mdMatch = planText.match(pattern);
140
- if (mdMatch) {
141
- const lines = mdMatch[1].split('\n').filter(l => l.trim());
142
- for (const line of lines) {
143
- // 匹配 "- Name" 或 "1. Name" 或 "1. Name: Description"
144
- const itemMatch = line.match(/[-*\d]+\.\s*(.+?)(?:[::\s](.*))?/);
145
- if (itemMatch) {
146
- const name = itemMatch[1].trim();
147
- const desc = itemMatch[2]?.trim() || `${name} module`;
148
- if (name.length > 0 && name.length < 30 && !modules.some(m => m.name === name)) {
149
- modules.push({
150
- name,
151
- description: desc,
152
- tables: [],
153
- type: 'domain',
154
- dependsOn: [],
155
- complexity: this.estimateModuleComplexity(name, [])
156
- });
157
- }
158
- }
159
- }
160
- }
161
- if (modules.length > 0) {
162
- this.analyzeModuleDependencies(modules, planText);
163
- break;
164
- }
165
- }
166
- }
167
- return { modules, techStack, raw: planText };
168
- }
169
- /**
170
- * 从 plan 中提取结构化信息(用于 fallback 时注入任务描述)
171
- * 即使无法解析模块,也能保留关键技术信息
172
- */
173
- extractPlanMetadata(plan) {
174
- const techStack = [];
175
- const interfaces = [];
176
- const dataModels = [];
177
- const keyDecisions = [];
178
- // 1. 提取技术栈
179
- const techMatch = plan.match(/(?:技术栈|Technology|Tech Stack)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i);
180
- if (techMatch) {
181
- techMatch[1].split('\n').forEach(line => {
182
- const trimmed = line.replace(/^[-*]\s*/, '').trim();
183
- if (trimmed && trimmed.length < 50)
184
- techStack.push(trimmed);
185
- });
186
- }
187
- // 2. 提取接口/API
188
- const interfacePatterns = [
189
- /(?:接口|API|Endpoints?|接口定义)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i,
190
- /(?:##\s*(?:接口|API|Endpoints?))[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
191
- ];
192
- for (const pattern of interfacePatterns) {
193
- const match = plan.match(pattern);
194
- if (match) {
195
- match[1].split('\n').forEach(line => {
196
- const trimmed = line.replace(/^[-*\d]+\.\s*/, '').trim();
197
- if (trimmed && trimmed.length < 80 && !interfaces.includes(trimmed)) {
198
- interfaces.push(trimmed);
199
- }
200
- });
201
- if (interfaces.length > 0)
202
- break;
203
- }
204
- }
205
- // 3. 提取数据模型/表
206
- const dataPatterns = [
207
- /(?:数据模型|Database|Tables?|Schema|实体|模型)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i,
208
- /(?:##\s*(?:数据模型|Database|实体))[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
209
- ];
210
- for (const pattern of dataPatterns) {
211
- const match = plan.match(pattern);
212
- if (match) {
213
- match[1].split('\n').forEach(line => {
214
- const trimmed = line.replace(/^[-*\d]+\.\s*/, '').trim();
215
- if (trimmed && trimmed.length < 50 && !dataModels.includes(trimmed)) {
216
- dataModels.push(trimmed);
217
- }
218
- });
219
- if (dataModels.length > 0)
220
- break;
221
- }
222
- }
223
- // 4. 提取关键决策
224
- const decisionPatterns = [
225
- /(?:关键决策|Key Decisions|重要决策|决策点)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i,
226
- /(?:##\s*(?:关键决策|Key Decisions|决策))[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
227
- ];
228
- for (const pattern of decisionPatterns) {
229
- const match = plan.match(pattern);
230
- if (match) {
231
- match[1].split('\n').forEach(line => {
232
- const trimmed = line.replace(/^[-*\d]+\.\s*/, '').trim();
233
- if (trimmed && trimmed.length < 100 && !keyDecisions.includes(trimmed)) {
234
- keyDecisions.push(trimmed);
235
- }
236
- });
237
- if (keyDecisions.length > 0)
238
- break;
239
- }
240
- }
241
- return { techStack, interfaces, dataModels, keyDecisions };
242
- }
243
- /**
244
- * 从 goals 推断模块结构(当 plan 无法解析模块时使用)
245
- */
246
- inferModulesFromGoals(parsedTask) {
247
- const modules = [];
248
- if (!parsedTask.goalTypes || parsedTask.goals.length < 3) {
249
- return modules;
250
- }
251
- for (let i = 0; i < parsedTask.goals.length; i++) {
252
- const goal = parsedTask.goals[i];
253
- const goalType = parsedTask.goalTypes[i];
254
- // 只将 development 类型的 goal 作为模块
255
- if (goalType === 'development') {
256
- modules.push({
257
- name: goal.replace(/^(实现|开发|完成|Develop|Implement)\s*:?\s*/i, '').trim(),
258
- description: goal,
259
- tables: [],
260
- type: 'domain',
261
- dependsOn: [],
262
- complexity: this.estimateComplexity(goal)
263
- });
264
- }
265
- }
266
- // 添加基础依赖:第一个模块无依赖,后续模块依赖前一模块
267
- for (let i = 1; i < modules.length; i++) {
268
- // 默认并行,除非 goal 描述中明确提到依赖
269
- if (parsedTask.goals[i].toLowerCase().includes('基于') ||
270
- parsedTask.goals[i].toLowerCase().includes('依赖') ||
271
- parsedTask.goals[i].toLowerCase().includes('需要') ||
272
- parsedTask.goals[i].toLowerCase().includes('after')) {
273
- modules[i].dependsOn.push(modules[i - 1].name);
274
- }
275
- }
276
- return modules;
277
- }
278
- /**
279
- * 分析模块间依赖关系
280
- *
281
- * 策略:从 plan 文本中提取 AI 明确写的依赖信息,不做架构猜测。
282
- * 如果 plan 中没有指定依赖,模块之间并行执行。
283
- */
284
- analyzeModuleDependencies(modules, planText) {
285
- for (let i = 0; i < modules.length; i++) {
286
- const mod = modules[i];
287
- if (mod.dependsOn.length > 0)
288
- continue; // parsePlan 中已提取的显式依赖,保留
289
- // 从 plan 文本中查找该模块是否提到依赖其他模块
290
- // 匹配模式:"X 依赖 Y", "X 基于 Y", "X 使用 Y", "after X", "depends on X"
291
- const modContext = this.extractModuleContext(planText, mod.name);
292
- if (modContext) {
293
- for (const other of modules) {
294
- if (other.name === mod.name)
295
- continue;
296
- // 如果上下文中提到其他模块且暗示依赖关系
297
- const depPatterns = [
298
- new RegExp(`${other.name}.*(?:依赖|基于|需要|使用|after|depends)`, 'i'),
299
- new RegExp(`(?:依赖|基于|需要|使用|after|depends).*${other.name}`, 'i'),
300
- ];
301
- for (const pattern of depPatterns) {
302
- if (pattern.test(modContext)) {
303
- mod.dependsOn.push(other.name);
304
- break;
305
- }
306
- }
307
- }
308
- }
309
- // 如果 plan 中模块是按顺序编号的,后面编号的模块依赖前面的
310
- // 这只在模块名称是编号格式时适用(如 "1. 基础架构" → "2. 用户模块")
311
- // 不做自动推断,保持模块间独立
312
- }
313
- }
314
- /**
315
- * 从 plan 文本中提取某个模块相关的上下文段落
316
- */
317
- extractModuleContext(planText, moduleName) {
318
- const lines = planText.split('\n');
319
- const startIdx = lines.findIndex(l => l.includes(moduleName));
320
- if (startIdx === -1)
321
- return null;
322
- // 从匹配行开始,收集到下一个编号标题或空行为止
323
- const contextLines = [];
324
- for (let i = startIdx; i < lines.length && i < startIdx + 20; i++) {
325
- const line = lines[i];
326
- if (contextLines.length > 0 && line.trim() === '')
327
- break;
328
- if (contextLines.length > 0 && /^\d+\./.test(line.trim()))
329
- break;
330
- contextLines.push(line);
331
- }
332
- return contextLines.join('\n');
333
- }
334
- /**
335
- * 预估模块复杂度 — 基于通用架构特征,不依赖具体业务领域
336
- */
337
- estimateModuleComplexity(name, tables) {
338
- // 表数量是最直接的复杂度指标
339
- if (tables.length >= 5)
340
- return 'high';
341
- if (tables.length >= 3)
342
- return 'medium';
343
- // 通用架构关键词
344
- const highKws = ['核心', '基础', '架构', '主循环', '引擎', '框架', '平台', '系统', 'orchestrator', 'engine', 'core', 'framework'];
345
- const mediumKws = ['管理', '服务', 'api', '接口', '控制器', '处理器', '管理器', 'manager', 'service', 'handler', 'processor', 'controller'];
346
- const lowKws = ['工具', '脚本', '配置', '样式', '工具', 'helper', 'util', 'config', 'style', 'theme'];
347
- const n = name.toLowerCase();
348
- if (highKws.some(kw => n.includes(kw)))
349
- return 'high';
350
- if (lowKws.some(kw => n.includes(kw)))
351
- return 'low';
352
- if (mediumKws.some(kw => n.includes(kw)))
353
- return 'medium';
354
- // 默认 medium(比默认 low 更保守,避免并行执行冲突)
355
- return 'medium';
356
- }
357
27
  breakdown(parsedTask, answers, qualityConfig, plan) {
358
- // 如果提供了 plan,尝试解析模块
359
- if (plan) {
360
- const parsed = this.parsePlan(plan);
361
- if (parsed.modules.length > 0) {
362
- return this.breakdownByModules(parsedTask, answers, qualityConfig, parsed, plan);
363
- }
364
- // plan 无法解析模块时,尝试从 goals 推断
365
- const inferredModules = this.inferModulesFromGoals(parsedTask);
366
- if (inferredModules.length > 0) {
367
- const inferredPlan = {
368
- modules: inferredModules,
369
- techStack: parsed.techStack,
370
- raw: plan
371
- };
372
- return this.breakdownByModules(parsedTask, answers, qualityConfig, inferredPlan, plan);
373
- }
374
- }
375
- // fallback: 按目标拆分,但保留 plan metadata
376
- const planMetadata = plan ? this.extractPlanMetadata(plan) : undefined;
377
- return this.breakdownByGoals(parsedTask, answers, qualityConfig, plan, planMetadata);
378
- }
379
- /**
380
- * 基于 plan 解析出的模块做细粒度任务拆分
381
- */
382
- breakdownByModules(parsedTask, answers, qualityConfig, parsedPlan, plan) {
383
- const breakdowns = [];
384
- const userContext = this.extractUserContext(answers);
385
- if (qualityConfig?.e2eTests) {
386
- userContext.e2eTests = true;
387
- if (!userContext.e2eType) {
388
- userContext.e2eType = 'web';
389
- }
390
- }
391
- const coverageTarget = this.getCoverageTarget(qualityConfig, userContext);
392
- const globalContext = this.buildGlobalContext(parsedTask, userContext, plan);
393
- // 提取 planMetadata 用于测试任务描述注入
394
- const planMetadata = this.extractPlanMetadata(plan);
395
- // 1. 为每个模块创建开发 + 测试任务对
396
- const devTaskIds = [];
397
- const moduleIdToTaskIds = new Map();
398
- for (const mod of parsedPlan.modules) {
399
- const modTaskId = this.generateTaskId();
400
- devTaskIds.push(modTaskId);
401
- moduleIdToTaskIds.set(mod.name, [modTaskId]);
402
- // 构建模块描述 — 包含表名、依赖等具体信息
403
- let modDescription = `## 模块实现: ${mod.name}\n\n${mod.description}\n\n${globalContext}`;
404
- if (mod.tables.length > 0) {
405
- modDescription += `\n\n## 数据模型\n需要实现以下数据库表:\n${mod.tables.map(t => `- \`${t}\``).join('\n')}`;
406
- }
407
- if (mod.dependsOn.length > 0) {
408
- modDescription += `\n\n## 模块依赖\n本模块依赖以下模块: ${mod.dependsOn.join(', ')}\n请确保依赖模块的接口已定义并可调用。`;
409
- }
410
- modDescription += `\n\n## 输出要求\n- 完成模块实现\n- 代码可编译\n- 遵循项目规范\n- 添加必要注释`;
411
- // 计算模块任务的实际依赖(转换为 taskId)
412
- const modDeps = [];
413
- for (const depName of mod.dependsOn) {
414
- const depTaskIds = moduleIdToTaskIds.get(depName);
415
- if (depTaskIds) {
416
- modDeps.push(...depTaskIds);
417
- }
418
- }
419
- // 同时保留 phase 级别的依赖
420
- this.enforcePhaseDependenciesForModule(breakdowns, parsedTask, mod, modDeps);
421
- const complexity = this.estimateModuleComplexity(mod.name, mod.tables);
422
- breakdowns.push({
423
- taskId: modTaskId,
424
- title: `实现: ${mod.name}`,
425
- description: modDescription,
426
- priority: this.determineModulePriority(mod, parsedPlan.modules.indexOf(mod)),
427
- dependencies: modDeps,
428
- estimatedComplexity: complexity,
429
- assignedAgent: 'coder',
430
- phase: 'develop',
431
- acceptanceCriteria: this.generateModuleAcceptanceCriteria(mod, coverageTarget, userContext),
432
- testTaskId: undefined
433
- });
434
- // 配对的测试任务
435
- const testTaskId = this.generateTaskId();
436
- breakdowns.push({
437
- taskId: testTaskId,
438
- title: `测试: ${mod.name}`,
439
- description: this.buildTestDescription(mod.name, modTaskId, coverageTarget, globalContext, planMetadata),
440
- priority: this.determineModulePriority(mod, parsedPlan.modules.indexOf(mod)),
441
- dependencies: [modTaskId],
442
- estimatedComplexity: 'medium',
443
- assignedAgent: 'tester',
444
- phase: 'verify',
445
- acceptanceCriteria: [
446
- `单元测试覆盖率 >= ${coverageTarget}%`,
447
- '边界情况已测试',
448
- '异常处理已验证',
449
- '所有测试通过'
450
- ]
451
- });
452
- breakdowns[breakdowns.length - 2].testTaskId = testTaskId;
453
- moduleIdToTaskIds.get(mod.name).push(testTaskId);
454
- }
455
- // 2. 系统集成任务
456
- let integrationTaskId;
457
- if (devTaskIds.length > 1) {
458
- integrationTaskId = this.generateTaskId();
459
- breakdowns.push({
460
- taskId: integrationTaskId,
461
- title: '系统集成: 将所有模块组装到主入口,确保可运行',
462
- description: `将前面所有模块连接在一起,使应用可以完整运行
463
-
464
- ${globalContext}
465
-
466
- ## 集成要求
467
- - 确定项目的主入口文件并实例化所有核心模块
468
- - 建立模块间通信和数据流
469
- - 确保应用可以启动并正常运行
470
-
471
- ## 已完成的模块
472
- ${parsedPlan.modules.map(m => `- ${m.name} (${m.tables.length > 0 ? '表: ' + m.tables.join(', ') : '无数据模型'})`).join('\n')}
473
-
474
- ## 输出
475
- - 更新后的主入口文件
476
- - 模块连接正确,应用可运行
477
- - 无运行时错误`,
478
- priority: 'P0',
479
- dependencies: [...devTaskIds],
480
- estimatedComplexity: 'high',
481
- assignedAgent: 'coder',
482
- phase: 'develop',
483
- acceptanceCriteria: [
484
- '主入口文件已更新',
485
- '所有核心模块已实例化并连接',
486
- '应用可以正常启动',
487
- '无运行时错误',
488
- '模块间通信正常'
489
- ]
490
- });
491
- }
492
- // 3. 代码审查任务
493
- if (devTaskIds.length > 0) {
494
- const reviewDeps = integrationTaskId
495
- ? [...devTaskIds, integrationTaskId]
496
- : [...devTaskIds];
497
- breakdowns.push({
498
- taskId: this.generateTaskId(),
499
- title: '代码审查',
500
- description: `对所有开发任务进行代码审查
501
-
502
- ${globalContext}
503
-
504
- ## 审查范围
505
- ${parsedPlan.modules.map(m => `- ${m.name}: ${m.description}`).join('\n')}
506
-
507
- ## 审查要点
508
- - 代码质量
509
- - 安全性
510
- - 性能
511
- - 最佳实践`,
512
- priority: 'P1',
513
- dependencies: reviewDeps,
514
- estimatedComplexity: 'medium',
515
- assignedAgent: 'reviewer',
516
- phase: 'verify',
517
- acceptanceCriteria: [
518
- '无严重代码问题',
519
- '无安全隐患',
520
- '代码符合规范',
521
- '审查报告已生成'
522
- ]
523
- });
524
- }
525
- // 4. 集成测试任务 (如果有多个交付物)
526
- if (parsedTask.deliverables.length > 1) {
527
- const integrationTestDeps = integrationTaskId
528
- ? [...devTaskIds, integrationTaskId]
529
- : [...devTaskIds];
530
- breakdowns.push({
531
- taskId: this.generateTaskId(),
532
- title: '集成测试',
533
- description: `验证所有交付物正确集成
534
-
535
- ${globalContext}
536
-
537
- ## 测试范围
538
- - 模块间接口
539
- - 端到端流程
540
- - 数据流验证`,
541
- priority: 'P1',
542
- dependencies: integrationTestDeps,
543
- estimatedComplexity: 'medium',
544
- assignedAgent: 'tester',
545
- phase: 'verify',
546
- acceptanceCriteria: [
547
- '所有模块正确集成',
548
- '端到端流程通过',
549
- '接口兼容性验证',
550
- '集成测试报告完整'
551
- ]
552
- });
553
- }
554
- // 5. E2E 测试任务
555
- if (userContext.e2eTests) {
556
- const e2eTaskId = this.generateTaskId();
557
- const e2eType = userContext.e2eType || 'web';
558
- const allTestDeps = [...devTaskIds];
559
- breakdowns.forEach(b => {
560
- if (b.phase === 'verify' && b.title.startsWith('测试:')) {
561
- allTestDeps.push(b.taskId);
562
- }
563
- });
564
- breakdowns.push({
565
- taskId: e2eTaskId,
566
- title: '端到端(E2E)测试',
567
- description: this.buildE2ETestDescription(e2eType, parsedTask, userContext),
568
- priority: 'P0',
569
- dependencies: allTestDeps,
570
- estimatedComplexity: 'high',
571
- assignedAgent: 'tester',
572
- phase: 'verify',
573
- acceptanceCriteria: [
574
- '所有 E2E 测试用例通过',
575
- '关键用户流程验证完成',
576
- '跨浏览器/设备兼容性验证',
577
- 'E2E 测试报告已生成',
578
- '无阻塞级别的缺陷'
579
- ]
580
- });
581
- }
582
- // 6. 文档任务
583
- if (userContext.documentationLevel && userContext.documentationLevel !== '无需文档') {
584
- breakdowns.push({
585
- taskId: this.generateTaskId(),
586
- title: '文档编写',
587
- description: `编写项目文档
588
-
589
- ## 文档级别
590
- ${userContext.documentationLevel}
591
-
592
- ## 文档内容
593
- - README 更新
594
- - API 文档
595
- - 使用说明`,
596
- priority: 'P2',
597
- dependencies: devTaskIds,
598
- estimatedComplexity: 'low',
599
- assignedAgent: 'executor',
600
- phase: 'accept',
601
- acceptanceCriteria: [
602
- 'README 已更新',
603
- 'API 文档完整',
604
- '使用说明清晰'
605
- ]
606
- });
607
- }
608
- return breakdowns;
609
- }
610
- /**
611
- * 为模块任务添加 phase 级别的跨阶段依赖
612
- */
613
- enforcePhaseDependenciesForModule(breakdowns, parsedTask, mod, modDeps) {
614
- // 检测是否有顺序阶段,如果有则添加跨阶段依赖
615
- const phaseIndices = this.detectSequentialPhases(parsedTask.goals);
616
- if (phaseIndices.length < 2)
617
- return;
618
- // 找出当前模块属于哪个 phase goal
619
- let currentPhaseIndex = -1;
620
- for (let pi = 0; pi < phaseIndices.length; pi++) {
621
- const goalIndex = phaseIndices[pi];
622
- const goal = parsedTask.goals[goalIndex];
623
- if (mod.name.toLowerCase().includes(goal.toLowerCase().slice(0, 10)) ||
624
- goal.toLowerCase().includes(mod.name.toLowerCase().slice(0, 6))) {
625
- currentPhaseIndex = pi;
626
- break;
627
- }
628
- }
629
- if (currentPhaseIndex <= 0)
630
- return;
631
- // 添加对前一阶段所有模块任务的依赖
632
- const prevGoalIndex = phaseIndices[currentPhaseIndex - 1];
633
- for (const b of breakdowns) {
634
- if (b.phase === 'develop' && b.title.startsWith('实现: ')) {
635
- // 检查这个任务是否属于前一阶段
636
- const prevGoal = parsedTask.goals[prevGoalIndex];
637
- if (b.description.includes(prevGoal)) {
638
- if (!modDeps.includes(b.taskId)) {
639
- modDeps.push(b.taskId);
640
- }
641
- }
642
- }
643
- }
644
- }
645
- /**
646
- * 确定模块任务优先级
647
- */
648
- determineModulePriority(mod, index) {
649
- // 基础设施模块优先级更高
650
- if (mod.type === 'infra')
651
- return 'P0';
652
- // 有依赖的模块通常更核心
653
- if (mod.dependsOn.length === 0 && index === 0)
654
- return 'P0';
655
- if (mod.complexity === 'high')
656
- return 'P1';
657
- return 'P2';
28
+ return this.breakdownByGoals(parsedTask, answers, qualityConfig, plan);
658
29
  }
659
30
  /**
660
- * 为模块生成验收标准
31
+ * 按目标拆分子任务
661
32
  */
662
- generateModuleAcceptanceCriteria(mod, coverageTarget, userContext) {
663
- const criteria = [
664
- `模块 "${mod.name}" 功能已实现`,
665
- '代码可编译,无错误',
666
- '代码符合项目规范'
667
- ];
668
- if (mod.tables.length > 0) {
669
- criteria.push(`数据库表 ${mod.tables.join(', ')} 已实现并可访问`);
670
- }
671
- criteria.push(`测试覆盖率 >= ${coverageTarget}%`);
672
- criteria.push('必要的注释已添加');
673
- criteria.push('无安全隐患');
674
- criteria.push('边界情况已处理');
675
- if (userContext.techStack?.length) {
676
- criteria.push(`使用指定技术栈: ${userContext.techStack.join(', ')}`);
677
- }
678
- return criteria;
679
- }
680
- /**
681
- * 按目标拆分的传统方式(fallback)
682
- */
683
- breakdownByGoals(parsedTask, answers, qualityConfig, plan, planMetadata) {
33
+ breakdownByGoals(parsedTask, answers, qualityConfig, plan) {
684
34
  const breakdowns = [];
685
35
  const seenTitles = new Set();
686
36
  const userContext = this.extractUserContext(answers);
687
37
  // qualityConfig 中的 e2eTests 优先级高于 answers 中的推断
688
38
  if (qualityConfig?.e2eTests) {
689
39
  userContext.e2eTests = true;
690
- // 如果 e2eType 未指定,从质量配置推断
691
40
  if (!userContext.e2eType) {
692
41
  userContext.e2eType = 'web';
693
42
  }
@@ -729,13 +78,11 @@ ${globalContext}
729
78
  const devTaskIds = [];
730
79
  for (let i = 0; i < parsedTask.goals.length; i++) {
731
80
  const goal = parsedTask.goals[i];
732
- // 跳过重复项
733
- if (seenTitles.has(goal)) {
81
+ if (seenTitles.has(goal))
734
82
  continue;
735
- }
736
83
  seenTitles.add(goal);
737
- // 优先使用 AI 标注的类型,fallback 到关键词检测
738
- const goalType = parsedTask.goalTypes?.[i] ?? this.classifyGoal(goal);
84
+ // goalTypes AI tasks-input.json 中必填
85
+ const goalType = parsedTask.goalTypes?.[i] ?? 'development';
739
86
  const deps = designTaskId ? [designTaskId] : [];
740
87
  if (goalType === 'development') {
741
88
  // 开发类目标: 拆分为实现 + 测试 对
@@ -745,10 +92,10 @@ ${globalContext}
745
92
  breakdowns.push({
746
93
  taskId: devTaskId,
747
94
  title: `实现: ${goal}`,
748
- description: this.buildTaskDescription(goal, globalContext, planMetadata),
95
+ description: this.buildTaskDescription(goal, globalContext),
749
96
  priority: this.determinePriority(i),
750
97
  dependencies: deps,
751
- estimatedComplexity: this.estimateComplexity(goal),
98
+ estimatedComplexity: this.estimateComplexity(goal, i, parsedTask),
752
99
  assignedAgent: 'coder',
753
100
  phase: 'develop',
754
101
  acceptanceCriteria,
@@ -759,7 +106,7 @@ ${globalContext}
759
106
  breakdowns.push({
760
107
  taskId: testTaskId,
761
108
  title: `测试: ${goal}`,
762
- description: this.buildTestDescription(goal, devTaskId, coverageTarget, globalContext, planMetadata),
109
+ description: this.buildTestDescription(goal, devTaskId, coverageTarget, globalContext),
763
110
  priority: this.determinePriority(i),
764
111
  dependencies: [devTaskId],
765
112
  estimatedComplexity: 'medium',
@@ -780,7 +127,21 @@ ${globalContext}
780
127
  breakdowns.push({
781
128
  taskId,
782
129
  title: goal,
783
- description: `## 测试目标\n${goal}\n\n${globalContext}\n\n## 测试要求\n- 单元测试覆盖率 >= ${coverageTarget}%\n- 测试正常流程\n- 测试边界情况\n- 测试异常处理\n\n## 输出\n- 测试文件\n- 测试报告\n- 覆盖率报告`,
130
+ description: `## 测试目标
131
+ ${goal}
132
+
133
+ ${globalContext}
134
+
135
+ ## 测试要求
136
+ - 单元测试覆盖率 >= ${coverageTarget}%
137
+ - 测试正常流程
138
+ - 测试边界情况
139
+ - 测试异常处理
140
+
141
+ ## 输出
142
+ - 测试文件
143
+ - 测试报告
144
+ - 覆盖率报告`,
784
145
  priority: this.determinePriority(i),
785
146
  dependencies: deps,
786
147
  estimatedComplexity: 'medium',
@@ -800,7 +161,15 @@ ${globalContext}
800
161
  breakdowns.push({
801
162
  taskId,
802
163
  title: goal,
803
- description: `## 文档目标\n${goal}\n\n${globalContext}\n\n## 输出要求\n- 文档内容完整\n- 格式清晰\n- 示例代码可运行`,
164
+ description: `## 文档目标
165
+ ${goal}
166
+
167
+ ${globalContext}
168
+
169
+ ## 输出要求
170
+ - 文档内容完整
171
+ - 格式清晰
172
+ - 示例代码可运行`,
804
173
  priority: this.determinePriority(i),
805
174
  dependencies: deps,
806
175
  estimatedComplexity: 'low',
@@ -819,10 +188,17 @@ ${globalContext}
819
188
  breakdowns.push({
820
189
  taskId,
821
190
  title: goal,
822
- description: `## 目标\n${goal}\n\n${globalContext}\n\n## 输出要求\n- 完成目标\n- 验证结果正确`,
191
+ description: `## 目标
192
+ ${goal}
193
+
194
+ ${globalContext}
195
+
196
+ ## 输出要求
197
+ - 完成目标
198
+ - 验证结果正确`,
823
199
  priority: this.determinePriority(i),
824
200
  dependencies: deps,
825
- estimatedComplexity: this.estimateComplexity(goal),
201
+ estimatedComplexity: this.estimateComplexity(goal, i, parsedTask),
826
202
  assignedAgent: 'coder',
827
203
  phase: 'develop',
828
204
  acceptanceCriteria: [
@@ -872,7 +248,6 @@ ${devTaskIds.map(id => `- ${id}: ${breakdowns.find(b => b.taskId === id)?.title
872
248
  }
873
249
  // 3. 代码审查任务 (仅在有开发任务时创建)
874
250
  if (devTaskIds.length > 0) {
875
- // 审查依赖所有开发任务 + 系统集成任务(如果有)
876
251
  const reviewDeps = integrationTaskId
877
252
  ? [...devTaskIds, integrationTaskId]
878
253
  : [...devTaskIds];
@@ -904,7 +279,7 @@ ${devTaskIds.map(id => `- ${id}`).join('\n')}
904
279
  ]
905
280
  });
906
281
  }
907
- // 3. 集成测试任务 (如果有多个交付物)
282
+ // 4. 集成测试任务 (如果有多个交付物)
908
283
  if (parsedTask.deliverables.length > 1) {
909
284
  const integrationTestDeps = integrationTaskId
910
285
  ? [...devTaskIds, integrationTaskId]
@@ -933,7 +308,7 @@ ${globalContext}
933
308
  ]
934
309
  });
935
310
  }
936
- // 4. E2E 测试任务 (如果启用)
311
+ // 5. E2E 测试任务 (如果启用)
937
312
  if (userContext.e2eTests) {
938
313
  const e2eTaskId = this.generateTaskId();
939
314
  const e2eType = userContext.e2eType || 'web';
@@ -961,8 +336,8 @@ ${globalContext}
961
336
  ]
962
337
  });
963
338
  }
964
- // 5. 文档任务 (如果需要)
965
- if (userContext.documentationLevel && userContext.documentationLevel !== '无需文档') {
339
+ // 6. 文档任务 (如果需要)
340
+ if (userContext.documentationLevel && userContext.documentationLevel !== '无需') {
966
341
  breakdowns.push({
967
342
  taskId: this.generateTaskId(),
968
343
  title: '文档编写',
@@ -987,26 +362,12 @@ ${userContext.documentationLevel}
987
362
  ]
988
363
  });
989
364
  }
990
- // 6. 检测顺序阶段并建立跨阶段依赖(Phase 1 → Phase 2 → Phase 3 → ...)
365
+ // 7. 检测顺序阶段并建立跨阶段依赖(Phase 1 → Phase 2 → Phase 3 → ...)
991
366
  this.enforcePhaseDependencies(breakdowns, parsedTask);
992
367
  return breakdowns;
993
368
  }
994
- /**
995
- * 判断是否需要设计阶段
996
- *
997
- * 条件: 多个 goal,或 goal 包含复杂关键词
998
- */
999
369
  needsDesignPhase(parsedTask) {
1000
- if (parsedTask.goals.length > 1)
1001
- return true;
1002
- // 单 goal 但包含复杂关键词
1003
- const complexKeywords = [
1004
- '系统', '架构', '模块', '集成', '完整', '平台',
1005
- '体系', '框架', '全栈', '端到端', '多个', '一系列',
1006
- 'system', 'architecture', 'framework', 'fullstack', 'integration'
1007
- ];
1008
- const allText = `${parsedTask.title} ${parsedTask.goals.join(' ')} ${parsedTask.description}`.toLowerCase();
1009
- return complexKeywords.some(kw => allText.includes(kw.toLowerCase()));
370
+ return parsedTask.goals.length > 1;
1010
371
  }
1011
372
  /**
1012
373
  * 获取测试覆盖率目标
@@ -1026,23 +387,24 @@ ${userContext.documentationLevel}
1026
387
  return 60;
1027
388
  }
1028
389
  /**
1029
- * 提取用户上下文
390
+ * 提取用户上下文 — 使用 translateBrainstormAnswers 映射后的规范化键
1030
391
  */
1031
392
  extractUserContext(answers) {
1032
- // 先翻译 brainstorm 规范键为 planner 期望的键
1033
393
  const translated = (0, answer_mapper_js_1.translateBrainstormAnswers)(answers);
1034
394
  const merged = { ...answers, ...translated };
1035
- // 辅助函数:提取字符串值(处理 string[] 情况)
1036
395
  const str = (v) => Array.isArray(v) ? v.join(', ') : v;
1037
- const e2eAnswer = str(merged['E2E测试'] || merged['e2eTests'] || merged['e2e']);
1038
- const e2eTypeAnswer = str(merged['E2E类型'] || merged['e2eType']);
1039
- const isE2EEnabled = e2eAnswer === 'true' || e2eAnswer === 'functional' || e2eAnswer === 'visual' || e2eAnswer === '✅ 启用 E2E 测试' || e2eAnswer === '是';
1040
- let e2eTypeValue = e2eAnswer === 'visual' ? 'visual' : e2eTypeAnswer === 'visual' ? 'visual' : e2eTypeAnswer === 'mobile' ? 'mobile' : e2eTypeAnswer === 'gui' ? 'gui' : 'web';
396
+ const e2eAnswer = str(merged['e2e_tests'] || merged['e2eTests']);
397
+ const e2eTypeAnswer = str(merged['e2e_type'] || merged['e2eType']);
398
+ const isE2EEnabled = e2eAnswer === 'functional' || e2eAnswer === 'visual' || e2eAnswer === 'true';
399
+ const e2eTypeValue = e2eAnswer === 'visual' ? 'visual' :
400
+ e2eTypeAnswer === 'visual' ? 'visual' :
401
+ e2eTypeAnswer === 'mobile' ? 'mobile' :
402
+ e2eTypeAnswer === 'gui' ? 'gui' : 'web';
1041
403
  return {
1042
- objective: str(merged['目标'] || merged['objective']),
1043
- techStack: this.parseArrayAnswer(str(merged['技术栈'] || merged['techStack']) || ''),
1044
- testCoverage: str(merged['测试'] || merged['testCoverage']),
1045
- documentationLevel: str(merged['文档'] || merged['documentationLevel']),
404
+ objective: str(merged['objective']),
405
+ techStack: this.parseArrayAnswer(str(merged['tech_stack'] || merged['techStack'])),
406
+ testCoverage: str(merged['test_coverage'] || merged['testCoverage']),
407
+ documentationLevel: str(merged['documentation_level'] || merged['documentationLevel']),
1046
408
  e2eTests: isE2EEnabled,
1047
409
  e2eType: e2eTypeValue,
1048
410
  additionalContext: merged
@@ -1059,72 +421,66 @@ ${userContext.documentationLevel}
1059
421
  buildGlobalContext(parsedTask, userContext, plan) {
1060
422
  const parts = [];
1061
423
  if (parsedTask.title) {
1062
- parts.push(`## 整体任务\n${parsedTask.title}`);
424
+ parts.push(`## 整体任务
425
+ ${parsedTask.title}`);
1063
426
  }
1064
427
  if (parsedTask.description) {
1065
- parts.push(`## 任务描述\n${parsedTask.description}`);
428
+ parts.push(`## 任务描述
429
+ ${parsedTask.description}`);
1066
430
  }
431
+ // Plan 原文完整透传,由 AI Agent 自行理解提取
1067
432
  if (plan) {
1068
- parts.push(`## 执行计划\n${plan}`);
433
+ parts.push(`## 执行计划
434
+ ${plan}`);
1069
435
  }
1070
436
  if (parsedTask.goals.length > 0) {
1071
- parts.push(`## 所有目标\n${parsedTask.goals.map((g, i) => `${i + 1}. ${g}`).join('\n')}`);
437
+ parts.push(`## 所有目标
438
+ ${parsedTask.goals.map((g, i) => `${i + 1}. ${g}`).join('\n')}`);
1072
439
  }
1073
440
  if (parsedTask.constraints.length > 0) {
1074
- parts.push(`## 约束条件\n${parsedTask.constraints.map(c => `- ${c}`).join('\n')}`);
441
+ parts.push(`## 约束条件
442
+ ${parsedTask.constraints.map(c => `- ${c}`).join('\n')}`);
1075
443
  }
1076
444
  if (parsedTask.deliverables.length > 0) {
1077
- parts.push(`## 交付物\n${parsedTask.deliverables.map(d => `- ${d}`).join('\n')}`);
445
+ parts.push(`## 交付物
446
+ ${parsedTask.deliverables.map(d => `- ${d}`).join('\n')}`);
1078
447
  }
1079
448
  if (userContext.techStack && userContext.techStack.length > 0) {
1080
- parts.push(`## 技术栈\n${userContext.techStack.map(t => `- ${t}`).join('\n')}`);
449
+ parts.push(`## 技术栈
450
+ ${userContext.techStack.map(t => `- ${t}`).join('\n')}`);
1081
451
  }
1082
452
  if (userContext.additionalContext) {
1083
- const relevantAnswers = Object.entries(userContext.additionalContext).filter(([key]) => !['目标', '技术栈', '测试', '文档', 'objective', 'techStack', 'testCoverage', 'documentationLevel'].includes(key));
453
+ const skipKeys = new Set(['objective', 'tech_stack', 'test_coverage', 'documentation_level', 'e2e_tests', 'e2e_type', 'quality_level', 'execution_mode']);
454
+ const relevantAnswers = Object.entries(userContext.additionalContext).filter(([key]) => !skipKeys.has(key));
1084
455
  if (relevantAnswers.length > 0) {
1085
- parts.push(`## 其他要求\n${relevantAnswers.map(([k, v]) => `- ${k}: ${v}`).join('\n')}`);
456
+ parts.push(`## 其他要求
457
+ ${relevantAnswers.map(([k, v]) => `- ${k}: ${v}`).join('\n')}`);
1086
458
  }
1087
459
  }
1088
460
  return parts.join('\n');
1089
461
  }
1090
- buildTaskDescription(goal, globalContext, planMetadata) {
1091
- let desc = `## 当前子任务目标\n${goal}\n\n${globalContext}`;
1092
- // 注入 plan 提取的关键信息
1093
- if (planMetadata && planMetadata.interfaces.length > 0) {
1094
- desc += `\n\n## 相关接口/API\n${planMetadata.interfaces.slice(0, 10).map(i => `- ${i}`).join('\n')}`;
1095
- }
1096
- if (planMetadata && planMetadata.dataModels.length > 0) {
1097
- desc += `\n\n## 相关数据模型\n${planMetadata.dataModels.slice(0, 10).map(d => `- ${d}`).join('\n')}`;
1098
- }
1099
- if (planMetadata && planMetadata.keyDecisions.length > 0) {
1100
- desc += `\n\n## 关键决策参考\n${planMetadata.keyDecisions.slice(0, 5).map(d => `- ${d}`).join('\n')}`;
1101
- }
1102
- desc += `\n\n## 输出要求
462
+ buildTaskDescription(goal, globalContext) {
463
+ return `## 当前子任务目标
464
+ ${goal}
465
+
466
+ ${globalContext}
467
+
468
+ ## 输出要求
1103
469
  - 完成功能实现
1104
470
  - 代码可编译
1105
471
  - 遵循项目规范
1106
472
  - 添加必要注释`;
1107
- return desc;
1108
473
  }
1109
- buildTestDescription(goal, devTaskId, coverageTarget, globalContext, planMetadata) {
1110
- let desc = `## 测试目标
474
+ buildTestDescription(goal, devTaskId, coverageTarget, globalContext) {
475
+ return `## 测试目标
1111
476
  为 "${goal}" 编写测试用例
1112
477
 
1113
478
  ${globalContext}
1114
479
 
1115
480
  ## 关联开发任务
1116
- ${devTaskId}`;
1117
- // 注入 plan 提取的关键信息(参考 buildTaskDescription)
1118
- if (planMetadata && planMetadata.interfaces.length > 0) {
1119
- desc += `\n\n## 相关接口/API\n${planMetadata.interfaces.slice(0, 10).map(i => `- ${i}`).join('\n')}`;
1120
- }
1121
- if (planMetadata && planMetadata.dataModels.length > 0) {
1122
- desc += `\n\n## 相关数据模型\n${planMetadata.dataModels.slice(0, 10).map(d => `- ${d}`).join('\n')}`;
1123
- }
1124
- if (planMetadata && planMetadata.keyDecisions.length > 0) {
1125
- desc += `\n\n## 关键决策参考\n${planMetadata.keyDecisions.slice(0, 5).map(d => `- ${d}`).join('\n')}`;
1126
- }
1127
- desc += `\n\n## 测试要求
481
+ ${devTaskId}
482
+
483
+ ## 测试要求
1128
484
  - 单元测试覆盖率 >= ${coverageTarget}%
1129
485
  - 测试正常流程
1130
486
  - 测试边界情况
@@ -1138,7 +494,6 @@ ${devTaskId}`;
1138
494
  - 测试文件
1139
495
  - 测试报告
1140
496
  - 覆盖率报告`;
1141
- return desc;
1142
497
  }
1143
498
  buildE2ETestDescription(e2eType, parsedTask, userContext) {
1144
499
  const typeConfig = this.getE2ETypeConfig(e2eType);
@@ -1151,7 +506,7 @@ ${devTaskId}`;
1151
506
  5. **截图对比**: 关键页面应生成截图,便于人工审核
1152
507
  ` : '';
1153
508
  return `## E2E 测试目标
1154
- 执行完整的端到端测试,验证关键用户流程
509
+ 执行完整的端到端测试,验证完整用户流程
1155
510
  ${e2eType === 'visual' ? '(需可视化验证,检查页面样式和布局)' : ''}
1156
511
 
1157
512
  ## 应用类型
@@ -1266,60 +621,12 @@ ${typeConfig.runCommand}
1266
621
  return 'P1';
1267
622
  return 'P2';
1268
623
  }
1269
- estimateComplexity(goal) {
1270
- if (goal.includes('测试'))
1271
- return 'medium';
1272
- if (goal.includes('实现') || goal.includes('开发'))
1273
- return 'medium';
1274
- if (goal.includes('设计') || goal.includes('研究'))
1275
- return 'high';
1276
- if (goal.includes('文档') || goal.includes('说明'))
1277
- return 'low';
624
+ estimateComplexity(_goal, goalIndex, task) {
625
+ // 优先使用 AI 标注的复杂度
626
+ if (task.goalComplexity?.[goalIndex])
627
+ return task.goalComplexity[goalIndex];
1278
628
  return 'medium';
1279
629
  }
1280
- /**
1281
- * 分类目标类型
1282
- * - development: 需要编写代码的功能实现 → 拆分为实现+测试对
1283
- * - testing: 已明确是测试任务 → 单个测试任务
1284
- * - documentation: 文档编写 → 单个文档任务
1285
- * - other: 其他类型(配置、优化、部署等) → 单个任务
1286
- */
1287
- classifyGoal(goal) {
1288
- const g = goal.toLowerCase();
1289
- // 测试类关键词
1290
- const testKeywords = [
1291
- '测试', 'test', 'testing', 'tdd', 'e2e', 'e2e测试',
1292
- '单元测试', '集成测试', '端到端', '覆盖率', 'coverage',
1293
- 'vitest', 'jest', 'mocha', 'playwright', 'cypress',
1294
- ];
1295
- if (testKeywords.some(kw => g.includes(kw))) {
1296
- return 'testing';
1297
- }
1298
- // 文档类关键词
1299
- const docKeywords = [
1300
- '文档', 'document', 'documentation', 'readme', '说明',
1301
- '指南', 'guide', 'tutorial', 'api文档',
1302
- ];
1303
- if (docKeywords.some(kw => g.includes(kw))) {
1304
- return 'documentation';
1305
- }
1306
- // 非开发类关键词(配置、部署等)
1307
- const nonDevKeywords = [
1308
- '配置', 'config', 'deploy', '部署', 'ci/cd', '发布',
1309
- 'release', '优化', '监控', 'monitor', '日志',
1310
- ];
1311
- // 如果只包含非开发关键词而不包含开发关键词,归为 other
1312
- const devKeywords = [
1313
- '实现', '开发', '编写', '创建', '构建', '添加', '修复',
1314
- 'implement', 'develop', 'build', 'create', 'add', 'fix',
1315
- '功能', 'feature', '模块', '组件', 'component', '系统',
1316
- ];
1317
- if (nonDevKeywords.some(kw => g.includes(kw)) && !devKeywords.some(kw => g.includes(kw))) {
1318
- return 'other';
1319
- }
1320
- // 默认为开发类
1321
- return 'development';
1322
- }
1323
630
  /**
1324
631
  * 检测顺序阶段目标(如 "Phase 1 基础架构"、"Phase 2 AI创作核心")
1325
632
  * 返回按阶段排序的索引列表
@@ -1351,7 +658,6 @@ ${typeConfig.runCommand}
1351
658
  if (phaseIndices.length < 2)
1352
659
  return; // 少于 2 个阶段,不需要建立依赖
1353
660
  // 收集每个阶段的开发任务 ID 和集成任务 ID
1354
- // 按阶段索引在 goals 中的原始位置分组
1355
661
  const phaseTaskMap = new Map();
1356
662
  for (let pi = 0; pi < phaseIndices.length; pi++) {
1357
663
  const goalIndex = phaseIndices[pi];
@@ -1366,14 +672,12 @@ ${typeConfig.runCommand}
1366
672
  }
1367
673
  // 集成任务标题包含 "系统集成"
1368
674
  if (b.title.startsWith('系统集成:') && devTaskIds.length > 0) {
1369
- // 集成任务依赖当前阶段的所有开发任务
1370
675
  const currentDeps = b.dependencies;
1371
676
  if (currentDeps.length === 0 || currentDeps.some(d => devTaskIds.includes(d))) {
1372
677
  integrationTaskId = b.taskId;
1373
678
  }
1374
679
  }
1375
680
  }
1376
- // 如果没有集成任务,使用最后一个开发任务 ID
1377
681
  phaseTaskMap.set(goalIndex, { devTaskIds, integrationTaskId });
1378
682
  }
1379
683
  // 建立跨阶段依赖:Phase N 的所有任务依赖 Phase N-1 的集成任务(或最后开发任务)
@@ -1383,18 +687,14 @@ ${typeConfig.runCommand}
1383
687
  const prevPhase = phaseTaskMap.get(prevGoalIndex);
1384
688
  if (!prevPhase || prevPhase.devTaskIds.length === 0)
1385
689
  continue;
1386
- // 前一阶段的核心依赖点(优先集成任务,其次最后开发任务)
1387
690
  const prevAnchor = prevPhase.integrationTaskId || prevPhase.devTaskIds[prevPhase.devTaskIds.length - 1];
1388
- // 找出当前阶段的所有开发任务和集成任务
1389
691
  for (const b of breakdowns) {
1390
692
  const isCurrentPhaseDev = b.title.startsWith('实现: ') &&
1391
693
  parsedTask.goals[currentGoalIndex] &&
1392
694
  b.description.includes(parsedTask.goals[currentGoalIndex]);
1393
695
  const isCurrentPhaseIntegration = b.title.startsWith('系统集成:');
1394
- // 简化判断:通过依赖关系推断——集成任务依赖当前阶段开发任务
1395
696
  const isCurrentPhaseTest = b.title.startsWith('测试: ') &&
1396
697
  b.dependencies.some(dep => {
1397
- // 测试任务依赖的开发任务属于当前阶段
1398
698
  const depTask = breakdowns.find(x => x.taskId === dep);
1399
699
  return depTask && depTask.title.startsWith('实现: ') &&
1400
700
  parsedTask.goals[currentGoalIndex] &&