skild 0.3.2 → 0.4.1

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 +320 -95
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6,10 +6,10 @@ import chalk15 from "chalk";
6
6
  import { createRequire } from "module";
7
7
 
8
8
  // src/commands/install.ts
9
- import fs from "fs";
10
- import path from "path";
9
+ import fs2 from "fs";
10
+ import path2 from "path";
11
11
  import chalk2 from "chalk";
12
- import { fetchWithTimeout, installRegistrySkill, installSkill, isValidAlias, loadRegistryAuth, resolveRegistryAlias, resolveRegistryUrl, SkildError, PLATFORMS } from "@skild/core";
12
+ import { deriveChildSource, fetchWithTimeout, installRegistrySkill, installSkill, isValidAlias, loadRegistryAuth, materializeSourceToTemp, resolveRegistryAlias, resolveRegistryUrl, SkildError, PLATFORMS } 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, path3, hasSkillMd) => {
60
+ skillEntry: (name, path4, 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 ${path3}`));
63
+ console.log(chalk.dim(` \u2514\u2500 ${path4}`));
64
64
  },
65
65
  /**
66
66
  * Log installation result details.
@@ -71,6 +71,153 @@ var logger = {
71
71
  }
72
72
  };
73
73
 
74
+ // src/utils/prompt.ts
75
+ import readline from "readline";
76
+ async function promptLine(question, defaultValue) {
77
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
78
+ try {
79
+ const suffix = defaultValue ? ` (${defaultValue})` : "";
80
+ const answer = await new Promise((resolve) => rl.question(`${question}${suffix}: `, resolve));
81
+ const trimmed = answer.trim();
82
+ return trimmed || defaultValue || "";
83
+ } finally {
84
+ rl.close();
85
+ }
86
+ }
87
+ async function promptPassword(question) {
88
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
89
+ return promptLine(question);
90
+ }
91
+ const stdin = process.stdin;
92
+ const stdout = process.stdout;
93
+ stdout.write(`${question}: `);
94
+ const wasRaw = Boolean(stdin.isRaw);
95
+ stdin.setRawMode(true);
96
+ stdin.resume();
97
+ readline.emitKeypressEvents(stdin);
98
+ const buf = [];
99
+ return await new Promise((resolve, reject) => {
100
+ function cleanup() {
101
+ stdin.off("keypress", onKeypress);
102
+ stdin.setRawMode(wasRaw);
103
+ stdin.pause();
104
+ }
105
+ function onKeypress(str, key) {
106
+ if (key?.ctrl && key?.name === "c") {
107
+ stdout.write("\n");
108
+ cleanup();
109
+ const err = new Error("Prompt cancelled");
110
+ err.code = "PROMPT_CANCELLED";
111
+ reject(err);
112
+ return;
113
+ }
114
+ if (key?.name === "return" || key?.name === "enter") {
115
+ stdout.write("\n");
116
+ cleanup();
117
+ resolve(buf.join(""));
118
+ return;
119
+ }
120
+ if (key?.name === "backspace" || key?.name === "delete") {
121
+ if (buf.length) buf.pop();
122
+ return;
123
+ }
124
+ if (!str) return;
125
+ if (key?.ctrl || key?.meta) return;
126
+ buf.push(str);
127
+ }
128
+ stdin.on("keypress", onKeypress);
129
+ });
130
+ }
131
+ async function promptConfirm(question, options = {}) {
132
+ const defaultValue = options.defaultValue ?? false;
133
+ const suffix = defaultValue ? " (Y/n)" : " (y/N)";
134
+ const answer = await promptLine(`${question}${suffix}`);
135
+ const v = answer.trim().toLowerCase();
136
+ if (!v) return defaultValue;
137
+ if (v === "y" || v === "yes") return true;
138
+ if (v === "n" || v === "no") return false;
139
+ return defaultValue;
140
+ }
141
+
142
+ // src/commands/install-discovery.ts
143
+ import fs from "fs";
144
+ import path from "path";
145
+ function parsePositiveInt(input, fallback) {
146
+ if (input == null) return fallback;
147
+ const n = typeof input === "number" ? input : Number(String(input).trim());
148
+ if (!Number.isFinite(n) || n <= 0) return fallback;
149
+ return Math.floor(n);
150
+ }
151
+ function normalizeRelPath(relPath) {
152
+ return relPath.split(path.sep).join("/").replace(/^\/+/, "").replace(/\/+$/, "");
153
+ }
154
+ function shouldSkipDir(name) {
155
+ if (!name) return true;
156
+ if (name === ".git") return true;
157
+ if (name === ".skild") return true;
158
+ if (name === "node_modules") return true;
159
+ if (name === "dist") return true;
160
+ if (name === "build") return true;
161
+ if (name === ".wrangler") return true;
162
+ return false;
163
+ }
164
+ function discoverSkillDirs(rootDir, options) {
165
+ const root = path.resolve(rootDir);
166
+ const found = [];
167
+ const stack = [{ dir: root, depth: 0 }];
168
+ while (stack.length) {
169
+ const next = stack.pop();
170
+ const dir = next.dir;
171
+ const depth = next.depth;
172
+ const skillMd = path.join(dir, "SKILL.md");
173
+ if (fs.existsSync(skillMd)) {
174
+ const relPath = path.relative(root, dir) || ".";
175
+ found.push({ relPath: normalizeRelPath(relPath), absDir: dir });
176
+ if (found.length >= options.maxSkills + 1) return found;
177
+ continue;
178
+ }
179
+ if (depth >= options.maxDepth) continue;
180
+ let entries;
181
+ try {
182
+ entries = fs.readdirSync(dir, { withFileTypes: true });
183
+ } catch {
184
+ continue;
185
+ }
186
+ for (const entry of entries) {
187
+ if (!entry.isDirectory()) continue;
188
+ if (shouldSkipDir(entry.name)) continue;
189
+ const child = path.join(dir, entry.name);
190
+ stack.push({ dir: child, depth: depth + 1 });
191
+ }
192
+ }
193
+ return found;
194
+ }
195
+ function discoverSkillDirsWithHeuristics(rootDir, options) {
196
+ const root = path.resolve(rootDir);
197
+ const candidates = [
198
+ "skills",
199
+ path.join(".agent", "skills"),
200
+ path.join(".claude", "skills"),
201
+ path.join(".codex", "skills"),
202
+ path.join(".github", "skills")
203
+ ];
204
+ for (const rel of candidates) {
205
+ const candidateDir = path.join(root, rel);
206
+ if (!fs.existsSync(candidateDir)) continue;
207
+ try {
208
+ if (!fs.statSync(candidateDir).isDirectory()) continue;
209
+ } catch {
210
+ continue;
211
+ }
212
+ const found = discoverSkillDirs(candidateDir, options).map((s) => ({
213
+ relPath: normalizeRelPath(path.join(rel, s.relPath)),
214
+ absDir: s.absDir
215
+ }));
216
+ if (found.length) return found;
217
+ }
218
+ return discoverSkillDirs(root, options);
219
+ }
220
+
74
221
  // src/commands/install.ts
75
222
  function looksLikeAlias(input) {
76
223
  const s = input.trim();
@@ -78,20 +225,39 @@ function looksLikeAlias(input) {
78
225
  if (s.startsWith("@")) return false;
79
226
  if (s.includes("/") || s.includes("\\")) return false;
80
227
  if (/^https?:\/\//i.test(s) || s.includes("github.com")) return false;
81
- if (fs.existsSync(path.resolve(s))) return false;
228
+ if (fs2.existsSync(path2.resolve(s))) return false;
82
229
  if (!isValidAlias(s)) return false;
83
230
  return true;
84
231
  }
232
+ function printJson(value) {
233
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
234
+ `);
235
+ }
236
+ function previewDiscovered(found, limit = 12) {
237
+ const preview = found.slice(0, limit).map((s) => ` - ${s.relPath}`).join("\n");
238
+ return `${preview}${found.length > limit ? "\n ..." : ""}`;
239
+ }
240
+ function asDiscoveredSkills(discovered, toSuggestedSource, toMaterializedDir) {
241
+ return discovered.map((d) => ({
242
+ relPath: d.relPath,
243
+ suggestedSource: toSuggestedSource(d),
244
+ materializedDir: toMaterializedDir ? toMaterializedDir(d) : void 0
245
+ }));
246
+ }
85
247
  async function install(source, options = {}) {
86
248
  const scope = options.local ? "project" : "global";
87
249
  const auth = loadRegistryAuth();
88
250
  const registryUrlForDeps = options.registry || auth?.registryUrl;
89
251
  const all = Boolean(options.all);
90
252
  const jsonOnly = Boolean(options.json);
253
+ const recursive = Boolean(options.recursive);
254
+ const yes = Boolean(options.yes);
255
+ const maxDepth = parsePositiveInt(options.depth, 6);
256
+ const maxSkills = parsePositiveInt(options.maxSkills, 200);
91
257
  const platform = options.target || "claude";
92
258
  if (all && options.target) {
93
259
  const message = "Invalid options: use either --all or --target, not both.";
94
- if (jsonOnly) console.log(JSON.stringify({ ok: false, error: message }, null, 2));
260
+ if (jsonOnly) printJson({ ok: false, error: message });
95
261
  else console.error(chalk2.red(message));
96
262
  process.exitCode = 1;
97
263
  return;
@@ -106,7 +272,7 @@ async function install(source, options = {}) {
106
272
  }
107
273
  } catch (error) {
108
274
  const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
109
- if (jsonOnly) console.log(JSON.stringify({ ok: false, error: message }, null, 2));
275
+ if (jsonOnly) printJson({ ok: false, error: message });
110
276
  else console.error(chalk2.red(message));
111
277
  process.exitCode = 1;
112
278
  return;
@@ -114,33 +280,148 @@ async function install(source, options = {}) {
114
280
  const targets = all ? [...PLATFORMS] : [platform];
115
281
  const results = [];
116
282
  const errors = [];
117
- try {
118
- const spinner = jsonOnly ? null : createSpinner(
119
- all ? `Installing ${chalk2.cyan(source)} to ${chalk2.dim("all platforms")} (${scope})...` : `Installing ${chalk2.cyan(source)} to ${chalk2.dim(platform)} (${scope})...`
120
- );
283
+ let effectiveRecursive = recursive;
284
+ let recursiveSkillCount = null;
285
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !jsonOnly;
286
+ async function installOne(inputSource, materializedDir) {
121
287
  for (const targetPlatform of targets) {
122
- if (spinner) spinner.text = `Installing ${chalk2.cyan(source)} to ${chalk2.dim(targetPlatform)} (${scope})...`;
123
288
  try {
124
- const record = resolvedSource.startsWith("@") && resolvedSource.includes("/") ? await installRegistrySkill(
125
- { spec: resolvedSource, registryUrl: registryUrlForDeps },
289
+ const record = inputSource.startsWith("@") && inputSource.includes("/") ? await installRegistrySkill(
290
+ { spec: inputSource, registryUrl: registryUrlForDeps },
126
291
  { platform: targetPlatform, scope, force: Boolean(options.force) }
127
292
  ) : await installSkill(
128
- { source: resolvedSource },
293
+ { source: inputSource, materializedDir },
129
294
  { platform: targetPlatform, scope, force: Boolean(options.force), registryUrl: registryUrlForDeps }
130
295
  );
131
296
  results.push(record);
132
297
  void reportDownload(record, registryUrlForDeps);
133
298
  } catch (error) {
134
299
  const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
135
- errors.push({ platform: targetPlatform, error: message });
300
+ errors.push({ platform: targetPlatform, error: message, inputSource });
301
+ }
302
+ }
303
+ }
304
+ async function maybeEnableRecursiveAndInstall(found) {
305
+ if (found.length === 0) return false;
306
+ if (found.length > maxSkills) {
307
+ const message = `Found more than ${maxSkills} skills. Increase --max-skills to proceed.`;
308
+ if (jsonOnly) printJson({ ok: false, error: "TOO_MANY_SKILLS", message, source, resolvedSource, maxSkills });
309
+ else console.error(chalk2.red(message));
310
+ process.exitCode = 1;
311
+ return true;
312
+ }
313
+ if (!effectiveRecursive) {
314
+ if (jsonOnly) {
315
+ const foundOutput = found.map(({ relPath, suggestedSource }) => ({ relPath, suggestedSource }));
316
+ printJson({
317
+ ok: false,
318
+ error: "MULTI_SKILL_SOURCE",
319
+ message: "Source is not a skill root (missing SKILL.md). Multiple skills were found.",
320
+ source,
321
+ resolvedSource,
322
+ found: foundOutput
323
+ });
324
+ process.exitCode = 1;
325
+ return true;
326
+ }
327
+ const headline = found.length === 1 ? `No SKILL.md found at root. Found 1 skill:
328
+ ${previewDiscovered(found)}
329
+ ` : `No SKILL.md found at root. Found ${found.length} skills:
330
+ ${previewDiscovered(found)}
331
+ `;
332
+ console.log(chalk2.yellow(headline));
333
+ const question = found.length === 1 ? "Install the discovered skill?" : "Install all discovered skills?";
334
+ const confirm = yes || interactive && await promptConfirm(question, { defaultValue: found.length === 1 });
335
+ if (!confirm) {
336
+ console.log(chalk2.dim(`Tip: rerun with ${chalk2.cyan("skild install <source> --recursive")} to install all.`));
337
+ process.exitCode = 1;
338
+ return true;
339
+ }
340
+ effectiveRecursive = true;
341
+ }
342
+ recursiveSkillCount = found.length;
343
+ return false;
344
+ }
345
+ try {
346
+ const spinner = jsonOnly ? null : createSpinner(
347
+ all ? `Installing ${chalk2.cyan(source)} to ${chalk2.dim("all platforms")} (${scope})...` : `Installing ${chalk2.cyan(source)} to ${chalk2.dim(platform)} (${scope})...`
348
+ );
349
+ let cleanupMaterialized = null;
350
+ let materializedRoot = null;
351
+ try {
352
+ if (resolvedSource.startsWith("@") && resolvedSource.includes("/")) {
353
+ await installOne(resolvedSource);
354
+ } else {
355
+ const maybeLocalRoot = path2.resolve(resolvedSource);
356
+ const isLocal = fs2.existsSync(maybeLocalRoot);
357
+ if (isLocal) {
358
+ const hasSkillMd = fs2.existsSync(path2.join(maybeLocalRoot, "SKILL.md"));
359
+ if (hasSkillMd) {
360
+ await installOne(resolvedSource);
361
+ } else {
362
+ const discovered = discoverSkillDirsWithHeuristics(maybeLocalRoot, { maxDepth, maxSkills });
363
+ if (discovered.length === 0) {
364
+ const message = `No SKILL.md found at ${maybeLocalRoot} (or within subdirectories).`;
365
+ if (jsonOnly) {
366
+ printJson({ ok: false, error: "SKILL_MD_NOT_FOUND", message, source, resolvedSource });
367
+ } else {
368
+ console.error(chalk2.red(message));
369
+ }
370
+ process.exitCode = 1;
371
+ return;
372
+ }
373
+ const found = asDiscoveredSkills(discovered, (d) => path2.join(maybeLocalRoot, d.relPath));
374
+ const didReturn = await maybeEnableRecursiveAndInstall(found);
375
+ if (didReturn) return;
376
+ if (spinner) spinner.text = `Installing ${chalk2.cyan(source)} \u2014 discovered ${found.length} skills...`;
377
+ for (const skill of found) {
378
+ if (spinner) spinner.text = `Installing ${chalk2.cyan(skill.relPath)} (${scope})...`;
379
+ await installOne(skill.suggestedSource, skill.materializedDir);
380
+ }
381
+ }
382
+ } else {
383
+ const materialized = await materializeSourceToTemp(resolvedSource);
384
+ cleanupMaterialized = materialized.cleanup;
385
+ materializedRoot = materialized.dir;
386
+ const hasSkillMd = fs2.existsSync(path2.join(materializedRoot, "SKILL.md"));
387
+ if (hasSkillMd) {
388
+ await installOne(resolvedSource, materializedRoot);
389
+ } else {
390
+ const discovered = discoverSkillDirsWithHeuristics(materializedRoot, { maxDepth, maxSkills });
391
+ if (discovered.length === 0) {
392
+ const message = `No SKILL.md found in source "${resolvedSource}".`;
393
+ if (jsonOnly) {
394
+ printJson({ ok: false, error: "SKILL_MD_NOT_FOUND", message, source, resolvedSource });
395
+ } else {
396
+ console.error(chalk2.red(message));
397
+ }
398
+ process.exitCode = 1;
399
+ return;
400
+ }
401
+ const found = asDiscoveredSkills(
402
+ discovered,
403
+ (d) => deriveChildSource(resolvedSource, d.relPath),
404
+ (d) => d.absDir
405
+ );
406
+ const didReturn = await maybeEnableRecursiveAndInstall(found);
407
+ if (didReturn) return;
408
+ if (spinner) spinner.text = `Installing ${chalk2.cyan(source)} \u2014 discovered ${found.length} skills...`;
409
+ for (const skill of found) {
410
+ if (spinner) spinner.text = `Installing ${chalk2.cyan(skill.relPath)} (${scope})...`;
411
+ await installOne(skill.suggestedSource, skill.materializedDir);
412
+ }
413
+ }
414
+ }
136
415
  }
416
+ } finally {
417
+ if (cleanupMaterialized) cleanupMaterialized();
137
418
  }
138
419
  if (jsonOnly) {
139
- if (!all) {
140
- if (errors.length) console.log(JSON.stringify({ ok: false, error: errors[0]?.error || "Install failed." }, null, 2));
141
- else console.log(JSON.stringify(results[0] ?? null, null, 2));
420
+ if (!all && !effectiveRecursive) {
421
+ if (errors.length) printJson({ ok: false, error: errors[0]?.error || "Install failed." });
422
+ else printJson(results[0] ?? null);
142
423
  } else {
143
- console.log(JSON.stringify({ ok: errors.length === 0, source, resolvedSource, scope, results, errors }, null, 2));
424
+ printJson({ ok: errors.length === 0, source, resolvedSource, scope, recursive: effectiveRecursive, all, recursiveSkillCount, results, errors });
144
425
  }
145
426
  process.exitCode = errors.length ? 1 : 0;
146
427
  return;
@@ -148,14 +429,17 @@ async function install(source, options = {}) {
148
429
  if (errors.length === 0) {
149
430
  const displayName = results[0]?.canonicalName || results[0]?.name || source;
150
431
  spinner.succeed(
151
- all ? `Installed ${chalk2.green(displayName)} to ${chalk2.dim(`${results.length} platforms`)}` : `Installed ${chalk2.green(displayName)} to ${chalk2.dim(results[0]?.installDir || "")}`
432
+ effectiveRecursive ? `Installed ${chalk2.green(String(recursiveSkillCount ?? results.length))}${chalk2.dim(" skills")} to ${chalk2.dim(`${targets.length} platforms`)}` : all ? `Installed ${chalk2.green(displayName)} to ${chalk2.dim(`${results.length} platforms`)}` : `Installed ${chalk2.green(displayName)} to ${chalk2.dim(results[0]?.installDir || "")}`
152
433
  );
153
434
  } else {
154
- spinner.fail(`Failed to install ${chalk2.red(source)} to ${errors.length}/${targets.length} platforms`);
435
+ const attempted = results.length + errors.length;
436
+ spinner.fail(
437
+ effectiveRecursive ? `Install had failures (${errors.length}/${attempted} installs failed)` : `Failed to install ${chalk2.red(source)} to ${errors.length}/${targets.length} platforms`
438
+ );
155
439
  process.exitCode = 1;
156
440
  if (!all && errors[0]) console.error(chalk2.red(errors[0].error));
157
441
  }
158
- if (!all && results[0]) {
442
+ if (!effectiveRecursive && !all && results[0]) {
159
443
  const record = results[0];
160
444
  if (record.hasSkillMd) logger.installDetail("SKILL.md found \u2713");
161
445
  else logger.installDetail("Warning: No SKILL.md found", true);
@@ -164,12 +448,13 @@ async function install(source, options = {}) {
164
448
  } else if (record.skill?.validation?.ok) {
165
449
  logger.installDetail(`Validation: ${chalk2.green("ok")}`);
166
450
  }
167
- } else if (all) {
168
- for (const r of results) {
451
+ } else if (effectiveRecursive || all) {
452
+ for (const r of results.slice(0, 60)) {
169
453
  const displayName = r.canonicalName || r.name;
170
454
  const suffix = r.hasSkillMd ? chalk2.green("\u2713") : chalk2.yellow("\u26A0");
171
455
  console.log(` ${suffix} ${chalk2.cyan(displayName)} \u2192 ${chalk2.dim(r.platform)}`);
172
456
  }
457
+ if (results.length > 60) console.log(chalk2.dim(` ... and ${results.length - 60} more`));
173
458
  if (errors.length) {
174
459
  console.log(chalk2.yellow("\nFailures:"));
175
460
  for (const e of errors) console.log(chalk2.yellow(` - ${e.platform}: ${e.error}`));
@@ -178,7 +463,7 @@ async function install(source, options = {}) {
178
463
  }
179
464
  } catch (error) {
180
465
  const message = error instanceof SkildError ? error.message : error instanceof Error ? error.message : String(error);
181
- if (jsonOnly) console.log(JSON.stringify({ ok: false, error: message }, null, 2));
466
+ if (jsonOnly) printJson({ ok: false, error: message });
182
467
  else console.error(chalk2.red(message));
183
468
  process.exitCode = 1;
184
469
  }
@@ -500,66 +785,6 @@ async function init(name, options = {}) {
500
785
  // src/commands/signup.ts
501
786
  import chalk9 from "chalk";
502
787
  import { fetchWithTimeout as fetchWithTimeout2, resolveRegistryUrl as resolveRegistryUrl2, SkildError as SkildError6 } from "@skild/core";
503
-
504
- // src/utils/prompt.ts
505
- import readline from "readline";
506
- async function promptLine(question, defaultValue) {
507
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
508
- try {
509
- const suffix = defaultValue ? ` (${defaultValue})` : "";
510
- const answer = await new Promise((resolve) => rl.question(`${question}${suffix}: `, resolve));
511
- const trimmed = answer.trim();
512
- return trimmed || defaultValue || "";
513
- } finally {
514
- rl.close();
515
- }
516
- }
517
- async function promptPassword(question) {
518
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
519
- return promptLine(question);
520
- }
521
- const stdin = process.stdin;
522
- const stdout = process.stdout;
523
- stdout.write(`${question}: `);
524
- const wasRaw = Boolean(stdin.isRaw);
525
- stdin.setRawMode(true);
526
- stdin.resume();
527
- readline.emitKeypressEvents(stdin);
528
- const buf = [];
529
- return await new Promise((resolve, reject) => {
530
- function cleanup() {
531
- stdin.off("keypress", onKeypress);
532
- stdin.setRawMode(wasRaw);
533
- stdin.pause();
534
- }
535
- function onKeypress(str, key) {
536
- if (key?.ctrl && key?.name === "c") {
537
- stdout.write("\n");
538
- cleanup();
539
- const err = new Error("Prompt cancelled");
540
- err.code = "PROMPT_CANCELLED";
541
- reject(err);
542
- return;
543
- }
544
- if (key?.name === "return" || key?.name === "enter") {
545
- stdout.write("\n");
546
- cleanup();
547
- resolve(buf.join(""));
548
- return;
549
- }
550
- if (key?.name === "backspace" || key?.name === "delete") {
551
- if (buf.length) buf.pop();
552
- return;
553
- }
554
- if (!str) return;
555
- if (key?.ctrl || key?.meta) return;
556
- buf.push(str);
557
- }
558
- stdin.on("keypress", onKeypress);
559
- });
560
- }
561
-
562
- // src/commands/signup.ts
563
788
  async function signup(options) {
564
789
  const registry = resolveRegistryUrl2(options.registry);
565
790
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
@@ -752,9 +977,9 @@ async function whoami() {
752
977
  }
753
978
 
754
979
  // src/commands/publish.ts
755
- import fs2 from "fs";
980
+ import fs3 from "fs";
756
981
  import os from "os";
757
- import path2 from "path";
982
+ import path3 from "path";
758
983
  import crypto from "crypto";
759
984
  import * as tar from "tar";
760
985
  import chalk13 from "chalk";
@@ -777,7 +1002,7 @@ async function publish(options = {}) {
777
1002
  process.exitCode = 1;
778
1003
  return;
779
1004
  }
780
- const dir = path2.resolve(options.dir || process.cwd());
1005
+ const dir = path3.resolve(options.dir || process.cwd());
781
1006
  const validation = validateSkillDir(dir);
782
1007
  if (!validation.ok) {
783
1008
  console.error(chalk13.red("Skill validation failed:"));
@@ -847,8 +1072,8 @@ async function publish(options = {}) {
847
1072
  }
848
1073
  }
849
1074
  const spinner = createSpinner(`Publishing ${chalk13.cyan(`${name}@${version2}`)} to ${chalk13.dim(registry)}...`);
850
- const tempDir = fs2.mkdtempSync(path2.join(os.tmpdir(), "skild-publish-"));
851
- const tarballPath = path2.join(tempDir, "skill.tgz");
1075
+ const tempDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "skild-publish-"));
1076
+ const tarballPath = path3.join(tempDir, "skill.tgz");
852
1077
  try {
853
1078
  await tar.c(
854
1079
  {
@@ -860,7 +1085,7 @@ async function publish(options = {}) {
860
1085
  },
861
1086
  ["."]
862
1087
  );
863
- const buf = fs2.readFileSync(tarballPath);
1088
+ const buf = fs3.readFileSync(tarballPath);
864
1089
  const integrity = sha256Hex(buf);
865
1090
  const form = new FormData();
866
1091
  form.set("version", version2);
@@ -927,7 +1152,7 @@ async function publish(options = {}) {
927
1152
  console.error(chalk13.red(message));
928
1153
  process.exitCode = 1;
929
1154
  } finally {
930
- fs2.rmSync(tempDir, { recursive: true, force: true });
1155
+ fs3.rmSync(tempDir, { recursive: true, force: true });
931
1156
  }
932
1157
  }
933
1158
 
@@ -969,7 +1194,7 @@ var require2 = createRequire(import.meta.url);
969
1194
  var { version } = require2("../package.json");
970
1195
  var program = new Command();
971
1196
  program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
972
- 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: ${PLATFORMS3.join(", ")}`).option("--all", `Install to all platforms: ${PLATFORMS3.join(", ")}`).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) => {
1197
+ 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: ${PLATFORMS3.join(", ")}`).option("--all", `Install to all platforms: ${PLATFORMS3.join(", ")}`).option("--recursive", "If source is a multi-skill directory/repo, install all discovered skills").option("-y, --yes", "Skip confirmation prompts (assume yes)").option("--depth <n>", "Max directory depth to scan for SKILL.md (default: 6)", "6").option("--max-skills <n>", "Max discovered skills to install (default: 200)", "200").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) => {
973
1198
  await install(source, options);
974
1199
  });
975
1200
  program.command("list").alias("ls").description("List installed Skills").option("-t, --target <platform>", `Target platform: ${PLATFORMS3.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));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skild",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
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.9"
40
+ "@skild/core": "^0.4.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^20.10.0",