openmatrix 0.1.62 → 0.1.64

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.
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AgentRunner = void 0;
37
- const fs = __importStar(require("fs"));
37
+ const fs = __importStar(require("fs/promises"));
38
38
  const path = __importStar(require("path"));
39
39
  /**
40
40
  * AgentRunner - 使用 Subagent 执行任务
@@ -57,7 +57,7 @@ class AgentRunner {
57
57
  this.approvalManager = approvalManager;
58
58
  this.config = {
59
59
  maxConcurrent: 3,
60
- taskTimeout: 120000,
60
+ taskTimeout: 600000, // 10 分钟
61
61
  ...config
62
62
  };
63
63
  }
@@ -177,23 +177,32 @@ ${agentPrompt.instructions}
177
177
  *
178
178
  * 读取所有已完成任务的 context.md,为当前 Agent 提供前序 Agent 的决策和知识
179
179
  */
180
- buildAccumulatedContext(currentTask) {
180
+ async buildAccumulatedContext(currentTask) {
181
181
  const omPath = path.join(process.cwd(), '.openmatrix');
182
182
  const tasksDir = path.join(omPath, 'tasks');
183
183
  try {
184
- if (!fs.existsSync(tasksDir))
184
+ // 异步检查目录是否存在
185
+ try {
186
+ await fs.access(tasksDir);
187
+ }
188
+ catch {
185
189
  return '';
190
+ }
186
191
  const contextParts = [];
187
- // 读取所有已完成任务的 context.md
188
- const taskDirs = fs.readdirSync(tasksDir).filter(name => name.startsWith('TASK-'));
192
+ // 异步读取目录
193
+ const taskDirs = (await fs.readdir(tasksDir)).filter(name => name.startsWith('TASK-'));
189
194
  for (const taskId of taskDirs) {
190
195
  const contextFile = path.join(tasksDir, taskId, 'context.md');
191
- if (fs.existsSync(contextFile)) {
192
- const content = fs.readFileSync(contextFile, 'utf-8').trim();
193
- if (content) {
194
- contextParts.push(`### ${taskId}\n${content}`);
196
+ try {
197
+ const content = await fs.readFile(contextFile, 'utf-8');
198
+ const trimmed = content.trim();
199
+ if (trimmed) {
200
+ contextParts.push(`### ${taskId}\n${trimmed}`);
195
201
  }
196
202
  }
203
+ catch {
204
+ // 文件不存在或读取失败,跳过
205
+ }
197
206
  }
198
207
  if (contextParts.length === 0)
199
208
  return '';
@@ -389,30 +389,26 @@ function generateBrainstormQuestions(taskContent, taskTitle) {
389
389
  question: '选择执行模式(控制 AI 执行过程中的审批节点)',
390
390
  header: '执行模式',
391
391
  options: [
392
- { label: 'auto (推荐)', description: '全自动执行,无需人工审批,遇到阻塞自动 Meeting' },
392
+ { label: '全自动执行 (推荐)', description: '全自动执行,无需人工审批,遇到阻塞自动 Meeting' },
393
393
  { label: 'confirm-key', description: '关键节点审批(计划、合并、部署)' },
394
394
  { label: 'confirm-all', description: '每个阶段都需人工确认' }
395
395
  ],
396
396
  multiSelect: false,
397
397
  why: '执行模式决定自动化程度和人工干预频率'
398
398
  });
399
- // 问题 10: E2E 测试(Web/Mobile/GUI 项目)
400
- if (content.includes('web') || content.includes('前端') || content.includes('页面') ||
401
- content.includes('网站') || content.includes('app') || content.includes('应用') ||
402
- content.includes('游戏') || content.includes('管理') || content.includes('后台') ||
403
- content.includes('ui') || content.includes('mobile') || content.includes('mobile')) {
404
- questions.push({
405
- id: 'e2e_tests',
406
- question: '是否启用端到端 (E2E) 测试?(适用于 Web/Mobile/GUI 项目,耗时较长)',
407
- header: 'E2E 测试',
408
- options: [
409
- { label: '启用 E2E 测试', description: '使用 Playwright/Cypress 等框架进行端到端测试' },
410
- { label: '不启用 (推荐)', description: '仅进行单元测试和集成测试,节省时间' }
411
- ],
412
- multiSelect: false,
413
- why: 'E2E 测试能验证完整用户流程,但增加执行时间'
414
- });
415
- }
399
+ // 问题 10: E2E 测试(必填)
400
+ // E2E 测试是必问问题,由用户在问答中选择是否启用
401
+ questions.push({
402
+ id: 'e2e_tests',
403
+ question: '是否启用端到端 (E2E) 测试?(适用于 Web/Mobile/GUI 项目,耗时较长)',
404
+ header: 'E2E 测试',
405
+ options: [
406
+ { label: '启用 E2E 测试', description: '使用 Playwright/Cypress 等框架进行端到端测试' },
407
+ { label: '不启用 (推荐)', description: '仅进行单元测试和集成测试,节省时间' }
408
+ ],
409
+ multiSelect: false,
410
+ why: 'E2E 测试能验证完整用户流程,但增加执行时间'
411
+ });
416
412
  return questions;
417
413
  }
418
414
  /**
@@ -449,6 +445,9 @@ async function generateSmartQuestions(taskContent, basePath) {
449
445
  multiSelect: q.type === 'multiple',
450
446
  why: ''
451
447
  }));
448
+ // 7. 追加领域分析问题(底层逻辑思考)
449
+ const domainQuestions = generateDomainAnalysisQuestions(taskContent);
450
+ questions.push(...domainQuestions);
452
451
  return questions;
453
452
  }
454
453
  catch (error) {
@@ -457,3 +456,121 @@ async function generateSmartQuestions(taskContent, basePath) {
457
456
  return generateBrainstormQuestions(taskContent, '');
458
457
  }
459
458
  }
459
+ /**
460
+ * 领域分析问题 — 底层逻辑思考
461
+ *
462
+ * 从任务描述中提取:
463
+ * 1. 核心领域实体及其关系
464
+ * 2. 数据流转路径
465
+ * 3. 关键不变量/约束
466
+ * 4. 核心用户场景链路
467
+ */
468
+ function generateDomainAnalysisQuestions(taskContent) {
469
+ const questions = [];
470
+ const content = taskContent.toLowerCase();
471
+ // ===== 问题 1: 领域实体建模 =====
472
+ const entityHints = extractEntities(taskContent);
473
+ if (entityHints.length > 0) {
474
+ questions.push({
475
+ id: 'domain_entities',
476
+ question: `从任务描述中识别到以下核心实体,请确认或补充:\n${entityHints.map(e => ` • ${e}`).join('\n')}`,
477
+ header: '领域实体',
478
+ options: [
479
+ { label: '以上实体正确', description: '已覆盖核心领域实体,无需补充' },
480
+ { label: '需要补充实体', description: '还有重要实体未被识别,我会在"其他"中补充' },
481
+ { label: '需要调整实体', description: '部分实体不准确,需要修正' }
482
+ ],
483
+ multiSelect: false,
484
+ why: '明确领域实体是设计数据模型和 API 的基础'
485
+ });
486
+ }
487
+ else {
488
+ questions.push({
489
+ id: 'domain_entities',
490
+ question: '这个系统涉及哪些核心领域实体?它们之间是什么关系?(如 用户-订单-商品)',
491
+ header: '领域实体',
492
+ options: [
493
+ { label: '单一实体', description: '系统围绕一个核心实体(如用户管理)' },
494
+ { label: '2-3 个实体', description: '少量实体间有简单关系(如用户-文章)' },
495
+ { label: '多实体复杂关系', description: '多个实体间有多对多等复杂关系' },
496
+ { label: '我来说明', description: '在"其他"中描述具体实体' }
497
+ ],
498
+ multiSelect: false,
499
+ why: '明确领域实体是设计数据模型和 API 的基础'
500
+ });
501
+ }
502
+ // ===== 问题 2: 数据流分析 =====
503
+ questions.push({
504
+ id: 'data_flow',
505
+ question: '数据在系统中如何流转?(从输入到存储到输出)',
506
+ header: '数据流',
507
+ options: [
508
+ { label: '用户输入 → 处理 → 存储 → 展示', description: '经典 CRUD 流程(如后台管理、博客)' },
509
+ { label: '外部 API → 转换 → 存储 → 查询', description: '数据采集/聚合类系统' },
510
+ { label: '事件触发 → 处理 → 通知/存储', description: '事件驱动型(如消息队列、Webhook)' },
511
+ { label: '实时流处理', description: '数据持续流入,实时处理(如监控、聊天)' },
512
+ { label: '我来说明', description: '在"其他"中描述具体数据流' }
513
+ ],
514
+ multiSelect: false,
515
+ why: '数据流决定架构选型(请求驱动 vs 事件驱动 vs 流处理)'
516
+ });
517
+ // ===== 问题 3: 不变量/约束 =====
518
+ questions.push({
519
+ id: 'invariants',
520
+ question: '系统中存在哪些关键不变量或业务约束?(什么条件必须永远成立)',
521
+ header: '不变量',
522
+ options: [
523
+ { label: '数据一致性', description: '如:余额不能为负、库存不能超卖、状态只能单向流转' },
524
+ { label: '权限控制', description: '如:用户只能操作自己的数据、管理员才能访问后台' },
525
+ { label: '唯一性约束', description: '如:用户名唯一、订单号不重复、同一时间只能有一个活跃会话' },
526
+ { label: '无明显约束', description: '纯展示或简单计算,无严格不变量' }
527
+ ],
528
+ multiSelect: true,
529
+ why: '不变量决定了哪些地方需要加锁、事务、校验和防御性编程'
530
+ });
531
+ // ===== 问题 4: 核心场景链路 =====
532
+ questions.push({
533
+ id: 'core_scenarios',
534
+ question: '从用户视角,核心操作链路是什么?(用户会走过的关键路径)',
535
+ header: '场景链路',
536
+ options: [
537
+ { label: '注册→登录→使用→退出', description: '典型用户系统链路' },
538
+ { label: '浏览→选择→下单→支付→确认', description: '电商/交易类链路' },
539
+ { label: '创建→编辑→预览→发布', description: '内容管理类链路' },
540
+ { label: '输入→处理→查看结果', description: '工具/计算类链路' },
541
+ { label: '我来说明', description: '在"其他"中描述具体场景链路' }
542
+ ],
543
+ multiSelect: true,
544
+ why: '核心场景链路决定了 MVP 的功能范围和优先级排序'
545
+ });
546
+ return questions;
547
+ }
548
+ /**
549
+ * 从任务描述中提取可能的领域实体
550
+ */
551
+ function extractEntities(taskContent) {
552
+ const entities = [];
553
+ // 中文常见领域实体
554
+ const zhPatterns = [
555
+ { pattern: /用户|账号|账户|注册|登录|权限|角色/gi, entity: '用户 (User)' },
556
+ { pattern: /订单|交易|购买|支付|退款|结算/gi, entity: '订单 (Order)' },
557
+ { pattern: /商品|产品|物品|库存|上架|下架/gi, entity: '商品 (Product)' },
558
+ { pattern: /文章|帖子|内容|评论|标签|分类/gi, entity: '内容 (Content)' },
559
+ { pattern: /任务|工单|项目|里程碑|迭代/gi, entity: '任务 (Task)' },
560
+ { pattern: /消息|通知|推送|邮件|短信/gi, entity: '消息 (Message)' },
561
+ { pattern: /文件|附件|图片|视频|媒体|上传/gi, entity: '文件 (File)' },
562
+ { pattern: /配置|设置|参数|选项|规则/gi, entity: '配置 (Config)' },
563
+ { pattern: /日志|记录|审计|追踪|监控/gi, entity: '日志 (Log)' },
564
+ { pattern: /数据|报表|统计|分析|仪表盘|dashboard/gi, entity: '数据 (Data)' },
565
+ { pattern: /API|接口|端点|路由|endpoint/gi, entity: 'API' },
566
+ { pattern: /数据库|存储|缓存|表|collection/gi, entity: '存储 (Storage)' },
567
+ ];
568
+ const seen = new Set();
569
+ for (const { pattern, entity } of zhPatterns) {
570
+ if (pattern.test(taskContent) && !seen.has(entity)) {
571
+ entities.push(entity);
572
+ seen.add(entity);
573
+ }
574
+ }
575
+ return entities;
576
+ }
@@ -38,6 +38,7 @@ exports.completeCommand = void 0;
38
38
  const commander_1 = require("commander");
39
39
  const state_manager_js_1 = require("../../storage/state-manager.js");
40
40
  const git_commit_manager_js_1 = require("../../orchestrator/git-commit-manager.js");
41
+ const fs = __importStar(require("fs/promises"));
41
42
  const path = __importStar(require("path"));
42
43
  exports.completeCommand = new commander_1.Command('complete')
43
44
  .description('标记任务完成并更新全局统计')
@@ -84,7 +85,13 @@ exports.completeCommand = new commander_1.Command('complete')
84
85
  completed: allTasks.filter(t => t.status === 'completed').length,
85
86
  inProgress: allTasks.filter(t => t.status === 'in_progress').length,
86
87
  failed: allTasks.filter(t => t.status === 'failed').length,
87
- pending: allTasks.filter(t => t.status === 'pending' || t.status === 'scheduled').length
88
+ pending: allTasks.filter(t => t.status === 'pending').length,
89
+ scheduled: allTasks.filter(t => t.status === 'scheduled').length,
90
+ blocked: allTasks.filter(t => t.status === 'blocked').length,
91
+ waiting: allTasks.filter(t => t.status === 'waiting').length,
92
+ verify: allTasks.filter(t => t.status === 'verify').length,
93
+ accept: allTasks.filter(t => t.status === 'accept').length,
94
+ retry_queue: allTasks.filter(t => t.status === 'retry_queue').length
88
95
  };
89
96
  // 4. 更新全局状态
90
97
  const allDone = stats.completed + stats.failed === stats.totalTasks;
@@ -94,7 +101,33 @@ exports.completeCommand = new commander_1.Command('complete')
94
101
  currentPhase: allDone ? 'completed' : 'execution',
95
102
  ...(allDone ? { completedAt: now } : {})
96
103
  });
97
- // 5. Git 自动提交
104
+ // 5. 写入 context.md (Agent Memory)
105
+ if (isSuccess) {
106
+ const taskDir = path.join(omPath, 'tasks', taskId);
107
+ const contextFile = path.join(taskDir, 'context.md');
108
+ try {
109
+ await fs.mkdir(taskDir, { recursive: true });
110
+ const contextContent = `## 任务:${task.id} ${task.title}
111
+
112
+ ### 关键决策
113
+ - [待补充]
114
+
115
+ ### 创建/修改的文件
116
+ - [待补充]
117
+
118
+ ### 重要发现
119
+ - [待补充]
120
+
121
+ ### 对后续任务的建议
122
+ - [待补充]
123
+ `;
124
+ await fs.writeFile(contextFile, contextContent, 'utf-8');
125
+ }
126
+ catch {
127
+ // 忽略写入错误
128
+ }
129
+ }
130
+ // 6. Git 自动提交
98
131
  if (isSuccess) {
99
132
  const state = await stateManager.getState();
100
133
  const gitManager = new git_commit_manager_js_1.GitCommitManager(basePath);
@@ -106,7 +106,7 @@ exports.startCommand = new commander_1.Command('start')
106
106
  // 路径 A: AI 已拆分 (--tasks-json)
107
107
  // ============================
108
108
  if (options.tasksJson) {
109
- await handleTasksJson(options, stateManager, state, omPath);
109
+ await handleTasksJson(options, stateManager, state, omPath, basePath);
110
110
  return;
111
111
  }
112
112
  // ============================
@@ -118,13 +118,16 @@ exports.startCommand = new commander_1.Command('start')
118
118
  * 处理 AI 解析的任务 (--tasks-json)
119
119
  * AI 提供 ParsedTask 格式的结构化数据,仍由 TaskPlanner 做拆分
120
120
  */
121
- async function handleTasksJson(options, stateManager, state, omPath) {
121
+ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
122
122
  let tasksInput;
123
123
  try {
124
124
  // 支持 @file 语法读取文件
125
125
  let jsonStr = options.tasksJson;
126
126
  if (jsonStr.startsWith('@')) {
127
- jsonStr = await fs.readFile(jsonStr.slice(1), 'utf-8');
127
+ const filePath = jsonStr.slice(1);
128
+ // 如果是相对路径,转换为绝对路径
129
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(basePath, filePath);
130
+ jsonStr = await fs.readFile(resolvedPath, 'utf-8');
128
131
  }
129
132
  tasksInput = JSON.parse(jsonStr);
130
133
  }
@@ -68,7 +68,13 @@ exports.stepCommand = new commander_1.Command('step')
68
68
  completed: completed,
69
69
  inProgress: allTasks.filter(t => t.status === 'in_progress').length,
70
70
  failed: allTasks.filter(t => t.status === 'failed').length,
71
- pending: allTasks.filter(t => t.status === 'pending').length
71
+ pending: allTasks.filter(t => t.status === 'pending').length,
72
+ scheduled: allTasks.filter(t => t.status === 'scheduled').length,
73
+ blocked: allTasks.filter(t => t.status === 'blocked').length,
74
+ waiting: allTasks.filter(t => t.status === 'waiting').length,
75
+ verify: allTasks.filter(t => t.status === 'verify').length,
76
+ accept: allTasks.filter(t => t.status === 'accept').length,
77
+ retry_queue: allTasks.filter(t => t.status === 'retry_queue').length
72
78
  };
73
79
  const allDone = completed + stats.failed === total;
74
80
  await stateManager.updateState({
@@ -39,7 +39,9 @@ export declare class OrchestratorExecutor {
39
39
  private approvalManager;
40
40
  private stateMachine;
41
41
  private phaseExecutor;
42
+ private retryManager;
42
43
  private config;
44
+ private taskTimers;
43
45
  constructor(stateManager: StateManager, approvalManager: ApprovalManager, config?: Partial<ExecutorConfig>);
44
46
  /**
45
47
  * 获取 PhaseExecutor 实例
@@ -112,4 +114,21 @@ export declare class OrchestratorExecutor {
112
114
  * 获取 Scheduler 实例
113
115
  */
114
116
  getScheduler(): Scheduler;
117
+ /**
118
+ * 设置任务超时计时器
119
+ */
120
+ private setupTaskTimeout;
121
+ /**
122
+ * 清除任务超时计时器
123
+ */
124
+ private clearTaskTimeout;
125
+ /**
126
+ * 处理失败任务的重试
127
+ * @returns 成功加入重试队列的任务数量
128
+ */
129
+ private processRetries;
130
+ /**
131
+ * 销毁执行器,清理所有资源
132
+ */
133
+ destroy(): void;
115
134
  }
@@ -6,6 +6,7 @@ const scheduler_js_1 = require("./scheduler.js");
6
6
  const agent_runner_js_1 = require("../agents/agent-runner.js");
7
7
  const state_machine_js_1 = require("./state-machine.js");
8
8
  const phase_executor_js_1 = require("./phase-executor.js");
9
+ const retry_manager_js_1 = require("./retry-manager.js");
9
10
  /**
10
11
  * OrchestratorExecutor - 执行循环核心
11
12
  *
@@ -18,13 +19,15 @@ class OrchestratorExecutor {
18
19
  approvalManager;
19
20
  stateMachine;
20
21
  phaseExecutor;
22
+ retryManager;
21
23
  config;
24
+ taskTimers = new Map();
22
25
  constructor(stateManager, approvalManager, config) {
23
26
  this.stateManager = stateManager;
24
27
  this.approvalManager = approvalManager;
25
28
  this.config = {
26
29
  maxConcurrent: 3,
27
- taskTimeout: 120000,
30
+ taskTimeout: 600000, // 10 分钟
28
31
  ...config
29
32
  };
30
33
  this.scheduler = new scheduler_js_1.Scheduler(stateManager, {
@@ -37,6 +40,7 @@ class OrchestratorExecutor {
37
40
  });
38
41
  this.stateMachine = new state_machine_js_1.StateMachine();
39
42
  this.phaseExecutor = new phase_executor_js_1.PhaseExecutor(stateManager, approvalManager);
43
+ this.retryManager = new retry_manager_js_1.RetryManager();
40
44
  }
41
45
  /**
42
46
  * 获取 PhaseExecutor 实例
@@ -64,6 +68,18 @@ class OrchestratorExecutor {
64
68
  // 4. 检查是否有失败任务需要重试
65
69
  const failedTasks = allTasks.filter(t => t.status === 'failed');
66
70
  if (failedTasks.length > 0) {
71
+ // 尝试自动重试
72
+ const retried = await this.processRetries(failedTasks);
73
+ if (retried > 0) {
74
+ // 有任务被重新放入队列,继续执行循环
75
+ return {
76
+ status: 'continue',
77
+ subagentTasks: [],
78
+ message: `${retried} 个失败任务已加入重试队列`,
79
+ statistics: this.getStatistics(state)
80
+ };
81
+ }
82
+ // 所有重试次数已耗尽
67
83
  return this.createRetryNeededResult(failedTasks, state);
68
84
  }
69
85
  // 5. 检查是否有阻塞任务
@@ -85,9 +101,10 @@ class OrchestratorExecutor {
85
101
  }
86
102
  // 10. 准备 Subagent 任务
87
103
  const subagentTasks = await this.agentRunner.prepareSubagentTasks(executableTasks);
88
- // 8. 更新任务状态为 scheduled
104
+ // 8. 更新任务状态为 scheduled 并设置超时
89
105
  for (const task of executableTasks) {
90
106
  await this.scheduler.markTaskStarted(task.id);
107
+ this.setupTaskTimeout(task.id);
91
108
  }
92
109
  return {
93
110
  status: 'continue',
@@ -241,6 +258,8 @@ class OrchestratorExecutor {
241
258
  throw new Error(`Task ${taskId} not found`);
242
259
  }
243
260
  if (result.success) {
261
+ // 清除超时计时器
262
+ this.clearTaskTimeout(taskId);
244
263
  // 更新阶段状态
245
264
  const currentPhase = this.getCurrentPhase(task);
246
265
  const updatedPhases = { ...task.phases };
@@ -281,6 +300,7 @@ class OrchestratorExecutor {
281
300
  }
282
301
  }
283
302
  else {
303
+ this.clearTaskTimeout(taskId);
284
304
  await this.scheduler.markTaskFailed(taskId, result.error || 'Unknown error');
285
305
  }
286
306
  }
@@ -321,5 +341,60 @@ class OrchestratorExecutor {
321
341
  getScheduler() {
322
342
  return this.scheduler;
323
343
  }
344
+ // ============ Timeout Management ============
345
+ /**
346
+ * 设置任务超时计时器
347
+ */
348
+ setupTaskTimeout(taskId) {
349
+ const timer = setTimeout(async () => {
350
+ console.error(`⏰ 任务超时: ${taskId} (${this.config.taskTimeout / 1000}s)`);
351
+ this.taskTimers.delete(taskId);
352
+ await this.scheduler.markTaskFailed(taskId, `Task timed out after ${this.config.taskTimeout / 1000}s`);
353
+ }, this.config.taskTimeout);
354
+ this.taskTimers.set(taskId, timer);
355
+ }
356
+ /**
357
+ * 清除任务超时计时器
358
+ */
359
+ clearTaskTimeout(taskId) {
360
+ const timer = this.taskTimers.get(taskId);
361
+ if (timer) {
362
+ clearTimeout(timer);
363
+ this.taskTimers.delete(taskId);
364
+ }
365
+ }
366
+ // ============ Retry Management ============
367
+ /**
368
+ * 处理失败任务的重试
369
+ * @returns 成功加入重试队列的任务数量
370
+ */
371
+ async processRetries(failedTasks) {
372
+ let retried = 0;
373
+ for (const task of failedTasks) {
374
+ this.retryManager.addToQueue(task.id, task.error || 'Unknown error');
375
+ if (this.retryManager.shouldRetry(task.id)) {
376
+ // 将任务从 failed 转为 retry_queue 再转为 pending
377
+ await this.stateManager.updateTask(task.id, {
378
+ status: 'retry_queue',
379
+ retryCount: (task.retryCount || 0) + 1
380
+ });
381
+ await this.stateManager.updateTask(task.id, {
382
+ status: 'pending'
383
+ });
384
+ this.retryManager.removeFromQueue(task.id);
385
+ retried++;
386
+ }
387
+ }
388
+ return retried;
389
+ }
390
+ /**
391
+ * 销毁执行器,清理所有资源
392
+ */
393
+ destroy() {
394
+ for (const timer of this.taskTimers.values()) {
395
+ clearTimeout(timer);
396
+ }
397
+ this.taskTimers.clear();
398
+ }
324
399
  }
325
400
  exports.OrchestratorExecutor = OrchestratorExecutor;
@@ -244,8 +244,18 @@ class GitCommitManager {
244
244
  };
245
245
  // 生成提交信息
246
246
  const commitMessage = this.generateCommitMessage(fullInfo);
247
- // 添加所有文件
248
- await execAsync('git add -A', { cwd: this.repoPath });
247
+ // 添加文件 - 使用 git add . 而不是 git add -A
248
+ // git add . 只添加当前目录及子目录的文件,不会添加上级目录的文件
249
+ // 同时通过 .gitignore 排除不需要的文件
250
+ await execAsync('git add .', { cwd: this.repoPath });
251
+ // 检查是否有文件被暂存(避免空提交)
252
+ const { stdout: staged } = await execAsync('git diff --cached --name-only', { cwd: this.repoPath });
253
+ if (!staged.trim()) {
254
+ return {
255
+ success: false,
256
+ message: 'No files to commit (all changes ignored or no changes)'
257
+ };
258
+ }
249
259
  // 使用临时文件传递 commit message(避免 Windows 下多行消息转义问题)
250
260
  const tmpFile = path.join(this.repoPath, '.git', 'COMMIT_MSG_TMP');
251
261
  await fs.writeFile(tmpFile, commitMessage, 'utf-8');
@@ -192,6 +192,9 @@ export declare class PhaseExecutor {
192
192
  parseBuildTestResult(output: string): BuildTestResult;
193
193
  /**
194
194
  * 解析质量报告
195
+ *
196
+ * 策略:优先解析 JSON 格式,失败时使用正则表达式回退
197
+ * 支持多种测试框架输出格式(Vitest, Jest, Mocha, Tape)
195
198
  */
196
199
  parseQualityReport(output: string): QualityGateResult;
197
200
  /**