itismyskillmarket 1.2.2 → 1.2.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.
Files changed (45) hide show
  1. package/.github/workflows/publish-npm.yml +54 -0
  2. package/.github/workflows/publish-skill.yml +72 -0
  3. package/5e51cb7aa8b8e60d49d86f4689f5d4d1.png +0 -0
  4. package/CHANGELOG.md +143 -0
  5. package/DEVELOPMENT.md +376 -0
  6. package/README.md +70 -4
  7. package/SKILLMARKET-GUIDE.md +277 -0
  8. package/dist/index.js +478 -177
  9. package/docs/plans/2026-04-01-skillmarket-design.md +267 -0
  10. package/docs/plans/2026-04-01-skillmarket-implementation.md +1031 -0
  11. package/docs/plans/2026-04-15-cross-platform-adapter-design.md +416 -0
  12. package/docs/plans/2026-04-15-cross-platform-adapter-plan.md +833 -0
  13. package/package.json +1 -6
  14. package/skills/README.md +52 -0
  15. package/skills/test-skill/SKILL.md +25 -0
  16. package/skills/test-skill/index.js +66 -0
  17. package/skills/test-skill/metadata.json +9 -0
  18. package/skills/test-skill/package.json +19 -0
  19. package/skills/test-skill-1/SKILL.md +24 -0
  20. package/skills/test-skill-1/index.js +13 -0
  21. package/skills/test-skill-1/metadata.json +9 -0
  22. package/skills/test-skill-1/package.json +16 -0
  23. package/skills/test-skill-2/SKILL.md +25 -0
  24. package/skills/test-skill-2/index.js +13 -0
  25. package/skills/test-skill-2/metadata.json +9 -0
  26. package/skills/test-skill-2/package.json +16 -0
  27. package/src/adapters/base.ts +87 -0
  28. package/src/adapters/claude.ts +31 -0
  29. package/src/adapters/index.ts +9 -0
  30. package/src/adapters/opencode.ts +40 -0
  31. package/src/adapters/registry.ts +77 -0
  32. package/src/adapters/vscode.ts +62 -0
  33. package/src/cli.ts +113 -49
  34. package/src/commands/info.ts +4 -15
  35. package/src/commands/install.ts +93 -54
  36. package/src/commands/ls.ts +69 -13
  37. package/src/commands/npm.ts +87 -12
  38. package/src/commands/sync.ts +6 -27
  39. package/src/commands/uninstall.ts +60 -7
  40. package/src/commands/update.ts +2 -2
  41. package/src/index.ts +27 -0
  42. package/src/types.ts +35 -0
  43. package/tsconfig.json +10 -0
  44. package/tsup.config.ts +22 -0
  45. package/wanxuchen-skillmarket-1.0.1.tgz +0 -0
package/dist/index.js CHANGED
@@ -3,6 +3,14 @@
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
5
 
6
+ // src/commands/registry.ts
7
+ import fs2 from "fs-extra";
8
+
9
+ // src/utils/dirs.ts
10
+ import os from "os";
11
+ import path from "path";
12
+ import fs from "fs-extra";
13
+
6
14
  // src/constants.ts
7
15
  var MARKET_DIR = "skillmarket";
8
16
  var SUBDIRS = {
@@ -30,13 +38,7 @@ var PLATFORMS = [
30
38
  var REGISTRY_FILE = "registry.json";
31
39
  var LATEST_LINK = "latest";
32
40
 
33
- // src/commands/registry.ts
34
- import fs2 from "fs-extra";
35
-
36
41
  // src/utils/dirs.ts
37
- import os from "os";
38
- import path from "path";
39
- import fs from "fs-extra";
40
42
  function getMarketHome() {
41
43
  return path.join(os.homedir(), MARKET_DIR);
42
44
  }
@@ -137,12 +139,46 @@ async function fetchNpmPackage(packageName) {
137
139
  });
138
140
  });
139
141
  }
