skillmds 1.3.7 → 1.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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"];
@@ -920,19 +1469,20 @@ async function registryCandidate(owner, name, source, client) {
920
1469
  type: skill.type,
921
1470
  securityFlags: skill.security_flags
922
1471
  };
1472
+ const missing = [];
1473
+ const bundle = await fetchBundle(client.base, registrySlug, client.token, { missing });
1474
+ if (bundle && bundle.length) {
1475
+ const sk = bundle.find((file) => file.path === "SKILL.md");
1476
+ const note2 = bundleNote(bundle, missing);
1477
+ return {
1478
+ ...meta,
1479
+ type: bundle.length > 1 ? "pack" : meta.type,
1480
+ raw: sk ? sk.contents.toString("utf8") : skillMdFor(skill),
1481
+ files: bundle,
1482
+ ...note2 ? { note: note2 } : {}
1483
+ };
1484
+ }
923
1485
  if (skill.type === "pack") {
924
- const missing = [];
925
- const bundle = await fetchBundle(client.base, registrySlug, client.token, { missing });
926
- if (bundle && bundle.length) {
927
- const sk = bundle.find((file) => file.path === "SKILL.md");
928
- const note2 = bundleNote(bundle, missing);
929
- return {
930
- ...meta,
931
- raw: sk ? sk.contents.toString("utf8") : skillMdFor(skill),
932
- files: bundle,
933
- ...note2 ? { note: note2 } : {}
934
- };
935
- }
936
1486
  const pack = await resolvePackFiles(skill);
937
1487
  if (pack) return { ...meta, ...pack };
938
1488
  }
@@ -1143,7 +1693,7 @@ function finish2(flags, outcome) {
1143
1693
  };
1144
1694
  }
1145
1695
  case "list": {
1146
- 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.";
1147
1697
  return { written: [], blocked: [], exitCode: 0, output: json ? JSON.stringify({ ok: true, skills: outcome.skills }, null, 2) : human };
1148
1698
  }
1149
1699
  case "cancel": {
@@ -1153,7 +1703,7 @@ function finish2(flags, outcome) {
1153
1703
  blocked: [],
1154
1704
  exitCode: 0,
1155
1705
  cancelled: true,
1156
- 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.")
1157
1707
  };
1158
1708
  }
1159
1709
  case "fail": {
@@ -1163,7 +1713,7 @@ function finish2(flags, outcome) {
1163
1713
  if (warnings.length) doc.warnings = warnings;
1164
1714
  const human = [
1165
1715
  ...(outcome.notices ?? []).map(noticeLine),
1166
- ...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)]
1167
1717
  ].join("\n");
1168
1718
  return { written: [], blocked: outcome.blocked ?? [], exitCode: 1, output: json ? JSON.stringify(doc, null, 2) : human };
1169
1719
  }
@@ -1173,7 +1723,7 @@ function renderHuman(notices, records, paths) {
1173
1723
  const lines = notices.map(noticeLine);
1174
1724
  for (const r of records) {
1175
1725
  if (r.kind === "blocked") {
1176
- lines.push(pc8.red(r.message));
1726
+ lines.push(pc10.red(r.message));
1177
1727
  continue;
1178
1728
  }
1179
1729
  const { doc } = r;
@@ -1385,14 +1935,14 @@ function addCommand() {
1385
1935
  });
1386
1936
  }
1387
1937
  var clackUI = {
1388
- intro: (m) => p3.intro(m),
1389
- spinner: () => p3.spinner(),
1390
- message: (m) => p3.log.message(m),
1391
- outro: (m) => p3.outro(m),
1392
- 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),
1393
1943
  confirm: async (m) => {
1394
- const answer = await p3.confirm({ message: m, initialValue: true });
1395
- return p3.isCancel(answer) ? null : answer;
1944
+ const answer = await confirm({ message: m, initialValue: true });
1945
+ return isCancel(answer) ? null : answer;
1396
1946
  }
1397
1947
  };
1398
1948
  var FIND_SKILLS = "skillmd/find-skills";
@@ -1421,7 +1971,7 @@ async function offerFindSkills(source, flags, run2, ui, deps) {
1421
1971
  project: !global,
1422
1972
  agent: run2.agents
1423
1973
  }, deps);
