skills 1.4.1 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -214,7 +214,7 @@ Skills can be installed to any of these agents:
214
214
  | Augment | `augment` | `.augment/skills/` | `~/.augment/skills/` |
215
215
  | Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
216
216
  | OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
217
- | Cline | `cline` | `.cline/skills/` | `~/.cline/skills/` |
217
+ | Cline | `cline` | `.agents/skills/` | `~/.agents/skills/` |
218
218
  | CodeBuddy | `codebuddy` | `.codebuddy/skills/` | `~/.codebuddy/skills/` |
219
219
  | Codex | `codex` | `.agents/skills/` | `~/.codex/skills/` |
220
220
  | Command Code | `command-code` | `.commandcode/skills/` | `~/.commandcode/skills/` |
@@ -321,7 +321,6 @@ The CLI searches for skills in these locations within a repository:
321
321
  - `.augment/skills/`
322
322
  - `.claude/skills/`
323
323
  - `./skills/`
324
- - `.cline/skills/`
325
324
  - `.codebuddy/skills/`
326
325
  - `.commandcode/skills/`
327
326
  - `.continue/skills/`
@@ -129,6 +129,65 @@ ${s}
129
129
  }).join(`
130
130
  ${import_picocolors.default.cyan(o)} `)}
131
131
  ${import_picocolors.default.cyan(d)}
132
+ `;
133
+ }
134
+ }
135
+ }).prompt();
136
+ }, be = (t) => {
137
+ const { selectableGroups: n = !0 } = t, r = (i, s, c = []) => {
138
+ const a = i.label ?? String(i.value), l = typeof i.group == "string", $ = l && (c[c.indexOf(i) + 1] ?? { group: !0 }), g = l && $.group === !0, p = l ? n ? `${g ? d : o} ` : " " : "";
139
+ if (s === "active") return `${import_picocolors.default.dim(p)}${import_picocolors.default.cyan(A)} ${a} ${i.hint ? import_picocolors.default.dim(`(${i.hint})`) : ""}`;
140
+ if (s === "group-active") return `${p}${import_picocolors.default.cyan(A)} ${import_picocolors.default.dim(a)}`;
141
+ if (s === "group-active-selected") return `${p}${import_picocolors.default.green(T)} ${import_picocolors.default.dim(a)}`;
142
+ if (s === "selected") {
143
+ const f = l || n ? import_picocolors.default.green(T) : "";
144
+ return `${import_picocolors.default.dim(p)}${f} ${import_picocolors.default.dim(a)} ${i.hint ? import_picocolors.default.dim(`(${i.hint})`) : ""}`;
145
+ }
146
+ if (s === "cancelled") return `${import_picocolors.default.strikethrough(import_picocolors.default.dim(a))}`;
147
+ if (s === "active-selected") return `${import_picocolors.default.dim(p)}${import_picocolors.default.green(T)} ${a} ${i.hint ? import_picocolors.default.dim(`(${i.hint})`) : ""}`;
148
+ if (s === "submitted") return `${import_picocolors.default.dim(a)}`;
149
+ const v = l || n ? import_picocolors.default.dim(F) : "";
150
+ return `${import_picocolors.default.dim(p)}${v} ${import_picocolors.default.dim(a)}`;
151
+ };
152
+ return new _D({
153
+ options: t.options,
154
+ initialValues: t.initialValues,
155
+ required: t.required ?? !0,
156
+ cursorAt: t.cursorAt,
157
+ selectableGroups: n,
158
+ validate(i) {
159
+ if (this.required && i.length === 0) return `Please select at least one option.
160
+ ${import_picocolors.default.reset(import_picocolors.default.dim(`Press ${import_picocolors.default.gray(import_picocolors.default.bgWhite(import_picocolors.default.inverse(" space ")))} to select, ${import_picocolors.default.gray(import_picocolors.default.bgWhite(import_picocolors.default.inverse(" enter ")))} to submit`))}`;
161
+ },
162
+ render() {
163
+ const i = `${import_picocolors.default.gray(o)}
164
+ ${b(this.state)} ${t.message}
165
+ `;
166
+ switch (this.state) {
167
+ case "submit": return `${i}${import_picocolors.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => r(s, "submitted")).join(import_picocolors.default.dim(", "))}`;
168
+ case "cancel": {
169
+ const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => r(c, "cancelled")).join(import_picocolors.default.dim(", "));
170
+ return `${i}${import_picocolors.default.gray(o)} ${s.trim() ? `${s}
171
+ ${import_picocolors.default.gray(o)}` : ""}`;
172
+ }
173
+ case "error": {
174
+ const s = this.error.split(`
175
+ `).map((c, a) => a === 0 ? `${import_picocolors.default.yellow(d)} ${import_picocolors.default.yellow(c)}` : ` ${c}`).join(`
176
+ `);
177
+ return `${i}${import_picocolors.default.yellow(o)} ${this.options.map((c, a, l) => {
178
+ const $ = this.value.includes(c.value) || c.group === !0 && this.isGroupSelected(`${c.value}`), g = a === this.cursor;
179
+ return !g && typeof c.group == "string" && this.options[this.cursor].value === c.group ? r(c, $ ? "group-active-selected" : "group-active", l) : g && $ ? r(c, "active-selected", l) : $ ? r(c, "selected", l) : r(c, g ? "active" : "inactive", l);
180
+ }).join(`
181
+ ${import_picocolors.default.yellow(o)} `)}
182
+ ${s}
183
+ `;
184
+ }
185
+ default: return `${i}${import_picocolors.default.cyan(o)} ${this.options.map((s, c, a) => {
186
+ const l = this.value.includes(s.value) || s.group === !0 && this.isGroupSelected(`${s.value}`), $ = c === this.cursor;
187
+ return !$ && typeof s.group == "string" && this.options[this.cursor].value === s.group ? r(s, l ? "group-active-selected" : "group-active", a) : $ && l ? r(s, "active-selected", a) : l ? r(s, "selected", a) : r(s, $ ? "active" : "inactive", a);
188
+ }).join(`
189
+ ${import_picocolors.default.cyan(o)} `)}
190
+ ${import_picocolors.default.cyan(d)}
132
191
  `;
133
192
  }
134
193
  }
@@ -272,4 +331,4 @@ ${J}${i.trimStart()}`), r = 3 + stripVTControlCharacters(i.trimStart()).length);
272
331
  }
273
332
  };
274
333
  };
275
- export { Y as a, xe as c, Se as i, ye as l, M as n, fe as o, Me as r, ve as s, Ie as t };
334
+ export { Y as a, ve as c, Se as i, xe as l, M as n, be as o, Me as r, fe as s, Ie as t, ye as u };
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { r as __toESM } from "./_chunks/rolldown-runtime.mjs";
3
3
  import { l as pD, u as require_picocolors } from "./_chunks/libs/@clack/core.mjs";
4
- import { a as Y, c as xe, i as Se, l as ye, n as M, o as fe, r as Me, s as ve, t as Ie } from "./_chunks/libs/@clack/prompts.mjs";
4
+ import { a as Y, c as ve, i as Se, l as xe, n as M, o as be, r as Me, s as fe, t as Ie, u as ye } from "./_chunks/libs/@clack/prompts.mjs";
5
5
  import "./_chunks/libs/@kwsites/file-exists.mjs";
