skild 0.1.3 → 0.1.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.
- package/dist/index.js +391 -29
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk15 from "chalk";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
7
|
|
|
8
8
|
// src/commands/install.ts
|
|
9
9
|
import chalk2 from "chalk";
|
|
10
|
-
import { installSkill, SkildError } from "@skild/core";
|
|
10
|
+
import { installRegistrySkill, installSkill, SkildError } from "@skild/core";
|
|
11
11
|
|
|
12
12
|
// src/utils/logger.ts
|
|
13
13
|
import chalk from "chalk";
|
|
@@ -55,10 +55,10 @@ var logger = {
|
|
|
55
55
|
/**
|
|
56
56
|
* Log a skill entry with status indicator.
|
|
57
57
|
*/
|
|
58
|
-
skillEntry: (name,
|
|
58
|
+
skillEntry: (name, path2, hasSkillMd) => {
|
|
59
59
|
const status = hasSkillMd ? chalk.green("\u2713") : chalk.yellow("\u26A0");
|
|
60
60
|
console.log(` ${status} ${chalk.cyan(name)}`);
|
|
61
|
-
console.log(chalk.dim(` \u2514\u2500 ${
|
|
61
|
+
console.log(chalk.dim(` \u2514\u2500 ${path2}`));
|
|
62
62
|
},
|
|
63
63
|
/**
|
|
64
64
|
* Log installation result details.
|
|
@@ -75,15 +75,12 @@ async function install(source, options = {}) {
|
|
|
75
75
|
const scope = options.local ? "project" : "global";
|
|
76
76
|
const spinner = createSpinner(`Installing ${chalk2.cyan(source)} to ${chalk2.dim(platform)} (${scope})...`);
|
|
77
77
|
try {
|
|
78
|
-
const record = await
|
|
79
|
-
{ source },
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
spinner.succeed(`Installed ${chalk2.green(record.name)} to ${chalk2.dim(record.installDir)}`);
|
|
78
|
+
const record = source.trim().startsWith("@") && source.includes("/") ? await installRegistrySkill(
|
|
79
|
+
{ spec: source, registryUrl: options.registry },
|
|
80
|
+
{ platform, scope, force: Boolean(options.force) }
|
|
81
|
+
) : await installSkill({ source }, { platform, scope, force: Boolean(options.force) });
|
|
82
|
+
const displayName = record.canonicalName || record.name;
|
|
83
|
+
spinner.succeed(`Installed ${chalk2.green(displayName)} to ${chalk2.dim(record.installDir)}`);
|
|
87
84
|
if (options.json) {
|
|
88
85
|
console.log(JSON.stringify(record, null, 2));
|
|
89
86
|
return;
|
|
@@ -128,7 +125,8 @@ async function list(options = {}) {
|
|
|
128
125
|
`));
|
|
129
126
|
for (const s of skills) {
|
|
130
127
|
const status = s.hasSkillMd ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
|
|
131
|
-
|
|
128
|
+
const displayName = s.record?.canonicalName || s.name;
|
|
129
|
+
console.log(` ${status} ${chalk3.cyan(displayName)}`);
|
|
132
130
|
console.log(chalk3.dim(` \u2514\u2500 ${s.installDir}`));
|
|
133
131
|
}
|
|
134
132
|
console.log("");
|
|
@@ -157,7 +155,8 @@ async function list(options = {}) {
|
|
|
157
155
|
}
|
|
158
156
|
for (const s of platformSkills) {
|
|
159
157
|
const status = s.hasSkillMd ? chalk3.green("\u2713") : chalk3.yellow("\u26A0");
|
|
160
|
-
|
|
158
|
+
const displayName = s.record?.canonicalName || s.name;
|
|
159
|
+
console.log(` ${status} ${chalk3.cyan(displayName)}`);
|
|
161
160
|
console.log(chalk3.dim(` \u2514\u2500 ${s.installDir}`));
|
|
162
161
|
}
|
|
163
162
|
}
|
|
@@ -166,18 +165,20 @@ async function list(options = {}) {
|
|
|
166
165
|
|
|
167
166
|
// src/commands/info.ts
|
|
168
167
|
import chalk4 from "chalk";
|
|
169
|
-
import { getSkillInfo, SkildError as SkildError2 } from "@skild/core";
|
|
168
|
+
import { canonicalNameToInstallDirName, getSkillInfo, SkildError as SkildError2 } from "@skild/core";
|
|
170
169
|
async function info(skill, options = {}) {
|
|
171
170
|
const platform = options.target || "claude";
|
|
172
171
|
const scope = options.local ? "project" : "global";
|
|
173
172
|
try {
|
|
174
|
-
const
|
|
173
|
+
const resolvedName = skill.trim().startsWith("@") && skill.includes("/") ? canonicalNameToInstallDirName(skill.trim()) : skill;
|
|
174
|
+
const record = getSkillInfo(resolvedName, { platform, scope });
|
|
175
175
|
if (options.json) {
|
|
176
176
|
console.log(JSON.stringify(record, null, 2));
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
+
const displayName = record.canonicalName || record.name;
|
|
179
180
|
console.log(chalk4.bold(`
|
|
180
|
-
${chalk4.cyan(
|
|
181
|
+
${chalk4.cyan(displayName)}
|
|
181
182
|
`));
|
|
182
183
|
console.log(` ${chalk4.dim("Path:")} ${record.installDir}`);
|
|
183
184
|
console.log(` ${chalk4.dim("Source:")} ${record.source}`);
|
|
@@ -206,16 +207,18 @@ ${chalk4.cyan(record.name)}
|
|
|
206
207
|
|
|
207
208
|
// src/commands/uninstall.ts
|
|
208
209
|
import chalk5 from "chalk";
|
|
209
|
-
import { uninstallSkill, SkildError as SkildError3 } from "@skild/core";
|
|
210
|
+
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName2, uninstallSkill, SkildError as SkildError3 } from "@skild/core";
|
|
210
211
|
async function uninstall(skill, options = {}) {
|
|
211
212
|
const platform = options.target || "claude";
|
|
212
213
|
const scope = options.local ? "project" : "global";
|
|
213
|
-
const
|
|
214
|
+
const canonical = skill.trim();
|
|
215
|
+
const resolvedName = canonical.startsWith("@") && canonical.includes("/") ? canonicalNameToInstallDirName2(canonical) : canonical;
|
|
216
|
+
const spinner = createSpinner(`Uninstalling ${chalk5.cyan(canonical)} from ${chalk5.dim(platform)} (${scope})...`);
|
|
214
217
|
try {
|
|
215
|
-
uninstallSkill(
|
|
216
|
-
spinner.succeed(`Uninstalled ${chalk5.green(
|
|
218
|
+
uninstallSkill(resolvedName, { platform, scope, allowMissingMetadata: Boolean(options.force) });
|
|
219
|
+
spinner.succeed(`Uninstalled ${chalk5.green(canonical)}`);
|
|
217
220
|
} catch (error) {
|
|
218
|
-
spinner.fail(`Failed to uninstall ${chalk5.red(
|
|
221
|
+
spinner.fail(`Failed to uninstall ${chalk5.red(canonical)}`);
|
|
219
222
|
const message = error instanceof SkildError3 ? error.message : error instanceof Error ? error.message : String(error);
|
|
220
223
|
console.error(chalk5.red(message));
|
|
221
224
|
process.exitCode = 1;
|
|
@@ -224,14 +227,15 @@ async function uninstall(skill, options = {}) {
|
|
|
224
227
|
|
|
225
228
|
// src/commands/update.ts
|
|
226
229
|
import chalk6 from "chalk";
|
|
227
|
-
import { updateSkill, SkildError as SkildError4 } from "@skild/core";
|
|
230
|
+
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName3, updateSkill, SkildError as SkildError4 } from "@skild/core";
|
|
228
231
|
async function update(skill, options = {}) {
|
|
229
232
|
const platform = options.target || "claude";
|
|
230
233
|
const scope = options.local ? "project" : "global";
|
|
231
234
|
const label = skill ? skill : "all skills";
|
|
235
|
+
const resolvedName = skill && skill.trim().startsWith("@") && skill.includes("/") ? canonicalNameToInstallDirName3(skill.trim()) : skill;
|
|
232
236
|
const spinner = createSpinner(`Updating ${chalk6.cyan(label)} on ${chalk6.dim(platform)} (${scope})...`);
|
|
233
237
|
try {
|
|
234
|
-
const results = await updateSkill(
|
|
238
|
+
const results = await updateSkill(resolvedName, { platform, scope });
|
|
235
239
|
spinner.succeed(`Updated ${chalk6.green(results.length.toString())} skill(s).`);
|
|
236
240
|
if (options.json) {
|
|
237
241
|
console.log(JSON.stringify(results, null, 2));
|
|
@@ -246,12 +250,13 @@ async function update(skill, options = {}) {
|
|
|
246
250
|
|
|
247
251
|
// src/commands/validate.ts
|
|
248
252
|
import chalk7 from "chalk";
|
|
249
|
-
import { validateSkill } from "@skild/core";
|
|
253
|
+
import { canonicalNameToInstallDirName as canonicalNameToInstallDirName4, validateSkill } from "@skild/core";
|
|
250
254
|
async function validate(target, options = {}) {
|
|
251
255
|
const platform = options.target || "claude";
|
|
252
256
|
const scope = options.local ? "project" : "global";
|
|
253
257
|
const value = target || ".";
|
|
254
|
-
const
|
|
258
|
+
const resolvedValue = value.trim().startsWith("@") && value.includes("/") ? canonicalNameToInstallDirName4(value.trim()) : value;
|
|
259
|
+
const result = validateSkill(resolvedValue, { platform, scope });
|
|
255
260
|
if (options.json) {
|
|
256
261
|
console.log(JSON.stringify(result, null, 2));
|
|
257
262
|
process.exitCode = result.ok ? 0 : 1;
|
|
@@ -291,13 +296,364 @@ async function init(name, options = {}) {
|
|
|
291
296
|
}
|
|
292
297
|
}
|
|
293
298
|
|
|
299
|
+
// src/commands/signup.ts
|
|
300
|
+
import chalk9 from "chalk";
|
|
301
|
+
import { fetchWithTimeout, resolveRegistryUrl, SkildError as SkildError6 } from "@skild/core";
|
|
302
|
+
|
|
303
|
+
// src/utils/prompt.ts
|
|
304
|
+
import readline from "readline";
|
|
305
|
+
async function promptLine(question, defaultValue) {
|
|
306
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
307
|
+
try {
|
|
308
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
309
|
+
const answer = await new Promise((resolve) => rl.question(`${question}${suffix}: `, resolve));
|
|
310
|
+
const trimmed = answer.trim();
|
|
311
|
+
return trimmed || defaultValue || "";
|
|
312
|
+
} finally {
|
|
313
|
+
rl.close();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async function promptPassword(question) {
|
|
317
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
318
|
+
rl.stdoutMuted = true;
|
|
319
|
+
rl._writeToOutput = function _writeToOutput(stringToWrite) {
|
|
320
|
+
if (this.stdoutMuted) return;
|
|
321
|
+
this.output.write(stringToWrite);
|
|
322
|
+
};
|
|
323
|
+
try {
|
|
324
|
+
const answer = await new Promise((resolve) => rl.question(`${question}: `, resolve));
|
|
325
|
+
return String(answer || "");
|
|
326
|
+
} finally {
|
|
327
|
+
rl.stdoutMuted = false;
|
|
328
|
+
rl.close();
|
|
329
|
+
process.stdout.write("\n");
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/commands/signup.ts
|
|
334
|
+
async function signup(options) {
|
|
335
|
+
const registry = resolveRegistryUrl(options.registry);
|
|
336
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
337
|
+
const email = options.email?.trim() || "";
|
|
338
|
+
const handle = options.handle?.trim() || "";
|
|
339
|
+
const password = options.password || "";
|
|
340
|
+
if ((!email || !handle || !password) && (!interactive || options.json)) {
|
|
341
|
+
console.error(chalk9.red("Missing signup fields. Use --email/--handle/--password, or run `skild signup` interactively."));
|
|
342
|
+
process.exitCode = 1;
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const finalEmail = email || await promptLine("Email");
|
|
346
|
+
const finalHandle = handle || (await promptLine("Handle (publisher scope)", void 0)).toLowerCase();
|
|
347
|
+
const finalPassword = password || await promptPassword("Password");
|
|
348
|
+
let text = "";
|
|
349
|
+
try {
|
|
350
|
+
const res = await fetchWithTimeout(
|
|
351
|
+
`${registry}/auth/signup`,
|
|
352
|
+
{
|
|
353
|
+
method: "POST",
|
|
354
|
+
headers: { "content-type": "application/json" },
|
|
355
|
+
body: JSON.stringify({
|
|
356
|
+
email: finalEmail,
|
|
357
|
+
handle: finalHandle,
|
|
358
|
+
password: finalPassword
|
|
359
|
+
})
|
|
360
|
+
},
|
|
361
|
+
1e4
|
|
362
|
+
);
|
|
363
|
+
text = await res.text();
|
|
364
|
+
if (!res.ok) {
|
|
365
|
+
console.error(chalk9.red(`Signup failed (${res.status}): ${text}`));
|
|
366
|
+
process.exitCode = 1;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
const message = error instanceof SkildError6 ? error.message : error instanceof Error ? error.message : String(error);
|
|
371
|
+
console.error(chalk9.red(`Signup failed: ${message}`));
|
|
372
|
+
process.exitCode = 1;
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (options.json) {
|
|
376
|
+
console.log(text || JSON.stringify({ ok: true }, null, 2));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
console.log(chalk9.green("Signup successful."));
|
|
380
|
+
console.log(chalk9.dim("Next: run `skild login`"));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/commands/login.ts
|
|
384
|
+
import chalk10 from "chalk";
|
|
385
|
+
import { fetchWithTimeout as fetchWithTimeout2, resolveRegistryUrl as resolveRegistryUrl2, saveRegistryAuth, SkildError as SkildError7 } from "@skild/core";
|
|
386
|
+
async function login(options) {
|
|
387
|
+
const registry = resolveRegistryUrl2(options.registry);
|
|
388
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
389
|
+
const handleOrEmail = options.handleOrEmail?.trim() || "";
|
|
390
|
+
const password = options.password || "";
|
|
391
|
+
if ((!handleOrEmail || !password) && (!interactive || options.json)) {
|
|
392
|
+
console.error(chalk10.red("Missing credentials. Use --handle-or-email and --password, or run `skild login` interactively."));
|
|
393
|
+
process.exitCode = 1;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const finalHandleOrEmail = handleOrEmail || await promptLine("Handle or email");
|
|
397
|
+
const finalPassword = password || await promptPassword("Password");
|
|
398
|
+
const finalTokenName = options.tokenName?.trim() || void 0;
|
|
399
|
+
let text = "";
|
|
400
|
+
try {
|
|
401
|
+
const res = await fetchWithTimeout2(
|
|
402
|
+
`${registry}/auth/login`,
|
|
403
|
+
{
|
|
404
|
+
method: "POST",
|
|
405
|
+
headers: { "content-type": "application/json" },
|
|
406
|
+
body: JSON.stringify({
|
|
407
|
+
handleOrEmail: finalHandleOrEmail,
|
|
408
|
+
password: finalPassword,
|
|
409
|
+
tokenName: finalTokenName
|
|
410
|
+
})
|
|
411
|
+
},
|
|
412
|
+
1e4
|
|
413
|
+
);
|
|
414
|
+
text = await res.text();
|
|
415
|
+
if (!res.ok) {
|
|
416
|
+
console.error(chalk10.red(`Login failed (${res.status}): ${text}`));
|
|
417
|
+
process.exitCode = 1;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
const message = error instanceof SkildError7 ? error.message : error instanceof Error ? error.message : String(error);
|
|
422
|
+
console.error(chalk10.red(`Login failed: ${message}`));
|
|
423
|
+
process.exitCode = 1;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const json = JSON.parse(text);
|
|
427
|
+
saveRegistryAuth({
|
|
428
|
+
schemaVersion: 1,
|
|
429
|
+
registryUrl: registry,
|
|
430
|
+
token: json.token,
|
|
431
|
+
publisher: json.publisher,
|
|
432
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
433
|
+
});
|
|
434
|
+
if (options.json) {
|
|
435
|
+
console.log(JSON.stringify({ ok: true, publisher: json.publisher }, null, 2));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
console.log(chalk10.green(`Logged in as ${chalk10.cyan(json.publisher.handle)}.`));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/commands/logout.ts
|
|
442
|
+
import chalk11 from "chalk";
|
|
443
|
+
import { clearRegistryAuth } from "@skild/core";
|
|
444
|
+
async function logout() {
|
|
445
|
+
clearRegistryAuth();
|
|
446
|
+
console.log(chalk11.green("Logged out."));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/commands/whoami.ts
|
|
450
|
+
import chalk12 from "chalk";
|
|
451
|
+
import { fetchWithTimeout as fetchWithTimeout3, loadRegistryAuth, resolveRegistryUrl as resolveRegistryUrl3, SkildError as SkildError8 } from "@skild/core";
|
|
452
|
+
async function whoami() {
|
|
453
|
+
const auth = loadRegistryAuth();
|
|
454
|
+
if (!auth) {
|
|
455
|
+
console.error(chalk12.red("Not logged in. Run `skild login` first."));
|
|
456
|
+
process.exitCode = 1;
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const registryUrl = resolveRegistryUrl3(auth.registryUrl);
|
|
460
|
+
try {
|
|
461
|
+
const res = await fetchWithTimeout3(
|
|
462
|
+
`${registryUrl}/auth/me`,
|
|
463
|
+
{ headers: { authorization: `Bearer ${auth.token}`, accept: "application/json" } },
|
|
464
|
+
5e3
|
|
465
|
+
);
|
|
466
|
+
const text = await res.text();
|
|
467
|
+
if (!res.ok) {
|
|
468
|
+
console.error(chalk12.red(`whoami failed (${res.status}): ${text}`));
|
|
469
|
+
process.exitCode = 1;
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const json = JSON.parse(text);
|
|
473
|
+
console.log(chalk12.cyan(json.publisher.handle));
|
|
474
|
+
} catch (error) {
|
|
475
|
+
const message = error instanceof SkildError8 ? error.message : error instanceof Error ? error.message : String(error);
|
|
476
|
+
console.error(chalk12.red(`whoami failed: ${message}`));
|
|
477
|
+
console.error(chalk12.dim("Tip: if you previously logged into a local registry, run `skild logout` then `skild login`."));
|
|
478
|
+
process.exitCode = 1;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/commands/publish.ts
|
|
483
|
+
import fs from "fs";
|
|
484
|
+
import os from "os";
|
|
485
|
+
import path from "path";
|
|
486
|
+
import crypto from "crypto";
|
|
487
|
+
import * as tar from "tar";
|
|
488
|
+
import chalk13 from "chalk";
|
|
489
|
+
import { fetchWithTimeout as fetchWithTimeout4, loadRegistryAuth as loadRegistryAuth2, resolveRegistryUrl as resolveRegistryUrl4, SkildError as SkildError9, splitCanonicalName, validateSkillDir } from "@skild/core";
|
|
490
|
+
function sha256Hex(buf) {
|
|
491
|
+
const h = crypto.createHash("sha256");
|
|
492
|
+
h.update(buf);
|
|
493
|
+
return h.digest("hex");
|
|
494
|
+
}
|
|
495
|
+
function parseTargets(raw) {
|
|
496
|
+
if (!raw?.trim()) return [];
|
|
497
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
498
|
+
}
|
|
499
|
+
async function publish(options = {}) {
|
|
500
|
+
const auth = loadRegistryAuth2();
|
|
501
|
+
const registry = resolveRegistryUrl4(options.registry || auth?.registryUrl);
|
|
502
|
+
const token = auth?.token;
|
|
503
|
+
if (!token) {
|
|
504
|
+
console.error(chalk13.red("Not logged in. Run `skild login` first."));
|
|
505
|
+
process.exitCode = 1;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const dir = path.resolve(options.dir || process.cwd());
|
|
509
|
+
const validation = validateSkillDir(dir);
|
|
510
|
+
if (!validation.ok) {
|
|
511
|
+
console.error(chalk13.red("Skill validation failed:"));
|
|
512
|
+
for (const issue of validation.issues) console.error(chalk13.red(`- ${issue.message}`));
|
|
513
|
+
process.exitCode = 1;
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const fm = validation.frontmatter;
|
|
517
|
+
let name = (options.name || fm.name || "").trim();
|
|
518
|
+
const version2 = (options.skillVersion || fm.version || "").trim();
|
|
519
|
+
const description = (options.description || fm.description || "").trim();
|
|
520
|
+
const tag = (options.tag || "latest").trim() || "latest";
|
|
521
|
+
const targets = parseTargets(options.targets);
|
|
522
|
+
if (!name) {
|
|
523
|
+
console.error(chalk13.red("Missing name. Provide SKILL.md frontmatter.name or --name."));
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (!name.startsWith("@")) {
|
|
528
|
+
const seg = name.trim();
|
|
529
|
+
if (!/^[a-z0-9][a-z0-9-]{1,63}$/.test(seg)) {
|
|
530
|
+
console.error(chalk13.red("Invalid name. Use @publisher/skill or a simple skill name (lowercase letters/digits/dashes)."));
|
|
531
|
+
process.exitCode = 1;
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const meRes = await fetchWithTimeout4(
|
|
535
|
+
`${registry}/auth/me`,
|
|
536
|
+
{ headers: { authorization: `Bearer ${token}` } },
|
|
537
|
+
1e4
|
|
538
|
+
);
|
|
539
|
+
const meText = await meRes.text();
|
|
540
|
+
if (!meRes.ok) {
|
|
541
|
+
console.error(chalk13.red(`Failed to infer publisher scope (${meRes.status}): ${meText}`));
|
|
542
|
+
process.exitCode = 1;
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const meJson = JSON.parse(meText);
|
|
546
|
+
const handle = String(meJson?.publisher?.handle || "").trim().toLowerCase();
|
|
547
|
+
if (!handle) {
|
|
548
|
+
console.error(chalk13.red("Failed to infer publisher scope from registry response."));
|
|
549
|
+
process.exitCode = 1;
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
name = `@${handle}/${seg}`;
|
|
553
|
+
}
|
|
554
|
+
if (!/^@[a-z0-9][a-z0-9-]{1,31}\/[a-z0-9][a-z0-9-]{1,63}$/.test(name)) {
|
|
555
|
+
console.error(chalk13.red("Invalid publish name. Expected @publisher/skill (lowercase letters/digits/dashes)."));
|
|
556
|
+
process.exitCode = 1;
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (!version2) {
|
|
560
|
+
console.error(chalk13.red("Missing version. Provide semver like 1.2.3 via SKILL.md frontmatter or --skill-version."));
|
|
561
|
+
process.exitCode = 1;
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const spinner = createSpinner(`Publishing ${chalk13.cyan(`${name}@${version2}`)} to ${chalk13.dim(registry)}...`);
|
|
565
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "skild-publish-"));
|
|
566
|
+
const tarballPath = path.join(tempDir, "skill.tgz");
|
|
567
|
+
try {
|
|
568
|
+
await tar.c(
|
|
569
|
+
{
|
|
570
|
+
gzip: true,
|
|
571
|
+
file: tarballPath,
|
|
572
|
+
cwd: dir,
|
|
573
|
+
portable: true,
|
|
574
|
+
filter: (p) => !p.startsWith(".skild") && !p.startsWith(".git")
|
|
575
|
+
},
|
|
576
|
+
["."]
|
|
577
|
+
);
|
|
578
|
+
const buf = fs.readFileSync(tarballPath);
|
|
579
|
+
const integrity = sha256Hex(buf);
|
|
580
|
+
const form = new FormData();
|
|
581
|
+
form.set("version", version2);
|
|
582
|
+
form.set("description", description);
|
|
583
|
+
form.set("targets", JSON.stringify(targets));
|
|
584
|
+
form.set("tag", tag);
|
|
585
|
+
form.append("tarball", new Blob([buf], { type: "application/gzip" }), "skill.tgz");
|
|
586
|
+
const { scope, name: skillName } = splitCanonicalName(name);
|
|
587
|
+
const res = await fetchWithTimeout4(
|
|
588
|
+
`${registry}/skills/${encodeURIComponent(scope)}/${encodeURIComponent(skillName)}/publish`,
|
|
589
|
+
{
|
|
590
|
+
method: "POST",
|
|
591
|
+
headers: { authorization: `Bearer ${token}` },
|
|
592
|
+
body: form
|
|
593
|
+
},
|
|
594
|
+
3e4
|
|
595
|
+
);
|
|
596
|
+
const text = await res.text();
|
|
597
|
+
if (!res.ok) {
|
|
598
|
+
spinner.fail(`Publish failed (${res.status})`);
|
|
599
|
+
console.error(chalk13.red(text));
|
|
600
|
+
process.exitCode = 1;
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (options.json) {
|
|
604
|
+
console.log(text);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
spinner.succeed(`Published ${chalk13.green(`${name}@${version2}`)} (sha256:${integrity.slice(0, 12)}\u2026)`);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
spinner.fail("Publish failed");
|
|
610
|
+
const message = error instanceof SkildError9 ? error.message : error instanceof Error ? error.message : String(error);
|
|
611
|
+
console.error(chalk13.red(message));
|
|
612
|
+
process.exitCode = 1;
|
|
613
|
+
} finally {
|
|
614
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/commands/search.ts
|
|
619
|
+
import chalk14 from "chalk";
|
|
620
|
+
import { resolveRegistryUrl as resolveRegistryUrl5, searchRegistrySkills, SkildError as SkildError10 } from "@skild/core";
|
|
621
|
+
async function search(query, options = {}) {
|
|
622
|
+
const registryUrl = resolveRegistryUrl5(options.registry);
|
|
623
|
+
const limit = Math.min(Math.max(Number.parseInt(options.limit || "50", 10) || 50, 1), 100);
|
|
624
|
+
try {
|
|
625
|
+
const skills = await searchRegistrySkills(registryUrl, query, limit);
|
|
626
|
+
if (options.json) {
|
|
627
|
+
console.log(JSON.stringify({ ok: true, registryUrl, skills }, null, 2));
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (!skills.length) {
|
|
631
|
+
console.log(chalk14.dim("No results."));
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
console.log(chalk14.bold(`
|
|
635
|
+
\u{1F50E} Results (${skills.length}) \u2014 ${chalk14.dim(registryUrl)}
|
|
636
|
+
`));
|
|
637
|
+
for (const s of skills) {
|
|
638
|
+
const name = String(s.name || "").trim();
|
|
639
|
+
const desc = String(s.description || "").trim();
|
|
640
|
+
if (!name) continue;
|
|
641
|
+
console.log(` ${chalk14.cyan(name)}${desc ? chalk14.dim(` \u2014 ${desc}`) : ""}`);
|
|
642
|
+
}
|
|
643
|
+
} catch (error) {
|
|
644
|
+
const message = error instanceof SkildError10 ? error.message : error instanceof Error ? error.message : String(error);
|
|
645
|
+
console.error(chalk14.red(message));
|
|
646
|
+
process.exitCode = 1;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
294
650
|
// src/index.ts
|
|
295
651
|
import { PLATFORMS as PLATFORMS2 } from "@skild/core";
|
|
296
652
|
var require2 = createRequire(import.meta.url);
|
|
297
653
|
var { version } = require2("../package.json");
|
|
298
654
|
var program = new Command();
|
|
299
655
|
program.name("skild").description("The npm for Agent Skills \u2014 Discover, install, manage, and publish AI Agent Skills with ease.").version(version);
|
|
300
|
-
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("--json", "Output JSON").action(async (source, options) => {
|
|
656
|
+
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) => {
|
|
301
657
|
await install(source, options);
|
|
302
658
|
});
|
|
303
659
|
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));
|
|
@@ -306,8 +662,14 @@ program.command("uninstall <skill>").alias("rm").description("Uninstall a Skill"
|
|
|
306
662
|
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));
|
|
307
663
|
program.command("validate [target]").alias("v").description("Validate a Skill folder (path) or an installed Skill name").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 (target, options) => validate(target, options));
|
|
308
664
|
program.command("init <name>").description("Create a new Skill project").option("--dir <path>", "Target directory (defaults to <name>)").option("--description <text>", "Skill description").option("-f, --force", "Overwrite target directory if it exists").action(async (name, options) => init(name, options));
|
|
665
|
+
program.command("signup").description("Create a publisher account in the registry (no GitHub required)").option("--registry <url>", "Registry base URL (default: https://registry.skild.sh)").option("--email <email>", "Email (optional; will prompt)").option("--handle <handle>", "Publisher handle (owns @handle/* scope) (optional; will prompt)").option("--password <password>", "Password (optional; will prompt)").option("--json", "Output JSON").action(async (options) => signup(options));
|
|
666
|
+
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));
|
|
667
|
+
program.command("logout").description("Remove stored registry credentials").action(async () => logout());
|
|
668
|
+
program.command("whoami").description("Show current registry identity").action(async () => whoami());
|
|
669
|
+
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));
|
|
670
|
+
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));
|
|
309
671
|
program.action(() => {
|
|
310
|
-
console.log(
|
|
672
|
+
console.log(chalk15.bold("\n\u{1F6E1}\uFE0F skild \u2014 Get your agents skilled.\n"));
|
|
311
673
|
program.outputHelp();
|
|
312
674
|
});
|
|
313
675
|
var argv = process.argv.slice();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skild",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"chalk": "^5.3.0",
|
|
37
37
|
"commander": "^12.1.0",
|
|
38
38
|
"ora": "^8.0.1",
|
|
39
|
-
"
|
|
39
|
+
"tar": "^7.4.3",
|
|
40
|
+
"@skild/core": "^0.1.4"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/node": "^20.10.0",
|