itismyskillmarket 1.3.3 → 1.3.5

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 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() {
@@ -575,6 +692,9 @@ async function detectPlatforms() {
575
692
  }
576
693
  return available;
577
694
  }
695
+ function getAllAdapters() {
696
+ return Array.from(adapters.values());
697
+ }
578
698
  function getAdapterByPlatform(platform) {
579
699
  const idMap = {
580
700
  opencode: "opencode",
@@ -584,8 +704,10 @@ function getAdapterByPlatform(platform) {
584
704
  // Cursor uses OpenCode-compatible structure
585
705
  codex: "opencode",
586
706
  // Codex uses OpenCode-compatible structure
587
- antigravity: "opencode"
707
+ antigravity: "opencode",
588
708
  // Antigravity uses OpenCode-compatible structure
709
+ openclaw: "openclaw",
710
+ hermes: "hermes"
589
711
  };
590
712
  return adapters.get(idMap[platform]);
591
713
  }
@@ -1391,19 +1513,15 @@ var platformsCmd = program.command("platforms").description("Show available plat
1391
1513
  platformsCmd.action(async () => {
1392
1514
  try {
1393
1515
  const available = await detectPlatforms();
1516
+ const allAdapters = getAllAdapters();
1394
1517
  console.log("\n\u{1F4CD} Available Platforms:\n");
1395
- const allPlatforms = [
1396
- { name: "OpenCode", adapter: new OpenCodeAdapter() },
1397
- { name: "Claude Code", adapter: new ClaudeAdapter() },
1398
- { name: "VSCode", adapter: new VSCodeAdapter() }
1399
- ];
1400
- for (const { name, adapter } of allPlatforms) {
1518
+ for (const adapter of allAdapters) {
1401
1519
  const isAvailable = available.find((a) => a.id === adapter.id);
1402
1520
  const installed = await adapter.listInstalled();
1403
1521
  if (isAvailable) {
1404
- console.log(`${name.padEnd(12)} \u2705 Available (${installed.length} skills installed)`);
1522
+ console.log(`${adapter.name.padEnd(15)} \u2705 Available (${installed.length} skills installed)`);
1405
1523
  } else {
1406
- console.log(`${name.padEnd(12)} \u274C Not detected`);
1524
+ console.log(`${adapter.name.padEnd(15)} \u274C Not detected`);
1407
1525
  }
1408
1526
  }
1409
1527
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
+ }
@@ -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
+ });
@@ -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]);
package/src/cli.ts CHANGED
@@ -49,7 +49,7 @@ import { syncPlatformLinks } from './commands/sync.js'; // 同步命令
49
49
  import { updateSkill } from './commands/update.js'; // 更新命令
50
50
  import { uninstallSkill, uninstallAll } from './commands/uninstall.js'; // 卸载命令
51
51
  import { installFromGitHub, parseGitHubUrl } from './commands/github-install.js'; // GitHub 安装
52
- import { detectPlatforms, getAllAdapters, OpenCodeAdapter, ClaudeAdapter, VSCodeAdapter } from './adapters/index.js'; // 平台适配器
52
+ import { detectPlatforms, getAllAdapters } from './adapters/index.js'; // 平台适配器
53
53
 
54
54
  // -----------------------------------------------------------------------------
55
55
  // 创建命令程序实例
@@ -417,23 +417,18 @@ platformsCmd
417
417
  .action(async () => {
418
418
  try {
419
419
  const available = await detectPlatforms();
420
+ const allAdapters = getAllAdapters();
420
421
 
421
422
  console.log('\n📍 Available Platforms:\n');
422
423
 
423
- const allPlatforms = [
424
- { name: 'OpenCode', adapter: new OpenCodeAdapter() },
425
- { name: 'Claude Code', adapter: new ClaudeAdapter() },
426
- { name: 'VSCode', adapter: new VSCodeAdapter() },
427
- ];
428
-
429
- for (const { name, adapter } of allPlatforms) {
424
+ for (const adapter of allAdapters) {
430
425
  const isAvailable = available.find(a => a.id === adapter.id);
431
426
  const installed = await adapter.listInstalled();
432
427
 
433
428
  if (isAvailable) {
434
- console.log(`${name.padEnd(12)} ✅ Available (${installed.length} skills installed)`);
429
+ console.log(`${adapter.name.padEnd(15)} ✅ Available (${installed.length} skills installed)`);
435
430
  } else {
436
- console.log(`${name.padEnd(12)} ❌ Not detected`);
431
+ console.log(`${adapter.name.padEnd(15)} ❌ Not detected`);
437
432
  }
438
433
  }
439
434
 
@@ -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
  /**