1424
- 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}`));
1974
+ ui.message(r.exitCode === 0 ? `${pc10.green(GLYPH.ok)} find-skills added` : pc10.yellow(`${GLYPH.warn} find-skills could not be added. Try later: npx get-skill add ${FIND_SKILLS}`));
1425
1975
  }
1426
1976
  async function runAddWithUI(source, flags, opts = {}) {
1427
1977
  const term = opts.term ?? { ...realTerminal(), env: flags.env ?? process.env };
@@ -1438,14 +1988,14 @@ async function runAddWithUI(source, flags, opts = {}) {
1438
1988
  // this spread.
1439
1989
  ...base,
1440
1990
  resolve: async (arg, f) => {
1441
- const s = ui.spinner();
1442
- s.start(`Resolving ${arg}`);
1991
+ const s2 = ui.spinner();
1992
+ s2.start(`Resolving ${arg}`);
1443
1993
  try {
1444
1994
  const c = await base.resolve(arg, f);
1445
- s.stop(`${c.length} skill${c.length === 1 ? "" : "s"} found`);
1995
+ s2.stop(`${c.length} skill${c.length === 1 ? "" : "s"} found`);
1446
1996
  return c;
1447
1997
  } catch (e) {
1448
- s.stop(pc8.red("Could not resolve the source"));
1998
+ s2.stop(pc10.red("Could not resolve the source"));
1449
1999
  throw e;
1450
2000
  }
1451
2001
  }
@@ -1456,10 +2006,10 @@ async function runAddWithUI(source, flags, opts = {}) {
1456
2006
  }
1457
2007
  ui.message(run2.output);
1458
2008
  await offerFindSkills(source, flags, run2, ui, base);
1459
- ui.outro(run2.exitCode === 0 ? `Done. Run ${pc8.cyan("npx get-skill list")} to see everything installed.` : pc8.red("Some skills were not installed."));
2009
+ ui.outro(run2.exitCode === 0 ? `Done. Run ${pc10.cyan("npx get-skill list")} to see everything installed.` : pc10.red("Some skills were not installed."));
1460
2010
  return { run: run2, printed: true };
1461
2011
  } catch (e) {
1462
- ui.outro(pc8.red("Nothing was installed."));
2012
+ ui.outro(pc10.red("Nothing was installed."));
1463
2013
  throw e;
1464
2014
  }
1465
2015
  }
@@ -1468,20 +2018,20 @@ async function runAddWithUI(source, flags, opts = {}) {
1468
2018
  import { Command as Command8 } from "commander";
1469
2019
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
1470
2020
  import { join as join4 } from "path";
1471
- import pc9 from "picocolors";
2021
+ import pc11 from "picocolors";
1472
2022
  import { parseSkillMd as parseSkillMd3 } from "@skillmds/core";
1473
2023
  function runList(flags = {}) {
1474
2024
  const scopes = scopesFor(flags);
1475
2025
  let items = [];
1476
2026
  for (const global of scopes) {
1477
- for (const s of listInstalled({ global, cwd: flags.cwd, home: flags.home })) {
1478
- const md = join4(s.path, "SKILL.md");
2027
+ for (const s2 of listInstalled({ global, cwd: flags.cwd, home: flags.home })) {
2028
+ const md = join4(s2.path, "SKILL.md");
1479
2029
  let description = "";
1480
2030
  if (existsSync3(md)) {
1481
2031
  const parsed = parseSkillMd3(readFileSync2(md, "utf8"));
1482
2032
  description = "error" in parsed ? `(invalid SKILL.md \u2014 ${parsed.error})` : parsed.description;
1483
2033
  }
1484
- items.push({ ...s, description: safeText(description, 80) });
2034
+ items.push({ ...s2, description: safeText(description, 80) });
1485
2035
  }
1486
2036
  }
1487
2037
  if (flags.agent?.length) items = items.filter((i) => i.agents.some((a) => flags.agent.includes(a)));
@@ -1489,13 +2039,13 @@ function runList(flags = {}) {
1489
2039
  if (flags.json) {
1490
2040
  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) };
1491
2041
  }
1492
- if (items.length === 0) return { items, output: pc9.dim("No installed skills found. Install one with `npx get-skill add owner/name`.") };
2042
+ if (items.length === 0) return { items, output: pc11.dim("No installed skills found. Install one with `npx get-skill add owner/name`.") };
1493
2043
  const w = Math.max(...items.map((i) => i.name.length), 4);
1494
2044
  const lines = items.map((i) => {
1495
- const tag = i.tracked ? pc9.dim(i.source ?? "") : pc9.yellow("untracked");
2045
+ const tag = i.tracked ? pc11.dim(i.source ?? "") : pc11.yellow("untracked");
1496
2046
  const modes = [...new Set(Object.values(i.mode))].join("/");
1497
- return `${pc9.bold(i.name.padEnd(w))} ${pc9.cyan(i.scope.padEnd(7))} ${i.agents.join(", ").padEnd(24)} ${pc9.dim(modes.padEnd(9))} ${tag}
1498
- ${" ".repeat(w + 2)}${pc9.dim(i.description)}`;
2047
+ return `${pc11.bold(i.name.padEnd(w))} ${pc11.cyan(i.scope.padEnd(7))} ${i.agents.join(", ").padEnd(24)} ${pc11.dim(modes.padEnd(9))} ${tag}
2048
+ ${" ".repeat(w + 2)}${pc11.dim(i.description)}`;
1499
2049
  });
1500
2050
  return { items, output: lines.join("\n") };
1501
2051
  }
@@ -1509,16 +2059,15 @@ function listCommand() {
1509
2059
  import { Command as Command9 } from "commander";
1510
2060
  import { rmSync } from "fs";
1511
2061
  import { join as join5, resolve as resolve4 } from "path";
1512
- import * as p4 from "@clack/prompts";
1513
- import pc10 from "picocolors";
2062
+ import pc12 from "picocolors";
1514
2063
  var defaultDeps = (flags) => isInteractive(flags) ? {
1515
2064
  confirm: async (msg2) => {
1516
- const a = await p4.confirm({ message: msg2, initialValue: false });
1517
- return !p4.isCancel(a) && a;
2065
+ const a = await confirm({ message: msg2, initialValue: false });
2066
+ return !isCancel(a) && a;
1518
2067
  },
1519
2068
  pick: async ({ names }) => {
1520
- const a = await p4.multiselect({ message: "Which skills do you want to remove?", options: names.map((n) => ({ value: n, label: n })), required: true });
1521
- return p4.isCancel(a) ? null : a;
2069
+ const a = await multiselect({ message: "Which skills do you want to remove?", options: names.map((n) => ({ value: n, label: n })), required: true });
2070
+ return isCancel(a) ? null : a;
1522
2071
  }
1523
2072
  } : {};
1524
2073
  function finish3(flags, result, doc) {
@@ -1531,32 +2080,32 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1531
2080
  const lines = [];
1532
2081
  if (flags.all && names.length) {
1533
2082
  const error = "--all cannot be combined with skill names";
1534
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.red(error) }, { ok: false, error });
2083
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.red(error) }, { ok: false, error });
1535
2084
  }
1536
2085
  const perScope = scopes.map((scope) => ({ scope, installed: listInstalled(scope) }));
1537
- const allNames = [...new Set(perScope.flatMap((s) => s.installed.map((i) => i.name)))];
2086
+ const allNames = [...new Set(perScope.flatMap((s2) => s2.installed.map((i) => i.name)))];
1538
2087
  const nothingInstalled = `No installed skills found in ${where} scope.`;
1539
2088
  let targets = names;
1540
2089
  if (flags.all) {
1541
2090
  if (allNames.length === 0) {
1542
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
2091
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
1543
2092
  }
1544
2093
  targets = allNames;
1545
2094
  } else if (targets.length === 0) {
1546
2095
  if (!deps.pick) {
1547
2096
  const error = "Give one or more skill names, or --all.";
1548
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.red(`${error} `) + pc10.dim(nonInteractiveHint("skill names and -y")) }, { ok: false, error });
2097
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.red(`${error} `) + pc12.dim(nonInteractiveHint("skill names and -y")) }, { ok: false, error });
1549
2098
  }
1550
2099
  if (allNames.length === 0) {
1551
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
2100
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.dim(nothingInstalled) }, { ok: false, error: nothingInstalled });
1552
2101
  }
1553
2102
  const picked = await deps.pick({ names: allNames });
1554
- if (!picked) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc10.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
2103
+ if (!picked) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc12.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
1555
2104
  targets = picked;
1556
2105
  }
1557
2106
  const known = targets.filter((n) => allNames.includes(n));
1558
2107
  const missing = targets.filter((n) => !known.includes(n));
1559
- for (const n of missing) lines.push(pc10.yellow(`- ${n} not installed (${where} scope)`));
2108
+ for (const n of missing) lines.push(pc12.yellow(`- ${n} not installed (${where} scope)`));
1560
2109
  if (known.length === 0) {
1561
2110
  const output = lines.join("\n");
1562
2111
  return finish3(flags, { removed: [], exitCode: 1, output }, { ok: false, error: `not installed: ${missing.join(", ")}`, missing });
@@ -1566,17 +2115,17 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1566
2115
  const ok = deps.confirm ? await deps.confirm(`Remove ${known.length} skill${known.length === 1 ? "" : "s"} (${known.join(", ")}) ${what} in ${where} scope?`) : null;
1567
2116
  if (ok === null) {
1568
2117
  const error = nonInteractiveHint("-y");
1569
- return finish3(flags, { removed: [], exitCode: 1, output: pc10.red(error) }, { ok: false, error });
2118
+ return finish3(flags, { removed: [], exitCode: 1, output: pc12.red(error) }, { ok: false, error });
1570
2119
  }
1571
- if (!ok) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc10.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
2120
+ if (!ok) return finish3(flags, { removed: [], exitCode: 0, cancelled: true, output: pc12.dim("Nothing removed.") }, { ok: true, removed: [], cancelled: true });
1572
2121
  }
1573
2122
  const removed = [];
1574
2123
  const skipped = [];
1575
2124
  let changed = 0;
1576
2125
  for (const name of known) {
1577
2126
  for (const { scope, installed } of perScope) {
1578
- if (!installed.some((s) => s.name === name)) continue;
1579
- const tag = scopes.length > 1 ? pc10.dim(` [${scopeLabel(scope)}]`) : "";
2127
+ if (!installed.some((s2) => s2.name === name)) continue;
2128
+ const tag = scopes.length > 1 ? pc12.dim(` [${scopeLabel(scope)}]`) : "";
1580
2129
  if (flags.agent?.length) {
1581
2130
  const lock = readLock({ global: Boolean(scope.global), cwd: scope.cwd, home: scope.home });
1582
2131
  const entry = lock.skills[name];
@@ -1587,11 +2136,11 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1587
2136
  try {
1588
2137
  dir = agentDir(a, scope);
1589
2138
  } catch (e) {
1590
- lines.push(pc10.yellow(`- ${a}: ${e instanceof Error ? e.message : String(e)}`));
2139
+ lines.push(pc12.yellow(`- ${a}: ${e instanceof Error ? e.message : String(e)}`));
1591
2140
  continue;
1592
2141
  }
1593
2142
  if (resolve4(dir) === canonRoot || entry?.mode[a] === "canonical") {
1594
- 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)`));
2143
+ 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)`));
1595
2144
  skipped.push({ skill: name, agent: a, scope: scopeLabel(scope), reason: "reads the canonical copy directly" });
1596
2145
  acted = true;
1597
2146
  continue;
@@ -1601,17 +2150,17 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1601
2150
  rmSync(pth, { recursive: true, force: true });
1602
2151
  removed.push(pth);
1603
2152
  acted = true;
1604
- lines.push(pc10.green(`${GLYPH.ok} removed ${name} from ${a}`) + tag);
2153
+ lines.push(pc12.green(`${GLYPH.ok} removed ${name} from ${a}`) + tag);
1605
2154
  }
1606
2155
  }
1607
- if (!acted) lines.push(pc10.yellow(`- ${name} is not linked into ${flags.agent.join(", ")}`) + tag);
2156
+ if (!acted) lines.push(pc12.yellow(`- ${name} is not linked into ${flags.agent.join(", ")}`) + tag);
1608
2157
  if (entry) {
1609
2158
  const left = entry.agents.filter((a) => !flags.agent.includes(a));
1610
2159
  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))) });
1611
2160
  else {
1612
2161
  const r2 = uninstallSkill(name, scope);
1613
2162
  removed.push(...r2.removed);
1614
- lines.push(pc10.dim(` (no agents left \u2014 removed the canonical copy too)`));
2163
+ lines.push(pc12.dim(` (no agents left \u2014 removed the canonical copy too)`));
1615
2164
  }
1616
2165
  }
1617
2166
  if (acted) changed++;
@@ -1620,7 +2169,7 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
1620
2169
  const r = uninstallSkill(name, scope);
1621
2170
  removed.push(...r.removed);
1622
2171
  changed++;
1623
- lines.push(pc10.green(`${GLYPH.ok} removed ${name}`) + tag + pc10.dim(` (${r.removed.length} path${r.removed.length === 1 ? "" : "s"}${r.untracked ? ", untracked" : ""})`));
2172
+ lines.push(pc12.green(`${GLYPH.ok} removed ${name}`) + tag + pc12.dim(` (${r.removed.length} path${r.removed.length === 1 ? "" : "s"}${r.untracked ? ", untracked" : ""})`));
1624
2173
  }
1625
2174
  }
1626
2175
  const exitCode = changed ? 0 : 1;
@@ -1638,7 +2187,7 @@ function removeCommand() {
1638
2187
 
1639
2188
  // src/commands/update.ts
1640
2189
  import { Command as Command10 } from "commander";
1641
- import pc11 from "picocolors";
2190
+ import pc13 from "picocolors";
1642
2191
  import { lint as lint3 } from "@skillmds/core";
1643
2192
  function pickSkillTree(files, want) {
1644
2193
  const norm = files.map((f) => ({ path: f.path.replace(/\\/g, "/"), contents: f.contents }));
@@ -1666,9 +2215,9 @@ function makeDefaultUpdateDeps(opts = {}) {
1666
2215
  if (e.status === 404) return null;
1667
2216
  throw e;
1668
2217
  }
2218
+ const bundle = await fetchBundle(base, slug, token);
2219
+ if (bundle?.length) return { files: bundle, commit_sha: skill.commit_sha ?? void 0 };
1669
2220
  if (skill.type === "pack") {
1670
- const bundle = await fetchBundle(base, slug, token);
1671
- if (bundle?.length) return { files: bundle, commit_sha: skill.commit_sha ?? void 0 };
1672
2221
  const pack = await resolvePackFiles(skill);
1673
2222
  if (pack) return { files: pack.files, commit_sha: skill.commit_sha ?? void 0 };
1674
2223
  }
@@ -1701,64 +2250,64 @@ async function runUpdate(names, flags, deps = defaultUpdateDeps) {
1701
2250
  for (const global of scopes) {
1702
2251
  const scope = { global, cwd: flags.cwd, home: flags.home };
1703
2252
  const lock = readLock(scope);
1704
- const installed = listInstalled(scope).filter((s) => names.length === 0 || names.includes(s.name));
1705
- for (const s of installed) {
1706
- seen.add(s.name);
1707
- const label2 = `${s.name}${scopes.length > 1 ? pc11.dim(` [${s.scope}]`) : ""}`;
1708
- const entry = lock.skills[s.name];
2253
+ const installed = listInstalled(scope).filter((s2) => names.length === 0 || names.includes(s2.name));
2254
+ for (const s2 of installed) {
2255
+ seen.add(s2.name);
2256
+ const label2 = `${s2.name}${scopes.length > 1 ? pc13.dim(` [${s2.scope}]`) : ""}`;
2257
+ const entry = lock.skills[s2.name];
1709
2258
  if (!entry) {
1710
- lines.push(pc11.dim(`- ${label2} untracked \u2014 reinstall with \`npx get-skill add\` to track updates`));
2259
+ lines.push(pc13.dim(`- ${label2} untracked \u2014 reinstall with \`npx get-skill add\` to track updates`));
1711
2260
  continue;