6
6
  import "./_chunks/libs/@kwsites/promise-deferred.mjs";
7
7
  import { t as esm_default } from "./_chunks/libs/simple-git.mjs";
@@ -49,15 +49,6 @@ async function isRepoPrivate(owner, repo) {
49
49
  function isLocalPath(input) {
50
50
  return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
51
51
  }
52
- function isDirectSkillUrl(input) {
53
- if (!input.startsWith("http://") && !input.startsWith("https://")) return false;
54
- if (!input.toLowerCase().endsWith("/skill.md")) return false;
55
- if (input.includes("github.com/") && !input.includes("raw.githubusercontent.com")) {
56
- if (!input.includes("/blob/") && !input.includes("/raw/")) return false;
57
- }
58
- if (input.includes("gitlab.com/") && !input.includes("/-/raw/")) return false;
59
- return true;
60
- }
61
52
  const SOURCE_ALIASES = { "coinbase/agentWallet": "coinbase/agentic-wallet-skills" };
62
53
  function parseSource(input) {
63
54
  const alias = SOURCE_ALIASES[input];
@@ -70,10 +61,6 @@ function parseSource(input) {
70
61
  localPath: resolvedPath
71
62
  };
72
63
  }
73
- if (isDirectSkillUrl(input)) return {
74
- type: "direct-url",
75
- url: input
76
- };
77
64
  const githubTreeWithPathMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
78
65
  if (githubTreeWithPathMatch) {
79
66
  const [, owner, repo, ref, subpath] = githubTreeWithPathMatch;
@@ -162,10 +149,8 @@ function isWellKnownUrl(input) {
162
149
  if ([
163
150
  "github.com",
164
151
  "gitlab.com",
165
- "huggingface.co",
166
152
  "raw.githubusercontent.com"
167
153
  ].includes(parsed.hostname)) return false;
168
- if (input.toLowerCase().endsWith("/skill.md")) return false;
169
154
  if (input.endsWith(".git")) return false;
170
155
  return true;
171
156
  } catch {
@@ -424,6 +409,36 @@ async function getPluginSkillPaths(basePath) {
424
409
  } catch {}
425
410
  return searchDirs;
426
411
  }
412
+ async function getPluginGroupings(basePath) {
413
+ const groupings = /* @__PURE__ */ new Map();
414
+ try {
415
+ const content = await readFile(join(basePath, ".claude-plugin/marketplace.json"), "utf-8");
416
+ const manifest = JSON.parse(content);
417
+ const pluginRoot = manifest.metadata?.pluginRoot;
418
+ if (pluginRoot === void 0 || isValidRelativePath(pluginRoot)) for (const plugin of manifest.plugins ?? []) {
419
+ if (!plugin.name) continue;
420
+ if (typeof plugin.source !== "string" && plugin.source !== void 0) continue;
421
+ if (plugin.source !== void 0 && !isValidRelativePath(plugin.source)) continue;
422
+ const pluginBase = join(basePath, pluginRoot ?? "", plugin.source ?? "");
423
+ if (!isContainedIn(pluginBase, basePath)) continue;
424
+ if (plugin.skills && plugin.skills.length > 0) for (const skillPath of plugin.skills) {
425
+ if (!isValidRelativePath(skillPath)) continue;
426
+ const skillDir = join(pluginBase, skillPath);
427
+ if (isContainedIn(skillDir, basePath)) groupings.set(resolve(skillDir), plugin.name);
428
+ }
429
+ }
430
+ } catch {}
431
+ try {
432
+ const content = await readFile(join(basePath, ".claude-plugin/plugin.json"), "utf-8");
433
+ const manifest = JSON.parse(content);
434
+ if (manifest.name && manifest.skills && manifest.skills.length > 0) for (const skillPath of manifest.skills) {
435
+ if (!isValidRelativePath(skillPath)) continue;
436
+ const skillDir = join(basePath, skillPath);
437
+ if (isContainedIn(skillDir, basePath)) groupings.set(resolve(skillDir), manifest.name);
438
+ }
439
+ } catch {}
440
+ return groupings;
441
+ }
427
442
  const SKIP_DIRS = [
428
443
  "node_modules",
429
444
  ".git",
@@ -475,9 +490,16 @@ async function discoverSkills(basePath, subpath, options) {
475
490
  const skills = [];
476
491
  const seenNames = /* @__PURE__ */ new Set();
477
492
  const searchPath = subpath ? join(basePath, subpath) : basePath;
493
+ const pluginGroupings = await getPluginGroupings(searchPath);
494
+ const enhanceSkill = (skill) => {
495
+ const resolvedPath = resolve(skill.path);
496
+ if (pluginGroupings.has(resolvedPath)) skill.pluginName = pluginGroupings.get(resolvedPath);
497
+ return skill;
498
+ };
478
499
  if (await hasSkillMd(searchPath)) {
479
- const skill = await parseSkillMd(join(searchPath, "SKILL.md"), options);
500
+ let skill = await parseSkillMd(join(searchPath, "SKILL.md"), options);
480
501
  if (skill) {
502
+ skill = enhanceSkill(skill);
481
503
  skills.push(skill);
482
504
  seenNames.add(skill.name);
483
505
  if (!options?.fullDepth) return skills;
@@ -520,8 +542,9 @@ async function discoverSkills(basePath, subpath, options) {
520
542
  for (const entry of entries) if (entry.isDirectory()) {
521
543
  const skillDir = join(dir, entry.name);
522
544
  if (await hasSkillMd(skillDir)) {
523
- const skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
545
+ let skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
524
546
  if (skill && !seenNames.has(skill.name)) {
547
+ skill = enhanceSkill(skill);
525
548
  skills.push(skill);
526
549
  seenNames.add(skill.name);
527
550
  }
@@ -531,8 +554,9 @@ async function discoverSkills(basePath, subpath, options) {
531
554
  if (skills.length === 0 || options?.fullDepth) {
532
555
  const allSkillDirs = await findSkillDirs(searchPath);
533
556
  for (const skillDir of allSkillDirs) {
534
- const skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
557
+ let skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
535
558
  if (skill && !seenNames.has(skill.name)) {
559
+ skill = enhanceSkill(skill);
536
560
  skills.push(skill);
537
561
  seenNames.add(skill.name);
538
562
  }
@@ -610,8 +634,8 @@ const agents = {
610
634
  cline: {
611
635
  name: "cline",
612
636
  displayName: "Cline",
613
- skillsDir: ".cline/skills",
614
- globalSkillsDir: join(home, ".cline/skills"),
637
+ skillsDir: ".agents/skills",
638
+ globalSkillsDir: join(home, ".agents", "skills"),
615
639
  detectInstalled: async () => {
616
640
  return existsSync(join(home, ".cline"));
617
641
  }
@@ -1140,78 +1164,6 @@ function getCanonicalPath(skillName, options = {}) {
1140
1164
  if (!isPathSafe(canonicalBase, canonicalPath)) throw new Error("Invalid skill name: potential path traversal detected");
1141
1165
  return canonicalPath;
1142
1166
  }
1143
- async function installRemoteSkillForAgent(skill, agentType, options = {}) {
1144
- const agent = agents[agentType];
1145
- const isGlobal = options.global ?? false;
1146
- const cwd = options.cwd || process.cwd();
1147
- const installMode = options.mode ?? "symlink";
1148
- if (isGlobal && agent.globalSkillsDir === void 0) return {
1149
- success: false,
1150
- path: "",
1151
- mode: installMode,
1152
- error: `${agent.displayName} does not support global skill installation`
1153
- };
1154
- const skillName = sanitizeName(skill.installName);
1155
- const canonicalBase = getCanonicalSkillsDir(isGlobal, cwd);
1156
- const canonicalDir = join(canonicalBase, skillName);
1157
- const agentBase = getAgentBaseDir(agentType, isGlobal, cwd);
1158
- const agentDir = join(agentBase, skillName);
1159
- if (!isPathSafe(canonicalBase, canonicalDir)) return {
1160
- success: false,
1161
- path: agentDir,
1162
- mode: installMode,
1163
- error: "Invalid skill name: potential path traversal detected"
1164
- };
1165
- if (!isPathSafe(agentBase, agentDir)) return {
1166
- success: false,
1167
- path: agentDir,
1168
- mode: installMode,
1169
- error: "Invalid skill name: potential path traversal detected"
1170
- };
1171
- try {
1172
- if (installMode === "copy") {
1173
- await cleanAndCreateDirectory(agentDir);
1174
- await writeFile(join(agentDir, "SKILL.md"), skill.content, "utf-8");
1175
- return {
1176
- success: true,
1177
- path: agentDir,
1178
- mode: "copy"
1179
- };
1180
- }
1181
- await cleanAndCreateDirectory(canonicalDir);
1182
- await writeFile(join(canonicalDir, "SKILL.md"), skill.content, "utf-8");
1183
- if (isGlobal && isUniversalAgent(agentType)) return {
1184
- success: true,
1185
- path: canonicalDir,
1186
- canonicalPath: canonicalDir,
1187
- mode: "symlink"
1188
- };
1189
- if (!await createSymlink(canonicalDir, agentDir)) {
1190
- await cleanAndCreateDirectory(agentDir);
1191
- await writeFile(join(agentDir, "SKILL.md"), skill.content, "utf-8");
1192
- return {
1193
- success: true,
1194
- path: agentDir,
1195
- canonicalPath: canonicalDir,
1196
- mode: "symlink",
1197
- symlinkFailed: true
1198
- };
1199
- }
1200
- return {
1201
- success: true,
1202
- path: agentDir,
1203
- canonicalPath: canonicalDir,
1204
- mode: "symlink"
1205
- };
1206
- } catch (error) {
1207
- return {
1208
- success: false,
1209
- path: agentDir,
1210
- mode: installMode,
1211
- error: error instanceof Error ? error.message : "Unknown error"
1212
- };
1213
- }
1214
- }
1215
1167
  async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
1216
1168
  const agent = agents[agentType];
1217
1169
  const isGlobal = options.global ?? false;
@@ -1456,108 +1408,7 @@ var ProviderRegistryImpl = class {
1456
1408
  return [...this.providers];
1457
1409
  }
1458
1410
  };
1459
- const registry = new ProviderRegistryImpl();
1460
- function registerProvider(provider) {
1461
- registry.register(provider);
1462
- }
1463
- function findProvider(url) {
1464
- return registry.findProvider(url);
1465
- }
1466
- var MintlifyProvider = class {
1467
- id = "mintlify";
1468
- displayName = "Mintlify";
1469
- match(url) {
1470
- if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
1471
- if (!url.toLowerCase().endsWith("/skill.md")) return { matches: false };
1472
- if (url.includes("github.com") || url.includes("gitlab.com")) return { matches: false };
1473
- if (url.includes("huggingface.co")) return { matches: false };
1474
- return { matches: true };
1475
- }
1476
- async fetchSkill(url) {
1477
- try {
1478
- const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
1479
- if (!response.ok) return null;
1480
- const content = await response.text();
1481
- const { data } = (0, import_gray_matter.default)(content);
1482
- const mintlifySite = data.metadata?.["mintlify-proj"];
1483
- if (!mintlifySite) return null;
1484
- if (!data.name || !data.description) return null;
1485
- return {
1486
- name: data.name,
1487
- description: data.description,
1488
- content,
1489
- installName: mintlifySite,
1490
- sourceUrl: url,
1491
- metadata: data.metadata
1492
- };
1493
- } catch {
1494
- return null;
1495
- }
1496
- }
1497
- toRawUrl(url) {
1498
- return url;
1499
- }
1500
- getSourceIdentifier(url) {
1501
- return "mintlify/com";
1502
- }
1503
- };
1504
- const mintlifyProvider = new MintlifyProvider();
1505
- var HuggingFaceProvider = class {
1506
- id = "huggingface";
1507
- displayName = "HuggingFace";
1508
- HOST = "huggingface.co";
1509
- match(url) {
1510
- if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
1511
- try {
1512
- if (new URL(url).hostname !== this.HOST) return { matches: false };
1513
- } catch {
1514
- return { matches: false };
1515
- }
1516
- if (!url.toLowerCase().endsWith("/skill.md")) return { matches: false };
1517
- if (!url.includes("/spaces/")) return { matches: false };
1518
- return { matches: true };
1519
- }
1520
- async fetchSkill(url) {
1521
- try {
1522
- const rawUrl = this.toRawUrl(url);
1523
- const response = await fetch(rawUrl, { signal: AbortSignal.timeout(3e4) });
1524
- if (!response.ok) return null;
1525
- const content = await response.text();
1526
- const { data } = (0, import_gray_matter.default)(content);
1527
- if (!data.name || !data.description) return null;
1528
- const parsed = this.parseUrl(url);
1529
- if (!parsed) return null;
1530
- const installName = data.metadata?.["install-name"] || parsed.repo;
1531
- return {
1532
- name: data.name,
1533
- description: data.description,
1534
- content,
1535
- installName,
1536
- sourceUrl: url,
1537
- metadata: data.metadata
1538
- };
1539
- } catch {
1540
- return null;
1541
- }
1542
- }
1543
- toRawUrl(url) {
1544
- return url.replace("/blob/", "/raw/");
1545
- }
1546
- getSourceIdentifier(url) {
1547
- const parsed = this.parseUrl(url);
1548
- if (!parsed) return "huggingface/unknown";
1549
- return `huggingface/${parsed.owner}/${parsed.repo}`;
1550
- }
1551
- parseUrl(url) {
1552
- const match = url.match(/\/spaces\/([^/]+)\/([^/]+)/);
1553
- if (!match || !match[1] || !match[2]) return null;
1554
- return {
1555
- owner: match[1],
1556
- repo: match[2]
1557
- };
1558
- }
1559
- };
1560
- const huggingFaceProvider = new HuggingFaceProvider();
1411
+ new ProviderRegistryImpl();
1561
1412
  var WellKnownProvider = class {
1562
1413
  id = "well-known";
1563
1414
  displayName = "Well-Known Skills";
@@ -1714,15 +1565,9 @@ var WellKnownProvider = class {
1714
1565
  }
1715
1566
  getSourceIdentifier(url) {
1716
1567
  try {
1717
- const parsed = new URL(url);
1718
- const hostParts = parsed.hostname.split(".");
1719
- if (hostParts.length >= 2) {
1720
- const tld = hostParts[hostParts.length - 1];
1721
- return `${hostParts[hostParts.length - 2]}/${tld}`;
1722
- }
1723
- return parsed.hostname.replace(".", "/");
1568
+ return new URL(url).hostname.replace(/^www\./, "");
1724
1569
  } catch {
1725
- return "unknown/unknown";
1570
+ return "unknown";
1726
1571
  }
1727
1572
  }
1728
1573
  async hasSkillsIndex(url) {
@@ -1730,28 +1575,6 @@ var WellKnownProvider = class {
1730
1575
  }
1731
1576
  };
1732
1577
  const wellKnownProvider = new WellKnownProvider();
1733
- registerProvider(mintlifyProvider);
1734
- registerProvider(huggingFaceProvider);
1735
- async function fetchMintlifySkill(url) {
1736
- try {
1737
- const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
1738
- if (!response.ok) return null;
1739
- const content = await response.text();
1740
- const { data } = (0, import_gray_matter.default)(content);
1741
- const mintlifySite = data.metadata?.["mintlify-proj"];
1742
- if (!mintlifySite) return null;
1743
- if (!data.name || !data.description) return null;
1744
- return {
1745
- name: data.name,
1746
- description: data.description,
1747
- content,
1748
- mintlifySite,
1749
- sourceUrl: url
1750
- };
1751
- } catch {
1752
- return null;
1753
- }
1754
- }
1755
1578
  const AGENTS_DIR$1 = ".agents";
1756
1579
  const LOCK_FILE$1 = ".skill-lock.json";
1757
1580
  const CURRENT_VERSION$1 = 3;
@@ -1835,6 +1658,9 @@ async function removeSkillFromLock(skillName) {
1835
1658
  async function getSkillFromLock(skillName) {
1836
1659
  return (await readSkillLock$1()).skills[skillName] ?? null;
1837
1660
  }
1661
+ async function getAllLockedSkills() {
1662
+ return (await readSkillLock$1()).skills;
1663
+ }
1838
1664
  function createEmptyLockFile() {
1839
1665
  return {
1840
1666
  version: CURRENT_VERSION$1,
@@ -1925,7 +1751,7 @@ function createEmptyLocalLock() {
1925
1751
  skills: {}
1926
1752
  };
1927
1753
  }
1928
- var version$1 = "1.4.1";
1754
+ var version$1 = "1.4.3";
1929
1755
  const isCancelled$1 = (value) => typeof value === "symbol";
1930
1756
  async function isSourcePrivate(source) {
1931
1757
  const ownerRepo = parseOwnerRepo(source);
@@ -2091,232 +1917,6 @@ async function selectAgentsInteractive(options) {
2091
1917
  return selected;
2092
1918
  }
2093
1919
  setVersion(version$1);
2094
- async function handleRemoteSkill(source, url, options, spinner) {
2095
- const provider = findProvider(url);
2096
- if (!provider) {
2097
- await handleDirectUrlSkillLegacy(source, url, options, spinner);
2098
- return;
2099
- }
2100
- spinner.start(`Fetching skill.md from ${provider.displayName}...`);
2101
- const providerSkill = await provider.fetchSkill(url);
2102
- if (!providerSkill) {
2103
- spinner.stop(import_picocolors.default.red("Invalid skill"));
2104
- Se(import_picocolors.default.red("Could not fetch skill.md or missing required frontmatter (name, description)."));
2105
- process.exit(1);
2106
- }
2107
- const remoteSkill = {
2108
- name: providerSkill.name,
2109
- description: providerSkill.description,
2110
- content: providerSkill.content,
2111
- installName: providerSkill.installName,
2112
- sourceUrl: providerSkill.sourceUrl,
2113
- providerId: provider.id,
2114
- sourceIdentifier: provider.getSourceIdentifier(url),
2115
- metadata: providerSkill.metadata
2116
- };
2117
- spinner.stop(`Found skill: ${import_picocolors.default.cyan(remoteSkill.installName)}`);
2118
- M.info(`Skill: ${import_picocolors.default.cyan(remoteSkill.name)}`);
2119
- M.message(import_picocolors.default.dim(remoteSkill.description));
2120
- M.message(import_picocolors.default.dim(`Source: ${remoteSkill.sourceIdentifier}`));
2121
- if (options.list) {
2122
- console.log();
2123
- M.step(import_picocolors.default.bold("Skill Details"));
2124
- M.message(` ${import_picocolors.default.cyan("Name:")} ${remoteSkill.name}`);
2125
- M.message(` ${import_picocolors.default.cyan("Install as:")} ${remoteSkill.installName}`);
2126
- M.message(` ${import_picocolors.default.cyan("Provider:")} ${provider.displayName}`);
2127
- M.message(` ${import_picocolors.default.cyan("Description:")} ${remoteSkill.description}`);
2128
- console.log();
2129
- Se("Run without --list to install");
2130
- process.exit(0);
2131
- }
2132
- let targetAgents;
2133
- const validAgents = Object.keys(agents);
2134
- const universalAgents = getUniversalAgents();
2135
- if (options.agent?.includes("*")) {
2136
- targetAgents = validAgents;
2137
- M.info(`Installing to all ${targetAgents.length} agents`);
2138
- } else if (options.agent && options.agent.length > 0) {
2139
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
2140
- if (invalidAgents.length > 0) {
2141
- M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
2142
- M.info(`Valid agents: ${validAgents.join(", ")}`);
2143
- process.exit(1);
2144
- }
2145
- targetAgents = options.agent;
2146
- } else {
2147
- spinner.start("Loading agents...");
2148
- const installedAgents = await detectInstalledAgents();
2149
- const totalAgents = Object.keys(agents).length;
2150
- spinner.stop(`${totalAgents} agents`);
2151
- if (installedAgents.length === 0) if (options.yes) {
2152
- targetAgents = universalAgents;
2153
- M.info(`Installing to universal agents`);
2154
- } else {
2155
- const selected = await selectAgentsInteractive({ global: options.global });
2156
- if (pD(selected)) {
2157
- xe("Installation cancelled");
2158
- process.exit(0);
2159
- }
2160
- targetAgents = selected;
2161
- }
2162
- else if (installedAgents.length === 1 || options.yes) {
2163
- targetAgents = ensureUniversalAgents(installedAgents);
2164
- const { universal, symlinked } = splitAgentsByType(targetAgents);
2165
- if (symlinked.length > 0) M.info(`Installing to: ${import_picocolors.default.green("universal")} + ${symlinked.map((a) => import_picocolors.default.cyan(a)).join(", ")}`);
2166
- else M.info(`Installing to: ${import_picocolors.default.green("universal agents")}`);
2167
- } else {
2168
- const selected = await selectAgentsInteractive({ global: options.global });
2169
- if (pD(selected)) {
2170
- xe("Installation cancelled");
2171
- process.exit(0);
2172
- }
2173
- targetAgents = selected;
2174
- }
2175
- }
2176
- let installGlobally = options.global ?? false;
2177
- const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
2178
- if (options.global === void 0 && !options.yes && supportsGlobal) {
2179
- const scope = await ve({
2180
- message: "Installation scope",
2181
- options: [{
2182
- value: false,
2183
- label: "Project",
2184
- hint: "Install in current directory (committed with your project)"
2185
- }, {
2186
- value: true,
2187
- label: "Global",
2188
- hint: "Install in home directory (available across all projects)"
2189
- }]
2190
- });
2191
- if (pD(scope)) {
2192
- xe("Installation cancelled");
2193
- process.exit(0);
2194
- }
2195
- installGlobally = scope;
2196
- }
2197
- let installMode = options.copy ? "copy" : "symlink";
2198
- if (!options.copy && !options.yes) {
2199
- const modeChoice = await ve({
2200
- message: "Installation method",
2201
- options: [{
2202
- value: "symlink",
2203
- label: "Symlink (Recommended)",
2204
- hint: "Single source of truth, easy updates"
2205
- }, {
2206
- value: "copy",
2207
- label: "Copy to all agents",
2208
- hint: "Independent copies for each agent"
2209
- }]
2210
- });
2211
- if (pD(modeChoice)) {
2212
- xe("Installation cancelled");
2213
- process.exit(0);
2214
- }
2215
- installMode = modeChoice;
2216
- }
2217
- const cwd = process.cwd();
2218
- const overwriteChecks = await Promise.all(targetAgents.map(async (agent) => ({
2219
- agent,
2220
- installed: await isSkillInstalled(remoteSkill.installName, agent, { global: installGlobally })
2221
- })));
2222
- const overwriteStatus = new Map(overwriteChecks.map(({ agent, installed }) => [agent, installed]));
2223
- const summaryLines = [];
2224
- const shortCanonical = shortenPath$2(getCanonicalPath(remoteSkill.installName, { global: installGlobally }), cwd);
2225
- summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
2226
- summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
2227
- const overwriteAgents = targetAgents.filter((a) => overwriteStatus.get(a)).map((a) => agents[a].displayName);
2228
- if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
2229
- console.log();
2230
- Me(summaryLines.join("\n"), "Installation Summary");
2231
- if (!options.yes) {
2232
- const confirmed = await ye({ message: "Proceed with installation?" });
2233
- if (pD(confirmed) || !confirmed) {
2234
- xe("Installation cancelled");
2235
- process.exit(0);
2236
- }
2237
- }
2238
- spinner.start("Installing skill...");
2239
- const results = [];
2240
- for (const agent of targetAgents) {
2241
- const result = await installRemoteSkillForAgent(remoteSkill, agent, {
2242
- global: installGlobally,
2243
- mode: installMode
2244
- });
2245
- results.push({
2246
- skill: remoteSkill.installName,
2247
- agent: agents[agent].displayName,
2248
- ...result
2249
- });
2250
- }
2251
- spinner.stop("Installation complete");
2252
- console.log();
2253
- const successful = results.filter((r) => r.success);
2254
- const failed = results.filter((r) => !r.success);
2255
- if (await isSourcePrivate(remoteSkill.sourceIdentifier) !== true) track({
2256
- event: "install",
2257
- source: remoteSkill.sourceIdentifier,
2258
- skills: remoteSkill.installName,
2259
- agents: targetAgents.join(","),
2260
- ...installGlobally && { global: "1" },
2261
- skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
2262
- sourceType: remoteSkill.providerId
2263
- });
2264
- if (successful.length > 0 && installGlobally) try {
2265
- let skillFolderHash = "";
2266
- if (remoteSkill.providerId === "github") {
2267
- const hash = await fetchSkillFolderHash(remoteSkill.sourceIdentifier, url);
2268
- if (hash) skillFolderHash = hash;
2269
- }
2270
- await addSkillToLock(remoteSkill.installName, {
2271
- source: remoteSkill.sourceIdentifier,
2272
- sourceType: remoteSkill.providerId,
2273
- sourceUrl: url,
2274
- skillFolderHash
2275
- });
2276
- } catch {}
2277
- if (successful.length > 0 && !installGlobally) try {
2278
- const firstResult = successful[0];
2279
- const computedHash = await computeSkillFolderHash(firstResult.canonicalPath || firstResult.path);
2280
- await addSkillToLocalLock(remoteSkill.installName, {
2281
- source: remoteSkill.sourceIdentifier,
2282
- sourceType: remoteSkill.providerId,
2283
- computedHash
2284
- }, cwd);
2285
- } catch {}
2286
- if (successful.length > 0) {
2287
- const resultLines = [];
2288
- const firstResult = successful[0];
2289
- if (firstResult.mode === "copy") {
2290
- resultLines.push(`${import_picocolors.default.green("✓")} ${remoteSkill.installName} ${import_picocolors.default.dim("(copied)")}`);
2291
- for (const r of successful) {
2292
- const shortPath = shortenPath$2(r.path, cwd);
2293
- resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
2294
- }
2295
- } else {
2296
- if (firstResult.canonicalPath) {
2297
- const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
2298
- resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
2299
- } else resultLines.push(`${import_picocolors.default.green("✓")} ${remoteSkill.installName}`);
2300
- resultLines.push(...buildResultLines(successful, targetAgents));
2301
- }
2302
- const title = import_picocolors.default.green("Installed 1 skill");
2303
- Me(resultLines.join("\n"), title);
2304
- const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
2305
- if (symlinkFailures.length > 0) {
2306
- const copiedAgentNames = symlinkFailures.map((r) => r.agent);
2307
- M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgentNames)}`));
2308
- M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
2309
- }
2310
- }
2311
- if (failed.length > 0) {
2312
- console.log();
2313
- M.error(import_picocolors.default.red(`Failed to install ${failed.length}`));
2314
- for (const r of failed) M.message(` ${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
2315
- }
2316
- console.log();
2317
- Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
2318
- await promptForFindSkills(options, targetAgents);
2319
- }
2320
1920
  async function handleWellKnownSkills(source, url, options, spinner) {
2321
1921
  spinner.start("Discovering skills from well-known endpoint...");
2322
1922
  const skills = await wellKnownProvider.fetchAllSkills(url);
@@ -2355,7 +1955,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
2355
1955
  for (const s of skills) M.message(` - ${s.installName}`);
2356
1956
  process.exit(1);
2357
1957
  }
2358
- M.info(`Selected ${selectedSkills.length} skill${selectedSkills.length !== 1 ? "s" : ""}: ${selectedSkills.map((s) => import_picocolors.default.cyan(s.installName)).join(", ")}`);
2359
1958
  } else if (skills.length === 1) {
2360
1959
  selectedSkills = skills;
2361
1960
  const firstSkill = skills[0];
@@ -2598,188 +2197,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
2598
2197
  Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
2599
2198
  await promptForFindSkills(options, targetAgents);
2600
2199
  }
2601
- async function handleDirectUrlSkillLegacy(source, url, options, spinner) {
2602
- spinner.start("Fetching skill.md...");
2603
- const mintlifySkill = await fetchMintlifySkill(url);
2604
- if (!mintlifySkill) {
2605
- spinner.stop(import_picocolors.default.red("Invalid skill"));
2606
- Se(import_picocolors.default.red("Could not fetch skill.md or missing required frontmatter (name, description, mintlify-proj)."));
2607
- process.exit(1);
2608
- }
2609
- const remoteSkill = {
2610
- name: mintlifySkill.name,
2611
- description: mintlifySkill.description,
2612
- content: mintlifySkill.content,
2613
- installName: mintlifySkill.mintlifySite,
2614
- sourceUrl: mintlifySkill.sourceUrl,
2615
- providerId: "mintlify",
2616
- sourceIdentifier: "mintlify/com"
2617
- };
2618
- spinner.stop(`Found skill: ${import_picocolors.default.cyan(remoteSkill.installName)}`);
2619
- M.info(`Skill: ${import_picocolors.default.cyan(remoteSkill.name)}`);
2620
- M.message(import_picocolors.default.dim(remoteSkill.description));
2621
- if (options.list) {
2622
- console.log();
2623
- M.step(import_picocolors.default.bold("Skill Details"));
2624
- M.message(` ${import_picocolors.default.cyan("Name:")} ${remoteSkill.name}`);
2625
- M.message(` ${import_picocolors.default.cyan("Site:")} ${remoteSkill.installName}`);
2626
- M.message(` ${import_picocolors.default.cyan("Description:")} ${remoteSkill.description}`);
2627
- console.log();
2628
- Se("Run without --list to install");
2629
- process.exit(0);
2630
- }
2631
- let targetAgents;
2632
- const validAgents = Object.keys(agents);
2633
- if (options.agent?.includes("*")) {
2634
- targetAgents = validAgents;
2635
- M.info(`Installing to all ${targetAgents.length} agents`);
2636
- } else if (options.agent && options.agent.length > 0) {
2637
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
2638
- if (invalidAgents.length > 0) {
2639
- M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
2640
- M.info(`Valid agents: ${validAgents.join(", ")}`);
2641
- process.exit(1);
2642
- }
2643
- targetAgents = options.agent;
2644
- } else {
2645
- spinner.start("Loading agents...");
2646
- const installedAgents = await detectInstalledAgents();
2647
- const totalAgents = Object.keys(agents).length;
2648
- spinner.stop(`${totalAgents} agents`);
2649
- if (installedAgents.length === 0) if (options.yes) {
2650
- targetAgents = validAgents;
2651
- M.info("Installing to all agents");
2652
- } else {
2653
- M.info("Select agents to install skills to");
2654
- const selected = await promptForAgents("Which agents do you want to install to?", Object.entries(agents).map(([key, config]) => ({
2655
- value: key,
2656
- label: config.displayName
2657
- })));
2658
- if (pD(selected)) {
2659
- xe("Installation cancelled");
2660
- process.exit(0);
2661
- }
2662
- targetAgents = selected;
2663
- }
2664
- else if (installedAgents.length === 1 || options.yes) {
2665
- targetAgents = ensureUniversalAgents(installedAgents);
2666
- if (installedAgents.length === 1) {
2667
- const firstAgent = installedAgents[0];
2668
- M.info(`Installing to: ${import_picocolors.default.cyan(agents[firstAgent].displayName)}`);
2669
- } else M.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
2670
- } else {
2671
- const selected = await selectAgentsInteractive({ global: options.global });
2672
- if (pD(selected)) {
2673
- xe("Installation cancelled");
2674
- process.exit(0);
2675
- }
2676
- targetAgents = selected;
2677
- }
2678
- }
2679
- let installGlobally = options.global ?? false;
2680
- const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
2681
- if (options.global === void 0 && !options.yes && supportsGlobal) {
2682
- const scope = await ve({
2683
- message: "Installation scope",
2684
- options: [{
2685
- value: false,
2686
- label: "Project",
2687
- hint: "Install in current directory (committed with your project)"
2688
- }, {
2689
- value: true,
2690
- label: "Global",
2691
- hint: "Install in home directory (available across all projects)"
2692
- }]
2693
- });
2694
- if (pD(scope)) {
2695
- xe("Installation cancelled");
2696
- process.exit(0);
2697
- }
2698
- installGlobally = scope;
2699
- }
2700
- const installMode = "symlink";
2701
- const cwd = process.cwd();
2702
- const overwriteChecks = await Promise.all(targetAgents.map(async (agent) => ({
2703
- agent,
2704
- installed: await isSkillInstalled(remoteSkill.installName, agent, { global: installGlobally })
2705
- })));
2706
- const overwriteStatus = new Map(overwriteChecks.map(({ agent, installed }) => [agent, installed]));
2707
- const summaryLines = [];
2708
- targetAgents.map((a) => agents[a].displayName);
2709
- const shortCanonical = shortenPath$2(getCanonicalPath(remoteSkill.installName, { global: installGlobally }), cwd);
2710
- summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
2711
- summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
2712
- const overwriteAgents = targetAgents.filter((a) => overwriteStatus.get(a)).map((a) => agents[a].displayName);
2713
- if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
2714
- console.log();
2715
- Me(summaryLines.join("\n"), "Installation Summary");
2716
- if (!options.yes) {
2717
- const confirmed = await ye({ message: "Proceed with installation?" });
2718
- if (pD(confirmed) || !confirmed) {
2719
- xe("Installation cancelled");
2720
- process.exit(0);
2721
- }
2722
- }
2723
- spinner.start("Installing skill...");
2724
- const results = [];
2725
- for (const agent of targetAgents) {
2726
- const result = await installRemoteSkillForAgent(remoteSkill, agent, {
2727
- global: installGlobally,
2728
- mode: installMode
2729
- });
2730
- results.push({
2731
- skill: remoteSkill.installName,
2732
- agent: agents[agent].displayName,
2733
- ...result
2734
- });
2735
- }
2736
- spinner.stop("Installation complete");
2737
- console.log();
2738
- const successful = results.filter((r) => r.success);
2739
- const failed = results.filter((r) => !r.success);
2740
- track({
2741
- event: "install",
2742
- source: "mintlify/com",
2743
- skills: remoteSkill.installName,
2744
- agents: targetAgents.join(","),
2745
- ...installGlobally && { global: "1" },
2746
- skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
2747
- sourceType: "mintlify"
2748
- });
2749
- if (successful.length > 0 && installGlobally) try {
2750
- await addSkillToLock(remoteSkill.installName, {
2751
- source: `mintlify/${remoteSkill.installName}`,
2752
- sourceType: "mintlify",
2753
- sourceUrl: url,
2754
- skillFolderHash: ""
2755
- });
2756
- } catch {}
2757
- if (successful.length > 0) {
2758
- const resultLines = [];
2759
- const firstResult = successful[0];
2760
- if (firstResult.canonicalPath) {
2761
- const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
2762
- resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
2763
- } else resultLines.push(`${import_picocolors.default.green("✓")} ${remoteSkill.installName}`);
2764
- resultLines.push(...buildResultLines(successful, targetAgents));
2765
- const title = import_picocolors.default.green("Installed 1 skill");
2766
- Me(resultLines.join("\n"), title);
2767
- const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
2768
- if (symlinkFailures.length > 0) {
2769
- const copiedAgentNames = symlinkFailures.map((r) => r.agent);
2770
- M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgentNames)}`));
2771
- M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
2772
- }
2773
- }
2774
- if (failed.length > 0) {
2775
- console.log();
2776
- M.error(import_picocolors.default.red(`Failed to install ${failed.length}`));
2777
- for (const r of failed) M.message(` ${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
2778
- }
2779
- console.log();
2780
- Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
2781
- await promptForFindSkills(options, targetAgents);
2782
- }
2783
2200
  async function runAdd(args, options = {}) {
2784
2201
  const source = args[0];
2785
2202
  let installTipShown = false;
@@ -2814,10 +2231,6 @@ async function runAdd(args, options = {}) {
2814
2231
  spinner.start("Parsing source...");
2815
2232
  const parsed = parseSource(source);
2816
2233
  spinner.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)}` : ""}`);
2817
- if (parsed.type === "direct-url") {
2818
- await handleRemoteSkill(source, parsed.url, options, spinner);
2819
- return;
2820
- }
2821
2234
  if (parsed.type === "well-known") {
2822
2235
  await handleWellKnownSkills(source, parsed.url, options, spinner);
2823
2236
  return;
@@ -2858,9 +2271,29 @@ async function runAdd(args, options = {}) {
2858
2271
  if (options.list) {
2859
2272
  console.log();
2860
2273
  M.step(import_picocolors.default.bold("Available Skills"));
2861
- for (const skill of skills) {
2862
- M.message(` ${import_picocolors.default.cyan(getSkillDisplayName(skill))}`);
2863
- M.message(` ${import_picocolors.default.dim(skill.description)}`);
2274
+ const groupedSkills = {};
2275
+ const ungroupedSkills = [];
2276
+ for (const skill of skills) if (skill.pluginName) {
2277
+ const group = skill.pluginName;
2278
+ if (!groupedSkills[group]) groupedSkills[group] = [];
2279
+ groupedSkills[group].push(skill);
2280
+ } else ungroupedSkills.push(skill);
2281
+ const sortedGroups = Object.keys(groupedSkills).sort();
2282
+ for (const group of sortedGroups) {
2283
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2284
+ console.log(import_picocolors.default.bold(title));
2285
+ for (const skill of groupedSkills[group]) {
2286
+ M.message(` ${import_picocolors.default.cyan(getSkillDisplayName(skill))}`);
2287
+ M.message(` ${import_picocolors.default.dim(skill.description)}`);
2288
+ }
2289
+ console.log();
2290
+ }
2291
+ if (ungroupedSkills.length > 0) {
2292
+ if (sortedGroups.length > 0) console.log(import_picocolors.default.bold("General"));
2293
+ for (const skill of ungroupedSkills) {
2294
+ M.message(` ${import_picocolors.default.cyan(getSkillDisplayName(skill))}`);
2295
+ M.message(` ${import_picocolors.default.dim(skill.description)}`);
2296
+ }
2864
2297
  }
2865
2298
  console.log();
2866
2299
  Se("Use --skill <name> to install specific skills");
@@ -2890,9 +2323,34 @@ async function runAdd(args, options = {}) {
2890
2323
  selectedSkills = skills;
2891
2324
  M.info(`Installing all ${skills.length} skills`);
2892
2325
  } else {
2893
- const selected = await multiselect({
2326
+ const sortedSkills = [...skills].sort((a, b) => {
2327
+ if (a.pluginName && !b.pluginName) return -1;
2328
+ if (!a.pluginName && b.pluginName) return 1;
2329
+ if (a.pluginName && b.pluginName && a.pluginName !== b.pluginName) return a.pluginName.localeCompare(b.pluginName);
2330
+ return getSkillDisplayName(a).localeCompare(getSkillDisplayName(b));
2331
+ });
2332
+ const hasGroups = sortedSkills.some((s) => s.pluginName);
2333
+ let selected;
2334
+ if (hasGroups) {
2335
+ const kebabToTitle = (s) => s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2336
+ const grouped = {};
2337
+ for (const s of sortedSkills) {
2338
+ const groupName = s.pluginName ? kebabToTitle(s.pluginName) : "Other";
2339
+ if (!grouped[groupName]) grouped[groupName] = [];
2340
+ grouped[groupName].push({
2341
+ value: s,
2342
+ label: getSkillDisplayName(s),
2343
+ hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
2344
+ });
2345
+ }
2346
+ selected = await be({
2347
+ message: `Select skills to install ${import_picocolors.default.dim("(space to toggle)")}`,
2348
+ options: grouped,
2349
+ required: true
2350
+ });
2351
+ } else selected = await multiselect({
2894
2352
  message: "Select skills to install",
2895
- options: skills.map((s) => ({
2353
+ options: sortedSkills.map((s) => ({
2896
2354
  value: s,
2897
2355
  label: getSkillDisplayName(s),
2898
2356
  hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
@@ -3015,14 +2473,37 @@ async function runAdd(args, options = {}) {
3015
2473
  if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
3016
2474
  overwriteStatus.get(skillName).set(agent, installed);
3017
2475
  }
3018
- for (const skill of selectedSkills) {
3019
- if (summaryLines.length > 0) summaryLines.push("");
3020
- const shortCanonical = shortenPath$2(getCanonicalPath(skill.name, { global: installGlobally }), cwd);
3021
- summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
3022
- summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
3023
- const skillOverwrites = overwriteStatus.get(skill.name);
3024
- const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
3025
- if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
2476
+ const groupedSummary = {};
2477
+ const ungroupedSummary = [];
2478
+ for (const skill of selectedSkills) if (skill.pluginName) {
2479
+ const group = skill.pluginName;
2480
+ if (!groupedSummary[group]) groupedSummary[group] = [];
2481
+ groupedSummary[group].push(skill);
2482
+ } else ungroupedSummary.push(skill);
2483
+ const printSkillSummary = (skills) => {
2484
+ for (const skill of skills) {
2485
+ if (summaryLines.length > 0) summaryLines.push("");
2486
+ const shortCanonical = shortenPath$2(getCanonicalPath(skill.name, { global: installGlobally }), cwd);
2487
+ summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
2488
+ summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
2489
+ const skillOverwrites = overwriteStatus.get(skill.name);
2490
+ const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
2491
+ if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
2492
+ }
2493
+ };
2494
+ const sortedGroups = Object.keys(groupedSummary).sort();
2495
+ for (const group of sortedGroups) {
2496
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2497
+ summaryLines.push("");
2498
+ summaryLines.push(import_picocolors.default.bold(title));
2499
+ printSkillSummary(groupedSummary[group]);
2500
+ }
2501
+ if (ungroupedSummary.length > 0) {
2502
+ if (sortedGroups.length > 0) {
2503
+ summaryLines.push("");
2504
+ summaryLines.push(import_picocolors.default.bold("General"));
2505
+ }
2506
+ printSkillSummary(ungroupedSummary);
3026
2507
  }
3027
2508
  console.log();
3028
2509
  Me(summaryLines.join("\n"), "Installation Summary");
@@ -3054,6 +2535,7 @@ async function runAdd(args, options = {}) {
3054
2535
  results.push({
3055
2536
  skill: getSkillDisplayName(skill),
3056
2537
  agent: agents[agent].displayName,
2538
+ pluginName: skill.pluginName,
3057
2539
  ...result
3058
2540
  });
3059
2541
  }
@@ -3106,7 +2588,8 @@ async function runAdd(args, options = {}) {
3106
2588
  sourceType: parsed.type,
3107
2589
  sourceUrl: parsed.url,
3108
2590
  skillPath: skillPathValue,
3109
- skillFolderHash
2591
+ skillFolderHash,
2592
+ pluginName: skill.pluginName
3110
2593
  });
3111
2594
  } catch {}
3112
2595
  }
@@ -3127,30 +2610,54 @@ async function runAdd(args, options = {}) {
3127
2610
  }
3128
2611
  if (successful.length > 0) {
3129
2612
  const bySkill = /* @__PURE__ */ new Map();
2613
+ const groupedResults = {};
2614
+ const ungroupedResults = [];
3130
2615
  for (const r of successful) {
3131
2616
  const skillResults = bySkill.get(r.skill) || [];
3132
2617
  skillResults.push(r);
3133
2618
  bySkill.set(r.skill, skillResults);
2619
+ if (skillResults.length === 1) if (r.pluginName) {
2620
+ const group = r.pluginName;
2621
+ if (!groupedResults[group]) groupedResults[group] = [];
2622
+ groupedResults[group].push(r);
2623
+ } else ungroupedResults.push(r);
3134
2624
  }
3135
2625
  const skillCount = bySkill.size;
3136
2626
  const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
3137
2627
  const copiedAgents = symlinkFailures.map((r) => r.agent);
3138
2628
  const resultLines = [];
3139
- for (const [skillName, skillResults] of bySkill) {
3140
- const firstResult = skillResults[0];
3141
- if (firstResult.mode === "copy") {
3142
- resultLines.push(`${import_picocolors.default.green("✓")} ${skillName} ${import_picocolors.default.dim("(copied)")}`);
3143
- for (const r of skillResults) {
3144
- const shortPath = shortenPath$2(r.path, cwd);
3145
- resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
2629
+ const printSkillResults = (entries) => {
2630
+ for (const entry of entries) {
2631
+ const skillResults = bySkill.get(entry.skill) || [];
2632
+ const firstResult = skillResults[0];
2633
+ if (firstResult.mode === "copy") {
2634
+ resultLines.push(`${import_picocolors.default.green("✓")} ${entry.skill} ${import_picocolors.default.dim("(copied)")}`);
2635
+ for (const r of skillResults) {
2636
+ const shortPath = shortenPath$2(r.path, cwd);
2637
+ resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
2638
+ }
2639
+ } else {
2640
+ if (firstResult.canonicalPath) {
2641
+ const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
2642
+ resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
2643
+ } else resultLines.push(`${import_picocolors.default.green("✓")} ${entry.skill}`);
2644
+ resultLines.push(...buildResultLines(skillResults, targetAgents));
3146
2645
  }
3147
- } else {
3148
- if (firstResult.canonicalPath) {
3149
- const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
3150
- resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
3151
- } else resultLines.push(`${import_picocolors.default.green("✓")} ${skillName}`);
3152
- resultLines.push(...buildResultLines(skillResults, targetAgents));
3153
2646
  }
2647
+ };
2648
+ const sortedResultGroups = Object.keys(groupedResults).sort();
2649
+ for (const group of sortedResultGroups) {
2650
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2651
+ resultLines.push("");
2652
+ resultLines.push(import_picocolors.default.bold(title));
2653
+ printSkillResults(groupedResults[group]);
2654
+ }
2655
+ if (ungroupedResults.length > 0) {
2656
+ if (sortedResultGroups.length > 0) {
2657
+ resultLines.push("");
2658
+ resultLines.push(import_picocolors.default.bold("General"));
2659
+ }
2660
+ printSkillResults(ungroupedResults);
3154
2661
  }
3155
2662
  const title = import_picocolors.default.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""}`);
3156
2663
  Me(resultLines.join("\n"), title);
@@ -3864,6 +3371,7 @@ async function runList(args) {
3864
3371
  global: scope,
3865
3372
  agentFilter
3866
3373
  });
3374
+ const lockedSkills = await getAllLockedSkills();
3867
3375
  const cwd = process.cwd();
3868
3376
  const scopeLabel = scope ? "Global" : "Project";
3869
3377
  if (installedSkills.length === 0) {
@@ -3872,17 +3380,44 @@ async function runList(args) {
3872
3380
  else console.log(`${DIM$1}Try listing global skills with -g${RESET$1}`);
3873
3381
  return;
3874
3382
  }
3875
- function printSkill(skill) {
3383
+ function printSkill(skill, indent = false) {
3384
+ const prefix = indent ? " " : "";
3876
3385
  const shortPath = shortenPath(skill.canonicalPath, cwd);
3877
3386
  const agentNames = skill.agents.map((a) => agents[a].displayName);
3878
3387
  const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW}not linked${RESET$1}`;
3879
- console.log(`${CYAN}${skill.name}${RESET$1} ${DIM$1}${shortPath}${RESET$1}`);
3880
- console.log(` ${DIM$1}Agents:${RESET$1} ${agentInfo}`);
3388
+ console.log(`${prefix}${CYAN}${skill.name}${RESET$1} ${DIM$1}${shortPath}${RESET$1}`);
3389
+ console.log(`${prefix} ${DIM$1}Agents:${RESET$1} ${agentInfo}`);
3881
3390
  }
3882
3391
  console.log(`${BOLD$1}${scopeLabel} Skills${RESET$1}`);
3883
3392
  console.log();
3884
- for (const skill of installedSkills) printSkill(skill);
3885
- console.log();
3393
+ const groupedSkills = {};
3394
+ const ungroupedSkills = [];
3395
+ for (const skill of installedSkills) {
3396
+ const lockEntry = lockedSkills[skill.name];
3397
+ if (lockEntry?.pluginName) {
3398
+ const group = lockEntry.pluginName;
3399
+ if (!groupedSkills[group]) groupedSkills[group] = [];
3400
+ groupedSkills[group].push(skill);
3401
+ } else ungroupedSkills.push(skill);
3402
+ }
3403
+ if (Object.keys(groupedSkills).length > 0) {
3404
+ const sortedGroups = Object.keys(groupedSkills).sort();
3405
+ for (const group of sortedGroups) {
3406
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3407
+ console.log(`${BOLD$1}${title}${RESET$1}`);
3408
+ const skills = groupedSkills[group];
3409
+ if (skills) for (const skill of skills) printSkill(skill, true);
3410
+ console.log();
3411
+ }
3412
+ if (ungroupedSkills.length > 0) {
3413
+ console.log(`${BOLD$1}General${RESET$1}`);
3414
+ for (const skill of ungroupedSkills) printSkill(skill, true);
3415
+ console.log();
3416
+ }
3417
+ } else {
3418
+ for (const skill of installedSkills) printSkill(skill);
3419
+ console.log();
3420
+ }
3886
3421
  }
3887
3422
  async function removeCommand(skillNames, options) {
3888
3423
  const isGlobal = options.global ?? false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {