ctx7 0.2.3 → 0.2.4

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/dist/index.js CHANGED
@@ -305,113 +305,171 @@ var log = {
305
305
  };
306
306
 
307
307
  // src/utils/ide.ts
308
- import pc2 from "picocolors";
309
- import { select, checkbox, confirm } from "@inquirer/prompts";
308
+ import pc3 from "picocolors";
309
+ import { select, confirm } from "@inquirer/prompts";
310
310
  import { access } from "fs/promises";
311
- import { join } from "path";
311
+ import { join, dirname } from "path";
312
312
  import { homedir } from "os";
313
313
 
314
+ // src/utils/prompts.ts
315
+ import pc2 from "picocolors";
316
+ import { checkbox } from "@inquirer/prompts";
317
+ import readline from "readline";
318
+ function terminalLink(text, url, color) {
319
+ const colorFn = color ?? ((s) => s);
320
+ return `\x1B]8;;${url}\x07${colorFn(text)}\x1B]8;;\x07 ${pc2.white("\u2197")}`;
321
+ }
322
+ function formatInstallCount(count, placeholder = "") {
323
+ if (count === void 0 || count === 0) return placeholder;
324
+ return pc2.yellow(String(count));
325
+ }
326
+ function formatTrustScore(score) {
327
+ if (score === void 0 || score < 0) return pc2.dim("-");
328
+ if (score < 3) return pc2.red(score.toFixed(1));
329
+ return pc2.yellow(score.toFixed(1));
330
+ }
331
+ async function checkboxWithHover(config, options) {
332
+ const choices = config.choices.filter(
333
+ (c) => typeof c === "object" && c !== null && !("type" in c && c.type === "separator")
334
+ );
335
+ const values = choices.map((c) => c.value);
336
+ const totalItems = values.length;
337
+ let cursorPosition = 0;
338
+ const getName = options?.getName ?? ((v) => v.name);
339
+ const keypressHandler = (_str, key) => {
340
+ if (key.name === "up" && cursorPosition > 0) {
341
+ cursorPosition--;
342
+ } else if (key.name === "down" && cursorPosition < totalItems - 1) {
343
+ cursorPosition++;
344
+ }
345
+ };
346
+ readline.emitKeypressEvents(process.stdin);
347
+ process.stdin.on("keypress", keypressHandler);
348
+ const customConfig = {
349
+ ...config,
350
+ theme: {
351
+ ...config.theme,
352
+ style: {
353
+ ...config.theme?.style,
354
+ highlight: (text) => pc2.green(text),
355
+ renderSelectedChoices: (selected, _allChoices) => {
356
+ if (selected.length === 0) {
357
+ return pc2.dim(getName(values[cursorPosition]));
358
+ }
359
+ return selected.map((c) => getName(c.value)).join(", ");
360
+ }
361
+ }
362
+ }
363
+ };
364
+ try {
365
+ const selected = await checkbox(customConfig);
366
+ if (selected.length === 0) {
367
+ return [values[cursorPosition]];
368
+ }
369
+ return selected;
370
+ } finally {
371
+ process.stdin.removeListener("keypress", keypressHandler);
372
+ }
373
+ }
374
+
314
375
  // src/types.ts
315
376
  var IDE_PATHS = {
316
377
  claude: ".claude/skills",
317
378
  cursor: ".cursor/skills",
318
- codex: ".codex/skills",
319
- opencode: ".opencode/skills",
320
- amp: ".agents/skills",
321
- antigravity: ".agent/skills"
379
+ antigravity: ".agent/skills",
380
+ universal: ".agents/skills"
322
381
  };
323
382
  var IDE_GLOBAL_PATHS = {
324
383
  claude: ".claude/skills",
325
384
  cursor: ".cursor/skills",
326
- codex: ".codex/skills",
327
- opencode: ".config/opencode/skills",
328
- amp: ".config/agents/skills",
329
- antigravity: ".agent/skills"
385
+ antigravity: ".agent/skills",
386
+ universal: ".config/agents/skills"
330
387
  };
331
388
  var IDE_NAMES = {
332
389
  claude: "Claude Code",
333
390
  cursor: "Cursor",
334
- codex: "Codex",
335
- opencode: "OpenCode",
336
- amp: "Amp",
337
- antigravity: "Antigravity"
391
+ antigravity: "Antigravity",
392
+ universal: "Universal"
338
393
  };
394
+ var UNIVERSAL_SKILLS_PATH = ".agents/skills";
395
+ var UNIVERSAL_SKILLS_GLOBAL_PATH = ".config/agents/skills";
396
+ var UNIVERSAL_AGENTS_LABEL = "Amp, Codex, Gemini CLI, GitHub Copilot, OpenCode + more";
397
+ var VENDOR_SPECIFIC_AGENTS = ["claude", "cursor", "antigravity"];
339
398
  var DEFAULT_CONFIG = {
340
- defaultIde: "claude",
399
+ defaultIde: "universal",
341
400
  defaultScope: "project"
342
401
  };
343
402
 
344
403
  // src/utils/ide.ts
345
- import { dirname } from "path";
346
404
  function getSelectedIdes(options) {
347
405
  const ides = [];
348
406
  if (options.claude) ides.push("claude");
349
407
  if (options.cursor) ides.push("cursor");
350
- if (options.codex) ides.push("codex");
351
- if (options.opencode) ides.push("opencode");
352
- if (options.amp) ides.push("amp");
408
+ if (options.universal) ides.push("universal");
353
409
  if (options.antigravity) ides.push("antigravity");
354
410
  return ides;
355
411
  }
356
412
  function hasExplicitIdeOption(options) {
357
- return !!(options.claude || options.cursor || options.codex || options.opencode || options.amp || options.antigravity);
358
- }
359
- async function detectInstalledIdes(preferredScope) {
360
- const allIdes = Object.keys(IDE_PATHS);
361
- if (preferredScope === "global") {
362
- const globalIdes = [];
363
- for (const ide of allIdes) {
364
- const detectionPath = dirname(IDE_GLOBAL_PATHS[ide]);
365
- const globalParent = join(homedir(), detectionPath);
366
- try {
367
- await access(globalParent);
368
- globalIdes.push(ide);
369
- } catch {
370
- }
371
- }
372
- if (globalIdes.length > 0) {
373
- return { ides: globalIdes, scope: "global" };
374
- }
375
- return null;
376
- }
377
- const projectIdes = [];
378
- for (const ide of allIdes) {
379
- const detectionPath = dirname(IDE_PATHS[ide]);
380
- const projectParent = join(process.cwd(), detectionPath);
413
+ return !!(options.claude || options.cursor || options.universal || options.antigravity);
414
+ }
415
+ async function detectVendorSpecificAgents(scope) {
416
+ const baseDir = scope === "global" ? homedir() : process.cwd();
417
+ const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
418
+ const detected = [];
419
+ for (const ide of VENDOR_SPECIFIC_AGENTS) {
420
+ const parentDir = dirname(pathMap[ide]);
381
421
  try {
382
- await access(projectParent);
383
- projectIdes.push(ide);
422
+ await access(join(baseDir, parentDir));
423
+ detected.push(ide);
384
424
  } catch {
385
425
  }
386
426
  }
387
- if (projectIdes.length > 0) {
388
- return { ides: projectIdes, scope: "project" };
427
+ return detected;
428
+ }
429
+ function getUniversalDir(scope) {
430
+ if (scope === "global") {
431
+ return join(homedir(), UNIVERSAL_SKILLS_GLOBAL_PATH);
389
432
  }
390
- return null;
433
+ return join(process.cwd(), UNIVERSAL_SKILLS_PATH);
391
434
  }
392
435
  async function promptForInstallTargets(options) {
393
436
  if (hasExplicitIdeOption(options)) {
394
- const ides = getSelectedIdes(options);
437
+ const ides2 = getSelectedIdes(options);
395
438
  const scope2 = options.global ? "global" : "project";
396
439
  return {
397
- ides: ides.length > 0 ? ides : [DEFAULT_CONFIG.defaultIde],
440
+ ides: ides2.length > 0 ? ides2 : [DEFAULT_CONFIG.defaultIde],
398
441
  scopes: [scope2]
399
442
  };
400
443
  }
401
- const preferredScope = options.global ? "global" : void 0;
402
- const detected = await detectInstalledIdes(preferredScope);
403
- if (detected) {
404
- const scope2 = options.global ? "global" : detected.scope;
405
- const pathMap2 = scope2 === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
406
- const baseDir2 = scope2 === "global" ? homedir() : process.cwd();
407
- const paths = detected.ides.map((ide) => join(baseDir2, pathMap2[ide]));
408
- const pathList = paths.join("\n");
444
+ const scope = options.global ? "global" : "project";
445
+ const baseDir = scope === "global" ? homedir() : process.cwd();
446
+ const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
447
+ const universalPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
448
+ const detectedVendor = await detectVendorSpecificAgents(scope);
449
+ let hasUniversalDir = false;
450
+ try {
451
+ await access(join(baseDir, dirname(universalPath)));
452
+ hasUniversalDir = true;
453
+ } catch {
454
+ }
455
+ const detectedIdes = [
456
+ ...hasUniversalDir ? ["universal"] : [],
457
+ ...detectedVendor
458
+ ];
459
+ if (detectedIdes.length > 0) {
460
+ const pathLines = [];
461
+ if (hasUniversalDir) {
462
+ pathLines.push(join(baseDir, universalPath));
463
+ }
464
+ for (const ide of detectedVendor) {
465
+ pathLines.push(join(baseDir, pathMap[ide]));
466
+ }
409
467
  log.blank();
410
468
  let confirmed;
411
469
  try {
412
470
  confirmed = await confirm({
413
471
  message: `Install to detected location(s)?
414
- ${pc2.dim(pathList)}`,
472
+ ${pc3.dim(pathLines.join("\n"))}`,
415
473
  default: true
416
474
  });
417
475
  } catch {
@@ -421,35 +479,45 @@ ${pc2.dim(pathList)}`,
421
479
  log.warn("Installation cancelled");
422
480
  return null;
423
481
  }
424
- return { ides: detected.ides, scopes: [scope2] };
482
+ return { ides: detectedIdes, scopes: [scope] };
483
+ }
484
+ const universalLabel = `Universal \u2014 ${UNIVERSAL_AGENTS_LABEL} ${pc3.dim(`(${universalPath})`)}`;
485
+ const choices = [
486
+ { name: universalLabel, value: "universal", checked: true }
487
+ ];
488
+ for (const ide of VENDOR_SPECIFIC_AGENTS) {
489
+ choices.push({
490
+ name: `${IDE_NAMES[ide]} ${pc3.dim(`(${pathMap[ide]})`)}`,
491
+ value: ide,
492
+ checked: false
493
+ });
425
494
  }
426
495
  log.blank();
427
- const scope = options.global ? "global" : "project";
428
- const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
429
- const baseDir = scope === "global" ? homedir() : process.cwd();
430
- const ideChoices = Object.keys(IDE_NAMES).map((ide) => ({
431
- name: `${IDE_NAMES[ide]} ${pc2.dim(`(${pathMap[ide]})`)}`,
432
- value: ide,
433
- checked: ide === DEFAULT_CONFIG.defaultIde
434
- }));
435
496
  let selectedIdes;
436
497
  try {
437
- selectedIdes = await checkbox({
438
- message: `Which clients do you want to install the skill(s) for?
439
- ${pc2.dim(baseDir)}`,
440
- choices: ideChoices,
441
- required: true,
442
- loop: false,
443
- theme: { style: { highlight: (text) => pc2.green(text) } }
444
- });
498
+ selectedIdes = await checkboxWithHover(
499
+ {
500
+ message: `Which agents do you want to install to?
501
+ ${pc3.dim(` ${baseDir}`)}`,
502
+ choices,
503
+ loop: false,
504
+ theme: {
505
+ style: {
506
+ highlight: (text) => pc3.green(text),
507
+ message: (text, status) => {
508
+ if (status === "done") return pc3.dim(text.split("\n")[0]);
509
+ return pc3.bold(text);
510
+ }
511
+ }
512
+ }
513
+ },
514
+ { getName: (ide) => IDE_NAMES[ide] }
515
+ );
445
516
  } catch {
446
517
  return null;
447
518
  }
448
- if (selectedIdes.length === 0) {
449
- log.warn("You must select at least one client");
450
- return null;
451
- }
452
- return { ides: selectedIdes, scopes: [scope] };
519
+ const ides = ["universal", ...selectedIdes.filter((ide) => ide !== "universal")];
520
+ return { ides, scopes: [scope] };
453
521
  }
454
522
  async function promptForSingleTarget(options) {
455
523
  if (hasExplicitIdeOption(options)) {
@@ -459,18 +527,22 @@ async function promptForSingleTarget(options) {
459
527
  return { ide, scope };
460
528
  }
461
529
  log.blank();
462
- const ideChoices = Object.keys(IDE_NAMES).map((ide) => ({
463
- name: `${IDE_NAMES[ide]} ${pc2.dim(`(${IDE_PATHS[ide]})`)}`,
464
- value: ide
465
- }));
530
+ const universalLabel = `Universal ${pc3.dim(`(${UNIVERSAL_SKILLS_PATH})`)}`;
531
+ const choices = [{ name: universalLabel, value: "universal" }];
532
+ for (const ide of VENDOR_SPECIFIC_AGENTS) {
533
+ choices.push({
534
+ name: `${IDE_NAMES[ide]} ${pc3.dim(`(${IDE_PATHS[ide]})`)}`,
535
+ value: ide
536
+ });
537
+ }
466
538
  let selectedIde;
467
539
  try {
468
540
  selectedIde = await select({
469
- message: "Which client?",
470
- choices: ideChoices,
541
+ message: "Which location?",
542
+ choices,
471
543
  default: DEFAULT_CONFIG.defaultIde,
472
544
  loop: false,
473
- theme: { style: { highlight: (text) => pc2.green(text) } }
545
+ theme: { style: { highlight: (text) => pc3.green(text) } }
474
546
  });
475
547
  } catch {
476
548
  return null;
@@ -484,17 +556,17 @@ async function promptForSingleTarget(options) {
484
556
  message: "Which scope?",
485
557
  choices: [
486
558
  {
487
- name: `Project ${pc2.dim("(current directory)")}`,
559
+ name: `Project ${pc3.dim("(current directory)")}`,
488
560
  value: "project"
489
561
  },
490
562
  {
491
- name: `Global ${pc2.dim("(home directory)")}`,
563
+ name: `Global ${pc3.dim("(home directory)")}`,
492
564
  value: "global"
493
565
  }
494
566
  ],
495
567
  default: DEFAULT_CONFIG.defaultScope,
496
568
  loop: false,
497
- theme: { style: { highlight: (text) => pc2.green(text) } }
569
+ theme: { style: { highlight: (text) => pc3.green(text) } }
498
570
  });
499
571
  } catch {
500
572
  return null;
@@ -503,91 +575,32 @@ async function promptForSingleTarget(options) {
503
575
  return { ide: selectedIde, scope: selectedScope };
504
576
  }
505
577
  function getTargetDirs(targets) {
506
- const sortedIdes = [...targets.ides].sort((a, b) => {
507
- if (a === "claude") return -1;
508
- if (b === "claude") return 1;
509
- return 0;
510
- });
578
+ const hasUniversal = targets.ides.some((ide) => ide === "universal");
511
579
  const dirs = [];
512
- for (const ide of sortedIdes) {
513
- for (const scope of targets.scopes) {
514
- if (scope === "global") {
515
- dirs.push(join(homedir(), IDE_GLOBAL_PATHS[ide]));
516
- } else {
517
- dirs.push(join(process.cwd(), IDE_PATHS[ide]));
518
- }
580
+ for (const scope of targets.scopes) {
581
+ const baseDir = scope === "global" ? homedir() : process.cwd();
582
+ if (hasUniversal) {
583
+ const uniPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
584
+ dirs.push(join(baseDir, uniPath));
585
+ }
586
+ for (const ide of targets.ides) {
587
+ if (ide === "universal") continue;
588
+ const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
589
+ dirs.push(join(baseDir, pathMap[ide]));
519
590
  }
520
591
  }
521
592
  return dirs;
522
593
  }
523
594
  function getTargetDirFromSelection(ide, scope) {
595
+ if (ide === "universal") {
596
+ return getUniversalDir(scope);
597
+ }
524
598
  if (scope === "global") {
525
599
  return join(homedir(), IDE_GLOBAL_PATHS[ide]);
526
600
  }
527
601
  return join(process.cwd(), IDE_PATHS[ide]);
528
602
  }
529
603
 
530
- // src/utils/prompts.ts
531
- import pc3 from "picocolors";
532
- import { checkbox as checkbox2 } from "@inquirer/prompts";
533
- import readline from "readline";
534
- function terminalLink(text, url, color) {
535
- const colorFn = color ?? ((s) => s);
536
- return `\x1B]8;;${url}\x07${colorFn(text)}\x1B]8;;\x07 ${pc3.white("\u2197")}`;
537
- }
538
- function formatInstallCount(count, placeholder = "") {
539
- if (count === void 0 || count === 0) return placeholder;
540
- return pc3.yellow(String(count));
541
- }
542
- function formatTrustScore(score) {
543
- if (score === void 0 || score < 0) return pc3.dim("-");
544
- if (score < 3) return pc3.red(score.toFixed(1));
545
- return pc3.yellow(score.toFixed(1));
546
- }
547
- async function checkboxWithHover(config, options) {
548
- const choices = config.choices.filter(
549
- (c) => typeof c === "object" && c !== null && !("type" in c && c.type === "separator")
550
- );
551
- const values = choices.map((c) => c.value);
552
- const totalItems = values.length;
553
- let cursorPosition = 0;
554
- const getName = options?.getName ?? ((v) => v.name);
555
- const keypressHandler = (_str, key) => {
556
- if (key.name === "up" && cursorPosition > 0) {
557
- cursorPosition--;
558
- } else if (key.name === "down" && cursorPosition < totalItems - 1) {
559
- cursorPosition++;
560
- }
561
- };
562
- readline.emitKeypressEvents(process.stdin);
563
- process.stdin.on("keypress", keypressHandler);
564
- const customConfig = {
565
- ...config,
566
- theme: {
567
- ...config.theme,
568
- style: {
569
- ...config.theme?.style,
570
- highlight: (text) => pc3.green(text),
571
- renderSelectedChoices: (selected, _allChoices) => {
572
- if (selected.length === 0) {
573
- return pc3.dim(getName(values[cursorPosition]));
574
- }
575
- return selected.map((c) => getName(c.value)).join(", ");
576
- }
577
- }
578
- }
579
- };
580
- try {
581
- const selected = await checkbox2(customConfig);
582
- if (selected.length === 0) {
583
- return [values[cursorPosition]];
584
- }
585
- return selected;
586
- } finally {
587
- process.stdin.removeListener("keypress", keypressHandler);
588
- }
589
- }
590
-
591
604
  // src/utils/installer.ts
592
605
  import { mkdir, writeFile, rm, symlink, lstat } from "fs/promises";
593
606
  import { join as join2 } from "path";
@@ -1062,7 +1075,7 @@ var selectOrInput_default = selectOrInput;
1062
1075
 
1063
1076
  // src/commands/generate.ts
1064
1077
  function registerGenerateCommand(skillCommand) {
1065
- skillCommand.command("generate").alias("gen").alias("g").option("-o, --output <dir>", "Output directory (default: current directory)").option("--all", "Generate for all detected IDEs").option("--global", "Generate in global skills directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Generate a skill for a library using AI").action(async (options) => {
1078
+ skillCommand.command("generate").alias("gen").alias("g").option("-o, --output <dir>", "Output directory (default: current directory)").option("--all", "Generate for all detected IDEs").option("--global", "Generate in global skills directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Generate a skill for a library using AI").action(async (options) => {
1066
1079
  await generateCommand(options);
1067
1080
  });
1068
1081
  }
@@ -1582,48 +1595,56 @@ async function detectProjectDependencies(cwd) {
1582
1595
  // src/commands/skill.ts
1583
1596
  function logInstallSummary(targets, targetDirs, skillNames) {
1584
1597
  log.blank();
1598
+ const hasUniversal = targets.ides.some((ide) => ide === "universal");
1599
+ const vendorIdes = targets.ides.filter((ide) => ide !== "universal");
1585
1600
  let dirIndex = 0;
1586
- for (const ide of targets.ides) {
1587
- for (let i = 0; i < targets.scopes.length; i++) {
1588
- const dir = targetDirs[dirIndex++];
1589
- log.dim(`${IDE_NAMES[ide]}: ${dir}`);
1590
- for (const name of skillNames) {
1591
- log.itemAdd(name);
1592
- }
1601
+ if (hasUniversal && dirIndex < targetDirs.length) {
1602
+ log.plain(`${pc7.bold("Universal")} ${pc7.dim(targetDirs[dirIndex])}`);
1603
+ for (const name of skillNames) {
1604
+ log.itemAdd(name);
1605
+ }
1606
+ dirIndex++;
1607
+ }
1608
+ for (const ide of vendorIdes) {
1609
+ if (dirIndex >= targetDirs.length) break;
1610
+ log.plain(`${pc7.bold(IDE_NAMES[ide])} ${pc7.dim(targetDirs[dirIndex])}`);
1611
+ for (const name of skillNames) {
1612
+ log.itemAdd(name);
1593
1613
  }
1614
+ dirIndex++;
1594
1615
  }
1595
1616
  log.blank();
1596
1617
  }
1597
1618
  function registerSkillCommands(program2) {
1598
1619
  const skill = program2.command("skills").alias("skill").description("Manage AI coding skills");
1599
1620
  registerGenerateCommand(skill);
1600
- skill.command("install").alias("i").alias("add").argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills from a repository").action(async (project, skillName, options) => {
1621
+ skill.command("install").alias("i").alias("add").argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills from a repository").action(async (project, skillName, options) => {
1601
1622
  await installCommand(project, skillName, options);
1602
1623
  });
1603
1624
  skill.command("search").alias("s").argument("<keywords...>", "Search keywords").description("Search for skills across all indexed repositories").action(async (keywords) => {
1604
1625
  await searchCommand(keywords.join(" "));
1605
1626
  });
1606
- skill.command("list").alias("ls").option("--global", "List global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("List installed skills").action(async (options) => {
1627
+ skill.command("list").alias("ls").option("--global", "List global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("List installed skills").action(async (options) => {
1607
1628
  await listCommand(options);
1608
1629
  });
1609
- skill.command("remove").alias("rm").alias("delete").argument("<name>", "Skill name to remove").option("--global", "Remove from global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Remove an installed skill").action(async (name, options) => {
1630
+ skill.command("remove").alias("rm").alias("delete").argument("<name>", "Skill name to remove").option("--global", "Remove from global skills").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Remove an installed skill").action(async (name, options) => {
1610
1631
  await removeCommand(name, options);
1611
1632
  });
1612
1633
  skill.command("info").argument("<repository>", "GitHub repository (/owner/repo)").description("Show skills in a repository").action(async (project) => {
1613
1634
  await infoCommand(project);
1614
1635
  });
1615
- skill.command("suggest").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills based on your project dependencies").action(async (options) => {
1636
+ skill.command("suggest").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills based on your project dependencies").action(async (options) => {
1616
1637
  await suggestCommand(options);
1617
1638
  });
1618
1639
  }
1619
1640
  function registerSkillAliases(program2) {
1620
- program2.command("si", { hidden: true }).argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills (alias for: skills install)").action(async (project, skillName, options) => {
1641
+ program2.command("si", { hidden: true }).argument("<repository>", "GitHub repository (/owner/repo)").argument("[skill]", "Specific skill name to install").option("--all", "Install all skills without prompting").option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Install skills (alias for: skills install)").action(async (project, skillName, options) => {
1621
1642
  await installCommand(project, skillName, options);
1622
1643
  });
1623
1644
  program2.command("ss", { hidden: true }).argument("<keywords...>", "Search keywords").description("Search for skills (alias for: skills search)").action(async (keywords) => {
1624
1645
  await searchCommand(keywords.join(" "));
1625
1646
  });
1626
- program2.command("ssg", { hidden: true }).option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--codex", "Codex (.codex/skills/)").option("--opencode", "OpenCode (.opencode/skills/)").option("--amp", "Amp (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills (alias for: skills suggest)").action(async (options) => {
1647
+ program2.command("ssg", { hidden: true }).option("--global", "Install globally instead of current directory").option("--claude", "Claude Code (.claude/skills/)").option("--cursor", "Cursor (.cursor/skills/)").option("--universal", "Universal (.agents/skills/)").option("--antigravity", "Antigravity (.agent/skills/)").description("Suggest skills (alias for: skills suggest)").action(async (options) => {
1627
1648
  await suggestCommand(options);
1628
1649
  });
1629
1650
  }
@@ -1686,10 +1707,13 @@ async function installCommand(input2, skillName, options) {
1686
1707
  } else {
1687
1708
  const indexWidth = data.skills.length.toString().length;
1688
1709
  const maxNameLen = Math.max(...data.skills.map((s) => s.name.length));
1710
+ const installsColWidth = 10;
1689
1711
  const choices = skillsWithRepo.map((s, index) => {
1690
1712
  const indexStr = pc7.dim(`${(index + 1).toString().padStart(indexWidth)}.`);
1691
1713
  const paddedName = s.name.padEnd(maxNameLen);
1692
- const installs = formatInstallCount(s.installCount);
1714
+ const installsRaw = s.installCount ? String(s.installCount) : "-";
1715
+ const paddedInstalls = formatInstallCount(s.installCount, pc7.dim("-")) + " ".repeat(installsColWidth - installsRaw.length);
1716
+ const trust = formatTrustScore(s.trustScore);
1693
1717
  const skillUrl = `https://context7.com/skills${s.project}/${s.name}`;
1694
1718
  const skillLink = terminalLink(s.name, skillUrl, pc7.white);
1695
1719
  const repoLink = terminalLink(s.project, `https://github.com${s.project}`, pc7.white);
@@ -1702,20 +1726,30 @@ async function installCommand(input2, skillName, options) {
1702
1726
  pc7.white(s.description || "No description")
1703
1727
  ];
1704
1728
  return {
1705
- name: installs ? `${indexStr} ${paddedName} ${installs}` : `${indexStr} ${paddedName}`,
1729
+ name: `${indexStr} ${paddedName} ${paddedInstalls}${trust}`,
1706
1730
  value: s,
1707
1731
  description: metadataLines.join("\n")
1708
1732
  };
1709
1733
  });
1710
1734
  log.blank();
1711
- const installsOffset = 4 + indexWidth + 1 + 1 + maxNameLen + 1 - 3;
1712
- const message = "Select skills:" + " ".repeat(Math.max(1, installsOffset - 14)) + pc7.dim("installs");
1735
+ const checkboxPrefixWidth = 3;
1736
+ const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
1737
+ const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)");
1713
1738
  try {
1714
1739
  selectedSkills = await checkboxWithHover({
1715
- message,
1740
+ message: `Select skills to install:
1741
+ ${headerLine}`,
1716
1742
  choices,
1717
1743
  pageSize: 15,
1718
- loop: false
1744
+ loop: false,
1745
+ theme: {
1746
+ style: {
1747
+ message: (text, status) => {
1748
+ if (status === "done") return pc7.dim(text.split("\n")[0]);
1749
+ return pc7.bold(text);
1750
+ }
1751
+ }
1752
+ }
1719
1753
  });
1720
1754
  } catch {
1721
1755
  log.warn("Installation cancelled");
@@ -1817,6 +1851,7 @@ async function searchCommand(query) {
1817
1851
  }
1818
1852
  spinner.succeed(`Found ${data.results.length} skill(s)`);
1819
1853
  trackEvent("search_query", { query, resultCount: data.results.length });
1854
+ log.blank();
1820
1855
  const indexWidth = data.results.length.toString().length;
1821
1856
  const maxNameLen = Math.max(...data.results.map((s) => s.name.length));
1822
1857
  const installsColWidth = 10;
@@ -1846,18 +1881,25 @@ async function searchCommand(query) {
1846
1881
  description: metadataLines.join("\n")
1847
1882
  };
1848
1883
  });
1849
- log.blank();
1850
1884
  const checkboxPrefixWidth = 3;
1851
1885
  const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
1852
1886
  const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)");
1853
- const message = "Select skills to install:\n" + headerLine;
1854
1887
  let selectedSkills;
1855
1888
  try {
1856
1889
  selectedSkills = await checkboxWithHover({
1857
- message,
1890
+ message: `Select skills to install:
1891
+ ${headerLine}`,
1858
1892
  choices,
1859
1893
  pageSize: 15,
1860
- loop: false
1894
+ loop: false,
1895
+ theme: {
1896
+ style: {
1897
+ message: (text, status) => {
1898
+ if (status === "done") return pc7.dim(text.split("\n")[0]);
1899
+ return pc7.bold(text);
1900
+ }
1901
+ }
1902
+ }
1861
1903
  });
1862
1904
  } catch {
1863
1905
  log.warn("Installation cancelled");
@@ -1940,19 +1982,40 @@ async function searchCommand(query) {
1940
1982
  async function listCommand(options) {
1941
1983
  trackEvent("command", { name: "list" });
1942
1984
  const scope = options.global ? "global" : "project";
1943
- const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
1944
1985
  const baseDir = scope === "global" ? homedir4() : process.cwd();
1945
- const idesToCheck = hasExplicitIdeOption(options) ? getSelectedIdes(options) : Object.keys(IDE_NAMES);
1946
1986
  const results = [];
1947
- for (const ide of idesToCheck) {
1948
- const skillsDir = join6(baseDir, pathMap[ide]);
1987
+ async function scanDir(dir) {
1949
1988
  try {
1950
- const entries = await readdir(skillsDir, { withFileTypes: true });
1951
- const skillFolders = entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
1952
- if (skillFolders.length > 0) {
1953
- results.push({ ide, skills: skillFolders });
1954
- }
1989
+ const entries = await readdir(dir, { withFileTypes: true });
1990
+ return entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);
1955
1991
  } catch {
1992
+ return [];
1993
+ }
1994
+ }
1995
+ if (hasExplicitIdeOption(options)) {
1996
+ const ides = getSelectedIdes(options);
1997
+ for (const ide of ides) {
1998
+ const dir = ide === "universal" ? join6(baseDir, scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH) : join6(baseDir, (scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS)[ide]);
1999
+ const label = ide === "universal" ? UNIVERSAL_AGENTS_LABEL : IDE_NAMES[ide];
2000
+ const skills = await scanDir(dir);
2001
+ if (skills.length > 0) {
2002
+ results.push({ label, path: dir, skills });
2003
+ }
2004
+ }
2005
+ } else {
2006
+ const universalPath = scope === "global" ? UNIVERSAL_SKILLS_GLOBAL_PATH : UNIVERSAL_SKILLS_PATH;
2007
+ const universalDir = join6(baseDir, universalPath);
2008
+ const universalSkills = await scanDir(universalDir);
2009
+ if (universalSkills.length > 0) {
2010
+ results.push({ label: UNIVERSAL_AGENTS_LABEL, path: universalPath, skills: universalSkills });
2011
+ }
2012
+ for (const ide of VENDOR_SPECIFIC_AGENTS) {
2013
+ const pathMap = scope === "global" ? IDE_GLOBAL_PATHS : IDE_PATHS;
2014
+ const dir = join6(baseDir, pathMap[ide]);
2015
+ const skills = await scanDir(dir);
2016
+ if (skills.length > 0) {
2017
+ results.push({ label: IDE_NAMES[ide], path: pathMap[ide], skills });
2018
+ }
1956
2019
  }
1957
2020
  }
1958
2021
  if (results.length === 0) {
@@ -1960,10 +2023,8 @@ async function listCommand(options) {
1960
2023
  return;
1961
2024
  }
1962
2025
  log.blank();
1963
- for (const { ide, skills } of results) {
1964
- const ideName = IDE_NAMES[ide];
1965
- const path2 = pathMap[ide];
1966
- log.plain(`${pc7.bold(ideName)} ${pc7.dim(path2)}`);
2026
+ for (const { label, path: path2, skills } of results) {
2027
+ log.plain(`${pc7.bold(label)} ${pc7.dim(path2)}`);
1967
2028
  for (const skill of skills) {
1968
2029
  log.plain(` ${pc7.green(skill)}`);
1969
2030
  }
@@ -2100,14 +2161,22 @@ async function suggestCommand(options) {
2100
2161
  const checkboxPrefixWidth = 3;
2101
2162
  const headerPad = " ".repeat(checkboxPrefixWidth + indexWidth + 1 + 1 + maxNameLen + 1);
2102
2163
  const headerLine = headerPad + pc7.dim("Installs".padEnd(installsColWidth)) + pc7.dim("Trust(0-10)".padEnd(trustColWidth)) + pc7.dim("Relevant");
2103
- const message = "Select skills to install:\n" + headerLine;
2104
2164
  let selectedSkills;
2105
2165
  try {
2106
2166
  selectedSkills = await checkboxWithHover({
2107
- message,
2167
+ message: `Select skills to install:
2168
+ ${headerLine}`,
2108
2169
  choices,
2109
2170
  pageSize: 15,
2110
- loop: false
2171
+ loop: false,
2172
+ theme: {
2173
+ style: {
2174
+ message: (text, status) => {
2175
+ if (status === "done") return pc7.dim(text.split("\n")[0]);
2176
+ return pc7.bold(text);
2177
+ }
2178
+ }
2179
+ }
2111
2180
  });
2112
2181
  } catch {
2113
2182
  log.warn("Installation cancelled");