1712
2261
  }
1713
2262
  if (entry.source.startsWith("local:")) {
1714
- lines.push(pc11.dim(`- ${label2} local source \u2014 not auto-updated`));
2263
+ lines.push(pc13.dim(`- ${label2} local source \u2014 not auto-updated`));
1715
2264
  continue;
1716
2265
  }
1717
2266
  try {
1718
- const latest = await deps.fetchLatest(entry.source, flags, s.name);
2267
+ const latest = await deps.fetchLatest(entry.source, flags, s2.name);
1719
2268
  if (!latest) {
1720
- failed.push(s.name);
1721
- lines.push(pc11.red(`${GLYPH.blocked} ${label2} no longer available upstream (${entry.source})`));
2269
+ failed.push(s2.name);
2270
+ lines.push(pc13.red(`${GLYPH.blocked} ${label2} no longer available upstream (${entry.source})`));
1722
2271
  continue;
1723
2272
  }
1724
2273
  if (entry.source.startsWith("registry:") && latest.commit_sha && entry.commit_sha && latest.commit_sha === entry.commit_sha) {
1725
- lines.push(pc11.dim(`- ${label2} up to date`));
2274
+ lines.push(pc13.dim(`- ${label2} up to date`));
1726
2275
  continue;
1727
2276
  }
1728
2277
  if (digestOf(latest.files) === entry.digest) {
1729
- lines.push(pc11.dim(`- ${label2} up to date`));
2278
+ lines.push(pc13.dim(`- ${label2} up to date`));
1730
2279
  continue;
1731
2280
  }
1732
2281
  if (flags.check) {
1733
- available.push(s.name);
1734
- lines.push(pc11.yellow(`\u2191 ${label2} update available`));
2282
+ available.push(s2.name);
2283
+ lines.push(pc13.yellow(`\u2191 ${label2} update available`));
1735
2284
  continue;
1736
2285
  }
1737
2286
  const md = latest.files.find((f) => f.path === "SKILL.md");
1738
2287
  const raw = md ? typeof md.contents === "string" ? md.contents : md.contents.toString("utf8") : "";
1739
- const result = lint3(raw, { slug: s.name });
2288
+ const result = lint3(raw, { slug: s2.name });
1740
2289
  if (!result.ok) {
1741
- failed.push(s.name);
1742
- 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`));
2290
+ failed.push(s2.name);
2291
+ 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`));
1743
2292
  continue;
