skillops-cli 1.3.7 → 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.7";
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) {
@@ -5684,27 +5789,51 @@ function renderMultiSelectFrame(title, state, options = {}) {
5684
5789
  const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5685
5790
  const submitVerb = options.submitActionLabel ?? "import";
5686
5791
  const isSinglePage = totalAgents === 0 || totalSkills === 0;
5687
- const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5688
- const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5689
- 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)}`;
5690
5807
  const lines = [header];
5691
5808
  if (options.subtitle) {
5692
- lines.push(`${bar} ${options.subtitle}`);
5809
+ lines.push(`${bar} ${fgHex(CHARM_PALETTE.lavender, options.subtitle, c)}`);
5693
5810
  }
5694
5811
  lines.push(instructions, bar);
5695
5812
  const hasQuery = state.searchQuery.length > 0;
5696
5813
  const skillCountStr = hasQuery ? `${realFilteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5697
5814
  const agentCountStr = hasQuery ? `${realFilteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5698
- const skillTabLabel = state.activeTab === "skill" ? style.bold(
5699
- style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5700
- c
5701
- ) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5702
- const agentTabLabel = state.activeTab === "agent" ? style.bold(
5703
- style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c),
5704
- c
5705
- ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
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);
5706
5827
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5707
- 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)}`;
5708
5837
  const singleTabLabel = totalSkills > 0 ? skillTabLabel : agentTabLabel;
5709
5838
  lines.push(
5710
5839
  isSinglePage ? `${style.dim("\u251C\u2500", c)} ${singleTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
@@ -5722,8 +5851,16 @@ function renderMultiSelectFrame(title, state, options = {}) {
5722
5851
  startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
5723
5852
  }
5724
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)}` : "";
5725
5860
  if (startIdx > 0) {
5726
- 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
+ );
5727
5864
  }
5728
5865
  const maxLabelCols = Math.min(
5729
5866
  26,
@@ -5736,21 +5873,21 @@ function renderMultiSelectFrame(title, state, options = {}) {
5736
5873
  const allRealSelected = realVisible.length > 0 && realVisible.every((o) => state.selected.has(o.value));
5737
5874
  const someRealSelected = realVisible.some((o) => state.selected.has(o.value)) && !allRealSelected;
5738
5875
  const isChecked = opt.isSelectAll ? allRealSelected : state.selected.has(opt.value);
5739
- const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5740
- const checkbox = isChecked ? style.green("\u25C9", c) : opt.isSelectAll && someRealSelected ? style.cyan("\u25D0", c) : style.dim("\u25EF", c);
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);
5741
5878
  const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5742
5879
  const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5743
- 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;
5744
5881
  const prefixWidth = 7 + maxLabelCols + 1;
5745
5882
  const maxHintCols = Math.max(10, 76 - prefixWidth);
5746
5883
  const rawHint = opt.hint ?? "";
5747
5884
  const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
5748
- const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
5885
+ const hintPart = shortHint ? ` ${isCurrent ? fgHex(CHARM_PALETTE.lavender, shortHint, c) : style.dim(shortHint, c)}` : "";
5749
5886
  lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
5750
5887
  }
5751
5888
  if (endIdx < total) {
5752
5889
  lines.push(
5753
- `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
5890
+ `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}${pageDots}`
5754
5891
  );
5755
5892
  }
5756
5893
  }
@@ -5766,8 +5903,9 @@ function renderMultiSelectFrame(title, state, options = {}) {
5766
5903
  const rawTarget = options.targetHint ?? "";
5767
5904
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5768
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);
5769
5907
  lines.push(
5770
- `${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)}`
5771
5909
  );
5772
5910
  return lines.join("\n");
5773
5911
  }
@@ -7038,16 +7176,19 @@ function runCli(argv) {
7038
7176
  unit: "tokens"
7039
7177
  });
7040
7178
  const firstSkill = summary.skills[0]?.skillName ?? "security-sast-gate";
7179
+ const skillBadge = formatBadge("skill", "SKILL");
7180
+ const subagentBadge = formatBadge("subagent", "SUBAGENT");
7041
7181
  const lines = [
7042
- `=== SkillOps Catalog (v${SKILLOPS_VERSION}) ===`,
7043
- `Skills (${summary.skills.length}) \u2014 Total L1 Routing Tax: ${summary.totalL1Tokens} tokens`,
7044
- `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}`,
7045
7185
  ...summary.skills.map(
7046
- (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}`
7047
7187
  ),
7048
- `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:`,
7049
7190
  ...summary.subagents.map(
7050
- (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})`
7051
7192
  ),
7052
7193
  renderNextSteps([
7053
7194
  {
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.7";
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
  *
@@ -2361,4 +2422,4 @@ declare const DEFAULT_VALID_COMMANDS: readonly string[];
2361
2422
  */
2362
2423
  declare function suggestCommand(unknownCommand: string, validCommands?: readonly string[]): string | null;
2363
2424
 
2364
- 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, 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 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.7";
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) {
@@ -5702,27 +5845,51 @@ function renderMultiSelectFrame(title, state, options = {}) {
5702
5845
  const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5703
5846
  const submitVerb = options.submitActionLabel ?? "import";
5704
5847
  const isSinglePage = totalAgents === 0 || totalSkills === 0;
5705
- const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5706
- const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5707
- 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)}`;
5708
5863
  const lines = [header];
