itismyskillmarket 1.3.3 → 1.3.4
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/README.md +3 -1
- package/dist/index.js +121 -2
- package/package.json +1 -1
- package/src/adapters/hermes.test.ts +39 -0
- package/src/adapters/hermes.ts +77 -0
- package/src/adapters/index.ts +2 -0
- package/src/adapters/openclaw.test.ts +40 -0
- package/src/adapters/openclaw.ts +69 -0
- package/src/adapters/registry.test.ts +29 -0
- package/src/adapters/registry.ts +8 -0
- package/src/constants.test.ts +18 -0
- package/src/constants.ts +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SkillMarket
|
|
2
2
|
|
|
3
|
-
Cross-platform skill manager for AI coding tools (Cursor, VSCode, Codex, OpenCode, Claude Code, Antigravity).
|
|
3
|
+
Cross-platform skill manager for AI coding tools (Cursor, VSCode, Codex, OpenCode, Claude Code, Antigravity, OpenClaw, Hermes Agent).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -104,6 +104,8 @@ $ skm platforms
|
|
|
104
104
|
OpenCode ✅ Available (2 skills installed)
|
|
105
105
|
Claude Code ✅ Available (1 skills installed)
|
|
106
106
|
VSCode ✅ Available (0 skills installed)
|
|
107
|
+
OpenClaw ✅ Available
|
|
108
|
+
Hermes Agent ✅ Available
|
|
107
109
|
```
|
|
108
110
|
|
|
109
111
|
## Development
|
package/dist/index.js
CHANGED
|
@@ -35,8 +35,12 @@ var PLATFORMS = [
|
|
|
35
35
|
// OpenCode - 开源 AI 编程工具
|
|
36
36
|
"claude",
|
|
37
37
|
// Claude Code - Anthropic CLI 工具
|
|
38
|
-
"antigravity"
|
|
38
|
+
"antigravity",
|
|
39
39
|
// Antigravity - AI 编程助手
|
|
40
|
+
"openclaw",
|
|
41
|
+
// OpenClaw - AgentSkills compatible agent
|
|
42
|
+
"hermes"
|
|
43
|
+
// Hermes Agent - NousResearch agent framework
|
|
40
44
|
];
|
|
41
45
|
var REGISTRY_FILE = "registry.json";
|
|
42
46
|
var LATEST_LINK = "latest";
|
|
@@ -555,15 +559,128 @@ var VSCodeAdapter = class extends BaseAdapter {
|
|
|
555
559
|
}
|
|
556
560
|
};
|
|
557
561
|
|
|
562
|
+
// src/adapters/openclaw.ts
|
|
563
|
+
import { readdirSync, existsSync, cpSync, rmSync } from "fs";
|
|
564
|
+
import { join } from "path";
|
|
565
|
+
import { homedir } from "os";
|
|
566
|
+
import { ensureDirSync } from "fs-extra";
|
|
567
|
+
var OpenClawAdapter = class {
|
|
568
|
+
id = "openclaw";
|
|
569
|
+
name = "OpenClaw";
|
|
570
|
+
skillDir = join(homedir(), ".openclaw", "skills");
|
|
571
|
+
async isAvailable() {
|
|
572
|
+
try {
|
|
573
|
+
return existsSync(join(homedir(), ".openclaw"));
|
|
574
|
+
} catch {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async isInstalled(skillId) {
|
|
579
|
+
try {
|
|
580
|
+
const skillPath = join(this.skillDir, skillId);
|
|
581
|
+
return existsSync(skillPath);
|
|
582
|
+
} catch {
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
async install(skillId, sourceDir) {
|
|
587
|
+
ensureDirSync(this.skillDir);
|
|
588
|
+
const targetDir = join(this.skillDir, skillId);
|
|
589
|
+
if (existsSync(targetDir)) {
|
|
590
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
591
|
+
}
|
|
592
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
593
|
+
}
|
|
594
|
+
async uninstall(skillId) {
|
|
595
|
+
const targetDir = join(this.skillDir, skillId);
|
|
596
|
+
if (existsSync(targetDir)) {
|
|
597
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async listInstalled() {
|
|
601
|
+
try {
|
|
602
|
+
if (!existsSync(this.skillDir)) {
|
|
603
|
+
return [];
|
|
604
|
+
}
|
|
605
|
+
return readdirSync(this.skillDir).filter((name) => {
|
|
606
|
+
const fullPath = join(this.skillDir, name);
|
|
607
|
+
return existsSync(fullPath) && name !== ".";
|
|
608
|
+
});
|
|
609
|
+
} catch {
|
|
610
|
+
return [];
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// src/adapters/hermes.ts
|
|
616
|
+
import { readdirSync as readdirSync2, existsSync as existsSync2, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
|
|
617
|
+
import { join as join2 } from "path";
|
|
618
|
+
import { homedir as homedir2 } from "os";
|
|
619
|
+
import { ensureDirSync as ensureDirSync2 } from "fs-extra";
|
|
620
|
+
var HermesAdapter = class {
|
|
621
|
+
id = "hermes";
|
|
622
|
+
name = "Hermes Agent";
|
|
623
|
+
skillDir = join2(homedir2(), ".hermes", "skills");
|
|
624
|
+
async isAvailable() {
|
|
625
|
+
try {
|
|
626
|
+
if (existsSync2(join2(homedir2(), ".hermes"))) {
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
return false;
|
|
630
|
+
} catch {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async isInstalled(skillId) {
|
|
635
|
+
try {
|
|
636
|
+
const skillPath = join2(this.skillDir, skillId);
|
|
637
|
+
return existsSync2(skillPath);
|
|
638
|
+
} catch {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
async install(skillId, sourceDir) {
|
|
643
|
+
ensureDirSync2(this.skillDir);
|
|
644
|
+
const targetDir = join2(this.skillDir, skillId);
|
|
645
|
+
if (existsSync2(targetDir)) {
|
|
646
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
647
|
+
}
|
|
648
|
+
cpSync2(sourceDir, targetDir, { recursive: true });
|
|
649
|
+
}
|
|
650
|
+
async uninstall(skillId) {
|
|
651
|
+
const targetDir = join2(this.skillDir, skillId);
|
|
652
|
+
if (existsSync2(targetDir)) {
|
|
653
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
async listInstalled() {
|
|
657
|
+
try {
|
|
658
|
+
if (!existsSync2(this.skillDir)) {
|
|
659
|
+
return [];
|
|
660
|
+
}
|
|
661
|
+
return readdirSync2(this.skillDir).filter((name) => {
|
|
662
|
+
const fullPath = join2(this.skillDir, name);
|
|
663
|
+
return existsSync2(fullPath) && name !== ".";
|
|
664
|
+
});
|
|
665
|
+
} catch {
|
|
666
|
+
return [];
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
558
671
|
// src/adapters/registry.ts
|
|
559
672
|
var adapters = /* @__PURE__ */ new Map();
|
|
560
673
|
function registerAdapters() {
|
|
561
674
|
const opencode = new OpenCodeAdapter();
|
|
562
675
|
const claude = new ClaudeAdapter();
|
|
563
676
|
const vscode = new VSCodeAdapter();
|
|
677
|
+
const openclaw = new OpenClawAdapter();
|
|
678
|
+
const hermes = new HermesAdapter();
|
|
564
679
|
adapters.set(opencode.id, opencode);
|
|
565
680
|
adapters.set(claude.id, claude);
|
|
566
681
|
adapters.set(vscode.id, vscode);
|
|
682
|
+
adapters.set(openclaw.id, openclaw);
|
|
683
|
+
adapters.set(hermes.id, hermes);
|
|
567
684
|
}
|
|
568
685
|
registerAdapters();
|
|
569
686
|
async function detectPlatforms() {
|
|
@@ -584,8 +701,10 @@ function getAdapterByPlatform(platform) {
|
|
|
584
701
|
// Cursor uses OpenCode-compatible structure
|
|
585
702
|
codex: "opencode",
|
|
586
703
|
// Codex uses OpenCode-compatible structure
|
|
587
|
-
antigravity: "opencode"
|
|
704
|
+
antigravity: "opencode",
|
|
588
705
|
// Antigravity uses OpenCode-compatible structure
|
|
706
|
+
openclaw: "openclaw",
|
|
707
|
+
hermes: "hermes"
|
|
589
708
|
};
|
|
590
709
|
return adapters.get(idMap[platform]);
|
|
591
710
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { HermesAdapter } from './hermes.js';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
|
|
6
|
+
describe('HermesAdapter', () => {
|
|
7
|
+
let adapter: HermesAdapter;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
adapter = new HermesAdapter();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should have id "hermes"', () => {
|
|
14
|
+
expect(adapter.id).toBe('hermes');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should have name "Hermes Agent"', () => {
|
|
18
|
+
expect(adapter.name).toBe('Hermes Agent');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should have correct skillDir', () => {
|
|
22
|
+
expect(adapter.skillDir).toBe(join(homedir(), '.hermes', 'skills'));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should check availability based on ~/.hermes/ existence', async () => {
|
|
26
|
+
const result = await adapter.isAvailable();
|
|
27
|
+
expect(typeof result).toBe('boolean');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should check if skill is installed', async () => {
|
|
31
|
+
const result = await adapter.isInstalled('test-skill');
|
|
32
|
+
expect(typeof result).toBe('boolean');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should list installed skills', async () => {
|
|
36
|
+
const result = await adapter.listInstalled();
|
|
37
|
+
expect(Array.isArray(result)).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermes Agent Platform Adapter
|
|
3
|
+
*
|
|
4
|
+
* Installs skills to ~/.hermes/skills/
|
|
5
|
+
* Hermes also supports project-level skills/ directory
|
|
6
|
+
* Hermes uses AgentSkills-compatible SKILL.md format with metadata.hermes
|
|
7
|
+
*/
|
|
8
|
+
import { PlatformAdapter } from './base.js';
|
|
9
|
+
import { readdirSync, existsSync, cpSync, rmSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { homedir } from 'os';
|
|
12
|
+
import { ensureDirSync } from 'fs-extra';
|
|
13
|
+
|
|
14
|
+
export class HermesAdapter implements PlatformAdapter {
|
|
15
|
+
readonly id = 'hermes';
|
|
16
|
+
readonly name = 'Hermes Agent';
|
|
17
|
+
readonly skillDir = join(homedir(), '.hermes', 'skills');
|
|
18
|
+
|
|
19
|
+
async isAvailable(): Promise<boolean> {
|
|
20
|
+
try {
|
|
21
|
+
// Check global hermes directory
|
|
22
|
+
if (existsSync(join(homedir(), '.hermes'))) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Could also check for project-level skills/ directory
|
|
27
|
+
// but for SkillMarket, we focus on global installation
|
|
28
|
+
return false;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async isInstalled(skillId: string): Promise<boolean> {
|
|
35
|
+
try {
|
|
36
|
+
const skillPath = join(this.skillDir, skillId);
|
|
37
|
+
return existsSync(skillPath);
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async install(skillId: string, sourceDir: string): Promise<void> {
|
|
44
|
+
ensureDirSync(this.skillDir);
|
|
45
|
+
const targetDir = join(this.skillDir, skillId);
|
|
46
|
+
|
|
47
|
+
// Remove existing skill directory if present
|
|
48
|
+
if (existsSync(targetDir)) {
|
|
49
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Copy entire skill directory (SKILL.md + supporting files)
|
|
53
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async uninstall(skillId: string): Promise<void> {
|
|
57
|
+
const targetDir = join(this.skillDir, skillId);
|
|
58
|
+
if (existsSync(targetDir)) {
|
|
59
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async listInstalled(): Promise<string[]> {
|
|
64
|
+
try {
|
|
65
|
+
if (!existsSync(this.skillDir)) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
return readdirSync(this.skillDir)
|
|
69
|
+
.filter(name => {
|
|
70
|
+
const fullPath = join(this.skillDir, name);
|
|
71
|
+
return existsSync(fullPath) && name !== '.';
|
|
72
|
+
});
|
|
73
|
+
} catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/adapters/index.ts
CHANGED
|
@@ -6,4 +6,6 @@ export { BaseAdapter } from './base.js';
|
|
|
6
6
|
export { OpenCodeAdapter } from './opencode.js';
|
|
7
7
|
export { ClaudeAdapter } from './claude.js';
|
|
8
8
|
export { VSCodeAdapter } from './vscode.js';
|
|
9
|
+
export { OpenClawAdapter } from './openclaw.js';
|
|
10
|
+
export { HermesAdapter } from './hermes.js';
|
|
9
11
|
export { detectPlatforms, getPlatformAdapter, getAllAdapters, getAdapterByPlatform } from './registry.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { OpenClawAdapter } from './openclaw.js';
|
|
3
|
+
import { readdirSync, existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
|
|
7
|
+
describe('OpenClawAdapter', () => {
|
|
8
|
+
let adapter: OpenClawAdapter;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
adapter = new OpenClawAdapter();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should have id "openclaw"', () => {
|
|
15
|
+
expect(adapter.id).toBe('openclaw');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should have name "OpenClaw"', () => {
|
|
19
|
+
expect(adapter.name).toBe('OpenClaw');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should have correct skillDir', () => {
|
|
23
|
+
expect(adapter.skillDir).toBe(join(homedir(), '.openclaw', 'skills'));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should check availability based on ~/.openclaw/ existence', async () => {
|
|
27
|
+
const result = await adapter.isAvailable();
|
|
28
|
+
expect(typeof result).toBe('boolean');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should check if skill is installed', async () => {
|
|
32
|
+
const result = await adapter.isInstalled('test-skill');
|
|
33
|
+
expect(typeof result).toBe('boolean');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should list installed skills', async () => {
|
|
37
|
+
const result = await adapter.listInstalled();
|
|
38
|
+
expect(Array.isArray(result)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Platform Adapter
|
|
3
|
+
*
|
|
4
|
+
* Installs skills to ~/.openclaw/skills/
|
|
5
|
+
* OpenClaw uses AgentSkills-compatible SKILL.md format
|
|
6
|
+
*/
|
|
7
|
+
import { PlatformAdapter } from './base.js';
|
|
8
|
+
import { readdirSync, existsSync, cpSync, rmSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { ensureDirSync } from 'fs-extra';
|
|
12
|
+
|
|
13
|
+
export class OpenClawAdapter implements PlatformAdapter {
|
|
14
|
+
readonly id = 'openclaw';
|
|
15
|
+
readonly name = 'OpenClaw';
|
|
16
|
+
readonly skillDir = join(homedir(), '.openclaw', 'skills');
|
|
17
|
+
|
|
18
|
+
async isAvailable(): Promise<boolean> {
|
|
19
|
+
try {
|
|
20
|
+
return existsSync(join(homedir(), '.openclaw'));
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async isInstalled(skillId: string): Promise<boolean> {
|
|
27
|
+
try {
|
|
28
|
+
const skillPath = join(this.skillDir, skillId);
|
|
29
|
+
return existsSync(skillPath);
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async install(skillId: string, sourceDir: string): Promise<void> {
|
|
36
|
+
ensureDirSync(this.skillDir);
|
|
37
|
+
const targetDir = join(this.skillDir, skillId);
|
|
38
|
+
|
|
39
|
+
// Remove existing skill directory if present
|
|
40
|
+
if (existsSync(targetDir)) {
|
|
41
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Copy entire skill directory (SKILL.md + supporting files)
|
|
45
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async uninstall(skillId: string): Promise<void> {
|
|
49
|
+
const targetDir = join(this.skillDir, skillId);
|
|
50
|
+
if (existsSync(targetDir)) {
|
|
51
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async listInstalled(): Promise<string[]> {
|
|
56
|
+
try {
|
|
57
|
+
if (!existsSync(this.skillDir)) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
return readdirSync(this.skillDir)
|
|
61
|
+
.filter(name => {
|
|
62
|
+
const fullPath = join(this.skillDir, name);
|
|
63
|
+
return existsSync(fullPath) && name !== '.';
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getAdapterByPlatform } from './registry.js';
|
|
3
|
+
import { OpenClawAdapter } from './openclaw.js';
|
|
4
|
+
import { HermesAdapter } from './hermes.js';
|
|
5
|
+
|
|
6
|
+
describe('getAdapterByPlatform', () => {
|
|
7
|
+
it('should return OpenClawAdapter for "openclaw"', () => {
|
|
8
|
+
const adapter = getAdapterByPlatform('openclaw');
|
|
9
|
+
expect(adapter.id).toBe('openclaw');
|
|
10
|
+
expect(adapter.name).toBe('OpenClaw');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return HermesAdapter for "hermes"', () => {
|
|
14
|
+
const adapter = getAdapterByPlatform('hermes');
|
|
15
|
+
expect(adapter.id).toBe('hermes');
|
|
16
|
+
expect(adapter.name).toBe('Hermes Agent');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should return adapter for "opencode"', () => {
|
|
20
|
+
const adapter = getAdapterByPlatform('opencode');
|
|
21
|
+
expect(adapter).toBeDefined();
|
|
22
|
+
expect(adapter.id).toBe('opencode');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return undefined for unknown platform', () => {
|
|
26
|
+
const adapter = getAdapterByPlatform('unknown' as any);
|
|
27
|
+
expect(adapter).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
});
|
package/src/adapters/registry.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { OpenCodeAdapter } from './opencode.js';
|
|
11
11
|
import { ClaudeAdapter } from './claude.js';
|
|
12
12
|
import { VSCodeAdapter } from './vscode.js';
|
|
13
|
+
import { OpenClawAdapter } from './openclaw.js';
|
|
14
|
+
import { HermesAdapter } from './hermes.js';
|
|
13
15
|
import type { PlatformAdapter } from '../types.js';
|
|
14
16
|
import type { Platform } from '../constants.js';
|
|
15
17
|
|
|
@@ -22,10 +24,14 @@ function registerAdapters(): void {
|
|
|
22
24
|
const opencode = new OpenCodeAdapter();
|
|
23
25
|
const claude = new ClaudeAdapter();
|
|
24
26
|
const vscode = new VSCodeAdapter();
|
|
27
|
+
const openclaw = new OpenClawAdapter();
|
|
28
|
+
const hermes = new HermesAdapter();
|
|
25
29
|
|
|
26
30
|
adapters.set(opencode.id, opencode);
|
|
27
31
|
adapters.set(claude.id, claude);
|
|
28
32
|
adapters.set(vscode.id, vscode);
|
|
33
|
+
adapters.set(openclaw.id, openclaw);
|
|
34
|
+
adapters.set(hermes.id, hermes);
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
// Register adapters on module load
|
|
@@ -71,6 +77,8 @@ export function getAdapterByPlatform(platform: Platform): PlatformAdapter | unde
|
|
|
71
77
|
cursor: 'opencode', // Cursor uses OpenCode-compatible structure
|
|
72
78
|
codex: 'opencode', // Codex uses OpenCode-compatible structure
|
|
73
79
|
antigravity: 'opencode', // Antigravity uses OpenCode-compatible structure
|
|
80
|
+
openclaw: 'openclaw',
|
|
81
|
+
hermes: 'hermes',
|
|
74
82
|
};
|
|
75
83
|
|
|
76
84
|
return adapters.get(idMap[platform]);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PLATFORMS } from './constants.js';
|
|
3
|
+
|
|
4
|
+
describe('PLATFORMS', () => {
|
|
5
|
+
it('should include openclaw', () => {
|
|
6
|
+
expect(PLATFORMS).toContain('openclaw');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should include hermes', () => {
|
|
10
|
+
expect(PLATFORMS).toContain('hermes');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should maintain existing platforms', () => {
|
|
14
|
+
expect(PLATFORMS).toContain('opencode');
|
|
15
|
+
expect(PLATFORMS).toContain('claude');
|
|
16
|
+
expect(PLATFORMS).toContain('vscode');
|
|
17
|
+
});
|
|
18
|
+
});
|
package/src/constants.ts
CHANGED
|
@@ -81,7 +81,9 @@ export const PLATFORMS = [
|
|
|
81
81
|
'codex', // OpenAI Codex - OpenAI 代码生成模型
|
|
82
82
|
'opencode', // OpenCode - 开源 AI 编程工具
|
|
83
83
|
'claude', // Claude Code - Anthropic CLI 工具
|
|
84
|
-
'antigravity' // Antigravity - AI 编程助手
|
|
84
|
+
'antigravity', // Antigravity - AI 编程助手
|
|
85
|
+
'openclaw', // OpenClaw - AgentSkills compatible agent
|
|
86
|
+
'hermes', // Hermes Agent - NousResearch agent framework
|
|
85
87
|
] as const;
|
|
86
88
|
|
|
87
89
|
/**
|