@cantemizyurek/skillz 0.1.3 → 0.1.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/dist/index.cjs CHANGED
@@ -6823,7 +6823,7 @@ var require_semver2 = __commonJS({
6823
6823
 
6824
6824
  // src/index.ts
6825
6825
  var import_node_path5 = __toESM(require("path"), 1);
6826
- var import_node_process = __toESM(require("process"), 1);
6826
+ var import_node_process2 = __toESM(require("process"), 1);
6827
6827
  var import_prompts2 = require("@inquirer/prompts");
6828
6828
 
6829
6829
  // ../shared/dist/index.js
@@ -11248,6 +11248,7 @@ var skillNameSchema = external_exports.string().min(1).max(64).regex(
11248
11248
  SKILL_NAME_REGEX,
11249
11249
  "Skill name must be lowercase alphanumeric with internal dashes"
11250
11250
  );
11251
+ var namespaceSchema = usernameSchema;
11251
11252
  var namespaceNameSchema = external_exports.string().regex(
11252
11253
  /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])?\/[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/,
11253
11254
  "Expected namespace/name"
@@ -11318,6 +11319,16 @@ var RegistryClient = class {
11318
11319
  method: "GET"
11319
11320
  });
11320
11321
  }
11322
+ async list(namespace) {
11323
+ const params = new URLSearchParams();
11324
+ params.set("sort", "downloads");
11325
+ if (namespace) {
11326
+ params.set("namespace", namespace);
11327
+ }
11328
+ return this.request(`/skills?${params.toString()}`, {
11329
+ method: "GET"
11330
+ });
11331
+ }
11321
11332
  async skillInfo(namespace, name) {
11322
11333
  return this.request(`/skills/${namespace}/${name}`, {
11323
11334
  method: "GET"
@@ -11406,6 +11417,7 @@ async function readErrorMessage(response) {
11406
11417
  var import_promises2 = __toESM(require("fs/promises"), 1);
11407
11418
  var import_node_os2 = __toESM(require("os"), 1);
11408
11419
  var import_node_path2 = __toESM(require("path"), 1);
11420
+ var import_node_process = __toESM(require("process"), 1);
11409
11421
  var import_prompts = require("@inquirer/prompts");
11410
11422
 
11411
11423
  // src/git.ts
@@ -11459,17 +11471,18 @@ function parseSkillRef(input) {
11459
11471
  }
11460
11472
  return { namespace, name };
11461
11473
  }
11462
- async function chooseAgent(input) {
11463
- if (input === "codex" || input === "claude" || input === "cursor") {
11464
- return input;
11474
+ async function chooseAgents(input) {
11475
+ if (input && input.length > 0) {
11476
+ return dedupeAgents(input);
11465
11477
  }
11466
- return (0, import_prompts.select)({
11467
- message: "Choose an agent target",
11478
+ return (0, import_prompts.checkbox)({
11479
+ message: "Choose agent targets",
11468
11480
  choices: [
11469
11481
  { name: "Codex", value: "codex" },
11470
11482
  { name: "Claude Code", value: "claude" },
11471
11483
  { name: "Cursor", value: "cursor" }
11472
- ]
11484
+ ],
11485
+ validate: (value) => value.length > 0 || "Choose at least one agent"
11473
11486
  });
11474
11487
  }
11475
11488
  async function chooseScope(input) {
@@ -11506,6 +11519,18 @@ async function chooseVersion(availableVersions, preferredVersion) {
11506
11519
  }))
11507
11520
  });
11508
11521
  }
11522
+ async function chooseSymlinkMode(input) {
11523
+ if (typeof input.provided === "boolean") {
11524
+ return input.provided;
11525
+ }
11526
+ if (input.agents.length <= 1) {
11527
+ return false;
11528
+ }
11529
+ return (0, import_prompts.confirm)({
11530
+ message: "Install once in .agents/skills and symlink into other selected agent folders?",
11531
+ default: true
11532
+ });
11533
+ }
11509
11534
  async function resolveInstallTarget(input) {
11510
11535
  let repoRoot = input.cwd;
11511
11536
  let usedCwdFallback = false;
@@ -11567,6 +11592,42 @@ async function installArchiveBuffer(archiveBuffer, archiveFormat, destinationPat
11567
11592
  await import_promises2.default.rm(tempRoot, { recursive: true, force: true });
11568
11593
  }
11569
11594
  }
11595
+ async function createOrReplaceSkillSymlink(input) {
11596
+ await import_promises2.default.mkdir(import_node_path2.default.dirname(input.linkPath), { recursive: true });
11597
+ await import_promises2.default.rm(input.linkPath, { recursive: true, force: true });
11598
+ const linkTarget = import_node_process.default.platform === "win32" ? import_node_path2.default.resolve(input.targetPath) : import_node_path2.default.relative(import_node_path2.default.dirname(input.linkPath), input.targetPath);
11599
+ await import_promises2.default.symlink(
11600
+ linkTarget,
11601
+ input.linkPath,
11602
+ import_node_process.default.platform === "win32" ? "junction" : "dir"
11603
+ );
11604
+ }
11605
+ async function resolveInstallWritePath(installPath) {
11606
+ try {
11607
+ const stats = await import_promises2.default.lstat(installPath);
11608
+ if (stats.isSymbolicLink()) {
11609
+ return await import_promises2.default.realpath(installPath);
11610
+ }
11611
+ } catch (error) {
11612
+ if (error.code === "ENOENT") {
11613
+ return installPath;
11614
+ }
11615
+ throw error;
11616
+ }
11617
+ return installPath;
11618
+ }
11619
+ function dedupeAgents(input) {
11620
+ const seen = /* @__PURE__ */ new Set();
11621
+ const deduped = [];
11622
+ for (const agent of input) {
11623
+ if (seen.has(agent)) {
11624
+ continue;
11625
+ }
11626
+ seen.add(agent);
11627
+ deduped.push(agent);
11628
+ }
11629
+ return deduped;
11630
+ }
11570
11631
 
11571
11632
  // src/publish.ts
11572
11633
  var import_promises3 = __toESM(require("fs/promises"), 1);
@@ -11749,6 +11810,26 @@ program.command("whoami").description("Show current authenticated user").action(
11749
11810
  const me = await client.whoami();
11750
11811
  console.log(`${me.user.username} (${me.user.role})`);
11751
11812
  });
11813
+ program.command("list").description("List skills by download count (descending)").argument("[namespace]", "optional namespace").action(async (namespaceInput, _options, command) => {
11814
+ const { client } = await makeClient(command, true);
11815
+ const namespace = namespaceInput ? namespaceSchema.parse(namespaceInput) : void 0;
11816
+ const result = await client.list(namespace);
11817
+ if (result.skills.length === 0) {
11818
+ if (namespace) {
11819
+ console.log(`No skills found for namespace ${namespace}.`);
11820
+ } else {
11821
+ console.log("No skills found.");
11822
+ }
11823
+ return;
11824
+ }
11825
+ for (const skill of result.skills) {
11826
+ const latest = skill.latestVersion ?? "none";
11827
+ console.log(
11828
+ `${skill.namespace}/${skill.name} downloads=${skill.downloadCount} latest=${latest}`
11829
+ );
11830
+ console.log(` ${skill.description}`);
11831
+ }
11832
+ });
11752
11833
  program.command("search").description("Search skills").argument("<query>", "search query").action(async (query, _options, command) => {
11753
11834
  const { client } = await makeClient(command, true);
11754
11835
  const result = await client.search(query);
@@ -11776,51 +11857,108 @@ program.command("info").description("Show skill metadata and versions").argument
11776
11857
  console.log(`Downloads: ${info.skill.downloadCount}`);
11777
11858
  console.log(`Versions: ${versions.versions.map((version) => version.version).join(", ") || "none"}`);
11778
11859
  });
11779
- program.command("install").description("Install a skill").argument("<namespace/skill>", "skill reference").option("--version <x.y.z>", "specific version").option("--agent <agent>", "codex | claude | cursor").option("--scope <scope>", "user | project").option("--dir <path>", "custom root directory").action(async (skillRefInput, options2, command) => {
11860
+ program.command("install").description("Install a skill").argument("<namespace/skill>", "skill reference").option("--version <x.y.z>", "specific version").option("--agent <agents>", "codex | claude | cursor (comma-separated)").option("--scope <scope>", "user | project").option("--dir <path>", "custom root directory").action(async (skillRefInput, options2, command) => {
11780
11861
  namespaceNameSchema.parse(skillRefInput);
11781
11862
  const skillRef = parseSkillRef(skillRefInput);
11782
11863
  const { client, registryBaseUrl } = await makeClient(command, true);
11783
- const agent = await chooseAgent(
11784
- options2.agent ? agentSchema.parse(options2.agent) : void 0
11785
- );
11864
+ const requestedAgents = options2.agent ? parseAgentTargetsOption(options2.agent) : void 0;
11865
+ const agents = await chooseAgents(requestedAgents);
11786
11866
  const scope = await chooseScope(
11787
11867
  options2.scope ? scopeSchema.parse(options2.scope) : void 0
11788
11868
  );
11869
+ const symlinkMode = await chooseSymlinkMode({
11870
+ agents
11871
+ });
11789
11872
  const versionList = await client.skillVersions(skillRef.namespace, skillRef.name);
11790
11873
  const selectedVersion = await chooseVersion(
11791
11874
  versionList.versions.map((row) => row.version),
11792
11875
  options2.version ? semverSchema.parse(options2.version) : void 0
11793
11876
  );
11794
- const resolution = await resolveInstallTarget({
11795
- agent,
11796
- scope,
11797
- skillName: skillRef.name,
11798
- cwd: import_node_process.default.cwd(),
11799
- dirOverride: options2.dir
11800
- });
11801
- if (resolution.usedCwdFallback) {
11877
+ const resolutions = await Promise.all(
11878
+ agents.map(
11879
+ (agent) => resolveInstallTarget({
11880
+ agent,
11881
+ scope,
11882
+ skillName: skillRef.name,
11883
+ cwd: import_node_process2.default.cwd(),
11884
+ dirOverride: options2.dir
11885
+ })
11886
+ )
11887
+ );
11888
+ if (resolutions.some((resolution) => resolution.usedCwdFallback)) {
11802
11889
  console.log(
11803
- `No git repo detected. Using current directory as repo root: ${import_node_process.default.cwd()}`
11890
+ `No git repo detected. Using current directory as repo root: ${import_node_process2.default.cwd()}`
11804
11891
  );
11805
11892
  }
11806
- await downloadAndInstallSkill(
11807
- client,
11808
- skillRef,
11809
- selectedVersion,
11810
- resolution.installPath
11811
- );
11812
- await upsertManifestEntry({
11813
- namespace: skillRef.namespace,
11814
- name: skillRef.name,
11815
- version: selectedVersion,
11816
- agent,
11817
- scope,
11818
- installPath: resolution.installPath,
11819
- registryBaseUrl,
11820
- installedAt: (/* @__PURE__ */ new Date()).toISOString()
11821
- });
11893
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
11894
+ if (symlinkMode) {
11895
+ const sharedResolution = resolutions.find((resolution) => resolution.agent === "codex") ?? await resolveInstallTarget({
11896
+ agent: "codex",
11897
+ scope,
11898
+ skillName: skillRef.name,
11899
+ cwd: import_node_process2.default.cwd(),
11900
+ dirOverride: options2.dir
11901
+ });
11902
+ await downloadAndInstallSkill(
11903
+ client,
11904
+ skillRef,
11905
+ selectedVersion,
11906
+ sharedResolution.installPath
11907
+ );
11908
+ for (const resolution of resolutions) {
11909
+ if (resolution.agent !== "codex") {
11910
+ await createOrReplaceSkillSymlink({
11911
+ linkPath: resolution.installPath,
11912
+ targetPath: sharedResolution.installPath
11913
+ });
11914
+ }
11915
+ await upsertManifestEntry({
11916
+ namespace: skillRef.namespace,
11917
+ name: skillRef.name,
11918
+ version: selectedVersion,
11919
+ agent: resolution.agent,
11920
+ scope,
11921
+ installPath: resolution.installPath,
11922
+ sourceInstallPath: resolution.agent === "codex" ? void 0 : sharedResolution.installPath,
11923
+ registryBaseUrl,
11924
+ installedAt
11925
+ });
11926
+ }
11927
+ console.log(`Installed ${skillRef.namespace}/${skillRef.name}@${selectedVersion}`);
11928
+ console.log(`Shared path: ${sharedResolution.installPath}`);
11929
+ for (const resolution of resolutions) {
11930
+ if (resolution.agent === "codex") {
11931
+ console.log(`Codex: ${resolution.installPath}`);
11932
+ continue;
11933
+ }
11934
+ console.log(
11935
+ `${resolution.agent}: ${resolution.installPath} -> ${sharedResolution.installPath}`
11936
+ );
11937
+ }
11938
+ return;
11939
+ }
11940
+ for (const resolution of resolutions) {
11941
+ await downloadAndInstallSkill(
11942
+ client,
11943
+ skillRef,
11944
+ selectedVersion,
11945
+ resolution.installPath
11946
+ );
11947
+ await upsertManifestEntry({
11948
+ namespace: skillRef.namespace,
11949
+ name: skillRef.name,
11950
+ version: selectedVersion,
11951
+ agent: resolution.agent,
11952
+ scope,
11953
+ installPath: resolution.installPath,
11954
+ registryBaseUrl,
11955
+ installedAt
11956
+ });
11957
+ }
11822
11958
  console.log(`Installed ${skillRef.namespace}/${skillRef.name}@${selectedVersion}`);
11823
- console.log(`Path: ${resolution.installPath}`);
11959
+ for (const resolution of resolutions) {
11960
+ console.log(`${resolution.agent}: ${resolution.installPath}`);
11961
+ }
11824
11962
  });
11825
11963
  program.command("update").description("Update installed skills").argument("[namespace/skill]", "skill reference").option("--all", "update all installed skills").option("--agent <agent>", "codex | claude | cursor").option("--scope <scope>", "user | project").action(async (skillRefInput, options2, command) => {
11826
11964
  const updateAll = Boolean(options2.all);
@@ -11856,8 +11994,10 @@ program.command("update").description("Update installed skills").argument("[name
11856
11994
  if (!config.token) {
11857
11995
  throw new Error("Not logged in. Run skillz login.");
11858
11996
  }
11997
+ const candidateGroups = groupManifestEntriesBySource(candidates);
11859
11998
  let updatedCount = 0;
11860
- for (const installed of candidates) {
11999
+ for (const group of candidateGroups) {
12000
+ const installed = group[0];
11861
12001
  if (installed.registryBaseUrl !== resolveRegistryBaseUrl(command, config)) {
11862
12002
  console.log(
11863
12003
  `Skipping ${installed.namespace}/${installed.name} because it belongs to ${installed.registryBaseUrl}`
@@ -11867,9 +12007,11 @@ program.command("update").description("Update installed skills").argument("[name
11867
12007
  const client = new RegistryClient(installed.registryBaseUrl, config.token);
11868
12008
  const versions = await client.skillVersions(installed.namespace, installed.name);
11869
12009
  const latestVersion = versions.versions[0]?.version;
11870
- if (!latestVersion || !isSemverGreater(latestVersion, installed.version)) {
12010
+ if (!latestVersion || !group.some((entry) => isSemverGreater(latestVersion, entry.version))) {
11871
12011
  continue;
11872
12012
  }
12013
+ const sourcePath = installed.sourceInstallPath ?? installed.installPath;
12014
+ const writePath = await resolveInstallWritePath(sourcePath);
11873
12015
  await downloadAndInstallSkill(
11874
12016
  client,
11875
12017
  {
@@ -11877,13 +12019,16 @@ program.command("update").description("Update installed skills").argument("[name
11877
12019
  name: installed.name
11878
12020
  },
11879
12021
  latestVersion,
11880
- installed.installPath
12022
+ writePath
11881
12023
  );
11882
- installed.version = latestVersion;
11883
- installed.installedAt = (/* @__PURE__ */ new Date()).toISOString();
11884
- updatedCount += 1;
12024
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
12025
+ for (const entry of group) {
12026
+ entry.version = latestVersion;
12027
+ entry.installedAt = updatedAt;
12028
+ }
12029
+ updatedCount += group.length;
11885
12030
  console.log(
11886
- `Updated ${installed.namespace}/${installed.name} to ${latestVersion}`
12031
+ `Updated ${installed.namespace}/${installed.name} to ${latestVersion} (${group.map((entry) => entry.agent).join(", ")})`
11887
12032
  );
11888
12033
  }
11889
12034
  if (updatedCount === 0) {
@@ -11912,10 +12057,10 @@ invitesCommand.command("create").description("Create a single-use invite code (a
11912
12057
  const invite = await client.createInvite();
11913
12058
  console.log(invite.inviteCode);
11914
12059
  });
11915
- program.parseAsync(import_node_process.default.argv).catch((error) => {
12060
+ program.parseAsync(import_node_process2.default.argv).catch((error) => {
11916
12061
  const message = error instanceof Error ? error.message : String(error);
11917
12062
  console.error(`Error: ${message}`);
11918
- import_node_process.default.exit(1);
12063
+ import_node_process2.default.exit(1);
11919
12064
  });
11920
12065
  async function makeClient(command, requireAuth) {
11921
12066
  const commandContext = isCommand(command) ? command : program;
@@ -11934,7 +12079,7 @@ async function makeClient(command, requireAuth) {
11934
12079
  }
11935
12080
  function resolveRegistryBaseUrl(command, config) {
11936
12081
  const options2 = resolveGlobalOptions(command);
11937
- const resolved = options2.registry ?? import_node_process.default.env.SKILLS_REGISTRY_API_URL ?? config.apiBaseUrl ?? "https://skillz-api.vercel.app";
12082
+ const resolved = options2.registry ?? import_node_process2.default.env.SKILLS_REGISTRY_API_URL ?? config.apiBaseUrl ?? "https://skillz-api.vercel.app";
11938
12083
  return resolved.replace(/\/$/, "");
11939
12084
  }
11940
12085
  function resolveGlobalOptions(command) {
@@ -11955,6 +12100,32 @@ function resolveGlobalOptions(command) {
11955
12100
  function isCommand(value) {
11956
12101
  return Boolean(value) && typeof value.opts === "function";
11957
12102
  }
12103
+ function parseAgentTargetsOption(input) {
12104
+ const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0).map((value) => agentSchema.parse(value));
12105
+ if (values.length === 0) {
12106
+ throw new Error("Provide at least one agent in --agent");
12107
+ }
12108
+ return Array.from(new Set(values));
12109
+ }
12110
+ function groupManifestEntriesBySource(entries) {
12111
+ const grouped = /* @__PURE__ */ new Map();
12112
+ for (const entry of entries) {
12113
+ const sourcePath = entry.sourceInstallPath ?? entry.installPath;
12114
+ const key = [
12115
+ entry.registryBaseUrl,
12116
+ entry.namespace,
12117
+ entry.name,
12118
+ sourcePath
12119
+ ].join("::");
12120
+ const group = grouped.get(key);
12121
+ if (group) {
12122
+ group.push(entry);
12123
+ } else {
12124
+ grouped.set(key, [entry]);
12125
+ }
12126
+ }
12127
+ return Array.from(grouped.values());
12128
+ }
11958
12129
  /*! Bundled license information:
11959
12130
 
11960
12131
  is-extendable/index.js: