skills 1.5.21 → 1.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +39 -9
  2. package/dist/cli.mjs +604 -181
  3. package/package.json +3 -1
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, intro, log, multiselect as multiselect$1, note, outro, select, spinner } from "./_chunks/libs/@clack/prompts.mjs";
4
+ import { cancel, confirm, intro, log, multiselect, 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";
@@ -16,7 +16,7 @@ import { homedir, platform, tmpdir } from "os";
16
16
  import * as readline from "readline";
17
17
  import { Writable } from "stream";
18
18
  import { promisify } from "util";
19
- import { execFile, execSync, spawn, spawnSync } from "child_process";
19
+ import { execFile, spawn, spawnSync } from "child_process";
20
20
  import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
21
21
  import { parse } from "yaml";
22
22
  import { createHash } from "crypto";
@@ -26,6 +26,7 @@ import { crc32, gunzipSync, inflateRawSync } from "node:zlib";
26
26
  import { tmpdir as tmpdir$1 } from "node:os";
27
27
  import { pipeline } from "node:stream/promises";
28
28
  import * as tar from "tar";
29
+ import { execFile as execFile$1 } from "node:child_process";
29
30
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
30
31
  const DEFAULT_GITHUB_HOST = "github.com";
31
32
  function getGitHubHost() {
@@ -416,8 +417,19 @@ function toggleSearchEntry(selected, entry) {
416
417
  } else if (entry?.type === "item") if (selected.has(entry.item.value)) selected.delete(entry.item.value);
417
418
  else selected.add(entry.item.value);
418
419
  }
420
+ function getSelectAllState(selected, items) {
421
+ const selectedCount = items.filter((item) => selected.has(item.value)).length;
422
+ if (selectedCount === 0) return "none";
423
+ if (selectedCount === items.length) return "all";
424
+ return "partial";
425
+ }
426
+ function toggleAllItems(selected, items) {
427
+ const shouldClear = getSelectAllState(selected, items) === "all";
428
+ for (const item of items) if (shouldClear) selected.delete(item.value);
429
+ else selected.add(item.value);
430
+ }
419
431
  async function searchMultiselect(options) {
420
- const { message, items, maxVisible = 8, initialSelected = [], required = false, lockedSection, searchable = true, showDetail = false, detailLines = 2, showSelectedSummary = true, selectGroups = false } = options;
432
+ const { message, items, maxVisible = 8, initialSelected = [], required = false, lockedSection, searchable = true, showDetail = false, detailLines = 2, showSelectedSummary = true, selectGroups = false, selectAll = false } = options;
421
433
  return new Promise((resolve) => {
422
434
  const rl = readline.createInterface({
423
435
  input: process.stdin,
@@ -444,6 +456,8 @@ async function searchMultiselect(options) {
444
456
  const lines = [];
445
457
  const filtered = getFiltered();
446
458
  const entries = buildSearchEntries(filtered, selectGroups, collapsedGroups);
459
+ const hasSelectAll = selectAll && items.length > 0;
460
+ const entryCursor = cursor - (hasSelectAll ? 1 : 0);
447
461
  const icon = state === "active" ? S_STEP_ACTIVE : state === "cancel" ? S_STEP_CANCEL : S_STEP_SUBMIT;
448
462
  lines.push(`${icon} ${import_picocolors.default.bold(message)}`);
449
463
  if (state === "active") {
@@ -462,21 +476,59 @@ async function searchMultiselect(options) {
462
476
  lines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, space select, enter confirm")}`);
463
477
  lines.push(`${S_BAR}`);
464
478
  }
465
- const visibleStart = Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), entries.length - maxVisible));
466
- const visibleEnd = Math.min(entries.length, visibleStart + maxVisible);
467
- const visibleEntries = entries.slice(visibleStart, visibleEnd);
468
- if (filtered.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("No matches found")}`);
469
- else {
479
+ if (hasSelectAll) {
480
+ const selectedCount = items.filter((item) => selected.has(item.value)).length;
481
+ const selectAllState = getSelectAllState(selected, items);
482
+ const radio = selectAllState === "all" ? S_RADIO_ACTIVE : selectAllState === "partial" ? import_picocolors.default.yellow("◐") : S_RADIO_INACTIVE;
483
+ const isCursor = cursor === 0;
484
+ const prefix = isCursor ? import_picocolors.default.cyan("❯") : " ";
485
+ const label = isCursor ? import_picocolors.default.underline(import_picocolors.default.bold("Select All")) : import_picocolors.default.bold("Select All");
486
+ lines.push(`${S_BAR} ${prefix} ${radio} ${label} ${import_picocolors.default.dim(`(${selectedCount}/${items.length})`)}`);
487
+ lines.push(`${S_BAR} ${S_BAR_H.repeat(36)}`);
488
+ }
489
+ const columns = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
490
+ const buildFooterLines = (includeDetail, includeSelectedSummary) => {
491
+ const footerLines = [];
492
+ if (includeDetail) {
493
+ const entry = entries[entryCursor];
494
+ const detail = hasSelectAll && cursor === 0 ? `Select or clear all ${items.length} skills.` : entry?.type === "group" ? `Select all ${entry.items.length} skills in ${entry.group}.` : entry?.item.detail;
495
+ const detailWidth = Math.max(1, columns - 5);
496
+ footerLines.push(`${S_BAR}`);
497
+ footerLines.push(`${S_BAR} ${import_picocolors.default.dim("Description")}`);
498
+ for (const line of formatDetailLines(detail, detailWidth, detailLines)) footerLines.push(`${S_BAR} ${import_picocolors.default.dim(line)}`);
499
+ }
500
+ if (includeSelectedSummary) {
501
+ footerLines.push(`${S_BAR}`);
502
+ const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
503
+ if (allSelectedLabels.length === 0) footerLines.push(`${S_BAR} ${import_picocolors.default.dim("Selected: (none)")}`);
504
+ else {
505
+ const summary = allSelectedLabels.length <= 3 ? allSelectedLabels.join(", ") : `${allSelectedLabels.slice(0, 3).join(", ")} +${allSelectedLabels.length - 3} more`;
506
+ footerLines.push(`${S_BAR} ${import_picocolors.default.green("Selected:")} ${summary}`);
507
+ }
508
+ }
509
+ if (!searchable) {
510
+ footerLines.push(`${S_BAR}`);
511
+ footerLines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, ←→ collapse/expand, space select, enter confirm")}`);
512
+ }
513
+ footerLines.push(`${import_picocolors.default.dim("└")}`);
514
+ return footerLines;
515
+ };
516
+ const buildItemLines = (visibleLimit) => {
517
+ if (filtered.length === 0) return [`${S_BAR} ${import_picocolors.default.dim("No matches found")}`];
518
+ const itemLines = [];
519
+ const visibleStart = Math.max(0, Math.min(entryCursor - Math.floor(visibleLimit / 2), entries.length - visibleLimit));
520
+ const visibleEnd = Math.min(entries.length, visibleStart + visibleLimit);
521
+ const visibleEntries = entries.slice(visibleStart, visibleEnd);
470
522
  for (let i = 0; i < visibleEntries.length; i++) {
471
523
  const entry = visibleEntries[i];
472
- const isCursor = visibleStart + i === cursor;
524
+ const isCursor = visibleStart + i === entryCursor;
473
525
  if (entry.type === "group") {
474
526
  const selectedCount = entry.items.filter((item) => selected.has(item.value)).length;
475
527
  const radio = selectedCount === entry.items.length ? S_RADIO_ACTIVE : selectedCount > 0 ? import_picocolors.default.yellow("◐") : S_RADIO_INACTIVE;
476
528
  const label = isCursor ? import_picocolors.default.underline(import_picocolors.default.bold(entry.group)) : import_picocolors.default.bold(entry.group);
477
529
  const prefix = isCursor ? import_picocolors.default.cyan("❯") : " ";
478
530
  const disclosure = import_picocolors.default.dim(entry.collapsed ? "▸" : "▾");
479
- lines.push(`${S_BAR} ${prefix} ${disclosure} ${radio} ${label}`);
531
+ itemLines.push(`${S_BAR} ${prefix} ${disclosure} ${radio} ${label}`);
480
532
  continue;
481
533
  }
482
534
  const item = entry.item;
@@ -487,7 +539,7 @@ async function searchMultiselect(options) {
487
539
  const groupItems = selectGroups && item.group ? filtered.filter((i) => i.group === item.group) : [];
488
540
  const isLastInGroup = groupItems.at(-1) === item;
489
541
  const tree = groupItems.length > 0 ? `${import_picocolors.default.dim(isLastInGroup ? "└─" : "├─")} ` : "";
490
- lines.push(`${S_BAR} ${prefix} ${tree}${radio} ${label}${hint}`);
542
+ itemLines.push(`${S_BAR} ${prefix} ${tree}${radio} ${label}${hint}`);
491
543
  }
492
544
  const hiddenBefore = visibleStart;
493
545
  const hiddenAfter = entries.length - visibleEnd;
@@ -495,32 +547,48 @@ async function searchMultiselect(options) {
495
547
  const parts = [];
496
548
  if (hiddenBefore > 0) parts.push(`↑ ${hiddenBefore} more`);
497
549
  if (hiddenAfter > 0) parts.push(`↓ ${hiddenAfter} more`);
498
- lines.push(`${S_BAR} ${import_picocolors.default.dim(parts.join(" "))}`);
550
+ itemLines.push(`${S_BAR} ${import_picocolors.default.dim(parts.join(" "))}`);
499
551
  }
500
- }
501
- if (showDetail) {
502
- const entry = entries[cursor];
503
- const detail = entry?.type === "group" ? `Select all ${entry.items.length} skills in ${entry.group}.` : entry?.item.detail;
504
- const columns = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
505
- const detailWidth = Math.max(1, columns - 5);
506
- lines.push(`${S_BAR}`);
507
- lines.push(`${S_BAR} ${import_picocolors.default.dim("Description")}`);
508
- for (const line of formatDetailLines(detail, detailWidth, detailLines)) lines.push(`${S_BAR} ${import_picocolors.default.dim(line)}`);
509
- }
510
- if (showSelectedSummary) {
511
- lines.push(`${S_BAR}`);
512
- const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
513
- if (allSelectedLabels.length === 0) lines.push(`${S_BAR} ${import_picocolors.default.dim("Selected: (none)")}`);
514
- else {
515
- const summary = allSelectedLabels.length <= 3 ? allSelectedLabels.join(", ") : `${allSelectedLabels.slice(0, 3).join(", ")} +${allSelectedLabels.length - 3} more`;
516
- lines.push(`${S_BAR} ${import_picocolors.default.green("Selected:")} ${summary}`);
552
+ return itemLines;
553
+ };
554
+ const terminalRows = process.stdout.rows && process.stdout.rows > 0 ? process.stdout.rows : void 0;
555
+ const maxFrameRows = terminalRows ? Math.max(1, terminalRows - 1) : void 0;
556
+ const fitFrame = (includeDetail, includeSelectedSummary) => {
557
+ const footerLines = buildFooterLines(includeDetail, includeSelectedSummary);
558
+ let visibleLimit = Math.max(1, maxVisible);
559
+ let itemLines = buildItemLines(visibleLimit);
560
+ let frameRows = countVisualRowsForLines([
561
+ ...lines,
562
+ ...itemLines,
563
+ ...footerLines
564
+ ], columns);
565
+ while (maxFrameRows && frameRows > maxFrameRows && visibleLimit > 1) {
566
+ visibleLimit -= 1;
567
+ itemLines = buildItemLines(visibleLimit);
568
+ frameRows = countVisualRowsForLines([
569
+ ...lines,
570
+ ...itemLines,
571
+ ...footerLines
572
+ ], columns);
517
573
  }
574
+ return {
575
+ itemLines,
576
+ footerLines,
577
+ frameRows
578
+ };
579
+ };
580
+ let includeDetail = showDetail;
581
+ let includeSelectedSummary = showSelectedSummary;
582
+ let fitted = fitFrame(includeDetail, includeSelectedSummary);
583
+ if (maxFrameRows && fitted.frameRows > maxFrameRows && includeDetail) {
584
+ includeDetail = false;
585
+ fitted = fitFrame(includeDetail, includeSelectedSummary);
518
586
  }
519
- if (!searchable) {
520
- lines.push(`${S_BAR}`);
521
- lines.push(`${S_BAR} ${import_picocolors.default.dim("↑↓ move, ←→ collapse/expand, space select, enter confirm")}`);
587
+ if (maxFrameRows && fitted.frameRows > maxFrameRows && includeSelectedSummary) {
588
+ includeSelectedSummary = false;
589
+ fitted = fitFrame(includeDetail, includeSelectedSummary);
522
590
  }
523
- lines.push(`${import_picocolors.default.dim("└")}`);
591
+ lines.push(...fitted.itemLines, ...fitted.footerLines);
524
592
  } else if (state === "submit") {
525
593
  const allSelectedLabels = [...lockedSection ? lockedSection.items.map((i) => i.label) : [], ...items.filter((item) => selected.has(item.value)).map((item) => item.label)];
526
594
  lines.push(`${S_BAR} ${import_picocolors.default.dim(allSelectedLabels.join(", "))}`);
@@ -548,6 +616,9 @@ async function searchMultiselect(options) {
548
616
  const keypressHandler = (_str, key) => {
549
617
  if (!key) return;
550
618
  const entries = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups);
619
+ const hasSelectAll = selectAll && items.length > 0;
620
+ const cursorOffset = hasSelectAll ? 1 : 0;
621
+ const entry = entries[cursor - cursorOffset];
551
622
  if (key.name === "return") {
552
623
  submit();
553
624
  return;
@@ -562,12 +633,11 @@ async function searchMultiselect(options) {
562
633
  return;
563
634
  }
564
635
  if (key.name === "down") {
565
- cursor = Math.min(entries.length - 1, cursor + 1);
636
+ cursor = Math.min(entries.length + cursorOffset - 1, cursor + 1);
566
637
  render();
567
638
  return;
568
639
  }
569
640
  if (selectGroups && key.name === "right") {
570
- const entry = entries[cursor];
571
641
  if (entry?.type === "group" && entry.collapsed) {
572
642
  collapsedGroups.delete(entry.group);
573
643
  render();
@@ -575,18 +645,17 @@ async function searchMultiselect(options) {
575
645
  return;
576
646
  }
577
647
  if (selectGroups && key.name === "left") {
578
- const entry = entries[cursor];
579
648
  const group = entry?.type === "group" ? entry.group : entry?.item.group;
580
649
  if (group) {
581
650
  collapsedGroups.add(group);
582
- cursor = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups).findIndex((collapsedEntry) => collapsedEntry.type === "group" && collapsedEntry.group === group);
651
+ cursor = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups).findIndex((collapsedEntry) => collapsedEntry.type === "group" && collapsedEntry.group === group) + cursorOffset;
583
652
  render();
584
653
  }
585
654
  return;
586
655
  }
587
656
  if (key.name === "space") {
588
- const entry = entries[cursor];
589
- toggleSearchEntry(selected, entry);
657
+ if (hasSelectAll && cursor === 0) toggleAllItems(selected, items);
658
+ else toggleSearchEntry(selected, entry);
590
659
  render();
591
660
  return;
592
661
  }
@@ -811,6 +880,38 @@ async function cloneRepo(url, ref) {
811
880
  throw new GitCloneError(`Failed to clone ${url}: ${errorMessage}`, url, false, false);
812
881
  }
813
882
  }
883
+ async function getGitTreeHash(repoDir, skillPath) {
884
+ const segments = skillPath.replace(/\\/g, "/").split("/");
885
+ segments.pop();
886
+ const folderPath = segments.join("/");
887
+ const revision = folderPath ? `HEAD:${folderPath}` : "HEAD^{tree}";
888
+ try {
889
+ const hash = (await new Promise((resolve, reject) => {
890
+ execFile("git", [
891
+ "-C",
892
+ repoDir,
893
+ "rev-parse",
894
+ "--verify",
895
+ "--end-of-options",
896
+ revision
897
+ ], {
898
+ encoding: "utf8",
899
+ timeout: CLONE_TIMEOUT_MS,
900
+ env: {
901
+ ...process.env,
902
+ GIT_OPTIONAL_LOCKS: "0",
903
+ GIT_TERMINAL_PROMPT: "0"
904
+ }
905
+ }, (error, output) => {
906
+ if (error) reject(error);
907
+ else resolve(output);
908
+ });
909
+ })).trim();
910
+ return /^[0-9a-f]{40}$/i.test(hash) ? hash.toLowerCase() : null;
911
+ } catch {
912
+ return null;
913
+ }
914
+ }
814
915
  async function cleanupTempDir(dir) {
815
916
  const normalizedDir = normalize(resolve(dir));
816
917
  const normalizedTmpDir = normalize(resolve(tmpdir()));
@@ -902,27 +1003,45 @@ function getLocalLockPath(cwd) {
902
1003
  return join(cwd || process.cwd(), LOCAL_LOCK_FILE);
903
1004
  }
904
1005
  async function readLocalLock(cwd) {
905
- const lockPath = getLocalLockPath(cwd);
1006
+ const lockDir = cwd || process.cwd();
1007
+ const lockPath = getLocalLockPath(lockDir);
906
1008
  try {
907
1009
  const content = await readFile(lockPath, "utf-8");
908
1010
  const parsed = JSON.parse(content);
909
1011
  if (typeof parsed.version !== "number" || !parsed.skills) return createEmptyLocalLock();
910
1012
  if (parsed.version < CURRENT_VERSION$1) return createEmptyLocalLock();
1013
+ for (const entry of Object.values(parsed.skills)) if (entry.sourceType === "local" && !isAbsolute(entry.source)) entry.source = resolve(lockDir, entry.source);
911
1014
  return parsed;
912
1015
  } catch {
913
1016
  return createEmptyLocalLock();
914
1017
  }
915
1018
  }
916
1019
  async function writeLocalLock(lock, cwd) {
917
- const lockPath = getLocalLockPath(cwd);
1020
+ const lockDir = cwd || process.cwd();
1021
+ const lockPath = getLocalLockPath(lockDir);
918
1022
  const sortedSkills = {};
919
- for (const key of Object.keys(lock.skills).sort()) sortedSkills[key] = lock.skills[key];
1023
+ for (const key of Object.keys(lock.skills).sort()) {
1024
+ const entry = lock.skills[key];
1025
+ sortedSkills[key] = entry.sourceType === "local" ? {
1026
+ ...entry,
1027
+ source: getPortableLocalSource(entry.source, lockDir)
1028
+ } : entry;
1029
+ }
920
1030
  const sorted = {
921
1031
  version: lock.version,
922
1032
  skills: sortedSkills
923
1033
  };
924
1034
  await writeFile(lockPath, JSON.stringify(sorted, null, 2) + "\n", "utf-8");
925
1035
  }
1036
+ function getPortableLocalSource(source, lockDir) {
1037
+ const absoluteSource = isAbsolute(source) ? source : resolve(lockDir, source);
1038
+ const relativeSource = relative(lockDir, absoluteSource);
1039
+ if (isAbsolute(relativeSource)) return absoluteSource.split(sep).join("/");
1040
+ const portableSource = relativeSource.split(sep).join("/");
1041
+ if (!portableSource) return ".";
1042
+ if (portableSource === ".." || portableSource.startsWith("../")) return portableSource;
1043
+ return `./${portableSource}`;
1044
+ }
926
1045
  async function computeSkillFolderHash(skillDir) {
927
1046
  const files = [];
928
1047
  await collectFiles(skillDir, skillDir, files);
@@ -969,6 +1088,8 @@ function createEmptyLocalLock() {
969
1088
  skills: {}
970
1089
  };
971
1090
  }
1091
+ const AGENTS_DIR$1 = ".agents";
1092
+ const SKILLS_SUBDIR = "skills";
972
1093
  const SKIP_DIRS = [
973
1094
  "node_modules",
974
1095
  ".git",
@@ -992,11 +1113,13 @@ const AGENT_PROJECT_SKILL_DIRS = [
992
1113
  ".kilocode/skills",
993
1114
  ".kimchi/skills",
994
1115
  ".kiro/skills",
1116
+ ".minimax/skills",
995
1117
  ".mux/skills",
996
1118
  ".neovate/skills",
997
1119
  ".opencode/skills",
998
1120
  ".openhands/skills",
999
1121
  ".pi/skills",
1122
+ ".posit/assistant/skills",
1000
1123
  ".qoder/skills",
1001
1124
  ".roo/skills",
1002
1125
  ".trae/skills",
@@ -1138,25 +1261,18 @@ async function discoverSkills(basePath, subpath, options) {
1138
1261
  seenNames.add(skill.name);
1139
1262
  return true;
1140
1263
  };
1141
- for (const dir of prioritySearchDirs) {
1142
- const walkDeep = deepContainerDirs.has(dir);
1264
+ const walkSkillDirs = async (dir, maxDepth, depth = 1) => {
1143
1265
  try {
1144
1266
  const entries = await readdir(dir, { withFileTypes: true });
1145
1267
  for (const entry of entries) {
1146
1268
  if (!entry.isDirectory()) continue;
1147
1269
  const childDir = join(dir, entry.name);
1148
- if (await tryAddSkillAt(childDir) || !walkDeep) continue;
1149
- if (SKIP_DIRS.includes(entry.name)) continue;
1150
- try {
1151
- const grandEntries = await readdir(childDir, { withFileTypes: true });
1152
- for (const grand of grandEntries) {
1153
- if (!grand.isDirectory() || SKIP_DIRS.includes(grand.name)) continue;
1154
- await tryAddSkillAt(join(childDir, grand.name));
1155
- }
1156
- } catch {}
1270
+ if (await tryAddSkillAt(childDir) || depth >= maxDepth || SKIP_DIRS.includes(entry.name)) continue;
1271
+ await walkSkillDirs(childDir, maxDepth, depth + 1);
1157
1272
  }
1158
1273
  } catch {}
1159
- }
1274
+ };
1275
+ for (const dir of prioritySearchDirs) await walkSkillDirs(dir, deepContainerDirs.has(dir) ? 3 : 1);
1160
1276
  if (skills.length === 0 || options?.fullDepth) {
1161
1277
  const allSkillDirs = await findSkillDirs(searchPath);
1162
1278
  for (const skillDir of allSkillDirs) {
@@ -1211,6 +1327,12 @@ function isZCodeInstalled(homeDir = home, pathExists = existsSync) {
1211
1327
  function isKimchiInstalled(homeDir = home, pathExists = existsSync) {
1212
1328
  return pathExists(join(homeDir, ".config", "kimchi"));
1213
1329
  }
1330
+ function isMiniMaxCodeInstalled(homeDir = home, pathExists = existsSync) {
1331
+ return pathExists(join(homeDir, ".minimax")) || pathExists("/Applications/MiniMax Code.app");
1332
+ }
1333
+ function isPositAssistantInstalled(homeDir = home, pathExists = existsSync) {
1334
+ return pathExists(join(homeDir, ".posit/assistant")) || pathExists(join(homeDir, ".positai"));
1335
+ }
1214
1336
  const agents = {
1215
1337
  "aider-desk": {
1216
1338
  name: "aider-desk",
@@ -1621,6 +1743,15 @@ const agents = {
1621
1743
  return existsSync(join(home, ".mcpjam"));
1622
1744
  }
1623
1745
  },
1746
+ "minimax-code": {
1747
+ name: "minimax-code",
1748
+ displayName: "MiniMax Code",
1749
+ skillsDir: ".minimax/skills",
1750
+ globalSkillsDir: join(home, ".minimax/skills"),
1751
+ detectInstalled: async () => {
1752
+ return isMiniMaxCodeInstalled();
1753
+ }
1754
+ },
1624
1755
  "mistral-vibe": {
1625
1756
  name: "mistral-vibe",
1626
1757
  displayName: "Mistral Vibe",
@@ -1684,6 +1815,15 @@ const agents = {
1684
1815
  return existsSync(join(home, ".pi/agent"));
1685
1816
  }
1686
1817
  },
1818
+ "posit-assistant": {
1819
+ name: "posit-assistant",
1820
+ displayName: "Posit Assistant",
1821
+ skillsDir: ".posit/assistant/skills",
1822
+ globalSkillsDir: join(home, ".posit/assistant/skills"),
1823
+ detectInstalled: async () => {
1824
+ return isPositAssistantInstalled();
1825
+ }
1826
+ },
1687
1827
  qoder: {
1688
1828
  name: "qoder",
1689
1829
  displayName: "Qoder",
@@ -1921,8 +2061,6 @@ function getNonUniversalAgents() {
1921
2061
  function isUniversalAgent(type) {
1922
2062
  return agents[type].skillsDir === ".agents/skills";
1923
2063
  }
1924
- const AGENTS_DIR$1 = ".agents";
1925
- const SKILLS_SUBDIR = "skills";
1926
2064
  function sanitizeName(name) {
1927
2065
  return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "").substring(0, 255) || "unnamed-skill";
1928
2066
  }
@@ -2543,6 +2681,7 @@ function setVersion(version) {
2543
2681
  cliVersion = version;
2544
2682
  }
2545
2683
  async function fetchAuditData(source, skillSlugs, timeoutMs = 3e3) {
2684
+ if (!isEnabled()) return null;
2546
2685
  if (skillSlugs.length === 0) return null;
2547
2686
  try {
2548
2687
  const params = new URLSearchParams({
@@ -2851,6 +2990,16 @@ const DISCOVERY_SCHEMA_V2 = "https://schemas.agentskills.io/discovery/0.2.0/sche
2851
2990
  const MAX_ARCHIVE_UNPACKED_BYTES = 50 * 1024 * 1024;
2852
2991
  const MAX_ARCHIVE_FILES = 1e3;
2853
2992
  const DISCOVERY_TIMEOUT_MS = 1e4;
2993
+ var WellKnownScopeNotFoundError = class extends Error {
2994
+ scopePath;
2995
+ rootUrl;
2996
+ constructor(scopePath, rootUrl) {
2997
+ super(`No skills found for the scoped path '${scopePath}' on ${rootUrl}. Not falling back to the root skills index because that would install every skill the host publishes. Check the URL, or run 'skills add ${rootUrl}' to install from the root index.`);
2998
+ this.name = "WellKnownScopeNotFoundError";
2999
+ this.scopePath = scopePath;
3000
+ this.rootUrl = rootUrl;
3001
+ }
3002
+ };
2854
3003
  var WellKnownProvider = class {
2855
3004
  id = "well-known";
2856
3005
  displayName = "Well-Known Skills";
@@ -2873,10 +3022,10 @@ var WellKnownProvider = class {
2873
3022
  return { matches: false };
2874
3023
  }
2875
3024
  }
2876
- async fetchIndex(baseUrl) {
2877
- return (await this.fetchIndexCandidates(baseUrl))[0] ?? null;
3025
+ async fetchIndex(baseUrl, options) {
3026
+ return (await this.fetchIndexCandidates(baseUrl, options))[0] ?? null;
2878
3027
  }
2879
- async fetchIndexCandidates(baseUrl) {
3028
+ async fetchIndexCandidates(baseUrl, options) {
2880
3029
  try {
2881
3030
  const parsed = new URL(baseUrl);
2882
3031
  const basePath = parsed.pathname.replace(/\/$/, "");
@@ -2896,7 +3045,10 @@ var WellKnownProvider = class {
2896
3045
  }
2897
3046
  const candidates = [];
2898
3047
  for (const { indexUrl, baseUrl: resolvedBase, wellKnownPath } of urlsToTry) try {
2899
- const response = await fetch(indexUrl, { signal });
3048
+ const response = await fetch(indexUrl, {
3049
+ signal,
3050
+ ...options?.updateCheck ? { headers: { "X-Skills-Update-Check": "1" } } : {}
3051
+ });
2900
3052
  if (!response.ok) continue;
2901
3053
  const rawIndex = await response.json();
2902
3054
  const normalized = this.normalizeIndex(rawIndex, indexUrl, wellKnownPath);
@@ -3146,16 +3298,34 @@ var WellKnownProvider = class {
3146
3298
  indexEntry: input.indexEntry
3147
3299
  };
3148
3300
  }
3149
- async fetchAllSkills(url) {
3301
+ getScope(url) {
3302
+ try {
3303
+ const parsed = new URL(url);
3304
+ const scopePath = parsed.pathname.replace(/\/$/, "");
3305
+ if (!scopePath) return null;
3306
+ return {
3307
+ scopePath,
3308
+ rootBaseUrl: `${parsed.protocol}//${parsed.host}`
3309
+ };
3310
+ } catch {
3311
+ return null;
3312
+ }
3313
+ }
3314
+ async fetchAllSkills(url, options = {}) {
3150
3315
  try {
3151
3316
  const candidates = await this.fetchIndexCandidates(url);
3152
- for (const result of candidates) {
3317
+ const scope = this.getScope(url);
3318
+ const scopedCandidates = scope ? candidates.filter((c) => c.resolvedBaseUrl !== scope.rootBaseUrl) : candidates;
3319
+ const includeInternal = options.includeInternal || shouldInstallInternalSkills();
3320
+ for (const result of scopedCandidates) {
3153
3321
  const skillPromises = result.entries.map((entry) => this.fetchSkillByEntry(entry));
3154
- const skills = (await Promise.all(skillPromises)).filter((s) => s !== null);
3322
+ const skills = (await Promise.all(skillPromises)).filter((s) => s !== null).filter((skill) => includeInternal || skill.metadata?.internal !== true);
3155
3323
  if (skills.length > 0) return skills;
3156
3324
  }
3325
+ if (scope && scopedCandidates.length < candidates.length) throw new WellKnownScopeNotFoundError(scope.scopePath, scope.rootBaseUrl);
3157
3326
  return [];
3158
- } catch {
3327
+ } catch (error) {
3328
+ if (error instanceof WellKnownScopeNotFoundError) throw error;
3159
3329
  return [];
3160
3330
  }
3161
3331
  }
@@ -3253,6 +3423,17 @@ var WellKnownProvider = class {
3253
3423
  return await this.fetchIndex(url) !== null;
3254
3424
  }
3255
3425
  };
3426
+ function computeWellKnownSkillDigest(skill) {
3427
+ if ("digest" in skill.indexEntry && skill.indexEntry.digest) return skill.indexEntry.digest;
3428
+ const hash = createHash$1("sha256");
3429
+ for (const path of Array.from(skill.files.keys()).sort()) {
3430
+ hash.update(path);
3431
+ hash.update("\0");
3432
+ hash.update(skill.files.get(path));
3433
+ hash.update("\0");
3434
+ }
3435
+ return `sha256:${hash.digest("hex")}`;
3436
+ }
3256
3437
  const wellKnownProvider = new WellKnownProvider();
3257
3438
  const DEFAULT_DOWNLOAD_MAX_BYTES = 10 * 1024 * 1024;
3258
3439
  const DEFAULT_EXTRACT_MAX_BYTES = 25 * 1024 * 1024;
@@ -3460,25 +3641,9 @@ async function writeSkillLock(lock) {
3460
3641
  await mkdir(dirname(lockPath), { recursive: true });
3461
3642
  await writeFile(lockPath, JSON.stringify(lock, null, 2), "utf-8");
3462
3643
  }
3463
- let _ghWarningShown = false;
3464
3644
  function getGitHubToken() {
3465
3645
  if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
3466
3646
  if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
3467
- if (!_ghWarningShown) {
3468
- 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")}`);
3469
- _ghWarningShown = true;
3470
- }
3471
- try {
3472
- const token = execSync("gh auth token", {
3473
- encoding: "utf-8",
3474
- stdio: [
3475
- "pipe",
3476
- "pipe",
3477
- "pipe"
3478
- ]
3479
- }).trim();
3480
- if (token) return token;
3481
- } catch {}
3482
3647
  return null;
3483
3648
  }
3484
3649
  async function addSkillToLock(skillName, entry) {
@@ -3532,13 +3697,15 @@ async function saveSelectedAgents(agents) {
3532
3697
  const DOWNLOAD_BASE_URL = process.env.SKILLS_DOWNLOAD_URL || "https://skills.sh";
3533
3698
  const BLOB_ALLOWED_REPOS = { "zapier/connectors": { downloadUrl: (slug) => `https://connectors-skills.zapier.com/download/${encodeURIComponent(slug)}/snapshot.json` } };
3534
3699
  const FETCH_TIMEOUT = 1e4;
3700
+ const GH_API_MAX_BUFFER = 16 * 1024 * 1024;
3535
3701
  function toSkillSlug(name) {
3536
3702
  return name.toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
3537
3703
  }
3538
3704
  let _rateLimitedThisSession = false;
3539
3705
  async function fetchTreeBranch(ownerRepo, branch, token) {
3540
3706
  try {
3541
- const url = `https://api.github.com/repos/${ownerRepo}/git/trees/${encodeURIComponent(branch)}?recursive=1`;
3707
+ const githubHost = getGitHubHost();
3708
+ const url = `${githubHost === "github.com" ? "https://api.github.com" : `https://${githubHost}/api/v3`}/repos/${ownerRepo}/git/trees/${encodeURIComponent(branch)}?recursive=1`;
3542
3709
  const headers = {
3543
3710
  Accept: "application/vnd.github.v3+json",
3544
3711
  "User-Agent": "skills-cli"
@@ -3582,13 +3749,55 @@ async function fetchTreeWithToken(ownerRepo, branches, getToken) {
3582
3749
  }
3583
3750
  return null;
3584
3751
  }
3752
+ async function fetchTreeWithGitHubCli(ownerRepo, branches) {
3753
+ for (const branch of branches) try {
3754
+ const endpoint = `repos/${ownerRepo}/git/trees/${encodeURIComponent(branch)}?recursive=1`;
3755
+ const stdout = await new Promise((resolve, reject) => {
3756
+ execFile$1("gh", [
3757
+ "api",
3758
+ endpoint,
3759
+ "--method",
3760
+ "GET",
3761
+ "--hostname",
3762
+ getGitHubHost()
3763
+ ], {
3764
+ encoding: "utf8",
3765
+ timeout: FETCH_TIMEOUT,
3766
+ maxBuffer: GH_API_MAX_BUFFER,
3767
+ windowsHide: true,
3768
+ env: {
3769
+ ...process.env,
3770
+ GH_PROMPT_DISABLED: "1"
3771
+ }
3772
+ }, (error, output) => {
3773
+ if (error) reject(error);
3774
+ else resolve(output);
3775
+ });
3776
+ });
3777
+ const data = JSON.parse(stdout);
3778
+ if (typeof data.sha !== "string" || !Array.isArray(data.tree)) continue;
3779
+ return {
3780
+ sha: data.sha,
3781
+ branch,
3782
+ tree: data.tree
3783
+ };
3784
+ } catch {}
3785
+ return null;
3786
+ }
3787
+ async function fetchTreeWithAvailableAuth(ownerRepo, branches, getToken) {
3788
+ if (getToken) {
3789
+ const tree = await fetchTreeWithToken(ownerRepo, branches, getToken);
3790
+ if (tree) return tree;
3791
+ }
3792
+ return fetchTreeWithGitHubCli(ownerRepo, branches);
3793
+ }
3585
3794
  async function fetchRepoTree(ownerRepo, ref, getToken) {
3586
3795
  const branches = ref ? [ref] : [
3587
3796
  "HEAD",
3588
3797
  "main",
3589
3798
  "master"
3590
3799
  ];
3591
- if (_rateLimitedThisSession && getToken) return fetchTreeWithToken(ownerRepo, branches, getToken);
3800
+ if (_rateLimitedThisSession) return fetchTreeWithAvailableAuth(ownerRepo, branches, getToken);
3592
3801
  let rateLimited = false;
3593
3802
  let authRetryable = false;
3594
3803
  for (const branch of branches) {
@@ -3603,9 +3812,9 @@ async function fetchRepoTree(ownerRepo, ref, getToken) {
3603
3812
  break;
3604
3813
  }
3605
3814
  }
3606
- if (!getToken || !(rateLimited || authRetryable)) return null;
3815
+ if (!(rateLimited || authRetryable)) return null;
3607
3816
  if (rateLimited) _rateLimitedThisSession = true;
3608
- return fetchTreeWithToken(ownerRepo, branches, getToken);
3817
+ return fetchTreeWithAvailableAuth(ownerRepo, branches, getToken);
3609
3818
  }
3610
3819
  function getSkillFolderHashFromTree(tree, skillPath) {
3611
3820
  let folderPath = skillPath.replace(/\\/g, "/");
@@ -3636,11 +3845,13 @@ const PRIORITY_PREFIXES = [
3636
3845
  ".kilocode/skills/",
3637
3846
  ".kimchi/skills/",
3638
3847
  ".kiro/skills/",
3848
+ ".minimax/skills/",
3639
3849
  ".mux/skills/",
3640
3850
  ".neovate/skills/",
3641
3851
  ".opencode/skills/",
3642
3852
  ".openhands/skills/",
3643
3853
  ".pi/skills/",
3854
+ ".posit/assistant/skills/",
3644
3855
  ".qoder/skills/",
3645
3856
  ".roo/skills/",
3646
3857
  ".trae/skills/",
@@ -3684,9 +3895,13 @@ function findSkillMdPaths(tree, subpath) {
3684
3895
  }
3685
3896
  continue;
3686
3897
  }
3687
- if (isContainer && parts.length === 3 && parts[2].toLowerCase() === "skill.md" && !SKIP_DIRS.has(parts[0]) && !SKIP_DIRS.has(parts[1])) {
3688
- const parentSkillMd = `${fullPrefix}${parts[0]}/SKILL.md`.toLowerCase();
3689
- if (!lowerSkillMdSet.has(parentSkillMd) && !seen.has(skillMd)) {
3898
+ const skillDirs = parts.slice(0, -1);
3899
+ const hasAncestorSkill = skillDirs.slice(0, -1).some((_, index) => {
3900
+ const ancestorPath = skillDirs.slice(0, index + 1).join("/");
3901
+ return lowerSkillMdSet.has(`${fullPrefix}${ancestorPath}/SKILL.md`.toLowerCase());
3902
+ });
3903
+ if (isContainer && parts.length >= 3 && parts.length <= 4 && parts.at(-1).toLowerCase() === "skill.md" && skillDirs.every((part) => !SKIP_DIRS.has(part)) && !hasAncestorSkill) {
3904
+ if (!seen.has(skillMd)) {
3690
3905
  priorityResults.push(skillMd);
3691
3906
  seen.add(skillMd);
3692
3907
  }
@@ -3729,6 +3944,7 @@ function computeSnapshotHash(files) {
3729
3944
  return hash.digest("hex");
3730
3945
  }
3731
3946
  async function tryBlobInstall(ownerRepo, options = {}) {
3947
+ if (options.ref !== void 0) return null;
3732
3948
  const tree = await fetchRepoTree(ownerRepo, options.ref, options.getToken);
3733
3949
  if (!tree) return null;
3734
3950
  let skillMdPaths = findSkillMdPaths(tree, options.subpath);
@@ -3801,7 +4017,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
3801
4017
  tree
3802
4018
  };
3803
4019
  }
3804
- var version$1 = "1.5.21";
4020
+ var version$1 = "1.5.23";
3805
4021
  const isCancelled$1 = (value) => typeof value === "symbol";
3806
4022
  const EVE_AGENT_LABEL = "eve agent";
3807
4023
  async function isSourcePrivate(source) {
@@ -3954,13 +4170,6 @@ function buildResultLines(results, targetAgents) {
3954
4170
  if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(failedSymlinks)}`);
3955
4171
  return lines;
3956
4172
  }
3957
- function multiselect(opts) {
3958
- return multiselect$1({
3959
- ...opts,
3960
- options: opts.options,
3961
- message: `${opts.message} ${import_picocolors.default.dim("(space to toggle)")}`
3962
- });
3963
- }
3964
4173
  async function promptForAgents(message, choices) {
3965
4174
  let lastSelected;
3966
4175
  try {
@@ -4020,9 +4229,26 @@ async function selectAgentsInteractive(options) {
4020
4229
  return selected;
4021
4230
  }
4022
4231
  setVersion(version$1);
4232
+ function isSkillsShPackUrl(url) {
4233
+ try {
4234
+ const parsed = new URL(url);
4235
+ return parsed.hostname.replace(/^www\./, "") === "skills.sh" && /^\/p\/[^/]+/.test(parsed.pathname);
4236
+ } catch {
4237
+ return false;
4238
+ }
4239
+ }
4023
4240
  async function handleWellKnownSkills(source, url, options, spinner) {
4024
4241
  spinner.start("Discovering skills from well-known endpoint...");
4025
- const skills = await wellKnownProvider.fetchAllSkills(url).catch(() => []);
4242
+ let skills = [];
4243
+ try {
4244
+ skills = await wellKnownProvider.fetchAllSkills(url, { includeInternal: Boolean(options.skill && options.skill.length > 0 && !options.skill.includes("*")) });
4245
+ } catch (error) {
4246
+ if (error instanceof WellKnownScopeNotFoundError) {
4247
+ spinner.stop(import_picocolors.default.red("No matching skills"));
4248
+ log.error(error.message);
4249
+ process.exit(1);
4250
+ }
4251
+ }
4026
4252
  if (skills.length === 0) {
4027
4253
  spinner.stop(import_picocolors.default.dim("No well-known skills found; trying direct download..."));
4028
4254
  return false;
@@ -4065,16 +4291,19 @@ async function handleWellKnownSkills(source, url, options, spinner) {
4065
4291
  selectedSkills = skills;
4066
4292
  log.info(`Installing all ${skills.length} skills`);
4067
4293
  } else {
4068
- const selected = await multiselect({
4294
+ const selected = await searchMultiselect({
4069
4295
  message: "Select skills to install",
4070
- options: skills.map((s) => ({
4296
+ items: skills.map((s) => ({
4071
4297
  value: s,
4072
4298
  label: s.installName,
4073
4299
  hint: s.description.length > 60 ? s.description.slice(0, 57) + "…" : s.description
4074
4300
  })),
4075
- required: true
4301
+ initialSelected: isSkillsShPackUrl(url) ? skills : void 0,
4302
+ required: true,
4303
+ maxVisible: 20,
4304
+ selectAll: true
4076
4305
  });
4077
- if (isCancel(selected)) {
4306
+ if (isCancelled$1(selected)) {
4078
4307
  cancel("Installation cancelled");
4079
4308
  process.exit(0);
4080
4309
  }
@@ -4241,7 +4470,9 @@ async function handleWellKnownSkills(source, url, options, spinner) {
4241
4470
  source: sourceIdentifier,
4242
4471
  sourceType: "well-known",
4243
4472
  sourceUrl: skill.sourceUrl,
4244
- skillFolderHash: ""
4473
+ skillFolderHash: "",
4474
+ sourceBaseUrl: url,
4475
+ wellKnownDigest: computeWellKnownSkillDigest(skill)
4245
4476
  });
4246
4477
  } catch {}
4247
4478
  }
@@ -4254,8 +4485,10 @@ async function handleWellKnownSkills(source, url, options, spinner) {
4254
4485
  const computedHash = await computeSkillFolderHash(installDir);
4255
4486
  await addSkillToLocalLock(skill.installName, {
4256
4487
  source: sourceIdentifier,
4488
+ sourceUrl: url,
4257
4489
  sourceType: "well-known",
4258
- computedHash
4490
+ computedHash,
4491
+ wellKnownDigest: computeWellKnownSkillDigest(skill)
4259
4492
  }, cwd);
4260
4493
  }
4261
4494
  } catch {}
@@ -4368,7 +4601,7 @@ async function runAdd(args, options = {}) {
4368
4601
  options.skill = options.skill || [];
4369
4602
  if (!options.skill.includes(parsed.skillFilter)) options.skill.push(parsed.skillFilter);
4370
4603
  }
4371
- const includeInternal = !!(options.skill && options.skill.length > 0);
4604
+ const includeInternal = !!(options.skill && options.skill.length > 0 && !options.skill.includes("*"));
4372
4605
  let skills;
4373
4606
  let blobResult = null;
4374
4607
  if (parsed.type === "local") {
@@ -4521,7 +4754,8 @@ async function runAdd(args, options = {}) {
4521
4754
  searchable: !hasGroups,
4522
4755
  showDetail: true,
4523
4756
  showSelectedSummary: false,
4524
- selectGroups: hasGroups
4757
+ selectGroups: hasGroups,
4758
+ selectAll: true
4525
4759
  });
4526
4760
  if (isCancelled$1(selected)) {
4527
4761
  cancel("Installation cancelled");
@@ -4531,7 +4765,7 @@ async function runAdd(args, options = {}) {
4531
4765
  selectedSkills = selected;
4532
4766
  }
4533
4767
  const ownerRepoForAudit = getOwnerRepo(parsed);
4534
- const auditPromise = ownerRepoForAudit ? fetchAuditData(ownerRepoForAudit, selectedSkills.map((s) => getSkillDisplayName(s))) : Promise.resolve(null);
4768
+ const auditPromise = ownerRepoForAudit ? repoPrivacyPromise.then((isPrivate) => isPrivate === false ? fetchAuditData(ownerRepoForAudit, selectedSkills.map((s) => getSkillDisplayName(s))) : null) : Promise.resolve(null);
4535
4769
  let targetAgents;
4536
4770
  const validAgents = Object.keys(agents);
4537
4771
  if (options.agent?.includes("*")) {
@@ -4611,7 +4845,7 @@ async function runAdd(args, options = {}) {
4611
4845
  const availableSubagents = getEveSubagents(process.cwd());
4612
4846
  if (options.subagent && options.subagent.length > 0) eveSubagentTargets = options.subagent.map((s) => s === "root" || s === "." ? void 0 : s);
4613
4847
  else if (availableSubagents.length > 0 && !options.yes) {
4614
- const selectedSubagents = await multiselect$1({
4848
+ const selectedSubagents = await multiselect({
4615
4849
  message: "Where should Eve skills be installed?",
4616
4850
  options: [{
4617
4851
  value: "",
@@ -5051,6 +5285,7 @@ const DIM$3 = "\x1B[38;5;102m";
5051
5285
  const TEXT$2 = "\x1B[38;5;145m";
5052
5286
  const CYAN$1 = "\x1B[36m";
5053
5287
  const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
5288
+ const SEARCH_RESULT_LIMIT = "20";
5054
5289
  function formatInstalls(count) {
5055
5290
  if (!count || count <= 0) return "";
5056
5291
  if (count >= 1e6) return `${(count / 1e6).toFixed(1).replace(/\.0$/, "")}M installs`;
@@ -5101,7 +5336,7 @@ async function searchSkillsAPI(query, owner) {
5101
5336
  try {
5102
5337
  const params = new URLSearchParams({
5103
5338
  q: query,
5104
- limit: "10"
5339
+ limit: SEARCH_RESULT_LIMIT
5105
5340
  });
5106
5341
  if (owner) params.set("owner", owner);
5107
5342
  const url = `${SEARCH_API_BASE}/api/search?${params.toString()}`;
@@ -5277,7 +5512,7 @@ ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
5277
5512
  }
5278
5513
  console.log(`${DIM$3}Install with${RESET$3} npx skills add <owner/repo@skill>`);
5279
5514
  console.log();
5280
- for (const skill of results.slice(0, 6)) {
5515
+ for (const skill of results) {
5281
5516
  const pkg = skill.source || skill.slug;
5282
5517
  const installs = formatInstalls(skill.installs);
5283
5518
  console.log(`${TEXT$2}${pkg}@${skill.name}${RESET$3}${installs ? ` ${CYAN$1}${installs}${RESET$3}` : ""}`);
@@ -5653,6 +5888,12 @@ function getLocalSource(entry) {
5653
5888
  if ((entry.sourceType === "git" || entry.sourceType === "gitlab") && isBareShorthand(entry.source)) return null;
5654
5889
  return entry.source;
5655
5890
  }
5891
+ function buildLocalCloneSource(entry) {
5892
+ const source = getLocalSource(entry);
5893
+ if (!source) return null;
5894
+ if (entry.sourceType === "github" && isBareShorthand(source)) return `https://github.com/${source.replace(/\.git$/, "")}.git`;
5895
+ return source;
5896
+ }
5656
5897
  function shouldUseFullDepthForUpdate(entry) {
5657
5898
  if (!entry.skillPath) return false;
5658
5899
  const source = entry.sourceType && entry.sourceType !== "github" ? getLocalSource(entry) : entry.source;
@@ -5898,6 +6139,17 @@ async function removeCommand(skillNames, options) {
5898
6139
  options.yes = true;
5899
6140
  log.info(import_picocolors.default.bgCyan(import_picocolors.default.black(import_picocolors.default.bold(` ${agentResult.agent.name} `))) + " Agent detected — removing non-interactively");
5900
6141
  }
6142
+ if (skillNames.includes("*")) {
6143
+ options.all = true;
6144
+ skillNames = skillNames.filter((name) => name !== "*");
6145
+ }
6146
+ const namedSkills = skillNames.filter((name) => name !== "*");
6147
+ if (options.all && namedSkills.length > 0) {
6148
+ log.error("Cannot combine --all with specific skill names.");
6149
+ log.info("Use `skills remove --all` to remove every skill, or omit --all to remove only the named skills.");
6150
+ log.info(`Example: skills remove ${namedSkills[0]} -y`);
6151
+ process.exit(1);
6152
+ }
5901
6153
  const isGlobal = options.global ?? false;
5902
6154
  const cwd = process.cwd();
5903
6155
  const spinner$1 = spinner();
@@ -5950,7 +6202,7 @@ async function removeCommand(skillNames, options) {
5950
6202
  value: s,
5951
6203
  label: s
5952
6204
  }));
5953
- const selected = await multiselect$1({
6205
+ const selected = await multiselect({
5954
6206
  message: `Select skills to remove ${import_picocolors.default.dim("(space to toggle)")}`,
5955
6207
  options: choices,
5956
6208
  required: true
@@ -6029,12 +6281,12 @@ async function removeCommand(skillNames, options) {
6029
6281
  const lockEntry = await getSkillFromLock(skillName);
6030
6282
  effectiveSource = lockEntry?.source || "local";
6031
6283
  effectiveSourceType = lockEntry?.sourceType || "local";
6032
- await removeSkillFromLock(skillName);
6284
+ if (!isStillUsed) await removeSkillFromLock(skillName);
6033
6285
  } else {
6034
6286
  const lockEntry = (await readLocalLock(cwd)).skills[skillName];
6035
6287
  effectiveSource = lockEntry?.source || "local";
6036
6288
  effectiveSourceType = lockEntry?.sourceType || "local";
6037
- await removeSkillFromLocalLock(skillName, cwd);
6289
+ if (!isStillUsed) await removeSkillFromLocalLock(skillName, cwd);
6038
6290
  }
6039
6291
  results.push({
6040
6292
  skill: skillName,
@@ -6085,8 +6337,19 @@ function parseRemoveOptions(args) {
6085
6337
  const arg = args[i];
6086
6338
  if (arg === "-g" || arg === "--global") options.global = true;
6087
6339
  else if (arg === "-y" || arg === "--yes") options.yes = true;
6088
- else if (arg === "--all") options.all = true;
6089
- else if (arg === "-a" || arg === "--agent") {
6340
+ else if (arg === "--all") {
6341
+ options.all = true;
6342
+ options.yes = true;
6343
+ } else if (arg === "-s" || arg === "--skill") {
6344
+ i++;
6345
+ let nextArg = args[i];
6346
+ while (i < args.length && nextArg && !nextArg.startsWith("-")) {
6347
+ skills.push(nextArg);
6348
+ i++;
6349
+ nextArg = args[i];
6350
+ }
6351
+ i--;
6352
+ } else if (arg === "-a" || arg === "--agent") {
6090
6353
  options.agent = options.agent || [];
6091
6354
  i++;
6092
6355
  let nextArg = args[i];
@@ -6108,6 +6371,13 @@ const RESET$1 = "\x1B[0m";
6108
6371
  const BOLD$1 = "\x1B[1m";
6109
6372
  const DIM$1 = "\x1B[38;5;102m";
6110
6373
  const TEXT$1 = "\x1B[38;5;145m";
6374
+ function getUpdateChildEnv(sourceType) {
6375
+ if (sourceType !== "github") return;
6376
+ return {
6377
+ ...process.env,
6378
+ GH_HOST: "github.com"
6379
+ };
6380
+ }
6111
6381
  function parseUpdateOptions(args) {
6112
6382
  const options = {};
6113
6383
  const positional = [];
@@ -6224,29 +6494,143 @@ async function getProjectSkillsForUpdate(skillFilter) {
6224
6494
  }
6225
6495
  return skills;
6226
6496
  }
6497
+ async function promptDeletions(source, deletedSkills, isGlobal, options) {
6498
+ if (deletedSkills.length === 0) return;
6499
+ console.log();
6500
+ console.log(`${DIM$1}Warning:${RESET$1} The following skills from ${DIM$1}${source}${RESET$1} appear to have been deleted upstream:`);
6501
+ for (const s of deletedSkills) console.log(` ${DIM$1}•${RESET$1} ${s}`);
6502
+ if (options.yes || !process.stdin.isTTY) {
6503
+ console.log(`${DIM$1}Skipping deletion in non-interactive mode.${RESET$1}`);
6504
+ return;
6505
+ }
6506
+ const confirmed = await confirm({ message: `Would you like to remove the local copies of these deleted skills?` });
6507
+ if (confirmed && !isCancel(confirmed)) for (const s of deletedSkills) {
6508
+ console.log(`${DIM$1}Removing${RESET$1} ${s}…`);
6509
+ await removeCommand([s], {
6510
+ yes: true,
6511
+ global: isGlobal
6512
+ });
6513
+ }
6514
+ }
6227
6515
  async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills, isGlobal, options, discoveredPaths) {
6228
6516
  const deletedSkills = allLockedForSource.filter((name) => {
6229
6517
  const entry = lockSkills[name];
6230
6518
  if (!entry?.skillPath) return false;
6231
6519
  return !discoveredPaths.includes(entry.skillPath);
6232
6520
  });
6233
- if (deletedSkills.length > 0) {
6234
- console.log();
6235
- console.log(`${DIM$1}Warning:${RESET$1} The following skills from ${DIM$1}${source}${RESET$1} appear to have been deleted upstream:`);
6236
- for (const s of deletedSkills) console.log(` ${DIM$1}•${RESET$1} ${s}`);
6237
- if (options.yes || !process.stdin.isTTY) console.log(`${DIM$1}Skipping deletion in non-interactive mode.${RESET$1}`);
6238
- else {
6239
- const confirmed = await confirm({ message: `Would you like to remove the local copies of these deleted skills?` });
6240
- if (confirmed && !isCancel(confirmed)) for (const s of deletedSkills) {
6241
- console.log(`${DIM$1}Removing${RESET$1} ${s}…`);
6242
- await removeCommand([s], {
6243
- yes: true,
6244
- global: isGlobal
6245
- });
6521
+ await promptDeletions(source, deletedSkills, isGlobal, options);
6522
+ return deletedSkills;
6523
+ }
6524
+ async function checkWellKnownForUpdates(baseUrl, items) {
6525
+ let indexResult;
6526
+ try {
6527
+ indexResult = await wellKnownProvider.fetchIndex(baseUrl, { updateCheck: true });
6528
+ } catch {
6529
+ return { status: "error" };
6530
+ }
6531
+ if (!indexResult) return { status: "error" };
6532
+ const byName = new Map(indexResult.entries.map((entry) => [entry.name, entry]));
6533
+ const removedSkills = items.filter((item) => !byName.has(item.name)).map((item) => item.name);
6534
+ const localNames = new Set(items.map((item) => item.name));
6535
+ const newSkills = indexResult.entries.map((entry) => entry.name).filter((name) => !localNames.has(name));
6536
+ const changedSkills = [];
6537
+ const needsContentCheck = [];
6538
+ for (const item of items) {
6539
+ const entry = byName.get(item.name);
6540
+ if (!entry) continue;
6541
+ if (entry.version === "0.2.0") {
6542
+ if (!item.digest || entry.digest !== item.digest) changedSkills.push(item.name);
6543
+ } else needsContentCheck.push(item);
6544
+ }
6545
+ if (needsContentCheck.length > 0) {
6546
+ const tracked = new Set(needsContentCheck.map((item) => item.name));
6547
+ const skills = (await Promise.all(indexResult.entries.filter((entry) => tracked.has(entry.name)).map((entry) => wellKnownProvider.fetchSkillByEntry(entry).catch(() => null)))).filter((skill) => skill !== null);
6548
+ if (skills.length === 0) return { status: "error" };
6549
+ const digests = new Map(skills.map((skill) => [skill.installName, computeWellKnownSkillDigest(skill)]));
6550
+ for (const item of needsContentCheck) {
6551
+ const digest = digests.get(item.name);
6552
+ if (!digest || !item.digest || digest !== item.digest) changedSkills.push(item.name);
6553
+ }
6554
+ }
6555
+ if (changedSkills.length === 0 && removedSkills.length === 0) return {
6556
+ status: "current",
6557
+ newSkills
6558
+ };
6559
+ return {
6560
+ status: "changed",
6561
+ changedSkills,
6562
+ removedSkills,
6563
+ newSkills
6564
+ };
6565
+ }
6566
+ function printNewSkills(baseUrl, newSkills, isGlobal) {
6567
+ if (newSkills.length === 0) return;
6568
+ const names = newSkills.map(sanitizeMetadata);
6569
+ console.log(` ${DIM$1}${newSkills.length} new skill(s) available from this source:${RESET$1} ${names.join(", ")}`);
6570
+ console.log(` ${DIM$1}To install: ${TEXT$1}npx skills add ${baseUrl} --skill ${names.join(" ")}${isGlobal ? " -g" : ""}${RESET$1}`);
6571
+ }
6572
+ async function processWellKnownUpdates(groups, isGlobal, options) {
6573
+ let successCount = 0;
6574
+ let failCount = 0;
6575
+ let changed = false;
6576
+ for (const [baseUrl, items] of groups) {
6577
+ process.stdout.write(`\r${DIM$1}Checking skills from source: ${baseUrl}${RESET$1}\x1b[K\n`);
6578
+ const result = await checkWellKnownForUpdates(baseUrl, items);
6579
+ if (result.status === "error") {
6580
+ console.log(` ${DIM$1}✗ Failed to check skills from ${baseUrl}${RESET$1}`);
6581
+ continue;
6582
+ }
6583
+ if (result.status === "current") {
6584
+ printNewSkills(baseUrl, result.newSkills, isGlobal);
6585
+ continue;
6586
+ }
6587
+ changed = true;
6588
+ await promptDeletions(baseUrl, result.removedSkills, isGlobal, options);
6589
+ printNewSkills(baseUrl, result.newSkills, isGlobal);
6590
+ if (result.changedSkills.length === 0) continue;
6591
+ const cliEntry = join(__dirname$1, "..", "bin", "cli.mjs");
6592
+ if (!existsSync(cliEntry)) {
6593
+ failCount += result.changedSkills.length;
6594
+ console.log(` ${DIM$1}✗ CLI entrypoint not found at ${cliEntry}${RESET$1}`);
6595
+ continue;
6596
+ }
6597
+ const itemByName = new Map(items.map((item) => [item.name, item]));
6598
+ for (const name of result.changedSkills) {
6599
+ const safeName = sanitizeMetadata(name);
6600
+ console.log(`${TEXT$1}Updating ${safeName}…${RESET$1}`);
6601
+ const subagents = itemByName.get(name)?.subagents;
6602
+ const subagentArgs = !isGlobal && subagents?.length ? ["--subagent", ...subagents.map((s) => s === "" ? "root" : s)] : [];
6603
+ if (spawnSync(process.execPath, [
6604
+ cliEntry,
6605
+ "add",
6606
+ baseUrl,
6607
+ "--skill",
6608
+ name,
6609
+ ...subagentArgs,
6610
+ ...isGlobal ? ["-g"] : [],
6611
+ "-y"
6612
+ ], {
6613
+ stdio: [
6614
+ "inherit",
6615
+ "pipe",
6616
+ "pipe"
6617
+ ],
6618
+ encoding: "utf-8",
6619
+ shell: false
6620
+ }).status === 0) {
6621
+ successCount++;
6622
+ console.log(` ${TEXT$1}✓${RESET$1} Updated ${safeName}`);
6623
+ } else {
6624
+ failCount++;
6625
+ console.log(` ${DIM$1}✗ Failed to update ${safeName}${RESET$1}`);
6246
6626
  }
6247
6627
  }
6248
6628
  }
6249
- return deletedSkills;
6629
+ return {
6630
+ successCount,
6631
+ failCount,
6632
+ changed
6633
+ };
6250
6634
  }
6251
6635
  async function updateGlobalSkills(options = {}) {
6252
6636
  const lock = await readSkillLock();
@@ -6267,10 +6651,20 @@ async function updateGlobalSkills(options = {}) {
6267
6651
  const updates = [];
6268
6652
  const skipped = [];
6269
6653
  const checkable = [];
6654
+ const wellKnownGroups = /* @__PURE__ */ new Map();
6270
6655
  for (const skillName of skillNames) {
6271
6656
  if (!matchesSkillFilter(skillName, options.skills)) continue;
6272
6657
  const entry = lock.skills[skillName];
6273
6658
  if (!entry) continue;
6659
+ if (entry.sourceType === "well-known" && entry.sourceBaseUrl && entry.wellKnownDigest) {
6660
+ const group = wellKnownGroups.get(entry.sourceBaseUrl) || [];
6661
+ group.push({
6662
+ name: skillName,
6663
+ digest: entry.wellKnownDigest
6664
+ });
6665
+ wellKnownGroups.set(entry.sourceBaseUrl, group);
6666
+ continue;
6667
+ }
6274
6668
  if (!entry.skillFolderHash || !entry.skillPath) {
6275
6669
  skipped.push({
6276
6670
  name: skillName,
@@ -6286,50 +6680,55 @@ async function updateGlobalSkills(options = {}) {
6286
6680
  entry
6287
6681
  });
6288
6682
  }
6683
+ const wellKnownCount = Array.from(wellKnownGroups.values()).reduce((sum, items) => sum + items.length, 0);
6684
+ const { successCount: wkSuccessCount, failCount: wkFailCount, changed: wkChanged } = await processWellKnownUpdates(wellKnownGroups, true, options);
6685
+ successCount += wkSuccessCount;
6686
+ failCount += wkFailCount;
6289
6687
  const bySource = /* @__PURE__ */ new Map();
6290
6688
  for (const item of checkable) {
6291
- const source = item.entry.source;
6292
- const existing = bySource.get(source) || [];
6689
+ const key = `${item.entry.source}\n${item.entry.ref ?? ""}`;
6690
+ const existing = bySource.get(key) || [];
6293
6691
  existing.push(item);
6294
- bySource.set(source, existing);
6692
+ bySource.set(key, existing);
6295
6693
  }
6296
- for (const [source, itemsForSource] of bySource) {
6694
+ for (const [, itemsForSource] of bySource) {
6297
6695
  const firstEntry = itemsForSource[0].entry;
6696
+ const source = firstEntry.source;
6298
6697
  const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
6299
6698
  let tempDir = null;
6300
6699
  process.stdout.write(`\r${DIM$1}Checking skills from source: ${source}${RESET$1}\x1b[K\n`);
6301
6700
  try {
6302
- if (firstEntry.sourceType === "github") {
6701
+ const isGitHubSource = firstEntry.sourceType === "github";
6702
+ if (isGitHubSource) {
6303
6703
  const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
6304
- if (!tree) {
6305
- console.log(` ${DIM$1}✗ Failed to fetch tree for ${source}${RESET$1}`);
6704
+ if (tree) {
6705
+ const discoveredPaths = tree.tree.filter((entry) => entry.type === "blob").map((entry) => entry.path);
6706
+ const deletedSkills = await checkAndPromptForDeletions(source, Object.entries(lock.skills).filter(([_, entry]) => entry.source === source && entry.ref === firstEntry.ref).map(([name, _]) => name), lock.skills, true, options, discoveredPaths);
6707
+ const deletedSkillSet = new Set(deletedSkills);
6708
+ for (const { name: skillName, entry } of itemsForSource) {
6709
+ if (deletedSkillSet.has(skillName)) continue;
6710
+ const latestHash = getSkillFolderHashFromTree(tree, entry.skillPath);
6711
+ if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
6712
+ name: skillName,
6713
+ source,
6714
+ entry
6715
+ });
6716
+ }
6306
6717
  continue;
6307
6718
  }
6308
- const discoveredPaths = tree.tree.filter((entry) => entry.type === "blob").map((entry) => entry.path);
6309
- const deletedSkills = await checkAndPromptForDeletions(source, Object.entries(lock.skills).filter(([_, entry]) => entry.source === source).map(([name, _]) => name), lock.skills, true, options, discoveredPaths);
6310
- const deletedSkillSet = new Set(deletedSkills);
6311
- for (const { name: skillName, entry } of itemsForSource) {
6312
- if (deletedSkillSet.has(skillName)) continue;
6313
- const latestHash = getSkillFolderHashFromTree(tree, entry.skillPath);
6314
- if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
6315
- name: skillName,
6316
- source,
6317
- entry
6318
- });
6319
- }
6320
- continue;
6719
+ console.log(` ${DIM$1}GitHub API unavailable; checking via Git clone${RESET$1}`);
6321
6720
  }
6322
6721
  tempDir = await cloneRepo(sourceUrl, firstEntry.ref);
6323
6722
  const discoveredPaths = (await discoverSkills(tempDir, void 0, { fullDepth: true })).map((skill) => {
6324
6723
  return join(relative(tempDir, skill.path), "SKILL.md").split(sep).join("/");
6325
6724
  });
6326
- const deletedSkills = await checkAndPromptForDeletions(source, Object.entries(lock.skills).filter(([_, entry]) => entry.source === source).map(([name, _]) => name), lock.skills, true, options, discoveredPaths);
6725
+ const deletedSkills = await checkAndPromptForDeletions(source, Object.entries(lock.skills).filter(([_, entry]) => entry.source === source && entry.ref === firstEntry.ref).map(([name, _]) => name), lock.skills, true, options, discoveredPaths);
6327
6726
  const deletedSkillSet = new Set(deletedSkills);
6328
6727
  for (const { name: skillName, entry } of itemsForSource) {
6329
6728
  if (deletedSkillSet.has(skillName)) continue;
6330
6729
  const skillPath = entry.skillPath;
6331
6730
  if (!discoveredPaths.includes(skillPath)) continue;
6332
- const latestHash = await computeSkillFolderHash(join(tempDir, dirname(skillPath)));
6731
+ const latestHash = isGitHubSource && /^[0-9a-f]{40}$/i.test(entry.skillFolderHash) ? await getGitTreeHash(tempDir, skillPath) : await computeSkillFolderHash(join(tempDir, dirname(skillPath)));
6333
6732
  if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
6334
6733
  name: skillName,
6335
6734
  source,
@@ -6343,8 +6742,8 @@ async function updateGlobalSkills(options = {}) {
6343
6742
  }
6344
6743
  }
6345
6744
  if (checkable.length > 0) process.stdout.write("\r\x1B[K");
6346
- const checkedCount = checkable.length + skipped.length;
6347
- if (checkable.length === 0 && skipped.length === 0) {
6745
+ const checkedCount = checkable.length + skipped.length + wellKnownCount;
6746
+ if (checkable.length === 0 && skipped.length === 0 && wellKnownCount === 0) {
6348
6747
  if (!options.skills) console.log(`${DIM$1}No global skills to check.${RESET$1}`);
6349
6748
  return {
6350
6749
  successCount,
@@ -6352,6 +6751,14 @@ async function updateGlobalSkills(options = {}) {
6352
6751
  checkedCount: 0
6353
6752
  };
6354
6753
  }
6754
+ if (checkable.length === 0 && skipped.length === 0) {
6755
+ if (!wkChanged) console.log(`${TEXT$1}✓ All global skills are up to date${RESET$1}`);
6756
+ return {
6757
+ successCount,
6758
+ failCount,
6759
+ checkedCount
6760
+ };
6761
+ }
6355
6762
  if (checkable.length === 0 && skipped.length > 0) {
6356
6763
  printSkippedSkills(skipped);
6357
6764
  return {
@@ -6361,7 +6768,7 @@ async function updateGlobalSkills(options = {}) {
6361
6768
  };
6362
6769
  }
6363
6770
  if (updates.length === 0) {
6364
- console.log(`${TEXT$1}✓ All global skills are up to date${RESET$1}`);
6771
+ if (!wkChanged) console.log(`${TEXT$1}✓ All global skills are up to date${RESET$1}`);
6365
6772
  return {
6366
6773
  successCount,
6367
6774
  failCount,
@@ -6390,6 +6797,8 @@ async function updateGlobalSkills(options = {}) {
6390
6797
  cliEntry,
6391
6798
  "add",
6392
6799
  installUrl,
6800
+ "--skill",
6801
+ update.name,
6393
6802
  ...fullDepthArgs,
6394
6803
  "-g",
6395
6804
  "-y"
@@ -6400,6 +6809,7 @@ async function updateGlobalSkills(options = {}) {
6400
6809
  "pipe"
6401
6810
  ],
6402
6811
  encoding: "utf-8",
6812
+ env: getUpdateChildEnv(update.entry.sourceType),
6403
6813
  shell: false
6404
6814
  }).status === 0) {
6405
6815
  successCount++;
@@ -6431,9 +6841,24 @@ async function updateProjectSkills(options = {}) {
6431
6841
  foundCount: 0
6432
6842
  };
6433
6843
  }
6434
- const updatable = projectSkills.filter((s) => s.entry.skillPath);
6435
- const legacy = projectSkills.filter((s) => !s.entry.skillPath);
6436
- if (updatable.length === 0) {
6844
+ const wellKnownGroups = /* @__PURE__ */ new Map();
6845
+ const nonWellKnown = [];
6846
+ for (const skill of projectSkills) {
6847
+ const { entry } = skill;
6848
+ if (entry.sourceType === "well-known" && entry.sourceUrl && entry.wellKnownDigest) {
6849
+ const group = wellKnownGroups.get(entry.sourceUrl) || [];
6850
+ group.push({
6851
+ name: skill.name,
6852
+ digest: entry.wellKnownDigest,
6853
+ subagents: entry.subagents
6854
+ });
6855
+ wellKnownGroups.set(entry.sourceUrl, group);
6856
+ } else nonWellKnown.push(skill);
6857
+ }
6858
+ const wellKnownCount = Array.from(wellKnownGroups.values()).reduce((sum, items) => sum + items.length, 0);
6859
+ const updatable = nonWellKnown.filter((s) => s.entry.skillPath);
6860
+ const legacy = nonWellKnown.filter((s) => !s.entry.skillPath);
6861
+ if (updatable.length === 0 && wellKnownCount === 0) {
6437
6862
  console.log(`${DIM$1}No project skills can be updated in place.${RESET$1}`);
6438
6863
  printLegacyProjectSkills(legacy);
6439
6864
  return {
@@ -6455,39 +6880,43 @@ async function updateProjectSkills(options = {}) {
6455
6880
  if (hasUniversal) targetParts.push("Universal");
6456
6881
  targetParts.push(...targetAgentNames);
6457
6882
  if (targetParts.length > 0) console.log(`${TEXT$1}Updating for: ${targetParts.join(", ")}${RESET$1}`);
6458
- console.log(`${TEXT$1}Refreshing ${updatable.length} skill(s)…${RESET$1}`);
6883
+ console.log(`${TEXT$1}Refreshing ${updatable.length + wellKnownCount} skill(s)…${RESET$1}`);
6459
6884
  console.log();
6885
+ const { successCount: wkSuccessCount, failCount: wkFailCount } = await processWellKnownUpdates(wellKnownGroups, false, options);
6886
+ successCount += wkSuccessCount;
6887
+ failCount += wkFailCount;
6460
6888
  const bySource = /* @__PURE__ */ new Map();
6461
6889
  for (const skill of updatable) {
6462
- const source = skill.entry.sourceUrl || skill.entry.source;
6463
- const existing = bySource.get(source) || [];
6890
+ const key = `${skill.entry.sourceUrl || skill.entry.source}\n${skill.entry.ref ?? ""}`;
6891
+ const existing = bySource.get(key) || [];
6464
6892
  existing.push(skill);
6465
- bySource.set(source, existing);
6893
+ bySource.set(key, existing);
6466
6894
  }
6467
6895
  const localLock = await readLocalLock();
6468
6896
  const cliEntry = join(__dirname$1, "..", "bin", "cli.mjs");
6469
- if (!existsSync(cliEntry)) {
6897
+ if (updatable.length > 0 && !existsSync(cliEntry)) {
6470
6898
  console.log(`${DIM$1}✗ CLI entrypoint not found at ${cliEntry}${RESET$1}`);
6471
6899
  return {
6472
6900
  successCount,
6473
- failCount: updatable.length,
6901
+ failCount: failCount + updatable.length,
6474
6902
  foundCount: projectSkills.length
6475
6903
  };
6476
6904
  }
6477
- for (const [source, skillsForSource] of bySource) {
6905
+ for (const [, skillsForSource] of bySource) {
6478
6906
  const firstEntry = skillsForSource[0].entry;
6479
- const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
6907
+ const source = firstEntry.sourceUrl || firstEntry.source;
6908
+ const cloneSource = buildLocalCloneSource(firstEntry);
6480
6909
  const ref = firstEntry.ref;
6481
- const allLockedForSource = Object.entries(localLock.skills).filter(([_, entry]) => (entry.sourceUrl || entry.source) === source).map(([name, _]) => name);
6910
+ const allLockedForSource = Object.entries(localLock.skills).filter(([_, entry]) => (entry.sourceUrl || entry.source) === source && entry.ref === ref).map(([name, _]) => name);
6482
6911
  let tempDir = null;
6483
6912
  let deletedSkills = [];
6484
- if (buildLocalUpdateSource(firstEntry) === null) {
6913
+ if (cloneSource === null) {
6485
6914
  failCount += skillsForSource.length;
6486
6915
  console.log(`${DIM$1}✗ Cannot update ${source}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$1}`);
6487
6916
  continue;
6488
6917
  }
6489
6918
  try {
6490
- tempDir = await cloneRepo(sourceUrl, ref);
6919
+ tempDir = await cloneRepo(cloneSource, ref);
6491
6920
  const discoveredPaths = (await discoverSkills(tempDir, void 0, { fullDepth: true })).map((s) => {
6492
6921
  return join(relative(tempDir, s.path), "SKILL.md").split(sep).join("/");
6493
6922
  });
@@ -6525,6 +6954,7 @@ async function updateProjectSkills(options = {}) {
6525
6954
  "pipe"
6526
6955
  ],
6527
6956
  encoding: "utf-8",
6957
+ env: getUpdateChildEnv(skill.entry.sourceType),
6528
6958
  shell: false
6529
6959
  }).status === 0) {
6530
6960
  successCount++;
@@ -6624,7 +7054,6 @@ function parseUseOptions(args) {
6624
7054
  if (!arg) continue;
6625
7055
  if (arg === "--help" || arg === "-h") options.help = true;
6626
7056
  else if (arg === "--full-depth") options.fullDepth = true;
6627
- else if (arg === "--dangerously-accept-openclaw-risks") options.dangerouslyAcceptOpenclawRisks = true;
6628
7057
  else if (arg === "--skill" || arg === "-s") {
6629
7058
  const value = args[i + 1];
6630
7059
  if (!value || value.startsWith("-")) errors.push(`${arg} requires a skill name`);
@@ -6695,16 +7124,14 @@ async function runUse(sourceArgs, options = {}, parseErrors = []) {
6695
7124
  if (useAgent && !USE_AGENT_CONFIGS[useAgent]) fail(formatUnsupportedAgentError(useAgent));
6696
7125
  const source = sourceArgs[0];
6697
7126
  const parsed = parseSource(source);
6698
- if (getOwnerRepo(parsed)?.split("/")[0]?.toLowerCase() === "openclaw" && !options.dangerouslyAcceptOpenclawRisks) fail([
6699
- "OpenClaw skills are unverified community submissions.",
6700
- "Skills run with full agent permissions and could be malicious.",
6701
- `If you understand the risks, re-run with: skills use ${source} --dangerously-accept-openclaw-risks`
6702
- ].join("\n"));
6703
7127
  const selector = resolveSelector(parsed.skillFilter, options.skill);
6704
7128
  const includeInternal = selector !== void 0;
6705
7129
  let selectedSkill;
6706
7130
  if (parsed.type === "well-known") {
6707
- const skills = await wellKnownProvider.fetchAllSkills(parsed.url);
7131
+ const skills = await wellKnownProvider.fetchAllSkills(parsed.url, { includeInternal }).catch((error) => {
7132
+ if (error instanceof WellKnownScopeNotFoundError) fail(error.message);
7133
+ return [];
7134
+ });
6708
7135
  if (skills.length > 0) selectedSkill = selectWellKnownSkill(skills, selector, source);
6709
7136
  else {
6710
7137
  const downloaded = await downloadSource(parsed.url);
@@ -6833,8 +7260,6 @@ Options:
6833
7260
  -s, --skill <skill> Select the skill to use
6834
7261
  -a, --agent <agent> Start one supported agent interactively (${SUPPORTED_USE_AGENTS.join(", ")})
6835
7262
  --full-depth Search nested directories like skills add --full-depth
6836
- --dangerously-accept-openclaw-risks
6837
- Allow unverified OpenClaw community skills
6838
7263
  -h, --help Show this help message
6839
7264
 
6840
7265
  Examples:
@@ -7084,15 +7509,13 @@ ${BOLD}Use Options:${RESET}
7084
7509
  -s, --skill <skill> Specify the skill to use
7085
7510
  -a, --agent <agent> Start one supported agent interactively
7086
7511
  --full-depth Search all subdirectories even when a root SKILL.md exists
7087
- --dangerously-accept-openclaw-risks
7088
- Allow unverified OpenClaw community skills
7089
7512
 
7090
7513
  ${BOLD}Remove Options:${RESET}
7091
7514
  -g, --global Remove from global scope
7092
- -a, --agent <agents> Remove from specific agents (use '*' for all agents)
7515
+ -a, --agent <agents> Remove from specific agents (omit to clean all agent links)
7093
7516
  -s, --skill <skills> Specify skills to remove (use '*' for all skills)
7094
7517
  -y, --yes Skip confirmation prompts
7095
- --all Shorthand for --skill '*' --agent '*' -y
7518
+ --all Remove every installed skill (-y implied). Do not combine with named skills.
7096
7519
 
7097
7520
  ${BOLD}Experimental Sync Options:${RESET}
7098
7521
  -a, --agent <agents> Specify agents to install to (use '*' for all agents)
@@ -7148,10 +7571,10 @@ ${BOLD}Arguments:${RESET}
7148
7571
 
7149
7572
  ${BOLD}Options:${RESET}
7150
7573
  -g, --global Remove from global scope (~/) instead of project scope
7151
- -a, --agent Remove from specific agents (use '*' for all agents)
7574
+ -a, --agent Remove from specific agents (omit to clean all agent links)
7152
7575
  -s, --skill Specify skills to remove (use '*' for all skills)
7153
7576
  -y, --yes Skip confirmation prompts
7154
- --all Shorthand for --skill '*' --agent '*' -y
7577
+ --all Remove every installed skill (-y implied). Do not combine with named skills.
7155
7578
 
7156
7579
  ${BOLD}Examples:${RESET}
7157
7580
  ${DIM}$${RESET} skills remove ${DIM}# interactive selection${RESET}