1744
2293
  }
1745
- 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" });
1746
- updated.push(s.name);
1747
- lines.push(pc11.green(`${GLYPH.ok} ${label2} updated`));
2294
+ 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" });
2295
+ updated.push(s2.name);
2296
+ lines.push(pc13.green(`${GLYPH.ok} ${label2} updated`));
1748
2297
  } catch (e) {
1749
- failed.push(s.name);
1750
- lines.push(pc11.red(`${GLYPH.blocked} ${label2} ${e instanceof Error ? e.message : String(e)}`));
2298
+ failed.push(s2.name);
2299
+ lines.push(pc13.red(`${GLYPH.blocked} ${label2} ${e instanceof Error ? e.message : String(e)}`));
1751
2300
  }
1752
2301
  }
1753
2302
  }
1754
2303
  for (const n of names) {
1755
2304
  if (seen.has(n)) continue;
1756
2305
  failed.push(n);
1757
- lines.push(pc11.yellow(`- ${n} not installed`));
2306
+ lines.push(pc13.yellow(`- ${n} not installed`));
1758
2307
  }
1759
2308
  const exitCode = failed.length ? 1 : 0;
1760
2309
  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) };
1761
- if (lines.length === 0) lines.push(pc11.dim("No installed skills found."));
2310
+ if (lines.length === 0) lines.push(pc13.dim("No installed skills found."));
1762
2311
  return { updated, available, failed, exitCode, output: lines.join("\n") };
1763
2312
  }
1764
2313
  function scopeOptions(cmd) {
@@ -1766,14 +2315,16 @@ function scopeOptions(cmd) {
1766
2315
  }
1767
2316
  function updateCommand() {
1768
2317
  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) => {
1769
- const run2 = await runUpdate(names, { ...cmd.parent?.opts(), ...opts });
2318
+ const flags = { ...cmd.parent?.opts(), ...opts };
2319
+ const run2 = await waitFor("walk", flags.check ? "Checking for updates" : "Updating skills", runUpdate(names, flags), { json: flags.json });
1770
2320
  console.log(run2.output);
1771
2321
  process.exitCode = run2.exitCode;
1772
2322
  });
1773
2323
  }
1774
2324
  function checkCommand() {
1775
2325
  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) => {
1776
- const run2 = await runUpdate(names, { ...cmd.parent?.opts(), ...opts, check: true });
2326
+ const flags = { ...cmd.parent?.opts(), ...opts, check: true };
2327
+ const run2 = await waitFor("walk", "Checking for updates", runUpdate(names, flags), { json: flags.json });
1777
2328
  console.log(run2.output);
1778
2329
  process.exitCode = run2.exitCode;
1779
2330
  });
@@ -1781,7 +2332,7 @@ function checkCommand() {
1781
2332
 
1782
2333
  // src/commands/publish.ts
1783
2334
  import { Command as Command11 } from "commander";
1784
- import pc12 from "picocolors";
2335
+ import pc14 from "picocolors";
1785
2336
  import { lint as lint4, toText } from "@skillmds/core";
1786
2337
  var defaultDeps2 = {
1787
2338
  resolve: resolveSource,
@@ -1805,25 +2356,25 @@ async function runPublish(target, flags, deps = defaultDeps2) {
1805
2356
  const report = toText([{ file: src.path, result }], { decorate });
1806
2357
  if (!result.ok) {
1807
2358
  return { result, published: false, exitCode: 1, output: `${report}
1808
- ${pc12.red("\u2717 Refusing to publish: fix the errors above.")}` };
2359
+ ${pc14.red("\u2717 Refusing to publish: fix the errors above.")}` };
1809
2360
  }
1810
2361
  const hasWarn = result.diagnostics.some((d) => d.severity === "warn");
1811
2362
  if (hasWarn && !flags.force) {
1812
2363
  return { result, published: false, exitCode: 1, output: `${report}
1813
- ${pc12.yellow("\u2717 Warnings present. Re-run with --force to publish anyway.")}` };
2364
+ ${pc14.yellow("\u2717 Warnings present. Re-run with --force to publish anyway.")}` };
1814
2365
  }
1815
2366
  if (flags.dryRun) {
1816
2367
  return { result, published: false, exitCode: 0, output: `${report}
1817
- ${pc12.green("\u2713 Dry run: would publish.")}` };
2368
+ ${pc14.green("\u2713 Dry run: would publish.")}` };
1818
2369
  }
1819
2370
  const res = await deps.submit({ content: src.raw, type: flags.type ?? "single" }, flags);
1820
2371
  return { result, published: true, slug: res.slug, exitCode: 0, output: `${report}
1821
- ${pc12.green(`\u2713 Published ${res.slug ?? src.slug} (status: ${res.status ?? "pending"})`)}` };
2372
+ ${pc14.green(`\u2713 Published ${res.slug ?? src.slug} (status: ${res.status ?? "pending"})`)}` };
1822
2373
  }
1823
2374
  function publishCommand() {
1824
2375
  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) => {
1825
2376
  const merged = { ...cmd.parent?.opts(), ...opts };
1826
- const run2 = await runPublish(target, merged);
2377
+ const run2 = await waitFor("walk", merged.dryRun ? "Checking" : "Publishing", runPublish(target, merged));
1827
2378
  console.log(run2.output);
1828
2379
  process.exitCode = run2.exitCode;
1829
2380
  });
