skillops-cli 1.3.6 → 1.3.8

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/cli.js CHANGED
@@ -10,7 +10,7 @@ import YAML6 from "yaml";
10
10
  import fs from "fs";
11
11
  import YAML from "yaml";
12
12
  import { z } from "zod";
13
- var SKILLOPS_VERSION = "1.3.6";
13
+ var SKILLOPS_VERSION = "1.3.8";
14
14
  var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
15
  var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
16
16
  var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -1426,9 +1426,104 @@ import fs3 from "fs";
1426
1426
  import path2 from "path";
1427
1427
  import YAML2 from "yaml";
1428
1428
 
1429
- // src/ui/terminal.ts
1429
+ // src/ui/lipgloss.ts
1430
1430
  var ESC = String.fromCharCode(27);
1431
- var ANSI_ESCAPE_REGEX = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
1431
+ var CHARM_PALETTE = {
1432
+ indigo: "#7D56F4",
1433
+ fuchsia: "#EE6FF8",
1434
+ hotPink: "#F25D94",
1435
+ mint: "#04B575",
1436
+ amber: "#FFBD2E",
1437
+ coral: "#FF5F56",
1438
+ cyan: "#00D7FF",
1439
+ lavender: "#B4A7D6",
1440
+ white: "#FAFAFA",
1441
+ muted: "#6C6C6C",
1442
+ subtle: "#4A4A4A",
1443
+ surface: "#2A2438",
1444
+ keycapBg: "#302B3F"
1445
+ };
1446
+ function hexToRgb(hex) {
1447
+ const cleaned = hex.trim().replace(/^#/, "");
1448
+ if (cleaned.length === 3) {
1449
+ const r2 = Number.parseInt(`${cleaned[0]}${cleaned[0]}`, 16);
1450
+ const g2 = Number.parseInt(`${cleaned[1]}${cleaned[1]}`, 16);
1451
+ const b2 = Number.parseInt(`${cleaned[2]}${cleaned[2]}`, 16);
1452
+ return [
1453
+ Number.isNaN(r2) ? 255 : r2,
1454
+ Number.isNaN(g2) ? 255 : g2,
1455
+ Number.isNaN(b2) ? 255 : b2
1456
+ ];
1457
+ }
1458
+ const r = Number.parseInt(cleaned.slice(0, 2), 16);
1459
+ const g = Number.parseInt(cleaned.slice(2, 4), 16);
1460
+ const b = Number.parseInt(cleaned.slice(4, 6), 16);
1461
+ return [
1462
+ Number.isNaN(r) ? 255 : r,
1463
+ Number.isNaN(g) ? 255 : g,
1464
+ Number.isNaN(b) ? 255 : b
1465
+ ];
1466
+ }
1467
+ function fgHex(hex, text, enabled) {
1468
+ const useColor = enabled ?? isColorEnabled();
1469
+ if (!useColor || text.length === 0) {
1470
+ return text;
1471
+ }
1472
+ const [r, g, b] = hexToRgb(hex);
1473
+ return `${ESC}[38;2;${r};${g};${b}m${text}${ESC}[39m`;
1474
+ }
1475
+ function bgHex(hex, text, enabled) {
1476
+ const useColor = enabled ?? isColorEnabled();
1477
+ if (!useColor || text.length === 0) {
1478
+ return text;
1479
+ }
1480
+ const [r, g, b] = hexToRgb(hex);
1481
+ return `${ESC}[48;2;${r};${g};${b}m${text}${ESC}[49m`;
1482
+ }
1483
+ function renderPill(text, options) {
1484
+ const useColor = options?.colorEnabled ?? isColorEnabled();
1485
+ if (!useColor || text.length === 0) {
1486
+ return text;
1487
+ }
1488
+ const fg = options?.fg ?? CHARM_PALETTE.white;
1489
+ const bg = options?.bg ?? CHARM_PALETTE.indigo;
1490
+ const bold = options?.bold ?? true;
1491
+ let styled = fgHex(fg, text, true);
1492
+ styled = bgHex(bg, styled, true);
1493
+ if (bold) {
1494
+ styled = `${ESC}[1m${styled}${ESC}[22m`;
1495
+ }
1496
+ return styled;
1497
+ }
1498
+ function renderKeycap(keyLabel, actionLabel, colorEnabled) {
1499
+ const useColor = colorEnabled ?? isColorEnabled();
1500
+ if (!useColor) {
1501
+ return `${keyLabel} ${actionLabel}`;
1502
+ }
1503
+ const keyStyled = `${ESC}[1m${fgHex(CHARM_PALETTE.lavender, keyLabel, true)}${ESC}[22m`;
1504
+ const actionStyled = fgHex(CHARM_PALETTE.muted, actionLabel, true);
1505
+ return `${keyStyled} ${actionStyled}`;
1506
+ }
1507
+ function renderPaginationDots(currentPage, totalPages, colorEnabled) {
1508
+ const safeTotal = Math.max(1, Math.min(12, totalPages));
1509
+ const safeCurrent = Math.max(0, Math.min(safeTotal - 1, currentPage));
1510
+ const useColor = colorEnabled ?? isColorEnabled();
1511
+ const dots = [];
1512
+ for (let i = 0; i < safeTotal; i++) {
1513
+ if (i === safeCurrent) {
1514
+ dots.push(
1515
+ useColor ? `${ESC}[1m${fgHex(CHARM_PALETTE.hotPink, "\u2022", true)}${ESC}[22m` : "\u2022"
1516
+ );
1517
+ } else {
1518
+ dots.push(useColor ? fgHex(CHARM_PALETTE.muted, "\u25E6", true) : "\u25E6");
1519
+ }
1520
+ }
1521
+ return dots.join(" ");
1522
+ }
1523
+
1524
+ // src/ui/terminal.ts
1525
+ var ESC2 = String.fromCharCode(27);
1526
+ var ANSI_ESCAPE_REGEX = new RegExp(`${ESC2}\\[[0-9;]*m`, "g");
1432
1527
  function isColorEnabled(options) {
1433
1528
  const env = options?.env ?? process.env;
1434
1529
  const isTTY = options?.isTTY ?? Boolean(process.stdout?.isTTY);
@@ -1454,21 +1549,21 @@ function wrapAnsi(openCode, closeCode, text, enabled) {
1454
1549
  if (!useColor || text.length === 0) {
1455
1550
  return text;
1456
1551
  }
1457
- return `${ESC}[${openCode}m${text}${ESC}[${closeCode}m`;
1552
+ return `${ESC2}[${openCode}m${text}${ESC2}[${closeCode}m`;
1458
1553
  }
1459
1554
  var style = {
1460
1555
  bold: (text, enabled) => wrapAnsi("1", "22", text, enabled),
1461
1556
  dim: (text, enabled) => wrapAnsi("2", "22", text, enabled),
1462
1557
  italic: (text, enabled) => wrapAnsi("3", "23", text, enabled),
1463
1558
  underline: (text, enabled) => wrapAnsi("4", "24", text, enabled),
1464
- cyan: (text, enabled) => wrapAnsi("36", "39", text, enabled),
1465
- green: (text, enabled) => wrapAnsi("32", "39", text, enabled),
1466
- yellow: (text, enabled) => wrapAnsi("33", "39", text, enabled),
1467
- red: (text, enabled) => wrapAnsi("31", "39", text, enabled),
1468
- magenta: (text, enabled) => wrapAnsi("35", "39", text, enabled),
1469
- blue: (text, enabled) => wrapAnsi("34", "39", text, enabled),
1470
- gray: (text, enabled) => wrapAnsi("90", "39", text, enabled),
1471
- white: (text, enabled) => wrapAnsi("37", "39", text, enabled)
1559
+ cyan: (text, enabled) => fgHex(CHARM_PALETTE.cyan, text, enabled),
1560
+ green: (text, enabled) => fgHex(CHARM_PALETTE.mint, text, enabled),
1561
+ yellow: (text, enabled) => fgHex(CHARM_PALETTE.amber, text, enabled),
1562
+ red: (text, enabled) => fgHex(CHARM_PALETTE.coral, text, enabled),
1563
+ magenta: (text, enabled) => fgHex(CHARM_PALETTE.fuchsia, text, enabled),
1564
+ blue: (text, enabled) => fgHex(CHARM_PALETTE.indigo, text, enabled),
1565
+ gray: (text, enabled) => fgHex(CHARM_PALETTE.muted, text, enabled),
1566
+ white: (text, enabled) => fgHex(CHARM_PALETTE.white, text, enabled)
1472
1567
  };
1473
1568
  function stripAnsi(text) {
1474
1569
  return text.replace(ANSI_ESCAPE_REGEX, "");
@@ -1495,7 +1590,13 @@ function padVisible(text, width, align = "left") {
1495
1590
  function renderHeaderBanner(subtitle = "Framework & Library for AI Skills & Subagents", options) {
1496
1591
  const ver = options?.version ?? SKILLOPS_VERSION;
1497
1592
  const c = options?.colorEnabled;
1498
- return `${style.bold(style.cyan(`\u25C6 SkillOps v${ver}`, c), c)} ${style.dim("\u2014", c)} ${subtitle}`;
1593
+ const brandPill = renderPill(`\u25C6 SkillOps v${ver}`, {
1594
+ fg: CHARM_PALETTE.white,
1595
+ bg: CHARM_PALETTE.indigo,
1596
+ bold: true,
1597
+ colorEnabled: c
1598
+ });
1599
+ return `${brandPill} ${style.dim("\u2014", c)} ${fgHex(CHARM_PALETTE.lavender, subtitle, c)}`;
1499
1600
  }
1500
1601
  function renderTable(headers, rows, options) {
1501
1602
  const indent = " ".repeat(options?.indent ?? 2);
@@ -1572,15 +1673,19 @@ function formatBadge(type, customLabel, colorEnabled) {
1572
1673
  colorEnabled
1573
1674
  );
1574
1675
  case "skill":
1575
- return style.bold(
1576
- style.cyan(`[${customLabel ?? "SKILL"}]`, colorEnabled),
1676
+ return renderPill(`[${customLabel ?? "SKILL"}]`, {
1677
+ fg: CHARM_PALETTE.white,
1678
+ bg: CHARM_PALETTE.indigo,
1679
+ bold: true,
1577
1680
  colorEnabled
1578
- );
1681
+ });
1579
1682
  case "subagent":
1580
- return style.bold(
1581
- style.magenta(`[${customLabel ?? "SUBAGENT"}]`, colorEnabled),
1683
+ return renderPill(`[${customLabel ?? "SUBAGENT"}]`, {
1684
+ fg: CHARM_PALETTE.white,
1685
+ bg: CHARM_PALETTE.hotPink,
1686
+ bold: true,
1582
1687
  colorEnabled
1583
- );
1688
+ });
1584
1689
  }
1585
1690
  }
1586
1691
  function renderStep(step, total, title, detail, status = "done", colorEnabled) {
@@ -5412,6 +5517,9 @@ function optionMatchesQuery(opt, rawQuery) {
5412
5517
  if (!q) {
5413
5518
  return true;
5414
5519
  }
5520
+ if (opt.isSelectAll) {
5521
+ return false;
5522
+ }
5415
5523
  return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
5416
5524
  }
5417
5525
  function getFilteredTabOptions(state, tab = state.activeTab) {
@@ -5419,16 +5527,47 @@ function getFilteredTabOptions(state, tab = state.activeTab) {
5419
5527
  (opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
5420
5528
  );
5421
5529
  }
5422
- function createMultiSelectState(options) {
5530
+ function createMultiSelectState(options, stateOptions = {}) {
5531
+ let finalOptions = options;
5532
+ if (stateOptions.showSelectAll && !options.some((opt) => opt.isSelectAll === true)) {
5533
+ const skillItems = options.filter((opt) => optionMatchesTab(opt, "skill"));
5534
+ const agentItems = options.filter((opt) => optionMatchesTab(opt, "agent"));
5535
+ const withAll = [];
5536
+ if (skillItems.length > 0) {
5537
+ withAll.push({
5538
+ value: "__all_skills__",
5539
+ label: "[ALL] Select all Skills",
5540
+ kind: "skill",
5541
+ tab: "skill",
5542
+ isSelectAll: true,
5543
+ hint: `Select or deselect all ${skillItems.length} Skills in this tab`
5544
+ });
5545
+ withAll.push(...skillItems);
5546
+ }
5547
+ if (agentItems.length > 0) {
5548
+ withAll.push({
5549
+ value: "__all_agents__",
5550
+ label: "[ALL] Select all Agents",
5551
+ kind: "agent",
5552
+ tab: "agent",
5553
+ isSelectAll: true,
5554
+ hint: `Select or deselect all ${agentItems.length} Agents in this tab`
5555
+ });
5556
+ withAll.push(...agentItems);
5557
+ }
5558
+ if (withAll.length > 0) {
5559
+ finalOptions = withAll;
5560
+ }
5561
+ }
5423
5562
  const initial = /* @__PURE__ */ new Set();
5424
- for (const opt of options) {
5425
- if (opt.initialSelected) {
5563
+ for (const opt of finalOptions) {
5564
+ if (opt.initialSelected && !opt.isSelectAll) {
5426
5565
  initial.add(opt.value);
5427
5566
  }
5428
5567
  }
5429
- const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
5568
+ const hasSkills = finalOptions.some((opt) => optionMatchesTab(opt, "skill"));
5430
5569
  return {
5431
- options,
5570
+ options: finalOptions,
5432
5571
  activeTab: hasSkills ? "skill" : "agent",
5433
5572
  cursor: 0,
5434
5573
  selected: initial,
@@ -5518,6 +5657,29 @@ function applyMultiSelectKey(state, key, options = {}) {
5518
5657
  if (!currentOpt) {
5519
5658
  return state;
5520
5659
  }
5660
+ if (currentOpt.isSelectAll) {
5661
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5662
+ if (realVisible.length === 0) {
5663
+ return state;
5664
+ }
5665
+ const allTabSelected = realVisible.every(
5666
+ (opt) => state.selected.has(opt.value)
5667
+ );
5668
+ const nextSelected2 = new Set(state.selected);
5669
+ if (allTabSelected) {
5670
+ for (const opt of realVisible) {
5671
+ nextSelected2.delete(opt.value);
5672
+ }
5673
+ } else {
5674
+ for (const opt of realVisible) {
5675
+ nextSelected2.add(opt.value);
5676
+ }
5677
+ }
5678
+ return {
5679
+ ...state,
5680
+ selected: nextSelected2
5681
+ };
5682
+ }
5521
5683
  const nextSelected = new Set(state.selected);
5522
5684
  if (nextSelected.has(currentOpt.value)) {
5523
5685
  nextSelected.delete(currentOpt.value);
@@ -5581,19 +5743,20 @@ function applyMultiSelectKey(state, key, options = {}) {
5581
5743
  };
5582
5744
  }
5583
5745
  if (key === "a" || key === "A") {
5584
- if (visible.length === 0) {
5746
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5747
+ if (realVisible.length === 0) {
5585
5748
  return state;
5586
5749
  }
5587
- const allTabSelected = visible.every(
5750
+ const allTabSelected = realVisible.every(
5588
5751
  (opt) => state.selected.has(opt.value)
5589
5752
  );
5590
5753
  const nextSelected = new Set(state.selected);
5591
5754
  if (allTabSelected) {
5592
- for (const opt of visible) {
5755
+ for (const opt of realVisible) {
5593
5756
  nextSelected.delete(opt.value);
5594
5757
  }
5595
5758
  } else {
5596
- for (const opt of visible) {
5759
+ for (const opt of realVisible) {
5597
5760
  nextSelected.add(opt.value);
5598
5761
  }
5599
5762
  }
@@ -5608,41 +5771,69 @@ function renderMultiSelectFrame(title, state, options = {}) {
5608
5771
  const c = options.colorEnabled;
5609
5772
  const bar = style.dim("\u2502", c);
5610
5773
  const maxVisible = options.maxVisible ?? 10;
5611
- const totalSkills = state.options.filter(
5774
+ const realOptions = state.options.filter((o) => !o.isSelectAll);
5775
+ const totalSkills = realOptions.filter(
5612
5776
  (o) => optionMatchesTab(o, "skill")
5613
5777
  ).length;
5614
- const totalAgents = state.options.filter(
5778
+ const totalAgents = realOptions.filter(
5615
5779
  (o) => optionMatchesTab(o, "agent")
5616
5780
  ).length;
5617
5781
  const filteredSkills = getFilteredTabOptions(state, "skill");
5618
5782
  const filteredAgents = getFilteredTabOptions(state, "agent");
5783
+ const realFilteredSkills = filteredSkills.filter((o) => !o.isSelectAll);
5784
+ const realFilteredAgents = filteredAgents.filter((o) => !o.isSelectAll);
5619
5785
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5786
+ const realVisible = state.activeTab === "skill" ? realFilteredSkills : realFilteredAgents;
5620
5787
  const total = visible.length;
5621
5788
  const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5622
5789
  const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5623
5790
  const submitVerb = options.submitActionLabel ?? "import";
5624
5791
  const isSinglePage = totalAgents === 0 || totalSkills === 0;
5625
- const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5626
- const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5627
- const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
5792
+ const header = renderPill(`\u25C6 ${title}`, {
5793
+ fg: CHARM_PALETTE.white,
5794
+ bg: CHARM_PALETTE.indigo,
5795
+ bold: true,
5796
+ colorEnabled: c
5797
+ });
5798
+ const sep = style.dim(" \xB7 ", c);
5799
+ const keyItems = [
5800
+ renderKeycap("\u2191/\u2193", "move", c),
5801
+ ...isSinglePage ? [] : [renderKeycap("\u2190/\u2192", "tab", c)],
5802
+ renderKeycap("Space", "select", c),
5803
+ ...state.searchMode ? [renderKeycap("Esc", "clear", c)] : [renderKeycap("/", "search", c), renderKeycap("a", "all", c)],
5804
+ renderKeycap("Enter", submitVerb, c)
5805
+ ];
5806
+ const instructions = `${bar} ${keyItems.join(sep)}`;
5628
5807
  const lines = [header];
5629
5808
  if (options.subtitle) {
5630
- lines.push(`${bar} ${options.subtitle}`);
5809
+ lines.push(`${bar} ${fgHex(CHARM_PALETTE.lavender, options.subtitle, c)}`);
5631
5810
  }
5632
5811
  lines.push(instructions, bar);
5633
5812
  const hasQuery = state.searchQuery.length > 0;
5634
- const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5635
- const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5636
- const skillTabLabel = state.activeTab === "skill" ? style.bold(
5637
- style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5638
- c
5639
- ) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5640
- const agentTabLabel = state.activeTab === "agent" ? style.bold(
5641
- style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c),
5642
- c
5643
- ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5813
+ const skillCountStr = hasQuery ? `${realFilteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5814
+ const agentCountStr = hasQuery ? `${realFilteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5815
+ const skillTabLabel = state.activeTab === "skill" ? renderPill(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, {
5816
+ fg: CHARM_PALETTE.white,
5817
+ bg: CHARM_PALETTE.indigo,
5818
+ bold: true,
5819
+ colorEnabled: c
5820
+ }) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5821
+ const agentTabLabel = state.activeTab === "agent" ? renderPill(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, {
5822
+ fg: CHARM_PALETTE.white,
5823
+ bg: CHARM_PALETTE.hotPink,
5824
+ bold: true,
5825
+ colorEnabled: c
5826
+ }) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5644
5827
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5645
- const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5828
+ const searchSuffix = state.searchMode || hasQuery ? ` ${renderPill(
5829
+ `/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`,
5830
+ {
5831
+ fg: CHARM_PALETTE.amber,
5832
+ bg: CHARM_PALETTE.surface,
5833
+ bold: true,
5834
+ colorEnabled: c
5835
+ }
5836
+ )}` : ` ${style.dim("(/ search)", c)}`;
5646
5837
  const singleTabLabel = totalSkills > 0 ? skillTabLabel : agentTabLabel;
5647
5838
  lines.push(
5648
5839
  isSinglePage ? `${style.dim("\u251C\u2500", c)} ${singleTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
@@ -5660,8 +5851,16 @@ function renderMultiSelectFrame(title, state, options = {}) {
5660
5851
  startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
5661
5852
  }
5662
5853
  const endIdx = Math.min(total, startIdx + maxVisible);
5854
+ const totalPages = Math.max(1, Math.ceil(total / maxVisible));
5855
+ const currentPage = Math.min(
5856
+ totalPages - 1,
5857
+ Math.floor(state.cursor / maxVisible)
5858
+ );
5859
+ const pageDots = totalPages > 1 ? ` ${renderPaginationDots(currentPage, totalPages, c)}` : "";
5663
5860
  if (startIdx > 0) {
5664
- lines.push(`${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}`);
5861
+ lines.push(
5862
+ `${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}${endIdx >= total ? pageDots : ""}`
5863
+ );
5665
5864
  }
5666
5865
  const maxLabelCols = Math.min(
5667
5866
  26,
@@ -5671,30 +5870,32 @@ function renderMultiSelectFrame(title, state, options = {}) {
5671
5870
  const opt = visible[i];
5672
5871
  if (!opt) continue;
5673
5872
  const isCurrent = i === state.cursor;
5674
- const isChecked = state.selected.has(opt.value);
5675
- const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5676
- const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
5873
+ const allRealSelected = realVisible.length > 0 && realVisible.every((o) => state.selected.has(o.value));
5874
+ const someRealSelected = realVisible.some((o) => state.selected.has(o.value)) && !allRealSelected;
5875
+ const isChecked = opt.isSelectAll ? allRealSelected : state.selected.has(opt.value);
5876
+ const pointer = isCurrent ? fgHex(CHARM_PALETTE.hotPink, style.bold("\u276F", c), c) : " ";
5877
+ const checkbox = isChecked ? style.green("\u25C9", c) : opt.isSelectAll && someRealSelected ? fgHex(CHARM_PALETTE.fuchsia, "\u25D0", c) : style.dim("\u25EF", c);
5677
5878
  const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5678
5879
  const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5679
- const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
5880
+ const styledLabel = isCurrent ? fgHex(CHARM_PALETTE.fuchsia, style.bold(labelPadded, c), c) : isChecked ? style.green(labelPadded, c) : labelPadded;
5680
5881
  const prefixWidth = 7 + maxLabelCols + 1;
5681
5882
  const maxHintCols = Math.max(10, 76 - prefixWidth);
5682
5883
  const rawHint = opt.hint ?? "";
5683
5884
  const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
5684
- const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
5885
+ const hintPart = shortHint ? ` ${isCurrent ? fgHex(CHARM_PALETTE.lavender, shortHint, c) : style.dim(shortHint, c)}` : "";
5685
5886
  lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
5686
5887
  }
5687
5888
  if (endIdx < total) {
5688
5889
  lines.push(
5689
- `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
5890
+ `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}${pageDots}`
5690
5891
  );
5691
5892
  }
5692
5893
  }
5693
5894
  lines.push(bar);
5694
- const selectedSkillsCount = state.options.filter(
5895
+ const selectedSkillsCount = realOptions.filter(
5695
5896
  (o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
5696
5897
  ).length;
5697
- const selectedAgentsCount = state.options.filter(
5898
+ const selectedAgentsCount = realOptions.filter(
5698
5899
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5699
5900
  ).length;
5700
5901
  const currentOptLabel = visible[state.cursor]?.label ?? "";
@@ -5702,8 +5903,9 @@ function renderMultiSelectFrame(title, state, options = {}) {
5702
5903
  const rawTarget = options.targetHint ?? "";
5703
5904
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5704
5905
  const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
5906
+ const styledSummary = state.selected.size > 0 ? fgHex(CHARM_PALETTE.mint, style.bold(summaryText, c), c) : fgHex(CHARM_PALETTE.lavender, style.bold(summaryText, c), c);
5705
5907
  lines.push(
5706
- `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5908
+ `${style.dim("\u2570\u2500", c)} ${styledSummary}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5707
5909
  );
5708
5910
  return lines.join("\n");
5709
5911
  }
@@ -5711,7 +5913,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5711
5913
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
5712
5914
  return null;
5713
5915
  }
5714
- let state = createMultiSelectState(options);
5916
+ let state = createMultiSelectState(options, {
5917
+ showSelectAll: renderOpts.showSelectAll
5918
+ });
5715
5919
  let renderedLineCount = 0;
5716
5920
  const render = () => {
5717
5921
  const frame = renderMultiSelectFrame(title, state, renderOpts);
@@ -6972,16 +7176,19 @@ function runCli(argv) {
6972
7176
  unit: "tokens"
6973
7177
  });
6974
7178
  const firstSkill = summary.skills[0]?.skillName ?? "security-sast-gate";
7179
+ const skillBadge = formatBadge("skill", "SKILL");
7180
+ const subagentBadge = formatBadge("subagent", "SUBAGENT");
6975
7181
  const lines = [
6976
- `=== SkillOps Catalog (v${SKILLOPS_VERSION}) ===`,
6977
- `Skills (${summary.skills.length}) \u2014 Total L1 Routing Tax: ${summary.totalL1Tokens} tokens`,
6978
- `L1 Budget Meter: ${l1Bar}`,
7182
+ renderHeaderBanner("Catalog \u2014 Hybrid Skills & Context-Isolated Agents"),
7183
+ `${style.dim("\u251C\u2500")} ${style.bold(`Skills (${summary.skills.length})`)} \u2014 Total L1 Routing Tax: ${style.green(`${summary.totalL1Tokens} tokens`)}`,
7184
+ `${style.dim("\u2502")} L1 Budget Meter: ${l1Bar}`,
6979
7185
  ...summary.skills.map(
6980
- (s) => ` [SKILL] ${s.skillName} \u2014 L1: ${s.l1Tokens}t | L2: ${s.l2Lines} lines (${s.l2Tokens}t) | L3 Scripts: ${s.scriptsCount}`
7186
+ (s) => `${style.dim("\u2502")} ${skillBadge} ${style.bold(s.skillName)} \u2014 L1: ${s.l1Tokens}t | L2: ${s.l2Lines} lines (${s.l2Tokens}t) | L3 Scripts: ${s.scriptsCount}`
6981
7187
  ),
6982
- `Subagents (${summary.subagents.length}) \u2014 Context-Isolated Specialist Agents:`,
7188
+ style.dim("\u2502"),
7189
+ `${style.dim("\u251C\u2500")} ${style.bold(`Subagents (${summary.subagents.length})`)} \u2014 Context-Isolated Specialist Agents:`,
6983
7190
  ...summary.subagents.map(
6984
- (a) => ` [SUBAGENT] ${a.subagentName} (prompt=${a.systemPromptTokens}t, max_out<=${a.maxOutputTokens}t, tools=${a.tools.length})`
7191
+ (a) => `${style.dim("\u2502")} ${subagentBadge} ${style.bold(a.subagentName)} (prompt=${a.systemPromptTokens}t, max_out<=${a.maxOutputTokens}t, tools=${a.tools.length})`
6985
7192
  ),
6986
7193
  renderNextSteps([
6987
7194
  {
@@ -7478,7 +7685,8 @@ async function runCliInteractive(argv) {
7478
7685
  const options = buildInteractiveCatalogOptions(catalogDir);
7479
7686
  const step2Title = !explicitTargetsArg ? "SkillOps \u2014 Step 2/2: Select Skills & Agents to Import" : "SkillOps \u2014 Select Skills & Agents to Import";
7480
7687
  const selected = await runMultiSelectPrompt(step2Title, options, {
7481
- targetHint: targets.join(", ")
7688
+ targetHint: targets.join(", "),
7689
+ showSelectAll: true
7482
7690
  });
7483
7691
  if (!selected || selected.length === 0) {
7484
7692
  process.stdout.write("No Skills or Agents selected.\n");
@@ -7529,7 +7737,8 @@ async function runCliInteractive(argv) {
7529
7737
  installedOptions,
7530
7738
  {
7531
7739
  submitActionLabel: "remove",
7532
- targetHint: targets ? targets.join(", ") : void 0
7740
+ targetHint: targets ? targets.join(", ") : void 0,
7741
+ showSelectAll: true
7533
7742
  }
7534
7743
  );
7535
7744
  if (!selected || selected.length === 0) {
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import { z } from 'zod';
8
8
  * telemetry and Multi-Runtime Transpilation lockfiles.
9
9
  */
10
10
 
11
- declare const SKILLOPS_VERSION = "1.3.6";
11
+ declare const SKILLOPS_VERSION = "1.3.8";
12
12
  /**
13
13
  * Count whitespace-delimited words in text.
14
14
  */
@@ -2173,6 +2173,67 @@ declare function renderGlobalHelp(version: string, colorEnabled?: boolean): stri
2173
2173
  */
2174
2174
  declare function renderCommandHelp(spec: CommandHelpSpec, version: string, colorEnabled?: boolean): string;
2175
2175
 
2176
+ /**
2177
+ * Charmbracelet Lip Gloss & Bubbles Inspired Terminal Styling Engine (`src/ui/lipgloss.ts`).
2178
+ *
2179
+ * Zero-dependency TypeScript implementation of Lip Gloss TrueColor (24-bit RGB)
2180
+ * palettes, background pills, Bubbles paginator dots, keycap status bars, and
2181
+ * ANSI-aware horizontal block composition (`joinHorizontal`).
2182
+ */
2183
+ /**
2184
+ * Official Charmbracelet Lip Gloss / Huh / Bubbles inspired TrueColor palette.
2185
+ */
2186
+ declare const CHARM_PALETTE: {
2187
+ readonly indigo: "#7D56F4";
2188
+ readonly fuchsia: "#EE6FF8";
2189
+ readonly hotPink: "#F25D94";
2190
+ readonly mint: "#04B575";
2191
+ readonly amber: "#FFBD2E";
2192
+ readonly coral: "#FF5F56";
2193
+ readonly cyan: "#00D7FF";
2194
+ readonly lavender: "#B4A7D6";
2195
+ readonly white: "#FAFAFA";
2196
+ readonly muted: "#6C6C6C";
2197
+ readonly subtle: "#4A4A4A";
2198
+ readonly surface: "#2A2438";
2199
+ readonly keycapBg: "#302B3F";
2200
+ };
2201
+ type CharmColorKey = keyof typeof CHARM_PALETTE;
2202
+ /**
2203
+ * Parse a `#RRGGBB` or `#RGB` hex color string into an `[r, g, b]` tuple.
2204
+ */
2205
+ declare function hexToRgb(hex: string): readonly [number, number, number];
2206
+ /**
2207
+ * Apply 24-bit TrueColor foreground (`\x1b[38;2;R;G;Bm`) when color is enabled.
2208
+ */
2209
+ declare function fgHex(hex: string, text: string, enabled?: boolean): string;
2210
+ /**
2211
+ * Apply 24-bit TrueColor background (`\x1b[48;2;R;G;Bm`) when color is enabled.
2212
+ */
2213
+ declare function bgHex(hex: string, text: string, enabled?: boolean): string;
2214
+ interface PillOptions {
2215
+ readonly fg?: string;
2216
+ readonly bg?: string;
2217
+ readonly bold?: boolean;
2218
+ readonly colorEnabled?: boolean;
2219
+ }
2220
+ /**
2221
+ * Render a Lip Gloss background pill badge without altering visible character length.
2222
+ */
2223
+ declare function renderPill(text: string, options?: PillOptions): string;
2224
+ /**
2225
+ * Render a Charm `bubbles/help` style keybinding hint (`key action`).
2226
+ */
2227
+ declare function renderKeycap(keyLabel: string, actionLabel: string, colorEnabled?: boolean): string;
2228
+ /**
2229
+ * Render Charm `bubbles/paginator` style pagination dots (`• ◦ ◦`).
2230
+ */
2231
+ declare function renderPaginationDots(currentPage: number, totalPages: number, colorEnabled?: boolean): string;
2232
+ /**
2233
+ * Compose two multi-line blocks horizontally (`lipgloss.JoinHorizontal`).
2234
+ */
2235
+ declare function joinHorizontal(align: "top" | "center" | "bottom", leftBlock: string, rightBlock: string, gap?: number): string;
2236
+
2176
2237
  /**
2177
2238
  * Zero-dependency interactive terminal multi-select prompt (`src/ui/prompt.ts`).
2178
2239
  *
@@ -2190,6 +2251,10 @@ interface MultiSelectOption {
2190
2251
  readonly tab?: MultiSelectTab;
2191
2252
  readonly hint?: string;
2192
2253
  readonly initialSelected?: boolean;
2254
+ readonly isSelectAll?: boolean;
2255
+ }
2256
+ interface CreateMultiSelectStateOptions {
2257
+ readonly showSelectAll?: boolean;
2193
2258
  }
2194
2259
  interface MultiSelectState {
2195
2260
  readonly options: readonly MultiSelectOption[];
@@ -2212,6 +2277,7 @@ interface RenderMultiSelectOptions {
2212
2277
  };
2213
2278
  readonly submitActionLabel?: string;
2214
2279
  readonly selectCursorOnEmptySubmit?: boolean;
2280
+ readonly showSelectAll?: boolean;
2215
2281
  readonly maxVisible?: number;
2216
2282
  readonly colorEnabled?: boolean;
2217
2283
  }
@@ -2222,7 +2288,7 @@ declare function getFilteredTabOptions(state: MultiSelectState, tab?: MultiSelec
2222
2288
  /**
2223
2289
  * Initialize a deterministic multi-select state from a list of options.
2224
2290
  */
2225
- declare function createMultiSelectState(options: readonly MultiSelectOption[]): MultiSelectState;
2291
+ declare function createMultiSelectState(options: readonly MultiSelectOption[], stateOptions?: CreateMultiSelectStateOptions): MultiSelectState;
2226
2292
  /**
2227
2293
  * Pure state reducer for keyboard input in the tabbed + searchable multi-select TUI.
2228
2294
  */
@@ -2356,4 +2422,4 @@ declare const DEFAULT_VALID_COMMANDS: readonly string[];
2356
2422
  */
2357
2423
  declare function suggestCommand(unknownCommand: string, validCommands?: readonly string[]): string | null;
2358
2424
 
2359
- export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type ApplyMultiSelectKeyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, inspectCatalogItem, inspectHarnesses, isColorEnabled, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderMultiSelectFrame, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
2425
+ export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type ApplyMultiSelectKeyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, CHARM_PALETTE, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CharmColorKey, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, type CreateMultiSelectStateOptions, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type PillOptions, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, bgHex, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, fgHex, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, hexToRgb, inspectCatalogItem, inspectHarnesses, isColorEnabled, joinHorizontal, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderKeycap, renderMultiSelectFrame, renderNextSteps, renderPaginationDots, renderPill, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import fs from "fs";
3
3
  import YAML from "yaml";
4
4
  import { z } from "zod";
5
- var SKILLOPS_VERSION = "1.3.6";
5
+ var SKILLOPS_VERSION = "1.3.8";
6
6
  var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
7
7
  var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
8
8
  var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -1418,9 +1418,142 @@ import fs3 from "fs";
1418
1418
  import path2 from "path";
1419
1419
  import YAML2 from "yaml";
1420
1420
 
1421
- // src/ui/terminal.ts
1421
+ // src/ui/lipgloss.ts
1422
1422
  var ESC = String.fromCharCode(27);
1423
- var ANSI_ESCAPE_REGEX = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
1423
+ var CHARM_PALETTE = {
1424
+ indigo: "#7D56F4",
1425
+ fuchsia: "#EE6FF8",
1426
+ hotPink: "#F25D94",
1427
+ mint: "#04B575",
1428
+ amber: "#FFBD2E",
1429
+ coral: "#FF5F56",
1430
+ cyan: "#00D7FF",
1431
+ lavender: "#B4A7D6",
1432
+ white: "#FAFAFA",
1433
+ muted: "#6C6C6C",
1434
+ subtle: "#4A4A4A",
1435
+ surface: "#2A2438",
1436
+ keycapBg: "#302B3F"
1437
+ };
1438
+ function hexToRgb(hex) {
1439
+ const cleaned = hex.trim().replace(/^#/, "");
1440
+ if (cleaned.length === 3) {
1441
+ const r2 = Number.parseInt(`${cleaned[0]}${cleaned[0]}`, 16);
1442
+ const g2 = Number.parseInt(`${cleaned[1]}${cleaned[1]}`, 16);
1443
+ const b2 = Number.parseInt(`${cleaned[2]}${cleaned[2]}`, 16);
1444
+ return [
1445
+ Number.isNaN(r2) ? 255 : r2,
1446
+ Number.isNaN(g2) ? 255 : g2,
1447
+ Number.isNaN(b2) ? 255 : b2
1448
+ ];
1449
+ }
1450
+ const r = Number.parseInt(cleaned.slice(0, 2), 16);
1451
+ const g = Number.parseInt(cleaned.slice(2, 4), 16);
1452
+ const b = Number.parseInt(cleaned.slice(4, 6), 16);
1453
+ return [
1454
+ Number.isNaN(r) ? 255 : r,
1455
+ Number.isNaN(g) ? 255 : g,
1456
+ Number.isNaN(b) ? 255 : b
1457
+ ];
1458
+ }
1459
+ function fgHex(hex, text, enabled) {
1460
+ const useColor = enabled ?? isColorEnabled();
1461
+ if (!useColor || text.length === 0) {
1462
+ return text;
1463
+ }
1464
+ const [r, g, b] = hexToRgb(hex);
1465
+ return `${ESC}[38;2;${r};${g};${b}m${text}${ESC}[39m`;
1466
+ }
1467
+ function bgHex(hex, text, enabled) {
1468
+ const useColor = enabled ?? isColorEnabled();
1469
+ if (!useColor || text.length === 0) {
1470
+ return text;
1471
+ }
1472
+ const [r, g, b] = hexToRgb(hex);
1473
+ return `${ESC}[48;2;${r};${g};${b}m${text}${ESC}[49m`;
1474
+ }
1475
+ function renderPill(text, options) {
1476
+ const useColor = options?.colorEnabled ?? isColorEnabled();
1477
+ if (!useColor || text.length === 0) {
1478
+ return text;
1479
+ }
1480
+ const fg = options?.fg ?? CHARM_PALETTE.white;
1481
+ const bg = options?.bg ?? CHARM_PALETTE.indigo;
1482
+ const bold = options?.bold ?? true;
1483
+ let styled = fgHex(fg, text, true);
1484
+ styled = bgHex(bg, styled, true);
1485
+ if (bold) {
1486
+ styled = `${ESC}[1m${styled}${ESC}[22m`;
1487
+ }
1488
+ return styled;
1489
+ }
1490
+ function renderKeycap(keyLabel, actionLabel, colorEnabled) {
1491
+ const useColor = colorEnabled ?? isColorEnabled();
1492
+ if (!useColor) {
1493
+ return `${keyLabel} ${actionLabel}`;
1494
+ }
1495
+ const keyStyled = `${ESC}[1m${fgHex(CHARM_PALETTE.lavender, keyLabel, true)}${ESC}[22m`;
1496
+ const actionStyled = fgHex(CHARM_PALETTE.muted, actionLabel, true);
1497
+ return `${keyStyled} ${actionStyled}`;
1498
+ }
1499
+ function renderPaginationDots(currentPage, totalPages, colorEnabled) {
1500
+ const safeTotal = Math.max(1, Math.min(12, totalPages));
1501
+ const safeCurrent = Math.max(0, Math.min(safeTotal - 1, currentPage));
1502
+ const useColor = colorEnabled ?? isColorEnabled();
1503
+ const dots = [];
1504
+ for (let i = 0; i < safeTotal; i++) {
1505
+ if (i === safeCurrent) {
1506
+ dots.push(
1507
+ useColor ? `${ESC}[1m${fgHex(CHARM_PALETTE.hotPink, "\u2022", true)}${ESC}[22m` : "\u2022"
1508
+ );
1509
+ } else {
1510
+ dots.push(useColor ? fgHex(CHARM_PALETTE.muted, "\u25E6", true) : "\u25E6");
1511
+ }
1512
+ }
1513
+ return dots.join(" ");
1514
+ }
1515
+ function joinHorizontal(align, leftBlock, rightBlock, gap = 2) {
1516
+ const leftLines = leftBlock.split("\n");
1517
+ const rightLines = rightBlock.split("\n");
1518
+ const totalRows = Math.max(leftLines.length, rightLines.length);
1519
+ const leftWidth = leftLines.reduce(
1520
+ (max, line) => Math.max(max, visibleLength(line)),
1521
+ 0
1522
+ );
1523
+ const gapStr = " ".repeat(Math.max(0, gap));
1524
+ const padBlockLines = (lines) => {
1525
+ const diff = totalRows - lines.length;
1526
+ if (diff <= 0) {
1527
+ return lines;
1528
+ }
1529
+ if (align === "bottom") {
1530
+ return [...Array.from({ length: diff }, () => ""), ...lines];
1531
+ }
1532
+ if (align === "center") {
1533
+ const top = Math.floor(diff / 2);
1534
+ const bottom = diff - top;
1535
+ return [
1536
+ ...Array.from({ length: top }, () => ""),
1537
+ ...lines,
1538
+ ...Array.from({ length: bottom }, () => "")
1539
+ ];
1540
+ }
1541
+ return [...lines, ...Array.from({ length: diff }, () => "")];
1542
+ };
1543
+ const paddedLeft = padBlockLines(leftLines);
1544
+ const paddedRight = padBlockLines(rightLines);
1545
+ const out = [];
1546
+ for (let i = 0; i < totalRows; i++) {
1547
+ const l = padVisible(paddedLeft[i] ?? "", leftWidth, "left");
1548
+ const r = paddedRight[i] ?? "";
1549
+ out.push(r.length > 0 ? `${l}${gapStr}${r}` : l.trimEnd());
1550
+ }
1551
+ return out.join("\n");
1552
+ }
1553
+
1554
+ // src/ui/terminal.ts
1555
+ var ESC2 = String.fromCharCode(27);
1556
+ var ANSI_ESCAPE_REGEX = new RegExp(`${ESC2}\\[[0-9;]*m`, "g");
1424
1557
  function isColorEnabled(options) {
1425
1558
  const env = options?.env ?? process.env;
1426
1559
  const isTTY = options?.isTTY ?? Boolean(process.stdout?.isTTY);
@@ -1446,21 +1579,21 @@ function wrapAnsi(openCode, closeCode, text, enabled) {
1446
1579
  if (!useColor || text.length === 0) {
1447
1580
  return text;
1448
1581
  }
1449
- return `${ESC}[${openCode}m${text}${ESC}[${closeCode}m`;
1582
+ return `${ESC2}[${openCode}m${text}${ESC2}[${closeCode}m`;
1450
1583
  }
1451
1584
  var style = {
1452
1585
  bold: (text, enabled) => wrapAnsi("1", "22", text, enabled),
1453
1586
  dim: (text, enabled) => wrapAnsi("2", "22", text, enabled),
1454
1587
  italic: (text, enabled) => wrapAnsi("3", "23", text, enabled),
1455
1588
  underline: (text, enabled) => wrapAnsi("4", "24", text, enabled),
1456
- cyan: (text, enabled) => wrapAnsi("36", "39", text, enabled),
1457
- green: (text, enabled) => wrapAnsi("32", "39", text, enabled),
1458
- yellow: (text, enabled) => wrapAnsi("33", "39", text, enabled),
1459
- red: (text, enabled) => wrapAnsi("31", "39", text, enabled),
1460
- magenta: (text, enabled) => wrapAnsi("35", "39", text, enabled),
1461
- blue: (text, enabled) => wrapAnsi("34", "39", text, enabled),
1462
- gray: (text, enabled) => wrapAnsi("90", "39", text, enabled),
1463
- white: (text, enabled) => wrapAnsi("37", "39", text, enabled)
1589
+ cyan: (text, enabled) => fgHex(CHARM_PALETTE.cyan, text, enabled),
1590
+ green: (text, enabled) => fgHex(CHARM_PALETTE.mint, text, enabled),
1591
+ yellow: (text, enabled) => fgHex(CHARM_PALETTE.amber, text, enabled),
1592
+ red: (text, enabled) => fgHex(CHARM_PALETTE.coral, text, enabled),
1593
+ magenta: (text, enabled) => fgHex(CHARM_PALETTE.fuchsia, text, enabled),
1594
+ blue: (text, enabled) => fgHex(CHARM_PALETTE.indigo, text, enabled),
1595
+ gray: (text, enabled) => fgHex(CHARM_PALETTE.muted, text, enabled),
1596
+ white: (text, enabled) => fgHex(CHARM_PALETTE.white, text, enabled)
1464
1597
  };
1465
1598
  function stripAnsi(text) {
1466
1599
  return text.replace(ANSI_ESCAPE_REGEX, "");
@@ -1488,7 +1621,7 @@ function renderBox(lines, options) {
1488
1621
  const paddingX = options?.paddingX ?? 2;
1489
1622
  const minWidth = options?.minWidth ?? 36;
1490
1623
  const colorEnabled = options?.colorEnabled;
1491
- const borderFn = options?.borderColor ?? style.cyan;
1624
+ const borderFn = options?.borderColor ?? style.blue;
1492
1625
  const titleText = options?.title ? ` ${options.title.trim()} ` : "";
1493
1626
  const titleVisibleLen = visibleLength(titleText);
1494
1627
  const maxLineLen = lines.reduce(
@@ -1513,7 +1646,13 @@ function renderBox(lines, options) {
1513
1646
  function renderHeaderBanner(subtitle = "Framework & Library for AI Skills & Subagents", options) {
1514
1647
  const ver = options?.version ?? SKILLOPS_VERSION;
1515
1648
  const c = options?.colorEnabled;
1516
- return `${style.bold(style.cyan(`\u25C6 SkillOps v${ver}`, c), c)} ${style.dim("\u2014", c)} ${subtitle}`;
1649
+ const brandPill = renderPill(`\u25C6 SkillOps v${ver}`, {
1650
+ fg: CHARM_PALETTE.white,
1651
+ bg: CHARM_PALETTE.indigo,
1652
+ bold: true,
1653
+ colorEnabled: c
1654
+ });
1655
+ return `${brandPill} ${style.dim("\u2014", c)} ${fgHex(CHARM_PALETTE.lavender, subtitle, c)}`;
1517
1656
  }
1518
1657
  function renderTable(headers, rows, options) {
1519
1658
  const indent = " ".repeat(options?.indent ?? 2);
@@ -1590,15 +1729,19 @@ function formatBadge(type, customLabel, colorEnabled) {
1590
1729
  colorEnabled
1591
1730
  );
1592
1731
  case "skill":
1593
- return style.bold(
1594
- style.cyan(`[${customLabel ?? "SKILL"}]`, colorEnabled),
1732
+ return renderPill(`[${customLabel ?? "SKILL"}]`, {
1733
+ fg: CHARM_PALETTE.white,
1734
+ bg: CHARM_PALETTE.indigo,
1735
+ bold: true,
1595
1736
  colorEnabled
1596
- );
1737
+ });
1597
1738
  case "subagent":
1598
- return style.bold(
1599
- style.magenta(`[${customLabel ?? "SUBAGENT"}]`, colorEnabled),
1739
+ return renderPill(`[${customLabel ?? "SUBAGENT"}]`, {
1740
+ fg: CHARM_PALETTE.white,
1741
+ bg: CHARM_PALETTE.hotPink,
1742
+ bold: true,
1600
1743
  colorEnabled
1601
- );
1744
+ });
1602
1745
  }
1603
1746
  }
1604
1747
  function renderStep(step, total, title, detail, status = "done", colorEnabled) {
@@ -5430,6 +5573,9 @@ function optionMatchesQuery(opt, rawQuery) {
5430
5573
  if (!q) {
5431
5574
  return true;
5432
5575
  }
5576
+ if (opt.isSelectAll) {
5577
+ return false;
5578
+ }
5433
5579
  return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
5434
5580
  }
5435
5581
  function getFilteredTabOptions(state, tab = state.activeTab) {
@@ -5437,16 +5583,47 @@ function getFilteredTabOptions(state, tab = state.activeTab) {
5437
5583
  (opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
5438
5584
  );
5439
5585
  }
5440
- function createMultiSelectState(options) {
5586
+ function createMultiSelectState(options, stateOptions = {}) {
5587
+ let finalOptions = options;
5588
+ if (stateOptions.showSelectAll && !options.some((opt) => opt.isSelectAll === true)) {
5589
+ const skillItems = options.filter((opt) => optionMatchesTab(opt, "skill"));
5590
+ const agentItems = options.filter((opt) => optionMatchesTab(opt, "agent"));
5591
+ const withAll = [];
5592
+ if (skillItems.length > 0) {
5593
+ withAll.push({
5594
+ value: "__all_skills__",
5595
+ label: "[ALL] Select all Skills",
5596
+ kind: "skill",
5597
+ tab: "skill",
5598
+ isSelectAll: true,
5599
+ hint: `Select or deselect all ${skillItems.length} Skills in this tab`
5600
+ });
5601
+ withAll.push(...skillItems);
5602
+ }
5603
+ if (agentItems.length > 0) {
5604
+ withAll.push({
5605
+ value: "__all_agents__",
5606
+ label: "[ALL] Select all Agents",
5607
+ kind: "agent",
5608
+ tab: "agent",
5609
+ isSelectAll: true,
5610
+ hint: `Select or deselect all ${agentItems.length} Agents in this tab`
5611
+ });
5612
+ withAll.push(...agentItems);
5613
+ }
5614
+ if (withAll.length > 0) {
5615
+ finalOptions = withAll;
5616
+ }
5617
+ }
5441
5618
  const initial = /* @__PURE__ */ new Set();
5442
- for (const opt of options) {
5443
- if (opt.initialSelected) {
5619
+ for (const opt of finalOptions) {
5620
+ if (opt.initialSelected && !opt.isSelectAll) {
5444
5621
  initial.add(opt.value);
5445
5622
  }
5446
5623
  }
5447
- const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
5624
+ const hasSkills = finalOptions.some((opt) => optionMatchesTab(opt, "skill"));
5448
5625
  return {
5449
- options,
5626
+ options: finalOptions,
5450
5627
  activeTab: hasSkills ? "skill" : "agent",
5451
5628
  cursor: 0,
5452
5629
  selected: initial,
@@ -5536,6 +5713,29 @@ function applyMultiSelectKey(state, key, options = {}) {
5536
5713
  if (!currentOpt) {
5537
5714
  return state;
5538
5715
  }
5716
+ if (currentOpt.isSelectAll) {
5717
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5718
+ if (realVisible.length === 0) {
5719
+ return state;
5720
+ }
5721
+ const allTabSelected = realVisible.every(
5722
+ (opt) => state.selected.has(opt.value)
5723
+ );
5724
+ const nextSelected2 = new Set(state.selected);
5725
+ if (allTabSelected) {
5726
+ for (const opt of realVisible) {
5727
+ nextSelected2.delete(opt.value);
5728
+ }
5729
+ } else {
5730
+ for (const opt of realVisible) {
5731
+ nextSelected2.add(opt.value);
5732
+ }
5733
+ }
5734
+ return {
5735
+ ...state,
5736
+ selected: nextSelected2
5737
+ };
5738
+ }
5539
5739
  const nextSelected = new Set(state.selected);
5540
5740
  if (nextSelected.has(currentOpt.value)) {
5541
5741
  nextSelected.delete(currentOpt.value);
@@ -5599,19 +5799,20 @@ function applyMultiSelectKey(state, key, options = {}) {
5599
5799
  };
5600
5800
  }
5601
5801
  if (key === "a" || key === "A") {
5602
- if (visible.length === 0) {
5802
+ const realVisible = visible.filter((opt) => !opt.isSelectAll);
5803
+ if (realVisible.length === 0) {
5603
5804
  return state;
5604
5805
  }
5605
- const allTabSelected = visible.every(
5806
+ const allTabSelected = realVisible.every(
5606
5807
  (opt) => state.selected.has(opt.value)
5607
5808
  );
5608
5809
  const nextSelected = new Set(state.selected);
5609
5810
  if (allTabSelected) {
5610
- for (const opt of visible) {
5811
+ for (const opt of realVisible) {
5611
5812
  nextSelected.delete(opt.value);
5612
5813
  }
5613
5814
  } else {
5614
- for (const opt of visible) {
5815
+ for (const opt of realVisible) {
5615
5816
  nextSelected.add(opt.value);
5616
5817
  }
5617
5818
  }
@@ -5626,41 +5827,69 @@ function renderMultiSelectFrame(title, state, options = {}) {
5626
5827
  const c = options.colorEnabled;
5627
5828
  const bar = style.dim("\u2502", c);
5628
5829
  const maxVisible = options.maxVisible ?? 10;
5629
- const totalSkills = state.options.filter(
5830
+ const realOptions = state.options.filter((o) => !o.isSelectAll);
5831
+ const totalSkills = realOptions.filter(
5630
5832
  (o) => optionMatchesTab(o, "skill")
5631
5833
  ).length;
5632
- const totalAgents = state.options.filter(
5834
+ const totalAgents = realOptions.filter(
5633
5835
  (o) => optionMatchesTab(o, "agent")
5634
5836
  ).length;
5635
5837
  const filteredSkills = getFilteredTabOptions(state, "skill");
5636
5838
  const filteredAgents = getFilteredTabOptions(state, "agent");
5839
+ const realFilteredSkills = filteredSkills.filter((o) => !o.isSelectAll);
5840
+ const realFilteredAgents = filteredAgents.filter((o) => !o.isSelectAll);
5637
5841
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5842
+ const realVisible = state.activeTab === "skill" ? realFilteredSkills : realFilteredAgents;
5638
5843
  const total = visible.length;
5639
5844
  const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5640
5845
  const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5641
5846
  const submitVerb = options.submitActionLabel ?? "import";
5642
5847
  const isSinglePage = totalAgents === 0 || totalSkills === 0;
5643
- const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5644
- const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5645
- const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
5848
+ const header = renderPill(`\u25C6 ${title}`, {
5849
+ fg: CHARM_PALETTE.white,
5850
+ bg: CHARM_PALETTE.indigo,
5851
+ bold: true,
5852
+ colorEnabled: c
5853
+ });
5854
+ const sep = style.dim(" \xB7 ", c);
5855
+ const keyItems = [
5856
+ renderKeycap("\u2191/\u2193", "move", c),
5857
+ ...isSinglePage ? [] : [renderKeycap("\u2190/\u2192", "tab", c)],
5858
+ renderKeycap("Space", "select", c),
5859
+ ...state.searchMode ? [renderKeycap("Esc", "clear", c)] : [renderKeycap("/", "search", c), renderKeycap("a", "all", c)],
5860
+ renderKeycap("Enter", submitVerb, c)
5861
+ ];
5862
+ const instructions = `${bar} ${keyItems.join(sep)}`;
5646
5863
  const lines = [header];
5647
5864
  if (options.subtitle) {
5648
- lines.push(`${bar} ${options.subtitle}`);
5865
+ lines.push(`${bar} ${fgHex(CHARM_PALETTE.lavender, options.subtitle, c)}`);
5649
5866
  }
5650
5867
  lines.push(instructions, bar);
5651
5868
  const hasQuery = state.searchQuery.length > 0;
5652
- const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5653
- const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5654
- const skillTabLabel = state.activeTab === "skill" ? style.bold(
5655
- style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5656
- c
5657
- ) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5658
- const agentTabLabel = state.activeTab === "agent" ? style.bold(
5659
- style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c),
5660
- c
5661
- ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5869
+ const skillCountStr = hasQuery ? `${realFilteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5870
+ const agentCountStr = hasQuery ? `${realFilteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5871
+ const skillTabLabel = state.activeTab === "skill" ? renderPill(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, {
5872
+ fg: CHARM_PALETTE.white,
5873
+ bg: CHARM_PALETTE.indigo,
5874
+ bold: true,
5875
+ colorEnabled: c
5876
+ }) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5877
+ const agentTabLabel = state.activeTab === "agent" ? renderPill(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, {
5878
+ fg: CHARM_PALETTE.white,
5879
+ bg: CHARM_PALETTE.hotPink,
5880
+ bold: true,
5881
+ colorEnabled: c
5882
+ }) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5662
5883
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5663
- const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5884
+ const searchSuffix = state.searchMode || hasQuery ? ` ${renderPill(
5885
+ `/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`,
5886
+ {
5887
+ fg: CHARM_PALETTE.amber,
5888
+ bg: CHARM_PALETTE.surface,
5889
+ bold: true,
5890
+ colorEnabled: c
5891
+ }
5892
+ )}` : ` ${style.dim("(/ search)", c)}`;
5664
5893
  const singleTabLabel = totalSkills > 0 ? skillTabLabel : agentTabLabel;
5665
5894
  lines.push(
5666
5895
  isSinglePage ? `${style.dim("\u251C\u2500", c)} ${singleTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
@@ -5678,8 +5907,16 @@ function renderMultiSelectFrame(title, state, options = {}) {
5678
5907
  startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
5679
5908
  }
5680
5909
  const endIdx = Math.min(total, startIdx + maxVisible);
5910
+ const totalPages = Math.max(1, Math.ceil(total / maxVisible));
5911
+ const currentPage = Math.min(
5912
+ totalPages - 1,
5913
+ Math.floor(state.cursor / maxVisible)
5914
+ );
5915
+ const pageDots = totalPages > 1 ? ` ${renderPaginationDots(currentPage, totalPages, c)}` : "";
5681
5916
  if (startIdx > 0) {
5682
- lines.push(`${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}`);
5917
+ lines.push(
5918
+ `${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}${endIdx >= total ? pageDots : ""}`
5919
+ );
5683
5920
  }
5684
5921
  const maxLabelCols = Math.min(
5685
5922
  26,
@@ -5689,30 +5926,32 @@ function renderMultiSelectFrame(title, state, options = {}) {
5689
5926
  const opt = visible[i];
5690
5927
  if (!opt) continue;
5691
5928
  const isCurrent = i === state.cursor;
5692
- const isChecked = state.selected.has(opt.value);
5693
- const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5694
- const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
5929
+ const allRealSelected = realVisible.length > 0 && realVisible.every((o) => state.selected.has(o.value));
5930
+ const someRealSelected = realVisible.some((o) => state.selected.has(o.value)) && !allRealSelected;
5931
+ const isChecked = opt.isSelectAll ? allRealSelected : state.selected.has(opt.value);
5932
+ const pointer = isCurrent ? fgHex(CHARM_PALETTE.hotPink, style.bold("\u276F", c), c) : " ";
5933
+ const checkbox = isChecked ? style.green("\u25C9", c) : opt.isSelectAll && someRealSelected ? fgHex(CHARM_PALETTE.fuchsia, "\u25D0", c) : style.dim("\u25EF", c);
5695
5934
  const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5696
5935
  const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5697
- const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
5936
+ const styledLabel = isCurrent ? fgHex(CHARM_PALETTE.fuchsia, style.bold(labelPadded, c), c) : isChecked ? style.green(labelPadded, c) : labelPadded;
5698
5937
  const prefixWidth = 7 + maxLabelCols + 1;
5699
5938
  const maxHintCols = Math.max(10, 76 - prefixWidth);
5700
5939
  const rawHint = opt.hint ?? "";
5701
5940
  const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
5702
- const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
5941
+ const hintPart = shortHint ? ` ${isCurrent ? fgHex(CHARM_PALETTE.lavender, shortHint, c) : style.dim(shortHint, c)}` : "";
5703
5942
  lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
5704
5943
  }
5705
5944
  if (endIdx < total) {
5706
5945
  lines.push(
5707
- `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
5946
+ `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}${pageDots}`
5708
5947
  );
5709
5948
  }
5710
5949
  }
5711
5950
  lines.push(bar);
5712
- const selectedSkillsCount = state.options.filter(
5951
+ const selectedSkillsCount = realOptions.filter(
5713
5952
  (o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
5714
5953
  ).length;
5715
- const selectedAgentsCount = state.options.filter(
5954
+ const selectedAgentsCount = realOptions.filter(
5716
5955
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5717
5956
  ).length;
5718
5957
  const currentOptLabel = visible[state.cursor]?.label ?? "";
@@ -5720,8 +5959,9 @@ function renderMultiSelectFrame(title, state, options = {}) {
5720
5959
  const rawTarget = options.targetHint ?? "";
5721
5960
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5722
5961
  const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
5962
+ const styledSummary = state.selected.size > 0 ? fgHex(CHARM_PALETTE.mint, style.bold(summaryText, c), c) : fgHex(CHARM_PALETTE.lavender, style.bold(summaryText, c), c);
5723
5963
  lines.push(
5724
- `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5964
+ `${style.dim("\u2570\u2500", c)} ${styledSummary}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5725
5965
  );
5726
5966
  return lines.join("\n");
5727
5967
  }
@@ -5729,7 +5969,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5729
5969
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
5730
5970
  return null;
5731
5971
  }
5732
- let state = createMultiSelectState(options);
5972
+ let state = createMultiSelectState(options, {
5973
+ showSelectAll: renderOpts.showSelectAll
5974
+ });
5733
5975
  let renderedLineCount = 0;
5734
5976
  const render = () => {
5735
5977
  const frame = renderMultiSelectFrame(title, state, renderOpts);
@@ -6500,6 +6742,7 @@ export {
6500
6742
  ALL_RUNTIME_TARGETS,
6501
6743
  BenchmarkArmSchema,
6502
6744
  BenchmarkDeltaSchema,
6745
+ CHARM_PALETTE,
6503
6746
  COMMAND_HELP_SPECS,
6504
6747
  CORE_RUNTIME_TARGETS,
6505
6748
  CatalogEvalSummarySchema,
@@ -6544,6 +6787,7 @@ export {
6544
6787
  auditCatalogTokenBudgets,
6545
6788
  auditSkillTokenBudget,
6546
6789
  auditSubagentTokenBudget,
6790
+ bgHex,
6547
6791
  classifyCollisionSeverity,
6548
6792
  cleanSyncedArtifacts,
6549
6793
  compileSkillBundle,
@@ -6565,6 +6809,7 @@ export {
6565
6809
  evaluateCatalog,
6566
6810
  evaluateSkillDirectory,
6567
6811
  extractSkillFeatures,
6812
+ fgHex,
6568
6813
  findCommandHelpSpec,
6569
6814
  fingerprintRepository,
6570
6815
  formatBadge,
@@ -6581,9 +6826,11 @@ export {
6581
6826
  formatTokenEconomyReport,
6582
6827
  getFilteredTabOptions,
6583
6828
  handleMcpJsonRpcRequest,
6829
+ hexToRgb,
6584
6830
  inspectCatalogItem,
6585
6831
  inspectHarnesses,
6586
6832
  isColorEnabled,
6833
+ joinHorizontal,
6587
6834
  levenshteinDistance,
6588
6835
  loadSkillOpsConfig,
6589
6836
  normalizePackageName,
@@ -6599,8 +6846,11 @@ export {
6599
6846
  renderCommandHelp,
6600
6847
  renderGlobalHelp,
6601
6848
  renderHeaderBanner,
6849
+ renderKeycap,
6602
6850
  renderMultiSelectFrame,
6603
6851
  renderNextSteps,
6852
+ renderPaginationDots,
6853
+ renderPill,
6604
6854
  renderProgressBar,
6605
6855
  renderSectionHeader,
6606
6856
  renderStep,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillops-cli",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "SkillOps — The Zero-API Framework & Benchmark-Verified Library for High-End AI Coding Skills & Context-Isolated Subagents with Universal Top-28 AI Agent Transpilation & Token Economy Auditing.",
5
5
  "type": "module",
6
6
  "license": "MIT",