openmatrix 0.2.27 → 0.2.29
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.
- package/dist/cli/commands/install-skills.js +101 -79
- package/dist/orchestrator/git-commit-manager.d.ts +13 -0
- package/dist/orchestrator/git-commit-manager.js +57 -0
- package/dist/utils/gitignore.js +2 -0
- package/dist/utils/worktree-sync.d.ts +100 -0
- package/dist/utils/worktree-sync.js +352 -0
- package/package.json +1 -1
- package/scripts/install-skills.js +13 -0
- package/skills/auto.md +5 -0
- package/skills/start.md +758 -691
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WorktreeSyncManager = void 0;
|
|
37
|
+
// src/utils/worktree-sync.ts
|
|
38
|
+
const child_process_1 = require("child_process");
|
|
39
|
+
const util_1 = require("util");
|
|
40
|
+
const fs = __importStar(require("fs/promises"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const error_handler_js_1 = require("./error-handler.js");
|
|
43
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
44
|
+
/**
|
|
45
|
+
* WorktreeSyncManager - 管理 Git Worktree 同步
|
|
46
|
+
*
|
|
47
|
+
* 当 Agent 在 worktree 中工作时,改动会提交到 worktree 的分支。
|
|
48
|
+
* 此类负责将这些改动同步回主工作树。
|
|
49
|
+
*
|
|
50
|
+
* 同步策略:
|
|
51
|
+
* 1. 检测 Agent 创建的 worktree
|
|
52
|
+
* 2. 获取 worktree 中的提交和改动文件
|
|
53
|
+
* 3. 将改动文件复制到主工作树(或 cherry-pick 提交)
|
|
54
|
+
* 4. 清理临时 worktree
|
|
55
|
+
*/
|
|
56
|
+
class WorktreeSyncManager {
|
|
57
|
+
repoPath;
|
|
58
|
+
gitRoot = null;
|
|
59
|
+
constructor(repoPath = process.cwd()) {
|
|
60
|
+
this.repoPath = repoPath;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 获取 git 仓库根目录
|
|
64
|
+
*/
|
|
65
|
+
async getGitRoot() {
|
|
66
|
+
if (this.gitRoot)
|
|
67
|
+
return this.gitRoot;
|
|
68
|
+
try {
|
|
69
|
+
const { stdout } = await execAsync('git rev-parse --show-toplevel', { cwd: this.repoPath });
|
|
70
|
+
this.gitRoot = stdout.trim();
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
(0, error_handler_js_1.logError)(error, { operation: 'getGitRoot', file: this.repoPath });
|
|
74
|
+
this.gitRoot = this.repoPath;
|
|
75
|
+
}
|
|
76
|
+
return this.gitRoot;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 列出所有 worktree
|
|
80
|
+
*/
|
|
81
|
+
async listWorktrees() {
|
|
82
|
+
try {
|
|
83
|
+
const { stdout } = await execAsync('git worktree list --porcelain', { cwd: this.repoPath });
|
|
84
|
+
const worktrees = [];
|
|
85
|
+
const lines = stdout.split('\n');
|
|
86
|
+
let current = {};
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
if (line.startsWith('worktree ')) {
|
|
89
|
+
if (current.path) {
|
|
90
|
+
worktrees.push({
|
|
91
|
+
path: current.path,
|
|
92
|
+
branch: current.branch || '',
|
|
93
|
+
commit: current.commit || '',
|
|
94
|
+
isMain: current.isMain || false
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
current = { path: line.slice(9) };
|
|
98
|
+
// 第一个 worktree 是主工作树
|
|
99
|
+
current.isMain = worktrees.length === 0;
|
|
100
|
+
}
|
|
101
|
+
else if (line.startsWith('HEAD ')) {
|
|
102
|
+
current.commit = line.slice(5);
|
|
103
|
+
}
|
|
104
|
+
else if (line.startsWith('branch ')) {
|
|
105
|
+
current.branch = line.slice(7);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// 添加最后一个
|
|
109
|
+
if (current.path) {
|
|
110
|
+
worktrees.push({
|
|
111
|
+
path: current.path,
|
|
112
|
+
branch: current.branch || '',
|
|
113
|
+
commit: current.commit || '',
|
|
114
|
+
isMain: current.isMain || false
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return worktrees;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
(0, error_handler_js_1.logError)(error, { operation: 'listWorktrees', file: this.repoPath });
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 获取 Agent worktree 目录
|
|
126
|
+
*
|
|
127
|
+
* Claude Code 创建的 worktree 通常在 .claude/worktrees/ 目录下
|
|
128
|
+
*/
|
|
129
|
+
async getAgentWorktrees() {
|
|
130
|
+
const worktrees = await this.listWorktrees();
|
|
131
|
+
const gitRoot = await this.getGitRoot();
|
|
132
|
+
// 过滤出 Agent 创建的 worktree(通常以 agent- 开头或在 .claude/worktrees 下)
|
|
133
|
+
return worktrees.filter(w => !w.isMain &&
|
|
134
|
+
(w.path.includes('.claude/worktrees') || w.path.includes('agent-')));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 获取 worktree 相对于主工作树的改动
|
|
138
|
+
*/
|
|
139
|
+
async getWorktreeChanges(worktreePath) {
|
|
140
|
+
try {
|
|
141
|
+
const gitRoot = await this.getGitRoot();
|
|
142
|
+
// 获取 worktree 相对于主分支的改动文件
|
|
143
|
+
const { stdout } = await execAsync(`git diff --name-only HEAD "${worktreePath}"`, { cwd: gitRoot });
|
|
144
|
+
return stdout.split('\n').filter(f => f.trim());
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
// 如果上面的方法失败,尝试直接在 worktree 中获取未提交的改动
|
|
148
|
+
try {
|
|
149
|
+
const { stdout } = await execAsync('git diff --name-only HEAD', { cwd: worktreePath });
|
|
150
|
+
return stdout.split('\n').filter(f => f.trim());
|
|
151
|
+
}
|
|
152
|
+
catch (innerError) {
|
|
153
|
+
(0, error_handler_js_1.logError)(innerError, { operation: 'getWorktreeChanges', file: worktreePath });
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 获取 worktree 中最新提交的改动文件
|
|
160
|
+
*/
|
|
161
|
+
async getLatestCommitChanges(worktreePath) {
|
|
162
|
+
try {
|
|
163
|
+
// 在 worktree 中获取最新提交改动的文件
|
|
164
|
+
const { stdout } = await execAsync('git diff --name-only HEAD~1 HEAD', { cwd: worktreePath });
|
|
165
|
+
return stdout.split('\n').filter(f => f.trim());
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
// 可能只有一个提交,尝试获取与初始提交的差异
|
|
169
|
+
try {
|
|
170
|
+
const { stdout } = await execAsync('git diff --name-only HEAD', { cwd: worktreePath });
|
|
171
|
+
return stdout.split('\n').filter(f => f.trim());
|
|
172
|
+
}
|
|
173
|
+
catch (innerError) {
|
|
174
|
+
(0, error_handler_js_1.logError)(innerError, { operation: 'getLatestCommitChanges', file: worktreePath });
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* 同步 worktree 改动到主工作树
|
|
181
|
+
*
|
|
182
|
+
* 策略:
|
|
183
|
+
* 1. 获取 worktree 中已提交但未同步的改动
|
|
184
|
+
* 2. 将改动文件复制到主工作树
|
|
185
|
+
* 3. 在主工作树中暂存这些文件
|
|
186
|
+
*
|
|
187
|
+
* @param worktreePath worktree 路径(可选,不提供则自动检测所有 Agent worktree)
|
|
188
|
+
*/
|
|
189
|
+
async syncWorktreeToMain(worktreePath) {
|
|
190
|
+
const gitRoot = await this.getGitRoot();
|
|
191
|
+
const worktrees = worktreePath
|
|
192
|
+
? [{ path: worktreePath, branch: '', commit: '', isMain: false }]
|
|
193
|
+
: await this.getAgentWorktrees();
|
|
194
|
+
if (worktrees.length === 0) {
|
|
195
|
+
return [{
|
|
196
|
+
success: true,
|
|
197
|
+
syncedFiles: [],
|
|
198
|
+
error: 'No agent worktrees found'
|
|
199
|
+
}];
|
|
200
|
+
}
|
|
201
|
+
const results = [];
|
|
202
|
+
for (const wt of worktrees) {
|
|
203
|
+
try {
|
|
204
|
+
// 1. 获取改动文件
|
|
205
|
+
const changedFiles = await this.getLatestCommitChanges(wt.path);
|
|
206
|
+
if (changedFiles.length === 0) {
|
|
207
|
+
results.push({
|
|
208
|
+
success: true,
|
|
209
|
+
syncedFiles: [],
|
|
210
|
+
branch: wt.branch,
|
|
211
|
+
error: 'No changes in worktree'
|
|
212
|
+
});
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
// 2. 复制文件到主工作树
|
|
216
|
+
const syncedFiles = [];
|
|
217
|
+
for (const file of changedFiles) {
|
|
218
|
+
const srcPath = path.join(wt.path, file);
|
|
219
|
+
const destPath = path.join(gitRoot, file);
|
|
220
|
+
try {
|
|
221
|
+
// 确保目标目录存在
|
|
222
|
+
const destDir = path.dirname(destPath);
|
|
223
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
224
|
+
// 复制文件
|
|
225
|
+
await fs.copyFile(srcPath, destPath);
|
|
226
|
+
syncedFiles.push(file);
|
|
227
|
+
}
|
|
228
|
+
catch (copyError) {
|
|
229
|
+
(0, error_handler_js_1.logError)(copyError, { operation: 'copyFile', file: srcPath, metadata: { dest: destPath } });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// 3. 在主工作树中暂存文件
|
|
233
|
+
if (syncedFiles.length > 0) {
|
|
234
|
+
const filesArg = syncedFiles.map(f => `"${f}"`).join(' ');
|
|
235
|
+
await execAsync(`git add ${filesArg}`, { cwd: gitRoot });
|
|
236
|
+
}
|
|
237
|
+
// 4. 获取 worktree 的最新提交 hash
|
|
238
|
+
let commitHash;
|
|
239
|
+
try {
|
|
240
|
+
const { stdout } = await execAsync('git rev-parse HEAD', { cwd: wt.path });
|
|
241
|
+
commitHash = stdout.trim();
|
|
242
|
+
}
|
|
243
|
+
catch { }
|
|
244
|
+
results.push({
|
|
245
|
+
success: true,
|
|
246
|
+
syncedFiles,
|
|
247
|
+
commitHash,
|
|
248
|
+
branch: wt.branch
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
results.push({
|
|
253
|
+
success: false,
|
|
254
|
+
syncedFiles: [],
|
|
255
|
+
branch: wt.branch,
|
|
256
|
+
error: error instanceof Error ? error.message : String(error)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return results;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Cherry-pick worktree 的提交到主工作树
|
|
264
|
+
*
|
|
265
|
+
* 替代方案:直接 cherry-pick worktree 的提交
|
|
266
|
+
* 适用于:改动较大、需要保留完整提交历史的场景
|
|
267
|
+
*/
|
|
268
|
+
async cherryPickWorktreeCommit(worktreePath) {
|
|
269
|
+
const gitRoot = await this.getGitRoot();
|
|
270
|
+
try {
|
|
271
|
+
// 1. 获取 worktree 的最新提交 hash
|
|
272
|
+
const { stdout: commitHash } = await execAsync('git rev-parse HEAD', { cwd: worktreePath });
|
|
273
|
+
const hash = commitHash.trim();
|
|
274
|
+
// 2. 在主工作树中 cherry-pick 这个提交
|
|
275
|
+
await execAsync(`git cherry-pick ${hash} --no-commit`, { cwd: gitRoot });
|
|
276
|
+
// 3. 获取被 cherry-pick 的文件列表
|
|
277
|
+
const { stdout: files } = await execAsync('git diff --name-only --cached', { cwd: gitRoot });
|
|
278
|
+
const syncedFiles = files.split('\n').filter(f => f.trim());
|
|
279
|
+
return {
|
|
280
|
+
success: true,
|
|
281
|
+
syncedFiles,
|
|
282
|
+
commitHash: hash
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
// Cherry-pick 可能失败(冲突),尝试放弃并返回错误
|
|
287
|
+
try {
|
|
288
|
+
await execAsync('git cherry-pick --abort', { cwd: gitRoot });
|
|
289
|
+
}
|
|
290
|
+
catch { }
|
|
291
|
+
return {
|
|
292
|
+
success: false,
|
|
293
|
+
syncedFiles: [],
|
|
294
|
+
error: error instanceof Error ? error.message : String(error)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* 清理已同步的 worktree
|
|
300
|
+
*/
|
|
301
|
+
async cleanupWorktree(worktreePath) {
|
|
302
|
+
try {
|
|
303
|
+
const gitRoot = await this.getGitRoot();
|
|
304
|
+
// 1. 移除 worktree
|
|
305
|
+
await execAsync(`git worktree remove "${worktreePath}" --force`, { cwd: gitRoot });
|
|
306
|
+
// 2. 清理可能的残留分支(如果是临时分支)
|
|
307
|
+
// 注意:不自动删除分支,因为可能还需要
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
(0, error_handler_js_1.logError)(error, { operation: 'cleanupWorktree', file: worktreePath });
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 完整的同步流程
|
|
317
|
+
*
|
|
318
|
+
* 1. 检测所有 Agent worktree
|
|
319
|
+
* 2. 同步改动到主工作树
|
|
320
|
+
* 3. 清理 worktree
|
|
321
|
+
*
|
|
322
|
+
* @returns 同步结果
|
|
323
|
+
*/
|
|
324
|
+
async fullSync() {
|
|
325
|
+
// 1. 同步
|
|
326
|
+
const syncResults = await this.syncWorktreeToMain();
|
|
327
|
+
// 2. 清理已成功同步的 worktree
|
|
328
|
+
const cleanupResults = [];
|
|
329
|
+
const worktrees = await this.getAgentWorktrees();
|
|
330
|
+
for (const wt of worktrees) {
|
|
331
|
+
const syncResult = syncResults.find(r => r.branch === wt.branch);
|
|
332
|
+
if (syncResult?.success && syncResult.syncedFiles.length > 0) {
|
|
333
|
+
const cleanupSuccess = await this.cleanupWorktree(wt.path);
|
|
334
|
+
cleanupResults.push({ path: wt.path, success: cleanupSuccess });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { syncResults, cleanupResults };
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 检查是否有未同步的 worktree 改动
|
|
341
|
+
*/
|
|
342
|
+
async hasUnsyncedChanges() {
|
|
343
|
+
const worktrees = await this.getAgentWorktrees();
|
|
344
|
+
for (const wt of worktrees) {
|
|
345
|
+
const changes = await this.getLatestCommitChanges(wt.path);
|
|
346
|
+
if (changes.length > 0)
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
exports.WorktreeSyncManager = WorktreeSyncManager;
|
package/package.json
CHANGED
|
@@ -23,6 +23,19 @@ const targets = [
|
|
|
23
23
|
},
|
|
24
24
|
];
|
|
25
25
|
|
|
26
|
+
// Check for MatrixCode installation
|
|
27
|
+
// MatrixCode uses ~/.matrix/ directory for configuration
|
|
28
|
+
const matrixDir = path.join(os.homedir(), '.matrix');
|
|
29
|
+
const matrixSkillsDir = path.join(matrixDir, 'skills', 'om');
|
|
30
|
+
|
|
31
|
+
// If ~/.matrix/ exists, add MatrixCode as a target
|
|
32
|
+
if (fs.existsSync(matrixDir)) {
|
|
33
|
+
targets.push({
|
|
34
|
+
name: 'MatrixCode',
|
|
35
|
+
dir: matrixSkillsDir,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
if (!fs.existsSync(skillsDir)) {
|
|
27
40
|
console.log('Skills directory not found, skipping installation.');
|
|
28
41
|
process.exit(0);
|
package/skills/auto.md
CHANGED
|
@@ -285,6 +285,11 @@ Agent({
|
|
|
285
285
|
openmatrix complete TASK-XXX --success
|
|
286
286
|
```
|
|
287
287
|
|
|
288
|
+
**注意**: `openmatrix complete` 会自动执行以下操作:
|
|
289
|
+
1. 🔄 **同步 Worktree 改动** - 如果 Agent 在 worktree 中工作,自动将改动同步到主工作树
|
|
290
|
+
2. 📝 **更新上下文** - 将执行摘要写入全局 context.md
|
|
291
|
+
3. ✅ **标记完成** - 更新任务状态并触发 Git 提交
|
|
292
|
+
|
|
288
293
|
3. **获取下一个任务(必须执行,防止上下文压缩丢失):**
|
|
289
294
|
```bash
|
|
290
295
|
openmatrix step --json
|