5709
5864
  if (options.subtitle) {
5710
- lines.push(`${bar} ${options.subtitle}`);
5865
+ lines.push(`${bar} ${fgHex(CHARM_PALETTE.lavender, options.subtitle, c)}`);
5711
5866
  }
5712
5867
  lines.push(instructions, bar);
5713
5868
  const hasQuery = state.searchQuery.length > 0;
5714
5869
  const skillCountStr = hasQuery ? `${realFilteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5715
5870
  const agentCountStr = hasQuery ? `${realFilteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5716
- const skillTabLabel = state.activeTab === "skill" ? style.bold(
5717
- style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5718
- c
5719
- ) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5720
- const agentTabLabel = state.activeTab === "agent" ? style.bold(
5721
- style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c),
5722
- c
5723
- ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
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);
5724
5883
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5725
- 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)}`;
5726
5893
  const singleTabLabel = totalSkills > 0 ? skillTabLabel : agentTabLabel;
5727
5894
  lines.push(
5728
5895
  isSinglePage ? `${style.dim("\u251C\u2500", c)} ${singleTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
@@ -5740,8 +5907,16 @@ function renderMultiSelectFrame(title, state, options = {}) {
5740
5907
  startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
5741
5908
  }
5742
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)}` : "";
5743
5916
  if (startIdx > 0) {
5744
- 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
+ );
5745
5920
  }
5746
5921
  const maxLabelCols = Math.min(
5747
5922
  26,
@@ -5754,21 +5929,21 @@ function renderMultiSelectFrame(title, state, options = {}) {
5754
5929
  const allRealSelected = realVisible.length > 0 && realVisible.every((o) => state.selected.has(o.value));
5755
5930
  const someRealSelected = realVisible.some((o) => state.selected.has(o.value)) && !allRealSelected;
5756
5931
  const isChecked = opt.isSelectAll ? allRealSelected : state.selected.has(opt.value);
5757
- const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5758
- const checkbox = isChecked ? style.green("\u25C9", c) : opt.isSelectAll && someRealSelected ? style.cyan("\u25D0", c) : style.dim("\u25EF", c);
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);
5759
5934
  const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5760
5935
  const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5761
- 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;
5762
5937
  const prefixWidth = 7 + maxLabelCols + 1;
5763
5938
  const maxHintCols = Math.max(10, 76 - prefixWidth);
5764
5939
  const rawHint = opt.hint ?? "";
5765
5940
  const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
5766
- const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
5941
+ const hintPart = shortHint ? ` ${isCurrent ? fgHex(CHARM_PALETTE.lavender, shortHint, c) : style.dim(shortHint, c)}` : "";
5767
5942
  lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
5768
5943
  }
5769
5944
  if (endIdx < total) {
5770
5945
  lines.push(
5771
- `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
5946
+ `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}${pageDots}`
5772
5947
  );
5773
5948
  }
5774
5949
  }
@@ -5784,8 +5959,9 @@ function renderMultiSelectFrame(title, state, options = {}) {
5784
5959
  const rawTarget = options.targetHint ?? "";
5785
5960
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5786
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);
5787
5963
  lines.push(
5788
- `${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)}`
5789
5965
  );
5790
5966
  return lines.join("\n");
5791
5967
  }
@@ -6566,6 +6742,7 @@ export {
6566
6742
  ALL_RUNTIME_TARGETS,
6567
6743
  BenchmarkArmSchema,
6568
6744
  BenchmarkDeltaSchema,
6745
+ CHARM_PALETTE,
6569
6746
  COMMAND_HELP_SPECS,
6570
6747
  CORE_RUNTIME_TARGETS,
6571
6748
  CatalogEvalSummarySchema,
@@ -6610,6 +6787,7 @@ export {
6610
6787
  auditCatalogTokenBudgets,
6611
6788
  auditSkillTokenBudget,
6612
6789
  auditSubagentTokenBudget,
6790
+ bgHex,
6613
6791
  classifyCollisionSeverity,
6614
6792
  cleanSyncedArtifacts,
6615
6793
  compileSkillBundle,
@@ -6631,6 +6809,7 @@ export {
6631
6809
  evaluateCatalog,
6632
6810
  evaluateSkillDirectory,
6633
6811
  extractSkillFeatures,
6812
+ fgHex,
6634
6813
  findCommandHelpSpec,
6635
6814
  fingerprintRepository,
6636
6815
  formatBadge,
@@ -6647,9 +6826,11 @@ export {
6647
6826
  formatTokenEconomyReport,
6648
6827
  getFilteredTabOptions,
6649
6828
  handleMcpJsonRpcRequest,
6829
+ hexToRgb,
6650
6830
  inspectCatalogItem,
6651
6831
  inspectHarnesses,
6652
6832
  isColorEnabled,
6833
+ joinHorizontal,
6653
6834
  levenshteinDistance,
6654
6835
  loadSkillOpsConfig,
6655
6836
  normalizePackageName,
@@ -6665,8 +6846,11 @@ export {
6665
6846
  renderCommandHelp,
6666
6847
  renderGlobalHelp,
6667
6848
  renderHeaderBanner,
6849
+ renderKeycap,
6668
6850
  renderMultiSelectFrame,
6669
6851
  renderNextSteps,
6852
+ renderPaginationDots,
6853
+ renderPill,
6670
6854
  renderProgressBar,
6671
6855
  renderSectionHeader,
6672
6856
  renderStep,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillops-cli",
3
- "version": "1.3.7",
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",