skills 1.5.17 → 1.5.19

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.
@@ -627,60 +627,6 @@ var r = class extends V {
627
627
  });
628
628
  }
629
629
  };
630
- let u$2 = class u extends V {
631
- options;
632
- cursor = 0;
633
- #t;
634
- getGroupItems(t) {
635
- return this.options.filter((r) => r.group === t);
636
- }
637
- isGroupSelected(t) {
638
- const r = this.getGroupItems(t), e = this.value;
639
- return e === void 0 ? false : r.every((s) => e.includes(s.value));
640
- }
641
- toggleValue() {
642
- const t = this.options[this.cursor];
643
- if (t !== void 0) if (this.value === void 0 && (this.value = []), t.group === true) {
644
- const r = t.value, e = this.getGroupItems(r);
645
- this.isGroupSelected(r) ? this.value = this.value.filter((s) => e.findIndex((i) => i.value === s) === -1) : this.value = [...this.value, ...e.map((s) => s.value)], this.value = Array.from(new Set(this.value));
646
- } else {
647
- const r = this.value.includes(t.value);
648
- this.value = r ? this.value.filter((e) => e !== t.value) : [...this.value, t.value];
649
- }
650
- }
651
- constructor(t) {
652
- super(t, false);
653
- const { options: r } = t;
654
- this.#t = t.selectableGroups !== false, this.options = Object.entries(r).flatMap(([e, s]) => [{
655
- value: e,
656
- group: true,
657
- label: e
658
- }, ...s.map((i) => ({
659
- ...i,
660
- group: e
661
- }))]), this.value = [...t.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: e }) => e === t.cursorAt), this.#t ? 0 : 1), this.on("cursor", (e) => {
662
- switch (e) {
663
- case "left":
664
- case "up": {
665
- this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
666
- const s = this.options[this.cursor]?.group === true;
667
- !this.#t && s && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
668
- break;
669
- }
670
- case "down":
671
- case "right": {
672
- this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
673
- const s = this.options[this.cursor]?.group === true;
674
- !this.#t && s && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
675
- break;
676
- }
677
- case "space":
678
- this.toggleValue();
679
- break;
680
- }
681
- });
682
- }
683
- };
684
630
  var a = class extends V {
685
631
  options;
686
632
  cursor = 0;
@@ -755,4 +701,4 @@ let n$1 = class n extends V {
755
701
  });
756
702
  }
757
703
  };
758
- export { a, block, fastStringWidth, getColumns, getRows, isCancel, n$1, r, require_src, settings, u$2, wrapAnsi, wrapTextWithPrefix };
704
+ export { a, block, fastStringWidth, getColumns, getRows, isCancel, n$1, r, require_src, settings, wrapAnsi, wrapTextWithPrefix };
@@ -1,4 +1,4 @@
1
- import { a, block, fastStringWidth, getColumns, getRows, n$1, r, require_src, settings, u$2, wrapAnsi, wrapTextWithPrefix } from "./core.mjs";
1
+ import { a, block, fastStringWidth, getColumns, getRows, n$1, r, require_src, settings, wrapAnsi, wrapTextWithPrefix } from "./core.mjs";
2
2
  import { styleText } from "node:util";
3
3
  import process$1 from "node:process";
4
4
  var import_src = require_src();
@@ -213,100 +213,6 @@ ${h}
213
213
  }
214
214
  }).prompt();
215
215
  };
216
- const groupMultiselect = (o) => {
217
- const { selectableGroups: h = true, groupSpacing: x = 0 } = o, m = (n, l, g = []) => {
218
- const a = n.label ?? String(n.value), t = typeof n.group == "string", s = t && (g[g.indexOf(n) + 1] ?? { group: true }), u = t && s && s.group === true;
219
- let r = "", c = "";
220
- t && (h ? (r = u ? `${S_BAR_END} ` : `${S_BAR} `, c = u ? " " : `${S_BAR} `) : r = " ");
221
- let i = "";
222
- if (x > 0 && !t && (i = `
223
- `.repeat(x)), l === "active") return wrapTextWithPrefix(o.output, `${a}${n.hint ? ` ${styleText("dim", `(${n.hint})`)}` : ""}`, `${i}${styleText("dim", r)} `, `${i}${styleText("dim", r)}${styleText("cyan", S_CHECKBOX_ACTIVE)} `, `${i}${styleText("dim", c)} `);
224
- if (l === "group-active") return wrapTextWithPrefix(o.output, a, `${i}${r} `, `${i}${r}${styleText("cyan", S_CHECKBOX_ACTIVE)} `, `${i}${c} `, (d) => styleText("dim", d));
225
- if (l === "group-active-selected") return wrapTextWithPrefix(o.output, a, `${i}${r} `, `${i}${r}${styleText("green", S_CHECKBOX_SELECTED)} `, `${i}${c} `, (d) => styleText("dim", d));
226
- if (l === "selected") {
227
- const d = t || h ? styleText("green", S_CHECKBOX_SELECTED) : "";
228
- return wrapTextWithPrefix(o.output, `${a}${n.hint ? ` (${n.hint})` : ""}`, `${i}${styleText("dim", r)} `, `${i}${styleText("dim", r)}${d} `, `${i}${styleText("dim", c)} `, (S) => styleText("dim", S));
229
- }
230
- if (l === "cancelled") return `${styleText(["strikethrough", "dim"], a)}`;
231
- if (l === "active-selected") return wrapTextWithPrefix(o.output, `${a}${n.hint ? ` ${styleText("dim", `(${n.hint})`)}` : ""}`, `${i}${styleText("dim", r)} `, `${i}${styleText("dim", r)}${styleText("green", S_CHECKBOX_SELECTED)} `, `${i}${styleText("dim", c)} `);
232
- if (l === "submitted") return `${styleText("dim", a)}`;
233
- const f = t || h ? styleText("dim", S_CHECKBOX_INACTIVE) : "";
234
- return wrapTextWithPrefix(o.output, a, `${i}${styleText("dim", r)} `, `${i}${styleText("dim", r)}${f} `, `${i}${styleText("dim", c)} `, (d) => styleText("dim", d));
235
- }, y = o.required ?? true, I = o.showInstructions ?? true;
236
- return new u$2({
237
- options: o.options,
238
- signal: o.signal,
239
- input: o.input,
240
- output: o.output,
241
- initialValues: o.initialValues,
242
- required: y,
243
- cursorAt: o.cursorAt,
244
- selectableGroups: h,
245
- validate(n) {
246
- if (y && (n === void 0 || n.length === 0)) return `Please select at least one option.
247
- ${styleText("reset", styleText("dim", `Press ${styleText([
248
- "gray",
249
- "bgWhite",
250
- "inverse"
251
- ], " space ")} to select, ${styleText("gray", styleText(["bgWhite", "inverse"], " enter "))} to submit`))}`;
252
- },
253
- render() {
254
- const n = o.withGuide ?? settings.withGuide, l = `${n ? `${styleText("gray", S_BAR)}
255
- ` : ""}${symbol(this.state)} ${o.message}
256
- `, g = this.value ?? [], a = (t, s) => {
257
- const u = this.options, r = g.includes(t.value) || t.group === true && this.isGroupSelected(`${t.value}`);
258
- return !s && typeof t.group == "string" && this.options[this.cursor]?.value === t.group ? m(t, r ? "group-active-selected" : "group-active", u) : s && r ? m(t, "active-selected", u) : r ? m(t, "selected", u) : m(t, s ? "active" : "inactive", u);
259
- };
260
- switch (this.state) {
261
- case "submit": {
262
- const t = this.options.filter(({ value: u }) => g.includes(u)).map((u) => m(u, "submitted")), s = t.length === 0 ? "" : ` ${t.join(styleText("dim", ", "))}`;
263
- return `${l}${n ? styleText("gray", S_BAR) : ""}${s}`;
264
- }
265
- case "cancel": {
266
- const t = this.options.filter(({ value: s }) => g.includes(s)).map((s) => m(s, "cancelled")).join(styleText("dim", ", "));
267
- return `${l}${n ? `${styleText("gray", S_BAR)} ` : ""}${t.trim() ? `${t}${n ? `
268
- ${styleText("gray", S_BAR)}` : ""}` : ""}`;
269
- }
270
- case "error": {
271
- const t = n ? `${styleText("yellow", S_BAR)} ` : "", s = this.error.split(`
272
- `).map((i, f) => f === 0 ? `${n ? `${styleText("yellow", S_BAR_END)} ` : ""}${styleText("yellow", i)}` : ` ${i}`).join(`
273
- `), u = l.split(`
274
- `).length, r = s.split(`
275
- `).length + 1;
276
- return `${l}${t}${limitOptions({
277
- output: o.output,
278
- options: this.options,
279
- cursor: this.cursor,
280
- maxItems: o.maxItems,
281
- columnPadding: t.length,
282
- rowPadding: u + r,
283
- style: a
284
- }).join(`
285
- ${t}`)}
286
- ${s}
287
- `;
288
- }
289
- default: {
290
- const t = n ? `${styleText("cyan", S_BAR)} ` : "", s = l.split(`
291
- `).length, u = I ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, n) : n ? [styleText("cyan", S_BAR_END)] : [], r = u.join(`
292
- `), c = u.length + 1;
293
- return `${l}${t}${limitOptions({
294
- output: o.output,
295
- options: this.options,
296
- cursor: this.cursor,
297
- maxItems: o.maxItems,
298
- columnPadding: t.length,
299
- rowPadding: s + c,
300
- style: a
301
- }).join(`
302
- ${t}`)}
303
- ${r}
304
- `;
305
- }
306
- }
307
- }
308
- }).prompt();
309
- };
310
216
  const log = {
311
217
  message: (s = [], { symbol: e = styleText("gray", S_BAR), secondarySymbol: r = styleText("gray", S_BAR), output: m = process.stdout, spacing: l = 1, withGuide: c } = {}) => {
312
218
  const t = [], o = c ?? settings.withGuide, f = o ? r : "", O = o ? `${e} ` : "", u = o ? `${r} ` : "";
@@ -536,4 +442,4 @@ ${b}
536
442
  }).prompt();
