itismyskillmarket 1.3.0 → 1.3.2

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.
Files changed (49) hide show
  1. package/.github/workflows/publish-npm.yml +59 -0
  2. package/.github/workflows/publish-skill.yml +72 -0
  3. package/5e51cb7aa8b8e60d49d86f4689f5d4d1.png +0 -0
  4. package/CHANGELOG.md +410 -0
  5. package/DEVELOPMENT.md +376 -0
  6. package/README.md +75 -6
  7. package/SKILLMARKET-GUIDE.md +288 -0
  8. package/dist/index.js +733 -212
  9. package/docs/WEEKLY-UPDATE-2026-04-23.md +43 -0
  10. package/docs/plans/2026-04-01-skillmarket-design.md +267 -0
  11. package/docs/plans/2026-04-01-skillmarket-implementation.md +1031 -0
  12. package/docs/plans/2026-04-15-cross-platform-adapter-design.md +416 -0
  13. package/docs/plans/2026-04-15-cross-platform-adapter-plan.md +833 -0
  14. package/docs/plans/2026-04-16-keyword-search-design.md +143 -0
  15. package/docs/plans/2026-04-29-weekly-update.md +57 -0
  16. package/package.json +1 -6
  17. package/skills/README.md +54 -0
  18. package/skills/test-skill/SKILL.md +25 -0
  19. package/skills/test-skill/index.js +66 -0
  20. package/skills/test-skill/metadata.json +9 -0
  21. package/skills/test-skill/package.json +19 -0
  22. package/skills/test-skill-1/SKILL.md +24 -0
  23. package/skills/test-skill-1/index.js +13 -0
  24. package/skills/test-skill-1/metadata.json +9 -0
  25. package/skills/test-skill-1/package.json +16 -0
  26. package/skills/test-skill-2/SKILL.md +25 -0
  27. package/skills/test-skill-2/index.js +13 -0
  28. package/skills/test-skill-2/metadata.json +9 -0
  29. package/skills/test-skill-2/package.json +16 -0
  30. package/src/adapters/base.ts +87 -0
  31. package/src/adapters/claude.ts +31 -0
  32. package/src/adapters/index.ts +9 -0
  33. package/src/adapters/opencode.ts +40 -0
  34. package/src/adapters/registry.ts +77 -0
  35. package/src/adapters/vscode.ts +62 -0
  36. package/src/cli.ts +189 -75
  37. package/src/commands/info.ts +4 -15
  38. package/src/commands/install.ts +93 -54
  39. package/src/commands/ls.ts +182 -17
  40. package/src/commands/npm.ts +118 -16
  41. package/src/commands/search.ts +12 -7
  42. package/src/commands/sync.ts +6 -27
  43. package/src/commands/uninstall.ts +313 -15
  44. package/src/commands/update.ts +2 -2
  45. package/src/index.ts +27 -0
  46. package/src/types.ts +35 -0
  47. package/tsconfig.json +10 -0
  48. package/tsup.config.ts +22 -0
  49. package/wanxuchen-skillmarket-1.0.1.tgz +0 -0
package/dist/index.js CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
+ import { readFileSync } from "fs";
6
+ import { fileURLToPath } from "url";
7
+ import { dirname, resolve } from "path";
8
+
9
+ // src/commands/registry.ts
10
+ import fs2 from "fs-extra";
11
+
12
+ // src/utils/dirs.ts
13
+ import os from "os";
14
+ import path from "path";
15
+ import fs from "fs-extra";
5
16
 
6
17
  // src/constants.ts
7
18
  var MARKET_DIR = "skillmarket";
