skills 1.4.1 → 1.4.2

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";
@@ -424,6 +424,36 @@ async function getPluginSkillPaths(basePath) {
424
424
  } catch {}
425
425
  return searchDirs;
426
426
  }
427
+ async function getPluginGroupings(basePath) {
428
+ const groupings = /* @__PURE__ */ new Map();
429
+ try {
430
+ const content = await readFile(join(basePath, ".claude-plugin/marketplace.json"), "utf-8");
431
+ const manifest = JSON.parse(content);
432
+ const pluginRoot = manifest.metadata?.pluginRoot;
433
+ if (pluginRoot === void 0 || isValidRelativePath(pluginRoot)) for (const plugin of manifest.plugins ?? []) {
434
+ if (!plugin.name) continue;
435
+ if (typeof plugin.source !== "string" && plugin.source !== void 0) continue;
436
+ if (plugin.source !== void 0 && !isValidRelativePath(plugin.source)) continue;
437
+ const pluginBase = join(basePath, pluginRoot ?? "", plugin.source ?? "");
438
+ if (!isContainedIn(pluginBase, basePath)) continue;
439
+ if (plugin.skills && plugin.skills.length > 0) for (const skillPath of plugin.skills) {
440
+ if (!isValidRelativePath(skillPath)) continue;
441
+ const skillDir = join(pluginBase, skillPath);
442
+ if (isContainedIn(skillDir, basePath)) groupings.set(resolve(skillDir), plugin.name);
443
+ }
444
+ }
445
+ } catch {}
446
+ try {
447
+ const content = await readFile(join(basePath, ".claude-plugin/plugin.json"), "utf-8");
448
+ const manifest = JSON.parse(content);
449
+ if (manifest.name && manifest.skills && manifest.skills.length > 0) for (const skillPath of manifest.skills) {
450
+ if (!isValidRelativePath(skillPath)) continue;
451
+ const skillDir = join(basePath, skillPath);
452
+ if (isContainedIn(skillDir, basePath)) groupings.set(resolve(skillDir), manifest.name);
453
+ }
454
+ } catch {}
455
+ return groupings;
456
+ }
427
457
  const SKIP_DIRS = [
428
458
  "node_modules",
429
459
  ".git",
@@ -475,9 +505,16 @@ async function discoverSkills(basePath, subpath, options) {
475
505
  const skills = [];
476
506
  const seenNames = /* @__PURE__ */ new Set();
477
507
  const searchPath = subpath ? join(basePath, subpath) : basePath;
508
+ const pluginGroupings = await getPluginGroupings(searchPath);
509
+ const enhanceSkill = (skill) => {
510
+ const resolvedPath = resolve(skill.path);
511
+ if (pluginGroupings.has(resolvedPath)) skill.pluginName = pluginGroupings.get(resolvedPath);
512
+ return skill;
513
+ };
478
514
  if (await hasSkillMd(searchPath)) {
479
- const skill = await parseSkillMd(join(searchPath, "SKILL.md"), options);
515
+ let skill = await parseSkillMd(join(searchPath, "SKILL.md"), options);
480
516
  if (skill) {
517
+ skill = enhanceSkill(skill);
481
518
  skills.push(skill);
482
519
  seenNames.add(skill.name);
483
520
  if (!options?.fullDepth) return skills;
@@ -520,8 +557,9 @@ async function discoverSkills(basePath, subpath, options) {
520
557
  for (const entry of entries) if (entry.isDirectory()) {
521
558
  const skillDir = join(dir, entry.name);
522
559
  if (await hasSkillMd(skillDir)) {
523
- const skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
560
+ let skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
524
561
  if (skill && !seenNames.has(skill.name)) {
562
+ skill = enhanceSkill(skill);
525
563
  skills.push(skill);
526
564
  seenNames.add(skill.name);
527
565
  }
@@ -531,8 +569,9 @@ async function discoverSkills(basePath, subpath, options) {
531
569
  if (skills.length === 0 || options?.fullDepth) {
532
570
  const allSkillDirs = await findSkillDirs(searchPath);
533
571
  for (const skillDir of allSkillDirs) {
534
- const skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
572
+ let skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
535
573
  if (skill && !seenNames.has(skill.name)) {
574
+ skill = enhanceSkill(skill);
536
575
  skills.push(skill);
537
576
  seenNames.add(skill.name);
538
577
  }
@@ -610,8 +649,8 @@ const agents = {
610
649
  cline: {
611
650
  name: "cline",
612
651
  displayName: "Cline",
613
- skillsDir: ".cline/skills",
614
- globalSkillsDir: join(home, ".cline/skills"),
652
+ skillsDir: ".agents/skills",
653
+ globalSkillsDir: join(home, ".agents", "skills"),
615
654
  detectInstalled: async () => {
616
655
  return existsSync(join(home, ".cline"));
617
656
  }
@@ -1835,6 +1874,9 @@ async function removeSkillFromLock(skillName) {
1835
1874
  async function getSkillFromLock(skillName) {
1836
1875
  return (await readSkillLock$1()).skills[skillName] ?? null;
1837
1876
  }
1877
+ async function getAllLockedSkills() {
1878
+ return (await readSkillLock$1()).skills;
1879
+ }
1838
1880
  function createEmptyLockFile() {
1839
1881
  return {
1840
1882
  version: CURRENT_VERSION$1,
@@ -1925,7 +1967,7 @@ function createEmptyLocalLock() {
1925
1967
  skills: {}
1926
1968
  };
1927
1969
  }
1928
- var version$1 = "1.4.1";
1970
+ var version$1 = "1.4.2";
1929
1971
  const isCancelled$1 = (value) => typeof value === "symbol";
1930
1972
  async function isSourcePrivate(source) {
1931
1973
  const ownerRepo = parseOwnerRepo(source);
@@ -2355,7 +2397,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
2355
2397
  for (const s of skills) M.message(` - ${s.installName}`);
2356
2398
  process.exit(1);
2357
2399
  }
2358
- M.info(`Selected ${selectedSkills.length} skill${selectedSkills.length !== 1 ? "s" : ""}: ${selectedSkills.map((s) => import_picocolors.default.cyan(s.installName)).join(", ")}`);
2359
2400
  } else if (skills.length === 1) {
2360
2401
  selectedSkills = skills;
2361
2402
  const firstSkill = skills[0];
@@ -2858,9 +2899,29 @@ async function runAdd(args, options = {}) {
2858
2899
  if (options.list) {
2859
2900
  console.log();
2860
2901
  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)}`);
2902
+ const groupedSkills = {};
2903
+ const ungroupedSkills = [];
2904
+ for (const skill of skills) if (skill.pluginName) {
2905
+ const group = skill.pluginName;
2906
+ if (!groupedSkills[group]) groupedSkills[group] = [];
2907
+ groupedSkills[group].push(skill);
2908
+ } else ungroupedSkills.push(skill);
2909
+ const sortedGroups = Object.keys(groupedSkills).sort();
2910
+ for (const group of sortedGroups) {
2911
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2912
+ console.log(import_picocolors.default.bold(title));
2913
+ for (const skill of groupedSkills[group]) {
2914
+ M.message(` ${import_picocolors.default.cyan(getSkillDisplayName(skill))}`);
2915
+ M.message(` ${import_picocolors.default.dim(skill.description)}`);
2916
+ }
2917
+ console.log();
2918
+ }
2919
+ if (ungroupedSkills.length > 0) {
2920
+ if (sortedGroups.length > 0) console.log(import_picocolors.default.bold("General"));
2921
+ for (const skill of ungroupedSkills) {
2922
+ M.message(` ${import_picocolors.default.cyan(getSkillDisplayName(skill))}`);
2923
+ M.message(` ${import_picocolors.default.dim(skill.description)}`);
2924
+ }
2864
2925
  }
2865
2926
  console.log();
2866
2927
  Se("Use --skill <name> to install specific skills");
@@ -2890,9 +2951,34 @@ async function runAdd(args, options = {}) {
2890
2951
  selectedSkills = skills;
2891
2952
  M.info(`Installing all ${skills.length} skills`);
2892
2953
  } else {
2893
- const selected = await multiselect({
2954
+ const sortedSkills = [...skills].sort((a, b) => {
2955
+ if (a.pluginName && !b.pluginName) return -1;
2956
+ if (!a.pluginName && b.pluginName) return 1;
2957
+ if (a.pluginName && b.pluginName && a.pluginName !== b.pluginName) return a.pluginName.localeCompare(b.pluginName);
2958
+ return getSkillDisplayName(a).localeCompare(getSkillDisplayName(b));
2959
+ });
2960
+ const hasGroups = sortedSkills.some((s) => s.pluginName);
2961
+ let selected;
2962
+ if (hasGroups) {
2963
+ const kebabToTitle = (s) => s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2964
+ const grouped = {};
2965
+ for (const s of sortedSkills) {
2966
+ const groupName = s.pluginName ? kebabToTitle(s.pluginName) : "Other";
2967
+ if (!grouped[groupName]) grouped[groupName] = [];
2968
+ grouped[groupName].push({
2969
+ value: s,
2970
+ label: getSkillDisplayName(s),
2971
+ hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
2972
+ });
2973
+ }
2974
+ selected = await be({
2975
+ message: `Select skills to install ${import_picocolors.default.dim("(space to toggle)")}`,
2976
+ options: grouped,
2977
+ required: true
2978
+ });
2979
+ } else selected = await multiselect({
2894
2980
  message: "Select skills to install",
2895
- options: skills.map((s) => ({
2981
+ options: sortedSkills.map((s) => ({
2896
2982
  value: s,
2897
2983
  label: getSkillDisplayName(s),
2898
2984
  hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
@@ -3015,14 +3101,37 @@ async function runAdd(args, options = {}) {
3015
3101
  if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
3016
3102
  overwriteStatus.get(skillName).set(agent, installed);
3017
3103
  }
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)}`);
3104
+ const groupedSummary = {};
3105
+ const ungroupedSummary = [];
3106
+ for (const skill of selectedSkills) if (skill.pluginName) {
3107
+ const group = skill.pluginName;
3108
+ if (!groupedSummary[group]) groupedSummary[group] = [];
3109
+ groupedSummary[group].push(skill);
3110
+ } else ungroupedSummary.push(skill);
3111
+ const printSkillSummary = (skills) => {
3112
+ for (const skill of skills) {
3113
+ if (summaryLines.length > 0) summaryLines.push("");
3114
+ const shortCanonical = shortenPath$2(getCanonicalPath(skill.name, { global: installGlobally }), cwd);
3115
+ summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
3116
+ summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
3117
+ const skillOverwrites = overwriteStatus.get(skill.name);
3118
+ const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
3119
+ if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
3120
+ }
3121
+ };
3122
+ const sortedGroups = Object.keys(groupedSummary).sort();
3123
+ for (const group of sortedGroups) {
3124
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3125
+ summaryLines.push("");
3126
+ summaryLines.push(import_picocolors.default.bold(title));
3127
+ printSkillSummary(groupedSummary[group]);
3128
+ }
3129
+ if (ungroupedSummary.length > 0) {
3130
+ if (sortedGroups.length > 0) {
3131
+ summaryLines.push("");
3132
+ summaryLines.push(import_picocolors.default.bold("General"));
3133
+ }
3134
+ printSkillSummary(ungroupedSummary);
3026
3135
  }
3027
3136
  console.log();
3028
3137
  Me(summaryLines.join("\n"), "Installation Summary");
@@ -3054,6 +3163,7 @@ async function runAdd(args, options = {}) {
3054
3163
  results.push({
3055
3164
  skill: getSkillDisplayName(skill),
3056
3165
  agent: agents[agent].displayName,
3166
+ pluginName: skill.pluginName,
3057
3167
  ...result
3058
3168
  });
3059
3169
  }
@@ -3106,7 +3216,8 @@ async function runAdd(args, options = {}) {
3106
3216
  sourceType: parsed.type,
3107
3217
  sourceUrl: parsed.url,
3108
3218
  skillPath: skillPathValue,
3109
- skillFolderHash
3219
+ skillFolderHash,
3220
+ pluginName: skill.pluginName
3110
3221
  });
3111
3222
  } catch {}
3112
3223
  }
@@ -3127,30 +3238,54 @@ async function runAdd(args, options = {}) {
3127
3238
  }
3128
3239
  if (successful.length > 0) {
3129
3240
  const bySkill = /* @__PURE__ */ new Map();
3241
+ const groupedResults = {};
3242
+ const ungroupedResults = [];
3130
3243
  for (const r of successful) {
3131
3244
  const skillResults = bySkill.get(r.skill) || [];
3132
3245
  skillResults.push(r);
3133
3246
  bySkill.set(r.skill, skillResults);
3247
+ if (skillResults.length === 1) if (r.pluginName) {
3248
+ const group = r.pluginName;
3249
+ if (!groupedResults[group]) groupedResults[group] = [];
3250
+ groupedResults[group].push(r);
3251
+ } else ungroupedResults.push(r);
3134
3252
  }
3135
3253
  const skillCount = bySkill.size;
3136
3254
  const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
3137
3255
  const copiedAgents = symlinkFailures.map((r) => r.agent);
3138
3256
  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}`);
3257
+ const printSkillResults = (entries) => {
3258
+ for (const entry of entries) {
3259
+ const skillResults = bySkill.get(entry.skill) || [];
3260
+ const firstResult = skillResults[0];
3261
+ if (firstResult.mode === "copy") {
3262
+ resultLines.push(`${import_picocolors.default.green("✓")} ${entry.skill} ${import_picocolors.default.dim("(copied)")}`);
3263
+ for (const r of skillResults) {
3264
+ const shortPath = shortenPath$2(r.path, cwd);
3265
+ resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
3266
+ }
3267
+ } else {
3268
+ if (firstResult.canonicalPath) {
3269
+ const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
3270
+ resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
3271
+ } else resultLines.push(`${import_picocolors.default.green("✓")} ${entry.skill}`);
3272
+ resultLines.push(...buildResultLines(skillResults, targetAgents));
3146
3273
  }
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
3274
  }
3275
+ };
3276
+ const sortedResultGroups = Object.keys(groupedResults).sort();
3277
+ for (const group of sortedResultGroups) {
3278
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3279
+ resultLines.push("");
3280
+ resultLines.push(import_picocolors.default.bold(title));
3281
+ printSkillResults(groupedResults[group]);
3282
+ }
3283
+ if (ungroupedResults.length > 0) {
3284
+ if (sortedResultGroups.length > 0) {
3285
+ resultLines.push("");
3286
+ resultLines.push(import_picocolors.default.bold("General"));
3287
+ }
3288
+ printSkillResults(ungroupedResults);
3154
3289
  }
3155
3290
  const title = import_picocolors.default.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""}`);
3156
3291
  Me(resultLines.join("\n"), title);
@@ -3864,6 +3999,7 @@ async function runList(args) {
3864
3999
  global: scope,
3865
4000
  agentFilter
3866
4001
  });
4002
+ const lockedSkills = await getAllLockedSkills();
3867
4003
  const cwd = process.cwd();
3868
4004
  const scopeLabel = scope ? "Global" : "Project";
3869
4005
  if (installedSkills.length === 0) {
@@ -3872,17 +4008,44 @@ async function runList(args) {
3872
4008
  else console.log(`${DIM$1}Try listing global skills with -g${RESET$1}`);
3873
4009
  return;
3874
4010
  }
3875
- function printSkill(skill) {
4011
+ function printSkill(skill, indent = false) {
4012
+ const prefix = indent ? " " : "";
3876
4013
  const shortPath = shortenPath(skill.canonicalPath, cwd);
3877
4014
  const agentNames = skill.agents.map((a) => agents[a].displayName);
3878
4015
  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}`);
4016
+ console.log(`${prefix}${CYAN}${skill.name}${RESET$1} ${DIM$1}${shortPath}${RESET$1}`);
4017
+ console.log(`${prefix} ${DIM$1}Agents:${RESET$1} ${agentInfo}`);
3881
4018
  }
3882
4019
  console.log(`${BOLD$1}${scopeLabel} Skills${RESET$1}`);
3883
4020
  console.log();
3884
- for (const skill of installedSkills) printSkill(skill);
3885
- console.log();
4021
+ const groupedSkills = {};
4022
+ const ungroupedSkills = [];
4023
+ for (const skill of installedSkills) {
4024
+ const lockEntry = lockedSkills[skill.name];
4025
+ if (lockEntry?.pluginName) {
4026
+ const group = lockEntry.pluginName;
4027
+ if (!groupedSkills[group]) groupedSkills[group] = [];
4028
+ groupedSkills[group].push(skill);
4029
+ } else ungroupedSkills.push(skill);
4030
+ }
4031
+ if (Object.keys(groupedSkills).length > 0) {
4032
+ const sortedGroups = Object.keys(groupedSkills).sort();
4033
+ for (const group of sortedGroups) {
4034
+ const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
4035
+ console.log(`${BOLD$1}${title}${RESET$1}`);
4036
+ const skills = groupedSkills[group];
4037
+ if (skills) for (const skill of skills) printSkill(skill, true);
4038
+ console.log();
4039
+ }
4040
+ if (ungroupedSkills.length > 0) {
4041
+ console.log(`${BOLD$1}General${RESET$1}`);
4042
+ for (const skill of ungroupedSkills) printSkill(skill, true);
4043
+ console.log();
4044
+ }
4045
+ } else {
4046
+ for (const skill of installedSkills) printSkill(skill);
4047
+ console.log();
4048
+ }
3886
4049
  }
3887
4050
  async function removeCommand(skillNames, options) {
3888
4051
  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.2",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {