openmatrix 0.2.25 → 0.2.27

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.
@@ -13,11 +13,16 @@ const DEFAULT_CONFIG = {
13
13
  model: 'claude-sonnet-4-6'
14
14
  };
15
15
  class StateManager {
16
+ rootStore;
16
17
  store;
18
+ basePath;
17
19
  stateCache = null;
18
- lockDepth = 0; // 可重入:同一进程内嵌套调用不阻塞
20
+ lockDepth = 0;
19
21
  constructor(basePath) {
20
- this.store = new file_store_js_1.FileStore(basePath);
22
+ this.basePath = basePath;
23
+ this.rootStore = new file_store_js_1.FileStore(basePath);
24
+ // store will be set in initialize() or reset() to point to {basePath}/{runId}/
25
+ this.store = this.rootStore;
21
26
  }
22
27
  /**
23
28
  * 跨进程文件锁 — 防止多个 openmatrix CLI 进程同时读写 state.json
@@ -84,49 +89,108 @@ class StateManager {
84
89
  }
85
90
  }
86
91
  async initialize() {
87
- const existing = await this.store.readJson('state.json');
88
- if (!existing) {
89
- const initialState = {
90
- version: '1.0',
91
- runId: this.generateRunId(),
92
- status: 'initialized',
93
- currentPhase: 'planning',
94
- startedAt: new Date().toISOString(),
95
- config: DEFAULT_CONFIG,
96
- statistics: {
97
- totalTasks: 0,
98
- completed: 0,
99
- inProgress: 0,
100
- failed: 0,
101
- pending: 0,
102
- scheduled: 0,
103
- blocked: 0,
104
- waiting: 0,
105
- verify: 0,
106
- accept: 0,
107
- retry_queue: 0
108
- }
109
- };
110
- await this.store.writeJson('state.json', initialState);
111
- this.stateCache = initialState;
92
+ // 读取根索引,确定当前 runId
93
+ const current = await this.rootStore.readJson('current.json');
94
+ if (current?.runId) {
95
+ // 切换 store 到当前运行目录
96
+ this.store = new file_store_js_1.FileStore((0, path_1.join)(this.basePath, current.runId));
97
+ const existing = await this.store.readJson('state.json');
98
+ if (existing) {
99
+ existing.statistics = {
100
+ totalTasks: existing.statistics?.totalTasks ?? 0,
101
+ completed: existing.statistics?.completed ?? 0,
102
+ inProgress: existing.statistics?.inProgress ?? 0,
103
+ failed: existing.statistics?.failed ?? 0,
104
+ pending: existing.statistics?.pending ?? 0,
105
+ scheduled: existing.statistics?.scheduled ?? 0,
106
+ blocked: existing.statistics?.blocked ?? 0,
107
+ waiting: existing.statistics?.waiting ?? 0,
108
+ verify: existing.statistics?.verify ?? 0,
109
+ accept: existing.statistics?.accept ?? 0,
110
+ retry_queue: existing.statistics?.retry_queue ?? 0
111
+ };
112
+ this.stateCache = existing;
113
+ // 迁移旧版本文件到 runId 目录
114
+ await this.migrateFromLegacy();
115
+ return;
116
+ }
112
117
  }
113
- else {
114
- // 合并旧状态的统计字段(兼容旧版本,statistics 可能不存在)
115
- existing.statistics = {
116
- totalTasks: existing.statistics?.totalTasks ?? 0,
117
- completed: existing.statistics?.completed ?? 0,
118
- inProgress: existing.statistics?.inProgress ?? 0,
119
- failed: existing.statistics?.failed ?? 0,
120
- pending: existing.statistics?.pending ?? 0,
121
- scheduled: existing.statistics?.scheduled ?? 0,
122
- blocked: existing.statistics?.blocked ?? 0,
123
- waiting: existing.statistics?.waiting ?? 0,
124
- verify: existing.statistics?.verify ?? 0,
125
- accept: existing.statistics?.accept ?? 0,
126
- retry_queue: existing.statistics?.retry_queue ?? 0
127
- };
128
- this.stateCache = existing;
118
+ // 没有 current.json 或 state.json 不存在 — 创建新运行
119
+ const runId = this.generateRunId();
120
+ this.store = new file_store_js_1.FileStore((0, path_1.join)(this.basePath, runId));
121
+ await this.rootStore.writeJson('current.json', { runId });
122
+ const initialState = {
123
+ version: '1.0',
124
+ runId,
125
+ status: 'initialized',
126
+ currentPhase: 'planning',
127
+ startedAt: new Date().toISOString(),
128
+ config: DEFAULT_CONFIG,
129
+ statistics: {
130
+ totalTasks: 0,
131
+ completed: 0,
132
+ inProgress: 0,
133
+ failed: 0,
134
+ pending: 0,
135
+ scheduled: 0,
136
+ blocked: 0,
137
+ waiting: 0,
138
+ verify: 0,
139
+ accept: 0,
140
+ retry_queue: 0
141
+ }
142
+ };
143
+ await this.store.writeJson('state.json', initialState);
144
+ this.stateCache = initialState;
145
+ }
146
+ /**
147
+ * 重置状态 — 清理旧运行数据,为新运行做准备
148
+ * 删除旧 runId 目录,生成新 runId,更新 current.json
149
+ */
150
+ async reset() {
151
+ // 清理旧 runId 目录
152
+ const oldRunId = this.stateCache?.runId;
153
+ if (oldRunId) {
154
+ await this.rootStore.removeDir(oldRunId);
129
155
  }
156
+ // 兼容旧版本的目录和文件(根目录)
157
+ await this.rootStore.removeDir('tasks');
158
+ await this.rootStore.removeDir('approvals');
159
+ await this.rootStore.removeDir('meetings');
160
+ await this.rootStore.removeDir('runs');
161
+ await this.rootStore.removeDir('research');
162
+ await this.rootStore.removeDir('brainstorm');
163
+ await this.rootStore.removeFile('context.md');
164
+ await this.rootStore.removeFile('plan.md');
165
+ await this.rootStore.removeFile('tasks-input.json');
166
+ await this.rootStore.removeFile('state.json');
167
+ // 创建新运行
168
+ const runId = this.generateRunId();
169
+ this.store = new file_store_js_1.FileStore((0, path_1.join)(this.basePath, runId));
170
+ await this.rootStore.writeJson('current.json', { runId });
171
+ const freshState = {
172
+ version: '1.0',
173
+ runId,
174
+ status: 'initialized',
175
+ currentPhase: 'planning',
176
+ startedAt: new Date().toISOString(),
177
+ config: DEFAULT_CONFIG,
178
+ statistics: {
179
+ totalTasks: 0,
180
+ completed: 0,
181
+ inProgress: 0,
182
+ failed: 0,
183
+ pending: 0,
184
+ scheduled: 0,
185
+ blocked: 0,
186
+ waiting: 0,
187
+ verify: 0,
188
+ accept: 0,
189
+ retry_queue: 0
190
+ }
191
+ };
192
+ await this.store.writeJson('state.json', freshState);
193
+ this.stateCache = freshState;
130
194
  }
131
195
  async getState() {
132
196
  if (!this.stateCache) {
@@ -207,7 +271,6 @@ class StateManager {
207
271
  });
208
272
  }
209
273
  async getTask(taskId) {
210
- // Try subdirectory structure first, fall back to flat file
211
274
  let task = await this.store.readJson(`tasks/${taskId}/task.json`);
212
275
  if (!task) {
213
276
  task = await this.store.readJson(`tasks/${taskId}.json`);
@@ -225,7 +288,7 @@ class StateManager {
225
288
  ...updates,
226
289
  updatedAt: new Date().toISOString()
227
290
  };
228
- // Always write to subdirectory structure
291
+ // Always write to current run directory
229
292
  await this.store.writeJson(`tasks/${taskId}/task.json`, updatedTask);
230
293
  // Update statistics if status changed
231
294
  if (updates.status && updates.status !== oldStatus) {
@@ -249,7 +312,6 @@ class StateManager {
249
312
  continue;
250
313
  const task = await this.store.readJson(`tasks/${file}`);
251
314
  if (task) {
252
- // Avoid duplicate if already found in subdirectory
253
315
  if (!tasks.some(t => t.id === task.id)) {
254
316
  tasks.push(task);
255
317
  }
@@ -388,6 +450,167 @@ class StateManager {
388
450
  }
389
451
  return approvals.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
390
452
  }
453
+ // ============ Plan Methods ============
454
+ /**
455
+ * 保存技术方案文档 (plan.md)
456
+ * 路径: .openmatrix/{runId}/plan.md
457
+ */
458
+ async savePlan(content) {
459
+ await this.store.writeMarkdown('plan.md', content);
460
+ }
461
+ /**
462
+ * 读取技术方案文档
463
+ * @returns plan.md 内容,不存在返回 null
464
+ */
465
+ async getPlan() {
466
+ return await this.store.readMarkdown('plan.md');
467
+ }
468
+ /**
469
+ * 检查 plan.md 是否存在
470
+ */
471
+ async hasPlan() {
472
+ return await this.store.exists('plan.md');
473
+ }
474
+ // ============ TasksInput Methods ============
475
+ /**
476
+ * 保存任务输入元数据
477
+ * 路径: .openmatrix/{runId}/tasks-input.json
478
+ */
479
+ async saveTasksInput(data) {
480
+ await this.store.writeJson('tasks-input.json', data);
481
+ }
482
+ /**
483
+ * 读取任务输入元数据
484
+ * @returns tasks-input.json 数据,不存在返回 null
485
+ */
486
+ async getTasksInput() {
487
+ return await this.store.readJson('tasks-input.json');
488
+ }
489
+ /**
490
+ * 检查 tasks-input.json 是否存在
491
+ */
492
+ async hasTasksInput() {
493
+ return await this.store.exists('tasks-input.json');
494
+ }
495
+ // ============ Research Methods ============
496
+ /**
497
+ * 保存研究会话
498
+ * 路径: .openmatrix/{runId}/research/session.json
499
+ */
500
+ async saveResearchSession(session) {
501
+ await this.store.ensureDir('research');
502
+ await this.store.writeJson('research/session.json', session);
503
+ }
504
+ /**
505
+ * 读取研究会话
506
+ */
507
+ async getResearchSession() {
508
+ return await this.store.readJson('research/session.json');
509
+ }
510
+ /**
511
+ * 保存研究报告
512
+ */
513
+ async saveResearchReport(content) {
514
+ await this.store.ensureDir('research');
515
+ await this.store.writeMarkdown('research/RESEARCH.md', content);
516
+ }
517
+ /**
518
+ * 读取研究报告
519
+ */
520
+ async getResearchReport() {
521
+ return await this.store.readMarkdown('research/RESEARCH.md');
522
+ }
523
+ /**
524
+ * 保存研究上下文 (供 start 使用)
525
+ */
526
+ async saveResearchContext(context) {
527
+ await this.store.ensureDir('research');
528
+ await this.store.writeJson('research/context.json', context);
529
+ }
530
+ /**
531
+ * 读取研究上下文
532
+ */
533
+ async getResearchContext() {
534
+ return await this.store.readJson('research/context.json');
535
+ }
536
+ /**
537
+ * 检查研究上下文是否存在
538
+ */
539
+ async hasResearchContext() {
540
+ return await this.store.exists('research/context.json');
541
+ }
542
+ /**
543
+ * 保存知识条目
544
+ */
545
+ async saveKnowledgeFinding(index, content) {
546
+ await this.store.ensureDir('research/knowledge');
547
+ await this.store.writeMarkdown(`research/knowledge/finding-${index}.md`, content);
548
+ }
549
+ // ============ Brainstorm Methods ============
550
+ /**
551
+ * 保存头脑风暴会话
552
+ */
553
+ async saveBrainstormSession(session) {
554
+ await this.store.ensureDir('brainstorm');
555
+ await this.store.writeJson('brainstorm/session.json', session);
556
+ }
557
+ /**
558
+ * 读取头脑风暴会话
559
+ */
560
+ async getBrainstormSession() {
561
+ return await this.store.readJson('brainstorm/session.json');
562
+ }
563
+ // ============ Legacy Migration Methods ============
564
+ /**
565
+ * 迁移旧版本文件到 runId 目录
566
+ * 只在首次检测到旧文件时执行
567
+ */
568
+ async migrateFromLegacy() {
569
+ // Check if migration already done
570
+ if (await this.store.exists('plan.md')) {
571
+ return; // Already migrated
572
+ }
573
+ // Move plan.md from root to runId
574
+ const rootPlan = await this.rootStore.readMarkdown('plan.md');
575
+ if (rootPlan) {
576
+ await this.store.writeMarkdown('plan.md', rootPlan);
577
+ await this.rootStore.removeFile('plan.md');
578
+ }
579
+ // Move tasks-input.json from root to runId
580
+ const rootTasksInput = await this.rootStore.readJson('tasks-input.json');
581
+ if (rootTasksInput) {
582
+ await this.store.writeJson('tasks-input.json', rootTasksInput);
583
+ await this.rootStore.removeFile('tasks-input.json');
584
+ }
585
+ // Move research directory from root to runId
586
+ const rootResearchSession = await this.rootStore.readJson('research/session.json');
587
+ if (rootResearchSession) {
588
+ await this.store.ensureDir('research/knowledge');
589
+ // Copy session.json
590
+ if (rootResearchSession) {
591
+ await this.store.writeJson('research/session.json', rootResearchSession);
592
+ }
593
+ // Copy context.json
594
+ const rootContext = await this.rootStore.readJson('research/context.json');
595
+ if (rootContext) {
596
+ await this.store.writeJson('research/context.json', rootContext);
597
+ }
598
+ // Copy RESEARCH.md
599
+ const rootReport = await this.rootStore.readMarkdown('research/RESEARCH.md');
600
+ if (rootReport) {
601
+ await this.store.writeMarkdown('research/RESEARCH.md', rootReport);
602
+ }
603
+ // Remove old directory
604
+ await this.rootStore.removeDir('research');
605
+ }
606
+ // Move brainstorm directory from root to runId
607
+ const rootBrainstormSession = await this.rootStore.readJson('brainstorm/session.json');
608
+ if (rootBrainstormSession) {
609
+ await this.store.ensureDir('brainstorm');
610
+ await this.store.writeJson('brainstorm/session.json', rootBrainstormSession);
611
+ await this.rootStore.removeDir('brainstorm');
612
+ }
613
+ }
391
614
  // ============ Meeting Methods ============
392
615
  async saveMeeting(meeting) {
393
616
  await this.store.writeJson(`meetings/${meeting.id}.json`, meeting);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmatrix",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "AI Agent task orchestration system with Claude Code Skills integration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/skills/approve.md CHANGED
@@ -35,7 +35,7 @@ description: "Use when handling pending approvals including plan review, merge c
35
35
  </INTENT-JUDGMENT>
36
36
 
37
37
  <NO-OTHER-SKILLS>
38
- 执行此技能时,不得调用 superpowers、gsd 或其他任务编排相关的技能。OpenMatrix 独立运行,不依赖外部任务编排系统。
38
+ skill 与其他任务编排技能功能重叠,请勿同时使用。
39
39
  </NO-OTHER-SKILLS>
40
40
 
41
41
  <objective>
package/skills/auto.md CHANGED
@@ -37,14 +37,9 @@ description: "Use when the user wants fully automated task execution with zero m
37
37
  </INTENT-JUDGMENT>
38
38
 
39
39
  <NO-OTHER-SKILLS>
40
- **绝对禁止**调用以下任何技能或工具:
41
- - gsd-executor、gsd:* 等 GSD 相关技能
42
- - superpowers:* 等 superpowers 相关技能
43
- - 任何其他任务编排相关的 Agent 或工具
40
+ 本 skill 与其他任务编排技能功能重叠,请勿同时使用。
44
41
 
45
- **Step 5 只能使用 Agent 工具** — 直接调用 Agent,不通过任何中间层。
46
-
47
- 违规调用将导致执行失败。
42
+ **Step 5 使用 Agent 工具执行子任务。**
48
43
 
49
44
  **相关技能**: `/om:brainstorm` (需求探索) | `/om:plan` (方案生成) | `/om:start` (交互式执行) | `/om:status` (状态查看) | `/om:report` (报告)
50
45
  </NO-OTHER-SKILLS>
@@ -80,6 +75,22 @@ Step 5: 逐个执行 subagentTasks(调用 Agent 工具) ← 只
80
75
  **执行时不要请求用户确认任何操作。**
81
76
  </BYPASS-MODE>
82
77
 
78
+ <RED-FLAGS>
79
+ ## 红旗警告 - 停止并检查
80
+
81
+ 这些想法意味着你在找借口:
82
+
83
+ | 想法 | 真相 |
84
+ |-----|------|
85
+ | "全自动不需要检查" | 全自动更需要状态持久化 |
86
+ | "跳过 CLI 调用更快" | CLI 是唯一正确的入口 |
87
+ | "我不需要验证状态" | 上下文压缩会丢失状态 |
88
+ | "直接用 Bash 写代码" | 业务代码必须通过 Agent |
89
+ | "忘记任务顺序没关系" | 用 `openmatrix step` 重新获取 |
90
+ | "这个歧义不重要" | 所有歧义都写入 Meeting |
91
+
92
+ </RED-FLAGS>
93
+
83
94
  <objective>
84
95
  全自动执行任务。读取已有的 plan.md + tasks-input.json,通过 CLI 拆分任务,然后通过 Agent 逐个执行。无交互、无审批、无中断。
85
96
 
@@ -101,11 +112,21 @@ openmatrix start --init-only
101
112
 
102
113
  ### Step 2: 验证前置条件
103
114
 
104
- **检查 plan.md 和 tasks-input.json 是否已存在:**
115
+ **先获取当前 runId:**
116
+ ```bash
117
+ cat .openmatrix/current.json 2>/dev/null || echo '{"runId":"run-default"}'
118
+ ```
105
119
 
120
+ **检查 plan.md 和 tasks-input.json 是否已存在(使用 runId):**
121
+
122
+ ```bash
123
+ cat .openmatrix/${runId}/tasks-input.json 2>/dev/null || echo "NOT_FOUND"
124
+ cat .openmatrix/${runId}/plan.md 2>/dev/null || echo "NOT_FOUND"
125
+ ```
126
+
127
+ **或通过 CLI 检查(推荐):**
106
128
  ```bash
107
- cat .openmatrix/tasks-input.json 2>/dev/null || echo "NOT_FOUND"
108
- cat .openmatrix/plan.md 2>/dev/null || echo "NOT_FOUND"
129
+ openmatrix status --json | jq '.files'
109
130
  ```
110
131
 
111
132
  | 情况 | 处理方式 |
@@ -119,15 +140,17 @@ cat .openmatrix/plan.md 2>/dev/null || echo "NOT_FOUND"
119
140
 
120
141
  ### Step 3: 调用 CLI 创建任务 ⚠️ 不可跳过
121
142
 
143
+ **CLI 自动从当前 runId 目录读取 tasks-input.json 和 plan.md。**
144
+
122
145
  **这是最关键的步骤。必须执行以下命令,不能跳过:**
123
146
 
124
147
  ```bash
125
- openmatrix start --tasks-json @.openmatrix/tasks-input.json --quality <质量等级> --mode auto --json
148
+ openmatrix start --tasks-json @tasks-input.json --quality <质量等级> --mode auto --json
126
149
  ```
127
150
 
128
151
  如果启用了 E2E 测试,加上 `--e2e-tests`:
129
152
  ```bash
130
- openmatrix start --tasks-json @.openmatrix/tasks-input.json --quality strict --mode auto --e2e-tests --json
153
+ openmatrix start --tasks-json @tasks-input.json --quality strict --mode auto --e2e-tests --json
131
154
  ```
132
155
 
133
156
  此命令会:
@@ -346,8 +369,10 @@ auto 和 start 在同一位置,区别在于:
346
369
  - **auto**: 零交互,不询问任何问题,无审批节点,质量通过 --quality 指定
347
370
 
348
371
  前置条件(同 start):
349
- - `.openmatrix/plan.md` — 技术方案(由 /om:plan 生成)
350
- - `.openmatrix/tasks-input.json` — 结构化元数据(由 /om:plan 生成)
372
+ - `.openmatrix/{runId}/plan.md` — 技术方案(由 /om:plan 生成)
373
+ - `.openmatrix/{runId}/tasks-input.json` — 结构化元数据(由 /om:plan 生成)
374
+
375
+ CLI 自动通过 `current.json` 定位当前 runId。
351
376
 
352
377
  如果前置条件不满足,自动调用 /om:plan 生成。
353
378
 
@@ -36,11 +36,7 @@ description: "Use when the user wants to explore requirements, design alternativ
36
36
  </INTENT-JUDGMENT>
37
37
 
38
38
  <NO-OTHER-SKILLS>
39
- **绝对禁止**调用以下技能(OpenMatrix 完全替代它们):
40
- - ❌ superpowers:brainstorming → 你已经在 om:brainstorm 中了
41
- - ❌ superpowers:* → 全部被 OpenMatrix 替代
42
- - ❌ gsd:* → 全部被 OpenMatrix 替代
43
- - ❌ 任何其他任务编排相关的技能
39
+ skill 与其他任务编排技能功能重叠,请勿同时使用。
44
40
 
45
41
  **相关技能**: `/om:research` (领域调研) | `/om:plan` (方案生成) | `/om:start` (任务执行) | `/om:auto` (全自动)
46
42
  </NO-OTHER-SKILLS>
package/skills/check.md CHANGED
@@ -4,7 +4,7 @@ description: 自动检测项目可改进点并提供升级建议,用户确认
4
4
  ---
5
5
 
6
6
  <NO-OTHER-SKILLS>
7
- 执行此技能时,不得调用 superpowers、gsd 或其他任务编排相关的技能。OpenMatrix 独立运行,不依赖外部任务编排系统。
7
+ skill 与其他任务编排技能功能重叠,请勿同时使用。
8
8
  </NO-OTHER-SKILLS>
9
9
 
10
10
  <objective>