skild 0.2.7 → 0.2.9

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 (2) hide show
  1. package/dist/index.js +160 -90
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { createRequire } from "module";
9
9
  import fs from "fs";
10
10
  import path from "path";
11
11
  import chalk2 from "chalk";
12
- import { fetchWithTimeout, installRegistrySkill, installSkill, loadRegistryAuth, resolveRegistryAlias, resolveRegistryUrl, SkildError } from "@skild/core";
12
+ import { fetchWithTimeout, installRegistrySkill, installSkill, isValidAlias, loadRegistryAuth, resolveRegistryAlias, resolveRegistryUrl, SkildError } from "@skild/core";
13
13
 
14
14
  // src/utils/logger.ts
15
15
  import chalk from "chalk";
@@ -57,10 +57,10 @@ var logger = {
57
57
  /**
58
58
  * Log a skill entry with status indicator.
59
59
  */
60
- skillEntry: (name, path4, hasSkillMd) => {
60
+ skillEntry: (name, path3, hasSkillMd) => {
61
61
  const status = hasSkillMd ? chalk.green("\u2713") : chalk.yellow("\u26A0");
62
62
  console.log(` ${status} ${chalk.cyan(name)}`);
63
- console.log(chalk.dim(` \u2514\u2500 ${path4}`));
63
+ console.log(chalk.dim(` \u2514\u2500 ${path3}`));
64
64
  },
65
65
  /**
66
66
  * Log installation result details.
@@ -79,9 +79,7 @@ function looksLikeAlias(input) {
79
79
  if (s.includes("/") || s.includes("\\")) return false;
80
80
  if (/^https?:\/\//i.test(s) || s.includes("github.com")) return false;
81
81
  if (fs.existsSync(path.resolve(s))) return false;
82
- if (s.length < 3 || s.length > 64) return false;
83
- if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(s)) return false;
84
- if (s.includes("--")) return false;
82
+ if (!isValidAlias(s)) return false;
85
83
  return true;
86
84
  }
87
85
  async function install(source, options = {}) {
@@ -166,54 +164,122 @@ async function reportDownload(record, registryOverride) {
166
164
 
167
165
  // src/commands/list.ts
168
166
  import chalk3 from "chalk";
169
- import fs2 from "fs";
170
- import path2 from "path";
171
167
  import { PLATFORMS, listAllSkills, listSkills } from "@skild/core";
172
- function toDisplayName(name, mapping) {
173
- return mapping.get(name) || name;
168
+ function isSkillset(skill) {
169
+ return Boolean(skill.record?.skillset || skill.record?.skill?.frontmatter?.skillset);
174
170
  }
175
- function buildDisplayEntries(skills) {
176
- const nameToDisplay = /* @__PURE__ */ new Map();
177
- for (const skill of skills) {
178
- const displayName = skill.record?.canonicalName || skill.name;
179
- nameToDisplay.set(skill.name, displayName);
180
- }
181
- const entries = skills.map((skill) => {
182
- const displayName = toDisplayName(skill.name, nameToDisplay);
183
- const flags = [];
184
- if (skill.record?.skillset || skill.record?.skill?.frontmatter?.skillset) {
185
- flags.push("skillset");
186
- }
187
- if (skill.record?.dependedBy?.length) {
188
- const dependedBy = skill.record.dependedBy.map((name) => toDisplayName(name, nameToDisplay));
189
- flags.push(`dep of: ${dependedBy.join(", ")}`);
190
- }
191
- return {
192
- name: displayName,
193
- installDir: skill.installDir,
194
- status: skill.hasSkillMd ? "ok" : "warn",
195
- flags
196
- };
197
- });
171
+ function getDisplayName(skill) {
172
+ return skill.record?.canonicalName || skill.record?.skill?.frontmatter?.name || skill.name;
173
+ }
174
+ function buildNameToDisplay(skills) {
175
+ const mapping = /* @__PURE__ */ new Map();
198
176
  for (const skill of skills) {
199
- const inlineDeps = skill.record?.installedDependencies?.filter((dep) => dep.sourceType === "inline") || [];
200
- if (!inlineDeps.length) continue;
201
- const ownerName = toDisplayName(skill.name, nameToDisplay);
202
- for (const dep of inlineDeps) {
203
- const inlineDir = dep.inlinePath ? path2.join(skill.installDir, dep.inlinePath) : path2.join(skill.installDir, dep.name);
204
- const hasSkillMd = fs2.existsSync(path2.join(inlineDir, "SKILL.md"));
205
- entries.push({
206
- name: dep.name,
207
- installDir: inlineDir,
208
- status: hasSkillMd ? "ok" : "warn",
209
- flags: [`dep of: ${ownerName}`]
210
- });
177
+ mapping.set(skill.name, getDisplayName(skill));
178
+ }
179
+ return mapping;
180
+ }
181
+ function statusIcon(skill) {
182
+ return skill.hasSkillMd ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
183
+ }
184
+ function missingSkillMdLabel(skill) {
185
+ return skill.hasSkillMd ? "" : chalk3.yellow(" (missing SKILL.md)");
186
+ }
187
+ function formatDepName(dep, nameToDisplay) {
188
+ return dep.canonicalName || nameToDisplay.get(dep.name) || dep.name;
189
+ }
190
+ function summarizeDeps(record) {
191
+ const deps = record?.installedDependencies || [];
192
+ if (deps.length === 0) return null;
193
+ const byType = deps.reduce(
194
+ (acc, d) => {
195
+ acc.total += 1;
196
+ if (d.sourceType === "inline") acc.inline += 1;
197
+ else acc.external += 1;
198
+ acc.bySourceType[d.sourceType] = (acc.bySourceType[d.sourceType] || 0) + 1;
199
+ return acc;
200
+ },
201
+ { total: 0, inline: 0, external: 0, bySourceType: {} }
202
+ );
203
+ const parts = [];
204
+ if (byType.inline) parts.push(`${byType.inline} inline`);
205
+ const externalParts = Object.entries(byType.bySourceType).filter(([t]) => t !== "inline").sort(([a], [b]) => a.localeCompare(b)).map(([t, c2]) => `${c2} ${t}`);
206
+ if (externalParts.length) parts.push(...externalParts);
207
+ return `deps: ${byType.total}${parts.length ? ` (${parts.join(", ")})` : ""}`;
208
+ }
209
+ function printSkillsetSection(skills, nameToDisplay, options) {
210
+ console.log(chalk3.bold(` Skillsets (${skills.length})`));
211
+ if (skills.length === 0) {
212
+ console.log(chalk3.dim(" (none)"));
213
+ return;
214
+ }
215
+ for (const s of skills) {
216
+ const label = `${chalk3.cyan(getDisplayName(s))}${chalk3.dim(" (skillset)")}${missingSkillMdLabel(s)}`;
217
+ console.log(` ${statusIcon(s)} ${label}`);
218
+ const summary = summarizeDeps(s.record);
219
+ if (summary) console.log(chalk3.dim(` ${summary}`));
220
+ if (options.verbose) {
221
+ const deps = (s.record?.installedDependencies || []).slice().sort((a, b) => a.name.localeCompare(b.name));
222
+ if (deps.length) {
223
+ console.log(chalk3.dim(` includes (${deps.length}):`));
224
+ for (const dep of deps) {
225
+ const depName = formatDepName(dep, nameToDisplay);
226
+ console.log(chalk3.dim(` - ${depName} [${dep.sourceType}]`));
227
+ }
228
+ }
211
229
  }
230
+ if (options.paths || !s.hasSkillMd) console.log(chalk3.dim(` path: ${s.installDir}`));
231
+ }
232
+ }
233
+ function printSkillsSection(skills, options) {
234
+ console.log(chalk3.bold(` Skills (${skills.length})`));
235
+ if (skills.length === 0) {
236
+ console.log(chalk3.dim(" (none)"));
237
+ return;
238
+ }
239
+ for (const s of skills) {
240
+ const label = `${chalk3.cyan(getDisplayName(s))}${missingSkillMdLabel(s)}`;
241
+ console.log(` ${statusIcon(s)} ${label}`);
242
+ if (options.paths || !s.hasSkillMd) console.log(chalk3.dim(` path: ${s.installDir}`));
212
243
  }
213
- return entries;
244
+ }
245
+ function printDependenciesSection(skills, nameToDisplay, options) {
246
+ console.log(chalk3.bold(` Dependencies (${skills.length})`));
247
+ if (skills.length === 0) {
248
+ console.log(chalk3.dim(" (none)"));
249
+ return;
250
+ }
251
+ for (const s of skills) {
252
+ const dependedBy = (s.record?.dependedBy || []).map((name) => nameToDisplay.get(name) || name).sort((a, b) => a.localeCompare(b));
253
+ const requiredBy = dependedBy.length ? chalk3.dim(` \u2190 required by: ${dependedBy.join(", ")}`) : "";
254
+ const label = `${chalk3.cyan(getDisplayName(s))}${missingSkillMdLabel(s)}${requiredBy}`;
255
+ console.log(` ${statusIcon(s)} ${label}`);
256
+ if (options.paths || !s.hasSkillMd) console.log(chalk3.dim(` path: ${s.installDir}`));
257
+ }
258
+ }
259
+ function printPlatform(skills, platform, scope, options) {
260
+ console.log(chalk3.bold(`
261
+ \u{1F4E6} Installed Skills \u2014 ${platform} (${scope})
262
+ `));
263
+ if (skills.length === 0) {
264
+ console.log(chalk3.dim(" No skills installed."));
265
+ console.log(chalk3.dim(` Use ${chalk3.cyan("skild install <source>")} to install a skill.`));
266
+ return;
267
+ }
268
+ const nameToDisplay = buildNameToDisplay(skills);
269
+ const skillsets = skills.filter(isSkillset).sort((a, b) => getDisplayName(a).localeCompare(getDisplayName(b)));
270
+ const dependencies = skills.filter((s) => !isSkillset(s) && Boolean(s.record?.dependedBy?.length)).sort((a, b) => getDisplayName(a).localeCompare(getDisplayName(b)));
271
+ const regular = skills.filter((s) => !isSkillset(s) && !Boolean(s.record?.dependedBy?.length)).sort((a, b) => getDisplayName(a).localeCompare(getDisplayName(b)));
272
+ printSkillsetSection(skillsets, nameToDisplay, options);
273
+ console.log("");
274
+ printSkillsSection(regular, options);
275
+ console.log("");
276
+ printDependenciesSection(dependencies, nameToDisplay, options);
277
+ console.log("");
214
278
  }
215
279
  async function list(options = {}) {
216
280
  const scope = options.local ? "project" : "global";
281
+ const paths = Boolean(options.paths);
282
+ const verbose = Boolean(options.verbose);
217
283
  const platform = options.target;
218
284
  if (platform) {
219
285
  const skills = listSkills({ platform, scope });
@@ -221,22 +287,7 @@ async function list(options = {}) {
221
287
  console.log(JSON.stringify(skills, null, 2));
222
288
  return;
223
289
  }
224
- if (skills.length === 0) {
225
- console.log(chalk3.dim("No skills installed."));
226
- console.log(chalk3.dim(`Use ${chalk3.cyan("skild install <source>")} to install a skill.`));
227
- return;
228
- }
229
- const entries = buildDisplayEntries(skills);
230
- console.log(chalk3.bold(`
231
- \u{1F4E6} Installed Skills (${entries.length}) \u2014 ${platform} (${scope}):
232
- `));
233
- for (const entry of entries) {
234
- const status = entry.status === "ok" ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
235
- const label = entry.flags.length ? `${entry.name} (${entry.flags.join("; ")})` : entry.name;
236
- console.log(` ${status} ${chalk3.cyan(label)}`);
237
- console.log(chalk3.dim(` \u2514\u2500 ${entry.installDir}`));
238
- }
239
- console.log("");
290
+ printPlatform(skills, platform, scope, { paths, verbose });
240
291
  return;
241
292
  }
242
293
  const allSkills = listAllSkills({ scope });
@@ -249,26 +300,11 @@ async function list(options = {}) {
249
300
  console.log(chalk3.dim(`Use ${chalk3.cyan("skild install <source>")} to install a skill.`));
250
301
  return;
251
302
  }
252
- console.log(chalk3.bold(`
253
- \u{1F4E6} Installed Skills \u2014 all platforms (${scope}):
254
- `));
255
303
  for (const p of PLATFORMS) {
256
304
  const platformSkills = allSkills.filter((s) => s.platform === p).sort((a, b) => a.name.localeCompare(b.name));
257
- const entries = buildDisplayEntries(platformSkills);
258
- const header = `${p} (${entries.length})`;
259
- console.log(chalk3.bold(` ${header}`));
260
- if (entries.length === 0) {
261
- console.log(chalk3.dim(" (none)"));
262
- continue;
263
- }
264
- for (const entry of entries) {
265
- const status = entry.status === "ok" ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
266
- const label = entry.flags.length ? `${entry.name} (${entry.flags.join("; ")})` : entry.name;
267
- console.log(` ${status} ${chalk3.cyan(label)}`);
268
- console.log(chalk3.dim(` \u2514\u2500 ${entry.installDir}`));
269
- }
305
+ if (platformSkills.length === 0) continue;
306
+ printPlatform(platformSkills, p, scope, { paths, verbose });
270
307
  }
271
- console.log("");
272
308
  }
273
309
 
274
310
  // src/commands/info.ts
@@ -664,13 +700,13 @@ async function whoami() {
664
700
  }
665
701
 
666
702
  // src/commands/publish.ts
667
- import fs3 from "fs";
703
+ import fs2 from "fs";
668
704
  import os from "os";
669
- import path3 from "path";
705
+ import path2 from "path";
670
706
  import crypto from "crypto";
671
707
  import * as tar from "tar";
672
708
  import chalk13 from "chalk";
673
- import { fetchWithTimeout as fetchWithTimeout5, loadRegistryAuth as loadRegistryAuth3, resolveRegistryUrl as resolveRegistryUrl5, SkildError as SkildError9, splitCanonicalName, validateSkillDir } from "@skild/core";
709
+ import { assertValidAlias, fetchWithTimeout as fetchWithTimeout5, loadRegistryAuth as loadRegistryAuth3, normalizeAlias, resolveRegistryUrl as resolveRegistryUrl5, SkildError as SkildError9, splitCanonicalName, validateSkillDir } from "@skild/core";
674
710
  function sha256Hex(buf) {
675
711
  const h = crypto.createHash("sha256");
676
712
  h.update(buf);
@@ -689,7 +725,7 @@ async function publish(options = {}) {
689
725
  process.exitCode = 1;
690
726
  return;
691
727
  }
692
- const dir = path3.resolve(options.dir || process.cwd());
728
+ const dir = path2.resolve(options.dir || process.cwd());
693
729
  const validation = validateSkillDir(dir);
694
730
  if (!validation.ok) {
695
731
  console.error(chalk13.red("Skill validation failed:"));
@@ -700,6 +736,7 @@ async function publish(options = {}) {
700
736
  const fm = validation.frontmatter;
701
737
  let name = (options.name || fm.name || "").trim();
702
738
  const version2 = (options.skillVersion || fm.version || "").trim();
739
+ const alias = normalizeAlias(options.alias);
703
740
  const description = (options.description || fm.description || "").trim();
704
741
  const tag = (options.tag || "latest").trim() || "latest";
705
742
  const targets = parseTargets(options.targets);
@@ -747,9 +784,19 @@ async function publish(options = {}) {
747
784
  process.exitCode = 1;
748
785
  return;
749
786
  }
787
+ if (alias) {
788
+ try {
789
+ assertValidAlias(alias);
790
+ } catch (error) {
791
+ const message = error instanceof SkildError9 ? error.message : error instanceof Error ? error.message : String(error);
792
+ console.error(chalk13.red(message));
793
+ process.exitCode = 1;
794
+ return;
795
+ }
796
+ }
750
797
  const spinner = createSpinner(`Publishing ${chalk13.cyan(`${name}@${version2}`)} to ${chalk13.dim(registry)}...`);
751
- const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "skild-publish-"));
752
- const tarballPath = path3.join(tempDir, "skill.tgz");
798
+ const tempDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "skild-publish-"));
799
+ const tarballPath = path2.join(tempDir, "skill.tgz");
753
800
  try {
754
801
  await tar.c(
755
802
  {
@@ -761,7 +808,7 @@ async function publish(options = {}) {
761
808
  },
762
809
  ["."]
763
810
  );
764
- const buf = fs3.readFileSync(tarballPath);
811
+ const buf = fs2.readFileSync(tarballPath);
765
812
  const integrity = sha256Hex(buf);
766
813
  const form = new FormData();
767
814
  form.set("version", version2);
@@ -788,11 +835,34 @@ async function publish(options = {}) {
788
835
  process.exitCode = 1;
789
836
  return;
790
837
  }
838
+ if (alias) {
839
+ spinner.text = `Publishing ${chalk13.cyan(`${name}@${version2}`)} \u2014 setting alias ${chalk13.cyan(alias)}...`;
840
+ const aliasRes = await fetchWithTimeout5(
841
+ `${registry}/publisher/skills/${encodeURIComponent(scope)}/${encodeURIComponent(skillName)}/alias`,
842
+ {
843
+ method: "POST",
844
+ headers: {
845
+ authorization: `Bearer ${token}`,
846
+ "content-type": "application/json"
847
+ },
848
+ body: JSON.stringify({ alias })
849
+ },
850
+ 1e4
851
+ );
852
+ const aliasText = await aliasRes.text();
853
+ if (!aliasRes.ok) {
854
+ spinner.fail(`Failed to set alias (${aliasRes.status})`);
855
+ console.error(chalk13.red(aliasText));
856
+ process.exitCode = 1;
857
+ return;
858
+ }
859
+ }
791
860
  if (options.json) {
792
861
  console.log(text);
793
862
  return;
794
863
  }
795
- spinner.succeed(`Published ${chalk13.green(`${name}@${version2}`)} (sha256:${integrity.slice(0, 12)}\u2026)`);
864
+ const suffix = alias ? ` (alias: ${chalk13.cyan(alias)})` : "";
865
+ spinner.succeed(`Published ${chalk13.green(`${name}@${version2}`)}${suffix} (sha256:${integrity.slice(0, 12)}\u2026)`);
796
866
  try {
797
867
  const parsed = JSON.parse(text);
798
868
  const warnings = Array.isArray(parsed.warnings) ? parsed.warnings.map(String).filter(Boolean) : [];
@@ -805,7 +875,7 @@ async function publish(options = {}) {
805
875
  console.error(chalk13.red(message));
806
876
  process.exitCode = 1;
807
877
  } finally {
808
- fs3.rmSync(tempDir, { recursive: true, force: true });
878
+ fs2.rmSync(tempDir, { recursive: true, force: true });
809
879
  }
810
880
  }
811
881
 
@@ -850,7 +920,7 @@ program.name("skild").description("The npm for Agent Skills \u2014 Discover, ins
850
920
  program.command("install <source>").alias("i").description("Install a Skill from a Git URL, degit shorthand, or local directory").option("-t, --target <platform>", `Target platform: ${PLATFORMS2.join(", ")}`, "claude").option("-l, --local", "Install to project-level directory instead of global").option("-f, --force", "Overwrite existing installation").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--json", "Output JSON").action(async (source, options) => {
851
921
  await install(source, options);
852
922
  });
853
- program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS2.join(", ")} (optional; omit to list all)`).option("-l, --local", "List project-level directory instead of global").option("--json", "Output JSON").action(async (options) => list(options));
923
+ program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS2.join(", ")} (optional; omit to list all)`).option("-l, --local", "List project-level directory instead of global").option("--paths", "Show install paths").option("--verbose", "Show skillset dependency details").option("--json", "Output JSON").action(async (options) => list(options));
854
924
  program.command("info <skill>").description("Show installed Skill details").option("-t, --target <platform>", `Target platform: ${PLATFORMS2.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (skill, options) => info(skill, options));
855
925
  program.command("uninstall <skill>").alias("rm").description("Uninstall a Skill").option("-t, --target <platform>", `Target platform: ${PLATFORMS2.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("-f, --force", "Uninstall even if metadata is missing").option("--with-deps", "Uninstall dependencies that are only required by this skill").action(async (skill, options) => uninstall(skill, options));
856
926
  program.command("update [skill]").alias("up").description("Update one or all installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS2.join(", ")}`, "claude").option("-l, --local", "Use project-level directory instead of global").option("--json", "Output JSON").action(async (skill, options) => update(skill, options));
@@ -860,7 +930,7 @@ program.command("signup").description("Create a publisher account in the registr
860
930
  program.command("login").description("Login to a registry and store an access token locally").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--handle-or-email <value>", "Handle or email (optional; will prompt)").option("--password <password>", "Password (optional; will prompt)").option("--token-name <name>", "Token label").option("--json", "Output JSON").action(async (options) => login(options));
861
931
  program.command("logout").description("Remove stored registry credentials").action(async () => logout());
862
932
  program.command("whoami").description("Show current registry identity").action(async () => whoami());
863
- program.command("publish").description("Publish a Skill directory to the registry (hosted tarball)").option("--dir <path>", "Skill directory (defaults to cwd)").option("--name <@publisher/skill>", "Override skill name (defaults to SKILL.md frontmatter)").option("--skill-version <semver>", "Override version (defaults to SKILL.md frontmatter)").option("--description <text>", "Override description (defaults to SKILL.md frontmatter)").option("--targets <list>", "Comma-separated target platforms metadata (optional)").option("--tag <tag>", "Dist-tag (default: latest)", "latest").option("--registry <url>", "Registry base URL (defaults to saved login)").option("--json", "Output JSON").action(async (options) => publish(options));
933
+ program.command("publish").description("Publish a Skill directory to the registry (hosted tarball)").option("--dir <path>", "Skill directory (defaults to cwd)").option("--name <@publisher/skill>", "Override skill name (defaults to SKILL.md frontmatter)").option("--skill-version <semver>", "Override version (defaults to SKILL.md frontmatter)").option("--alias <alias>", "Optional short identifier (global unique) for `skild install <alias>`").option("--description <text>", "Override description (defaults to SKILL.md frontmatter)").option("--targets <list>", "Comma-separated target platforms metadata (optional)").option("--tag <tag>", "Dist-tag (default: latest)", "latest").option("--registry <url>", "Registry base URL (defaults to saved login)").option("--json", "Output JSON").action(async (options) => publish(options));
864
934
  program.command("search <query>").description("Search Skills in the registry").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--limit <n>", "Max results (default: 50)", "50").option("--json", "Output JSON").action(async (query, options) => search(query, options));
865
935
  program.action(() => {
866
936
  console.log(chalk15.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skild",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "The npm for Agent Skills — Discover, install, manage, and publish AI Agent Skills with ease.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  "commander": "^12.1.0",
38
38
  "ora": "^8.0.1",
39
39
  "tar": "^7.4.3",
40
- "@skild/core": "^0.2.7"
40
+ "@skild/core": "^0.2.9"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^20.10.0",