ccski 1.0.3 → 2.0.0

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.
@@ -1,41 +1,11 @@
1
- import { createRequire } from "node:module";
1
+ import { n as __require, r as __toESM, t as __commonJS } from "./chunk-B7onSSqq.mjs";
2
2
  import { existsSync, readFileSync, readdirSync } from "node:fs";
3
3
  import { TextDecoder } from "node:util";
4
4
  import { homedir } from "node:os";
5
- import { dirname, isAbsolute, join, resolve } from "node:path";
5
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
6
  import * as tty from "tty";
7
+ import debug from "debug";
7
8
 
8
- //#region rolldown:runtime
9
- var __create = Object.create;
10
- var __defProp = Object.defineProperty;
11
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
- var __getOwnPropNames = Object.getOwnPropertyNames;
13
- var __getProtoOf = Object.getPrototypeOf;
14
- var __hasOwnProp = Object.prototype.hasOwnProperty;
15
- var __commonJS = (cb, mod) => function() {
16
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
17
- };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (var keys = __getOwnPropNames(from), i$1 = 0, n = keys.length, key; i$1 < n; i$1++) {
21
- key = keys[i$1];
22
- if (!__hasOwnProp.call(to, key) && key !== except) {
23
- __defProp(to, key, {
24
- get: ((k) => from[k]).bind(null, key),
25
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
26
- });
27
- }
28
- }
29
- }
30
- return to;
31
- };
32
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
33
- value: mod,
34
- enumerable: true
35
- }) : target, mod));
36
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
37
-
38
- //#endregion
39
9
  //#region src/types/errors.ts
40
10
  /**
41
11
  * Base error class for ccski errors
@@ -62,8 +32,8 @@ var SkillNotFoundError = class extends CcskiError {
62
32
  * Error thrown when a skill name is ambiguous
63
33
  */
64
34
  var AmbiguousSkillNameError = class extends CcskiError {
65
- constructor(skillName, matches) {
66
- super(`Skill name '${skillName}' is ambiguous. Multiple skills found: ${matches.join(", ")}`);
35
+ constructor(skillName, matches, suggestions = []) {
36
+ super(`Skill name '${skillName}' is ambiguous. Multiple skills found: ${matches.join(", ")}`, suggestions.length ? suggestions : matches);
67
37
  this.skillName = skillName;
68
38
  this.matches = matches;
69
39
  this.name = "AmbiguousSkillNameError";
@@ -11699,9 +11669,9 @@ var require_excerpt = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/gray-matt
11699
11669
  const opts = defaults$2(options);
11700
11670
  if (file.data == null) file.data = {};
11701
11671
  if (typeof opts.excerpt === "function") return opts.excerpt(file, opts);
11702
- const sep = file.data.excerpt_separator || opts.excerpt_separator;
11703
- if (sep == null && (opts.excerpt === false || opts.excerpt == null)) return file;
11704
- const delimiter = typeof opts.excerpt === "string" ? opts.excerpt : sep || opts.delimiters[0];
11672
+ const sep$1 = file.data.excerpt_separator || opts.excerpt_separator;
11673
+ if (sep$1 == null && (opts.excerpt === false || opts.excerpt == null)) return file;
11674
+ const delimiter = typeof opts.excerpt === "string" ? opts.excerpt : sep$1 || opts.delimiters[0];
11705
11675
  const idx = file.content.indexOf(delimiter);
11706
11676
  if (idx !== -1) file.excerpt = file.content.slice(0, idx);
11707
11677
  return file;
@@ -12323,9 +12293,37 @@ function error(text) {
12323
12293
  function warn(text) {
12324
12294
  return tone.warning(text);
12325
12295
  }
12296
+ function info(text) {
12297
+ return tone.info(text);
12298
+ }
12326
12299
  function heading(text) {
12327
12300
  return tone.underline(tone.bold(tone.accent(text)));
12328
12301
  }
12302
+ const SUPERSCRIPT_DIGITS = [
12303
+ "⁰",
12304
+ "¹",
12305
+ "²",
12306
+ "³",
12307
+ "⁴",
12308
+ "⁵",
12309
+ "⁶",
12310
+ "⁷",
12311
+ "⁸",
12312
+ "⁹"
12313
+ ];
12314
+ function toSuperscript(num) {
12315
+ return num.toString().split("").map((d) => SUPERSCRIPT_DIGITS[parseInt(d, 10)] ?? d).join("");
12316
+ }
12317
+ /**
12318
+ * Create a duplicate badge with superscript number.
12319
+ * isPrimary: the chosen skill in auto mode (shows just the number)
12320
+ * !isPrimary: the duplicate that was filtered out (shows [dup] + number)
12321
+ */
12322
+ function duplicateBadge(groupIndex, isPrimary) {
12323
+ const sup = toSuperscript(groupIndex);
12324
+ if (isPrimary) return tone.accent(sup);
12325
+ return tone.accent(`[dup]${sup}`);
12326
+ }
12329
12327
  function escapeRegExp$1(value) {
12330
12328
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12331
12329
  }
@@ -12353,13 +12351,15 @@ function renderList(items) {
12353
12351
  const meta = item.meta ? ` ${dim(item.meta)}` : "";
12354
12352
  const badge = item.badge ? ` ${item.badge}` : "";
12355
12353
  const wrapWidth = Math.max(20, Math.min(process.stdout?.columns ?? 80, 120) - 4);
12356
- return `- ${coloredTitle}${badge}${meta}${item.description ? "\n " + wrap(item.description, {
12354
+ const desc = item.description;
12355
+ const wrapped = desc ? wrap(desc, {
12357
12356
  width: wrapWidth,
12358
12357
  indent: "",
12359
12358
  newline: "\n",
12360
12359
  trim: true,
12361
12360
  cut: false
12362
- }).replace(/\n/g, "\n ") : ""}`;
12361
+ }) : "";
12362
+ return `- ${coloredTitle}${badge}${meta}${wrapped && wrapped.length > 0 ? "\n " + wrapped.replace(/\n/g, "\n ") : ""}`;
12363
12363
  }).join("\n\n");
12364
12364
  }
12365
12365
  function padAnsi(value, width) {
@@ -12373,15 +12373,34 @@ function displayWidth(value) {
12373
12373
 
12374
12374
  //#endregion
12375
12375
  //#region src/core/discovery.ts
12376
- /**
12377
- * Skill directories in priority order (highest to lowest)
12378
- */
12379
- const SKILL_DIRECTORIES = [
12380
- ".agent/skills",
12381
- ".claude/skills",
12382
- `${homedir()}/.agent/skills`,
12383
- `${homedir()}/.claude/skills`
12384
- ];
12376
+ function getDefaultSkillDirectories(userDir) {
12377
+ return [
12378
+ {
12379
+ path: ".agent/skills",
12380
+ provider: "claude"
12381
+ },
12382
+ {
12383
+ path: ".claude/skills",
12384
+ provider: "claude"
12385
+ },
12386
+ {
12387
+ path: join(userDir, ".agent/skills"),
12388
+ provider: "claude"
12389
+ },
12390
+ {
12391
+ path: join(userDir, ".claude/skills"),
12392
+ provider: "claude"
12393
+ },
12394
+ {
12395
+ path: ".codex/skills",
12396
+ provider: "codex"
12397
+ },
12398
+ {
12399
+ path: join(userDir, ".codex/skills"),
12400
+ provider: "codex"
12401
+ }
12402
+ ];
12403
+ }
12385
12404
  /**
12386
12405
  * Check if a directory has bundled resources
12387
12406
  */
@@ -12395,12 +12414,12 @@ function checkBundledResources(skillDir) {
12395
12414
  /**
12396
12415
  * Determine skill location type based on directory path
12397
12416
  */
12398
- function determineLocation(dirPath) {
12417
+ function determineLocation(dirPath, userDir) {
12399
12418
  const normalizedPath = resolve(dirPath);
12400
12419
  const cwd = process.cwd();
12401
- const home = homedir();
12402
- if (normalizedPath.startsWith(join(cwd, ".agent")) || normalizedPath.startsWith(join(cwd, ".claude"))) return "project";
12403
- if (normalizedPath.startsWith(join(home, ".agent")) || normalizedPath.startsWith(join(home, ".claude"))) return "user";
12420
+ const home = userDir;
12421
+ if (normalizedPath.startsWith(join(cwd, ".agent")) || normalizedPath.startsWith(join(cwd, ".claude")) || normalizedPath.startsWith(join(cwd, ".codex"))) return "project";
12422
+ if (normalizedPath.startsWith(join(home, ".agent")) || normalizedPath.startsWith(join(home, ".claude")) || normalizedPath.startsWith(join(home, ".codex"))) return "user";
12404
12423
  return "user";
12405
12424
  }
12406
12425
  function collectSkillDirectories(root, recursive, diagnostics, accumulator, includeDisabled) {
@@ -12426,7 +12445,7 @@ function collectSkillDirectories(root, recursive, diagnostics, accumulator, incl
12426
12445
  /**
12427
12446
  * Scan a single skill directory and return found skills
12428
12447
  */
12429
- function scanSkillDirectory(dirPath, { location, recursive = true, diagnostics, includeDisabled = false }) {
12448
+ function scanSkillDirectory(dirPath, { location, recursive = true, diagnostics, includeDisabled = false }, provider, userDir = homedir(), scope) {
12430
12449
  const skills = [];
12431
12450
  const skillDirectories = /* @__PURE__ */ new Set();
12432
12451
  collectSkillDirectories(dirPath, recursive, diagnostics, skillDirectories, includeDisabled);
@@ -12448,16 +12467,20 @@ function scanSkillDirectory(dirPath, { location, recursive = true, diagnostics,
12448
12467
  if (candidates.length === 0) continue;
12449
12468
  for (const candidate of candidates) try {
12450
12469
  const parsed = parseSkillFile(candidate.path);
12470
+ const baseName = parsed.frontmatter.name;
12471
+ const scopedName = scope && !baseName.includes(":") ? `${scope}:${baseName}` : baseName;
12451
12472
  const resources = checkBundledResources(skillDir);
12452
- const skillLocation = location ?? determineLocation(skillDir);
12473
+ const skillLocation = location ?? determineLocation(skillDir, userDir);
12453
12474
  skills.push({
12454
- name: parsed.frontmatter.name,
12475
+ name: scopedName,
12455
12476
  description: parsed.frontmatter.description,
12477
+ provider,
12456
12478
  location: skillLocation,
12457
12479
  path: skillDir,
12458
12480
  disabled: candidate.disabled,
12459
12481
  ...resources
12460
12482
  });
12483
+ diagnostics.byProvider[provider] = (diagnostics.byProvider[provider] ?? 0) + 1;
12461
12484
  } catch (error$1) {
12462
12485
  console.warn(colors.yellow(`Warning: Failed to parse ${candidate.path}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`));
12463
12486
  diagnostics.warnings.push(`Failed to parse ${candidate.path}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`);
@@ -12472,26 +12495,46 @@ function discoverSkills(options = {}) {
12472
12495
  const diagnostics = {
12473
12496
  scannedDirectories: [],
12474
12497
  warnings: [],
12475
- conflicts: []
12498
+ conflicts: [],
12499
+ byProvider: {
12500
+ claude: 0,
12501
+ codex: 0,
12502
+ file: 0
12503
+ }
12476
12504
  };
12505
+ const userDir = options.userDir ? resolve(options.userDir) : homedir();
12477
12506
  const directories = [];
12478
- if (options.customDirs?.length) directories.push(...options.customDirs);
12479
- if (options.scanDefaultDirs !== false) directories.push(...SKILL_DIRECTORIES);
12480
- const skillMap = /* @__PURE__ */ new Map();
12481
- for (const dir of directories) {
12482
- const skills = scanSkillDirectory(dir.startsWith("/") ? dir : resolve(process.cwd(), dir), {
12507
+ if (options.customDirs?.length) for (const entry of options.customDirs) {
12508
+ const provider = options.customProvider ?? "file";
12509
+ if (typeof entry === "string") directories.push({
12510
+ path: entry,
12511
+ provider,
12512
+ scope: provider === "file" ? "other" : void 0
12513
+ });
12514
+ else directories.push({
12515
+ path: entry.path,
12516
+ provider,
12517
+ scope: entry.scope ?? (provider === "file" ? "other" : void 0)
12518
+ });
12519
+ }
12520
+ if (options.scanDefaultDirs !== false) directories.push(...getDefaultSkillDirectories(userDir));
12521
+ const skills = [];
12522
+ const firstPathByName = /* @__PURE__ */ new Map();
12523
+ for (const entry of directories) {
12524
+ const { path: dir, provider, scope } = entry;
12525
+ const skillsFromDir = scanSkillDirectory(dir.startsWith("/") ? dir : resolve(process.cwd(), dir), {
12483
12526
  diagnostics,
12484
12527
  recursive: true,
12485
12528
  includeDisabled: options.includeDisabled === true
12486
- });
12487
- for (const skill of skills) if (!skillMap.has(skill.name)) skillMap.set(skill.name, skill);
12488
- else {
12489
- const existing = skillMap.get(skill.name);
12490
- diagnostics.conflicts.push(`Keeping ${existing?.path ?? "unknown"} and skipping ${skill.path} for skill '${skill.name}' (higher priority already loaded).`);
12529
+ }, provider, userDir, scope);
12530
+ for (const skill of skillsFromDir) {
12531
+ if (firstPathByName.has(skill.name)) diagnostics.conflicts.push(`Duplicate skill '${skill.name}' found at ${skill.path} (first seen at ${firstPathByName.get(skill.name)})`);
12532
+ else firstPathByName.set(skill.name, skill.path);
12533
+ skills.push(skill);
12491
12534
  }
12492
12535
  }
12493
12536
  return {
12494
- skills: Array.from(skillMap.values()),
12537
+ skills,
12495
12538
  diagnostics
12496
12539
  };
12497
12540
  }
@@ -12509,15 +12552,19 @@ function loadSkill(metadata) {
12509
12552
 
12510
12553
  //#endregion
12511
12554
  //#region src/core/plugins.ts
12512
- const PLUGINS_FILE = join(homedir(), ".claude/plugins/installed_plugins.json");
12513
- const PLUGINS_ROOT = join(homedir(), ".claude/plugins");
12555
+ function defaultPluginsFile(userDir) {
12556
+ return join(userDir, ".claude/plugins/installed_plugins.json");
12557
+ }
12558
+ function defaultPluginsRoot(userDir) {
12559
+ return join(userDir, ".claude/plugins");
12560
+ }
12514
12561
  function resolveInstallPath(rawPath, pluginsRoot) {
12515
12562
  return isAbsolute(rawPath) ? rawPath : join(pluginsRoot, rawPath);
12516
12563
  }
12517
12564
  /**
12518
12565
  * Parse installed_plugins.json
12519
12566
  */
12520
- function loadInstalledPlugins(pluginsFile = PLUGINS_FILE, diagnostics) {
12567
+ function loadInstalledPlugins(pluginsFile, diagnostics) {
12521
12568
  if (!existsSync(pluginsFile)) {
12522
12569
  console.info(`[ccski] No plugin marketplace skills found at ${pluginsFile}`);
12523
12570
  diagnostics?.warnings.push(`Plugins file not found at ${pluginsFile}`);
@@ -12562,31 +12609,47 @@ function findSkillFiles(dir, diagnostics) {
12562
12609
  * Discover skills from installed plugins
12563
12610
  */
12564
12611
  function discoverPluginSkills(options = {}) {
12612
+ const log = debug("ccski:plugins");
12565
12613
  const diagnostics = {
12566
12614
  scannedPlugins: [],
12567
12615
  warnings: []
12568
12616
  };
12569
- const pluginsFile = options.pluginsFile ?? PLUGINS_FILE;
12617
+ const userDir = options.userDir ? resolve(options.userDir) : homedir();
12618
+ const pluginsFile = options.pluginsFile ?? defaultPluginsFile(userDir);
12570
12619
  diagnostics.scannedPlugins.push(pluginsFile);
12571
- const plugins = loadInstalledPlugins(pluginsFile, diagnostics);
12572
- if (!plugins) return {
12573
- skills: [],
12574
- diagnostics
12575
- };
12576
12620
  const skills = [];
12577
- const pluginsRoot = options.pluginsRoot ?? PLUGINS_ROOT;
12578
- for (const [pluginKey, plugin] of Object.entries(plugins.plugins)) {
12621
+ const seenPaths = /* @__PURE__ */ new Set();
12622
+ const pluginsRoot = options.pluginsRoot ?? defaultPluginsRoot(userDir);
12623
+ let missingOrEmpty = false;
12624
+ const plugins = loadInstalledPlugins(pluginsFile, diagnostics);
12625
+ log("plugins file=%s loaded=%s", pluginsFile, Boolean(plugins));
12626
+ if (plugins) for (const [pluginKey, plugin] of Object.entries(plugins.plugins)) {
12579
12627
  const [pluginName, marketplace] = pluginKey.split("@");
12580
12628
  if (!pluginName || !marketplace) continue;
12581
12629
  const installPath = resolveInstallPath(plugin.installPath, pluginsRoot);
12582
12630
  const skillFiles = findSkillFiles(installPath, diagnostics);
12631
+ if (!skillFiles.length) {
12632
+ if (!existsSync(installPath)) {
12633
+ diagnostics.warnings.push(`Plugin install path not found: ${installPath}`);
12634
+ log("missing installPath: %s", installPath);
12635
+ } else {
12636
+ diagnostics.warnings.push(`No skills found in plugin install path: ${installPath}`);
12637
+ log("empty installPath: %s", installPath);
12638
+ }
12639
+ missingOrEmpty = true;
12640
+ }
12583
12641
  diagnostics.scannedPlugins.push(installPath);
12642
+ log("scanned manifest plugin=%s skills=%d", pluginName, skillFiles.length);
12584
12643
  for (const skillFile of skillFiles) try {
12585
12644
  const parsed = parseSkillFile(skillFile);
12586
12645
  const skillDir = dirname(skillFile);
12646
+ if (seenPaths.has(skillDir)) continue;
12647
+ const skillName = parsed.frontmatter.name;
12648
+ const namespaced = skillName.includes(":") ? skillName : pluginName === skillName ? skillName : `${pluginName}:${skillName}`;
12587
12649
  skills.push({
12588
- name: `${pluginName}:${parsed.frontmatter.name}`,
12650
+ name: namespaced,
12589
12651
  description: parsed.frontmatter.description,
12652
+ provider: "claude",
12590
12653
  location: "plugin",
12591
12654
  path: skillDir,
12592
12655
  hasReferences: existsSync(join(skillDir, "references")),
@@ -12598,6 +12661,48 @@ function discoverPluginSkills(options = {}) {
12598
12661
  version: plugin.version
12599
12662
  }
12600
12663
  });
12664
+ seenPaths.add(skillDir);
12665
+ } catch (error$1) {
12666
+ console.warn(colors.yellow(`Warning: Failed to parse plugin skill ${skillFile}:`), error$1);
12667
+ diagnostics.warnings.push(`Failed to parse plugin skill ${skillFile}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`);
12668
+ }
12669
+ }
12670
+ if (!(process.env.CCSKI_CLAUDE_PLUGINS_FALLBACK === "true")) {
12671
+ log("fallback scan skipped (CCSKI_CLAUDE_PLUGINS_FALLBACK!=true)");
12672
+ return {
12673
+ skills,
12674
+ diagnostics
12675
+ };
12676
+ }
12677
+ if (!plugins || skills.length === 0 || missingOrEmpty) {
12678
+ const fallbackRoot = join(pluginsRoot, "skills");
12679
+ diagnostics.scannedPlugins.push(fallbackRoot);
12680
+ log("running fallback scan at %s", fallbackRoot);
12681
+ const skillFiles = findSkillFiles(fallbackRoot, diagnostics);
12682
+ for (const skillFile of skillFiles) try {
12683
+ const parsed = parseSkillFile(skillFile);
12684
+ const skillDir = dirname(skillFile);
12685
+ if (seenPaths.has(skillDir)) continue;
12686
+ const pluginName = relative(fallbackRoot, skillDir).split(sep).filter(Boolean)[0] ?? "local";
12687
+ const skillName = parsed.frontmatter.name;
12688
+ const namespaced = skillName.includes(":") ? skillName : pluginName === skillName ? skillName : `${pluginName}:${skillName}`;
12689
+ skills.push({
12690
+ name: namespaced,
12691
+ description: parsed.frontmatter.description,
12692
+ provider: "claude",
12693
+ location: "plugin",
12694
+ path: skillDir,
12695
+ hasReferences: existsSync(join(skillDir, "references")),
12696
+ hasScripts: existsSync(join(skillDir, "scripts")),
12697
+ hasAssets: existsSync(join(skillDir, "assets")),
12698
+ pluginInfo: {
12699
+ pluginName,
12700
+ marketplace: "local",
12701
+ version: "unknown"
12702
+ }
12703
+ });
12704
+ seenPaths.add(skillDir);
12705
+ log("fallback skill added %s (%s)", namespaced, skillDir);
12601
12706
  } catch (error$1) {
12602
12707
  console.warn(colors.yellow(`Warning: Failed to parse plugin skill ${skillFile}:`), error$1);
12603
12708
  diagnostics.warnings.push(`Failed to parse plugin skill ${skillFile}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`);
@@ -12655,8 +12760,8 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
12655
12760
  toUpper: (str) => str.toLocaleUpperCase(),
12656
12761
  toLower: (str) => str.toLocaleLowerCase(),
12657
12762
  compare: null,
12658
- sort: (info, haystack, needle, compare = cmp) => {
12659
- let { idx, chars, terms, interLft2, interLft1, start, intraIns, interIns, cases } = info;
12763
+ sort: (info$1, haystack, needle, compare = cmp) => {
12764
+ let { idx, chars, terms, interLft2, interLft1, start, intraIns, interIns, cases } = info$1;
12660
12765
  return idx.map((v, i$1) => i$1).sort((ia, ib) => chars[ib] - chars[ia] || intraIns[ia] - intraIns[ib] || terms[ib] + interLft2[ib] + .5 * interLft1[ib] - (terms[ia] + interLft2[ia] + .5 * interLft1[ia]) || interIns[ia] - interIns[ib] || start[ia] - start[ib] || cases[ib] - cases[ia] || compare(haystack[idx[ia]], haystack[idx[ib]]));
12661
12766
  }
12662
12767
  };
@@ -12804,7 +12909,7 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
12804
12909
  let withIntraBound = !!_intraBound;
12805
12910
  let interBound = new RegExp(_interBound, uFlag);
12806
12911
  let intraBound = new RegExp(_intraBound, uFlag);
12807
- const info = (idxs, haystack, needle) => {
12912
+ const info$1 = (idxs, haystack, needle) => {
12808
12913
  let [query, parts, contrs] = prepQuery(needle, 1);
12809
12914
  let partsCased = split(needle, true);
12810
12915
  let [queryR] = prepQuery(needle, 2);
@@ -12821,7 +12926,7 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
12821
12926
  }
12822
12927
  let len = idxs.length;
12823
12928
  let field = Array(len).fill(0);
12824
- let info$1 = {
12929
+ let info$2 = {
12825
12930
  idx: Array(len),
12826
12931
  start: field.slice(),
12827
12932
  chars: field.slice(),
@@ -12956,17 +13061,17 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
12956
13061
  if (j < partsLen - 1) idxAcc += groupLen + m[k + 1].length;
12957
13062
  }
12958
13063
  if (!disc) {
12959
- info$1.idx[ii] = idxs[i$1];
12960
- info$1.interLft2[ii] = lft2;
12961
- info$1.interLft1[ii] = lft1;
12962
- info$1.interRgt2[ii] = rgt2;
12963
- info$1.interRgt1[ii] = rgt1;
12964
- info$1.chars[ii] = chars;
12965
- info$1.terms[ii] = terms;
12966
- info$1.cases[ii] = cases;
12967
- info$1.interIns[ii] = inter;
12968
- info$1.intraIns[ii] = intra;
12969
- info$1.start[ii] = start;
13064
+ info$2.idx[ii] = idxs[i$1];
13065
+ info$2.interLft2[ii] = lft2;
13066
+ info$2.interLft1[ii] = lft1;
13067
+ info$2.interRgt2[ii] = rgt2;
13068
+ info$2.interRgt1[ii] = rgt1;
13069
+ info$2.chars[ii] = chars;
13070
+ info$2.terms[ii] = terms;
13071
+ info$2.cases[ii] = cases;
13072
+ info$2.interIns[ii] = inter;
13073
+ info$2.intraIns[ii] = intra;
13074
+ info$2.start[ii] = start;
12970
13075
  let m$1 = mhstr.match(queryR);
12971
13076
  let idxAcc$1 = m$1.index + m$1[1].length;
12972
13077
  let refLen = refine.length;
@@ -12993,7 +13098,7 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
12993
13098
  }
12994
13099
  }
12995
13100
  idxAcc$1 = m$1.index + m$1[1].length;
12996
- let ranges = info$1.ranges[ii] = [];
13101
+ let ranges = info$2.ranges[ii] = [];
12997
13102
  let from = idxAcc$1;
12998
13103
  let to = idxAcc$1;
12999
13104
  for (let i$2 = 2; i$2 < m$1.length; i$2++) {
@@ -13009,8 +13114,8 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
13009
13114
  ii++;
13010
13115
  }
13011
13116
  }
13012
- if (ii < idxs.length) for (let k in info$1) info$1[k] = info$1[k].slice(0, ii);
13013
- return info$1;
13117
+ if (ii < idxs.length) for (let k in info$2) info$2[k] = info$2[k].slice(0, ii);
13118
+ return info$2;
13014
13119
  };
13015
13120
  const refineMatch = (m, k, idxInNext, termLen) => {
13016
13121
  let prepend = m[k] + m[k + 1].slice(0, idxInNext);
@@ -13091,7 +13196,7 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
13091
13196
  let idxs = matches[ni];
13092
13197
  if (idxs == null || idxs.length == 0) continue;
13093
13198
  let needle$1 = needles[ni];
13094
- let _info = info(idxs, haystack, needle$1);
13199
+ let _info = info$1(idxs, haystack, needle$1);
13095
13200
  let order = opts.sort(_info, haystack, needle$1, compare);
13096
13201
  if (ni > 0) for (let i$1 = 0; i$1 < order.length; i$1++) order[i$1] += retOrder.length;
13097
13202
  for (let k in _info) retInfo[k] = (retInfo[k] ?? []).concat(_info[k]);
@@ -13110,7 +13215,7 @@ var require_uFuzzy = /* @__PURE__ */ __commonJS({ "node_modules/.pnpm/@leeoniya+
13110
13215
  },
13111
13216
  split,
13112
13217
  filter,
13113
- info,
13218
+ info: info$1,
13114
13219
  sort: opts.sort
13115
13220
  };
13116
13221
  }
@@ -13215,18 +13320,85 @@ function containsCaseInsensitive(haystack, needle) {
13215
13320
  return haystack.toLowerCase().includes(needle.toLowerCase());
13216
13321
  }
13217
13322
 
13323
+ //#endregion
13324
+ //#region src/utils/skill-id.ts
13325
+ function stripPluginPrefix(name, pluginName) {
13326
+ const prefix = `${pluginName}:`;
13327
+ return name.startsWith(prefix) ? name.slice(prefix.length) : name;
13328
+ }
13329
+ const defaultFormatOptions = {
13330
+ includeProvider: true,
13331
+ color: true
13332
+ };
13333
+ /**
13334
+ * Build a copy/paste friendly skill id that includes provider and plugin (when present).
13335
+ * Format: provider:@plugin:name or provider:name
13336
+ */
13337
+ function formatSkillId(skill, opts) {
13338
+ const { includeProvider } = {
13339
+ ...defaultFormatOptions,
13340
+ ...opts
13341
+ };
13342
+ if (skill.pluginInfo) {
13343
+ const base = stripPluginPrefix(skill.name, skill.pluginInfo.pluginName);
13344
+ const id = `@${skill.pluginInfo.pluginName}:${base}`;
13345
+ return includeProvider ? `${skill.provider}:${id}` : id;
13346
+ }
13347
+ return includeProvider ? `${skill.provider}:${skill.name}` : skill.name;
13348
+ }
13349
+ /**
13350
+ * Return a set of aliases (lowercased) that should match user input, including plugin-qualified variants.
13351
+ */
13352
+ function skillAliases(skill) {
13353
+ const aliases = /* @__PURE__ */ new Set();
13354
+ aliases.add(skill.name.toLowerCase());
13355
+ const short = skill.name.toLowerCase().split(":").pop() ?? skill.name.toLowerCase();
13356
+ aliases.add(short);
13357
+ if (skill.pluginInfo) {
13358
+ const plugin = skill.pluginInfo.pluginName.toLowerCase();
13359
+ const base = stripPluginPrefix(skill.name.toLowerCase(), plugin);
13360
+ aliases.add(`${plugin}:${base}`);
13361
+ aliases.add(`@${plugin}:${base}`);
13362
+ aliases.add(`${skill.provider}:${plugin}:${base}`);
13363
+ aliases.add(`${skill.provider}:@${plugin}:${base}`);
13364
+ }
13365
+ return Array.from(aliases);
13366
+ }
13367
+ /**
13368
+ * Render a human-friendly label with colors, highlighting provider/plugin/name components.
13369
+ * Default includes provider to stay copy/pasteable; set includeProvider=false when a provider badge already exists.
13370
+ */
13371
+ function formatSkillLabel(skill, opts) {
13372
+ const { includeProvider, color, providerColor } = {
13373
+ ...defaultFormatOptions,
13374
+ ...opts
13375
+ };
13376
+ const useColor = color !== false;
13377
+ const providerPart = includeProvider ? `${skill.provider}:` : "";
13378
+ const pluginPart = skill.pluginInfo ? `@${skill.pluginInfo.pluginName}:` : "";
13379
+ const baseName = skill.pluginInfo ? stripPluginPrefix(skill.name, skill.pluginInfo.pluginName) : skill.name;
13380
+ if (!useColor) return `${providerPart}${pluginPart}${baseName}`;
13381
+ const defaultProviderColor = skill.provider === "claude" ? colors.green : skill.provider === "codex" ? colors.cyan : dim;
13382
+ const green$1 = (providerColor ?? defaultProviderColor)(providerPart.replace(/:$/, ""));
13383
+ const blue$1 = colors.blue(baseName);
13384
+ const pluginColored = pluginPart ? dim(pluginPart) : "";
13385
+ return `${includeProvider ? `${green$1}:` : ""}${pluginColored}${blue$1}`;
13386
+ }
13387
+
13218
13388
  //#endregion
13219
13389
  //#region src/core/registry.ts
13220
13390
  /**
13221
13391
  * Skill registry for managing discovered skills
13222
13392
  */
13223
13393
  var SkillRegistry = class {
13224
- skills = /* @__PURE__ */ new Map();
13394
+ skills = [];
13225
13395
  diagnostics = {
13226
13396
  directoriesScanned: [],
13227
13397
  pluginSources: [],
13228
13398
  warnings: [],
13229
- conflicts: []
13399
+ conflicts: [],
13400
+ byProvider: {},
13401
+ byLocation: {}
13230
13402
  };
13231
13403
  constructor(options = {}) {
13232
13404
  this.options = options;
@@ -13236,56 +13408,75 @@ var SkillRegistry = class {
13236
13408
  * Refresh the skill registry by rescanning directories
13237
13409
  */
13238
13410
  refresh() {
13239
- this.skills.clear();
13411
+ this.skills = [];
13240
13412
  this.diagnostics = {
13241
13413
  directoriesScanned: [],
13242
13414
  pluginSources: [],
13243
13415
  warnings: [],
13244
- conflicts: []
13416
+ conflicts: [],
13417
+ byProvider: {},
13418
+ byLocation: {}
13245
13419
  };
13246
13420
  const discovered = discoverSkills(this.options);
13247
13421
  this.diagnostics.directoriesScanned.push(...discovered.diagnostics.scannedDirectories);
13248
13422
  this.diagnostics.warnings.push(...discovered.diagnostics.warnings);
13249
13423
  this.diagnostics.conflicts.push(...discovered.diagnostics.conflicts);
13250
- for (const skill of discovered.skills) this.skills.set(skill.name, skill);
13424
+ for (const skill of discovered.skills) {
13425
+ this.skills.push(skill);
13426
+ this.diagnostics.byProvider[skill.provider] = (this.diagnostics.byProvider[skill.provider] ?? 0) + 1;
13427
+ this.diagnostics.byLocation[skill.location] = (this.diagnostics.byLocation[skill.location] ?? 0) + 1;
13428
+ }
13251
13429
  if (!this.options.skipPlugins) {
13252
13430
  const pluginOptions = {};
13253
13431
  if (this.options.pluginsFile) pluginOptions.pluginsFile = this.options.pluginsFile;
13254
13432
  if (this.options.pluginsRoot) pluginOptions.pluginsRoot = this.options.pluginsRoot;
13433
+ if (this.options.userDir) pluginOptions.userDir = this.options.userDir;
13255
13434
  const pluginSkills = discoverPluginSkills(pluginOptions);
13256
13435
  this.diagnostics.pluginSources.push(...pluginSkills.diagnostics.scannedPlugins);
13257
13436
  this.diagnostics.warnings.push(...pluginSkills.diagnostics.warnings);
13258
- for (const skill of pluginSkills.skills) if (!this.skills.has(skill.name)) this.skills.set(skill.name, skill);
13259
- else this.diagnostics.conflicts.push(`Skipping plugin skill ${skill.name} from ${skill.path} because a higher-priority skill is already loaded.`);
13437
+ for (const skill of pluginSkills.skills) {
13438
+ if (this.skills.some((existing) => existing.name.toLowerCase() === skill.name.toLowerCase() && existing.location !== "plugin")) this.diagnostics.conflicts.push(`Duplicate skill ${skill.name} from plugin at ${skill.path}; keeping for filter-stage resolution.`);
13439
+ this.skills.push(skill);
13440
+ this.diagnostics.byProvider[skill.provider] = (this.diagnostics.byProvider[skill.provider] ?? 0) + 1;
13441
+ this.diagnostics.byLocation[skill.location] = (this.diagnostics.byLocation[skill.location] ?? 0) + 1;
13442
+ }
13260
13443
  }
13261
13444
  }
13262
13445
  /**
13263
13446
  * Get all skills
13264
13447
  */
13265
13448
  getAll() {
13266
- return Array.from(this.skills.values());
13449
+ return [...this.skills];
13267
13450
  }
13268
13451
  /**
13269
13452
  * Find a skill by name (case-insensitive, supports full and short names)
13270
13453
  */
13271
13454
  find(name) {
13272
13455
  const normalizedName = name.toLowerCase();
13273
- for (const skill of this.skills.values()) if (skill.name.toLowerCase() === normalizedName) return skill;
13274
- if (name.includes(":")) throw new SkillNotFoundError(name, this.getSuggestions(name));
13275
- const shortMatches = Array.from(this.skills.values()).filter((skill) => {
13276
- return skill.name.split(":").pop()?.toLowerCase() === normalizedName;
13456
+ const [maybeProvider, rest] = normalizedName.includes(":") ? (() => {
13457
+ const parts = normalizedName.split(":");
13458
+ if (parts.length > 1 && [
13459
+ "claude",
13460
+ "codex",
13461
+ "file"
13462
+ ].includes(parts[0])) return [parts.shift(), parts.join(":")];
13463
+ return [void 0, normalizedName];
13464
+ })() : [void 0, normalizedName];
13465
+ const targetProvider = maybeProvider;
13466
+ const targetName = rest;
13467
+ const candidates = this.skills.filter((skill) => {
13468
+ const skillName = skill.name.toLowerCase();
13469
+ const shortName = skillName.split(":").pop();
13470
+ const matchesName = skillName === targetName || shortName === targetName;
13471
+ const matchesProvider = targetProvider ? skill.provider === targetProvider : true;
13472
+ return matchesName && matchesProvider;
13277
13473
  });
13278
- if (shortMatches.length === 0) throw new SkillNotFoundError(name, this.getSuggestions(name));
13279
- for (const tier of [
13280
- "project",
13281
- "user",
13282
- "plugin"
13283
- ]) {
13284
- const tierMatches = shortMatches.filter((skill) => skill.location === tier);
13285
- if (tierMatches.length === 1) return tierMatches[0];
13286
- if (tierMatches.length > 1) throw new AmbiguousSkillNameError(name, tierMatches.map((s) => s.name));
13287
- }
13288
- throw new SkillNotFoundError(name, this.getSuggestions(name));
13474
+ if (candidates.length === 1) return candidates[0];
13475
+ if (candidates.length > 1) {
13476
+ const suggestions = candidates.map((s) => this.formatNameWithProvider(s));
13477
+ throw new AmbiguousSkillNameError(name, candidates.map((s) => s.name), suggestions);
13478
+ }
13479
+ throw new SkillNotFoundError(name, this.getSuggestions(name, targetProvider));
13289
13480
  }
13290
13481
  /**
13291
13482
  * Check if a skill exists
@@ -13307,27 +13498,29 @@ var SkillRegistry = class {
13307
13498
  /**
13308
13499
  * Get suggestions for similar skill names (fuzzy matching)
13309
13500
  */
13310
- getSuggestions(query, limit = 3) {
13311
- const names = Array.from(this.skills.keys());
13312
- return rankStrings(names, query).slice(0, limit).map((index) => names[index]);
13501
+ getSuggestions(query, provider, limit = 3) {
13502
+ const pool = this.skills.filter((s) => provider ? s.provider === provider : true).map((s) => this.formatNameWithProvider(s));
13503
+ return rankStrings(pool, query).slice(0, limit).map((index) => pool[index]);
13504
+ }
13505
+ formatNameWithProvider(skill) {
13506
+ return formatSkillId(skill);
13313
13507
  }
13314
13508
  /**
13315
13509
  * Get diagnostic information
13316
13510
  */
13317
13511
  getDiagnostics() {
13318
- const byLocation = {};
13319
- for (const skill of this.skills.values()) byLocation[skill.location] = (byLocation[skill.location] ?? 0) + 1;
13320
13512
  return {
13321
- totalSkills: this.skills.size,
13322
- byLocation,
13323
- directoriesScanned: this.diagnostics.directoriesScanned,
13324
- pluginSources: this.diagnostics.pluginSources,
13325
- warnings: this.diagnostics.warnings,
13326
- conflicts: this.diagnostics.conflicts
13513
+ totalSkills: this.skills.length,
13514
+ byLocation: { ...this.diagnostics.byLocation },
13515
+ byProvider: { ...this.diagnostics.byProvider },
13516
+ directoriesScanned: [...this.diagnostics.directoriesScanned],
13517
+ pluginSources: [...this.diagnostics.pluginSources],
13518
+ warnings: [...this.diagnostics.warnings],
13519
+ conflicts: [...this.diagnostics.conflicts]
13327
13520
  };
13328
13521
  }
13329
13522
  };
13330
13523
 
13331
13524
  //#endregion
13332
- export { intersectionType as A, CcskiError as B, PluginEntrySchema as C, custom as D, booleanType as E, recordType as F, __require as G, SkillNotFoundError as H, stringType as I, __toESM as K, unionType as L, numberType as M, objectType as N, discriminatedUnionType as O, optionalType as P, unknownType as R, InstalledPluginsSchema as S, arrayType as T, ValidationError as U, ParseError as V, __commonJS as W, tone as _, discoverSkills as a, parseSkillFile as b, colors as c, formatBytes as d, heading as f, success as g, setColorEnabled as h, SKILL_DIRECTORIES as i, literalType as j, enumType as k, dim as l, renderList as m, containsCaseInsensitive as n, loadSkill as o, highlight$1 as p, rankStrings as r, scanSkillDirectory as s, SkillRegistry as t, error as u, warn as v, SkillFrontmatterSchema as w, validateSkillFile as x, wrap as y, AmbiguousSkillNameError as z };
13333
- //# sourceMappingURL=registry-ChUc2356.mjs.map
13525
+ export { arrayType as A, recordType as B, warn as C, InstalledPluginsSchema as D, validateSkillFile as E, intersectionType as F, CcskiError as G, unionType as H, literalType as I, ValidationError as J, ParseError as K, numberType as L, custom as M, discriminatedUnionType as N, PluginEntrySchema as O, enumType as P, objectType as R, tone as S, parseSkillFile as T, unknownType as U, stringType as V, AmbiguousSkillNameError as W, highlight$1 as _, containsCaseInsensitive as a, setColorEnabled as b, getDefaultSkillDirectories as c, colors as d, dim as f, heading as g, formatBytes as h, skillAliases as i, booleanType as j, SkillFrontmatterSchema as k, loadSkill as l, error as m, formatSkillId as n, rankStrings as o, duplicateBadge as p, SkillNotFoundError as q, formatSkillLabel as r, discoverSkills as s, SkillRegistry as t, scanSkillDirectory as u, info as v, wrap as w, success as x, renderList as y, optionalType as z };
13526
+ //# sourceMappingURL=registry-Do2Htns7.mjs.map