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
|
@@ -40,12 +40,16 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const os = __importStar(require("os"));
|
|
42
42
|
exports.installSkillsCommand = new commander_1.Command('install-skills')
|
|
43
|
-
.description('Install OpenMatrix skills to ~/.claude/commands/om/')
|
|
43
|
+
.description('Install OpenMatrix skills to ~/.claude/commands/om/ and ~/.matrix/skills/om/ (if MatrixCode detected)')
|
|
44
44
|
.option('-f, --force', 'Force overwrite existing skills', false)
|
|
45
45
|
.action((options) => {
|
|
46
46
|
const skillsDir = path.join(__dirname, '..', '..', '..', 'skills');
|
|
47
47
|
const claudeDir = path.join(os.homedir(), '.claude');
|
|
48
|
-
const
|
|
48
|
+
const claudeCommandsDir = path.join(claudeDir, 'commands', 'om');
|
|
49
|
+
// Check for MatrixCode installation
|
|
50
|
+
const matrixDir = path.join(os.homedir(), '.matrix');
|
|
51
|
+
const matrixSkillsDir = path.join(matrixDir, 'skills', 'om');
|
|
52
|
+
const hasMatrixCode = fs.existsSync(matrixDir);
|
|
49
53
|
console.log('📦 OpenMatrix Skills Installer\n');
|
|
50
54
|
// Check if skills directory exists
|
|
51
55
|
if (!fs.existsSync(skillsDir)) {
|
|
@@ -53,106 +57,124 @@ exports.installSkillsCommand = new commander_1.Command('install-skills')
|
|
|
53
57
|
console.error(' Make sure openmatrix is installed correctly.');
|
|
54
58
|
process.exit(1);
|
|
55
59
|
}
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
console.error('❌ Cannot create directory:', commandsDir);
|
|
65
|
-
console.error(' Error:', err instanceof Error ? err.message : String(err));
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
68
|
-
// Get skill files (excluding om.md and openmatrix.md which are handled separately)
|
|
60
|
+
// Define target directories
|
|
61
|
+
const targets = [
|
|
62
|
+
{ name: 'Claude Code', dir: claudeCommandsDir, enabled: true },
|
|
63
|
+
{ name: 'MatrixCode', dir: matrixSkillsDir, enabled: hasMatrixCode },
|
|
64
|
+
];
|
|
65
|
+
// Get skill files
|
|
69
66
|
const files = fs.readdirSync(skillsDir).filter(f => f.endsWith('.md') && f !== 'om.md' && f !== 'openmatrix.md');
|
|
70
67
|
if (files.length === 0) {
|
|
71
68
|
console.error('❌ No skill files found in:', skillsDir);
|
|
72
69
|
process.exit(1);
|
|
73
70
|
}
|
|
74
71
|
console.log(`📋 Found ${files.length} skill files\n`);
|
|
75
|
-
let
|
|
76
|
-
let
|
|
77
|
-
let
|
|
78
|
-
// Install
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
let totalInstalled = 0;
|
|
73
|
+
let totalSkipped = 0;
|
|
74
|
+
let totalFailed = 0;
|
|
75
|
+
// Install to each target
|
|
76
|
+
for (const target of targets) {
|
|
77
|
+
if (!target.enabled) {
|
|
78
|
+
console.log(`⏭️ ${target.name}: skipped (not installed)`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
console.log(`\n🔧 Installing to ${target.name}...`);
|
|
82
82
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.log(
|
|
86
|
-
skipped++;
|
|
87
|
-
return;
|
|
83
|
+
if (!fs.existsSync(target.dir)) {
|
|
84
|
+
fs.mkdirSync(target.dir, { recursive: true });
|
|
85
|
+
console.log(`📁 Created directory: ${target.dir}`);
|
|
88
86
|
}
|
|
89
|
-
fs.copyFileSync(src, dest);
|
|
90
|
-
const skillName = path.basename(file, '.md');
|
|
91
|
-
console.log(` ✅ Installed: /om:${skillName}`);
|
|
92
|
-
installed++;
|
|
93
87
|
}
|
|
94
88
|
catch (err) {
|
|
95
|
-
console.
|
|
96
|
-
|
|
89
|
+
console.error(`❌ Cannot create directory: ${target.dir}`);
|
|
90
|
+
console.error(` Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
91
|
+
continue;
|
|
97
92
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
let installed = 0;
|
|
94
|
+
let skipped = 0;
|
|
95
|
+
let failed = 0;
|
|
96
|
+
// Install skill files
|
|
97
|
+
files.forEach(file => {
|
|
98
|
+
const src = path.join(skillsDir, file);
|
|
99
|
+
const dest = path.join(target.dir, file);
|
|
100
|
+
try {
|
|
101
|
+
if (fs.existsSync(dest) && !options.force) {
|
|
102
|
+
skipped++;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
fs.copyFileSync(src, dest);
|
|
111
106
|
installed++;
|
|
112
107
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
failed++;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// Install auto-detection instructions
|
|
120
|
-
const autoSrc = path.join(skillsDir, 'openmatrix.md');
|
|
121
|
-
const autoDest = path.join(claudeDir, 'commands', 'openmatrix.md');
|
|
122
|
-
if (fs.existsSync(autoSrc)) {
|
|
123
|
-
try {
|
|
124
|
-
if (fs.existsSync(autoDest) && !options.force) {
|
|
125
|
-
console.log(` ⏭️ Skipped: openmatrix.md (already exists)`);
|
|
126
|
-
skipped++;
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.log(` ❌ Failed: ${file} (${err instanceof Error ? err.message : String(err)})`);
|
|
110
|
+
failed++;
|
|
127
111
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
112
|
+
});
|
|
113
|
+
// Install om.md to parent directory for Claude Code
|
|
114
|
+
if (target.name === 'Claude Code') {
|
|
115
|
+
const omSrc = path.join(skillsDir, 'om.md');
|
|
116
|
+
const omDest = path.join(claudeDir, 'commands', 'om.md');
|
|
117
|
+
if (fs.existsSync(omSrc)) {
|
|
118
|
+
try {
|
|
119
|
+
if (fs.existsSync(omDest) && !options.force) {
|
|
120
|
+
skipped++;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
fs.copyFileSync(omSrc, omDest);
|
|
124
|
+
installed++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
failed++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Install openmatrix.md (auto-detection)
|
|
132
|
+
const autoSrc = path.join(skillsDir, 'openmatrix.md');
|
|
133
|
+
const autoDest = path.join(claudeDir, 'commands', 'openmatrix.md');
|
|
134
|
+
if (fs.existsSync(autoSrc)) {
|
|
135
|
+
try {
|
|
136
|
+
if (fs.existsSync(autoDest) && !options.force) {
|
|
137
|
+
skipped++;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
fs.copyFileSync(autoSrc, autoDest);
|
|
141
|
+
installed++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
failed++;
|
|
146
|
+
}
|
|
132
147
|
}
|
|
133
148
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
console.log(` ✅ Installed: ${installed}`);
|
|
150
|
+
console.log(` ⏭️ Skipped: ${skipped}`);
|
|
151
|
+
if (failed > 0) {
|
|
152
|
+
console.log(` ❌ Failed: ${failed}`);
|
|
137
153
|
}
|
|
154
|
+
totalInstalled += installed;
|
|
155
|
+
totalSkipped += skipped;
|
|
156
|
+
totalFailed += failed;
|
|
138
157
|
}
|
|
139
158
|
console.log('\n' + '─'.repeat(50));
|
|
140
|
-
console.log(`📊 Summary:`);
|
|
141
|
-
console.log(` ✅ Installed: ${
|
|
142
|
-
console.log(` ⏭️ Skipped: ${
|
|
143
|
-
console.log(` ❌ Failed: ${
|
|
144
|
-
console.log(
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
console.log(`📊 Total Summary:`);
|
|
160
|
+
console.log(` ✅ Installed: ${totalInstalled}`);
|
|
161
|
+
console.log(` ⏭️ Skipped: ${totalSkipped}`);
|
|
162
|
+
console.log(` ❌ Failed: ${totalFailed}`);
|
|
163
|
+
console.log('\n📁 Installation locations:');
|
|
164
|
+
for (const target of targets) {
|
|
165
|
+
if (target.enabled) {
|
|
166
|
+
console.log(` ${target.name}: ${target.dir}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (totalInstalled > 0) {
|
|
147
170
|
console.log('\n🎉 Skills installed successfully!');
|
|
148
171
|
console.log(' Try: /om <your task>');
|
|
149
172
|
console.log(' Or: /om:start <your task>');
|
|
150
|
-
console.log('\n💡 Auto-detection enabled!');
|
|
151
|
-
console.log(' Type task descriptions directly:');
|
|
152
|
-
console.log(' - "实现用户登录功能" → auto invokes /om:start');
|
|
153
|
-
console.log(' - "fix the login bug" → auto invokes /om:start');
|
|
154
173
|
}
|
|
155
|
-
if (
|
|
174
|
+
if (hasMatrixCode) {
|
|
175
|
+
console.log('\n✅ MatrixCode detected! Skills also installed to ~/.matrix/skills/om/');
|
|
176
|
+
}
|
|
177
|
+
if (totalFailed > 0) {
|
|
156
178
|
process.exit(1);
|
|
157
179
|
}
|
|
158
180
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Task } from '../types/index.js';
|
|
2
|
+
import { WorktreeSyncManager } from '../utils/worktree-sync.js';
|
|
2
3
|
export interface CommitInfo {
|
|
3
4
|
taskId: string;
|
|
4
5
|
taskTitle: string;
|
|
@@ -25,6 +26,7 @@ export declare class GitCommitManager {
|
|
|
25
26
|
private repoPath;
|
|
26
27
|
private gitRoot;
|
|
27
28
|
private enabled;
|
|
29
|
+
private worktreeSyncManager;
|
|
28
30
|
constructor(repoPath?: string);
|
|
29
31
|
/**
|
|
30
32
|
* 获取 git 仓库根目录(支持 .git 在父级目录的情况)
|
|
@@ -107,4 +109,15 @@ export declare class GitCommitManager {
|
|
|
107
109
|
* 提交验收阶段完成
|
|
108
110
|
*/
|
|
109
111
|
commitAcceptComplete(task: Task, runId: string): Promise<CommitResult>;
|
|
112
|
+
/**
|
|
113
|
+
* 同步 worktree 改动到主工作树
|
|
114
|
+
*
|
|
115
|
+
* 当 Agent 在 worktree 中工作时,改动会提交到 worktree 的分支。
|
|
116
|
+
* 此方法将这些改动同步回主工作树,确保提交可见。
|
|
117
|
+
*/
|
|
118
|
+
private syncWorktreeChanges;
|
|
119
|
+
/**
|
|
120
|
+
* 获取 WorktreeSyncManager 实例(供外部调用)
|
|
121
|
+
*/
|
|
122
|
+
getWorktreeSyncManager(): WorktreeSyncManager;
|
|
110
123
|
}
|
|
@@ -41,6 +41,7 @@ const path = __importStar(require("path"));
|
|
|
41
41
|
const fs = __importStar(require("fs/promises"));
|
|
42
42
|
const gitignore_js_1 = require("../utils/gitignore.js");
|
|
43
43
|
const error_handler_js_1 = require("../utils/error-handler.js");
|
|
44
|
+
const worktree_sync_js_1 = require("../utils/worktree-sync.js");
|
|
44
45
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
45
46
|
/**
|
|
46
47
|
* GitCommitManager - Git 自动提交管理器
|
|
@@ -54,8 +55,10 @@ class GitCommitManager {
|
|
|
54
55
|
repoPath;
|
|
55
56
|
gitRoot = null;
|
|
56
57
|
enabled = true;
|
|
58
|
+
worktreeSyncManager;
|
|
57
59
|
constructor(repoPath = process.cwd()) {
|
|
58
60
|
this.repoPath = repoPath;
|
|
61
|
+
this.worktreeSyncManager = new worktree_sync_js_1.WorktreeSyncManager(repoPath);
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
61
64
|
* 获取 git 仓库根目录(支持 .git 在父级目录的情况)
|
|
@@ -329,6 +332,8 @@ class GitCommitManager {
|
|
|
329
332
|
}
|
|
330
333
|
// 确保 .gitignore 中包含 .openmatrix(写入到 git 根目录)
|
|
331
334
|
await (0, gitignore_js_1.ensureOpenmatrixGitignore)(this.repoPath);
|
|
335
|
+
// 🔄 同步 worktree 改动到主工作树(关键步骤)
|
|
336
|
+
await this.syncWorktreeChanges();
|
|
332
337
|
// 获取未提交的文件(用于生成 commit message,在 git add 之前获取)
|
|
333
338
|
const files = await this.getUncommittedFiles();
|
|
334
339
|
if (files.length === 0) {
|
|
@@ -449,5 +454,57 @@ class GitCommitManager {
|
|
|
449
454
|
impactScope: []
|
|
450
455
|
});
|
|
451
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* 同步 worktree 改动到主工作树
|
|
459
|
+
*
|
|
460
|
+
* 当 Agent 在 worktree 中工作时,改动会提交到 worktree 的分支。
|
|
461
|
+
* 此方法将这些改动同步回主工作树,确保提交可见。
|
|
462
|
+
*/
|
|
463
|
+
async syncWorktreeChanges() {
|
|
464
|
+
try {
|
|
465
|
+
// 检查是否有 Agent worktree
|
|
466
|
+
const hasChanges = await this.worktreeSyncManager.hasUnsyncedChanges();
|
|
467
|
+
if (!hasChanges) {
|
|
468
|
+
console.log('✅ 未检测到 worktree 改动,跳过同步');
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
console.log('🔄 检测到 worktree 改动,开始同步...');
|
|
472
|
+
// 执行完整同步
|
|
473
|
+
const { syncResults, cleanupResults } = await this.worktreeSyncManager.fullSync();
|
|
474
|
+
// 输出同步结果
|
|
475
|
+
for (const result of syncResults) {
|
|
476
|
+
if (result.success && result.syncedFiles.length > 0) {
|
|
477
|
+
console.log(`✅ 同步成功: ${result.syncedFiles.length} 个文件`);
|
|
478
|
+
console.log(` 文件: ${result.syncedFiles.slice(0, 5).join(', ')}${result.syncedFiles.length > 5 ? '...' : ''}`);
|
|
479
|
+
if (result.commitHash) {
|
|
480
|
+
console.log(` 来源提交: ${result.commitHash}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
else if (result.error) {
|
|
484
|
+
console.log(`⚠️ 同步跳过: ${result.error}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
// 输出清理结果
|
|
488
|
+
for (const cleanup of cleanupResults) {
|
|
489
|
+
if (cleanup.success) {
|
|
490
|
+
console.log(`🧹 清理 worktree: ${cleanup.path}`);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
console.log(`⚠️ 清理失败: ${cleanup.path}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
// 同步失败不应该阻止提交,但需要记录警告
|
|
499
|
+
console.warn('⚠️ Worktree 同步失败:', error instanceof Error ? error.message : String(error));
|
|
500
|
+
(0, error_handler_js_1.logError)(error, { operation: 'syncWorktreeChanges', file: this.repoPath });
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* 获取 WorktreeSyncManager 实例(供外部调用)
|
|
505
|
+
*/
|
|
506
|
+
getWorktreeSyncManager() {
|
|
507
|
+
return this.worktreeSyncManager;
|
|
508
|
+
}
|
|
452
509
|
}
|
|
453
510
|
exports.GitCommitManager = GitCommitManager;
|
package/dist/utils/gitignore.js
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree 同步结果
|
|
3
|
+
*/
|
|
4
|
+
export interface WorktreeSyncResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
syncedFiles: string[];
|
|
7
|
+
commitHash?: string;
|
|
8
|
+
branch?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Worktree 信息
|
|
13
|
+
*/
|
|
14
|
+
export interface WorktreeInfo {
|
|
15
|
+
path: string;
|
|
16
|
+
branch: string;
|
|
17
|
+
commit: string;
|
|
18
|
+
isMain: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* WorktreeSyncManager - 管理 Git Worktree 同步
|
|
22
|
+
*
|
|
23
|
+
* 当 Agent 在 worktree 中工作时,改动会提交到 worktree 的分支。
|
|
24
|
+
* 此类负责将这些改动同步回主工作树。
|
|
25
|
+
*
|
|
26
|
+
* 同步策略:
|
|
27
|
+
* 1. 检测 Agent 创建的 worktree
|
|
28
|
+
* 2. 获取 worktree 中的提交和改动文件
|
|
29
|
+
* 3. 将改动文件复制到主工作树(或 cherry-pick 提交)
|
|
30
|
+
* 4. 清理临时 worktree
|
|
31
|
+
*/
|
|
32
|
+
export declare class WorktreeSyncManager {
|
|
33
|
+
private repoPath;
|
|
34
|
+
private gitRoot;
|
|
35
|
+
constructor(repoPath?: string);
|
|
36
|
+
/**
|
|
37
|
+
* 获取 git 仓库根目录
|
|
38
|
+
*/
|
|
39
|
+
private getGitRoot;
|
|
40
|
+
/**
|
|
41
|
+
* 列出所有 worktree
|
|
42
|
+
*/
|
|
43
|
+
listWorktrees(): Promise<WorktreeInfo[]>;
|
|
44
|
+
/**
|
|
45
|
+
* 获取 Agent worktree 目录
|
|
46
|
+
*
|
|
47
|
+
* Claude Code 创建的 worktree 通常在 .claude/worktrees/ 目录下
|
|
48
|
+
*/
|
|
49
|
+
getAgentWorktrees(): Promise<WorktreeInfo[]>;
|
|
50
|
+
/**
|
|
51
|
+
* 获取 worktree 相对于主工作树的改动
|
|
52
|
+
*/
|
|
53
|
+
getWorktreeChanges(worktreePath: string): Promise<string[]>;
|
|
54
|
+
/**
|
|
55
|
+
* 获取 worktree 中最新提交的改动文件
|
|
56
|
+
*/
|
|
57
|
+
getLatestCommitChanges(worktreePath: string): Promise<string[]>;
|
|
58
|
+
/**
|
|
59
|
+
* 同步 worktree 改动到主工作树
|
|
60
|
+
*
|
|
61
|
+
* 策略:
|
|
62
|
+
* 1. 获取 worktree 中已提交但未同步的改动
|
|
63
|
+
* 2. 将改动文件复制到主工作树
|
|
64
|
+
* 3. 在主工作树中暂存这些文件
|
|
65
|
+
*
|
|
66
|
+
* @param worktreePath worktree 路径(可选,不提供则自动检测所有 Agent worktree)
|
|
67
|
+
*/
|
|
68
|
+
syncWorktreeToMain(worktreePath?: string): Promise<WorktreeSyncResult[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Cherry-pick worktree 的提交到主工作树
|
|
71
|
+
*
|
|
72
|
+
* 替代方案:直接 cherry-pick worktree 的提交
|
|
73
|
+
* 适用于:改动较大、需要保留完整提交历史的场景
|
|
74
|
+
*/
|
|
75
|
+
cherryPickWorktreeCommit(worktreePath: string): Promise<WorktreeSyncResult>;
|
|
76
|
+
/**
|
|
77
|
+
* 清理已同步的 worktree
|
|
78
|
+
*/
|
|
79
|
+
cleanupWorktree(worktreePath: string): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* 完整的同步流程
|
|
82
|
+
*
|
|
83
|
+
* 1. 检测所有 Agent worktree
|
|
84
|
+
* 2. 同步改动到主工作树
|
|
85
|
+
* 3. 清理 worktree
|
|
86
|
+
*
|
|
87
|
+
* @returns 同步结果
|
|
88
|
+
*/
|
|
89
|
+
fullSync(): Promise<{
|
|
90
|
+
syncResults: WorktreeSyncResult[];
|
|
91
|
+
cleanupResults: {
|
|
92
|
+
path: string;
|
|
93
|
+
success: boolean;
|
|
94
|
+
}[];
|
|
95
|
+
}>;
|
|
96
|
+
/**
|
|
97
|
+
* 检查是否有未同步的 worktree 改动
|
|
98
|
+
*/
|
|
99
|
+
hasUnsyncedChanges(): Promise<boolean>;
|
|
100
|
+
}
|