openmatrix 0.2.31 → 0.2.33

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.
Files changed (48) hide show
  1. package/README.md +154 -154
  2. package/dist/cli/commands/approve.js +35 -1
  3. package/dist/cli/commands/auto.js +2 -2
  4. package/dist/cli/commands/check-gitignore.js +34 -30
  5. package/dist/cli/commands/check.js +1 -1
  6. package/dist/cli/commands/complete.js +35 -7
  7. package/dist/cli/commands/debug.js +2 -1
  8. package/dist/cli/commands/deploy.js +1 -1
  9. package/dist/cli/commands/install-skills.js +3 -0
  10. package/dist/cli/commands/meeting.js +37 -1
  11. package/dist/cli/commands/report.js +1 -1
  12. package/dist/cli/commands/resume.js +35 -1
  13. package/dist/cli/commands/retry.js +130 -56
  14. package/dist/cli/commands/start.js +1 -1
  15. package/dist/cli/commands/status.js +32 -29
  16. package/dist/cli/commands/step.js +4 -1
  17. package/dist/orchestrator/ai-reviewer.d.ts +5 -0
  18. package/dist/orchestrator/ai-reviewer.js +9 -2
  19. package/dist/orchestrator/context-collector.js +17 -5
  20. package/dist/orchestrator/executor.d.ts +8 -0
  21. package/dist/orchestrator/executor.js +24 -5
  22. package/dist/orchestrator/phase-executor.d.ts +4 -0
  23. package/dist/orchestrator/phase-executor.js +21 -4
  24. package/dist/storage/file-store.js +8 -0
  25. package/dist/storage/state-manager.js +52 -19
  26. package/dist/test/generator.js +113 -113
  27. package/dist/utils/error-handler.d.ts +18 -0
  28. package/dist/utils/error-handler.js +32 -0
  29. package/dist/utils/worktree-sync.js +24 -3
  30. package/package.json +61 -61
  31. package/skills/SKILL.md +53 -53
  32. package/skills/auto.md +410 -413
  33. package/skills/brainstorm.md +19 -12
  34. package/skills/debug.md +694 -691
  35. package/skills/deploy.md +658 -658
  36. package/skills/feature.md +713 -686
  37. package/skills/plan.md +298 -296
  38. package/skills/report.md +9 -5
  39. package/skills/resume.md +292 -287
  40. package/skills/start.md +32 -20
  41. package/skills/status.md +5 -4
  42. package/skills/test.md +875 -875
  43. package/dist/agents/base-agent.d.ts +0 -46
  44. package/dist/agents/base-agent.js +0 -17
  45. package/dist/cli/commands/analyze.d.ts +0 -2
  46. package/dist/cli/commands/analyze.js +0 -50
  47. package/dist/orchestrator/smart-question-analyzer.d.ts +0 -90
  48. package/dist/orchestrator/smart-question-analyzer.js +0 -512
@@ -51,21 +51,30 @@ class OrchestratorExecutor {
51
51
  getPhaseExecutor() {
52
52
  return this.phaseExecutor;
53
53
  }
54
+ /**
55
+ * 设置运行 ID(同时更新 PhaseExecutor 和 AIReviewer)
56
+ */
57
+ setRunId(runId) {
58
+ this.phaseExecutor.setRunId(runId);
59
+ this.aiReviewer.setRunId(runId);
60
+ }
54
61
  async loadConfigFromState(stateManager) {
55
62
  try {
56
63
  const state = await stateManager.getState();
57
64
  if (state.config?.taskTimeout) {
58
65
  this.config.taskTimeout = state.config.taskTimeout;
59
66
  }
60
- // 只有在 state.config 中有明确的 agentMode 时才更新并发数
61
- // 否则使用传入的 config.maxConcurrent
67
+ // agentMode 的优先级最高,明确指定时直接设置并发数
62
68
  if (state.config?.agentMode === 'single') {
63
69
  this.config.maxConcurrent = 1;
64
70
  }
65
- else if (state.config?.maxConcurrentAgents && state.config?.maxConcurrentAgents !== 3) {
66
- // 只有当 maxConcurrentAgents 不是默认值 3 时才更新
67
- this.config.maxConcurrent = state.config.maxConcurrentAgents;
71
+ else if (state.config?.agentMode === 'parallel') {
72
+ this.config.maxConcurrent = 3;
68
73
  }
74
+ // 注意:不再从 maxConcurrentAgents 覆盖,因为:
75
+ // 1. agentMode 已经明确了意图(single=1, parallel=3)
76
+ // 2. 构造函数传入的 maxConcurrent 用于测试等场景
77
+ // 3. CLI 通过 agentMode 而不是 maxConcurrentAgents 控制并发
69
78
  }
70
79
  catch {
71
80
  // use default config
@@ -516,14 +525,24 @@ class OrchestratorExecutor {
516
525
  }
517
526
  /**
518
527
  * 获取 AgentRunner 实例
528
+ *
529
+ * 注意:此方法假设 executor 已完成初始化(通过 step() 或其他方法触发)
519
530
  */
520
531
  getAgentRunner() {
532
+ if (!this.agentRunner) {
533
+ throw new Error('Executor not initialized. Call step() first or wait for initialization.');
534
+ }
521
535
  return this.agentRunner;
522
536
  }
523
537
  /**
524
538
  * 获取 Scheduler 实例
539
+ *
540
+ * 注意:此方法假设 executor 已完成初始化(通过 step() 或其他方法触发)
525
541
  */
526
542
  getScheduler() {
543
+ if (!this.scheduler) {
544
+ throw new Error('Executor not initialized. Call step() first or wait for initialization.');
545
+ }
527
546
  return this.scheduler;
528
547
  }
529
548
  // ============ Timeout Management ============
@@ -102,6 +102,10 @@ export declare class PhaseExecutor {
102
102
  * 设置 Run ID (用于提交信息)
103
103
  */
104
104
  setRunId(runId: string): void;
105
+ /**
106
+ * 获取当前 runId — 优先使用手动设置的值,否则从 stateManager 读取
107
+ */
108
+ private getRunId;
105
109
  /**
106
110
  * 设置质量配置
107
111
  */
@@ -70,6 +70,21 @@ class PhaseExecutor {
70
70
  setRunId(runId) {
71
71
  this.runId = runId;
72
72
  }
73
+ /**
74
+ * 获取当前 runId — 优先使用手动设置的值,否则从 stateManager 读取
75
+ */
76
+ async getRunId() {
77
+ if (this.runId)
78
+ return this.runId;
79
+ try {
80
+ const state = await this.stateManager.getState();
81
+ this.runId = state.runId;
82
+ }
83
+ catch {
84
+ // fallback
85
+ }
86
+ return this.runId;
87
+ }
73
88
  /**
74
89
  * 设置质量配置
75
90
  */
@@ -154,6 +169,8 @@ class PhaseExecutor {
154
169
  * 准备阶段执行的 Subagent 任务
155
170
  */
156
171
  async preparePhaseExecution(task) {
172
+ // 确保 runId 已初始化(避免生成 .openmatrix//tasks/ 错误路径)
173
+ await this.getRunId();
157
174
  const currentPhase = this.getCurrentPhase(task);
158
175
  switch (currentPhase) {
159
176
  case 'tdd':
@@ -386,7 +403,7 @@ ${task.acceptanceCriteria.map((c, i) => `${i + 1}. [ ] ${c}`).join('\n')}`);
386
403
  - 圈复杂度: < 10
387
404
 
388
405
  ## 输出要求
389
- 完成后,在 \`.openmatrix/tasks/${task.id}/artifacts/\` 目录下创建:
406
+ 完成后,在 \`.openmatrix/${this.runId}/tasks/${task.id}/artifacts/\` 目录下创建:
390
407
  - \`result.md\` - 实现说明
391
408
  - \`changes.txt\` - 变更文件列表
392
409
 
@@ -621,7 +638,7 @@ fi
621
638
 
622
639
  ## 📊 质量报告格式
623
640
 
624
- 在 \`.openmatrix/tasks/${task.id}/artifacts/\` 目录下创建 \`quality-report.json\`:
641
+ 在 \`.openmatrix/${this.runId}/tasks/${task.id}/artifacts/\` 目录下创建 \`quality-report.json\`:
625
642
 
626
643
  \`\`\`json
627
644
  {
@@ -762,7 +779,7 @@ ${task.acceptanceCriteria.map((c, i) => `${i + 1}. [ ] ${c}`).join('\n')}`);
762
779
  ## 验收流程
763
780
 
764
781
  ### 1. 检查验证阶段结果
765
- 读取 \`.openmatrix/tasks/${task.id}/artifacts/verify-report.md\`
782
+ 读取 \`.openmatrix/${this.runId}/tasks/${task.id}/artifacts/verify-report.md\`
766
783
  确认所有检查项已通过
767
784
 
768
785
  ### 2. 验证验收标准
@@ -821,7 +838,7 @@ fi
821
838
  - **确认代码能实际运行**(不是只编译通过)
822
839
 
823
840
  ## 输出要求
824
- 在 \`.openmatrix/tasks/${task.id}/artifacts/\` 目录下创建:
841
+ 在 \`.openmatrix/${this.runId}/tasks/${task.id}/artifacts/\` 目录下创建:
825
842
  - \`accept-report.md\` - 验收报告
826
843
 
827
844
  ## 结果格式
@@ -91,6 +91,10 @@ class FileStore {
91
91
  .map((f) => f.name);
92
92
  }
93
93
  catch (error) {
94
+ const nodeError = error;
95
+ if (nodeError.code === 'ENOENT' || nodeError.code === 'EISDIR') {
96
+ return [];
97
+ }
94
98
  (0, error_handler_js_1.logError)(error, { operation: 'listFiles', file: fullPath });
95
99
  return [];
96
100
  }
@@ -104,6 +108,10 @@ class FileStore {
104
108
  .map((f) => f.name);
105
109
  }
106
110
  catch (error) {
111
+ const nodeError = error;
112
+ if (nodeError.code === 'ENOENT' || nodeError.code === 'EISDIR') {
113
+ return [];
114
+ }
107
115
  (0, error_handler_js_1.logError)(error, { operation: 'listDirs', file: fullPath });
108
116
  return [];
109
117
  }
@@ -4,6 +4,7 @@ exports.StateManager = void 0;
4
4
  const file_store_js_1 = require("./file-store.js");
5
5
  const promises_1 = require("fs/promises");
6
6
  const path_1 = require("path");
7
+ const error_handler_js_1 = require("../utils/error-handler.js");
7
8
  const DEFAULT_CONFIG = {
8
9
  timeout: 120,
9
10
  taskTimeout: 600000, // 10 分钟(毫秒)
@@ -271,6 +272,10 @@ class StateManager {
271
272
  });
272
273
  }
273
274
  async getTask(taskId) {
275
+ // 验证 taskId 格式防止路径遍历
276
+ if (!(0, error_handler_js_1.validateId)(taskId, 'taskId')) {
277
+ return null;
278
+ }
274
279
  let task = await this.store.readJson(`tasks/${taskId}/task.json`);
275
280
  if (!task) {
276
281
  task = await this.store.readJson(`tasks/${taskId}.json`);
@@ -324,11 +329,13 @@ class StateManager {
324
329
  * 保存 Phase 结果到任务子目录
325
330
  */
326
331
  async savePhaseResult(taskId, phase, result) {
327
- await this.store.writeJson(`tasks/${taskId}/${phase}.json`, {
328
- taskId,
329
- phase,
330
- timestamp: new Date().toISOString(),
331
- ...result
332
+ await this.withFileLock(async () => {
333
+ await this.store.writeJson(`tasks/${taskId}/${phase}.json`, {
334
+ taskId,
335
+ phase,
336
+ timestamp: new Date().toISOString(),
337
+ ...result
338
+ });
332
339
  });
333
340
  }
334
341
  /**
@@ -341,7 +348,9 @@ class StateManager {
341
348
  * 保存 Agent 上下文到 context.md
342
349
  */
343
350
  async saveTaskContext(taskId, content) {
344
- await this.store.writeMarkdown(`tasks/${taskId}/context.md`, content);
351
+ await this.withFileLock(async () => {
352
+ await this.store.writeMarkdown(`tasks/${taskId}/context.md`, content);
353
+ });
345
354
  }
346
355
  /**
347
356
  * 读取 Agent 上下文
@@ -420,13 +429,21 @@ class StateManager {
420
429
  }
421
430
  // ============ Approval Methods ============
422
431
  async saveApproval(approval) {
423
- await this.store.writeJson(`approvals/${approval.id}.json`, approval);
432
+ await this.withFileLock(async () => {
433
+ await this.store.writeJson(`approvals/${approval.id}.json`, approval);
434
+ });
424
435
  }
425
436
  async getApproval(approvalId) {
437
+ // 验证 approvalId 格式防止路径遍历
438
+ if (!(0, error_handler_js_1.validateId)(approvalId, 'approvalId')) {
439
+ return null;
440
+ }
426
441
  return await this.store.readJson(`approvals/${approvalId}.json`);
427
442
  }
428
443
  async updateApproval(approval) {
429
- await this.store.writeJson(`approvals/${approval.id}.json`, approval);
444
+ await this.withFileLock(async () => {
445
+ await this.store.writeJson(`approvals/${approval.id}.json`, approval);
446
+ });
430
447
  }
431
448
  async getApprovalsByStatus(status) {
432
449
  const files = await this.store.listFiles('approvals');
@@ -498,8 +515,10 @@ class StateManager {
498
515
  * 路径: .openmatrix/{runId}/research/session.json
499
516
  */
500
517
  async saveResearchSession(session) {
501
- await this.store.ensureDir('research');
502
- await this.store.writeJson('research/session.json', session);
518
+ await this.withFileLock(async () => {
519
+ await this.store.ensureDir('research');
520
+ await this.store.writeJson('research/session.json', session);
521
+ });
503
522
  }
504
523
  /**
505
524
  * 读取研究会话
@@ -511,8 +530,10 @@ class StateManager {
511
530
  * 保存研究报告
512
531
  */
513
532
  async saveResearchReport(content) {
514
- await this.store.ensureDir('research');
515
- await this.store.writeMarkdown('research/RESEARCH.md', content);
533
+ await this.withFileLock(async () => {
534
+ await this.store.ensureDir('research');
535
+ await this.store.writeMarkdown('research/RESEARCH.md', content);
536
+ });
516
537
  }
517
538
  /**
518
539
  * 读取研究报告
@@ -524,8 +545,10 @@ class StateManager {
524
545
  * 保存研究上下文 (供 start 使用)
525
546
  */
526
547
  async saveResearchContext(context) {
527
- await this.store.ensureDir('research');
528
- await this.store.writeJson('research/context.json', context);
548
+ await this.withFileLock(async () => {
549
+ await this.store.ensureDir('research');
550
+ await this.store.writeJson('research/context.json', context);
551
+ });
529
552
  }
530
553
  /**
531
554
  * 读取研究上下文
@@ -543,16 +566,20 @@ class StateManager {
543
566
  * 保存知识条目
544
567
  */
545
568
  async saveKnowledgeFinding(index, content) {
546
- await this.store.ensureDir('research/knowledge');
547
- await this.store.writeMarkdown(`research/knowledge/finding-${index}.md`, content);
569
+ await this.withFileLock(async () => {
570
+ await this.store.ensureDir('research/knowledge');
571
+ await this.store.writeMarkdown(`research/knowledge/finding-${index}.md`, content);
572
+ });
548
573
  }
549
574
  // ============ Brainstorm Methods ============
550
575
  /**
551
576
  * 保存头脑风暴会话
552
577
  */
553
578
  async saveBrainstormSession(session) {
554
- await this.store.ensureDir('brainstorm');
555
- await this.store.writeJson('brainstorm/session.json', session);
579
+ await this.withFileLock(async () => {
580
+ await this.store.ensureDir('brainstorm');
581
+ await this.store.writeJson('brainstorm/session.json', session);
582
+ });
556
583
  }
557
584
  /**
558
585
  * 读取头脑风暴会话
@@ -613,9 +640,15 @@ class StateManager {
613
640
  }
614
641
  // ============ Meeting Methods ============
615
642
  async saveMeeting(meeting) {
616
- await this.store.writeJson(`meetings/${meeting.id}.json`, meeting);
643
+ await this.withFileLock(async () => {
644
+ await this.store.writeJson(`meetings/${meeting.id}.json`, meeting);
645
+ });
617
646
  }
618
647
  async getMeeting(meetingId) {
648
+ // 验证 meetingId 格式防止路径遍历
649
+ if (!(0, error_handler_js_1.validateId)(meetingId, 'meetingId')) {
650
+ return null;
651
+ }
619
652
  return await this.store.readJson(`meetings/${meetingId}.json`);
620
653
  }
621
654
  async getMeetingsByStatus(status) {