@cantemizyurek/skillz 0.1.4 → 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
@@ -11417,6 +11417,7 @@ async function readErrorMessage(response) {
11417
11417
  var import_promises2 = __toESM(require("fs/promises"), 1);
11418
11418
  var import_node_os2 = __toESM(require("os"), 1);
11419
11419
  var import_node_path2 = __toESM(require("path"), 1);
11420
+ var import_node_process = __toESM(require("process"), 1);
11420
11421
  var import_prompts = require("@inquirer/prompts");
11421
11422
 
11422
11423
  // src/git.ts
@@ -11470,17 +11471,18 @@ function parseSkillRef(input) {
11470
11471
  }
11471
11472
  return { namespace, name };
11472
11473
  }
11473
- async function chooseAgent(input) {
11474
- if (input === "codex" || input === "claude" || input === "cursor") {
11475
- return input;
11474
+ async function chooseAgents(input) {
11475
+ if (input && input.length > 0) {
11476
+ return dedupeAgents(input);
11476
11477
  }
11477
- return (0, import_prompts.select)({
11478
- message: "Choose an agent target",
11478
+ return (0, import_prompts.checkbox)({
11479
+ message: "Choose agent targets",
11479
11480
  choices: [
11480
11481
  { name: "Codex", value: "codex" },
11481
11482
  { name: "Claude Code", value: "claude" },
11482
11483
  { name: "Cursor", value: "cursor" }
11483
- ]
11484
+ ],
11485
+ validate: (value) => value.length > 0 || "Choose at least one agent"
11484
11486
  });
11485
11487
  }
11486
11488
  async function chooseScope(input) {
@@ -11517,6 +11519,18 @@ async function chooseVersion(availableVersions, preferredVersion) {
11517
11519
  }))
11518
11520
  });
11519
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
+ }
11520
11534
  async function resolveInstallTarget(input) {
11521
11535
  let repoRoot = input.cwd;
11522
11536
  let usedCwdFallback = false;
@@ -11578,6 +11592,42 @@ async function installArchiveBuffer(archiveBuffer, archiveFormat, destinationPat
11578
11592
  await import_promises2.default.rm(tempRoot, { recursive: true, force: true });
11579
11593
  }
11580
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
+ }
11581
11631
 
11582
11632
  // src/publish.ts
11583
11633
  var import_promises3 = __toESM(require("fs/promises"), 1);
@@ -11807,51 +11857,108 @@ program.command("info").description("Show skill metadata and versions").argument
11807
11857
  console.log(`Downloads: ${info.skill.downloadCount}`);
11808
11858
  console.log(`Versions: ${versions.versions.map((version) => version.version).join(", ") || "none"}`);
11809
11859
  });
11810
- 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) => {
11811
11861
  namespaceNameSchema.parse(skillRefInput);
11812
11862
  const skillRef = parseSkillRef(skillRefInput);
11813
11863
  const { client, registryBaseUrl } = await makeClient(command, true);
11814
- const agent = await chooseAgent(
11815
- options2.agent ? agentSchema.parse(options2.agent) : void 0
11816
- );
11864
+ const requestedAgents = options2.agent ? parseAgentTargetsOption(options2.agent) : void 0;
11865
+ const agents = await chooseAgents(requestedAgents);
11817
11866
  const scope = await chooseScope(
11818
11867
  options2.scope ? scopeSchema.parse(options2.scope) : void 0
11819
11868
  );
11869
+ const symlinkMode = await chooseSymlinkMode({
11870
+ agents
11871
+ });
11820
11872
  const versionList = await client.skillVersions(skillRef.namespace, skillRef.name);
11821
11873
  const selectedVersion = await chooseVersion(
11822
11874
  versionList.versions.map((row) => row.version),
11823
11875
  options2.version ? semverSchema.parse(options2.version) : void 0
11824
11876
  );
11825
- const resolution = await resolveInstallTarget({
11826
- agent,
11827
- scope,
11828
- skillName: skillRef.name,
11829
- cwd: import_node_process.default.cwd(),
11830
- dirOverride: options2.dir
11831
- });
11832
- 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)) {
11833
11889
  console.log(
11834
- `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()}`
11835
11891
  );
11836
11892
  }
