get-skill 1.3.8 → 1.3.10

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/cli.js CHANGED
@@ -36,39 +36,622 @@ import {
36
36
  // src/index.ts
37
37
  import { Command as Command13 } from "commander";
38
38
  import { readFileSync as readFileSync3 } from "fs";
39
- import pc15 from "picocolors";
39
+ import pc17 from "picocolors";
40
40
 
41
41
  // src/commands/lint.ts
42
42
  import { Command } from "commander";
43
43
  import { writeFileSync } from "fs";
44
44
  import { homedir as homedir2 } from "os";
45
45
  import { resolve as resolve2 } from "path";
46
+ import pc4 from "picocolors";
47
+
48
+ // src/prompts.ts
49
+ import { ConfirmPrompt, MultiSelectPrompt, PasswordPrompt, SelectPrompt, TextPrompt, block } from "@clack/core";
46
50
  import pc2 from "picocolors";
47
- import * as p from "@clack/prompts";
51
+
52
+ // src/brand.ts
53
+ import pc from "picocolors";
54
+
55
+ // src/env.ts
56
+ function detectHostAgent(env = process.env) {
57
+ if (env.CLAUDECODE) return "claude-code";
58
+ if (env.CURSOR_AGENT || env.CURSOR_EXTENSION_HOST_ROLE === "agent-exec") return "cursor";
59
+ if (env.CODEX_SANDBOX || env.CODEX_THREAD_ID) return "codex";
60
+ if (env.GEMINI_CLI) return "gemini-cli";
61
+ if (env.CI && env.CI !== "false" && env.CI !== "0") return "ci";
62
+ return null;
63
+ }
64
+ var realTerminal = () => ({
65
+ stdinTTY: Boolean(process.stdin.isTTY),
66
+ stdoutTTY: Boolean(process.stdout.isTTY),
67
+ env: process.env
68
+ });
69
+ function isInteractive(flags, term = realTerminal()) {
70
+ if (flags.yes || flags.json) return false;
71
+ if (!term.stdinTTY || !term.stdoutTTY) return false;
72
+ return detectHostAgent(term.env) === null;
73
+ }
74
+ function scopesFor(flags) {
75
+ return flags.global && !flags.project ? [true] : flags.project && !flags.global ? [false] : [false, true];
76
+ }
77
+ function nonInteractiveHint(flags) {
78
+ return `Interactive prompt required but stdin is not a terminal. Nothing was changed. Re-run with ${flags} to run non-interactively.`;
79
+ }
80
+ function promptIf(flags, prompt) {
81
+ const term = { stdinTTY: Boolean(process.stdin.isTTY), stdoutTTY: Boolean(process.stdout.isTTY), env: flags.env ?? process.env };
82
+ return isInteractive(flags, term) ? { prompt } : {};
83
+ }
84
+
85
+ // src/brand.ts
86
+ function unicodeSupported(env = process.env, platform = process.platform) {
87
+ if (platform !== "win32") return env.TERM !== "linux";
88
+ return Boolean(env.CI) || Boolean(env.WT_SESSION) || Boolean(env.TERMINUS_SUBLIME) || env.ConEmuTask === "{cmd::Cmder}" || env.TERM_PROGRAM === "Terminus-Sublime" || env.TERM_PROGRAM === "vscode" || env.TERM === "xterm-256color" || env.TERM === "alacritty" || env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
89
+ }
90
+ function truecolor(env = process.env) {
91
+ if (/^(truecolor|24bit)$/i.test(env.COLORTERM ?? "")) return true;
92
+ if (env.WT_SESSION || env.KITTY_WINDOW_ID) return true;
93
+ if (/^(vscode|iTerm\.app|WezTerm|Hyper|ghostty)$/.test(env.TERM_PROGRAM ?? "")) return true;
94
+ return /(truecolor|24bit|direct|kitty|ghostty)/i.test(env.TERM ?? "");
95
+ }
96
+ function orange(text2, env = process.env, colour = pc.isColorSupported) {
97
+ if (!colour) return text2;
98
+ const open = truecolor(env) ? "\x1B[38;2;214;83;41m" : "\x1B[38;5;166m";
99
+ return `${open}${text2}\x1B[39m`;
100
+ }
101
+ function canAnimate(stream, env = process.env) {
102
+ return Boolean(stream.isTTY) && detectHostAgent(env) === null && env.TERM !== "dumb" && unicodeSupported(env);
103
+ }
104
+ var SPINNERS = {
105
+ drop: { frames: [" ", "\u2580 ", "\u2584 ", "\u2584\u2580", "\u2584\u2580", "\u2584\u2580", "\u2584\u2580", "\u2584\u2580", " "], interval: 90 },
106
+ hop: { frames: ["\u2584\u2580", "\u2580\u2584"], interval: 240 },
107
+ walk: { frames: ["\u2584\u2580 ", " \u2584\u2580 ", " \u2584\u2580", " \u2584", " ", "\u2580 "], interval: 110 }
108
+ };
109
+ var ASCII_SPINNER = { frames: ["\u2022", "o", "O", "0"], interval: 120 };
110
+ async function waitFor(kind, message, work, opts = {}) {
111
+ const stream = opts.stream ?? process.stderr;
112
+ const env = opts.env ?? process.env;
113
+ const job = typeof work === "function" ? work() : work;
114
+ const show = !opts.json && canAnimate(stream, env) && canAnimate(opts.also ?? process.stdout, env);
115
+ if (!show) return job;
116
+ const { frames, interval } = SPINNERS[kind];
117
+ const text2 = message.replace(/[.…]+$/, "");
118
+ let i = 0;
119
+ const draw = () => stream.write(`\r\x1B[2K${orange(frames[i++ % frames.length], env)} ${text2}\u2026`);
120
+ stream.write("\x1B[?25l");
121
+ draw();
122
+ const timer = setInterval(draw, interval);
123
+ const restore = () => stream.write("\x1B[?25h");
124
+ process.once("exit", restore);
125
+ try {
126
+ return await job;
127
+ } finally {
128
+ clearInterval(timer);
129
+ stream.write("\r\x1B[2K\x1B[?25h");
130
+ process.removeListener("exit", restore);
131
+ }
132
+ }
133
+ var LETTERS = [
134
+ { x: 0, rows: { 3: ".########", 4: "#########", 5: "##.......", 6: "########.", 7: ".########", 8: ".......##", 9: "#########", 10: "########." } },
135
+ // s
136
+ { x: 10, rows: { 0: "##", 1: "##", 2: "##", 3: "##...##", 4: "##..##", 5: "##.##", 6: "####", 7: "####", 8: "##.##", 9: "##..##", 10: "##...##" } },
137
+ // k
138
+ { x: 18, rows: { 0: "##", 1: "##", 3: "##", 4: "##", 5: "##", 6: "##", 7: "##", 8: "##", 9: "##", 10: "##" } },
139
+ // i
140
+ { x: 21, rows: Object.fromEntries([...Array(11).keys()].map((r) => [r, "##"])) },
141
+ // l
142
+ { x: 24, rows: Object.fromEntries([...Array(11).keys()].map((r) => [r, "##"])) },
143
+ // l
144
+ { x: 28, md: true, rows: { 3: ".#########", 4: "##########", 5: "##...##...##", 6: "##...##...##", 7: "##...##...##", 8: "##...##...##", 9: "##...##...##", 10: "##...##...##" } },
145
+ // m
146
+ { x: 41, md: true, rows: { 0: "......##", 1: "......##", 2: "......##", 3: ".#######", 4: "########", 5: "##....##", 6: "##....##", 7: "##....##", 8: "##....##", 9: "######", 10: "######" } }
147
+ // d
148
+ ];
149
+ var WORDMARK_WIDTH = 54;
150
+ var CANVAS_ROWS = 12;
151
+ var SQUARES = [{ x: 50, y: 10 }, { x: 52, y: 8 }];
152
+ function wordmarkLines(squares = SQUARES, paint = (s2) => orange(s2)) {
153
+ const grid = [...Array(CANVAS_ROWS)].map(() => Array(WORDMARK_WIDTH).fill(null));
154
+ for (const L of LETTERS) {
155
+ for (const [r, row] of Object.entries(L.rows)) {
156
+ [...row].forEach((c, i) => {
157
+ if (c === "#") grid[Number(r) + 1][L.x + i] = L.md ? "md" : "ink";
158
+ });
159
+ }
160
+ }
161
+ for (const sq of squares) {
162
+ for (let dy = 0; dy < 2; dy++) for (let dx = 0; dx < 2; dx++) {
163
+ const y = Math.round(sq.y) + dy, x = Math.round(sq.x) + dx;
164
+ if (y >= 0 && y < CANVAS_ROWS && x >= 0 && x < WORDMARK_WIDTH) grid[y][x] = "md";
165
+ }
166
+ }
167
+ const lines = [];
168
+ for (let r = 0; r < CANVAS_ROWS; r += 2) {
169
+ let line = "";
170
+ let run2 = "";
171
+ let runMd = false;
172
+ const flush = () => {
173
+ if (run2) line += runMd ? paint(run2) : run2;
174
+ run2 = "";
175
+ };
176
+ let end = WORDMARK_WIDTH;
177
+ while (end > 0 && !grid[r][end - 1] && !grid[r + 1][end - 1]) end--;
178
+ for (let x = 0; x < end; x++) {
179
+ const top = grid[r][x], bottom = grid[r + 1][x];
180
+ const md = (top ?? bottom) === "md";
181
+ const ch = top && bottom ? "\u2588" : top ? "\u2580" : bottom ? "\u2584" : " ";
182
+ if (ch !== " " && md !== runMd) flush();
183
+ if (ch !== " ") runMd = md;
184
+ run2 += ch;
185
+ }
186
+ flush();
187
+ lines.push(line);
188
+ }
189
+ return lines;
190
+ }
191
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
192
+ var easeIn = (t) => t * t;
193
+ async function playLogo(opts = {}) {
194
+ const out = opts.out ?? process.stdout;
195
+ const input = opts.input ?? process.stdin;
196
+ const env = opts.env ?? process.env;
197
+ const indent = opts.indent ?? " ";
198
+ const animate = opts.animate ?? (canAnimate(out, env) && pc.isColorSupported && Boolean(input.isTTY));
199
+ const show = (lines) => lines.map((l) => l ? indent + l : "").join("\n");
200
+ if (!animate) {
201
+ out.write(`${show(wordmarkLines())}
202
+ `);
203
+ return;
204
+ }
205
+ let skipped = false;
206
+ const onKey = (key) => {
207
+ skipped = true;
208
+ if (key.toString() === "") {
209
+ out.write("\x1B[?25h\n");
210
+ process.exit(130);
211
+ }
212
+ };
213
+ const restoreCursor = () => out.write("\x1B[?25h");
214
+ out.write("\x1B[?25l");
215
+ process.once("exit", restoreCursor);
216
+ input.setRawMode?.(true);
217
+ input.on("data", onKey);
218
+ input.resume();
219
+ let drawn = 0;
220
+ const paint = (squares) => {
221
+ const lines = wordmarkLines(squares);
222
+ out.write(`${drawn ? `\x1B[${drawn}A` : ""}${lines.map((l) => `\x1B[2K${l ? indent + l : ""}`).join("\n")}
223
+ `);
224
+ drawn = lines.length;
225
+ };
226
+ try {
227
+ paint([]);
228
+ await sleep(160);
229
+ const [first, second] = SQUARES;
230
+ for (const [n, target] of [first, second].entries()) {
231
+ const landed = n === 0 ? [] : [first];
232
+ const steps = 9;
233
+ for (let s2 = 0; s2 <= steps && !skipped; s2++) {
234
+ paint([...landed, { x: target.x, y: -2 + (target.y + 2) * easeIn(s2 / steps) }]);
235
+ await sleep(22);
236
+ }
237
+ if (skipped) break;
238
+ paint([...landed, { x: target.x, y: target.y - 1 }]);
239
+ await sleep(40);
240
+ paint([...landed, target]);
241
+ await sleep(90);
242
+ }
243
+ } finally {
244
+ paint(SQUARES);
245
+ input.removeListener("data", onKey);
246
+ input.setRawMode?.(false);
247
+ input.pause();
248
+ out.write("\x1B[?25h");
249
+ process.removeListener("exit", restoreCursor);
250
+ }
251
+ }
252
+ function brandWord(env = process.env, colour = pc.isColorSupported) {
253
+ return `${colour ? pc.bold("skill") : "skill"}${colour ? pc.bold(orange("md", env, colour)) : "md"}`;
254
+ }
255
+
256
+ // src/prompts.ts
257
+ import { isCancel } from "@clack/core";
258
+ var unicode = unicodeSupported();
259
+ var s = (c, fallback) => unicode ? c : fallback;
260
+ var S_STEP_ACTIVE = s("\u25C6", "*");
261
+ var S_STEP_CANCEL = s("\u25A0", "x");
262
+ var S_STEP_ERROR = s("\u25B2", "x");
263
+ var S_STEP_SUBMIT = s("\u25C7", "o");
264
+ var S_BAR_START = s("\u250C", "T");
265
+ var S_BAR = s("\u2502", "|");
266
+ var S_BAR_END = s("\u2514", "\u2014");
267
+ var S_RADIO_ACTIVE = s("\u25CF", ">");
268
+ var S_RADIO_INACTIVE = s("\u25CB", " ");
269
+ var S_CHECKBOX_ACTIVE = s("\u25FB", "[\u2022]");
270
+ var S_CHECKBOX_SELECTED = s("\u25FC", "[+]");
271
+ var S_CHECKBOX_INACTIVE = s("\u25FB", "[ ]");
272
+ var S_PASSWORD_MASK = s("\u25AA", "\u2022");
273
+ var S_BAR_H = s("\u2500", "-");
274
+ var S_CORNER_TOP_RIGHT = s("\u256E", "+");
275
+ var S_CONNECT_LEFT = s("\u251C", "+");
276
+ var S_CORNER_BOTTOM_RIGHT = s("\u256F", "+");
277
+ var S_INFO = s("\u25CF", "\u2022");
278
+ var S_SUCCESS = s("\u25C6", "*");
279
+ var S_WARN = s("\u25B2", "!");
280
+ var S_ERROR = s("\u25A0", "x");
281
+ var accent = (text2) => orange(text2);
282
+ var bar = pc2.gray(S_BAR);
283
+ var symbol = (state) => {
284
+ switch (state) {
285
+ case "initial":
286
+ case "active":
287
+ return accent(S_STEP_ACTIVE);
288
+ case "cancel":
289
+ return pc2.red(S_STEP_CANCEL);
290
+ case "error":
291
+ return pc2.yellow(S_STEP_ERROR);
292
+ case "submit":
293
+ return accent(S_STEP_SUBMIT);
294
+ }
295
+ };
296
+ var skipLead = false;
297
+ function startFrameAfterLogo() {
298
+ skipLead = true;
299
+ }
300
+ var lead = () => {
301
+ if (skipLead) {
302
+ skipLead = false;
303
+ return "";
304
+ }
305
+ return `${bar}
306
+ `;
307
+ };
308
+ var hint = (text2) => text2 ? ` ${pc2.dim(text2)}` : "";
309
+ var keys = (text2) => pc2.dim(text2);
310
+ function limitOptions(params) {
311
+ const { cursor, options, style } = params;
312
+ const outputMaxItems = Math.max((process.stdout.rows ?? 24) - 4, 0);
313
+ const maxItems = Math.min(outputMaxItems, Math.max(params.maxItems ?? Infinity, 5));
314
+ let window = 0;
315
+ if (cursor >= window + maxItems - 3) window = Math.max(Math.min(cursor - maxItems + 3, options.length - maxItems), 0);
316
+ else if (cursor < window + 2) window = Math.max(cursor - 2, 0);
317
+ const topEllipsis = maxItems < options.length && window > 0;
318
+ const bottomEllipsis = maxItems < options.length && window + maxItems < options.length;
319
+ return options.slice(window, window + maxItems).map((option, i, arr) => i === 0 && topEllipsis || i === arr.length - 1 && bottomEllipsis ? pc2.dim("...") : style(option, i + window === cursor));
320
+ }
321
+ var text = (opts) => {
322
+ const top = lead();
323
+ return new TextPrompt({
324
+ validate: opts.validate,
325
+ placeholder: opts.placeholder,
326
+ defaultValue: opts.defaultValue,
327
+ initialValue: opts.initialValue,
328
+ render() {
329
+ const title = `${top}${symbol(this.state)} ${opts.message}
330
+ `;
331
+ const placeholder = opts.placeholder ? pc2.inverse(opts.placeholder[0]) + pc2.dim(opts.placeholder.slice(1)) : pc2.inverse(pc2.hidden("_"));
332
+ const value = !this.value ? placeholder : this.valueWithCursor;
333
+ switch (this.state) {
334
+ case "error":
335
+ return `${title.trim()}
336
+ ${pc2.yellow(S_BAR)} ${value}
337
+ ${pc2.yellow(S_BAR_END)} ${pc2.yellow(this.error)}
338
+ `;
339
+ case "submit":
340
+ return `${title}${bar} ${pc2.dim(this.value || opts.placeholder || "")}`;
341
+ case "cancel":
342
+ return `${title}${bar} ${pc2.strikethrough(pc2.dim(this.value ?? ""))}${this.value?.trim() ? `
343
+ ${bar}` : ""}`;
344
+ default:
345
+ return `${title}${bar} ${value}
346
+ ${pc2.gray(S_BAR_END)}
347
+ `;
348
+ }
349
+ }
350
+ }).prompt();
351
+ };
352
+ var password = (opts) => {
353
+ const top = lead();
354
+ return new PasswordPrompt({
355
+ validate: opts.validate,
356
+ mask: opts.mask ?? S_PASSWORD_MASK,
357
+ render() {
358
+ const title = `${top}${symbol(this.state)} ${opts.message}
359
+ `;
360
+ const masked = this.masked;
361
+ switch (this.state) {
362
+ case "error":
363
+ return `${title.trim()}
364
+ ${pc2.yellow(S_BAR)} ${masked}
365
+ ${pc2.yellow(S_BAR_END)} ${pc2.yellow(this.error)}
366
+ `;
367
+ case "submit":
368
+ return `${title}${bar} ${pc2.dim(masked)}`;
369
+ case "cancel":
370
+ return `${title}${bar} ${pc2.strikethrough(pc2.dim(masked ?? ""))}${masked ? `
371
+ ${bar}` : ""}`;
372
+ default:
373
+ return `${title}${bar} ${this.valueWithCursor}
374
+ ${pc2.gray(S_BAR_END)}
375
+ `;
376
+ }
377
+ }
378
+ }).prompt();
379
+ };
380
+ var confirm = (opts) => {
381
+ const active = opts.active ?? "Yes";
382
+ const inactive = opts.inactive ?? "No";
383
+ const top = lead();
384
+ return new ConfirmPrompt({
385
+ active,
386
+ inactive,
387
+ initialValue: opts.initialValue ?? true,
388
+ render() {
389
+ const title = `${top}${symbol(this.state)} ${opts.message}
390
+ `;
391
+ const value = this.value ? active : inactive;
392
+ switch (this.state) {
393
+ case "submit":
394
+ return `${title}${bar} ${pc2.dim(value)}`;
395
+ case "cancel":
396
+ return `${title}${bar} ${pc2.strikethrough(pc2.dim(value))}
397
+ ${bar}`;
398
+ default: {
399
+ const on = (label2) => `${accent(S_RADIO_ACTIVE)} ${label2}`;
400
+ const off = (label2) => `${pc2.dim(S_RADIO_INACTIVE)} ${pc2.dim(label2)}`;
401
+ return `${title}${bar} ${this.value ? on(active) : off(active)} ${pc2.dim("/")} ${!this.value ? on(inactive) : off(inactive)}
402
+ ${pc2.gray(S_BAR_END)}
403
+ `;
404
+ }
405
+ }
406
+ }
407
+ }).prompt();
408
+ };
409
+ var select = (opts) => {
410
+ const opt = (option, state) => {
411
+ const label2 = option.label ?? String(option.value);
412
+ switch (state) {
413
+ case "selected":
414
+ return pc2.dim(label2);
415
+ case "active":
416
+ return `${accent(S_RADIO_ACTIVE)} ${label2}${hint(option.hint)}`;
417
+ case "cancelled":
418
+ return pc2.strikethrough(pc2.dim(label2));
419
+ default:
420
+ return `${pc2.dim(S_RADIO_INACTIVE)} ${pc2.dim(label2)}`;
421
+ }
422
+ };
423
+ const top = lead();
424
+ return new SelectPrompt({
425
+ options: opts.options,
426
+ initialValue: opts.initialValue,
427
+ render() {
428
+ const title = `${top}${symbol(this.state)} ${opts.message}
429
+ `;
430
+ switch (this.state) {
431
+ case "submit":
432
+ return `${title}${bar} ${opt(this.options[this.cursor], "selected")}`;
433
+ case "cancel":
434
+ return `${title}${bar} ${opt(this.options[this.cursor], "cancelled")}
435
+ ${bar}`;
436
+ default:
437
+ return `${title}${bar} ${limitOptions({
438
+ cursor: this.cursor,
439
+ options: this.options,
440
+ maxItems: opts.maxItems,
441
+ style: (item, isActive) => opt(item, isActive ? "active" : "inactive")
442
+ }).join(`
443
+ ${bar} `)}
444
+ ${pc2.gray(S_BAR_END)} ${keys("\u2191/\u2193 to move \xB7 enter to choose")}
445
+ `;
446
+ }
447
+ }
448
+ }).prompt();
449
+ };
450
+ var multiselect = (opts) => {
451
+ const opt = (option, state) => {
452
+ const label2 = option.label ?? String(option.value);
453
+ switch (state) {
454
+ case "active":
455
+ return `${accent(S_CHECKBOX_ACTIVE)} ${label2}${hint(option.hint)}`;
456
+ case "selected":
457
+ return `${accent(S_CHECKBOX_SELECTED)} ${pc2.dim(label2)}`;
458
+ case "cancelled":
459
+ return pc2.strikethrough(pc2.dim(label2));
460
+ case "active-selected":
461
+ return `${accent(S_CHECKBOX_SELECTED)} ${label2}${hint(option.hint)}`;
462
+ case "submitted":
463
+ return pc2.dim(label2);
464
+ default:
465
+ return `${pc2.dim(S_CHECKBOX_INACTIVE)} ${pc2.dim(label2)}`;
466
+ }
467
+ };
468
+ const top = lead();
469
+ return new MultiSelectPrompt({
470
+ options: opts.options,
471
+ initialValues: opts.initialValues,
472
+ required: opts.required ?? true,
473
+ cursorAt: opts.cursorAt,
474
+ validate(selected) {
475
+ if (this.required && selected.length === 0) {
476
+ return `Please select at least one option.
477
+ ${pc2.reset(pc2.dim("Press space to select, enter to confirm"))}`;
478
+ }
479
+ },
480
+ render() {
481
+ const title = `${top}${symbol(this.state)} ${opts.message}
482
+ `;
483
+ const styleOption = (option, isActive) => {
484
+ const selected = this.value.includes(option.value);
485
+ if (isActive && selected) return opt(option, "active-selected");
486
+ if (selected) return opt(option, "selected");
487
+ return opt(option, isActive ? "active" : "inactive");
488
+ };
489
+ const list = (edge) => limitOptions({
490
+ options: this.options,
491
+ cursor: this.cursor,
492
+ maxItems: opts.maxItems,
493
+ style: styleOption
494
+ }).join(`
495
+ ${edge} `);
496
+ switch (this.state) {
497
+ case "submit":
498
+ return `${title}${bar} ${this.options.filter(({ value }) => this.value.includes(value)).map((o) => opt(o, "submitted")).join(pc2.dim(", ")) || pc2.dim("none")}`;
499
+ case "cancel": {
500
+ const label2 = this.options.filter(({ value }) => this.value.includes(value)).map((o) => opt(o, "cancelled")).join(pc2.dim(", "));
501
+ return `${title}${bar} ${label2.trim() ? `${label2}
502
+ ${bar}` : ""}`;
503
+ }
504
+ case "error": {
505
+ const footer = this.error.split("\n").map((ln, i) => i === 0 ? `${pc2.yellow(S_BAR_END)} ${pc2.yellow(ln)}` : ` ${ln}`).join("\n");
506
+ return `${title}${pc2.yellow(S_BAR)} ${list(pc2.yellow(S_BAR))}
507
+ ${footer}
508
+ `;
509
+ }
510
+ default:
511
+ return `${title}${bar} ${list(bar)}
512
+ ${pc2.gray(S_BAR_END)} ${keys("space to select \xB7 enter to confirm")}
513
+ `;
514
+ }
515
+ }
516
+ }).prompt();
517
+ };
518
+ var strip = (str) => str.replace(ansiRegex(), "");
519
+ var note = (message = "", title = "") => {
520
+ const lines = `
521
+ ${message}
522
+ `.split("\n");
523
+ const titleLen = strip(title).length;
524
+ const len = Math.max(lines.reduce((max, ln) => Math.max(max, strip(ln).length), 0), titleLen) + 2;
525
+ const msg2 = lines.map((ln) => `${bar} ${pc2.dim(ln)}${" ".repeat(len - strip(ln).length)}${bar}`).join("\n");
526
+ process.stdout.write(
527
+ `${lead()}${accent(S_STEP_SUBMIT)} ${pc2.reset(title)} ${pc2.gray(S_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT)}
528
+ ${msg2}
529
+ ${pc2.gray(S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}
530
+ `
531
+ );
532
+ };
533
+ var cancel = (message = "") => {
534
+ process.stdout.write(`${pc2.gray(S_BAR_END)} ${pc2.red(message)}
535
+
536
+ `);
537
+ };
538
+ var intro = (title = "") => {
539
+ skipLead = false;
540
+ process.stdout.write(`${pc2.gray(S_BAR_START)} ${title}
541
+ `);
542
+ };
543
+ var outro = (message = "") => {
544
+ process.stdout.write(`${bar}
545
+ ${pc2.gray(S_BAR_END)} ${message}
546
+
547
+ `);
548
+ };
549
+ var log = {
550
+ message: (message = "", { symbol: mark = bar } = {}) => {
551
+ const top = lead().replace(/\n$/, "");
552
+ const parts = top ? [top] : [];
553
+ if (message) {
554
+ const [first, ...rest] = message.split("\n");
555
+ parts.push(`${mark} ${first}`, ...rest.map((ln) => `${bar} ${ln}`));
556
+ }
557
+ process.stdout.write(`${parts.join("\n")}
558
+ `);
559
+ },
560
+ info: (message) => log.message(message, { symbol: accent(S_INFO) }),
561
+ success: (message) => log.message(message, { symbol: pc2.green(S_SUCCESS) }),
562
+ step: (message) => log.message(message, { symbol: accent(S_STEP_SUBMIT) }),
563
+ warn: (message) => log.message(message, { symbol: pc2.yellow(S_WARN) }),
564
+ warning: (message) => log.warn(message),
565
+ error: (message) => log.message(message, { symbol: pc2.red(S_ERROR) })
566
+ };
567
+ var spinner = (kind = "drop") => {
568
+ const { frames, interval } = unicode ? SPINNERS[kind] : ASCII_SPINNER;
569
+ let unblock = () => {
570
+ };
571
+ let loop;
572
+ let active = false;
573
+ let message = "";
574
+ const handleExit = (code) => {
575
+ if (active) stop(code > 1 ? "Something went wrong" : "Canceled", code);
576
+ };
577
+ const onError = () => handleExit(2);
578
+ const onSignal = () => handleExit(1);
579
+ const registerHooks = () => {
580
+ process.on("uncaughtExceptionMonitor", onError);
581
+ process.on("unhandledRejection", onError);
582
+ process.on("SIGINT", onSignal);
583
+ process.on("SIGTERM", onSignal);
584
+ process.on("exit", handleExit);
585
+ };
586
+ const clearHooks = () => {
587
+ process.removeListener("uncaughtExceptionMonitor", onError);
588
+ process.removeListener("unhandledRejection", onError);
589
+ process.removeListener("SIGINT", onSignal);
590
+ process.removeListener("SIGTERM", onSignal);
591
+ process.removeListener("exit", handleExit);
592
+ };
593
+ const start = (msg2 = "") => {
594
+ active = true;
595
+ unblock = block();
596
+ message = msg2.replace(/[.…]+$/, "");
597
+ process.stdout.write(lead());
598
+ let i = 0;
599
+ const draw = () => {
600
+ process.stdout.write(`\r\x1B[J${accent(frames[i])} ${message}\u2026`);
601
+ i = (i + 1) % frames.length;
602
+ };
603
+ process.stdout.write("\x1B[?25l");
604
+ registerHooks();
605
+ draw();
606
+ loop = setInterval(draw, interval);
607
+ };
608
+ const stop = (msg2 = "", code = 0) => {
609
+ message = msg2 || message;
610
+ active = false;
611
+ if (loop) clearInterval(loop);
612
+ const mark = code === 0 ? pc2.green(S_STEP_SUBMIT) : code === 1 ? pc2.red(S_STEP_CANCEL) : pc2.red(S_STEP_ERROR);
613
+ process.stdout.write(`\r\x1B[J${mark} ${message}
614
+ \x1B[?25h`);
615
+ clearHooks();
616
+ unblock();
617
+ };
618
+ return { start, stop, message: (msg2 = "") => {
619
+ message = msg2 || message;
620
+ } };
621
+ };
622
+ function ansiRegex() {
623
+ const pattern = [
624
+ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
625
+ "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
626
+ ].join("|");
627
+ return new RegExp(pattern, "g");
628
+ }
629
+
630
+ // src/commands/lint.ts
48
631
  import { lint, toJson, toSarif, toGithub } from "@skillmds/core";
49
632
 
50
633
  // src/ui.ts
51
634
  import { homedir } from "os";
52
635
  import { relative, resolve, sep } from "path";
53
- import pc from "picocolors";
54
- var decorate = (text3, kind) => {
636
+ import pc3 from "picocolors";
637
+ var decorate = (text2, kind) => {
55
638
  switch (kind) {
56
639
  case "error":
57
- return pc.red(text3);
640
+ return pc3.red(text2);
58
641
  case "warn":
59
- return pc.yellow(text3);
642
+ return pc3.yellow(text2);
60
643
  case "info":
61
- return pc.cyan(text3);
644
+ return pc3.cyan(text2);
62
645
  case "ok":
63
- return pc.green(text3);
646
+ return pc3.green(text2);
64
647
  case "dim":
65
- return pc.dim(text3);
648
+ return pc3.dim(text2);
66
649
  }
67
650
  };
68
651
  var SEV_COLOR = {
69
- error: pc.red,
70
- warn: pc.yellow,
71
- info: pc.cyan
652
+ error: pc3.red,
653
+ warn: pc3.yellow,
654
+ info: pc3.cyan
72
655
  };
73
656
  var GLYPH = {
74
657
  error: "\u2716",
@@ -84,7 +667,7 @@ var SEV_ORDER = ["error", "warn", "info"];
84
667
  function chips(result) {
85
668
  const bySev = { error: [], warn: [], info: [] };
86
669
  for (const d of result.diagnostics) bySev[d.severity].push(d.id);
87
- return SEV_ORDER.filter((s) => bySev[s].length).map((s) => `${GLYPH[s]} ${SEV_COLOR[s](bySev[s].join(" "))}`).join(" ");
670
+ return SEV_ORDER.filter((s2) => bySev[s2].length).map((s2) => `${GLYPH[s2]} ${SEV_COLOR[s2](bySev[s2].join(" "))}`).join(" ");
88
671
  }
89
672
  var SEC_LABEL = {
90
673
  network_calls: "Calls Network",
@@ -92,7 +675,7 @@ var SEC_LABEL = {
92
675
  reads_secrets: "Reads Secrets"
93
676
  };
94
677
  var titleCase = (flag) => flag.split("_").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
95
- var scoreColor = (s) => s >= 80 ? pc.green : s >= 50 ? pc.yellow : pc.red;
678
+ var scoreColor = (s2) => s2 >= 80 ? pc3.green : s2 >= 50 ? pc3.yellow : pc3.red;
96
679
  function label(file) {
97
680
  const parts = file.split(/[\\/]/).filter(Boolean);
98
681
  const i = parts.lastIndexOf("SKILL.md");
@@ -103,27 +686,27 @@ function securityFlags(result) {
103
686
  }
104
687
  function secLabels(result) {
105
688
  const flags = securityFlags(result);
106
- return flags.map((f) => pc.magenta(`${SEC_GLYPH} ${SEC_LABEL[f] ?? titleCase(f)}`)).join(" ");
689
+ return flags.map((f) => pc3.magenta(`${SEC_GLYPH} ${SEC_LABEL[f] ?? titleCase(f)}`)).join(" ");
107
690
  }
108
691
  function statusDot(result) {
109
- if (!result.ok) return pc.red("\u25CF");
692
+ if (!result.ok) return pc3.red("\u25CF");
110
693
  const attention = result.diagnostics.some((d) => d.severity === "warn") || securityFlags(result).length > 0;
111
- return attention ? pc.yellow("\u25CF") : pc.green("\u25CF");
694
+ return attention ? pc3.yellow("\u25CF") : pc3.green("\u25CF");
112
695
  }
113
696
  function failureBlock(file, result) {
114
- const lines = [`${pc.red(GLYPH.error)} ${pc.bold(label(file))}`];
697
+ const lines = [`${pc3.red(GLYPH.error)} ${pc3.bold(label(file))}`];
115
698
  for (const d of result.diagnostics.filter((x) => x.severity === "error")) {
116
- const loc = d.line !== void 0 ? pc.dim(`:${d.line}`) : "";
699
+ const loc = d.line !== void 0 ? pc3.dim(`:${d.line}`) : "";
117
700
  lines.push(` ${SEV_COLOR.error(d.id)}${loc} ${d.message}`);
118
701
  }
119
- lines.push(` ${pc.dim(file)}`);
702
+ lines.push(` ${pc3.dim(file)}`);
120
703
  return lines.join("\n");
121
704
  }
122
705
  function renderOne(file, result) {
123
- const status = result.ok ? pc.green("\u25CF PASS") : pc.red("\u25CF FAIL");
124
- const out = [`${status} ${pc.bold(label(file))} ${pc.dim("quality")} ${scoreColor(result.score)(`${result.score}/100`)}`];
706
+ const status = result.ok ? pc3.green("\u25CF PASS") : pc3.red("\u25CF FAIL");
707
+ const out = [`${status} ${pc3.bold(label(file))} ${pc3.dim("quality")} ${scoreColor(result.score)(`${result.score}/100`)}`];
125
708
  for (const d of result.diagnostics) {
126
- const loc = d.line !== void 0 ? pc.dim(`:${d.line}`) : "";
709
+ const loc = d.line !== void 0 ? pc3.dim(`:${d.line}`) : "";
127
710
  out.push(` ${GLYPH[d.severity]} ${SEV_COLOR[d.severity](d.id)}${loc} ${d.message}`);
128
711
  }
129
712
  const sec = secLabels(result);
@@ -147,32 +730,32 @@ function renderMany(results) {
147
730
  const failed = results.filter((r) => !r.result.ok).length;
148
731
  const warned = results.filter((r) => r.result.ok && r.result.diagnostics.some((d) => d.severity === "warn")).length;
149
732
  const clean = results.length - failed - warned;
150
- const avg = Math.round(results.reduce((s, r) => s + r.result.score, 0) / results.length);
733
+ const avg = Math.round(results.reduce((s2, r) => s2 + r.result.score, 0) / results.length);
151
734
  out.push("");
152
- const parts = [pc.green(`${clean} clean`)];
153
- if (warned) parts.push(pc.yellow(`${warned} warn`));
154
- if (failed) parts.push(pc.red(`${failed} fail`));
155
- out.push(`${parts.join(pc.dim(" \xB7 "))} ${pc.dim("avg score")} ${scoreColor(avg)(`${avg}/100`)}`);
156
- if (anySec) out.push(pc.dim(`${SEC_GLYPH} = risky behaviour, factored into the score alongside spec correctness and completeness`));
735
+ const parts = [pc3.green(`${clean} clean`)];
736
+ if (warned) parts.push(pc3.yellow(`${warned} warn`));
737
+ if (failed) parts.push(pc3.red(`${failed} fail`));
738
+ out.push(`${parts.join(pc3.dim(" \xB7 "))} ${pc3.dim("avg score")} ${scoreColor(avg)(`${avg}/100`)}`);
739
+ if (anySec) out.push(pc3.dim(`${SEC_GLYPH} = risky behaviour, factored into the score alongside spec correctness and completeness`));
157
740
  const fails = results.filter((r) => !r.result.ok);
158
741
  if (fails.length) {
159
742
  out.push("");
160
- out.push(pc.red(pc.bold(`Failed (${fails.length}):`)));
743
+ out.push(pc3.red(pc3.bold(`Failed (${fails.length}):`)));
161
744
  for (const f of fails.slice(0, 25)) out.push(failureBlock(f.file, f.result));
162
- if (fails.length > 25) out.push(pc.dim(` \u2026and ${fails.length - 25} more \u2014 run with --errors-only to see all`));
745
+ if (fails.length > 25) out.push(pc3.dim(` \u2026and ${fails.length - 25} more \u2014 run with --errors-only to see all`));
163
746
  }
164
747
  return out.join("\n");
165
748
  }
166
749
  function renderFailuresView(results) {
167
750
  const fails = results.filter((r) => !r.result.ok);
168
- if (!fails.length) return pc.green(`${GLYPH.ok} No failures across ${results.length} skill${results.length === 1 ? "" : "s"}.`);
751
+ if (!fails.length) return pc3.green(`${GLYPH.ok} No failures across ${results.length} skill${results.length === 1 ? "" : "s"}.`);
169
752
  const out = fails.map((f) => failureBlock(f.file, f.result));
170
753
  out.push("");
171
- out.push(`${pc.red(`${fails.length} failed`)} ${pc.dim(`of ${results.length}`)}`);
754
+ out.push(`${pc3.red(`${fails.length} failed`)} ${pc3.dim(`of ${results.length}`)}`);
172
755
  return out.join("\n");
173
756
  }
174
757
  function renderReport(results, opts = {}) {
175
- if (results.length === 0) return pc.dim("No skills found.");
758
+ if (results.length === 0) return pc3.dim("No skills found.");
176
759
  if (opts.errorsOnly) return renderFailuresView(results);
177
760
  if (results.length === 1) return renderOne(results[0].file, results[0].result);
178
761
  return renderMany(results);
@@ -186,11 +769,11 @@ function exitCodeFor(results, policy = {}) {
186
769
  }
187
770
  return 0;
188
771
  }
189
- function shortPath(p7, ctx = {}) {
772
+ function shortPath(p, ctx = {}) {
190
773
  const home = resolve(ctx.home ?? homedir());
191
774
  const cwd = resolve(ctx.cwd ?? process.cwd());
192
- const abs = resolve(p7);
193
- const norm = (s) => s.split(sep).join("/");
775
+ const abs = resolve(p);
776
+ const norm = (s2) => s2.split(sep).join("/");
194
777
  if (abs === cwd) return ".";
195
778
  if (abs.startsWith(cwd + sep)) return `./${norm(relative(cwd, abs))}`;
196
779
  if (abs === home) return "~";
@@ -200,20 +783,20 @@ function shortPath(p7, ctx = {}) {
200
783
  var SGR = /\x1b\[[0-9;]*m/g;
201
784
  var SGR_AT = /\x1b\[[0-9;]*m/y;
202
785
  var RESET = "\x1B[0m";
203
- function stripAnsi(s) {
204
- return s.replace(SGR, "");
786
+ function stripAnsi(s2) {
787
+ return s2.replace(SGR, "");
205
788
  }
206
- var visible = (s) => stripAnsi(s).length;
207
- function truncateVisible(s, max) {
789
+ var visible = (s2) => stripAnsi(s2).length;
790
+ function truncateVisible(s2, max) {
208
791
  if (max <= 0) return "";
209
- if (visible(s) <= max) return s;
792
+ if (visible(s2) <= max) return s2;
210
793
  let out = "";
211
794
  let shown = 0;
212
795
  let open = false;
213
- for (let i = 0; i < s.length && shown < max - 1; ) {
214
- if (s[i] === "\x1B") {
796
+ for (let i = 0; i < s2.length && shown < max - 1; ) {
797
+ if (s2[i] === "\x1B") {
215
798
  SGR_AT.lastIndex = i;
216
- const m = SGR_AT.exec(s);
799
+ const m = SGR_AT.exec(s2);
217
800
  if (m) {
218
801
  out += m[0];
219
802
  open = m[0] !== RESET && m[0] !== "\x1B[m";
@@ -221,7 +804,7 @@ function truncateVisible(s, max) {
221
804
  continue;
222
805
  }
223
806
  }
224
- out += s[i];
807
+ out += s2[i];
225
808
  shown++;
226
809
  i++;
227
810
  }
@@ -252,7 +835,7 @@ function renderInstallSummary(i, ctx = {}) {
252
835
  const width = termWidth(ctx.width);
253
836
  const src = i.source.startsWith("local:") ? `local:${shortPath(i.source.slice(6), ctx)}` : i.source;
254
837
  const head = truncateVisible(
255
- pc.green(`${GLYPH.ok} ${i.name}`) + pc.dim(` score ${i.score}${i.flags.length ? ` ${i.flags.join(", ")}` : ""} from ${src}`),
838
+ pc3.green(`${GLYPH.ok} ${i.name}`) + pc3.dim(` score ${i.score}${i.flags.length ? ` ${i.flags.join(", ")}` : ""} from ${src}`),
256
839
  width
257
840
  );
258
841
  const byPath = /* @__PURE__ */ new Map();
@@ -261,13 +844,13 @@ function renderInstallSummary(i, ctx = {}) {
261
844
  if (e) e.agents.push(t.agent);
262
845
  else byPath.set(t.path, { agents: [t.agent], mode: t.mode });
263
846
  }
264
- const rows = [...byPath.entries()].map(([path, e]) => [pc.cyan(e.agents.join(", ")), pc.dim(e.mode), shortPath(path, ctx)]);
265
- const body = rows.length ? table(rows, { width, indent: " " }) : truncateVisible(pc.dim(` ${shortPath(i.canonical, ctx)} canonical only`), width);
266
- const skipped = i.skipped.map((s) => truncateVisible(pc.dim(` ${GLYPH.skip} ${s.agent}: ${s.reason}`), width));
267
- const replaced = i.replaced ? [truncateVisible(pc.yellow(` ${GLYPH.replaced} replaced previous install (${i.replaced === "untracked" ? "untracked directory" : i.replaced})`), width)] : [];
847
+ const rows = [...byPath.entries()].map(([path, e]) => [pc3.cyan(e.agents.join(", ")), pc3.dim(e.mode), shortPath(path, ctx)]);
848
+ const body = rows.length ? table(rows, { width, indent: " " }) : truncateVisible(pc3.dim(` ${shortPath(i.canonical, ctx)} canonical only`), width);
849
+ const skipped = i.skipped.map((s2) => truncateVisible(pc3.dim(` ${GLYPH.skip} ${s2.agent}: ${s2.reason}`), width));
850
+ const replaced = i.replaced ? [truncateVisible(pc3.yellow(` ${GLYPH.replaced} replaced previous install (${i.replaced === "untracked" ? "untracked directory" : i.replaced})`), width)] : [];
268
851
  return [head, body, ...skipped, ...replaced].join("\n");
269
852
  }
270
- var banner = () => pc.bgCyan(pc.black(" skillmd "));
853
+ var banner = () => brandWord();
271
854
 
272
855
  // src/commands/lint.ts
273
856
  function applyLicenseFix(raw) {
@@ -312,23 +895,23 @@ function lintCommand() {
312
895
  return new Command("lint").description("Validate SKILL.md file(s) against the spec and report a quality score").argument("[target]", "local path or GitHub source", ".").option("--format <fmt>", "text | json | sarif | github", "text").option("--strict", "treat warnings as errors").option("--fail-on-warning", "exit non-zero when warnings exist").option("--fix", "apply safe automatic fixes (e.g. add missing license)").option("--errors-only", "show only skills that failed").action(async (target, opts) => {
313
896
  const ui = Boolean(process.stdout.isTTY) && opts.format === "text";
314
897
  if (resolve2(target) === homedir2()) {
315
- process.stderr.write(pc2.yellow(`Warning: scanning your entire home directory. Pass a narrower path, e.g. skillmd lint ./my-skill
898
+ process.stderr.write(pc4.yellow(`Warning: scanning your entire home directory. Pass a narrower path, e.g. skillmd lint ./my-skill
316
899
  `));
317
900
  }
318
901
  if (ui) {
319
- p.intro(pc2.bgCyan(pc2.black(" skillmd lint ")));
320
- const s = p.spinner();
321
- s.start(`Scanning ${target}`);
902
+ intro(`${brandWord()} ${pc4.dim("lint")}`);
903
+ const s2 = spinner("walk");
904
+ s2.start(`Scanning ${target}`);
322
905
  try {
323
906
  const run3 = await runLint(target, opts);
324
- s.stop(`Scanned ${run3.results.length} skill${run3.results.length === 1 ? "" : "s"}`);
907
+ s2.stop(`Scanned ${run3.results.length} skill${run3.results.length === 1 ? "" : "s"}`);
325
908
  console.log(run3.output);
326
909
  const failed = run3.results.filter((r) => !r.result.ok).length;
327
- p.outro(failed === 0 ? pc2.green("All good") : pc2.red(`${failed} skill${failed === 1 ? "" : "s"} failed \u2014 details above`));
910
+ outro(failed === 0 ? pc4.green("All good") : pc4.red(`${failed} skill${failed === 1 ? "" : "s"} failed \u2014 details above`));
328
911
  process.exitCode = run3.exitCode;
329
912
  } catch (err) {
330
- s.stop(pc2.red("No skills found"));
331
- p.outro(err instanceof Error ? err.message : String(err));
913
+ s2.stop(pc4.red("No skills found"));
914
+ outro(err instanceof Error ? err.message : String(err));
332
915
  process.exitCode = 1;
333
916
  }
334
917
  return;
@@ -344,7 +927,7 @@ function lintCommand() {
344
927
  // src/commands/scan.ts
345
928
  import { Command as Command2 } from "commander";
346
929
  import { scanSecurity, parseSkillMd } from "@skillmds/core";
347
- import pc3 from "picocolors";
930
+ import pc5 from "picocolors";
348
931
  async function runScan(target, flags) {
349
932
  const resolved = await resolveSource(target);
350
933
  const deny = new Set(flags.deny ?? []);
@@ -363,11 +946,11 @@ async function runScan(target, flags) {
363
946
  const lines = [];
364
947
  for (const e of entries) {
365
948
  const flagStr = e.security.flags.join(", ");
366
- const head = e.denied.length ? pc3.red(`DENY ${e.file}`) : pc3.green(`OK ${e.file}`);
367
- lines.push(`${head} ${pc3.dim(flagStr)}`);
949
+ const head = e.denied.length ? pc5.red(`DENY ${e.file}`) : pc5.green(`OK ${e.file}`);
950
+ lines.push(`${head} ${pc5.dim(flagStr)}`);
368
951
  for (const f of e.security.findings) {
369
- const mark = deny.has(f.flag) ? pc3.red(f.flag) : pc3.yellow(f.flag);
370
- lines.push(` ${mark} :${f.line} ${pc3.dim(f.snippet)}`);
952
+ const mark = deny.has(f.flag) ? pc5.red(f.flag) : pc5.yellow(f.flag);
953
+ lines.push(` ${mark} :${f.line} ${pc5.dim(f.snippet)}`);
371
954
  }
372
955
  }
373
956
  output = lines.join("\n");
@@ -385,7 +968,7 @@ function scanCommand() {
385
968
  // src/commands/rules.ts
386
969
  import { Command as Command3 } from "commander";
387
970
  import { RULES } from "@skillmds/core";
388
- import pc4 from "picocolors";
971
+ import pc6 from "picocolors";
389
972
  function rulesCommand() {
390
973
  return new Command3("rules").description("List lint rules, or show detail for one rule id").argument("[id]", "a rule id, e.g. SK010").option("--json", "machine-readable output").action((id, opts) => {
391
974
  const list = id ? RULES.filter((r) => r.id.toLowerCase() === id.toLowerCase()) : RULES;
@@ -398,9 +981,9 @@ function rulesCommand() {
398
981
  console.log(JSON.stringify(list.map((r) => ({ id: r.id, severity: r.severity, description: r.description })), null, 2));
399
982
  return;
400
983
  }
401
- const badge = (sev) => sev === "error" ? pc4.bgRed(pc4.white(" error ")) : sev === "warn" ? pc4.bgYellow(pc4.black(" warn ")) : pc4.bgCyan(pc4.black(" info "));
402
- console.log(pc4.bold("\nLint rules\n"));
403
- for (const r of list) console.log(` ${pc4.bold(r.id)} ${badge(r.severity)} ${r.description}`);
984
+ const badge = (sev) => sev === "error" ? pc6.bgRed(pc6.white(" error ")) : sev === "warn" ? pc6.bgYellow(pc6.black(" warn ")) : pc6.bgCyan(pc6.black(" info "));
985
+ console.log(pc6.bold("\nLint rules\n"));
986
+ for (const r of list) console.log(` ${pc6.bold(r.id)} ${badge(r.severity)} ${r.description}`);
404
987
  console.log("");
405
988
  });
406
989
  }
@@ -410,40 +993,7 @@ import { Command as Command4 } from "commander";
410
993
  import { mkdirSync, writeFileSync as writeFileSync2, existsSync } from "fs";
411
994
  import { join } from "path";
412
995
  import { slugify } from "@skillmds/core";
413
- import * as p2 from "@clack/prompts";
414
- import pc5 from "picocolors";
415
-
416
- // src/env.ts
417
- function detectHostAgent(env = process.env) {
418
- if (env.CLAUDECODE) return "claude-code";
419
- if (env.CURSOR_AGENT || env.CURSOR_EXTENSION_HOST_ROLE === "agent-exec") return "cursor";
420
- if (env.CODEX_SANDBOX || env.CODEX_THREAD_ID) return "codex";
421
- if (env.GEMINI_CLI) return "gemini-cli";
422
- if (env.CI && env.CI !== "false" && env.CI !== "0") return "ci";
423
- return null;
424
- }
425
- var realTerminal = () => ({
426
- stdinTTY: Boolean(process.stdin.isTTY),
427
- stdoutTTY: Boolean(process.stdout.isTTY),
428
- env: process.env
429
- });
430
- function isInteractive(flags, term = realTerminal()) {
431
- if (flags.yes || flags.json) return false;
432
- if (!term.stdinTTY || !term.stdoutTTY) return false;
433
- return detectHostAgent(term.env) === null;
434
- }
435
- function scopesFor(flags) {
436
- return flags.global && !flags.project ? [true] : flags.project && !flags.global ? [false] : [false, true];
437
- }
438
- function nonInteractiveHint(flags) {
439
- return `Interactive prompt required but stdin is not a terminal. Nothing was changed. Re-run with ${flags} to run non-interactively.`;
440
- }
441
- function promptIf(flags, prompt) {
442
- const term = { stdinTTY: Boolean(process.stdin.isTTY), stdoutTTY: Boolean(process.stdout.isTTY), env: flags.env ?? process.env };
443
- return isInteractive(flags, term) ? { prompt } : {};
444
- }
445
-
446
- // src/commands/init.ts
996
+ import pc7 from "picocolors";
447
997
  function skillTemplate(name, description, license) {
448
998
  const desc = description.trim() || `A skill that helps agents with ${name}.`;
449
999
  return `---
@@ -480,8 +1030,8 @@ function initSkill(name, opts = {}) {
480
1030
  return file;
481
1031
  }
482
1032
  var defaultInitDeps = (flags) => promptIf(flags, async () => {
483
- const answer = await p2.text({ message: "Skill name?", placeholder: "my-skill" });
484
- return p2.isCancel(answer) ? null : answer;
1033
+ const answer = await text({ message: "Skill name?", placeholder: "my-skill" });
1034
+ return isCancel(answer) ? null : answer;
485
1035
  });
486
1036
  function finish(flags, result, doc) {
487
1037
  return flags.json ? { ...result, output: JSON.stringify(doc, null, 2) } : result;
@@ -491,22 +1041,22 @@ async function runInit(nameArg, flags, deps = defaultInitDeps(flags)) {
491
1041
  if (!name) {
492
1042
  if (!deps.prompt) {
493
1043
  const error = nonInteractiveHint("a name argument or --name <name>");
494
- return finish(flags, { exitCode: 1, output: pc5.red(error) }, { ok: false, error });
1044
+ return finish(flags, { exitCode: 1, output: pc7.red(error) }, { ok: false, error });
495
1045
  }
496
1046
  const answer = await deps.prompt();
497
- if (answer === null) return finish(flags, { exitCode: 0, output: pc5.dim("Cancelled.") }, { ok: false, cancelled: true });
1047
+ if (answer === null) return finish(flags, { exitCode: 0, output: pc7.dim("Cancelled.") }, { ok: false, cancelled: true });
498
1048
  name = answer;
499
1049
  }
500
1050
  if (!name) {
501
1051
  const error = "A skill name is required (pass a name or --name).";
502
- return finish(flags, { exitCode: 1, output: pc5.red(error) }, { ok: false, error });
1052
+ return finish(flags, { exitCode: 1, output: pc7.red(error) }, { ok: false, error });
503
1053
  }
504
1054
  try {
505
1055
  const file = initSkill(name, { dir: flags.dir, description: flags.description, license: flags.license });
506
1056
  return { exitCode: 0, file, output: flags.json ? JSON.stringify({ ok: true, file }, null, 2) : `Created ${file}` };
507
1057
  } catch (e) {
508
1058
  const msg2 = e instanceof Error ? e.message : String(e);
509
- return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc5.red(msg2) };
1059
+ return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc7.red(msg2) };
510
1060
  }
511
1061
  }
512
1062
  function initCommand() {
@@ -519,7 +1069,7 @@ function initCommand() {
519
1069
 
520
1070
  // src/commands/search.ts
521
1071
  import { Command as Command5 } from "commander";
522
- import pc6 from "picocolors";
1072
+ import pc8 from "picocolors";
523
1073
  var MAX_LINE_WIDTH = 100;
524
1074
  function formatStars(n) {
525
1075
  if (n >= 1e6) {
@@ -532,19 +1082,19 @@ function formatStars(n) {
532
1082
  }
533
1083
  return String(n);
534
1084
  }
535
- function truncate(text3, width) {
536
- if (text3.length <= width) return text3;
1085
+ function truncate(text2, width) {
1086
+ if (text2.length <= width) return text2;
537
1087
  if (width <= 1) return "\u2026";
538
- return `${text3.slice(0, width - 1).trimEnd()}\u2026`;
1088
+ return `${text2.slice(0, width - 1).trimEnd()}\u2026`;
539
1089
  }
540
1090
  function formatSearchResult(item, width = process.stdout.columns ?? 80) {
541
- const stars = item.repo_stars != null ? pc6.dim(`\u2605 ${formatStars(item.repo_stars)}`) : "";
542
- const check = item.verified ? pc6.green("\u2713") : "";
543
- const pack = item.type === "pack" ? pc6.dim("[pack]") : "";
544
- const head = [pc6.bold(safeText(item.slug, 200)), stars, check, pack].filter(Boolean).join(" ");
1091
+ const stars = item.repo_stars != null ? pc8.dim(`\u2605 ${formatStars(item.repo_stars)}`) : "";
1092
+ const check = item.verified ? pc8.green("\u2713") : "";
1093
+ const pack = item.type === "pack" ? pc8.dim("[pack]") : "";
1094
+ const head = [pc8.bold(safeText(item.slug, 200)), stars, check, pack].filter(Boolean).join(" ");
545
1095
  const indent = " ";
546
1096
  const lineWidth = Math.max(10, Math.min(width, MAX_LINE_WIDTH) - indent.length);
547
- const desc = `${indent}${pc6.dim(truncate(safeText(item.description ?? ""), lineWidth))}`;
1097
+ const desc = `${indent}${pc8.dim(truncate(safeText(item.description ?? ""), lineWidth))}`;
548
1098
  return `${head}
549
1099
  ${desc}`;
550
1100
  }
@@ -558,63 +1108,63 @@ async function runSearch(query, flags) {
558
1108
  const r = await api(`/api/search?${params}`);
559
1109
  const items = r.items ?? [];
560
1110
  if (flags.json) return { items, output: JSON.stringify(items, null, 2) };
561
- const lines = items.map((s) => formatSearchResult(s));
1111
+ const lines = items.map((s2) => formatSearchResult(s2));
562
1112
  return { items, output: lines.join("\n\n") || "No skills found." };
563
1113
  }
564
1114
  function searchCommand() {
565
1115
  return new Command5("search").description("Search the SkillMD registry").argument("<query>", "search terms").option("--category <slug>", "filter by category").option("--verified", "only verified skills").option("--type <type>", "single | pack").option("--min-rating <n>", "minimum average rating (0-5)").option("--limit <n>", "max results", "20").option("--json", "machine-readable output").action(async (query, opts, cmd) => {
566
1116
  const merged = { ...cmd.parent?.opts(), ...opts };
567
- const run2 = await runSearch(query, merged);
1117
+ const run2 = await waitFor("hop", `Searching for ${query}`, runSearch(query, merged), { json: Boolean(merged.json) });
568
1118
  console.log(run2.output);
569
1119
  });
570
1120
  }
571
1121
 
572
1122
  // src/commands/info.ts
573
1123
  import { Command as Command6 } from "commander";
574
- import pc7 from "picocolors";
1124
+ import pc9 from "picocolors";
575
1125
  async function runInfo(slug, flags, deps = {}) {
576
1126
  const [owner, name] = slug.split("/");
577
1127
  if (!owner || !name || slug.split("/").length !== 2) {
578
- return { ok: false, exitCode: 1, output: pc7.red("Expected owner/name, e.g. anthropic/pdf") };
1128
+ return { ok: false, exitCode: 1, output: pc9.red("Expected owner/name, e.g. anthropic/pdf") };
579
1129
  }
580
1130
  const api = deps.api ?? createClient(flags).api;
581
- let s;
1131
+ let s2;
582
1132
  try {
583
- s = await api(`/api/skills/${owner}/${name}`);
1133
+ s2 = await api(`/api/skills/${owner}/${name}`);
584
1134
  } catch (err) {
585
1135
  const message = err instanceof Error ? err.message : String(err);
586
1136
  if (/^SkillMD API 404 /.test(message)) {
587
- return { ok: false, exitCode: 1, output: pc7.red(`Skill not found on the registry: ${slug}`) };
1137
+ return { ok: false, exitCode: 1, output: pc9.red(`Skill not found on the registry: ${slug}`) };
588
1138
  }
589
- return { ok: false, exitCode: 1, output: pc7.red(`Could not reach the SkillMD registry: ${message}`) };
1139
+ return { ok: false, exitCode: 1, output: pc9.red(`Could not reach the SkillMD registry: ${message}`) };
590
1140
  }
591
- if (flags.json) return { ok: true, exitCode: 0, output: JSON.stringify(s, null, 2), skill: s };
592
- const slugTxt = safeText(s.slug, 200);
1141
+ if (flags.json) return { ok: true, exitCode: 0, output: JSON.stringify(s2, null, 2), skill: s2 };
1142
+ const slugTxt = safeText(s2.slug, 200);
593
1143
  const lines = [];
594
1144
  lines.push(
595
- `${pc7.bold(slugTxt)} ${pc7.dim(`[${safeText(s.type ?? "single", 40)}]`)} ${s.verified ? pc7.green("\u2713 verified") : pc7.yellow("unverified")}`
1145
+ `${pc9.bold(slugTxt)} ${pc9.dim(`[${safeText(s2.type ?? "single", 40)}]`)} ${s2.verified ? pc9.green("\u2713 verified") : pc9.yellow("unverified")}`
596
1146
  );
597
- lines.push(` ${safeText(s.description)}`);
1147
+ lines.push(` ${safeText(s2.description)}`);
598
1148
  lines.push(
599
- pc7.dim(
600
- ` category: ${safeText(s.category ?? "-", 100) || "-"} \xB7 rating: ${s.avg_rating != null ? s.avg_rating.toFixed(1) : "-"} (${s.rating_count ?? 0}) \xB7 installs: ${s.install_count ?? 0}`
1149
+ pc9.dim(
1150
+ ` category: ${safeText(s2.category ?? "-", 100) || "-"} \xB7 rating: ${s2.avg_rating != null ? s2.avg_rating.toFixed(1) : "-"} (${s2.rating_count ?? 0}) \xB7 installs: ${s2.install_count ?? 0}`
601
1151
  )
602
1152
  );
603
- const securityFlags2 = (s.security_flags ?? []).map((f) => safeText(f, 60)).filter(Boolean);
1153
+ const securityFlags2 = (s2.security_flags ?? []).map((f) => safeText(f, 60)).filter(Boolean);
604
1154
  lines.push(
605
- pc7.dim(` security: ${securityFlags2.join(", ") || "unscanned"} \xB7 license: ${safeText(s.provenance?.license ?? s.license ?? "unknown", 100) || "unknown"}`)
1155
+ pc9.dim(` security: ${securityFlags2.join(", ") || "unscanned"} \xB7 license: ${safeText(s2.provenance?.license ?? s2.license ?? "unknown", 100) || "unknown"}`)
606
1156
  );
607
- const sourceRepo = safeText(s.provenance?.source_repo ?? s.source_repo, 300);
608
- const commitSha = safeText(s.provenance?.commit_sha ?? s.commit_sha, 100);
609
- if (sourceRepo) lines.push(pc7.dim(` source: ${sourceRepo}${commitSha ? ` @ ${commitSha.slice(0, 8)}` : ""}`));
610
- lines.push(pc7.dim(` page: https://skillmd.com/skills/${slugTxt}`));
611
- lines.push(pc7.dim(` install: ${safeText(s.install?.cli, 300) || `npx get-skill add ${slugTxt}`}`));
612
- return { ok: true, exitCode: 0, output: lines.join("\n"), skill: s };
1157
+ const sourceRepo = safeText(s2.provenance?.source_repo ?? s2.source_repo, 300);
1158
+ const commitSha = safeText(s2.provenance?.commit_sha ?? s2.commit_sha, 100);
1159
+ if (sourceRepo) lines.push(pc9.dim(` source: ${sourceRepo}${commitSha ? ` @ ${commitSha.slice(0, 8)}` : ""}`));
1160
+ lines.push(pc9.dim(` page: https://skillmd.com/skills/${slugTxt}`));
1161
+ lines.push(pc9.dim(` install: ${safeText(s2.install?.cli, 300) || `npx get-skill add ${slugTxt}`}`));
1162
+ return { ok: true, exitCode: 0, output: lines.join("\n"), skill: s2 };
613
1163
  }
614
1164
  function infoCommand() {
615
1165
  return new Command6("info").description("Show registry details for a skill: verification, security flags, provenance, rating").argument("<slug>", "owner/name, e.g. anthropic/pdf").action(async (slug, _opts, cmd) => {
616
1166
  const flags = { ...cmd.parent?.opts() };
617
- const run2 = await runInfo(slug, flags);
1167
+ const run2 = await waitFor("drop", `Fetching ${slug}`, runInfo(slug, flags), { json: flags.json });
618
1168
  if (run2.output) (run2.ok ? console.log : console.error)(run2.output);
619
1169
  process.exitCode = run2.exitCode;
620
1170
  });
@@ -623,9 +1173,8 @@ function infoCommand() {
623
1173
  // src/commands/add.ts
624
1174
  import { Command as Command7, Option } from "commander";
625
1175
  import { homedir as homedir4 } from "os";
626
- import * as p3 from "@clack/prompts";
627
1176
  import { dirname as dirname2, resolve as resolve3 } from "path";
628
- import pc8 from "picocolors";
1177
+ import pc10 from "picocolors";
629
1178
  import { lint as lint2, parseSkillMd as parseSkillMd2 } from "@skillmds/core";
630
1179
 
631
1180
  // src/claudePlugin.ts
@@ -669,7 +1218,7 @@ function exec(deps) {
669
1218
  });
670
1219
  }
671
1220
  function marketplacePluginName(owner, slug) {
672
- const clean = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1221
+ const clean = (s2) => s2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
673
1222
  const base = clean(slug) || "plugin";
674
1223
  return owner.toLowerCase() === "skillmd" ? base : `${clean(owner)}-${base}`;
675
1224
  }
@@ -707,21 +1256,21 @@ ${r.stderr}`;
707
1256
  }
708
1257
  function msg(e) {
709
1258
  if (e && typeof e === "object" && "stderr" in e) {
710
- const s = String(e.stderr ?? "").trim();
711
- if (s) return s.split(/\r?\n/)[0];
1259
+ const s2 = String(e.stderr ?? "").trim();
1260
+ if (s2) return s2.split(/\r?\n/)[0];
712
1261
  }
713
1262
  return e instanceof Error ? e.message.split(/\r?\n/)[0] : String(e);
714
1263
  }
715
1264
  var SITE_URL = "https://skillmd.com";
716
1265
  function readJson(path) {
717
- let text3;
1266
+ let text2;
718
1267
  try {
719
- text3 = readFileSync(path, "utf8");
1268
+ text2 = readFileSync(path, "utf8");
720
1269
  } catch {
721
1270
  return "absent";
722
1271
  }
723
1272
  try {
724
- const v = JSON.parse(text3);
1273
+ const v = JSON.parse(text2);
725
1274
  return v && typeof v === "object" && !Array.isArray(v) ? v : "unparsable";
726
1275
  } catch {
727
1276
  return "unparsable";
@@ -812,7 +1361,7 @@ async function installPluginNatively(owner, slug, deps = {}) {
812
1361
  const plugins = obj(installedDoc.plugins);
813
1362
  const prior = Array.isArray(plugins[qualified]) ? plugins[qualified] : [];
814
1363
  plugins[qualified] = [
815
- ...prior.filter((p7) => p7?.scope !== "user"),
1364
+ ...prior.filter((p) => p?.scope !== "user"),
816
1365
  { scope: "user", installPath, version: version2, installedAt: now, lastUpdated: now }
817
1366
  ];
818
1367
  installedDoc.plugins = plugins;
@@ -831,22 +1380,22 @@ async function installPluginNatively(owner, slug, deps = {}) {
831
1380
  }
832
1381
 
833
1382
  // src/commands/add.ts
834
- var infoNotice = (text3) => ({ text: text3, style: "info" });
835
- var warnNotice = (text3) => ({ text: text3, style: "warn" });
836
- var skipNotice = (text3) => ({ text: text3, style: "skip" });
837
- var replacedNotice = (text3) => ({ text: text3, style: "warn", suppressInSummary: true });
1383
+ var infoNotice = (text2) => ({ text: text2, style: "info" });
1384
+ var warnNotice = (text2) => ({ text: text2, style: "warn" });
1385
+ var skipNotice = (text2) => ({ text: text2, style: "skip" });
1386
+ var replacedNotice = (text2) => ({ text: text2, style: "warn", suppressInSummary: true });
838
1387
  function noticeLine(n) {
839
1388
  switch (n.style) {
840
1389
  case "info":
841
- return pc8.dim(`${GLYPH.info} ${n.text}`);
1390
+ return pc10.dim(`${GLYPH.info} ${n.text}`);
842
1391
  case "skip":
843
- return pc8.dim(`${GLYPH.skip} ${n.text}`);
1392
+ return pc10.dim(`${GLYPH.skip} ${n.text}`);
844
1393
  case "warn":
845
- return pc8.yellow(`${GLYPH.warn} ${n.text}`);
1394
+ return pc10.yellow(`${GLYPH.warn} ${n.text}`);
846
1395
  }
847
1396
  }
848
1397
  async function promptScopeInteractive(ctx) {
849
- const choice = await p3.select({
1398
+ const choice = await select({
850
1399
  message: "Installation scope",
851
1400
  initialValue: ctx.suggestGlobal,
852
1401
  options: [
@@ -854,7 +1403,7 @@ async function promptScopeInteractive(ctx) {
854
1403
  { value: true, label: "Global", hint: "install in your home directory (available across all projects)" }
855
1404
  ]
856
1405
  });
857
- return p3.isCancel(choice) ? null : choice;
1406
+ return isCancel(choice) ? null : choice;
858
1407
  }
859
1408
  function agentsPromptMessage(managed) {
860
1409
  return managed ? `Claude Code already has ${managed} as a plugin. Which other agents should get its skills too?` : "Which agents do you want to install to?";
@@ -862,13 +1411,13 @@ function agentsPromptMessage(managed) {
862
1411
  async function promptAgentsInteractive(ctx) {
863
1412
  if (ctx.detected.length < 2) return ctx.detected;
864
1413
  const remembered = (readConfig().lastAgents ?? []).filter((id) => ctx.detected.includes(id));
865
- const picked = await p3.multiselect({
1414
+ const picked = await multiselect({
866
1415
  message: agentsPromptMessage(ctx.managed),
867
1416
  options: ctx.detected.map((id) => ({ value: id, label: id, hint: ctx.dirFor(id) })),
868
1417
  initialValues: remembered.length ? remembered : ctx.detected,
869
1418
  required: true
870
1419
  });
871
- return p3.isCancel(picked) ? null : picked;
1420
+ return isCancel(picked) ? null : picked;
872
1421
  }
873
1422
  function unverifiedFilesNote(n) {
874
1423
  const [f, it] = n === 1 ? ["file", "it"] : ["files", "them"];
@@ -1144,7 +1693,7 @@ function finish2(flags, outcome) {
1144
1693
  };
1145
1694
  }
1146
1695
  case "list": {
1147
- const human = outcome.skills.length ? outcome.skills.map((s) => `${pc8.bold(s.name)} ${pc8.dim(s.description)}`).join("\n") : "No skills found.";
1696
+ const human = outcome.skills.length ? outcome.skills.map((s2) => `${pc10.bold(s2.name)} ${pc10.dim(s2.description)}`).join("\n") : "No skills found.";
1148
1697
  return { written: [], blocked: [], exitCode: 0, output: json ? JSON.stringify({ ok: true, skills: outcome.skills }, null, 2) : human };
1149
1698
  }
1150
1699
  case "cancel": {
@@ -1154,7 +1703,7 @@ function finish2(flags, outcome) {
1154
1703
  blocked: [],
1155
1704
  exitCode: 0,
1156
1705
  cancelled: true,
1157
- output: json ? JSON.stringify(doc, null, 2) : pc8.dim("Installation cancelled \u2014 nothing was installed.")
1706
+ output: json ? JSON.stringify(doc, null, 2) : pc10.dim("Installation cancelled \u2014 nothing was installed.")
1158
1707
  };
1159
1708
  }
1160
1709
  case "fail": {
@@ -1164,7 +1713,7 @@ function finish2(flags, outcome) {
1164
1713
  if (warnings.length) doc.warnings = warnings;
1165
1714
  const human = [
1166
1715
  ...(outcome.notices ?? []).map(noticeLine),
1167
- ...outcome.blocked?.length ? outcome.blocked.map((b) => pc8.red(`${GLYPH.blocked} ${b.name} blocked \u2014 ${b.reason}`)) : [pc8.red(outcome.error)]
1716
+ ...outcome.blocked?.length ? outcome.blocked.map((b) => pc10.red(`${GLYPH.blocked} ${b.name} blocked \u2014 ${b.reason}`)) : [pc10.red(outcome.error)]
1168
1717
  ].join("\n");
1169
1718
  return { written: [], blocked: outcome.blocked ?? [], exitCode: 1, output: json ? JSON.stringify(doc, null, 2) : human };
1170
1719
  }
@@ -1174,7 +1723,7 @@ function renderHuman(notices, records, paths) {
1174
1723
  const lines = notices.map(noticeLine);
1175
1724
  for (const r of records) {
1176
1725
  if (r.kind === "blocked") {
1177
- lines.push(pc8.red(r.message));
1726
+ lines.push(pc10.red(r.message));
1178
1727
  continue;
1179
1728
  }
1180
1729
  const { doc } = r;
@@ -1386,32 +1935,39 @@ function addCommand() {
1386
1935
  });
1387
1936
  }
1388
1937
  var clackUI = {
1389
- intro: (m) => p3.intro(m),
1390
- spinner: () => p3.spinner(),
1391
- message: (m) => p3.log.message(m),
1392
- outro: (m) => p3.outro(m),
1393
- cancel: (m) => p3.cancel(m),
1938
+ intro: (m) => intro(m),
1939
+ spinner: () => spinner("drop"),
1940
+ message: (m) => log.message(m),
1941
+ outro: (m) => outro(m),
1942
+ cancel: (m) => cancel(m),
1394
1943
  confirm: async (m) => {
1395
- const answer = await p3.confirm({ message: m, initialValue: true });
1396
- return p3.isCancel(answer) ? null : answer;
1944
+ const answer = await confirm({ message: m, initialValue: true });
1945
+ return isCancel(answer) ? null : answer;
1397
1946
  }
1398
1947
  };
1399
- var FIND_SKILLS = "skillmd/find-skills";
1948
+ var FIND_A_SKILL = "skillmd/find-a-skill";
1949
+ var FIND_A_SKILL_SOURCES = [`registry:${FIND_A_SKILL}`, "registry:skillmd/find-skills"];
1950
+ function failureReason(r) {
1951
+ const blocked = r.blocked[0]?.reason;
1952
+ if (blocked) return blocked;
1953
+ const lines = stripAnsi(r.output).split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
1954
+ return lines.at(-1);
1955
+ }
1400
1956
  async function offerFindSkills(source, flags, run2, ui, deps) {
1401
1957
  if (!ui.confirm || run2.exitCode !== 0 || !run2.written.length || !run2.scope || !run2.agents?.length) return;
1402
- if (source.replace(/^registry:/, "").toLowerCase() === FIND_SKILLS) return;
1958
+ if (FIND_A_SKILL_SOURCES.includes(`registry:${source.replace(/^registry:/, "").toLowerCase()}`)) return;
1403
1959
  const cfg = readConfig(flags.home);
1404
1960
  if (cfg.findSkillsOffered) return;
1405
1961
  const global = run2.scope === "global";
1406
1962
  const lock = readLock({ global, cwd: flags.cwd, home: flags.home });
1407
- if (Object.values(lock.skills).some((e) => e.source === `registry:${FIND_SKILLS}`)) return;
1963
+ if (Object.values(lock.skills).some((e) => FIND_A_SKILL_SOURCES.includes(e.source))) return;
1408
1964
  try {
1409
1965
  writeConfig({ ...cfg, findSkillsOffered: true }, flags.home);
1410
1966
  } catch {
1411
1967
  }
1412
- const yes = await ui.confirm("Also add find-skills, so your agent can look for more skills on SkillMD when a task needs one?");
1968
+ const yes = await ui.confirm("Also add find-a-skill, so your agent can look for more skills on SkillMD when a task needs one?");
1413
1969
  if (!yes) return;
1414
- const r = await runAdd(FIND_SKILLS, {
1970
+ const r = await runAdd(FIND_A_SKILL, {
1415
1971
  ...flags,
1416
1972
  yes: true,
1417
1973
  json: false,
@@ -1422,7 +1978,8 @@ async function offerFindSkills(source, flags, run2, ui, deps) {
1422
1978
  project: !global,
1423
1979
  agent: run2.agents
1424
1980
  }, deps);
1425
- ui.message(r.exitCode === 0 ? `${pc8.green(GLYPH.ok)} find-skills added` : pc8.yellow(`${GLYPH.warn} find-skills could not be added. Try later: npx get-skill add ${FIND_SKILLS}`));
1981
+ const why = r.exitCode === 0 ? "" : failureReason(r);
1982
+ ui.message(r.exitCode === 0 ? `${pc10.green(GLYPH.ok)} find-a-skill added` : pc10.yellow(`${GLYPH.warn} find-a-skill was not added${why ? `: ${why}` : ""}. To add it yourself: npx get-skill add ${FIND_A_SKILL}`));
1426
1983
  }
1427
1984
  async function runAddWithUI(source, flags, opts = {}) {
1428
1985
  const term = opts.term ?? { ...realTerminal(), env: flags.env ?? process.env };
@@ -1439,14 +1996,14 @@ async function runAddWithUI(source, flags, opts = {}) {
1439
1996
  // this spread.
1440
1997
  ...base,
1441
1998
  resolve: async (arg, f) => {
1442
- const s = ui.spinner();
1443
- s.start(`Resolving ${arg}`);
1999
+ const s2 = ui.spinner();
2000
+ s2.start(`Resolving ${arg}`);
1444
2001
  try {
1445
2002
  const c = await base.resolve(arg, f);
1446
- s.stop(`${c.length} skill${c.length === 1 ? "" : "s"} found`);
2003
+ s2.stop(`${c.length} skill${c.length === 1 ? "" : "s"} found`);
1447
2004
  return c;
1448
2005
  } catch (e) {
1449
- s.stop(pc8.red("Could not resolve the source"));
2006
+ s2.stop(pc10.red("Could not resolve the source"));
1450
2007
  throw e;
1451
2008
  }
1452
2009
  }
@@ -1457,10 +2014,10 @@ async function runAddWithUI(source, flags, opts = {}) {
1457
2014
  }
1458
2015
  ui.message(run2.output);
1459
2016
  await offerFindSkills(source, flags, run2, ui, base);
1460
- ui.outro(run2.exitCode === 0 ? `Done. Run ${pc8.cyan("npx get-skill list")} to see everything installed.` : pc8.red("Some skills were not installed."));
2017
+ ui.outro(run2.exitCode === 0 ? `Done. Run ${pc10.cyan("npx get-skill list")} to see everything installed.` : pc10.red("Some skills were not installed."));
1461
2018
  return { run: run2, printed: true };
1462
2019
  } catch (e) {
1463
- ui.outro(pc8.red("Nothing was installed."));
2020
+ ui.outro(pc10.red("Nothing was installed."));
1464
2021
  throw e;
1465
2022
  }
1466
2023
  }
@@ -1469,20 +2026,20 @@ async function runAddWithUI(source, flags, opts = {}) {
1469
2026
  import { Command as Command8 } from "commander";
1470
2027
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
1471
2028
  import { join as join4 } from "path";
1472
- import pc9 from "picocolors";
2029
+ import pc11 from "picocolors";
1473
2030
  import { parseSkillMd as parseSkillMd3 } from "@skillmds/core";
1474
2031
  function runList(flags = {}) {
1475
2032
  const scopes = scopesFor(flags);
1476
2033
  let items = [];
1477
2034
  for (const global of scopes) {
1478
- for (const s of listInstalled({ global, cwd: flags.cwd, home: flags.home })) {
1479
- const md = join4(s.path, "SKILL.md");
2035
+ for (const s2 of listInstalled({ global, cwd: flags.cwd, home: flags.home })) {
2036
+ const md = join4(s2.path, "SKILL.md");
1480
2037
  let description = "";
1481
2038
  if (existsSync3(md)) {
1482
2039
  const parsed = parseSkillMd3(readFileSync2(md, "utf8"));
1483
2040
  description = "error" in parsed ? `(invalid SKILL.md \u2014 ${parsed.error})` : parsed.description;
1484
2041
  }
1485
- items.push({ ...s, description: safeText(description, 80) });
2042
+ items.push({ ...s2, description: safeText(description, 80) });
1486
2043
  }
1487
2044
  }
1488
2045
  if (flags.agent?.length) items = items.filter((i) => i.agents.some((a) => flags.agent.includes(a)));
@@ -1490,13 +2047,13 @@ function runList(flags = {}) {
1490
2047
  if (flags.json) {
1491
2048
  return { items, output: JSON.stringify(items.map(({ name, scope, path, agents, source, mode, tracked, description }) => ({ name, scope, path, agents, source: source ?? null, mode, tracked, description })), null, 2) };
1492
2049
  }
1493
- if (items.length === 0) return { items, output: pc9.dim("No installed skills found. Install one with `npx get-skill add owner/name`.") };
2050
+ if (items.length === 0) return { items, output: pc11.dim("No installed skills found. Install one with `npx get-skill add owner/name`.") };
1494
2051
  const w = Math.max(...items.map((i) => i.name.length), 4);
1495
2052
  const lines = items.map((i) => {
1496
- const tag = i.tracked ? pc9.dim(i.source ?? "") : pc9.yellow("untracked");
2053
+ const tag = i.tracked ? pc11.dim(i.source ?? "") : pc11.yellow("untracked");
1497
2054
  const modes = [...new Set(Object.values(i.mode))].join("/");
1498
- return `${pc9.bold(i.name.padEnd(w))} ${pc9.cyan(i.scope.padEnd(7))} ${i.agents.join(", ").padEnd(24)} ${pc9.dim(modes.padEnd(9))} ${tag}
1499
- ${" ".repeat(w + 2)}${pc9.dim(i.description)}`;
2055
+ return `${pc11.bold(i.name.padEnd(w))} ${pc11.cyan(i.scope.padEnd(7))} ${i.agents.join(", ").padEnd(24)} ${pc11.dim(modes.padEnd(9))} ${tag}
2056
+ ${" ".repeat(w + 2)}${pc11.dim(i.description)}`;
1500
2057
  });
1501
2058
  return { items, output: lines.join("\n") };
1502
2059
  }
@@ -1510,16 +2067,15 @@ function listCommand() {
1510
2067
  import { Command as Command9 } from "commander";
1511
2068
  import { rmSync } from "fs";
1512
2069
  import { join as join5, resolve as resolve4 } from "path";
1513
- import * as p4 from "@clack/prompts";
1514
- import pc10 from "picocolors";
2070
+ import pc12 from "picocolors";
1515
2071
  var defaultDeps = (flags) => isInteractive(flags) ? {
1516
2072
  confirm: async (msg2) => {
1517
- const a = await p4.confirm({ message: msg2, initialValue: false });
1518
- return !p4.isCancel(a) && a;
2073
+ const a = await confirm({ message: msg2, initialValue: false });
2074
+ return !isCancel(a) && a;
1519
2075
  },
1520
2076
  pick: async ({ names }) => {
1521
- const a = await p4.multiselect({ message: "Which skills do you want to remove?", options: names.map((n) => ({ value: n, label: n })), required: true });
1522
- return p4.isCancel(a) ? null : a;
2077
+ const a = await multiselect({ message: "Which skills do you want to remove?", options: names.map((n) => ({ value: n, label: n })), required: true });
2078
+ return isCancel(a) ? null : a;
1523
2079
  }
1524
2080
  } : {};
1525
2081
  function finish3(flags, result, doc) {
@@ -1532,32 +2088,32 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1532
2088
  const lines = [];
1533
2089
  if (flags.all && names.length) {
1534
2090
  const error = "--all cannot be combined with skill names";
1535
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.red(error) }, { ok: false, error });
2091
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.red(error) }, { ok: false, error });
1536
2092
  }
1537
2093
  const perScope = scopes.map((scope) => ({ scope, installed: listInstalled(scope) }));
1538
- const allNames = [...new Set(perScope.flatMap((s) => s.installed.map((i) => i.name)))];
2094
+ const allNames = [...new Set(perScope.flatMap((s2) => s2.installed.map((i) => i.name)))];
1539
2095
  const nothingInstalled = `No installed skills found in ${where} scope.`;
1540
2096
  let targets = names;
1541
2097
  if (flags.all) {
1542
2098
  if (allNames.length === 0) {
1543
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
2099
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
1544
2100
  }
1545
2101
  targets = allNames;
1546
2102
  } else if (targets.length === 0) {
1547
2103
  if (!deps.pick) {
1548
2104
  const error = "Give one or more skill names, or --all.";
1549
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.red(`${error} `) + pc10.dim(nonInteractiveHint("skill names and -y")) }, { ok: false, error });
2105
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.red(`${error} `) + pc12.dim(nonInteractiveHint("skill names and -y")) }, { ok: false, error });
1550
2106
  }
1551
2107
  if (allNames.length === 0) {
1552
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
2108
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
1553
2109
  }
1554
2110
  const picked = await deps.pick({ names: allNames });
1555
- if (!picked) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc10.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
2111
+ if (!picked) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc12.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
1556
2112
  targets = picked;
1557
2113
  }
1558
2114
  const known = targets.filter((n) => allNames.includes(n));
1559
2115
  const missing = targets.filter((n) => !known.includes(n));
1560
- for (const n of missing) lines.push(pc10.yellow(`- ${n} not installed (${where} scope)`));
2116
+ for (const n of missing) lines.push(pc12.yellow(`- ${n} not installed (${where} scope)`));
1561
2117
  if (known.length === 0) {
1562
2118
  const output = lines.join("\n");
1563
2119
  return finish3(flags, { removed: [], exitCode: 1, output }, { ok: false, error: `not installed: ${missing.join(", ")}`, missing });
@@ -1567,17 +2123,17 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1567
2123
  const ok = deps.confirm ? await deps.confirm(`Remove ${known.length} skill${known.length === 1 ? "" : "s"} (${known.join(", ")}) ${what} in ${where} scope?`) : null;
1568
2124
  if (ok === null) {
1569
2125
  const error = nonInteractiveHint("-y");
1570
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.red(error) }, { ok: false, error });
2126
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.red(error) }, { ok: false, error });
1571
2127
  }
1572
- if (!ok) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc10.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
2128
+ if (!ok) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc12.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
1573
2129
  }
1574
2130
  const removed = [];
1575
2131
  const skipped = [];
1576
2132
  let changed = 0;
1577
2133
  for (const name of known) {
1578
2134
  for (const { scope, installed } of perScope) {
1579
- if (!installed.some((s) => s.name === name)) continue;
1580
- const tag = scopes.length > 1 ? pc10.dim(` [${scopeLabel(scope)}]`) : "";
2135
+ if (!installed.some((s2) => s2.name === name)) continue;
2136
+ const tag = scopes.length > 1 ? pc12.dim(` [${scopeLabel(scope)}]`) : "";
1581
2137
  if (flags.agent?.length) {
1582
2138
  const lock = readLock({ global: Boolean(scope.global), cwd: scope.cwd, home: scope.home });
1583
2139
  const entry = lock.skills[name];
@@ -1588,11 +2144,11 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1588
2144
  try {
1589
2145
  dir = agentDir(a, scope);
1590
2146
  } catch (e) {
1591
- lines.push(pc10.yellow(`- ${a}: ${e instanceof Error ? e.message : String(e)}`));
2147
+ lines.push(pc12.yellow(`- ${a}: ${e instanceof Error ? e.message : String(e)}`));
1592
2148
  continue;
1593
2149
  }
1594
2150
  if (resolve4(dir) === canonRoot || entry?.mode[a] === "canonical") {
1595
- lines.push(pc10.dim(`${GLYPH.skip} ${name}: ${a} reads the canonical copy directly \u2014 unlinking is a no-op (use remove without -a to delete the skill)`));
2151
+ lines.push(pc12.dim(`${GLYPH.skip} ${name}: ${a} reads the canonical copy directly \u2014 unlinking is a no-op (use remove without -a to delete the skill)`));
1596
2152
  skipped.push({ skill: name, agent: a, scope: scopeLabel(scope), reason: "reads the canonical copy directly" });
1597
2153
  acted = true;
1598
2154
  continue;
@@ -1602,17 +2158,17 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1602
2158
  rmSync(pth, { recursive: true, force: true });
1603
2159
  removed.push(pth);
1604
2160
  acted = true;
1605
- lines.push(pc10.green(`${GLYPH.ok} removed ${name} from ${a}`) + tag);
2161
+ lines.push(pc12.green(`${GLYPH.ok} removed ${name} from ${a}`) + tag);
1606
2162
  }
1607
2163
  }
1608
- if (!acted) lines.push(pc10.yellow(`- ${name} is not linked into ${flags.agent.join(", ")}`) + tag);
2164
+ if (!acted) lines.push(pc12.yellow(`- ${name} is not linked into ${flags.agent.join(", ")}`) + tag);
1609
2165
  if (entry) {
1610
2166
  const left = entry.agents.filter((a) => !flags.agent.includes(a));
1611
2167
  if (left.length) upsertEntry({ global: Boolean(scope.global), cwd: scope.cwd, home: scope.home }, name, { ...entry, agents: left, mode: Object.fromEntries(Object.entries(entry.mode).filter(([a]) => left.includes(a))) });
1612
2168
  else {
1613
2169
  const r2 = uninstallSkill(name, scope);
1614
2170
  removed.push(...r2.removed);
1615
- lines.push(pc10.dim(` (no agents left \u2014 removed the canonical copy too)`));
2171
+ lines.push(pc12.dim(` (no agents left \u2014 removed the canonical copy too)`));
1616
2172
  }
1617
2173
  }
1618
2174
  if (acted) changed++;
@@ -1621,7 +2177,7 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1621
2177
  const r = uninstallSkill(name, scope);
1622
2178
  removed.push(...r.removed);
1623
2179
  changed++;
1624
- lines.push(pc10.green(`${GLYPH.ok} removed ${name}`) + tag + pc10.dim(` (${r.removed.length} path${r.removed.length === 1 ? "" : "s"}${r.untracked ? ", untracked" : ""})`));
2180
+ lines.push(pc12.green(`${GLYPH.ok} removed ${name}`) + tag + pc12.dim(` (${r.removed.length} path${r.removed.length === 1 ? "" : "s"}${r.untracked ? ", untracked" : ""})`));
1625
2181
  }
1626
2182
  }
1627
2183
  const exitCode = changed ? 0 : 1;
@@ -1639,7 +2195,7 @@ function removeCommand() {
1639
2195
 
1640
2196
  // src/commands/update.ts
1641
2197
  import { Command as Command10 } from "commander";
1642
- import pc11 from "picocolors";
2198
+ import pc13 from "picocolors";
1643
2199
  import { lint as lint3 } from "@skillmds/core";
1644
2200
  function pickSkillTree(files, want) {
1645
2201
  const norm = files.map((f) => ({ path: f.path.replace(/\\/g, "/"), contents: f.contents }));
@@ -1702,64 +2258,64 @@ async function runUpdate(names, flags, deps = defaultUpdateDeps) {
1702
2258
  for (const global of scopes) {
1703
2259
  const scope = { global, cwd: flags.cwd, home: flags.home };
1704
2260
  const lock = readLock(scope);
1705
- const installed = listInstalled(scope).filter((s) => names.length === 0 || names.includes(s.name));
1706
- for (const s of installed) {
1707
- seen.add(s.name);
1708
- const label2 = `${s.name}${scopes.length > 1 ? pc11.dim(` [${s.scope}]`) : ""}`;
1709
- const entry = lock.skills[s.name];
2261
+ const installed = listInstalled(scope).filter((s2) => names.length === 0 || names.includes(s2.name));
2262
+ for (const s2 of installed) {
2263
+ seen.add(s2.name);
2264
+ const label2 = `${s2.name}${scopes.length > 1 ? pc13.dim(` [${s2.scope}]`) : ""}`;
2265
+ const entry = lock.skills[s2.name];
1710
2266
  if (!entry) {
1711
- lines.push(pc11.dim(`- ${label2} untracked \u2014 reinstall with \`npx get-skill add\` to track updates`));
2267
+ lines.push(pc13.dim(`- ${label2} untracked \u2014 reinstall with \`npx get-skill add\` to track updates`));
1712
2268
  continue;
1713
2269
  }
1714
2270
  if (entry.source.startsWith("local:")) {
1715
- lines.push(pc11.dim(`- ${label2} local source \u2014 not auto-updated`));
2271
+ lines.push(pc13.dim(`- ${label2} local source \u2014 not auto-updated`));
1716
2272
  continue;
1717
2273
  }
1718
2274
  try {
1719
- const latest = await deps.fetchLatest(entry.source, flags, s.name);
2275
+ const latest = await deps.fetchLatest(entry.source, flags, s2.name);
1720
2276
  if (!latest) {
1721
- failed.push(s.name);
1722
- lines.push(pc11.red(`${GLYPH.blocked} ${label2} no longer available upstream (${entry.source})`));
2277
+ failed.push(s2.name);
2278
+ lines.push(pc13.red(`${GLYPH.blocked} ${label2} no longer available upstream (${entry.source})`));
1723
2279
  continue;
1724
2280
  }
1725
2281
  if (entry.source.startsWith("registry:") && latest.commit_sha && entry.commit_sha && latest.commit_sha === entry.commit_sha) {
1726
- lines.push(pc11.dim(`- ${label2} up to date`));
2282
+ lines.push(pc13.dim(`- ${label2} up to date`));
1727
2283
  continue;
1728
2284
  }
1729
2285
  if (digestOf(latest.files) === entry.digest) {
1730
- lines.push(pc11.dim(`- ${label2} up to date`));
2286
+ lines.push(pc13.dim(`- ${label2} up to date`));
1731
2287
  continue;
1732
2288
  }
1733
2289
  if (flags.check) {
1734
- available.push(s.name);
1735
- lines.push(pc11.yellow(`\u2191 ${label2} update available`));
2290
+ available.push(s2.name);
2291
+ lines.push(pc13.yellow(`\u2191 ${label2} update available`));
1736
2292
  continue;
1737
2293
  }
1738
2294
  const md = latest.files.find((f) => f.path === "SKILL.md");
1739
2295
  const raw = md ? typeof md.contents === "string" ? md.contents : md.contents.toString("utf8") : "";
1740
- const result = lint3(raw, { slug: s.name });
2296
+ const result = lint3(raw, { slug: s2.name });
1741
2297
  if (!result.ok) {
1742
- failed.push(s.name);
1743
- lines.push(pc11.red(`${GLYPH.blocked} ${label2} update failed lint (${result.diagnostics.filter((d) => d.severity === "error").map((d) => d.id).join(", ")}) \u2014 kept the installed version`));
2298
+ failed.push(s2.name);
2299
+ lines.push(pc13.red(`${GLYPH.blocked} ${label2} update failed lint (${result.diagnostics.filter((d) => d.severity === "error").map((d) => d.id).join(", ")}) \u2014 kept the installed version`));
1744
2300
  continue;
1745
2301
  }
1746
- await installSkill({ name: s.name, files: latest.files, source: entry.source, commit_sha: latest.commit_sha, scope, agents: entry.agents, explicitAgents: true, mode: Object.values(entry.mode).includes("copy") ? "copy" : "link" });
1747
- updated.push(s.name);
1748
- lines.push(pc11.green(`${GLYPH.ok} ${label2} updated`));
2302
+ await installSkill({ name: s2.name, files: latest.files, source: entry.source, commit_sha: latest.commit_sha, scope, agents: entry.agents, explicitAgents: true, mode: Object.values(entry.mode).includes("copy") ? "copy" : "link" });
2303
+ updated.push(s2.name);
2304
+ lines.push(pc13.green(`${GLYPH.ok} ${label2} updated`));
1749
2305
  } catch (e) {
1750
- failed.push(s.name);
1751
- lines.push(pc11.red(`${GLYPH.blocked} ${label2} ${e instanceof Error ? e.message : String(e)}`));
2306
+ failed.push(s2.name);
2307
+ lines.push(pc13.red(`${GLYPH.blocked} ${label2} ${e instanceof Error ? e.message : String(e)}`));
1752
2308
  }
1753
2309
  }
1754
2310
  }
1755
2311
  for (const n of names) {
1756
2312
  if (seen.has(n)) continue;
1757
2313
  failed.push(n);
1758
- lines.push(pc11.yellow(`- ${n} not installed`));
2314
+ lines.push(pc13.yellow(`- ${n} not installed`));
1759
2315
  }
1760
2316
  const exitCode = failed.length ? 1 : 0;
1761
2317
  if (flags.json) return { updated, available, failed, exitCode, output: JSON.stringify({ ok: exitCode === 0, updated: [...new Set(updated)], available: [...new Set(available)], failed: [...new Set(failed)] }, null, 2) };
1762
- if (lines.length === 0) lines.push(pc11.dim("No installed skills found."));
2318
+ if (lines.length === 0) lines.push(pc13.dim("No installed skills found."));
1763
2319
  return { updated, available, failed, exitCode, output: lines.join("\n") };
1764
2320
  }
1765
2321
  function scopeOptions(cmd) {
@@ -1767,14 +2323,16 @@ function scopeOptions(cmd) {
1767
2323
  }
1768
2324
  function updateCommand() {
1769
2325
  return scopeOptions(new Command10("update").description("Update installed skills from the source recorded in the lock file (re-lints before writing)").argument("[names...]", "specific skills (default: all tracked)").option("--check", "only report which skills have updates")).action(async (names, opts, cmd) => {
1770
- const run2 = await runUpdate(names, { ...cmd.parent?.opts(), ...opts });
2326
+ const flags = { ...cmd.parent?.opts(), ...opts };
2327
+ const run2 = await waitFor("walk", flags.check ? "Checking for updates" : "Updating skills", runUpdate(names, flags), { json: flags.json });
1771
2328
  console.log(run2.output);
1772
2329
  process.exitCode = run2.exitCode;
1773
2330
  });
1774
2331
  }
1775
2332
  function checkCommand() {
1776
2333
  return scopeOptions(new Command10("check").description("Report installed skills that have updates (alias of `update --check`)").argument("[names...]", "specific skills (default: all tracked)")).action(async (names, opts, cmd) => {
1777
- const run2 = await runUpdate(names, { ...cmd.parent?.opts(), ...opts, check: true });
2334
+ const flags = { ...cmd.parent?.opts(), ...opts, check: true };
2335
+ const run2 = await waitFor("walk", "Checking for updates", runUpdate(names, flags), { json: flags.json });
1778
2336
  console.log(run2.output);
1779
2337
  process.exitCode = run2.exitCode;
1780
2338
  });
@@ -1782,7 +2340,7 @@ function checkCommand() {
1782
2340
 
1783
2341
  // src/commands/publish.ts
1784
2342
  import { Command as Command11 } from "commander";
1785
- import pc12 from "picocolors";
2343
+ import pc14 from "picocolors";
1786
2344
  import { lint as lint4, toText } from "@skillmds/core";
1787
2345
  var defaultDeps2 = {
1788
2346
  resolve: resolveSource,
@@ -1806,25 +2364,25 @@ async function runPublish(target, flags, deps = defaultDeps2) {
1806
2364
  const report = toText([{ file: src.path, result }], { decorate });
1807
2365
  if (!result.ok) {
1808
2366
  return { result, published: false, exitCode: 1, output: `${report}
1809
- ${pc12.red("\u2717 Refusing to publish: fix the errors above.")}` };
2367
+ ${pc14.red("\u2717 Refusing to publish: fix the errors above.")}` };
1810
2368
  }
1811
2369
  const hasWarn = result.diagnostics.some((d) => d.severity === "warn");
1812
2370
  if (hasWarn && !flags.force) {
1813
2371
  return { result, published: false, exitCode: 1, output: `${report}
1814
- ${pc12.yellow("\u2717 Warnings present. Re-run with --force to publish anyway.")}` };
2372
+ ${pc14.yellow("\u2717 Warnings present. Re-run with --force to publish anyway.")}` };
1815
2373
  }
1816
2374
  if (flags.dryRun) {
1817
2375
  return { result, published: false, exitCode: 0, output: `${report}
1818
- ${pc12.green("\u2713 Dry run: would publish.")}` };
2376
+ ${pc14.green("\u2713 Dry run: would publish.")}` };
1819
2377
  }
1820
2378
  const res = await deps.submit({ content: src.raw, type: flags.type ?? "single" }, flags);
1821
2379
  return { result, published: true, slug: res.slug, exitCode: 0, output: `${report}
1822
- ${pc12.green(`\u2713 Published ${res.slug ?? src.slug} (status: ${res.status ?? "pending"})`)}` };
2380
+ ${pc14.green(`\u2713 Published ${res.slug ?? src.slug} (status: ${res.status ?? "pending"})`)}` };
1823
2381
  }
1824
2382
  function publishCommand() {
1825
2383
  return new Command11("publish").alias("submit").description("Publish a skill to the registry (refuses to publish on lint errors)").argument("[target]", "local path to the skill", ".").option("--type <type>", "single | pack", "single").option("--force", "publish despite warnings (never bypasses errors)").option("--dry-run", "lint only; do not publish").action(async (target, opts, cmd) => {
1826
2384
  const merged = { ...cmd.parent?.opts(), ...opts };
1827
- const run2 = await runPublish(target, merged);
2385
+ const run2 = await waitFor("walk", merged.dryRun ? "Checking" : "Publishing", runPublish(target, merged));
1828
2386
  console.log(run2.output);
1829
2387
  process.exitCode = run2.exitCode;
1830
2388
  });
@@ -1832,11 +2390,10 @@ function publishCommand() {
1832
2390
 
1833
2391
  // src/commands/login.ts
1834
2392
  import { Command as Command12 } from "commander";
1835
- import * as p5 from "@clack/prompts";
1836
- import pc13 from "picocolors";
2393
+ import pc15 from "picocolors";
1837
2394
  var defaultLoginDeps = (flags) => promptIf(flags, async () => {
1838
- const answer = await p5.password({ message: "Paste your SkillMD token (from your account page)" });
1839
- return p5.isCancel(answer) ? null : answer;
2395
+ const answer = await password({ message: "Paste your SkillMD token (from your account page)" });
2396
+ return isCancel(answer) ? null : answer;
1840
2397
  });
1841
2398
  function finish4(flags, result, doc) {
1842
2399
  return flags.json ? { ...result, output: JSON.stringify(doc, null, 2) } : result;
@@ -1847,15 +2404,15 @@ async function runLogin(flags, deps = defaultLoginDeps(flags)) {
1847
2404
  if (!token) {
1848
2405
  if (!deps.prompt) {
1849
2406
  const error = nonInteractiveHint("--token <token> (or set SKILLMD_TOKEN)");
1850
- return finish4(flags, { exitCode: 1, output: pc13.red(error) }, { ok: false, error });
2407
+ return finish4(flags, { exitCode: 1, output: pc15.red(error) }, { ok: false, error });
1851
2408
  }
1852
2409
  const answer = await deps.prompt();
1853
- if (answer === null) return finish4(flags, { exitCode: 0, output: pc13.dim("Cancelled.") }, { ok: false, cancelled: true });
2410
+ if (answer === null) return finish4(flags, { exitCode: 0, output: pc15.dim("Cancelled.") }, { ok: false, cancelled: true });
1854
2411
  token = answer;
1855
2412
  }
1856
2413
  if (!token) {
1857
2414
  const error = "A token is required.";
1858
- return finish4(flags, { exitCode: 1, output: pc13.red(error) }, { ok: false, error });
2415
+ return finish4(flags, { exitCode: 1, output: pc15.red(error) }, { ok: false, error });
1859
2416
  }
1860
2417
  const cfg = readConfig(flags.home);
1861
2418
  let host;
@@ -1863,7 +2420,7 @@ async function runLogin(flags, deps = defaultLoginDeps(flags)) {
1863
2420
  host = hostOf(resolveApi({ api: flags.api, insecureHttp: flags.insecureHttp }, { env, config: { ...cfg, api: flags.api ?? cfg.api } }));
1864
2421
  } catch (e) {
1865
2422
  const msg2 = e instanceof Error ? e.message : String(e);
1866
- return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc13.red(msg2) };
2423
+ return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc15.red(msg2) };
1867
2424
  }
1868
2425
  cfg.token = token;
1869
2426
  if (flags.api) cfg.api = flags.api;
@@ -1902,11 +2459,10 @@ function logoutCommand() {
1902
2459
  }
1903
2460
 
1904
2461
  // src/commands/interactive.ts
1905
- import * as p6 from "@clack/prompts";
1906
- import pc14 from "picocolors";
2462
+ import pc16 from "picocolors";
1907
2463
  function bail(v) {
1908
- if (p6.isCancel(v)) {
1909
- p6.cancel("Cancelled.");
2464
+ if (isCancel(v)) {
2465
+ cancel("Cancelled.");
1910
2466
  return true;
1911
2467
  }
1912
2468
  return false;
@@ -1914,7 +2470,7 @@ function bail(v) {
1914
2470
  async function browseSearchResults(items) {
1915
2471
  const cols = process.stdout.columns ?? 80;
1916
2472
  for (; ; ) {
1917
- const picked = await p6.select({
2473
+ const picked = await select({
1918
2474
  message: "Search results",
1919
2475
  options: [
1920
2476
  ...items.map((item2) => {
@@ -1922,7 +2478,7 @@ async function browseSearchResults(items) {
1922
2478
  const checkTxt = item2.verified ? " \u2713" : "";
1923
2479
  const slugTxt = safeText(item2.slug, 200);
1924
2480
  const plainLabel = `${slugTxt}${starsTxt}${checkTxt}`;
1925
- const label2 = `${pc14.bold(slugTxt)}${starsTxt ? pc14.dim(starsTxt) : ""}${checkTxt ? pc14.green(checkTxt) : ""}`;
2481
+ const label2 = `${pc16.bold(slugTxt)}${starsTxt ? pc16.dim(starsTxt) : ""}${checkTxt ? pc16.green(checkTxt) : ""}`;
1926
2482
  const hintWidth = Math.max(10, cols - plainLabel.length - 6);
1927
2483
  return {
1928
2484
  value: item2.slug,
@@ -1933,7 +2489,7 @@ async function browseSearchResults(items) {
1933
2489
  { value: "__back", label: "\u2190 Back to menu" }
1934
2490
  ]
1935
2491
  });
1936
- if (p6.isCancel(picked) || picked === "__back") return;
2492
+ if (isCancel(picked) || picked === "__back") return;
1937
2493
  const item = items.find((i) => i.slug === picked);
1938
2494
  if (!item) continue;
1939
2495
  await browseOneResult(item);
@@ -1944,12 +2500,12 @@ async function browseOneResult(item) {
1944
2500
  const slugTxt = safeText(item.slug, 200);
1945
2501
  const meta = [
1946
2502
  starsTxt,
1947
- item.verified ? pc14.green("\u2713 verified") : "unverified",
2503
+ item.verified ? pc16.green("\u2713 verified") : "unverified",
1948
2504
  item.type === "pack" ? "pack" : ""
1949
2505
  ].filter(Boolean).join(" \xB7 ");
1950
- p6.note([safeText(item.description ?? ""), pc14.dim(meta)].join("\n"), slugTxt);
2506
+ note([safeText(item.description ?? ""), pc16.dim(meta)].join("\n"), slugTxt);
1951
2507
  for (; ; ) {
1952
- const action = await p6.select({
2508
+ const action = await select({
1953
2509
  message: "What next?",
1954
2510
  options: [
1955
2511
  { value: "project", label: "Install to this project" },
@@ -1958,26 +2514,31 @@ async function browseOneResult(item) {
1958
2514
  { value: "back", label: "\u2190 Back to results" }
1959
2515
  ]
1960
2516
  });
1961
- if (p6.isCancel(action) || action === "back") return;
2517
+ if (isCancel(action) || action === "back") return;
1962
2518
  if (action === "url") {
1963
- p6.log.message(pc14.dim(`https://skillmd.com/skills/${slugTxt}`));
2519
+ log.message(pc16.dim(`https://skillmd.com/skills/${slugTxt}`));
1964
2520
  continue;
1965
2521
  }
1966
- p6.log.step(`Installing ${slugTxt}\u2026`);
2522
+ log.step(`Installing ${slugTxt}\u2026`);
1967
2523
  try {
1968
2524
  const run2 = await runAdd(item.slug, action === "global" ? { global: true } : { project: true });
1969
2525
  console.log(run2.output);
1970
- if (run2.exitCode === 0) p6.log.success("Installed.");
1971
- else p6.log.warn("Not installed.");
2526
+ if (run2.exitCode === 0) log.success("Installed.");
2527
+ else log.warn("Not installed.");
1972
2528
  } catch (err) {
1973
- p6.log.error(err instanceof Error ? err.message : String(err));
2529
+ log.error(err instanceof Error ? err.message : String(err));
1974
2530
  }
1975
2531
  }
1976
2532
  }
1977
- async function runInteractive() {
1978
- p6.intro(banner());
2533
+ async function runInteractive(opts = {}) {
2534
+ console.log();
2535
+ await playLogo();
2536
+ console.log(`
2537
+ ${pc16.dim("Teach your agent anything.")}${opts.version ? ` ${pc16.dim(`v${opts.version}`)}` : ""}
2538
+ `);
2539
+ startFrameAfterLogo();
1979
2540
  for (; ; ) {
1980
- const action = await p6.select({
2541
+ const action = await select({
1981
2542
  message: "What would you like to do?",
1982
2543
  options: [
1983
2544
  { value: "lint", label: "Lint a skill", hint: "validate a SKILL.md" },
@@ -1996,66 +2557,66 @@ async function runInteractive() {
1996
2557
  switch (action) {
1997
2558
  case "lint":
1998
2559
  case "scan": {
1999
- const path = await p6.text({ message: "Path to a skill or folder", placeholder: ".", defaultValue: "." });
2560
+ const path = await text({ message: "Path to a skill or folder", placeholder: ".", defaultValue: "." });
2000
2561
  if (bail(path)) break;
2001
2562
  const target = String(path || ".");
2002
- const s = p6.spinner();
2003
- s.start(action === "lint" ? "Linting" : "Scanning");
2563
+ const s2 = spinner("walk");
2564
+ s2.start(action === "lint" ? "Linting" : "Scanning");
2004
2565
  try {
2005
2566
  const run2 = action === "lint" ? await runLint(target, { format: "text" }) : await runScan(target, { format: "text" });
2006
- s.stop("Done");
2567
+ s2.stop("Done");
2007
2568
  console.log(run2.output);
2008
2569
  process.exitCode = run2.exitCode;
2009
2570
  } catch (err) {
2010
- s.stop(pc14.red(err instanceof Error ? err.message : String(err)));
2571
+ s2.stop(pc16.red(err instanceof Error ? err.message : String(err)));
2011
2572
  process.exitCode = 1;
2012
2573
  }
2013
2574
  break;
2014
2575
  }
2015
2576
  case "search": {
2016
- const query = await p6.text({ message: "Search the registry for" });
2577
+ const query = await text({ message: "Search the registry for" });
2017
2578
  if (bail(query)) break;
2018
- const verified = await p6.confirm({ message: "Verified skills only?", initialValue: false });
2579
+ const verified = await confirm({ message: "Verified skills only?", initialValue: false });
2019
2580
  if (bail(verified)) break;
2020
- const s = p6.spinner();
2021
- s.start("Searching");
2581
+ const s2 = spinner("hop");
2582
+ s2.start(`Searching for ${safeText(String(query))}`);
2022
2583
  try {
2023
2584
  const run2 = await runSearch(String(query), { verified: Boolean(verified), limit: "20" });
2024
- s.stop(`${run2.items.length} result${run2.items.length === 1 ? "" : "s"}`);
2585
+ s2.stop(`${run2.items.length} result${run2.items.length === 1 ? "" : "s"}`);
2025
2586
  if (run2.items.length === 0) {
2026
- p6.log.info("No results.");
2587
+ log.info("No results.");
2027
2588
  break;
2028
2589
  }
2029
2590
  await browseSearchResults(run2.items);
2030
2591
  } catch (err) {
2031
- s.stop(pc14.red(err instanceof Error ? err.message : String(err)));
2592
+ s2.stop(pc16.red(err instanceof Error ? err.message : String(err)));
2032
2593
  process.exitCode = 1;
2033
2594
  }
2034
2595
  break;
2035
2596
  }
2036
2597
  case "init": {
2037
- const name = await p6.text({ message: "Skill name", placeholder: "my-skill" });
2598
+ const name = await text({ message: "Skill name", placeholder: "my-skill" });
2038
2599
  if (bail(name)) break;
2039
- const description = await p6.text({ message: "One-line description", placeholder: "What this skill helps an agent do" });
2600
+ const description = await text({ message: "One-line description", placeholder: "What this skill helps an agent do" });
2040
2601
  if (bail(description)) break;
2041
2602
  try {
2042
2603
  const file = initSkill(String(name), { description: String(description || "") });
2043
- p6.log.success(`Created ${pc14.bold(file)}`);
2604
+ log.success(`Created ${pc16.bold(file)}`);
2044
2605
  } catch (err) {
2045
- p6.log.error(err instanceof Error ? err.message : String(err));
2606
+ log.error(err instanceof Error ? err.message : String(err));
2046
2607
  process.exitCode = 1;
2047
2608
  }
2048
2609
  break;
2049
2610
  }
2050
2611
  case "install": {
2051
- const src = await p6.text({ message: "Skill source", placeholder: "owner/name, https://github.com/\u2026, or ./path" });
2612
+ const src = await text({ message: "Skill source", placeholder: "owner/name, https://github.com/\u2026, or ./path" });
2052
2613
  if (bail(src)) break;
2053
2614
  try {
2054
2615
  const run2 = await runAdd(String(src), {});
2055
2616
  console.log(run2.output);
2056
2617
  process.exitCode = run2.exitCode;
2057
2618
  } catch (err) {
2058
- p6.log.error(err instanceof Error ? err.message : String(err));
2619
+ log.error(err instanceof Error ? err.message : String(err));
2059
2620
  process.exitCode = 1;
2060
2621
  }
2061
2622
  break;
@@ -2065,12 +2626,16 @@ async function runInteractive() {
2065
2626
  break;
2066
2627
  }
2067
2628
  case "update": {
2629
+ const s2 = spinner("walk");
2630
+ s2.start("Updating installed skills");
2068
2631
  try {
2069
2632
  const run2 = await runUpdate([], {});
2633
+ const n = new Set(run2.updated).size;
2634
+ s2.stop(n ? `Updated ${n} skill${n === 1 ? "" : "s"}` : "Done", run2.exitCode === 0 ? 0 : 2);
2070
2635
  console.log(run2.output);
2071
2636
  process.exitCode = run2.exitCode;
2072
2637
  } catch (err) {
2073
- p6.log.error(err instanceof Error ? err.message : String(err));
2638
+ s2.stop(pc16.red(err instanceof Error ? err.message : String(err)), 2);
2074
2639
  process.exitCode = 1;
2075
2640
  }
2076
2641
  break;
@@ -2081,23 +2646,23 @@ async function runInteractive() {
2081
2646
  console.log(run2.output);
2082
2647
  process.exitCode = run2.exitCode;
2083
2648
  } catch (err) {
2084
- p6.log.error(err instanceof Error ? err.message : String(err));
2649
+ log.error(err instanceof Error ? err.message : String(err));
2085
2650
  process.exitCode = 1;
2086
2651
  }
2087
2652
  break;
2088
2653
  }
2089
2654
  case "help": {
2090
- p6.note(
2655
+ note(
2091
2656
  [
2092
- `${pc14.bold("lint")} [path] validate SKILL.md + quality score`,
2093
- `${pc14.bold("scan")} [path] security scan`,
2094
- `${pc14.bold("search")} <query> search the registry`,
2095
- `${pc14.bold("add")} <source> install a skill (lints first)`,
2096
- `${pc14.bold("info")} <slug> registry details for a skill`,
2097
- `${pc14.bold("init")} [name] scaffold a new skill`,
2098
- `${pc14.bold("list / remove / update")} manage installed skills`,
2099
- `${pc14.bold("publish")} [path] publish (fails on lint errors)`,
2100
- `${pc14.bold("rules / login")}`
2657
+ `${pc16.bold("lint")} [path] validate SKILL.md + quality score`,
2658
+ `${pc16.bold("scan")} [path] security scan`,
2659
+ `${pc16.bold("search")} <query> search the registry`,
2660
+ `${pc16.bold("add")} <source> install a skill (lints first)`,
2661
+ `${pc16.bold("info")} <slug> registry details for a skill`,
2662
+ `${pc16.bold("init")} [name] scaffold a new skill`,
2663
+ `${pc16.bold("list / remove / update")} manage installed skills`,
2664
+ `${pc16.bold("publish")} [path] publish (fails on lint errors)`,
2665
+ `${pc16.bold("rules / login")}`
2101
2666
  ].join("\n"),
2102
2667
  "Commands (run: skillmd <command>)"
2103
2668
  );
@@ -2105,7 +2670,7 @@ async function runInteractive() {
2105
2670
  }
2106
2671
  }
2107
2672
  }
2108
- p6.outro(pc14.dim("skillmd.com"));
2673
+ outro(pc16.dim("skillmd.com"));
2109
2674
  }
2110
2675
 
2111
2676
  // src/index.ts
@@ -2135,7 +2700,7 @@ Docs: https://skillmd.com/docs/cli`);
2135
2700
  program.hook("preAction", (thisCmd) => {
2136
2701
  const opts = thisCmd.opts();
2137
2702
  if (opts.token && process.stderr.isTTY) {
2138
- console.error(pc15.yellow("\u26A0 --token is visible in your shell history and process list; prefer `npx get-skill login` or SKILLMD_TOKEN."));
2703
+ console.error(pc17.yellow("\u26A0 --token is visible in your shell history and process list; prefer `npx get-skill login` or SKILLMD_TOKEN."));
2139
2704
  }
2140
2705
  });
2141
2706
  program.addCommand(lintCommand());
@@ -2154,7 +2719,7 @@ program.addCommand(loginCommand());
2154
2719
  program.addCommand(logoutCommand());
2155
2720
  if (process.argv.slice(2).length === 0) {
2156
2721
  if (process.stdout.isTTY) {
2157
- await runInteractive();
2722
+ await runInteractive({ version });
2158
2723
  process.exit(process.exitCode ?? 0);
2159
2724
  }
2160
2725
  program.outputHelp();