537
443
  };
538
444
  `${styleText("gray", S_BAR)}`;
539
- export { cancel, confirm, groupMultiselect, intro, log, multiselect, note, outro, select, spinner };
445
+ export { cancel, confirm, intro, log, multiselect, note, outro, select, spinner };
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { __toESM } from "./_chunks/rolldown-runtime.mjs";
3
3
  import { isCancel } from "./_chunks/libs/@clack/core.mjs";
4
- import { cancel, confirm, groupMultiselect, intro, log, multiselect as multiselect$1, note, outro, select, spinner } from "./_chunks/libs/@clack/prompts.mjs";
4
+ import { cancel, confirm, intro, log, multiselect as multiselect$1, note, outro, select, spinner } from "./_chunks/libs/@clack/prompts.mjs";
5
5
  import { require_picocolors } from "./_chunks/libs/picocolors.mjs";
6
6
  import { esm_default } from "./_chunks/libs/simple-git.mjs";
7
7
  import { xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
@@ -9,7 +9,6 @@ import { require_dist } from "./_chunks/libs/@vercel/detect-agent.mjs";
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
10
10
  import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
11
11
  import { fileURLToPath } from "url";
12
- import { createHash } from "node:crypto";
13
12
  import { stripVTControlCharacters } from "node:util";
14
13
  import { homedir, platform, tmpdir } from "os";
15
14
  import * as readline from "readline";
@@ -18,7 +17,8 @@ import { promisify } from "util";
18
17
  import { execFile, execSync, spawn, spawnSync } from "child_process";
19
18
  import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
20
19
  import { parse } from "yaml";
21
- import { createHash as createHash$1 } from "crypto";
20
+ import { createHash } from "crypto";
21
+ import { createHash as createHash$1 } from "node:crypto";
22
22
  import { gunzipSync, inflateRawSync } from "node:zlib";
23
23
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
24
24
  function getOwnerRepo(parsed) {
@@ -71,7 +71,10 @@ function sanitizeSubpath(subpath) {
71
71
  function isLocalPath(input) {
72
72
  return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
73
73
  }
74
- const SOURCE_ALIASES = { "coinbase/agentWallet": "coinbase/agentic-wallet-skills" };
74
+ const SOURCE_ALIASES = {
75
+ "coinbase/agentWallet": "coinbase/agentic-wallet-skills",
76
+ "vercel-labs/vercel-skills": "vercel-labs/agent-skills"
77
+ };
75
78
  function decodeFragmentValue(value) {
76
79
  try {
77
80
  return decodeURIComponent(value);
@@ -277,8 +280,88 @@ function countVisualRowsForLines(lines, columns) {
277
280
  const cols = columns !== void 0 && columns > 0 ? columns : process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
278
281
  return lines.reduce((sum, line) => sum + visualRowsForLine(line, cols), 0);
279
282
  }
283
+ function truncateToWidth(text, width) {
284
+ let truncated = "";
285
+ for (const char of text) {
286
+ if (approxStringWidth(truncated + char) > width) break;
287
+ truncated += char;
288
+ }
289
+ return truncated;
290
+ }
291
+ function formatDetailLines(detail, width, maxLines) {
292
+ const safeWidth = Math.max(1, width);
293
+ const normalized = detail?.replace(/\s+/g, " ").trim() ?? "";
294
+ const lines = [];
295
+ let remaining = normalized;
296
+ while (remaining && lines.length < maxLines) {
297
+ if (approxStringWidth(remaining) <= safeWidth) {
298
+ lines.push(remaining);
299
+ remaining = "";
300
+ break;
301
+ }
302
+ const candidate = truncateToWidth(remaining, safeWidth);
303
+ const breakAt = candidate.lastIndexOf(" ");
304
+ if (breakAt > 0) {
305
+ lines.push(candidate.slice(0, breakAt).trimEnd());
306
+ remaining = remaining.slice(breakAt).trimStart();
307
+ } else {
308
+ lines.push(candidate);
309
+ remaining = remaining.slice(candidate.length).trimStart();
310
+ }
311
+ }
312
+ if (remaining && lines.length > 0) {
313
+ const last = lines.length - 1;
314
+ lines[last] = `${truncateToWidth(lines[last], Math.max(0, safeWidth - 1)).trimEnd()}…`;
315
+ }
316
+ while (lines.length < maxLines) lines.push("");
317
+ return lines;
318
+ }
319
+ function buildSearchEntries(items, selectGroups, collapsedGroups = /* @__PURE__ */ new Set()) {
320
+ if (!selectGroups) return items.map((item) => ({
321
+ type: "item",
322
+ item
323
+ }));
324
+ const entries = [];
325
+ let index = 0;
326
+ while (index < items.length) {
327
+ const item = items[index];
328
+ if (!item.group) {
329
+ entries.push({
330
+ type: "item",
331
+ item
332
+ });
333
+ index += 1;
334
+ continue;
335
+ }
336
+ const groupItems = [];
337
+ while (index < items.length && items[index].group === item.group) {
338
+ groupItems.push(items[index]);
339
+ index += 1;
340
+ }
341
+ const collapsed = collapsedGroups.has(item.group);
342
+ entries.push({
343
+ type: "group",
344
+ group: item.group,
345
+ items: groupItems,
346
+ collapsed
347
+ });
348
+ if (!collapsed) entries.push(...groupItems.map((groupItem) => ({
349
+ type: "item",
350
+ item: groupItem
351
+ })));
352
+ }
353
+ return entries;
354
+ }
355
+ function toggleSearchEntry(selected, entry) {
356
+ if (entry?.type === "group") {
357
+ const allSelected = entry.items.every((item) => selected.has(item.value));
358
+ for (const item of entry.items) if (allSelected) selected.delete(item.value);
359
+ else selected.add(item.value);
360
+ } else if (entry?.type === "item") if (selected.has(entry.item.value)) selected.delete(entry.item.value);
361
+ else selected.add(entry.item.value);
362
+ }
280
363
  async function searchMultiselect(options) {
281
- const { message, items, maxVisible = 8, initialSelected = [], required = false, lockedSection } = options;
364
+ const { message, items, maxVisible = 8, initialSelected = [], required = false, lockedSection, searchable = true, showDetail = false, detailLines = 2, showSelectedSummary = true, selectGroups = false } = options;
282
365
  return new Promise((resolve) => {
283
366
  const rl = readline.createInterface({
284
367
  input: process.stdin,
@@ -290,6 +373,7 @@ async function searchMultiselect(options) {
290
373
  let query = "";
291
374
  let cursor = 0;
292
375
  const selected = new Set(initialSelected);
376
+ const collapsedGroups = /* @__PURE__ */ new Set();
293
377
  let lastRenderHeight = 0;
294
378
  const lockedValues = lockedSection ? lockedSection.items.map((i) => i.value) : [];
295
379
  const filter = (item, q) => {
@@ -300,17 +384,10 @@ async function searchMultiselect(options) {
300
384
  const getFiltered = () => {
301
385
  return items.filter((item) => filter(item, query));
302
386
  };
303
- const clearRender = () => {
304
- if (lastRenderHeight > 0) {
305
- process.stdout.write(`\x1b[${lastRenderHeight}A`);
306
- for (let i = 0; i < lastRenderHeight; i++) process.stdout.write("\x1B[2K\x1B[1B");
307
- process.stdout.write(`\x1b[${lastRenderHeight}A`);
308
- }
309
- };
310
387
  const render = (state = "active") => {
311
- clearRender();
312
388
  const lines = [];
313
389
  const filtered = getFiltered();
390
+ const entries = buildSearchEntries(filtered, selectGroups, collapsedGroups);
314
391
  const icon = state === "active" ? S_STEP_ACTIVE : state === "cancel" ? S_STEP_CANCEL : S_STEP_SUBMIT;
315
392
  lines.push(`${icon} ${import_picocolors.default.bold(message)}`);
316
393
  if (state === "active") {
@@ -319,32 +396,45 @@ async function searchMultiselect(options) {
319
396
  const lockedTitle = `${import_picocolors.default.bold(lockedSection.title)} ${import_picocolors.default.dim("── always included")}`;
320
397
  lines.push(`${S_BAR} ${S_BAR_H}${S_BAR_H} ${lockedTitle} ${S_BAR_H.repeat(12)}`);
321
398
  for (const item of lockedSection.items) lines.push(`${S_BAR} ${S_BULLET} ${import_picocolors.default.bold(item.label)}`);
322
- if (lockedSection.hiddenCount && lockedSection.hiddenCount > 0) lines.push(`${S_BAR} ${import_picocolors.default.dim(`...and ${lockedSection.hiddenCount} more`)}`);
399
+ if (lockedSection.hiddenCount && lockedSection.hiddenCount > 0) lines.push(`${S_BAR} ${import_picocolors.default.dim(`…and ${lockedSection.hiddenCount} more`)}`);
323
400
  lines.push(`${S_BAR}`);
324
401
  lines.push(`${S_BAR} ${S_BAR_H}${S_BAR_H} ${import_picocolors.default.bold("Additional agents")} ${S_BAR_H.repeat(29)}`);
325
402
  }
326
- const searchLine = `${S_BAR} ${import_picocolors.default.dim("Search:")} ${query}${import_picocolors.default.inverse(" ")}`;
327
- lines.push(searchLine);
328
- lines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, space select, enter confirm")}`);
329
- lines.push(`${S_BAR}`);
330
- const visibleStart = Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), filtered.length - maxVisible));
331
- const visibleEnd = Math.min(filtered.length, visibleStart + maxVisible);
332
- const visibleItems = filtered.slice(visibleStart, visibleEnd);
403
+ if (searchable) {
404
+ const searchLine = `${S_BAR} ${import_picocolors.default.dim("Search:")} ${query}${import_picocolors.default.inverse(" ")}`;
405
+ lines.push(searchLine);
406
+ lines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, space select, enter confirm")}`);
407
+ lines.push(`${S_BAR}`);
408
+ }
409
+ const visibleStart = Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), entries.length - maxVisible));
410
+ const visibleEnd = Math.min(entries.length, visibleStart + maxVisible);
411
+ const visibleEntries = entries.slice(visibleStart, visibleEnd);
333
412
  if (filtered.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("No matches found")}`);
334
413
  else {
335
- for (let i = 0; i < visibleItems.length; i++) {
336
- const item = visibleItems[i];
337
- const actualIndex = visibleStart + i;
338
- const isSelected = selected.has(item.value);
339
- const isCursor = actualIndex === cursor;
340
- const radio = isSelected ? S_RADIO_ACTIVE : S_RADIO_INACTIVE;
414
+ for (let i = 0; i < visibleEntries.length; i++) {
415
+ const entry = visibleEntries[i];
416
+ const isCursor = visibleStart + i === cursor;
417
+ if (entry.type === "group") {
418
+ const selectedCount = entry.items.filter((item) => selected.has(item.value)).length;
419
+ const radio = selectedCount === entry.items.length ? S_RADIO_ACTIVE : selectedCount > 0 ? import_picocolors.default.yellow("◐") : S_RADIO_INACTIVE;
420
+ const label = isCursor ? import_picocolors.default.underline(import_picocolors.default.bold(entry.group)) : import_picocolors.default.bold(entry.group);
421
+ const prefix = isCursor ? import_picocolors.default.cyan("❯") : " ";
422
+ const disclosure = import_picocolors.default.dim(entry.collapsed ? "▸" : "▾");
423
+ lines.push(`${S_BAR} ${prefix} ${disclosure} ${radio} ${label}`);
424
+ continue;
425
+ }
426
+ const item = entry.item;
427
+ const radio = selected.has(item.value) ? S_RADIO_ACTIVE : S_RADIO_INACTIVE;
341
428
  const label = isCursor ? import_picocolors.default.underline(item.label) : item.label;
342
429
  const hint = item.hint ? import_picocolors.default.dim(` (${item.hint})`) : "";
343
430
  const prefix = isCursor ? import_picocolors.default.cyan("❯") : " ";
344
- lines.push(`${S_BAR} ${prefix} ${radio} ${label}${hint}`);
431
+ const groupItems = selectGroups && item.group ? filtered.filter((i) => i.group === item.group) : [];
432
+ const isLastInGroup = groupItems.at(-1) === item;
433
+ const tree = groupItems.length > 0 ? `${import_picocolors.default.dim(isLastInGroup ? "└─" : "├─")} ` : "";
434
+ lines.push(`${S_BAR} ${prefix} ${tree}${radio} ${label}${hint}`);
345
435
  }
346
436
  const hiddenBefore = visibleStart;
347
- const hiddenAfter = filtered.length - visibleEnd;
437
+ const hiddenAfter = entries.length - visibleEnd;
348
438
  if (hiddenBefore > 0 || hiddenAfter > 0) {
349
439
  const parts = [];
350
440
  if (hiddenBefore > 0) parts.push(`↑ ${hiddenBefore} more`);
@@ -352,19 +442,35 @@ async function searchMultiselect(options) {
352
442
  lines.push(`${S_BAR} ${import_picocolors.default.dim(parts.join(" "))}`);
353
443
  }
354
444
  }
355
- lines.push(`${S_BAR}`);
356
- const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
357
- if (allSelectedLabels.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("Selected: (none)")}`);
358
- else {
359
- const summary = allSelectedLabels.length <= 3 ? allSelectedLabels.join(", ") : `${allSelectedLabels.slice(0, 3).join(", ")} +${allSelectedLabels.length - 3} more`;
360
- lines.push(`${S_BAR} ${import_picocolors.default.green("Selected:")} ${summary}`);
445
+ if (showDetail) {
446
+ const entry = entries[cursor];
447
+ const detail = entry?.type === "group" ? `Select all ${entry.items.length} skills in ${entry.group}.` : entry?.item.detail;
448
+ const columns = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
449
+ const detailWidth = Math.max(1, columns - 5);
450
+ lines.push(`${S_BAR}`);
451
+ lines.push(`${S_BAR} ${import_picocolors.default.dim("Description")}`);
452
+ for (const line of formatDetailLines(detail, detailWidth, detailLines)) lines.push(`${S_BAR} ${import_picocolors.default.dim(line)}`);
453
+ }
454
+ if (showSelectedSummary) {
455
+ lines.push(`${S_BAR}`);
456
+ const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
457
+ if (allSelectedLabels.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("Selected: (none)")}`);
458
+ else {
459
+ const summary = allSelectedLabels.length <= 3 ? allSelectedLabels.join(", ") : `${allSelectedLabels.slice(0, 3).join(", ")} +${allSelectedLabels.length - 3} more`;
460
+ lines.push(`${S_BAR} ${import_picocolors.default.green("Selected:")} ${summary}`);
461
+ }
462
+ }
463
+ if (!searchable) {
464
+ lines.push(`${S_BAR}`);
465
+ lines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, ←→ collapse/expand, space select, enter confirm")}`);
361
466
  }
362
467
  lines.push(`${import_picocolors.default.dim("└")}`);
363
468
  } else if (state === "submit") {
364
469
  const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
365
470
  lines.push(`${S_BAR} ${import_picocolors.default.dim(allSelectedLabels.join(", "))}`);
366
471
  } else if (state === "cancel") lines.push(`${S_BAR} ${import_picocolors.default.strikethrough(import_picocolors.default.dim("Cancelled"))}`);
367
- process.stdout.write(lines.join("\n") + "\n");
472
+ const clearPreviousFrame = lastRenderHeight > 0 ? `\x1b[${lastRenderHeight}A\x1b[J` : "";
473
+ process.stdout.write(clearPreviousFrame + lines.join("\n") + "\n");
368
474
  lastRenderHeight = countVisualRowsForLines(lines, process.stdout.columns);
369
475
  };
370
476
  const cleanup = () => {
@@ -385,7 +491,7 @@ async function searchMultiselect(options) {
385
491
  };
386
492
  const keypressHandler = (_str, key) => {
387
493
  if (!key) return;
388
- const filtered = getFiltered();
494
+ const entries = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups);
389
495
  if (key.name === "return") {
390
496
  submit();
391
497
  return;
@@ -400,14 +506,31 @@ async function searchMultiselect(options) {
400
506
  return;
401
507
  }
402
508
  if (key.name === "down") {
403
- cursor = Math.min(filtered.length - 1, cursor + 1);
509
+ cursor = Math.min(entries.length - 1, cursor + 1);
404
510
  render();
405
511
  return;
406
512
  }
513
+ if (selectGroups && key.name === "right") {
514
+ const entry = entries[cursor];
515
+ if (entry?.type === "group" && entry.collapsed) {
516
+ collapsedGroups.delete(entry.group);
517
+ render();
518
+ }
519
+ return;
520
+ }
521
+ if (selectGroups && key.name === "left") {
522
+ const entry = entries[cursor];
523
+ const group = entry?.type === "group" ? entry.group : entry?.item.group;
524
+ if (group) {
525
+ collapsedGroups.add(group);
526
+ cursor = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups).findIndex((collapsedEntry) => collapsedEntry.type === "group" && collapsedEntry.group === group);
527
+ render();
528
+ }
529
+ return;
530
+ }
407
531
  if (key.name === "space") {
408
- const item = filtered[cursor];
409
- if (item) if (selected.has(item.value)) selected.delete(item.value);
410
- else selected.add(item.value);
532
+ const entry = entries[cursor];
533
+ toggleSearchEntry(selected, entry);
411
534
  render();
412
535
  return;
413
536
  }
@@ -417,7 +540,7 @@ async function searchMultiselect(options) {
417
540
  render();
418
541
  return;
419
542
  }
420
- if (key.sequence && !key.ctrl && !key.meta && key.sequence.length === 1) {
543
+ if (searchable && key.sequence && !key.ctrl && !key.meta && key.sequence.length === 1) {
421
544
  query += key.sequence;
422
545
  cursor = 0;
423
546
  render();
@@ -429,6 +552,7 @@ async function searchMultiselect(options) {
429
552
  });
430
553
  }
431
554
  const DEFAULT_CLONE_TIMEOUT_MS = 3e5;
555
+ const ALLOWED_GIT_PROTOCOLS = "https:http:ssh:git:file";
432
556
  const CLONE_TIMEOUT_MS = (() => {
433
557
  const raw = process.env.SKILLS_CLONE_TIMEOUT_MS;
434
558
  if (!raw) return DEFAULT_CLONE_TIMEOUT_MS;
@@ -492,23 +616,45 @@ function isGitHubSsoAuthError(message) {
492
616
  function isAuthFailure(message) {
493
617
  return message.includes("Authentication failed") || message.includes("could not read Username") || message.includes("Permission denied") || message.includes("Repository not found") || message.includes("requested URL returned error: 403") || isGitHubSsoAuthError(message);
494
618
  }
495
- function createGitClient(extraEnv) {
496
- return esm_default({
619
+ function createGitClient(sshCommand) {
620
+ const git = esm_default({
497
621
  timeout: { block: CLONE_TIMEOUT_MS },
498
- env: {
499
- ...process.env,
500
- GIT_TERMINAL_PROMPT: "0",
501
- GIT_LFS_SKIP_SMUDGE: "1",
502
- ...extraEnv
503
- },
504
622
  config: [
505
623
  "filter.lfs.required=false",
506
624
  "filter.lfs.smudge=",
507
625
  "filter.lfs.clean=",
508
626
  "filter.lfs.process="
509
627
  ],
510
- unsafe: { allowUnsafeFilter: true }
628
+ unsafe: {
629
+ allowUnsafeAlias: true,
630
+ allowUnsafeAskPass: true,
631
+ allowUnsafeConfigEnvCount: true,
632
+ allowUnsafeConfigPaths: true,
633
+ allowUnsafeCredentialHelper: true,
634
+ allowUnsafeDiffExternal: true,
635
+ allowUnsafeDiffTextConv: true,
636
+ allowUnsafeEditor: true,
637
+ allowUnsafeFilter: true,
638
+ allowUnsafeFsMonitor: true,
639
+ allowUnsafeGpgProgram: true,
640
+ allowUnsafeGitProxy: true,
641
+ allowUnsafeHooksPath: true,
642
+ allowUnsafeMergeDriver: true,
643
+ allowUnsafePack: true,
644
+ allowUnsafePager: true,
645
+ allowUnsafeProtocolOverride: true,
646
+ allowUnsafeSshCommand: true,
647
+ allowUnsafeTemplateDir: true
648
+ }
511
649
  });
650
+ git.env({
651
+ ...process.env,
652
+ GIT_TERMINAL_PROMPT: "0",
653
+ GIT_ALLOW_PROTOCOL: ALLOWED_GIT_PROTOCOLS,
654
+ GIT_LFS_SKIP_SMUDGE: "1",
655
+ ...sshCommand ? { GIT_SSH_COMMAND: sshCommand } : {}
656
+ });
657
+ return git;
512
658
  }
513
659
  async function resetTempDir(dir) {
514
660
  await rm(dir, {
@@ -552,7 +698,8 @@ async function tryGhClone(repo, tempDir, ref) {
552
698
  timeout: CLONE_TIMEOUT_MS,
553
699
  env: {
554
700
  ...process.env,
555
- GIT_TERMINAL_PROMPT: "0"
701
+ GIT_TERMINAL_PROMPT: "0",
702
+ GIT_ALLOW_PROTOCOL: ALLOWED_GIT_PROTOCOLS
556
703
  }
557
704
  });
558
705
  return true;
@@ -593,7 +740,7 @@ async function cloneRepo(url, ref) {
593
740
  } catch {}
594
741
  try {
595
742
  await resetTempDir(tempDir);
596
- await createGitClient({ GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes" }).clone(repo.sshUrl, tempDir, cloneOptions);
743
+ await createGitClient(process.env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes").clone(repo.sshUrl, tempDir, cloneOptions);
597
744
  return tempDir;
598
745
  } catch {}
599
746
  }
@@ -721,7 +868,7 @@ async function computeSkillFolderHash(skillDir) {
721
868
  const files = [];
722
869
  await collectFiles(skillDir, skillDir, files);
723
870
  files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
724
- const hash = createHash$1("sha256");
871
+ const hash = createHash("sha256");
725
872
  for (const file of files) {
726
873
  hash.update(file.relativePath);
727
874
  hash.update(file.content);
@@ -795,6 +942,9 @@ function normalizeSkillName(name) {
795
942
  function normalizeRelativePath(path) {
796
943
  return path.split(sep).join("/").replace(/\/+/g, "/");
797
944
  }
945
+ function isRecord(value) {
946
+ return typeof value === "object" && value !== null && !Array.isArray(value);
947
+ }
798
948
  function shouldInstallInternalSkills() {
799
949
  const envValue = process.env.INSTALL_INTERNAL_SKILLS;
800
950
  return envValue === "1" || envValue === "true";
@@ -812,13 +962,14 @@ async function parseSkillMd(skillMdPath, options) {
812
962
  const { data } = parseFrontmatter(content);
813
963
  if (!data.name || !data.description) return null;
814
964
  if (typeof data.name !== "string" || typeof data.description !== "string") return null;
815
- if (data.metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
965
+ const metadata = isRecord(data.metadata) ? data.metadata : void 0;
966
+ if (metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
816
967
  return {
817
968
  name: sanitizeMetadata(data.name),
818
969
  description: sanitizeMetadata(data.description),
819
970
  path: dirname(skillMdPath),
820
971
  rawContent: content,
821
- metadata: data.metadata
972
+ metadata
822
973
  };
823
974
  } catch {
824
975
  return null;
@@ -2680,7 +2831,7 @@ var WellKnownProvider = class {
2680
2831
  }
2681
2832
  }
2682
2833
  computeDigest(bytes) {
2683
- return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
2834
+ return `sha256:${createHash$1("sha256").update(bytes).digest("hex")}`;
2684
2835
  }
2685
2836
  extractArchive(bytes, artifactUrl, contentType) {
2686
2837
  if (this.isZipArchive(bytes, artifactUrl, contentType)) return this.extractZip(bytes);
@@ -2849,7 +3000,7 @@ function getGitHubToken() {
2849
3000
  if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
2850
3001
  if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
2851
3002
  if (!_ghWarningShown) {
2852
- process.stderr.write(`${import_picocolors.default.yellow("│")} ${import_picocolors.default.yellow("GitHub rate limit reached")} using your ${import_picocolors.default.cyan("gh")} login to continue.\n${import_picocolors.default.yellow("")} ${import_picocolors.default.dim(`Tip: set ${import_picocolors.default.cyan("GITHUB_TOKEN")} to avoid this prompt, or use ${import_picocolors.default.cyan("--full-depth")} to clone instead.\n`)}`);
3003
+ process.stderr.write(`${import_picocolors.default.dim("│ GitHub API request limit reached; checking existing ")}${import_picocolors.default.cyan("gh")}${import_picocolors.default.dim(" authentication…\n")}`);
2853
3004
  _ghWarningShown = true;
2854
3005
  }
2855
3006
  try {
@@ -3103,7 +3254,7 @@ async function fetchSkillDownload(source, slug) {
3103
3254
  }
3104
3255
  }
3105
3256
  function computeSnapshotHash(files) {
3106
- const hash = createHash("sha256");
3257
+ const hash = createHash$1("sha256");
3107
3258
  for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) {
3108
3259
  hash.update(file.path);
3109
3260
  hash.update(file.contents);
@@ -3183,7 +3334,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
3183
3334
  tree
3184
3335
  };
3185
3336
  }
3186
- var version$1 = "1.5.17";
3337
+ var version$1 = "1.5.19";
3187
3338
  const isCancelled$1 = (value) => typeof value === "symbol";
3188
3339
  const EVE_AGENT_LABEL = "eve agent";
3189
3340
  async function isSourcePrivate(source) {
@@ -3254,12 +3405,6 @@ function shortenPath$2(fullPath, cwd) {
3254
3405
  if (fullPath === cwd || fullPath.startsWith(cwd + sep)) return "." + fullPath.slice(cwd.length);
3255
3406
  return fullPath;
3256
3407
  }
3257
- function computeSingleFileSkillHash(contents) {
3258
- const hash = createHash("sha256");
3259
- hash.update("SKILL.md");
3260
- hash.update(contents);
3261
- return hash.digest("hex");
3262
- }
3263
3408
  function formatList$1(items, maxShow = 5) {
3264
3409
  if (items.length <= maxShow) return items.join(", ");
3265
3410
  const shown = items.slice(0, maxShow);
@@ -3409,7 +3554,7 @@ async function selectAgentsInteractive(options) {
3409
3554
  }
3410
3555
  setVersion(version$1);
3411
3556
  async function handleWellKnownSkills(source, url, options, spinner) {
3412
- spinner.start("Discovering skills from well-known endpoint...");
3557
+ spinner.start("Discovering skills from well-known endpoint");
3413
3558
  const skills = await wellKnownProvider.fetchAllSkills(url);
3414
3559
  if (skills.length === 0) {
3415
3560
  spinner.stop(import_picocolors.default.red("No skills found"));
@@ -3459,7 +3604,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3459
3604
  options: skills.map((s) => ({
3460
3605
  value: s,
3461
3606
  label: s.installName,
3462
- hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
3607
+ hint: s.description.length > 60 ? s.description.slice(0, 57) + "" : s.description
3463
3608
  })),
3464
3609
  required: true
3465
3610
  });
@@ -3483,7 +3628,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3483
3628
  }
3484
3629
  targetAgents = options.agent;
3485
3630
  } else {
3486
- spinner.start("Loading agents...");
3631
+ spinner.start("Loading agents");
3487
3632
  const installedAgents = await detectInstalledAgents();
3488
3633
  const totalAgents = Object.keys(agents).length;
3489
3634
  spinner.stop(`${totalAgents} agents`);
@@ -3593,7 +3738,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3593
3738
  }
3594
3739
  const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
3595
3740
  const wellKnownPrivacyPromise = isSourcePrivate(sourceIdentifier).catch(() => null);
3596
- spinner.start("Installing skills...");
3741
+ spinner.start("Installing skills");
3597
3742
  const results = [];
3598
3743
  for (const skill of selectedSkills) for (const agent of targetAgents) {
3599
3744
  const result = await installWellKnownSkillForAgent(skill, agent, {
@@ -3735,7 +3880,7 @@ async function runAdd(args, options = {}) {
3735
3880
  let tempDir = null;
3736
3881
  try {
3737
3882
  const spinner$3 = spinner();
3738
- spinner$3.start("Parsing source...");
3883
+ spinner$3.start("Parsing source");
3739
3884
  const parsed = parseSource(source);
3740
3885
  spinner$3.stop(`Source: ${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
3741
3886
  const ownerRepoRaw = getOwnerRepo(parsed);
@@ -3757,14 +3902,14 @@ async function runAdd(args, options = {}) {
3757
3902
  let skills;
3758
3903
  let blobResult = null;
3759
3904
  if (parsed.type === "local") {
3760
- spinner$3.start("Validating local path...");
3905
+ spinner$3.start("Validating local path");
3761
3906
  if (!existsSync(parsed.localPath)) {
3762
3907
  spinner$3.stop(import_picocolors.default.red("Path not found"));
3763
3908
  outro(import_picocolors.default.red(`Local path does not exist: ${parsed.localPath}`));
3764
3909
  process.exit(1);
3765
3910
  }
3766
3911
  spinner$3.stop("Local path validated");
3767
- spinner$3.start("Discovering skills...");
3912
+ spinner$3.start("Discovering skills");
3768
3913
  skills = await discoverSkills(parsed.localPath, parsed.subpath, {
3769
3914
  includeInternal,
3770
3915
  fullDepth: options.fullDepth
@@ -3779,7 +3924,7 @@ async function runAdd(args, options = {}) {
3779
3924
  const owner = ownerRepo?.split("/")[0]?.toLowerCase();
3780
3925
  const isSelfHostedRepo = !!ownerRepo && Object.hasOwn(BLOB_ALLOWED_REPOS, ownerRepo.toLowerCase());
3781
3926
  if (ownerRepo && owner && (isSelfHostedRepo || BLOB_ALLOWED_OWNERS.includes(owner))) {
3782
- spinner$3.start("Fetching skills...");
3927
+ spinner$3.start("Fetching skills");
3783
3928
  blobResult = await tryBlobInstall(ownerRepo, {
3784
3929
  subpath: parsed.subpath,
3785
3930
  skillFilter: parsed.skillFilter,
@@ -3787,26 +3932,26 @@ async function runAdd(args, options = {}) {
3787
3932
  getToken: getGitHubToken,
3788
3933
  includeInternal
3789
3934
  });
3790
- if (!blobResult) spinner$3.stop(import_picocolors.default.dim("Falling back to clone..."));
3935
+ if (!blobResult) spinner$3.stop(import_picocolors.default.dim("Falling back to clone"));
3791
3936
  }
3792
3937
  if (blobResult) {
3793
3938
  skills = blobResult.skills;
3794
3939
  spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
3795
3940
  } else {
3796
- spinner$3.start("Cloning repository...");
3941
+ spinner$3.start("Cloning repository");
3797
3942
  tempDir = await cloneRepo(parsed.url, parsed.ref);
3798
3943
  spinner$3.stop("Repository cloned");
3799
- spinner$3.start("Discovering skills...");
3944
+ spinner$3.start("Discovering skills");
3800
3945
  skills = await discoverSkills(tempDir, parsed.subpath, {
3801
3946
  includeInternal,
3802
3947
  fullDepth: options.fullDepth
3803
3948
  });
3804
3949
  }
3805
3950
  } else {
3806
- spinner$3.start("Cloning repository...");
3951
+ spinner$3.start("Cloning repository");
3807
3952
  tempDir = await cloneRepo(parsed.url, parsed.ref);
3808
3953
  spinner$3.stop("Repository cloned");
3809
- spinner$3.start("Discovering skills...");
3954
+ spinner$3.start("Discovering skills");
3810
3955
  skills = await discoverSkills(tempDir, parsed.subpath, {
3811
3956
  includeInternal,
3812
3957
  fullDepth: options.fullDepth
@@ -3881,34 +4026,24 @@ async function runAdd(args, options = {}) {
3881
4026
  return getSkillDisplayName(a).localeCompare(getSkillDisplayName(b));
3882
4027
  });
3883
4028
  const hasGroups = sortedSkills.some((s) => s.pluginName);
3884
- let selected;
3885
- if (hasGroups) {
3886
- const kebabToTitle = (s) => s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3887
- const grouped = {};
3888
- for (const s of sortedSkills) {
3889
- const groupName = s.pluginName ? kebabToTitle(s.pluginName) : "Other";
3890
- if (!grouped[groupName]) grouped[groupName] = [];
3891
- grouped[groupName].push({
3892
- value: s,
3893
- label: getSkillDisplayName(s),
3894
- hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
3895
- });
3896
- }
3897
- selected = await groupMultiselect({
3898
- message: `Select skills to install ${import_picocolors.default.dim("(space to toggle)")}`,
3899
- options: grouped,
3900
- required: true
3901
- });
3902
- } else selected = await multiselect({
3903
- message: "Select skills to install",
3904
- options: sortedSkills.map((s) => ({
3905
- value: s,
3906
- label: getSkillDisplayName(s),
3907
- hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
3908
- })),
3909
- required: true
4029
+ const kebabToTitle = (s) => s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
4030
+ const skillChoices = sortedSkills.map((s) => ({
4031
+ value: s,
4032
+ label: getSkillDisplayName(s),
4033
+ group: hasGroups ? s.pluginName ? kebabToTitle(s.pluginName) : "Other" : void 0,
4034
+ detail: s.description
4035
+ }));
4036
+ const selected = await searchMultiselect({
4037
+ message: hasGroups ? `Select skills to install ${import_picocolors.default.dim("(space to toggle)")}` : "Select skills to install",
4038
+ items: skillChoices,
4039
+ required: true,
4040
+ maxVisible: 20,
4041
+ searchable: !hasGroups,
4042
+ showDetail: true,
4043
+ showSelectedSummary: false,
4044
+ selectGroups: hasGroups
3910
4045
  });
3911
- if (isCancel(selected)) {
4046
+ if (isCancelled$1(selected)) {
3912
4047
  cancel("Installation cancelled");
3913
4048
  await cleanup(tempDir);
3914
4049
  process.exit(0);
@@ -3932,7 +4067,7 @@ async function runAdd(args, options = {}) {
3932
4067
  }
3933
4068
  targetAgents = options.agent;
3934
4069
  } else {
3935
- spinner$3.start("Loading agents...");
4070
+ spinner$3.start("Loading agents");
3936
4071
  const installedAgents = await detectInstalledAgents();
3937
4072
  const totalAgents = Object.keys(agents).length;
3938
4073
  spinner$3.stop(`${totalAgents} agents`);
@@ -4135,7 +4270,7 @@ async function runAdd(args, options = {}) {
4135
4270
  process.exit(0);
4136
4271
  }
4137
4272
  }
4138
- spinner$3.start("Installing skills...");
4273
+ spinner$3.start("Installing skills");
4139
4274
  const results = [];
4140
4275
  for (const skill of selectedSkills) for (const target of installTargets) {
4141
4276
  const { agent, subagent } = target;
@@ -4150,17 +4285,7 @@ async function runAdd(args, options = {}) {
4150
4285
  mode: installMode,
4151
4286
  eveSubagent: subagent
4152
4287
  });
4153
- } else if (tempDir && skill.path === tempDir && skill.rawContent) result = await installBlobSkillForAgent({
4154
- installName: skill.name,
4155
- files: [{
4156
- path: "SKILL.md",
4157
- contents: skill.rawContent
4158
- }]
4159
- }, agent, {
4160
- global: installGlobally,
4161
- mode: installMode
4162
- });
4163
- else result = await installSkillForAgent(skill, agent, {
4288
+ } else result = await installSkillForAgent(skill, agent, {
4164
4289
  global: installGlobally,
4165
4290
  mode: installMode,
4166
4291
  eveSubagent: subagent
@@ -4241,7 +4366,7 @@ async function runAdd(args, options = {}) {
4241
4366
  for (const skill of selectedSkills) {
4242
4367
  const skillDisplayName = getSkillDisplayName(skill);
4243
4368
  if (successfulSkillNames.has(skillDisplayName)) try {
4244
- const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : tempDir && skill.path === tempDir && skill.rawContent ? computeSingleFileSkillHash(skill.rawContent) : await computeSkillFolderHash(skill.path);
4369
+ const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : await computeSkillFolderHash(skill.path);
4245
4370
  const skillPathValue = skillFiles[skill.name];
4246
4371
  await addSkillToLocalLock(skill.name, {
4247
4372
  source: lockSource || parsed.url,
@@ -4363,7 +4488,7 @@ async function promptForFindSkills(options, targetAgents) {
4363
4488
  const findSkillsAgents = targetAgents?.filter((a) => a !== "replit");
4364
4489
  if (!findSkillsAgents || findSkillsAgents.length === 0) return;
4365
4490
  console.log();
4366
- log.step("Installing find-skills skill...");
4491
+ log.step("Installing find-skills skill");
4367
4492
  try {
4368
4493
  await runAdd(["vercel-labs/skills"], {
4369
4494
  skill: ["find-skills"],
@@ -4536,7 +4661,7 @@ async function runSearchPrompt(initialQuery = "", owner) {
4536
4661
  lines.push(`${TEXT$2}Search skills:${RESET$3} ${query}${cursor}`);
4537
4662
  lines.push("");
4538
4663
  if (!query || query.length < 2) lines.push(`${DIM$3}Start typing to search (min 2 chars)${RESET$3}`);
4539
- else if (results.length === 0 && loading) lines.push(`${DIM$3}Searching...${RESET$3}`);
4664
+ else if (results.length === 0 && loading) lines.push(`${DIM$3}Searching…${RESET$3}`);
4540
4665
  else if (results.length === 0) lines.push(`${DIM$3}No skills found${RESET$3}`);
4541
4666
  else {
4542
4667
  const visible = results.slice(0, 8);
@@ -4548,7 +4673,7 @@ async function runSearchPrompt(initialQuery = "", owner) {
4548
4673
  const source = skill.source ? ` ${DIM$3}${skill.source}${RESET$3}` : "";
4549
4674
  const installs = formatInstalls(skill.installs);
4550
4675
  const installsBadge = installs ? ` ${CYAN$1}${installs}${RESET$3}` : "";
4551
- const loadingIndicator = loading && i === 0 ? ` ${DIM$3}...${RESET$3}` : "";
4676
+ const loadingIndicator = loading && i === 0 ? ` ${DIM$3}…${RESET$3}` : "";
4552
4677
  lines.push(` ${arrow} ${name}${source}${installsBadge}${loadingIndicator}`);
4553
4678
  }
4554
4679
  }
@@ -4702,7 +4827,7 @@ ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
4702
4827
  const pkg = selected.source || selected.slug;
4703
4828
  const skillName = selected.name;
4704
4829
  console.log();
4705
- console.log(`${TEXT$2}Installing ${BOLD$3}${skillName}${RESET$3} from ${DIM$3}${pkg}${RESET$3}...`);
4830
+ console.log(`${TEXT$2}Installing ${BOLD$3}${skillName}${RESET$3} from ${DIM$3}${pkg}${RESET$3}…`);
4706
4831
  console.log();
4707
4832
  const { source, options: addOptions } = parseAddOptions([
4708
4833
  pkg,
@@ -4805,7 +4930,7 @@ async function runSync(args, options = {}) {
4805
4930
  if (!agentResult.isAgent) intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" skills experimental_sync ")));
4806
4931
  if (agentResult.isAgent) log.info(import_picocolors.default.bgCyan(import_picocolors.default.black(import_picocolors.default.bold(` ${agentResult.agent.name} `))) + " Agent detected — installing non-interactively");
4807
4932
  const spinner$2 = spinner();
4808
- spinner$2.start("Scanning node_modules for skills...");
4933
+ spinner$2.start("Scanning node_modules for skills");
4809
4934
  const discoveredSkills = await discoverNodeModuleSkills(cwd);
4810
4935
  if (discoveredSkills.length === 0) {
4811
4936
  spinner$2.stop(import_picocolors.default.yellow("No skills found"));
@@ -4858,7 +4983,7 @@ async function runSync(args, options = {}) {
4858
4983
  }
4859
4984
  targetAgents = options.agent;
4860
4985
  } else {
4861
- spinner$2.start("Loading agents...");
4986
+ spinner$2.start("Loading agents");
4862
4987
  const installedAgents = await detectInstalledAgents();
4863
4988
  const totalAgents = Object.keys(agents).length;
4864
4989
  spinner$2.stop(`${totalAgents} agents`);
@@ -4932,7 +5057,7 @@ async function runSync(args, options = {}) {
4932
5057
  process.exit(0);
4933
5058
  }
4934
5059
  }
4935
- spinner$2.start("Syncing skills...");
5060
+ spinner$2.start("Syncing skills");
4936
5061
  const results = [];
4937
5062
  for (const skill of toInstall) for (const agent of targetAgents) {
4938
5063
  const result = await installSkillForAgent(skill, agent, {
@@ -5269,7 +5394,7 @@ async function removeCommand(skillNames, options) {
5269
5394
  const isGlobal = options.global ?? false;
5270
5395
  const cwd = process.cwd();
5271
5396
  const spinner$1 = spinner();
5272
- spinner$1.start("Scanning for installed skills...");
5397
+ spinner$1.start("Scanning for installed skills");
5273
5398
  const skillNamesSet = /* @__PURE__ */ new Set();
5274
5399
  const scanDir = async (dir) => {
5275
5400
  try {
@@ -5343,7 +5468,7 @@ async function removeCommand(skillNames, options) {
5343
5468
  process.exit(0);
5344
5469
  }
5345
5470
  }
5346
- spinner$1.start("Removing skills...");
5471
+ spinner$1.start("Removing skills");
5347
5472
  const results = [];
5348
5473
  for (const skillName of selectedSkills) try {
5349
5474
  const canonicalPath = getCanonicalPath(skillName, {
@@ -5594,7 +5719,7 @@ async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills
5594
5719
  else {
5595
5720
  const confirmed = await confirm({ message: `Would you like to remove the local copies of these deleted skills?` });
5596
5721
  if (confirmed && !isCancel(confirmed)) for (const s of deletedSkills) {
5597
- console.log(`${DIM$1}Removing${RESET$1} ${s}...`);
5722
+ console.log(`${DIM$1}Removing${RESET$1} ${s}…`);
5598
5723
  await removeCommand([s], {
5599
5724
  yes: true,
5600
5725
  global: isGlobal
@@ -5728,7 +5853,7 @@ async function updateGlobalSkills(options = {}) {
5728
5853
  console.log();
5729
5854
  for (const update of updates) {
5730
5855
  const safeName = sanitizeMetadata(update.name);
5731
- console.log(`${TEXT$1}Updating ${safeName}...${RESET$1}`);
5856
+ console.log(`${TEXT$1}Updating ${safeName}…${RESET$1}`);
5732
5857
  const installUrl = buildUpdateInstallSource(update.entry);
5733
5858
  if (!installUrl) {
5734
5859
  failCount++;
@@ -5809,7 +5934,7 @@ async function updateProjectSkills(options = {}) {
5809
5934
  if (hasUniversal) targetParts.push("Universal");
5810
5935
  targetParts.push(...targetAgentNames);
5811
5936
  if (targetParts.length > 0) console.log(`${TEXT$1}Updating for: ${targetParts.join(", ")}${RESET$1}`);
5812
- console.log(`${TEXT$1}Refreshing ${updatable.length} skill(s)...${RESET$1}`);
5937
+ console.log(`${TEXT$1}Refreshing ${updatable.length} skill(s)…${RESET$1}`);
5813
5938
  console.log();
5814
5939
  const bySource = /* @__PURE__ */ new Map();
5815
5940
  for (const skill of updatable) {
@@ -5854,7 +5979,7 @@ async function updateProjectSkills(options = {}) {
5854
5979
  const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
5855
5980
  for (const skill of remainingSkills) {
5856
5981
  const safeName = sanitizeMetadata(skill.name);
5857
- console.log(`${TEXT$1}Updating ${safeName}...${RESET$1}`);
5982
+ console.log(`${TEXT$1}Updating ${safeName}…${RESET$1}`);
5858
5983
  const installUrl = buildLocalUpdateSource(skill.entry);
5859
5984
  if (!installUrl) {
5860
5985
  failCount++;
@@ -5908,8 +6033,8 @@ function printLegacyProjectSkills(legacy) {
5908
6033
  async function runUpdate(args = []) {
5909
6034
  const options = parseUpdateOptions(args);
5910
6035
  const scope = await resolveUpdateScope(options);
5911
- if (options.skills) console.log(`${TEXT$1}Updating ${options.skills.join(", ")}...${RESET$1}`);
5912
- else console.log(`${TEXT$1}Checking for skill updates...${RESET$1}`);
6036
+ if (options.skills) console.log(`${TEXT$1}Updating ${options.skills.join(", ")}…${RESET$1}`);
6037
+ else console.log(`${TEXT$1}Checking for skill updates…${RESET$1}`);
5913
6038
  console.log();
5914
6039
  let totalSuccess = 0;
5915
6040
  let totalFail = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills",
3
- "version": "1.5.17",
3
+ "version": "1.5.19",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {
@@ -130,13 +130,13 @@
130
130
  "obuild": "^0.4.22",
131
131
  "picocolors": "^1.1.1",
132
132
  "prettier": "^3.8.1",
133
- "simple-git": "^3.27.0",
133
+ "simple-git": "^3.36.0",
134
134
  "typescript": "^5.9.3",
135
135
  "vitest": "^4.0.17",
136
136
  "xdg-basedir": "^5.1.0"
137
137
  },
138
138
  "engines": {
139
- "node": ">=18"
139
+ "node": ">=22.20.0"
140
140
  },
141
141
  "packageManager": "pnpm@10.17.1",
142
142
  "dependencies": {