140
- async function searchSkillmarketPackages() {
142
+ var SKILL_SCOPES = [
143
+ "@wanxuchen",
144
+ // 原作者 scope
145
+ "@itismyskillmarket",
146
+ // 当前包名 scope
147
+ "@thisisskillmarket",
148
+ // 曾用 scope
149
+ "@this-is-skillmarket",
150
+ // 曾用 scope (带横线)
151
+ "@skillmarket"
152
+ // 通用 scope
153
+ ];
154
+ function getPossiblePackageNames(skillId) {
155
+ if (skillId.startsWith("@")) {
156
+ return [skillId];
157
+ }
158
+ return SKILL_SCOPES.map((scope) => `${scope}/${skillId}`);
159
+ }
160
+ async function fetchSkillPackage(skillId) {
161
+ const packageNames = getPossiblePackageNames(skillId);
162
+ for (const packageName of packageNames) {
163
+ try {
164
+ const info = await fetchNpmPackage(packageName);
165
+ if (info) {
166
+ return info;
167
+ }
168
+ } catch {
169
+ }
170
+ }
171
+ return null;
172
+ }
173
+ async function searchSkillmarketPackages(options = {}) {
174
+ const { from = 0, size = 100 } = options;
141
175
  const packages = [];
176
+ let total = 0;
142
177
  return new Promise((resolve, reject) => {
143
178
  const url = new URL("https://registry.npmjs.org/-/v1/search");
144
179
  url.searchParams.set("text", "keywords:skillmarket");
145
- url.searchParams.set("size", "100");
180
+ url.searchParams.set("size", String(size));
181
+ url.searchParams.set("from", String(from));
146
182
  const req = https.get(url.toString(), { timeout: 1e4 }, (res) => {
147
183
  let data = "";
148
184
  res.on("data", (chunk) => {
@@ -151,14 +187,17 @@ async function searchSkillmarketPackages() {
151
187
  res.on("end", () => {
152
188
  try {
153
189
  const result = JSON.parse(data);
154
- for (const item of result.objects || []) {
155
- if (item?.package?.name) {
156
- packages.push(item.package.name);
190
+ total = result.total || 0;
191
+ if (result.objects) {
192
+ for (const item of result.objects) {
193
+ if (item?.package?.name) {
194
+ packages.push(item.package.name);
195
+ }
157
196
  }
158
197
  }
159
- resolve(packages);
198
+ resolve({ packages, total });
160
199
  } catch {
161
- resolve([]);
200
+ resolve({ packages: [], total: 0 });
162
201
  }
163
202
  });
164
203
  });
@@ -172,41 +211,71 @@ async function searchSkillmarketPackages() {
172
211
 
173
212
  // src/commands/ls.ts
174
213
  async function listSkills(options) {
175
- const { installed, updates } = options;
214
+ const { installed, updates, page = 1, limit = 20 } = options;
176
215
  if (installed) {
177
216
  const skills = await getInstalledSkills();
217
+ const total = skills.length;
218
+ const totalPages = Math.ceil(total / limit) || 1;
219
+ const currentPage = Math.min(Math.max(1, page), totalPages);
178
220
  if (skills.length === 0) {
179
- console.log('No skills installed yet. Run "skm --ls" to see available skills.');
221
+ console.log('No skills installed yet. Run "skm ls" to see available skills.');
180
222
  return;
181
223
  }
182
- console.log("Installed Skills:\n");
183
- for (const skill of skills) {
224
+ const start = (currentPage - 1) * limit;
225
+ const end = Math.min(start + limit, total);
226
+ const pageSkills = skills.slice(start, end);
227
+ console.log(`Installed Skills (${total}):
228
+ `);
229
+ for (const skill of pageSkills) {
184
230
  console.log(` ${skill.id}@${skill.version}`);
185
231
  console.log(` Platforms: ${skill.platforms.join(", ")}`);
186
232
  console.log(` Installed: ${skill.installedAt}`);
187
233
  console.log();
188
234
  }
235
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
189
236
  return;
190
237
  }
191
238
  console.log("Searching npm registry...\n");
192
239
  try {
193
- const packages = await searchSkillmarketPackages();
240
+ const offset = (page - 1) * limit;
241
+ const { packages, total } = await searchSkillmarketPackages({
242
+ from: offset,
243
+ size: limit
244
+ });
245
+ const totalPages = Math.ceil(total / limit) || 1;
246
+ const currentPage = Math.min(Math.max(1, page), totalPages);
194
247
  if (packages.length === 0) {
195
248
  console.log("No skills found. Check back later!");
196
249
  return;
197
250
  }
198
- console.log(`Found ${packages.length} skill(s):
251
+ console.log(`Found ${total} skill(s):
199
252
  `);
200
253
  for (const pkgName of packages) {
201
- const info = await fetchNpmPackage(pkgName);
202
- if (info && info["dist-tags"]?.latest) {
203
- const latestVersion = info["dist-tags"].latest;
254
+ try {
255
+ const info = await fetchNpmPackage(pkgName);
256
+ if (!info) {
257
+ console.log(`\u{1F4E6} ${pkgName} (\u4FE1\u606F\u83B7\u53D6\u5931\u8D25)`);
258
+ console.log();
259
+ continue;
260
+ }
261
+ const latestVersion = info["dist-tags"]?.latest || "unknown";
204
262
  const pkg = info.versions?.[latestVersion];
205
- console.log(` ${info.name}@${latestVersion}`);
206
- console.log(` ${pkg?.description || "No description"}`);
263
+ const skillMeta = pkg?.skillmarket;
264
+ console.log(`\u{1F4E6} ${info.name}@${latestVersion}`);
265
+ const displayName = skillMeta?.displayName || info.name;
266
+ console.log(` \u540D\u79F0: ${displayName}`);
267
+ console.log(` \u63CF\u8FF0: ${pkg?.description || "N/A"}`);
268
+ const platforms = skillMeta?.platforms || [];
269
+ console.log(` \u5E73\u53F0: ${platforms.length > 0 ? platforms.join(", ") : "N/A"}`);
270
+ const npmLink = pkg?.links?.npm || `https://www.npmjs.com/package/${info.name}`;
271
+ console.log(` \u94FE\u63A5: ${npmLink}`);
272
+ console.log();
273
+ } catch (e) {
274
+ console.log(`\u{1F4E6} ${pkgName} (\u83B7\u53D6\u5931\u8D25: ${e})`);
207
275
  console.log();
208
276
  }
209
277
  }
278
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
210
279
  } catch (error) {
211
280
  console.log(`Error fetching skills: ${error}`);
212
281
  }
@@ -214,11 +283,10 @@ async function listSkills(options) {
214
283
 
215
284
  // src/commands/info.ts
216
285
  async function showSkillInfo(skillId) {
217
- const packageName = skillId.startsWith("@") ? skillId : `@skillmarket/${skillId}`;
218
- console.log(`Fetching info for: ${packageName}
286
+ console.log(`Fetching info for: ${skillId}
219
287
  `);
220
288
  try {
221
- const info = await fetchNpmPackage(packageName);
289
+ const info = await fetchSkillPackage(skillId);
222
290
  if (!info) {
223
291
  console.log(`Skill "${skillId}" not found in npm registry.`);
224
292
  return;
@@ -258,174 +326,344 @@ Status: Not installed (use skm install ${skillId} to install)`);
258
326
  }
259
327
 
260
328
  // src/commands/install.ts
261
- import fs4 from "fs-extra";
262
- import path3 from "path";
329
+ import fs7 from "fs-extra";
330
+ import path6 from "path";
263
331
  import { exec } from "child_process";
264
332
  import { promisify } from "util";
265
333
 
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
334
+ // src/adapters/base.ts
287
335
  import fs3 from "fs-extra";
288
336
  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];
337
+ var BaseAdapter = class {
338
+ /**
339
+ * Get the path where a specific skill should be installed
340
+ */
341
+ getSkillPath(skillId) {
342
+ return path2.join(this.skillDir, skillId);
343
+ }
344
+ /**
345
+ * Get the path to the SKILL.md file for a skill
346
+ */
347
+ getSkillFilePath(skillId) {
348
+ return path2.join(this.getSkillPath(skillId), "SKILL.md");
349
+ }
350
+ async isAvailable() {
351
+ try {
352
+ await fs3.ensureDir(this.skillDir);
353
+ return true;
354
+ } catch {
355
+ return false;
302
356
  }
303
- } else {
304
- platformsToSync = PLATFORMS;
305
357
  }
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
- }
358
+ async isInstalled(skillId) {
359
+ const skillFile = this.getSkillFilePath(skillId);
360
+ return fs3.pathExists(skillFile);
361
+ }
362
+ async install(skillId, sourceDir) {
363
+ const targetDir = this.getSkillPath(skillId);
364
+ const targetFile = this.getSkillFilePath(skillId);
365
+ await fs3.ensureDir(targetDir);
366
+ const sourceFile = path2.join(sourceDir, "SKILL.md");
367
+ if (!await fs3.pathExists(sourceFile)) {
368
+ throw new Error(`SKILL.md not found in ${sourceDir}`);
369
+ }
370
+ await fs3.copy(sourceFile, targetFile, { overwrite: true });
371
+ }
372
+ async uninstall(skillId) {
373
+ const targetDir = this.getSkillPath(skillId);
374
+ if (await fs3.pathExists(targetDir)) {
375
+ await fs3.remove(targetDir);
376
+ }
377
+ }
378
+ async listInstalled() {
379
+ if (!await fs3.pathExists(this.skillDir)) {
380
+ return [];
381
+ }
382
+ const entries = await fs3.readdir(this.skillDir, { withFileTypes: true });
383
+ const skills = [];
384
+ for (const entry of entries) {
385
+ if (entry.isDirectory()) {
386
+ const skillFile = path2.join(this.skillDir, entry.name, "SKILL.md");
387
+ if (await fs3.pathExists(skillFile)) {
388
+ skills.push(entry.name);
326
389
  }
327
390
  }
328
391
  }
392
+ return skills;
329
393
  }
330
- console.log("\n\u2705 Sync complete!");
394
+ };
395
+
396
+ // src/adapters/opencode.ts
397
+ import path3 from "path";
398
+ import os2 from "os";
399
+ import fs4 from "fs-extra";
400
+ var OpenCodeAdapter = class extends BaseAdapter {
401
+ id = "opencode";
402
+ name = "OpenCode";
403
+ get skillDir() {
404
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os2.homedir(), ".config", "opencode");
405
+ return path3.join(configDir, "skills");
406
+ }
407
+ async isAvailable() {
408
+ if (process.env.OPENCODE) return true;
409
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os2.homedir(), ".config", "opencode");
410
+ try {
411
+ await fs4.ensureDir(path3.join(configDir, "skills"));
412
+ return true;
413
+ } catch {
414
+ return false;
415
+ }
416
+ }
417
+ };
418
+
419
+ // src/adapters/claude.ts
420
+ import path4 from "path";
421
+ import os3 from "os";
422
+ import fs5 from "fs-extra";
423
+ var ClaudeAdapter = class extends BaseAdapter {
424
+ id = "claude";
425
+ name = "Claude Code";
426
+ get skillDir() {
427
+ return path4.join(os3.homedir(), ".claude", "skills");
428
+ }
429
+ async isAvailable() {
430
+ if (process.env.CLAUDE_CODE) return true;
431
+ const claudeDir = path4.join(os3.homedir(), ".claude");
432
+ return fs5.pathExists(claudeDir);
433
+ }
434
+ };
435
+
436
+ // src/adapters/vscode.ts
437
+ import path5 from "path";
438
+ import os4 from "os";
439
+ import fs6 from "fs-extra";
440
+ var VSCodeAdapter = class extends BaseAdapter {
441
+ id = "vscode";
442
+ name = "VSCode";
443
+ get skillDir() {
444
+ return path5.join(os4.homedir(), ".copilot", "skills");
445
+ }
446
+ async isAvailable() {
447
+ const possibleDirs = [
448
+ path5.join(os4.homedir(), ".copilot", "skills"),
449
+ path5.join(os4.homedir(), ".claude", "skills")
450
+ ];
451
+ for (const dir of possibleDirs) {
452
+ try {
453
+ await fs6.ensureDir(dir);
454
+ return true;
455
+ } catch {
456
+ continue;
457
+ }
458
+ }
459
+ return false;
460
+ }
461
+ async install(skillId, sourceDir) {
462
+ await super.install(skillId, sourceDir);
463
+ const claudeSkillDir = path5.join(os4.homedir(), ".claude", "skills");
464
+ const targetPath = this.getSkillPath(skillId);
465
+ const claudeTargetPath = path5.join(claudeSkillDir, skillId);
466
+ try {
467
+ await fs6.ensureDir(claudeSkillDir);
468
+ await fs6.remove(claudeTargetPath);
469
+ await fs6.symlink(targetPath, claudeTargetPath, "junction");
470
+ } catch {
471
+ }
472
+ }
473
+ };
474
+
475
+ // src/adapters/registry.ts
476
+ var adapters = /* @__PURE__ */ new Map();
477
+ function registerAdapters() {
478
+ const opencode = new OpenCodeAdapter();
479
+ const claude = new ClaudeAdapter();
480
+ const vscode = new VSCodeAdapter();
481
+ adapters.set(opencode.id, opencode);
482
+ adapters.set(claude.id, claude);
483
+ adapters.set(vscode.id, vscode);
484
+ }
485
+ registerAdapters();
486
+ async function detectPlatforms() {
487
+ const available = [];
488
+ for (const adapter of adapters.values()) {
489
+ if (await adapter.isAvailable()) {
490
+ available.push(adapter);
491
+ }
492
+ }
493
+ return available;
494
+ }
495
+ function getAdapterByPlatform(platform) {
496
+ const idMap = {
497
+ opencode: "opencode",
498
+ claude: "claude",
499
+ vscode: "vscode",
500
+ cursor: "opencode",
501
+ // Cursor uses OpenCode-compatible structure
502
+ codex: "opencode",
503
+ // Codex uses OpenCode-compatible structure
504
+ antigravity: "opencode"
505
+ // Antigravity uses OpenCode-compatible structure
506
+ };
507
+ return adapters.get(idMap[platform]);
331
508
  }
332
509
 
333
510
  // src/commands/install.ts
334
511
  var execAsync = promisify(exec);
335
- async function installSkill(skillId, version, targetPlatform) {
512
+ async function installSkill(skillId, version, options) {
336
513
  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);
514
+ console.log(`Installing ${skillId}${version ? `@${version}` : ""}...`);
515
+ const pkgInfo = await fetchSkillPackage(skillId);
342
516
  if (!pkgInfo) {
343
- throw new Error(`Package ${packageName} not found`);
517
+ throw new Error(`Package ${skillId} not found`);
344
518
  }
519
+ const packageName = pkgInfo.name;
345
520
  const targetVersion = version || pkgInfo["dist-tags"]?.latest;
346
521
  if (!targetVersion) {
347
522
  throw new Error(`No version found for ${packageName}`);
348
523
  }
349
524
  const cacheDir = getCacheDir();
350
- const targetDir = path3.join(cacheDir, `${packageName}@${targetVersion}`);
351
- if (!await fs4.pathExists(targetDir)) {
525
+ const targetDir = path6.join(cacheDir, `${packageName}@${targetVersion}`);
526
+ if (!await fs7.pathExists(targetDir)) {
352
527
  console.log("Downloading package...");
353
- await fs4.ensureDir(cacheDir);
528
+ await fs7.ensureDir(cacheDir);
354
529
  try {
355
530
  await execAsync(`npm pack ${packageName}@${targetVersion} --pack-destination ${cacheDir}`);
356
- const files = await fs4.readdir(cacheDir);
531
+ const files = await fs7.readdir(cacheDir);
357
532
  const tarball = files.find(
358
- (f) => f.endsWith(".tgz") && f.includes(packageName.replace("/", "-"))
533
+ (f) => f.endsWith(".tgz") && f.includes(packageName.replace(/^@/, "").replace("/", "-"))
359
534
  );
360
535
  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");
536
+ await execAsync(`tar -xzf "${path6.join(cacheDir, tarball)}" -C "${cacheDir}"`);
537
+ await fs7.remove(path6.join(cacheDir, tarball));
538
+ const extractedDir = path6.join(cacheDir, "package");
364
539
  const finalDir = targetDir;
365
- await fs4.move(extractedDir, finalDir, { overwrite: true });
540
+ await fs7.move(extractedDir, finalDir, { overwrite: true });
366
541
  }
367
542
  } catch (err) {
368
543
  throw new Error(`Failed to download package: ${err}`);
369
544
  }
370
545
  }
371
546
  const skillsDir = getSkillsDir();
372
- const skillVersionDir = path3.join(skillsDir, `${skillId}@${targetVersion}`);
547
+ const skillVersionDir = path6.join(skillsDir, `${skillId}@${targetVersion}`);
373
548
  console.log("Setting up skill...");
374
- await fs4.ensureDir(skillVersionDir);
549
+ await fs7.ensureDir(skillVersionDir);
375
550
  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")
551
+ if (await fs7.pathExists(path6.join(pkgRoot, "SKILL.md"))) {
552
+ await fs7.copy(
553
+ path6.join(pkgRoot, "SKILL.md"),
554
+ path6.join(skillVersionDir, "SKILL.md")
380
555
  );
381
556
  }
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")
557
+ if (await fs7.pathExists(path6.join(pkgRoot, "metadata.json"))) {
558
+ await fs7.copy(
559
+ path6.join(pkgRoot, "metadata.json"),
560
+ path6.join(skillVersionDir, "metadata.json")
386
561
  );
387
562
  }
388
- const skillDir = path3.join(skillsDir, skillId);
389
- await fs4.ensureDir(skillDir);
390
- const latestLink = path3.join(skillDir, LATEST_LINK);
563
+ const skillDir = path6.join(skillsDir, skillId);
564
+ await fs7.ensureDir(skillDir);
565
+ const latestLink = path6.join(skillDir, LATEST_LINK);
391
566
  try {
392
- await fs4.remove(latestLink);
393
- await fs4.symlink(skillVersionDir, latestLink, "junction");
567
+ await fs7.remove(latestLink);
568
+ await fs7.symlink(skillVersionDir, latestLink, "junction");
394
569
  } catch {
395
- await fs4.copy(skillVersionDir, path3.join(skillDir, LATEST_LINK), { overwrite: true });
570
+ await fs7.copy(skillVersionDir, path6.join(skillDir, LATEST_LINK), { overwrite: true });
396
571
  }
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(", ")}`);
572
+ let targetAdapters = [];
573
+ if (options?.platforms && options.platforms.length > 0) {
574
+ for (const platformStr of options.platforms) {
575
+ const platform = platformStr;
576
+ const adapter = getAdapterByPlatform(platform);
577
+ if (adapter) {
578
+ targetAdapters.push(adapter);
579
+ } else {
580
+ console.warn(`\u26A0\uFE0F Unknown platform: ${platformStr}`);
581
+ }
402
582
  }
403
- finalPlatform = parsed;
404
583
  } else {
405
- finalPlatform = detectPlatform();
584
+ targetAdapters = await detectPlatforms();
585
+ }
586
+ if (targetAdapters.length === 0) {
587
+ console.log("No target platforms detected.");
588
+ console.log("Use --platform to specify platforms manually.");
589
+ } else {
590
+ console.log(`
591
+ Installing to ${targetAdapters.length} platform(s)...
592
+ `);
593
+ const results = [];
594
+ for (const adapter of targetAdapters) {
595
+ try {
596
+ const isInstalled = await adapter.isInstalled(skillId);
597
+ if (isInstalled && !options?.force) {
598
+ console.log(`${adapter.name.padEnd(12)} \u26A0\uFE0F Already installed (use --force to overwrite)`);
599
+ results.push({ name: adapter.name, status: "skipped" });
600
+ continue;
601
+ }
602
+ await adapter.install(skillId, skillVersionDir);
603
+ console.log(`${adapter.name.padEnd(12)} \u2705 Installed successfully`);
604
+ results.push({ name: adapter.name, status: "installed" });
605
+ } catch (error) {
606
+ console.log(`${adapter.name.padEnd(12)} \u274C Failed: ${error}`);
607
+ results.push({ name: adapter.name, status: "failed", error: String(error) });
608
+ }
609
+ }
610
+ const installed = results.filter((r) => r.status === "installed").length;
611
+ const skipped = results.filter((r) => r.status === "skipped").length;
612
+ const failed = results.filter((r) => r.status === "failed").length;
613
+ console.log(`
614
+ \u{1F4CA} Summary: ${installed} installed, ${skipped} skipped, ${failed} failed`);
406
615
  }
407
- console.log(`Target platform: ${finalPlatform}`);
408
616
  const registry = await loadRegistry();
409
- const existingSkill = registry.skills[skillId];
410
- const existingPlatforms = existingSkill?.platforms || [];
617
+ const installedPlatforms = targetAdapters.map((a) => a.id);
411
618
  registry.skills[skillId] = {
412
619
  id: skillId,
413
620
  version: targetVersion,
414
621
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
415
- platforms: existingPlatforms.includes(finalPlatform) ? existingPlatforms : [...existingPlatforms, finalPlatform]
622
+ platforms: installedPlatforms
416
623
  };
417
624
  await saveRegistry(registry);
418
- console.log(`Syncing to ${finalPlatform}...`);
419
- await syncPlatformLinks(finalPlatform);
420
625
  console.log(`
421
626
  \u2705 ${skillId}@${targetVersion} installed successfully!`);
422
627
  console.log(` Use "skm info ${skillId}" for more details`);
423
628
  }
424
629
 
630
+ // src/commands/sync.ts
631
+ import fs8 from "fs-extra";
632
+ import path7 from "path";
633
+ async function syncPlatformLinks() {
634
+ await ensureMarketDirs();
635
+ const skillsDir = getSkillsDir();
636
+ const platformLinksDir = getPlatformLinksDir();
637
+ const registry = await loadRegistry();
638
+ console.log("Syncing platform links...\n");
639
+ for (const platform of PLATFORMS) {
640
+ const platformDir = path7.join(platformLinksDir, platform, "skills");
641
+ await fs8.ensureDir(platformDir);
642
+ for (const [skillId, skillInfo] of Object.entries(registry.skills)) {
643
+ const skillLatestLink = path7.join(skillsDir, skillId, LATEST_LINK);
644
+ const targetPlatformDir = path7.join(skillLatestLink, platform);
645
+ const platformSkillDir = path7.join(platformDir, skillId);
646
+ if (await fs8.pathExists(skillLatestLink)) {
647
+ if (await fs8.pathExists(targetPlatformDir)) {
648
+ try {
649
+ await fs8.remove(platformSkillDir);
650
+ await fs8.symlink(targetPlatformDir, platformSkillDir, "junction");
651
+ console.log(` Linked: ${platform}/${skillId}`);
652
+ } catch {
653
+ await fs8.copy(targetPlatformDir, platformSkillDir, { overwrite: true });
654
+ console.log(` Copied: ${platform}/${skillId}`);
655
+ }
656
+ }
657
+ }
658
+ }
659
+ }
660
+ console.log("\n\u2705 Sync complete!");
661
+ }
662
+
425
663
  // src/commands/update.ts
426
664
  async function updateSkill(skillId) {
427
665
  if (skillId) {
428
- const pkgInfo = await fetchNpmPackage(`@skillmarket/${skillId}`);
666
+ const pkgInfo = await fetchNpmPackage(`@itismyskillmarket/${skillId}`);
429
667
  if (pkgInfo) {
430
668
  const latestVersion = pkgInfo["dist-tags"]?.latest;
431
669
  console.log(`Updating ${skillId} to ${latestVersion}...`);
@@ -442,7 +680,7 @@ async function updateSkill(skillId) {
442
680
  `);
443
681
  let hasUpdates = false;
444
682
  for (const skill of installed) {
445
- const pkgInfo = await fetchNpmPackage(`@skillmarket/${skill.id}`);
683
+ const pkgInfo = await fetchNpmPackage(`@wanxuchen/${skill.id}`);
446
684
  if (pkgInfo) {
447
685
  const latestVersion = pkgInfo["dist-tags"]?.latest;
448
686
  if (latestVersion && latestVersion !== skill.version) {
@@ -464,9 +702,9 @@ async function updateSkill(skillId) {
464
702
  }
465
703
 
466
704
  // src/commands/uninstall.ts
467
- import fs5 from "fs-extra";
468
- import path4 from "path";
469
- async function uninstallSkill(skillId) {
705
+ import fs9 from "fs-extra";
706
+ import path8 from "path";
707
+ async function uninstallSkill(skillId, options) {
470
708
  const registry = await loadRegistry();
471
709
  if (!(skillId in registry.skills)) {
472
710
  console.log(`Skill "${skillId}" is not installed.`);
@@ -474,14 +712,37 @@ async function uninstallSkill(skillId) {
474
712
  }
475
713
  const skillInfo = registry.skills[skillId];
476
714
  console.log(`Uninstalling ${skillId}@${skillInfo.version}...`);
715
+ let targetAdapters = [];
716
+ if (options?.platforms && options.platforms.length > 0) {
717
+ for (const platformStr of options.platforms) {
718
+ const platform = platformStr;
719
+ targetAdapters.push(getAdapterByPlatform(platform));
720
+ }
721
+ } else {
722
+ targetAdapters = await detectPlatforms();
723
+ }
724
+ const validAdapters = targetAdapters.filter((a) => a !== void 0);
725
+ if (validAdapters.length > 0) {
726
+ console.log(`
727
+ Uninstalling from ${validAdapters.length} platform(s)...
728
+ `);
729
+ for (const adapter of validAdapters) {
730
+ try {
731
+ await adapter.uninstall(skillId);
732
+ console.log(`${adapter.name.padEnd(12)} \u2705 Uninstalled`);
733
+ } catch (error) {
734
+ console.log(`${adapter.name.padEnd(12)} \u274C Failed: ${error}`);
735
+ }
736
+ }
737
+ }
477
738
  const skillsDir = getSkillsDir();
478
- const skillDir = path4.join(skillsDir, skillId);
479
- await fs5.remove(skillDir);
739
+ const skillDir = path8.join(skillsDir, skillId);
740
+ await fs9.remove(skillDir);
480
741
  const platformLinksDir = getPlatformLinksDir();
481
742
  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);
743
+ const linkPath = path8.join(platformLinksDir, platform, "skills", skillId);
744
+ if (await fs9.pathExists(linkPath)) {
745
+ await fs9.remove(linkPath);
485
746
  }
486
747
  }
487
748
  delete registry.skills[skillId];
@@ -492,60 +753,79 @@ async function uninstallSkill(skillId) {
492
753
 
493
754
  // src/cli.ts
494
755
  var program = new Command();
495
- program.name("skm").description("SkillMarket - Cross-platform skill manager for AI coding tools").version("1.2.0");
496
- var helpCmd = program.command("help").description("Display help information");
497
- helpCmd.action(() => {
498
- console.log(`
756
+ program.name("skm").description("SkillMarket - Cross-platform skill manager for AI coding tools").version("1.0.0");
757
+ program.hook("preAction", (thisCommand) => {
758
+ if (thisCommand.opts().help) {
759
+ console.log(`
499
760
  SkillMarket CLI
500
761
 
501
762
  Usage: skm <command> [options]
502
763
 
503
764
  Commands:
504
- --help, -h Display this help message
505
- --ls [options] List available skills
506
- --installed Show only installed skills
507
- --updates Check for updates
508
- --info <skill-id> Display skill information
509
- --install <skill> Install a skill (e.g., skm --install brainstorming)
510
- @version Install specific version
511
- --all Install all available skills
512
- --uninstall <skill> Remove an installed skill
513
- --update [options] Update skills
514
- --all Update all skills
515
- --sync Synchronize platform links
516
- --platform <name> Set target platform (${PLATFORMS.join(", ")})
765
+ ls [options] List available skills
766
+ --installed Show only installed skills
767
+ --updates Check for updates
768
+ --page <n> Page number (default: 1)
769
+ --limit <n> Items per page (default: 20)
770
+ info <skill-id> Display skill information
771
+ install <skill> Install a skill
772
+ @version Install specific version
773
+ --platform Target platforms (opencode,claude,vscode)
774
+ --force Overwrite if already installed
775
+ uninstall <skill> Remove an installed skill
776
+ --platform Target platforms
777
+ update [options] Update skills
778
+ --all Update all skills
779
+ sync Synchronize platform links
780
+ platforms Show available platforms
517
781
 
518
782
  Examples:
519
- skm --ls List all available skills
520
- skm --ls --installed Show installed skills only
521
- skm --info brainstorming View skill details
522
- skm --install brainstorming Install a skill
523
- skm --install brainstorming@1.0.0 Install specific version
524
- skm --update --all Update all installed skills
525
- skm --sync Sync platform links
526
- `);
783
+ skm ls List all available skills (page 1)
784
+ skm ls --page 2 Go to page 2
785
+ skm ls --limit 10 Show 10 items per page
786
+ skm ls --installed Show installed skills only
787
+ skm ls --installed --page 2
788
+ skm info brainstorming View skill details
789
+ skm install brainstorming Install to all platforms
790
+ skm install brainstorming --platform opencode Install to OpenCode only
791
+ skm install brainstorming --platform claude,vscode Install to multiple
792
+ skm uninstall brainstorming
793
+ skm platforms Show available platforms
794
+ `);
795
+ process.exit(0);
796
+ }
527
797
  });
528
798
  var lsCmd = program.command("ls").description("List available skills");
529
- lsCmd.option("--installed", "Show only installed skills").option("--updates", "Check for updates").action((opts) => {
530
- listSkills(opts);
799
+ 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).action((opts) => {
800
+ const options = {
801
+ ...opts,
802
+ page: opts.page ?? 1,
803
+ limit: opts.limit ?? 20
804
+ };
805
+ listSkills(options);
531
806
  });
532
807
  var infoCmd = program.command("info").description("Display skill information");
533
808
  infoCmd.argument("<skill-id>", "Skill ID to show info").action((skillId) => {
534
809
  showSkillInfo(skillId);
535
810
  });
536
- var installCmd = program.command("install").description("Install a skill");
537
- 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) => {
811
+ var installCmd = program.command("install").description("Install a skill to local and platform directories");
812
+ 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) => {
538
813
  try {
539
- await installSkill(skill, void 0, opts.platform);
814
+ const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
815
+ await installSkill(skill, opts.version, {
816
+ platforms,
817
+ force: opts.force
818
+ });
540
819
  } catch (err) {
541
820
  console.error("Installation failed:", err);
542
821
  process.exit(1);
543
822
  }
544
823
  });
545
- var uninstallCmd = program.command("uninstall").description("Remove an installed skill");
546
- uninstallCmd.argument("<skill>", "Skill ID to uninstall").action(async (skill) => {
824
+ var uninstallCmd = program.command("uninstall").description("Remove an installed skill from local and platform directories");
825
+ uninstallCmd.argument("<skill>", "Skill ID to uninstall").option("-p, --platform <platforms>", "Target platforms (comma-separated)").action(async (skill, opts) => {
547
826
  try {
548
- await uninstallSkill(skill);
827
+ const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
828
+ await uninstallSkill(skill, { platforms });
549
829
  } catch (err) {
550
830
  console.error("Uninstall failed:", err);
551
831
  process.exit(1);
@@ -572,8 +852,29 @@ program.command("sync").description("Synchronize platform links").action(async (
572
852
  process.exit(1);
573
853
  }
574
854
  });
575
- var platformCmd = program.command("platform").description("Set target platform");
576
- platformCmd.argument("<name>", "Platform name").action((name) => {
577
- console.log("Platform command - name:", name);
855
+ var platformsCmd = program.command("platforms").description("Show available platforms");
856
+ platformsCmd.action(async () => {
857
+ try {
858
+ const available = await detectPlatforms();
859
+ console.log("\n\u{1F4CD} Available Platforms:\n");
860
+ const allPlatforms = [
861
+ { name: "OpenCode", adapter: new OpenCodeAdapter() },
862
+ { name: "Claude Code", adapter: new ClaudeAdapter() },
863
+ { name: "VSCode", adapter: new VSCodeAdapter() }
864
+ ];
865
+ for (const { name, adapter } of allPlatforms) {
866
+ const isAvailable = available.find((a) => a.id === adapter.id);
867
+ const installed = await adapter.listInstalled();
868
+ if (isAvailable) {
869
+ console.log(`${name.padEnd(12)} \u2705 Available (${installed.length} skills installed)`);
870
+ } else {
871
+ console.log(`${name.padEnd(12)} \u274C Not detected`);
872
+ }
873
+ }
874
+ console.log("");
875
+ } catch (err) {
876
+ console.error("Failed to list platforms:", err);
877
+ process.exit(1);
878
+ }
578
879
  });
579
880
  program.parse();