@@ -30,13 +41,7 @@ var PLATFORMS = [
30
41
  var REGISTRY_FILE = "registry.json";
31
42
  var LATEST_LINK = "latest";
32
43
 
33
- // src/commands/registry.ts
34
- import fs2 from "fs-extra";
35
-
36
44
  // src/utils/dirs.ts
37
- import os from "os";
38
- import path from "path";
39
- import fs from "fs-extra";
40
45
  function getMarketHome() {
41
46
  return path.join(os.homedir(), MARKET_DIR);
42
47
  }
@@ -95,7 +100,7 @@ async function isSkillInstalled(skillId) {
95
100
  import https from "https";
96
101
  import { URL } from "url";
97
102
  async function fetchNpmPackage(packageName) {
98
- return new Promise((resolve, reject) => {
103
+ return new Promise((resolve2, reject) => {
99
104
  const isScoped = packageName.startsWith("@");
100
105
  let encodedName;
101
106
  if (isScoped) {
@@ -121,12 +126,12 @@ async function fetchNpmPackage(packageName) {
121
126
  try {
122
127
  const parsed = JSON.parse(data);
123
128
  if (parsed.error) {
124
- resolve(null);
129
+ resolve2(null);
125
130
  return;
126
131
  }
127
- resolve(parsed);
132
+ resolve2(parsed);
128
133
  } catch {
129
- resolve(null);
134
+ resolve2(null);
130
135
  }
131
136
  });
132
137
  });
@@ -137,12 +142,46 @@ async function fetchNpmPackage(packageName) {
137
142
  });
138
143
  });
139
144
  }
140
- async function searchSkillmarketPackages() {
145
+ var SKILL_SCOPES = [
146
+ "@wanxuchen",
147
+ // 原作者 scope
148
+ "@itismyskillmarket",
149
+ // 当前包名 scope
150
+ "@thisisskillmarket",
151
+ // 曾用 scope
152
+ "@this-is-skillmarket",
153
+ // 曾用 scope (带横线)
154
+ "@skillmarket"
155
+ // 通用 scope
156
+ ];
157
+ function getPossiblePackageNames(skillId) {
158
+ if (skillId.startsWith("@")) {
159
+ return [skillId];
160
+ }
161
+ return SKILL_SCOPES.map((scope) => `${scope}/${skillId}`);
162
+ }
163
+ async function fetchSkillPackage(skillId) {
164
+ const packageNames = getPossiblePackageNames(skillId);
165
+ for (const packageName of packageNames) {
166
+ try {
167
+ const info = await fetchNpmPackage(packageName);
168
+ if (info) {
169
+ return info;
170
+ }
171
+ } catch {
172
+ }
173
+ }
174
+ return null;
175
+ }
176
+ async function searchSkillmarketPackages(options = {}) {
177
+ const { from = 0, size = 100, keyword } = options;
141
178
  const packages = [];
142
- return new Promise((resolve, reject) => {
179
+ let total = 0;
180
+ return new Promise((resolve2, reject) => {
143
181
  const url = new URL("https://registry.npmjs.org/-/v1/search");
144
182
  url.searchParams.set("text", "keywords:skillmarket");
145
- url.searchParams.set("size", "100");
183
+ url.searchParams.set("size", String(Math.max(size, 100)));
184
+ url.searchParams.set("from", String(from));
146
185
  const req = https.get(url.toString(), { timeout: 1e4 }, (res) => {
147
186
  let data = "";
148
187
  res.on("data", (chunk) => {
@@ -151,14 +190,32 @@ async function searchSkillmarketPackages() {
151
190
  res.on("end", () => {
152
191
  try {
153
192
  const result = JSON.parse(data);
154
- for (const item of result.objects || []) {
155
- if (item?.package?.name) {
156
- packages.push(item.package.name);
193
+ let filteredTotal = 0;
194
+ if (result.objects) {
195
+ for (const item of result.objects) {
196
+ if (item?.package?.name) {
197
+ const pkgName = item.package.name;
198
+ const pkgDesc = item.package.description || "";
199
+ if (keyword) {
200
+ const lowerKeyword = keyword.toLowerCase();
201
+ const shortName = pkgName.includes("/") ? pkgName.split("/")[1] : pkgName;
202
+ const nameMatch = shortName.toLowerCase().includes(lowerKeyword) || pkgName.toLowerCase().includes(lowerKeyword);
203
+ const descMatch = pkgDesc.toLowerCase().includes(lowerKeyword);
204
+ if (nameMatch || descMatch) {
205
+ packages.push(pkgName);
206
+ filteredTotal++;
207
+ }
208
+ } else {
209
+ packages.push(pkgName);
210
+ filteredTotal++;
211
+ }
212
+ }
157
213
  }
158
214
  }
159
- resolve(packages);
215
+ total = filteredTotal;
216
+ resolve2({ packages, total });
160
217
  } catch {
161
- resolve([]);
218
+ resolve2({ packages: [], total: 0 });
162
219
  }
163
220
  });
164
221
  });
@@ -171,33 +228,128 @@ async function searchSkillmarketPackages() {
171
228
  }
172
229
 
173
230
  // src/commands/ls.ts
231
+ function filterInstalledSkills(skills, keyword) {
232
+ const lower = keyword.toLowerCase();
233
+ return skills.filter(
234
+ (s) => s.id.toLowerCase().includes(lower) || s.displayName && s.displayName.toLowerCase().includes(lower) || s.description && s.description.toLowerCase().includes(lower)
235
+ );
236
+ }
174
237
  async function listSkills(options) {
175
- const { installed, updates } = options;
238
+ const { installed, updates, page = 1, limit = 20, search } = options;
176
239
  if (installed) {
177
- const skills = await getInstalledSkills();
240
+ let skills = await getInstalledSkills();
241
+ if (search) {
242
+ skills = filterInstalledSkills(skills, search);
243
+ }
244
+ const total = skills.length;
245
+ const totalPages = Math.ceil(total / limit) || 1;
246
+ const currentPage = Math.min(Math.max(1, page), totalPages);
178
247
  if (skills.length === 0) {
179
- console.log('No skills installed yet. Run "skm --ls" to see available skills.');
248
+ if (search) {
249
+ console.log(`No skills found matching "${search}".`);
250
+ } else {
251
+ console.log('No skills installed yet. Run "skm ls" to see available skills.');
252
+ }
180
253
  return;
181
254
  }
182
- console.log("Installed Skills:\n");
183
- for (const skill of skills) {
255
+ if (search) {
256
+ console.log(`Found ${total} match(es) for "${search}":
257
+ `);
258
+ } else {
259
+ console.log(`Installed Skills (${total}):
260
+ `);
261
+ }
262
+ const start = (currentPage - 1) * limit;
263
+ const end = Math.min(start + limit, total);
264
+ const pageSkills = skills.slice(start, end);
265
+ for (const skill of pageSkills) {
184
266
  console.log(` ${skill.id}@${skill.version}`);
185
267
  console.log(` Platforms: ${skill.platforms.join(", ")}`);
186
268
  console.log(` Installed: ${skill.installedAt}`);
187
269
  console.log();
188
270
  }
271
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
189
272
  return;
190
273
  }
191
- console.log("Searching npm registry...\n");
274
+ if (search) {
275
+ console.log(`Searching npm for "${search}"...
276
+ `);
277
+ } else {
278
+ console.log("Searching npm registry...\n");
279
+ }
192
280
  try {
193
- const packages = await searchSkillmarketPackages();
281
+ const offset = (page - 1) * limit;
282
+ const { packages, total } = await searchSkillmarketPackages({
283
+ from: offset,
284
+ size: limit,
285
+ keyword: search
286
+ });
287
+ const totalPages = Math.ceil(total / limit) || 1;
288
+ const currentPage = Math.min(Math.max(1, page), totalPages);
194
289
  if (packages.length === 0) {
195
- console.log("No skills found. Check back later!");
290
+ if (search) {
291
+ console.log(`No skills found matching "${search}".`);
292
+ } else {
293
+ console.log("No skills found. Check back later!");
294
+ }
196
295
  return;
197
296
  }
198
- console.log(`Found ${packages.length} skill(s):
297
+ if (search) {
298
+ console.log(`Found ${total} match(es) for "${search}":
199
299
  `);
300
+ } else {
301
+ console.log(`Found ${total} skill(s):
302
+ `);
303
+ }
200
304
  for (const pkgName of packages) {
305
+ try {
306
+ const info = await fetchNpmPackage(pkgName);
307
+ if (!info) {
308
+ console.log(`\u{1F4E6} ${pkgName} (\u4FE1\u606F\u83B7\u53D6\u5931\u8D25)`);
309
+ console.log();
310
+ continue;
311
+ }
312
+ const latestVersion = info["dist-tags"]?.latest || "unknown";
313
+ const pkg = info.versions?.[latestVersion];
314
+ const skillMeta = pkg?.skillmarket;
315
+ console.log(`\u{1F4E6} ${info.name}@${latestVersion}`);
316
+ const displayName = skillMeta?.displayName || info.name;
317
+ console.log(` \u540D\u79F0: ${displayName}`);
318
+ console.log(` \u63CF\u8FF0: ${pkg?.description || "N/A"}`);
319
+ const platforms = skillMeta?.platforms || [];
320
+ console.log(` \u5E73\u53F0: ${platforms.length > 0 ? platforms.join(", ") : "N/A"}`);
321
+ const npmLink = pkg?.links?.npm || `https://www.npmjs.com/package/${info.name}`;
322
+ console.log(` \u94FE\u63A5: ${npmLink}`);
323
+ console.log();
324
+ } catch (e) {
325
+ console.log(`\u{1F4E6} ${pkgName} (\u83B7\u53D6\u5931\u8D25: ${e})`);
326
+ console.log();
327
+ }
328
+ }
329
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
330
+ } catch (error) {
331
+ console.log(`Error fetching skills: ${error}`);
332
+ }
333
+ }
334
+
335
+ // src/commands/search.ts
336
+ async function searchSkills(keyword, options = {}) {
337
+ const limit = options.limit ?? 20;
338
+ console.log(`Searching for "${keyword}"...
339
+ `);
340
+ try {
341
+ const { packages } = await searchSkillmarketPackages({ keyword });
342
+ const filtered = packages.filter(
343
+ (pkg) => pkg.toLowerCase().includes(keyword.toLowerCase())
344
+ );
345
+ if (filtered.length === 0) {
346
+ console.log(`No skills found matching "${keyword}".`);
347
+ return;
348
+ }
349
+ const results = filtered.slice(0, limit);
350
+ console.log(`Found ${filtered.length} skill(s) (showing ${results.length}):
351
+ `);
352
+ for (const pkgName of results) {
201
353
  const info = await fetchNpmPackage(pkgName);
202
354
  if (info && info["dist-tags"]?.latest) {
203
355
  const latestVersion = info["dist-tags"].latest;
@@ -208,17 +360,16 @@ async function listSkills(options) {
208
360
  }
209
361
  }
210
362
  } catch (error) {
211
- console.log(`Error fetching skills: ${error}`);
363
+ console.log(`Error searching skills: ${error}`);
212
364
  }
213
365
  }
214
366
 
215
367
  // src/commands/info.ts
216
368
  async function showSkillInfo(skillId) {
217
- const packageName = skillId.startsWith("@") ? skillId : `@skillmarket/${skillId}`;
218
- console.log(`Fetching info for: ${packageName}
369
+ console.log(`Fetching info for: ${skillId}
219
370
  `);
220
371
  try {
221
- const info = await fetchNpmPackage(packageName);
372
+ const info = await fetchSkillPackage(skillId);
222
373
  if (!info) {
223
374
  console.log(`Skill "${skillId}" not found in npm registry.`);
224
375
  return;
@@ -258,174 +409,344 @@ Status: Not installed (use skm install ${skillId} to install)`);
258
409
  }
259
410
 
260
411
  // src/commands/install.ts
261
- import fs4 from "fs-extra";
262
- import path3 from "path";
412
+ import fs7 from "fs-extra";
413
+ import path6 from "path";
263
414
  import { exec } from "child_process";
264
415
  import { promisify } from "util";
265
416
 
266
- // src/utils/platform.ts
267
- function detectPlatform() {
268
- if (process.env.OPENCODE) return "opencode";
269
- if (process.env.CURSOR) return "cursor";
270
- if (process.env.VSCODE) return "vscode";
271
- if (process.env.CLAUDE_CODE) return "claude";
272
- if (process.env.ANTIGRAVITY) return "antigravity";
273
- return "codex";
274
- }
275
- function isValidPlatform(name) {
276
- return PLATFORMS.includes(name);
277
- }
278
- function getPlatformFromInput(name) {
279
- const lowerName = name.toLowerCase();
280
- if (isValidPlatform(lowerName)) {
281
- return lowerName;
282
- }
283
- return null;
284
- }
285
-
286
- // src/commands/sync.ts
417
+ // src/adapters/base.ts
287
418
  import fs3 from "fs-extra";
288
419
  import path2 from "path";
289
- async function syncPlatformLinks(targetPlatform) {
290
- await ensureMarketDirs();
291
- const skillsDir = getSkillsDir();
292
- const platformLinksDir = getPlatformLinksDir();
293
- const registry = await loadRegistry();
294
- let platformsToSync;
295
- if (targetPlatform) {
296
- const parsed = getPlatformFromInput(targetPlatform);
297
- if (!parsed) {
298
- console.warn(`Invalid platform: ${targetPlatform}, syncing all platforms`);
299
- platformsToSync = PLATFORMS;
300
- } else {
301
- platformsToSync = [parsed];
420
+ var BaseAdapter = class {
421
+ /**
422
+ * Get the path where a specific skill should be installed
423
+ */
424
+ getSkillPath(skillId) {
425
+ return path2.join(this.skillDir, skillId);
426
+ }
427
+ /**
428
+ * Get the path to the SKILL.md file for a skill
429
+ */
430
+ getSkillFilePath(skillId) {
431
+ return path2.join(this.getSkillPath(skillId), "SKILL.md");
432
+ }
433
+ async isAvailable() {
434
+ try {
435
+ await fs3.ensureDir(this.skillDir);
436
+ return true;
437
+ } catch {
438
+ return false;
302
439
  }
303
- } else {
304
- platformsToSync = PLATFORMS;
305
440
  }
306
- const targetDesc = targetPlatform ? targetPlatform : "all platforms";
307
- console.log(`Syncing platform links to ${targetDesc}...
308
- `);
309
- for (const platform of platformsToSync) {
310
- const platformDir = path2.join(platformLinksDir, platform, "skills");
311
- await fs3.ensureDir(platformDir);
312
- for (const [skillId, skillInfo] of Object.entries(registry.skills)) {
313
- const skillLatestLink = path2.join(skillsDir, skillId, LATEST_LINK);
314
- const targetPlatformDir = path2.join(skillLatestLink, platform);
315
- const platformSkillDir = path2.join(platformDir, skillId);
316
- if (await fs3.pathExists(skillLatestLink)) {
317
- if (await fs3.pathExists(targetPlatformDir)) {
318
- try {
319
- await fs3.remove(platformSkillDir);
320
- await fs3.symlink(targetPlatformDir, platformSkillDir, "junction");
321
- console.log(` Linked: ${platform}/${skillId}`);
322
- } catch {
323
- await fs3.copy(targetPlatformDir, platformSkillDir, { overwrite: true });
324
- console.log(` Copied: ${platform}/${skillId}`);
325
- }
441
+ async isInstalled(skillId) {
442
+ const skillFile = this.getSkillFilePath(skillId);
443
+ return fs3.pathExists(skillFile);
444
+ }
445
+ async install(skillId, sourceDir) {
446
+ const targetDir = this.getSkillPath(skillId);
447
+ const targetFile = this.getSkillFilePath(skillId);
448
+ await fs3.ensureDir(targetDir);
449
+ const sourceFile = path2.join(sourceDir, "SKILL.md");
450
+ if (!await fs3.pathExists(sourceFile)) {
451
+ throw new Error(`SKILL.md not found in ${sourceDir}`);
452
+ }
453
+ await fs3.copy(sourceFile, targetFile, { overwrite: true });
454
+ }
455
+ async uninstall(skillId) {
456
+ const targetDir = this.getSkillPath(skillId);
457
+ if (await fs3.pathExists(targetDir)) {
458
+ await fs3.remove(targetDir);
459
+ }
460
+ }
461
+ async listInstalled() {
462
+ if (!await fs3.pathExists(this.skillDir)) {
463
+ return [];
464
+ }
465
+ const entries = await fs3.readdir(this.skillDir, { withFileTypes: true });
466
+ const skills = [];
467
+ for (const entry of entries) {
468
+ if (entry.isDirectory()) {
469
+ const skillFile = path2.join(this.skillDir, entry.name, "SKILL.md");
470
+ if (await fs3.pathExists(skillFile)) {
471
+ skills.push(entry.name);
326
472
  }
327
473
  }
328
474
  }
475
+ return skills;
329
476
  }
330
- console.log("\n\u2705 Sync complete!");
477
+ };
478
+
479
+ // src/adapters/opencode.ts
480
+ import path3 from "path";
481
+ import os2 from "os";
482
+ import fs4 from "fs-extra";
483
+ var OpenCodeAdapter = class extends BaseAdapter {
484
+ id = "opencode";
485
+ name = "OpenCode";
486
+ get skillDir() {
487
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os2.homedir(), ".config", "opencode");
488
+ return path3.join(configDir, "skills");
489
+ }
490
+ async isAvailable() {
491
+ if (process.env.OPENCODE) return true;
492
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os2.homedir(), ".config", "opencode");
493
+ try {
494
+ await fs4.ensureDir(path3.join(configDir, "skills"));
495
+ return true;
496
+ } catch {
497
+ return false;
498
+ }
499
+ }
500
+ };
501
+
502
+ // src/adapters/claude.ts
503
+ import path4 from "path";
504
+ import os3 from "os";
505
+ import fs5 from "fs-extra";
506
+ var ClaudeAdapter = class extends BaseAdapter {
507
+ id = "claude";
508
+ name = "Claude Code";
509
+ get skillDir() {
510
+ return path4.join(os3.homedir(), ".claude", "skills");
511
+ }
512
+ async isAvailable() {
513
+ if (process.env.CLAUDE_CODE) return true;
514
+ const claudeDir = path4.join(os3.homedir(), ".claude");
515
+ return fs5.pathExists(claudeDir);
516
+ }
517
+ };
518
+
519
+ // src/adapters/vscode.ts
520
+ import path5 from "path";
521
+ import os4 from "os";
522
+ import fs6 from "fs-extra";
523
+ var VSCodeAdapter = class extends BaseAdapter {
524
+ id = "vscode";
525
+ name = "VSCode";
526
+ get skillDir() {
527
+ return path5.join(os4.homedir(), ".copilot", "skills");
528
+ }
529
+ async isAvailable() {
530
+ const possibleDirs = [
531
+ path5.join(os4.homedir(), ".copilot", "skills"),
532
+ path5.join(os4.homedir(), ".claude", "skills")
533
+ ];
534
+ for (const dir of possibleDirs) {
535
+ try {
536
+ await fs6.ensureDir(dir);
537
+ return true;
538
+ } catch {
539
+ continue;
540
+ }
541
+ }
542
+ return false;
543
+ }
544
+ async install(skillId, sourceDir) {
545
+ await super.install(skillId, sourceDir);
546
+ const claudeSkillDir = path5.join(os4.homedir(), ".claude", "skills");
547
+ const targetPath = this.getSkillPath(skillId);
548
+ const claudeTargetPath = path5.join(claudeSkillDir, skillId);
549
+ try {
550
+ await fs6.ensureDir(claudeSkillDir);
551
+ await fs6.remove(claudeTargetPath);
552
+ await fs6.symlink(targetPath, claudeTargetPath, "junction");
553
+ } catch {
554
+ }
555
+ }
556
+ };
557
+
558
+ // src/adapters/registry.ts
559
+ var adapters = /* @__PURE__ */ new Map();
560
+ function registerAdapters() {
561
+ const opencode = new OpenCodeAdapter();
562
+ const claude = new ClaudeAdapter();
563
+ const vscode = new VSCodeAdapter();
564
+ adapters.set(opencode.id, opencode);
565
+ adapters.set(claude.id, claude);
566
+ adapters.set(vscode.id, vscode);
567
+ }
568
+ registerAdapters();
569
+ async function detectPlatforms() {
570
+ const available = [];
571
+ for (const adapter of adapters.values()) {
572
+ if (await adapter.isAvailable()) {
573
+ available.push(adapter);
574
+ }
575
+ }
576
+ return available;
577
+ }
578
+ function getAdapterByPlatform(platform) {
579
+ const idMap = {
580
+ opencode: "opencode",
581
+ claude: "claude",
582
+ vscode: "vscode",
583
+ cursor: "opencode",
584
+ // Cursor uses OpenCode-compatible structure
585
+ codex: "opencode",
586
+ // Codex uses OpenCode-compatible structure
587
+ antigravity: "opencode"
588
+ // Antigravity uses OpenCode-compatible structure
589
+ };
590
+ return adapters.get(idMap[platform]);
331
591
  }
332
592
 
333
593
  // src/commands/install.ts
334
594
  var execAsync = promisify(exec);
335
- async function installSkill(skillId, version, targetPlatform) {
595
+ async function installSkill(skillId, version, options) {
336
596
  await ensureMarketDirs();
337
- const isScoped = skillId.startsWith("@");
338
- const packageName = isScoped ? skillId : `@skillmarket/${skillId}`;
339
- const shortName = skillId;
340
- console.log(`Installing ${packageName}${version ? `@${version}` : ""}...`);
341
- const pkgInfo = await fetchNpmPackage(packageName);
597
+ console.log(`Installing ${skillId}${version ? `@${version}` : ""}...`);
598
+ const pkgInfo = await fetchSkillPackage(skillId);
342
599
  if (!pkgInfo) {
343
- throw new Error(`Package ${packageName} not found`);
600
+ throw new Error(`Package ${skillId} not found`);
344
601
  }
602
+ const packageName = pkgInfo.name;
345
603
  const targetVersion = version || pkgInfo["dist-tags"]?.latest;
346
604
  if (!targetVersion) {
347
605
  throw new Error(`No version found for ${packageName}`);
348
606
  }
349
607
  const cacheDir = getCacheDir();
350
- const targetDir = path3.join(cacheDir, `${packageName}@${targetVersion}`);
351
- if (!await fs4.pathExists(targetDir)) {
608
+ const targetDir = path6.join(cacheDir, `${packageName}@${targetVersion}`);
609
+ if (!await fs7.pathExists(targetDir)) {
352
610
  console.log("Downloading package...");
353
- await fs4.ensureDir(cacheDir);
611
+ await fs7.ensureDir(cacheDir);
354
612
  try {
355
613
  await execAsync(`npm pack ${packageName}@${targetVersion} --pack-destination ${cacheDir}`);
356
- const files = await fs4.readdir(cacheDir);
614
+ const files = await fs7.readdir(cacheDir);
357
615
  const tarball = files.find(
358
- (f) => f.endsWith(".tgz") && f.includes(packageName.replace("/", "-"))
616
+ (f) => f.endsWith(".tgz") && f.includes(packageName.replace(/^@/, "").replace("/", "-"))
359
617
  );
360
618
  if (tarball) {
361
- await execAsync(`tar -xzf "${path3.join(cacheDir, tarball)}" -C "${cacheDir}"`);
362
- await fs4.remove(path3.join(cacheDir, tarball));
363
- const extractedDir = path3.join(cacheDir, "package");
619
+ await execAsync(`tar -xzf "${path6.join(cacheDir, tarball)}" -C "${cacheDir}"`);
620
+ await fs7.remove(path6.join(cacheDir, tarball));
621
+ const extractedDir = path6.join(cacheDir, "package");
364
622
  const finalDir = targetDir;
365
- await fs4.move(extractedDir, finalDir, { overwrite: true });
623
+ await fs7.move(extractedDir, finalDir, { overwrite: true });
366
624
  }
367
625
  } catch (err) {
368
626
  throw new Error(`Failed to download package: ${err}`);
369
627
  }
370
628
  }
371
629
  const skillsDir = getSkillsDir();
372
- const skillVersionDir = path3.join(skillsDir, `${skillId}@${targetVersion}`);
630
+ const skillVersionDir = path6.join(skillsDir, `${skillId}@${targetVersion}`);
373
631
  console.log("Setting up skill...");
374
- await fs4.ensureDir(skillVersionDir);
632
+ await fs7.ensureDir(skillVersionDir);
375
633
  const pkgRoot = targetDir;
376
- if (await fs4.pathExists(path3.join(pkgRoot, "SKILL.md"))) {
377
- await fs4.copy(
378
- path3.join(pkgRoot, "SKILL.md"),
379
- path3.join(skillVersionDir, "SKILL.md")
634
+ if (await fs7.pathExists(path6.join(pkgRoot, "SKILL.md"))) {
635
+ await fs7.copy(
636
+ path6.join(pkgRoot, "SKILL.md"),
637
+ path6.join(skillVersionDir, "SKILL.md")
380
638
  );
381
639
  }
382
- if (await fs4.pathExists(path3.join(pkgRoot, "metadata.json"))) {
383
- await fs4.copy(
384
- path3.join(pkgRoot, "metadata.json"),
385
- path3.join(skillVersionDir, "metadata.json")
640
+ if (await fs7.pathExists(path6.join(pkgRoot, "metadata.json"))) {
641
+ await fs7.copy(
642
+ path6.join(pkgRoot, "metadata.json"),
643
+ path6.join(skillVersionDir, "metadata.json")
386
644
  );
387
645
  }
388
- const skillDir = path3.join(skillsDir, skillId);
389
- await fs4.ensureDir(skillDir);
390
- const latestLink = path3.join(skillDir, LATEST_LINK);
646
+ const skillDir = path6.join(skillsDir, skillId);
647
+ await fs7.ensureDir(skillDir);
648
+ const latestLink = path6.join(skillDir, LATEST_LINK);
391
649
  try {
392
- await fs4.remove(latestLink);
393
- await fs4.symlink(skillVersionDir, latestLink, "junction");
650
+ await fs7.remove(latestLink);
651
+ await fs7.symlink(skillVersionDir, latestLink, "junction");
394
652
  } catch {
395
- await fs4.copy(skillVersionDir, path3.join(skillDir, LATEST_LINK), { overwrite: true });
653
+ await fs7.copy(skillVersionDir, path6.join(skillDir, LATEST_LINK), { overwrite: true });
396
654
  }
397
- let finalPlatform;
398
- if (targetPlatform) {
399
- const parsed = getPlatformFromInput(targetPlatform);
400
- if (!parsed) {
401
- throw new Error(`Invalid platform: ${targetPlatform}. Valid platforms: ${PLATFORMS.join(", ")}`);
655
+ let targetAdapters = [];
656
+ if (options?.platforms && options.platforms.length > 0) {
657
+ for (const platformStr of options.platforms) {
658
+ const platform = platformStr;
659
+ const adapter = getAdapterByPlatform(platform);
660
+ if (adapter) {
661
+ targetAdapters.push(adapter);
662
+ } else {
663
+ console.warn(`\u26A0\uFE0F Unknown platform: ${platformStr}`);
664
+ }
402
665
  }
403
- finalPlatform = parsed;
404
666
  } else {
405
- finalPlatform = detectPlatform();
667
+ targetAdapters = await detectPlatforms();
668
+ }
669
+ if (targetAdapters.length === 0) {
670
+ console.log("No target platforms detected.");
671
+ console.log("Use --platform to specify platforms manually.");
672
+ } else {
673
+ console.log(`
674
+ Installing to ${targetAdapters.length} platform(s)...
675
+ `);
676
+ const results = [];
677
+ for (const adapter of targetAdapters) {
678
+ try {
679
+ const isInstalled = await adapter.isInstalled(skillId);
680
+ if (isInstalled && !options?.force) {
681
+ console.log(`${adapter.name.padEnd(12)} \u26A0\uFE0F Already installed (use --force to overwrite)`);
682
+ results.push({ name: adapter.name, status: "skipped" });
683
+ continue;
684
+ }
685
+ await adapter.install(skillId, skillVersionDir);
686
+ console.log(`${adapter.name.padEnd(12)} \u2705 Installed successfully`);
687
+ results.push({ name: adapter.name, status: "installed" });
688
+ } catch (error) {
689
+ console.log(`${adapter.name.padEnd(12)} \u274C Failed: ${error}`);
690
+ results.push({ name: adapter.name, status: "failed", error: String(error) });
691
+ }
692
+ }
693
+ const installed = results.filter((r) => r.status === "installed").length;
694
+ const skipped = results.filter((r) => r.status === "skipped").length;
695
+ const failed = results.filter((r) => r.status === "failed").length;
696
+ console.log(`
697
+ \u{1F4CA} Summary: ${installed} installed, ${skipped} skipped, ${failed} failed`);
406
698
  }
407
- console.log(`Target platform: ${finalPlatform}`);
408
699
  const registry = await loadRegistry();
409
- const existingSkill = registry.skills[skillId];
410
- const existingPlatforms = existingSkill?.platforms || [];
700
+ const installedPlatforms = targetAdapters.map((a) => a.id);
411
701
  registry.skills[skillId] = {
412
702
  id: skillId,
413
703
  version: targetVersion,
414
704
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
415
- platforms: existingPlatforms.includes(finalPlatform) ? existingPlatforms : [...existingPlatforms, finalPlatform]
705
+ platforms: installedPlatforms
416
706
  };
417
707
  await saveRegistry(registry);
418
- console.log(`Syncing to ${finalPlatform}...`);
419
- await syncPlatformLinks(finalPlatform);
420
708
  console.log(`
421
709
  \u2705 ${skillId}@${targetVersion} installed successfully!`);
422
710
  console.log(` Use "skm info ${skillId}" for more details`);
423
711
  }
424
712
 
713
+ // src/commands/sync.ts
714
+ import fs8 from "fs-extra";
715
+ import path7 from "path";
716
+ async function syncPlatformLinks() {
717
+ await ensureMarketDirs();
718
+ const skillsDir = getSkillsDir();
719
+ const platformLinksDir = getPlatformLinksDir();
720
+ const registry = await loadRegistry();
721
+ console.log("Syncing platform links...\n");
722
+ for (const platform of PLATFORMS) {
723
+ const platformDir = path7.join(platformLinksDir, platform, "skills");
724
+ await fs8.ensureDir(platformDir);
725
+ for (const [skillId, skillInfo] of Object.entries(registry.skills)) {
726
+ const skillLatestLink = path7.join(skillsDir, skillId, LATEST_LINK);
727
+ const targetPlatformDir = path7.join(skillLatestLink, platform);
728
+ const platformSkillDir = path7.join(platformDir, skillId);
729
+ if (await fs8.pathExists(skillLatestLink)) {
730
+ if (await fs8.pathExists(targetPlatformDir)) {
731
+ try {
732
+ await fs8.remove(platformSkillDir);
733
+ await fs8.symlink(targetPlatformDir, platformSkillDir, "junction");
734
+ console.log(` Linked: ${platform}/${skillId}`);
735
+ } catch {
736
+ await fs8.copy(targetPlatformDir, platformSkillDir, { overwrite: true });
737
+ console.log(` Copied: ${platform}/${skillId}`);
738
+ }
739
+ }
740
+ }
741
+ }
742
+ }
743
+ console.log("\n\u2705 Sync complete!");
744
+ }
745
+
425
746
  // src/commands/update.ts
426
747
  async function updateSkill(skillId) {
427
748
  if (skillId) {
428
- const pkgInfo = await fetchNpmPackage(`@skillmarket/${skillId}`);
749
+ const pkgInfo = await fetchNpmPackage(`@itismyskillmarket/${skillId}`);
429
750
  if (pkgInfo) {
430
751
  const latestVersion = pkgInfo["dist-tags"]?.latest;
431
752
  console.log(`Updating ${skillId} to ${latestVersion}...`);
@@ -442,7 +763,7 @@ async function updateSkill(skillId) {
442
763
  `);
443
764
  let hasUpdates = false;
444
765
  for (const skill of installed) {
445
- const pkgInfo = await fetchNpmPackage(`@skillmarket/${skill.id}`);
766
+ const pkgInfo = await fetchNpmPackage(`@wanxuchen/${skill.id}`);
446
767
  if (pkgInfo) {
447
768
  const latestVersion = pkgInfo["dist-tags"]?.latest;
448
769
  if (latestVersion && latestVersion !== skill.version) {
@@ -464,118 +785,306 @@ async function updateSkill(skillId) {
464
785
  }
465
786
 
466
787
  // src/commands/uninstall.ts
467
- import fs5 from "fs-extra";
468
- import path4 from "path";
469
- async function uninstallSkill(skillId) {
788
+ import fs9 from "fs-extra";
789
+ import path8 from "path";
790
+ import readline from "readline";
791
+ async function askConfirmation(message) {
792
+ const rl = readline.createInterface({
793
+ input: process.stdin,
794
+ output: process.stdout
795
+ });
796
+ return new Promise((resolve2) => {
797
+ rl.question(`${message} (y/N): `, (answer) => {
798
+ rl.close();
799
+ resolve2(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
800
+ });
801
+ });
802
+ }
803
+ async function getUninstallPreview(skillId, options) {
804
+ const registry = await loadRegistry();
805
+ const skillInfo = registry.skills[skillId];
806
+ let platformNames = [];
807
+ if (options?.platforms && options.platforms.length > 0) {
808
+ platformNames = options.platforms;
809
+ } else {
810
+ const adapters2 = await detectPlatforms();
811
+ platformNames = adapters2.map((a) => a.name);
812
+ }
813
+ const skillsDir = getSkillsDir();
814
+ const localPath = path8.join(skillsDir, skillId);
815
+ const platformLinksDir = getPlatformLinksDir();
816
+ const platformLinks = [];
817
+ for (const platform of PLATFORMS) {
818
+ const linkPath = path8.join(platformLinksDir, platform, "skills", skillId);
819
+ if (await fs9.pathExists(linkPath)) {
820
+ platformLinks.push(linkPath);
821
+ }
822
+ }
823
+ return { skillInfo, platforms: platformNames, localPath, platformLinks };
824
+ }
825
+ async function uninstallSkill(skillId, options) {
470
826
  const registry = await loadRegistry();
471
827
  if (!(skillId in registry.skills)) {
472
- console.log(`Skill "${skillId}" is not installed.`);
473
- return;
828
+ console.log(`\u274C Skill "${skillId}" is not installed.`);
829
+ return false;
474
830
  }
475
831
  const skillInfo = registry.skills[skillId];
476
- console.log(`Uninstalling ${skillId}@${skillInfo.version}...`);
832
+ if (options?.dryRun) {
833
+ const preview = await getUninstallPreview(skillId, options);
834
+ console.log(`
835
+ \u{1F4CB} Uninstall Preview for "${skillId}":`);
836
+ console.log(` Version: ${skillInfo.version}`);
837
+ console.log(` Installed: ${skillInfo.installedAt}`);
838
+ console.log(` Platforms (from registry): ${preview.platforms.join(", ") || "none"}`);
839
+ console.log(`
840
+ Local files to remove:`);
841
+ console.log(` - ${preview.localPath}`);
842
+ if (preview.platformLinks.length > 0) {
843
+ console.log(`
844
+ Platform links to remove:`);
845
+ for (const link of preview.platformLinks) {
846
+ console.log(` - ${link}`);
847
+ }
848
+ }
849
+ console.log(`
850
+ \u26A0\uFE0F This was a dry-run. No files were actually deleted.`);
851
+ return true;
852
+ }
853
+ if (!options?.yes) {
854
+ const confirmed = await askConfirmation(`Are you sure you want to uninstall "${skillId}"?`);
855
+ if (!confirmed) {
856
+ console.log("Uninstall cancelled.");
857
+ return false;
858
+ }
859
+ }
860
+ console.log(`
861
+ Uninstalling ${skillId}@${skillInfo.version}...`);
862
+ let targetAdapters = [];
863
+ let platformUninstallErrors = [];
864
+ if (options?.platforms && options.platforms.length > 0) {
865
+ for (const platformStr of options.platforms) {
866
+ const platform = platformStr;
867
+ targetAdapters.push(getAdapterByPlatform(platform));
868
+ }
869
+ } else {
870
+ targetAdapters = await detectPlatforms();
871
+ }
872
+ const validAdapters = targetAdapters.filter((a) => a !== void 0);
873
+ if (validAdapters.length > 0) {
874
+ console.log(`
875
+ Uninstalling from ${validAdapters.length} platform(s)...
876
+ `);
877
+ for (const adapter of validAdapters) {
878
+ try {
879
+ await adapter.uninstall(skillId);
880
+ console.log(`${adapter.name.padEnd(12)} \u2705 Uninstalled`);
881
+ } catch (error) {
882
+ const errorMsg = error instanceof Error ? error.message : String(error);
883
+ console.log(`${adapter.name.padEnd(12)} \u274C Failed: ${errorMsg}`);
884
+ platformUninstallErrors.push({ name: adapter.name, error: errorMsg });
885
+ }
886
+ }
887
+ }
888
+ if (platformUninstallErrors.length > 0 && !options?.yes) {
889
+ const continueAnyway = await askConfirmation(
890
+ `\u26A0\uFE0F ${platformUninstallErrors.length} platform(s) failed to uninstall. Continue with local cleanup?`
891
+ );
892
+ if (!continueAnyway) {
893
+ console.log("Uninstall aborted. Platform files may still exist.");
894
+ return false;
895
+ }
896
+ }
477
897
  const skillsDir = getSkillsDir();
478
- const skillDir = path4.join(skillsDir, skillId);
479
- await fs5.remove(skillDir);
898
+ const skillDir = path8.join(skillsDir, skillId);
899
+ if (await fs9.pathExists(skillDir)) {
900
+ await fs9.remove(skillDir);
901
+ console.log(`\u2705 Removed local files: ${skillDir}`);
902
+ }
480
903
  const platformLinksDir = getPlatformLinksDir();
904
+ let removedLinks = 0;
481
905
  for (const platform of PLATFORMS) {
482
- const linkPath = path4.join(platformLinksDir, platform, "skills", skillId);
483
- if (await fs5.pathExists(linkPath)) {
484
- await fs5.remove(linkPath);
906
+ const linkPath = path8.join(platformLinksDir, platform, "skills", skillId);
907
+ if (await fs9.pathExists(linkPath)) {
908
+ await fs9.remove(linkPath);
909
+ removedLinks++;
485
910
  }
486
911
  }
912
+ if (removedLinks > 0) {
913
+ console.log(`\u2705 Removed ${removedLinks} platform link(s)`);
914
+ }
487
915
  delete registry.skills[skillId];
488
916
  await saveRegistry(registry);
917
+ console.log(`\u2705 Registry updated`);
489
918
  console.log(`
490
919
  \u2705 ${skillId} uninstalled successfully!`);
920
+ return true;
491
921
  }
492
-
493
- // src/commands/search.ts
494
- async function searchSkills(keyword) {
495
- console.log(`Searching for "${keyword}"...
496
- `);
497
- try {
498
- const packages = await searchSkillmarketPackages();
499
- const filtered = packages.filter(
500
- (pkg) => pkg.toLowerCase().includes(keyword.toLowerCase())
922
+ async function uninstallAll(options) {
923
+ const registry = await loadRegistry();
924
+ const installedSkills = Object.keys(registry.skills);
925
+ if (installedSkills.length === 0) {
926
+ console.log("No skills installed.");
927
+ return { success: 0, failed: 0 };
928
+ }
929
+ console.log(`
930
+ Found ${installedSkills.length} installed skill(s):`);
931
+ for (const skillId of installedSkills) {
932
+ const info = registry.skills[skillId];
933
+ console.log(` - ${skillId}@${info.version}`);
934
+ }
935
+ if (options?.dryRun) {
936
+ console.log(`
937
+ \u{1F4CB} Dry-run: Would uninstall ${installedSkills.length} skill(s).`);
938
+ console.log(`\u26A0\uFE0F No files were actually deleted.`);
939
+ return { success: installedSkills.length, failed: 0 };
940
+ }
941
+ if (!options?.yes) {
942
+ const confirmed = await askConfirmation(
943
+ `
944
+ \u26A0\uFE0F Are you sure you want to uninstall ALL ${installedSkills.length} skill(s)? This action cannot be undone.`
501
945
  );
502
- if (filtered.length === 0) {
503
- console.log(`No skills found matching "${keyword}".`);
504
- return;
946
+ if (!confirmed) {
947
+ console.log("Uninstall cancelled.");
948
+ return { success: 0, failed: 0 };
505
949
  }
506
- console.log(`Found ${filtered.length} skill(s):
950
+ }
951
+ console.log(`
952
+ Uninstalling all skills...
507
953
  `);
508
- for (const pkgName of filtered) {
509
- const info = await fetchNpmPackage(pkgName);
510
- if (info && info["dist-tags"]?.latest) {
511
- const latestVersion = info["dist-tags"].latest;
512
- const pkg = info.versions?.[latestVersion];
513
- console.log(` ${info.name}@${latestVersion}`);
514
- console.log(` ${pkg?.description || "No description"}`);
515
- console.log();
954
+ let successCount = 0;
955
+ let failedCount = 0;
956
+ for (const skillId of installedSkills) {
957
+ try {
958
+ const success = await uninstallSkill(skillId, { ...options, yes: true });
959
+ if (success) {
960
+ successCount++;
961
+ } else {
962
+ failedCount++;
516
963
  }
964
+ } catch (error) {
965
+ console.log(`\u274C Failed to uninstall ${skillId}: ${error}`);
966
+ failedCount++;
517
967
  }
518
- } catch (error) {
519
- console.log(`Error searching skills: ${error}`);
520
968
  }
969
+ console.log(`
970
+ \u{1F4CA} Summary:`);
971
+ console.log(` \u2705 Success: ${successCount}`);
972
+ console.log(` \u274C Failed: ${failedCount}`);
973
+ console.log(` \u{1F4E6} Total: ${installedSkills.length}`);
974
+ return { success: successCount, failed: failedCount };
521
975
  }
522
976
 
523
977
  // src/cli.ts
978
+ var __filename = fileURLToPath(import.meta.url);
979
+ var __dirname = dirname(__filename);
980
+ var packageJson = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
981
+ var VERSION = packageJson.version || "1.3.1";
524
982
  var program = new Command();
525
- program.name("skm").description("SkillMarket - Cross-platform skill manager for AI coding tools").version("1.2.10");
526
- var helpCmd = program.command("help").description("Display help information");
527
- helpCmd.action(() => {
528
- console.log(`
983
+ program.name("skm").description("SkillMarket - Cross-platform skill manager for AI coding tools").version(VERSION);
984
+ program.hook("preAction", (thisCommand) => {
985
+ if (thisCommand.opts().help) {
986
+ console.log(`
529
987
  SkillMarket CLI
530
988
 
531
989
  Usage: skm <command> [options]
532
990
 
533
991
  Commands:
534
- --help, -h Display this help message
535
- --ls [options] List available skills
536
- --installed Show only installed skills
537
- --updates Check for updates
538
- --info <skill-id> Display skill information
539
- --install <skill> Install a skill (e.g., skm --install brainstorming)
540
- @version Install specific version
541
- --all Install all available skills
542
- --uninstall <skill> Remove an installed skill
543
- --update [options] Update skills
544
- --all Update all skills
545
- --sync Synchronize platform links
546
- --platform <name> Set target platform (${PLATFORMS.join(", ")})
992
+ ls [options] List available skills
993
+ --installed Show only installed skills
994
+ --updates Check for updates
995
+ --page <n> Page number (default: 1)
996
+ --limit <n> Items per page (default: 20)
997
+ -s, --search Search by keyword
998
+ info <skill-id> Display skill information
999
+ install <skill> Install a skill
1000
+ @version Install specific version
1001
+ --platform Target platforms (opencode,claude,vscode)
1002
+ --force Overwrite if already installed
1003
+ uninstall <skill> Remove an installed skill
1004
+ --platform Target platforms
1005
+ --all Uninstall ALL installed skills
1006
+ --dry-run Preview without deleting
1007
+ -y, --yes Skip confirmation
1008
+ update [options] Update skills
1009
+ --all Update all skills
1010
+ sync Synchronize platform links
1011
+ platforms Show available platforms
547
1012
 
548
1013
  Examples:
549
- skm --ls List all available skills
550
- skm --ls --installed Show installed skills only
551
- skm --info brainstorming View skill details
552
- skm --install brainstorming Install a skill
553
- skm --install brainstorming@1.0.0 Install specific version
554
- skm --update --all Update all installed skills
555
- skm --sync Sync platform links
556
- `);
1014
+ skm ls List all available skills (page 1)
1015
+ skm ls --page 2 Go to page 2
1016
+ skm ls --limit 10 Show 10 items per page
1017
+ skm ls --search brain Search skills by keyword
1018
+ skm ls -s brain Search with short form
1019
+ skm ls --installed Show installed skills only
1020
+ skm ls --installed --search test Search installed skills
1021
+ skm ls --installed --page 2
1022
+ skm info brainstorming View skill details
1023
+ skm install brainstorming Install to all platforms
1024
+ skm install brainstorming --platform opencode Install to OpenCode only
1025
+ skm install brainstorming --platform claude,vscode Install to multiple
1026
+ skm uninstall brainstorming
1027
+ skm uninstall --all Uninstall all skills (with confirmation)
1028
+ skm uninstall --all --yes Force uninstall all without confirmation
1029
+ skm uninstall brainstorming --dry-run Preview uninstall
1030
+ skm platforms Show available platforms
1031
+ `);
1032
+ process.exit(0);
1033
+ }
557
1034
  });
558
1035
  var lsCmd = program.command("ls").description("List available skills");
559
- lsCmd.option("--installed", "Show only installed skills").option("--updates", "Check for updates").action((opts) => {
560
- listSkills(opts);
1036
+ lsCmd.option("--installed", "Show only installed skills").option("--updates", "Check for updates").option("-p, --page <number>", "Page number (default: 1)", parseInt).option("-l, --limit <number>", "Items per page (default: 20)", parseInt).option("-s, --search <keyword>", "Search by keyword (id, displayName, description)").action((opts) => {
1037
+ const options = {
1038
+ ...opts,
1039
+ page: opts.page ?? 1,
1040
+ limit: opts.limit ?? 20,
1041
+ search: opts.search
1042
+ };
1043
+ listSkills(options);
1044
+ });
1045
+ var searchCmd = program.command("search").description("Search skills from npm registry");
1046
+ searchCmd.argument("<keyword>", "Keyword to search").option("-l, --limit <number>", "Max results to show (default: 20)", parseInt).action(async (keyword, opts) => {
1047
+ const limit = opts.limit ?? 20;
1048
+ await searchSkills(keyword, limit);
561
1049
  });
562
1050
  var infoCmd = program.command("info").description("Display skill information");
563
1051
  infoCmd.argument("<skill-id>", "Skill ID to show info").action((skillId) => {
564
1052
  showSkillInfo(skillId);
565
1053
  });
566
- var installCmd = program.command("install").description("Install a skill");
567
- installCmd.argument("<skill>", "Skill ID to install (e.g., brainstorming or @scope/name)").option("--all", "Install all available skills").option("-p, --platform <platform>", `Target platform (${PLATFORMS.join(", ")})`).action(async (skill, opts) => {
1054
+ var installCmd = program.command("install").description("Install a skill to local and platform directories");
1055
+ installCmd.argument("<skill>", "Skill ID to install (e.g., brainstorming or @scope/name)").option("-p, --platform <platforms>", "Target platforms (comma-separated: opencode,claude,vscode)").option("-f, --force", "Overwrite if already installed").option("-v, --version <version>", "Specific version to install").action(async (skill, opts) => {
568
1056
  try {
569
- await installSkill(skill, void 0, opts.platform);
1057
+ const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
1058
+ await installSkill(skill, opts.version, {
1059
+ platforms,
1060
+ force: opts.force
1061
+ });
570
1062
  } catch (err) {
571
1063
  console.error("Installation failed:", err);
572
1064
  process.exit(1);
573
1065
  }
574
1066
  });
575
- var uninstallCmd = program.command("uninstall").description("Remove an installed skill");
576
- uninstallCmd.argument("<skill>", "Skill ID to uninstall").action(async (skill) => {
1067
+ var uninstallCmd = program.command("uninstall").description("Remove an installed skill from local and platform directories");
1068
+ uninstallCmd.argument("[skill]", "Skill ID to uninstall (required unless using --all)").option("-p, --platform <platforms>", "Target platforms (comma-separated)").option("-a, --all", "Uninstall ALL installed skills (requires confirmation)").option("-d, --dry-run", "Preview what would be uninstalled without actually deleting").option("-y, --yes", "Skip confirmation prompts").action(async (skill, opts) => {
577
1069
  try {
578
- await uninstallSkill(skill);
1070
+ const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
1071
+ if (opts.all) {
1072
+ await uninstallAll({
1073
+ platforms,
1074
+ dryRun: opts.dryRun,
1075
+ yes: opts.yes
1076
+ });
1077
+ return;
1078
+ }
1079
+ if (!skill) {
1080
+ console.error("Error: Skill ID is required (or use --all to uninstall all)");
1081
+ process.exit(1);
1082
+ }
1083
+ await uninstallSkill(skill, {
1084
+ platforms,
1085
+ dryRun: opts.dryRun,
1086
+ yes: opts.yes
1087
+ });
579
1088
  } catch (err) {
580
1089
  console.error("Uninstall failed:", err);
581
1090
  process.exit(1);
@@ -602,16 +1111,28 @@ program.command("sync").description("Synchronize platform links").action(async (
602
1111
  process.exit(1);
603
1112
  }
604
1113
  });
605
- var platformCmd = program.command("platform").description("Set target platform");
606
- platformCmd.argument("<name>", "Platform name").action((name) => {
607
- console.log("Platform command - name:", name);
608
- });
609
- var searchCmd = program.command("search").description("Search skills from npm registry");
610
- searchCmd.argument("<keyword>", "Keyword to search").action(async (keyword) => {
1114
+ var platformsCmd = program.command("platforms").description("Show available platforms");
1115
+ platformsCmd.action(async () => {
611
1116
  try {
612
- await searchSkills(keyword);
1117
+ const available = await detectPlatforms();
1118
+ console.log("\n\u{1F4CD} Available Platforms:\n");
1119
+ const allPlatforms = [
1120
+ { name: "OpenCode", adapter: new OpenCodeAdapter() },
1121
+ { name: "Claude Code", adapter: new ClaudeAdapter() },
1122
+ { name: "VSCode", adapter: new VSCodeAdapter() }
1123
+ ];
1124
+ for (const { name, adapter } of allPlatforms) {
1125
+ const isAvailable = available.find((a) => a.id === adapter.id);
1126
+ const installed = await adapter.listInstalled();
1127
+ if (isAvailable) {
1128
+ console.log(`${name.padEnd(12)} \u2705 Available (${installed.length} skills installed)`);
1129
+ } else {
1130
+ console.log(`${name.padEnd(12)} \u274C Not detected`);
1131
+ }
1132
+ }
1133
+ console.log("");
613
1134
  } catch (err) {
614
- console.error("Search failed:", err);
1135
+ console.error("Failed to list platforms:", err);
615
1136
  process.exit(1);
616
1137
  }
617
1138
  });