@@ -1831,11 +2382,10 @@ function publishCommand() {
1831
2382
 
1832
2383
  // src/commands/login.ts
1833
2384
  import { Command as Command12 } from "commander";
1834
- import * as p5 from "@clack/prompts";
1835
- import pc13 from "picocolors";
2385
+ import pc15 from "picocolors";
1836
2386
  var defaultLoginDeps = (flags) => promptIf(flags, async () => {
1837
- const answer = await p5.password({ message: "Paste your SkillMD token (from your account page)" });
1838
- return p5.isCancel(answer) ? null : answer;
2387
+ const answer = await password({ message: "Paste your SkillMD token (from your account page)" });
2388
+ return isCancel(answer) ? null : answer;
1839
2389
  });
1840
2390
  function finish4(flags, result, doc) {
1841
2391
  return flags.json ? { ...result, output: JSON.stringify(doc, null, 2) } : result;
@@ -1846,15 +2396,15 @@ async function runLogin(flags, deps = defaultLoginDeps(flags)) {
1846
2396
  if (!token) {
1847
2397
  if (!deps.prompt) {
1848
2398
  const error = nonInteractiveHint("--token <token> (or set SKILLMD_TOKEN)");
1849
- return finish4(flags, { exitCode: 1, output: pc13.red(error) }, { ok: false, error });
2399
+ return finish4(flags, { exitCode: 1, output: pc15.red(error) }, { ok: false, error });
1850
2400
  }
1851
2401
  const answer = await deps.prompt();
1852
- if (answer === null) return finish4(flags, { exitCode: 0, output: pc13.dim("Cancelled.") }, { ok: false, cancelled: true });
2402
+ if (answer === null) return finish4(flags, { exitCode: 0, output: pc15.dim("Cancelled.") }, { ok: false, cancelled: true });
1853
2403
  token = answer;
1854
2404
  }
1855
2405
  if (!token) {
1856
2406
  const error = "A token is required.";
1857
- 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 });
1858
2408
  }
1859
2409
  const cfg = readConfig(flags.home);
1860
2410
  let host;
@@ -1862,7 +2412,7 @@ async function runLogin(flags, deps = defaultLoginDeps(flags)) {
1862
2412
  host = hostOf(resolveApi({ api: flags.api, insecureHttp: flags.insecureHttp }, { env, config: { ...cfg, api: flags.api ?? cfg.api } }));
1863
2413
  } catch (e) {
1864
2414
  const msg2 = e instanceof Error ? e.message : String(e);
1865
- return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc13.red(msg2) };
2415
+ return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc15.red(msg2) };
1866
2416
  }
1867
2417
  cfg.token = token;
1868
2418
  if (flags.api) cfg.api = flags.api;
@@ -1901,11 +2451,10 @@ function logoutCommand() {
1901
2451
  }
1902
2452
 
1903
2453
  // src/commands/interactive.ts