11837
- await downloadAndInstallSkill(
11838
- client,
11839
- skillRef,
11840
- selectedVersion,
11841
- resolution.installPath
11842
- );
11843
- await upsertManifestEntry({
11844
- namespace: skillRef.namespace,
11845
- name: skillRef.name,
11846
- version: selectedVersion,
11847
- agent,
11848
- scope,
11849
- installPath: resolution.installPath,
11850
- registryBaseUrl,
11851
- installedAt: (/* @__PURE__ */ new Date()).toISOString()
11852
- });
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
+ }
11853
11958
  console.log(`Installed ${skillRef.namespace}/${skillRef.name}@${selectedVersion}`);
11854
- console.log(`Path: ${resolution.installPath}`);
11959
+ for (const resolution of resolutions) {
11960
+ console.log(`${resolution.agent}: ${resolution.installPath}`);
11961
+ }
11855
11962
  });
11856
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) => {
11857
11964
  const updateAll = Boolean(options2.all);
@@ -11887,8 +11994,10 @@ program.command("update").description("Update installed skills").argument("[name
11887
11994
  if (!config.token) {
11888
11995
  throw new Error("Not logged in. Run skillz login.");
11889
11996
  }
11997
+ const candidateGroups = groupManifestEntriesBySource(candidates);
11890
11998
  let updatedCount = 0;
11891
- for (const installed of candidates) {
11999
+ for (const group of candidateGroups) {
12000
+ const installed = group[0];
11892
12001
  if (installed.registryBaseUrl !== resolveRegistryBaseUrl(command, config)) {
11893
12002
  console.log(
11894
12003
  `Skipping ${installed.namespace}/${installed.name} because it belongs to ${installed.registryBaseUrl}`
@@ -11898,9 +12007,11 @@ program.command("update").description("Update installed skills").argument("[name
11898
12007
  const client = new RegistryClient(installed.registryBaseUrl, config.token);
11899
12008
  const versions = await client.skillVersions(installed.namespace, installed.name);
11900
12009
  const latestVersion = versions.versions[0]?.version;
11901
- if (!latestVersion || !isSemverGreater(latestVersion, installed.version)) {
12010
+ if (!latestVersion || !group.some((entry) => isSemverGreater(latestVersion, entry.version))) {
11902
12011
  continue;
11903
12012
  }
12013
+ const sourcePath = installed.sourceInstallPath ?? installed.installPath;
12014
+ const writePath = await resolveInstallWritePath(sourcePath);
11904
12015
  await downloadAndInstallSkill(
11905
12016
  client,
11906
12017
  {
@@ -11908,13 +12019,16 @@ program.command("update").description("Update installed skills").argument("[name
11908
12019
  name: installed.name
11909
12020
  },
11910
12021
  latestVersion,
11911
- installed.installPath
12022
+ writePath
11912
12023
  );
11913
- installed.version = latestVersion;
11914
- installed.installedAt = (/* @__PURE__ */ new Date()).toISOString();
11915
- 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;
11916
12030
  console.log(
11917
- `Updated ${installed.namespace}/${installed.name} to ${latestVersion}`
12031
+ `Updated ${installed.namespace}/${installed.name} to ${latestVersion} (${group.map((entry) => entry.agent).join(", ")})`
11918
12032
  );
11919
12033
  }
11920
12034
  if (updatedCount === 0) {
@@ -11943,10 +12057,10 @@ invitesCommand.command("create").description("Create a single-use invite code (a
11943
12057
  const invite = await client.createInvite();
11944
12058
  console.log(invite.inviteCode);
11945
12059
  });
11946
- program.parseAsync(import_node_process.default.argv).catch((error) => {
12060
+ program.parseAsync(import_node_process2.default.argv).catch((error) => {
11947
12061
  const message = error instanceof Error ? error.message : String(error);
11948
12062
  console.error(`Error: ${message}`);
11949
- import_node_process.default.exit(1);
12063
+ import_node_process2.default.exit(1);
11950
12064
  });
11951
12065
  async function makeClient(command, requireAuth) {
11952
12066
  const commandContext = isCommand(command) ? command : program;
@@ -11965,7 +12079,7 @@ async function makeClient(command, requireAuth) {
11965
12079
  }
11966
12080
  function resolveRegistryBaseUrl(command, config) {
11967
12081
  const options2 = resolveGlobalOptions(command);
11968
- 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";
11969
12083
  return resolved.replace(/\/$/, "");
11970
12084
  }
11971
12085
  function resolveGlobalOptions(command) {
@@ -11986,6 +12100,32 @@ function resolveGlobalOptions(command) {
11986
12100
  function isCommand(value) {
11987
12101
  return Boolean(value) && typeof value.opts === "function";
11988
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
+ }
11989
12129
  /*! Bundled license information:
11990
12130
 
11991
12131
  is-extendable/index.js: