openmatrix 0.1.63 → 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
  /**
@@ -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({
@@ -27,7 +27,7 @@ class OrchestratorExecutor {
27
27
  this.approvalManager = approvalManager;
28
28
  this.config = {
29
29
  maxConcurrent: 3,
30
- taskTimeout: 120000,
30
+ taskTimeout: 600000, // 10 分钟
31
31
  ...config
32
32
  };
33
33
  this.scheduler = new scheduler_js_1.Scheduler(stateManager, {
@@ -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
  /**