1904
- import * as p6 from "@clack/prompts";
1905
- import pc14 from "picocolors";
2454
+ import pc16 from "picocolors";
1906
2455
  function bail(v) {
1907
- if (p6.isCancel(v)) {
1908
- p6.cancel("Cancelled.");
2456
+ if (isCancel(v)) {
2457
+ cancel("Cancelled.");
1909
2458
  return true;
1910
2459
  }
1911
2460
  return false;
@@ -1913,7 +2462,7 @@ function bail(v) {
1913
2462
  async function browseSearchResults(items) {
1914
2463
  const cols = process.stdout.columns ?? 80;
1915
2464
  for (; ; ) {
1916
- const picked = await p6.select({
2465
+ const picked = await select({
1917
2466
  message: "Search results",
1918
2467
  options: [
1919
2468
  ...items.map((item2) => {
@@ -1921,7 +2470,7 @@ async function browseSearchResults(items) {
1921
2470
  const checkTxt = item2.verified ? " \u2713" : "";
1922
2471
  const slugTxt = safeText(item2.slug, 200);
1923
2472
  const plainLabel = `${slugTxt}${starsTxt}${checkTxt}`;
1924
- const label2 = `${pc14.bold(slugTxt)}${starsTxt ? pc14.dim(starsTxt) : ""}${checkTxt ? pc14.green(checkTxt) : ""}`;
2473
+ const label2 = `${pc16.bold(slugTxt)}${starsTxt ? pc16.dim(starsTxt) : ""}${checkTxt ? pc16.green(checkTxt) : ""}`;
1925
2474
  const hintWidth = Math.max(10, cols - plainLabel.length - 6);
1926
2475
  return {
1927
2476
  value: item2.slug,
@@ -1932,7 +2481,7 @@ async function browseSearchResults(items) {
1932
2481
  { value: "__back", label: "\u2190 Back to menu" }
1933
2482
  ]
1934
2483
  });
1935
- if (p6.isCancel(picked) || picked === "__back") return;
2484
+ if (isCancel(picked) || picked === "__back") return;
1936
2485
  const item = items.find((i) => i.slug === picked);
1937
2486
  if (!item) continue;
1938
2487
  await browseOneResult(item);
@@ -1943,12 +2492,12 @@ async function browseOneResult(item) {
1943
2492
  const slugTxt = safeText(item.slug, 200);
1944
2493
  const meta = [
1945
2494
  starsTxt,
1946
- item.verified ? pc14.green("\u2713 verified") : "unverified",
2495
+ item.verified ? pc16.green("\u2713 verified") : "unverified",
1947
2496
  item.type === "pack" ? "pack" : ""
1948
2497
  ].filter(Boolean).join(" \xB7 ");
1949
- p6.note([safeText(item.description ?? ""), pc14.dim(meta)].join("\n"), slugTxt);
2498
+ note([safeText(item.description ?? ""), pc16.dim(meta)].join("\n"), slugTxt);
1950
2499
  for (; ; ) {
1951
- const action = await p6.select({
2500
+ const action = await select({
1952
2501
  message: "What next?",
1953
2502
  options: [
1954
2503
  { value: "project", label: "Install to this project" },
@@ -1957,26 +2506,31 @@ async function browseOneResult(item) {
1957
2506
  { value: "back", label: "\u2190 Back to results" }
1958
2507
  ]
1959
2508
  });
1960
- if (p6.isCancel(action) || action === "back") return;
2509
+ if (isCancel(action) || action === "back") return;
1961
2510
  if (action === "url") {
1962
- p6.log.message(pc14.dim(`https://skillmd.com/skills/${slugTxt}`));
2511
+ log.message(pc16.dim(`https://skillmd.com/skills/${slugTxt}`));
1963
2512
  continue;
1964
2513
  }
1965
- p6.log.step(`Installing ${slugTxt}\u2026`);
2514
+ log.step(`Installing ${slugTxt}\u2026`);
1966
2515
  try {
1967
2516
  const run2 = await runAdd(item.slug, action === "global" ? { global: true } : { project: true });
1968
2517
  console.log(run2.output);
1969
- if (run2.exitCode === 0) p6.log.success("Installed.");
1970
- else p6.log.warn("Not installed.");
2518
+ if (run2.exitCode === 0) log.success("Installed.");
2519
+ else log.warn("Not installed.");
1971
2520
  } catch (err) {
1972
- p6.log.error(err instanceof Error ? err.message : String(err));
2521
+ log.error(err instanceof Error ? err.message : String(err));
1973
2522
  }
1974
2523
  }
1975
2524
  }
1976
- async function runInteractive() {
1977
- p6.intro(banner());
2525
+ async function runInteractive(opts = {}) {
2526
+ console.log();
2527
+ await playLogo();
2528
+ console.log(`
2529
+ ${pc16.dim("Teach your agent anything.")}${opts.version ? ` ${pc16.dim(`v${opts.version}`)}` : ""}
2530
+ `);
2531
+ startFrameAfterLogo();
1978
2532
  for (; ; ) {
1979
- const action = await p6.select({
2533
+ const action = await select({
1980
2534
  message: "What would you like to do?",
1981
2535
  options: [
1982
2536
  { value: "lint", label: "Lint a skill", hint: "validate a SKILL.md" },
@@ -1995,66 +2549,66 @@ async function runInteractive() {
1995
2549
  switch (action) {
1996
2550
  case "lint":
1997
2551
  case "scan": {
1998
- const path = await p6.text({ message: "Path to a skill or folder", placeholder: ".", defaultValue: "." });
2552
+ const path = await text({ message: "Path to a skill or folder", placeholder: ".", defaultValue: "." });
1999
2553
  if (bail(path)) break;
2000
2554
  const target = String(path || ".");
2001
- const s = p6.spinner();
2002
- s.start(action === "lint" ? "Linting" : "Scanning");
2555
+ const s2 = spinner("walk");
2556
+ s2.start(action === "lint" ? "Linting" : "Scanning");
2003
2557
  try {
2004
2558
  const run2 = action === "lint" ? await runLint(target, { format: "text" }) : await runScan(target, { format: "text" });
2005
- s.stop("Done");
2559
+ s2.stop("Done");
2006
2560
  console.log(run2.output);
2007
2561
  process.exitCode = run2.exitCode;
2008
2562
  } catch (err) {
2009
- s.stop(pc14.red(err instanceof Error ? err.message : String(err)));
2563
+ s2.stop(pc16.red(err instanceof Error ? err.message : String(err)));
2010
2564
  process.exitCode = 1;
2011
2565
  }
2012
2566
  break;
2013
2567
  }
2014
2568
  case "search": {
2015
- const query = await p6.text({ message: "Search the registry for" });
2569
+ const query = await text({ message: "Search the registry for" });
2016
2570
  if (bail(query)) break;
2017
- const verified = await p6.confirm({ message: "Verified skills only?", initialValue: false });
2571
+ const verified = await confirm({ message: "Verified skills only?", initialValue: false });
2018
2572
  if (bail(verified)) break;
2019
- const s = p6.spinner();
2020
- s.start("Searching");
2573
+ const s2 = spinner("hop");
2574
+ s2.start(`Searching for ${safeText(String(query))}`);
2021
2575
  try {
2022
2576
  const run2 = await runSearch(String(query), { verified: Boolean(verified), limit: "20" });
2023
- s.stop(`${run2.items.length} result${run2.items.length === 1 ? "" : "s"}`);
2577
+ s2.stop(`${run2.items.length} result${run2.items.length === 1 ? "" : "s"}`);
2024
2578
  if (run2.items.length === 0) {
2025
- p6.log.info("No results.");
2579
+ log.info("No results.");
2026
2580
  break;
2027
2581
  }
2028
2582
  await browseSearchResults(run2.items);
2029
2583
  } catch (err) {
2030
- s.stop(pc14.red(err instanceof Error ? err.message : String(err)));
2584
+ s2.stop(pc16.red(err instanceof Error ? err.message : String(err)));
2031
2585
  process.exitCode = 1;
2032
2586
  }
2033
2587
  break;
2034
2588
  }
2035
2589
  case "init": {
2036
- const name = await p6.text({ message: "Skill name", placeholder: "my-skill" });
2590
+ const name = await text({ message: "Skill name", placeholder: "my-skill" });
2037
2591
  if (bail(name)) break;
2038
- const description = await p6.text({ message: "One-line description", placeholder: "What this skill helps an agent do" });
2592
+ const description = await text({ message: "One-line description", placeholder: "What this skill helps an agent do" });
2039
2593
  if (bail(description)) break;
2040
2594
  try {
2041
2595
  const file = initSkill(String(name), { description: String(description || "") });
2042
- p6.log.success(`Created ${pc14.bold(file)}`);
2596
+ log.success(`Created ${pc16.bold(file)}`);
2043
2597
  } catch (err) {
2044
- p6.log.error(err instanceof Error ? err.message : String(err));
2598
+ log.error(err instanceof Error ? err.message : String(err));
2045
2599
  process.exitCode = 1;
2046
2600
  }
2047
2601
  break;
2048
2602
  }
2049
2603
  case "install": {
2050
- const src = await p6.text({ message: "Skill source", placeholder: "owner/name, https://github.com/\u2026, or ./path" });
2604
+ const src = await text({ message: "Skill source", placeholder: "owner/name, https://github.com/\u2026, or ./path" });
2051
2605
  if (bail(src)) break;
2052
2606
  try {
2053
2607
  const run2 = await runAdd(String(src), {});
2054
2608
  console.log(run2.output);
2055
2609
  process.exitCode = run2.exitCode;
2056
2610
  } catch (err) {
2057
- p6.log.error(err instanceof Error ? err.message : String(err));
2611
+ log.error(err instanceof Error ? err.message : String(err));
2058
2612
  process.exitCode = 1;
2059
2613
  }
2060
2614
  break;
@@ -2064,12 +2618,16 @@ async function runInteractive() {
2064
2618
  break;
2065
2619
  }
2066
2620
  case "update": {
2621
+ const s2 = spinner("walk");
2622
+ s2.start("Updating installed skills");
2067
2623
  try {
2068
2624
  const run2 = await runUpdate([], {});
2625
+ const n = new Set(run2.updated).size;
2626
+ s2.stop(n ? `Updated ${n} skill${n === 1 ? "" : "s"}` : "Done", run2.exitCode === 0 ? 0 : 2);
2069
2627
  console.log(run2.output);
2070
2628
  process.exitCode = run2.exitCode;
2071
2629
  } catch (err) {
2072
- p6.log.error(err instanceof Error ? err.message : String(err));
2630
+ s2.stop(pc16.red(err instanceof Error ? err.message : String(err)), 2);
2073
2631
  process.exitCode = 1;
2074
2632
  }
2075
2633
  break;
@@ -2080,23 +2638,23 @@ async function runInteractive() {
2080
2638
  console.log(run2.output);
2081
2639
  process.exitCode = run2.exitCode;
2082
2640
  } catch (err) {
2083
- p6.log.error(err instanceof Error ? err.message : String(err));
2641
+ log.error(err instanceof Error ? err.message : String(err));
2084
2642
  process.exitCode = 1;
2085
2643
  }
2086
2644
  break;
2087
2645
  }
2088
2646
  case "help": {
2089
- p6.note(
2647
+ note(
2090
2648
  [
2091
- `${pc14.bold("lint")} [path] validate SKILL.md + quality score`,
2092
- `${pc14.bold("scan")} [path] security scan`,
2093
- `${pc14.bold("search")} <query> search the registry`,
2094
- `${pc14.bold("add")} <source> install a skill (lints first)`,
2095
- `${pc14.bold("info")} <slug> registry details for a skill`,
2096
- `${pc14.bold("init")} [name] scaffold a new skill`,
2097
- `${pc14.bold("list / remove / update")} manage installed skills`,
2098
- `${pc14.bold("publish")} [path] publish (fails on lint errors)`,
2099
- `${pc14.bold("rules / login")}`
2649
+ `${pc16.bold("lint")} [path] validate SKILL.md + quality score`,
2650
+ `${pc16.bold("scan")} [path] security scan`,
2651
+ `${pc16.bold("search")} <query> search the registry`,
2652
+ `${pc16.bold("add")} <source> install a skill (lints first)`,
2653
+ `${pc16.bold("info")} <slug> registry details for a skill`,
2654
+ `${pc16.bold("init")} [name] scaffold a new skill`,
2655
+ `${pc16.bold("list / remove / update")} manage installed skills`,
2656
+ `${pc16.bold("publish")} [path] publish (fails on lint errors)`,
2657
+ `${pc16.bold("rules / login")}`
2100
2658
  ].join("\n"),
2101
2659
  "Commands (run: skillmd <command>)"
2102
2660
  );
@@ -2104,7 +2662,7 @@ async function runInteractive() {
2104
2662
  }
2105
2663
  }
2106
2664
  }
2107
- p6.outro(pc14.dim("skillmd.com"));
2665
+ outro(pc16.dim("skillmd.com"));
2108
2666
  }
2109
2667
 
2110
2668
  // src/index.ts
@@ -2134,7 +2692,7 @@ Docs: https://skillmd.com/docs/cli`);
2134
2692
  program.hook("preAction", (thisCmd) => {
2135
2693
  const opts = thisCmd.opts();
2136
2694
  if (opts.token && process.stderr.isTTY) {
2137
- console.error(pc15.yellow("\u26A0 --token is visible in your shell history and process list; prefer `npx get-skill login` or SKILLMD_TOKEN."));
2695
+ console.error(pc17.yellow("\u26A0 --token is visible in your shell history and process list; prefer `npx get-skill login` or SKILLMD_TOKEN."));
2138
2696
  }
2139
2697
  });
2140
2698
  program.addCommand(lintCommand());
@@ -2153,7 +2711,7 @@ program.addCommand(loginCommand());
2153
2711
  program.addCommand(logoutCommand());
2154
2712
  if (process.argv.slice(2).length === 0) {
2155
2713
  if (process.stdout.isTTY) {
2156
- await runInteractive();
2714
+ await runInteractive({ version });
2157
2715
  process.exit(process.exitCode ?? 0);
2158
2716
  }
2159
2717
  program.outputHelp();