playbooks 0.1.16 → 0.1.18

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.
Files changed (2) hide show
  1. package/dist/index.js +1760 -1093
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { program } from "commander";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "playbooks",
11
- version: "0.1.16",
11
+ version: "0.1.18",
12
12
  description: "Install agent skills, MCPs and docs into your coding agents from any git repository.",
13
13
  type: "module",
14
14
  bin: {
@@ -656,6 +656,31 @@ async function pollUrlMarkdown(jobId, timeoutMs = 6e4, pollIntervalMs = 1e3) {
656
656
  }
657
657
  throw new Error("Timed out waiting for markdown");
658
658
  }
659
+ async function fetchBundle(slug) {
660
+ const url = new URL(`${API_BASE}/bundles/${encodeURIComponent(slug)}`);
661
+ const response = await fetch(url.toString(), {
662
+ headers: {
663
+ "User-Agent": USER_AGENT
664
+ }
665
+ });
666
+ let payload = null;
667
+ try {
668
+ payload = await response.json();
669
+ } catch {
670
+ payload = null;
671
+ }
672
+ if (response.status === 404) {
673
+ throw new Error(`Bundle "${slug}" not found.`);
674
+ }
675
+ if (!response.ok || !payload?.success) {
676
+ const message = payload?.error || `Failed to fetch bundle (${response.status})`;
677
+ throw new Error(message);
678
+ }
679
+ if (!payload.bundle || !payload.skills) {
680
+ throw new Error("Invalid bundle response.");
681
+ }
682
+ return { bundle: payload.bundle, skills: payload.skills };
683
+ }
659
684
  async function fetchUrlMarkdown(url) {
660
685
  const response = await requestUrlMarkdown(url);
661
686
  if (response.success && response.data) {
@@ -1365,10 +1390,230 @@ function FlashBar({ align = "left" }) {
1365
1390
  );
1366
1391
  }
1367
1392
 
1368
- // src/tui/screens/AddConfirm.tsx
1369
- import { join as join9 } from "path";
1370
- import { Box as Box10, Text as Text10 } from "ink";
1371
- import React7 from "react";
1393
+ // src/tui/screens/AddBundleSelect.tsx
1394
+ import { basename as basename4 } from "path";
1395
+ import { Box as Box6, Text as Text6 } from "ink";
1396
+ import React5 from "react";
1397
+
1398
+ // src/tui/controls/MultiSelect.tsx
1399
+ import { Box as Box3, Text as Text3, useInput } from "ink";
1400
+ import React3 from "react";
1401
+
1402
+ // src/tui/ui/hints.ts
1403
+ var TEXT_INPUT_HINT = "Ctrl+D to clear, Esc to go back";
1404
+ var MENU_HINT = "Use \u2191\u2193 to navigate, Enter to select, m for main, q/esc to quit";
1405
+ var SINGLE_SELECT_HINT = "Use \u2191\u2193 to navigate, Enter to continue, m for main, q/esc to quit";
1406
+ var MULTI_SELECT_HINT = "Space to toggle, s to select all, Enter to continue, m for main, q/esc to quit";
1407
+ var BACK_QUIT_HINT = "Press \u2190 to go back, q/esc to quit";
1408
+ var FIND_SKILLS_HINT = "Space to select one or more skills, then Enter to install.";
1409
+ var FIND_RESULTS_HINT = "Space to toggle, s to select all, i for info, Enter to install, q/esc to quit";
1410
+ var UPDATE_HINT_NEEDS_ONLY = "Showing needs update only. u to show all, s to select all, m for main, q/esc to quit";
1411
+ var UPDATE_HINT_ALL = "u to show needs update only, s to select all, m for main, q/esc to quit";
1412
+ var UPDATE_EMPTY_HINT = "Press u to show all, m for main, q/esc to quit";
1413
+ var SCAN_SKILLS_HINT = "Use \u2191\u2193 to navigate, i for info, Enter for actions, m for main, q/esc to quit";
1414
+
1415
+ // src/tui/controls/MultiSelect.tsx
1416
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1417
+ var FILTER_THRESHOLD = 10;
1418
+ function MultiSelect({
1419
+ items,
1420
+ initialSelected = [],
1421
+ onSubmit,
1422
+ limit = 10,
1423
+ hint = MULTI_SELECT_HINT,
1424
+ enableFilter,
1425
+ lockedSection,
1426
+ hintMode = "all",
1427
+ onSelectionChange
1428
+ }) {
1429
+ const [cursor, setCursor] = React3.useState(0);
1430
+ const [infoIndex, setInfoIndex] = React3.useState(null);
1431
+ const [filter, setFilter] = React3.useState("");
1432
+ const [selected, setSelected] = React3.useState(
1433
+ new Set(
1434
+ initialSelected.length > 0 ? items.map((item, index) => ({ item, index })).filter(({ item }) => initialSelected.includes(item.value)).map(({ index }) => index) : []
1435
+ )
1436
+ );
1437
+ const showFilter = enableFilter ?? items.length >= FILTER_THRESHOLD;
1438
+ const { setTextInputActive, setTextInputEscMode } = useNavigation();
1439
+ const resetFocus = React3.useCallback(() => {
1440
+ setCursor(0);
1441
+ setInfoIndex(null);
1442
+ }, []);
1443
+ React3.useEffect(() => {
1444
+ if (!showFilter) return;
1445
+ setTextInputActive(true);
1446
+ setTextInputEscMode("back");
1447
+ return () => {
1448
+ setTextInputActive(false);
1449
+ setTextInputEscMode("back");
1450
+ };
1451
+ }, [showFilter, setTextInputActive, setTextInputEscMode]);
1452
+ const filteredItems = React3.useMemo(() => {
1453
+ if (!filter) return items.map((item, index) => ({ item, originalIndex: index }));
1454
+ const lowerFilter = filter.toLowerCase();
1455
+ return items.map((item, index) => ({ item, originalIndex: index })).filter(
1456
+ ({ item }) => item.label.toLowerCase().includes(lowerFilter) || String(item.value).toLowerCase().includes(lowerFilter)
1457
+ );
1458
+ }, [items, filter]);
1459
+ const total = filteredItems.length;
1460
+ const maxItems = Math.max(5, Math.min(limit, total));
1461
+ const windowStart = Math.min(
1462
+ Math.max(0, cursor - Math.floor(maxItems / 2)),
1463
+ Math.max(0, total - maxItems)
1464
+ );
1465
+ const visible = filteredItems.slice(windowStart, windowStart + maxItems);
1466
+ const truncate = (value, max = 100) => {
1467
+ if (value.length <= max) return value;
1468
+ return `${value.slice(0, max - 3)}...`;
1469
+ };
1470
+ const getSelectedValues = React3.useCallback(
1471
+ (nextSelected) => {
1472
+ const lockedValues = lockedSection ? lockedSection.items.map((i) => i.value) : [];
1473
+ const selectedValues = Array.from(nextSelected).map((index) => items[index]?.value).filter((value) => value !== void 0);
1474
+ return [...lockedValues, ...selectedValues];
1475
+ },
1476
+ [items, lockedSection]
1477
+ );
1478
+ useInput((input, key) => {
1479
+ if (showFilter) {
1480
+ if (key.backspace || key.delete) {
1481
+ setFilter((prev) => prev.slice(0, -1));
1482
+ resetFocus();
1483
+ return;
1484
+ }
1485
+ if (input && input.length === 1 && !key.ctrl && !key.meta && !key.return && !key.tab && input !== " " && input !== "s" && input !== "S" && input !== "i" && input !== "I") {
1486
+ setFilter((prev) => prev + input);
1487
+ resetFocus();
1488
+ return;
1489
+ }
1490
+ }
1491
+ if (key.downArrow) {
1492
+ setCursor((prev) => {
1493
+ if (total === 0) return 0;
1494
+ const next = (prev + 1) % total;
1495
+ if (infoIndex !== null) {
1496
+ setInfoIndex(null);
1497
+ }
1498
+ return next;
1499
+ });
1500
+ } else if (key.upArrow) {
1501
+ setCursor((prev) => {
1502
+ if (total === 0) return 0;
1503
+ const next = (prev - 1 + total) % total;
1504
+ if (infoIndex !== null) {
1505
+ setInfoIndex(null);
1506
+ }
1507
+ return next;
1508
+ });
1509
+ } else if (input === " ") {
1510
+ if (total === 0) return;
1511
+ const currentFiltered = filteredItems[cursor];
1512
+ if (!currentFiltered) return;
1513
+ if (currentFiltered.item.disabled) return;
1514
+ const originalIndex = currentFiltered.originalIndex;
1515
+ setSelected((prev) => {
1516
+ const next = new Set(prev);
1517
+ if (next.has(originalIndex)) next.delete(originalIndex);
1518
+ else next.add(originalIndex);
1519
+ if (onSelectionChange) {
1520
+ onSelectionChange(getSelectedValues(next));
1521
+ }
1522
+ return next;
1523
+ });
1524
+ } else if (input === "s" || input === "S") {
1525
+ const selectableIndices = filteredItems.filter(({ item }) => !item.disabled).map(({ originalIndex }) => originalIndex);
1526
+ setSelected((prev) => {
1527
+ const allSelected = selectableIndices.length > 0 && selectableIndices.every((index) => prev.has(index));
1528
+ if (allSelected) {
1529
+ const next2 = new Set(prev);
1530
+ for (const index of selectableIndices) {
1531
+ next2.delete(index);
1532
+ }
1533
+ if (onSelectionChange) {
1534
+ onSelectionChange(getSelectedValues(next2));
1535
+ }
1536
+ return next2;
1537
+ }
1538
+ const next = new Set(prev);
1539
+ for (const index of selectableIndices) {
1540
+ next.add(index);
1541
+ }
1542
+ if (onSelectionChange) {
1543
+ onSelectionChange(getSelectedValues(next));
1544
+ }
1545
+ return next;
1546
+ });
1547
+ } else if (input === "i" || input === "I") {
1548
+ setInfoIndex((prev) => prev === cursor ? null : cursor);
1549
+ } else if (key.return) {
1550
+ const lockedValues = lockedSection ? lockedSection.items.map((i) => i.value) : [];
1551
+ const selectedValues = Array.from(selected).map((index) => items[index]?.value).filter((value) => value !== void 0);
1552
+ onSubmit([...lockedValues, ...selectedValues]);
1553
+ }
1554
+ });
1555
+ const filterHint = showFilter ? "Type to filter, " : "";
1556
+ const displayHint = filterHint + hint;
1557
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
1558
+ lockedSection && lockedSection.items.length > 0 && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginBottom: 1, children: [
1559
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1560
+ "\u2500\u2500 ",
1561
+ lockedSection.title,
1562
+ " \u2500\u2500"
1563
+ ] }),
1564
+ lockedSection.items.map((item) => /* @__PURE__ */ jsxs3(Text3, { color: "green", children: [
1565
+ " ",
1566
+ "\u2713 ",
1567
+ item.label
1568
+ ] }, String(item.value)))
1569
+ ] }),
1570
+ lockedSection && items.length > 0 && /* @__PURE__ */ jsx4(Box3, { marginBottom: 0, children: /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "\u2500\u2500 Other agents \u2500\u2500" }) }),
1571
+ showFilter && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, marginTop: lockedSection ? 1 : 0, children: [
1572
+ /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "Filter: " }),
1573
+ /* @__PURE__ */ jsx4(Text3, { children: filter || " " }),
1574
+ /* @__PURE__ */ jsx4(Text3, { dimColor: true, inverse: true, children: " " }),
1575
+ filter && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1576
+ " ",
1577
+ "(",
1578
+ filteredItems.length,
1579
+ "/",
1580
+ items.length,
1581
+ ")"
1582
+ ] })
1583
+ ] }),
1584
+ total === 0 ? /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "No matches found" }) : visible.map(({ item, originalIndex }, index) => {
1585
+ const visibleIndex = windowStart + index;
1586
+ const isActive = visibleIndex === cursor;
1587
+ const isSelected = selected.has(originalIndex);
1588
+ const marker = isSelected ? "\u25FC" : "\u25FB";
1589
+ const pointer = isActive ? "\u276F" : " ";
1590
+ const color = item.disabled ? "gray" : isActive ? "cyan" : void 0;
1591
+ const showHint = hintMode === "all" || hintMode === "active" && isActive;
1592
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
1593
+ /* @__PURE__ */ jsxs3(Text3, { color, children: [
1594
+ pointer,
1595
+ " ",
1596
+ marker,
1597
+ " ",
1598
+ item.label
1599
+ ] }),
1600
+ infoIndex === visibleIndex && item.info ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1601
+ " ",
1602
+ " ",
1603
+ truncate(item.info)
1604
+ ] }) : item.hint && showHint ? /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
1605
+ " ",
1606
+ " ",
1607
+ item.hint
1608
+ ] }) : null
1609
+ ] }, `${item.label}-${originalIndex}`);
1610
+ }),
1611
+ /* @__PURE__ */ jsx4(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: displayHint }) })
1612
+ ] });
1613
+ }
1614
+
1615
+ // src/tui/ui/AddFlowHeader.tsx
1616
+ import { Box as Box5, Text as Text5 } from "ink";
1372
1617
 
1373
1618
  // src/cli-utils.ts
1374
1619
  import { homedir as homedir2 } from "os";
@@ -1391,25 +1636,368 @@ function formatList(items, maxShow = 5) {
1391
1636
  return `${shown.join(", ")} +${remaining} more`;
1392
1637
  }
1393
1638
 
1394
- // src/flows/plan-summary.ts
1395
- import chalk from "chalk";
1639
+ // src/tui/ui/Header.tsx
1640
+ import { Box as Box4, Text as Text4 } from "ink";
1641
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1642
+ function Header({ title }) {
1643
+ return /* @__PURE__ */ jsx5(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: title }) });
1644
+ }
1396
1645
 
1397
- // src/installer/install.ts
1398
- import { access, mkdir as mkdir2, rm as rm4, writeFile } from "fs/promises";
1399
- import { basename as basename3, join as join7 } from "path";
1646
+ // src/tui/ui/AddFlowHeader.tsx
1647
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1648
+ function AddFlowHeader({ title }) {
1649
+ const { invocation, addSkill } = useNavigation();
1650
+ const source = addSkill.source ?? invocation.source;
1651
+ const cwd = process.cwd();
1652
+ let sourceLabel = source ?? "";
1653
+ if (addSkill.parsed?.type === "local" && addSkill.parsed.localPath) {
1654
+ sourceLabel = shortenPath(addSkill.parsed.localPath, cwd);
1655
+ }
1656
+ const selected = addSkill.selectedSkills;
1657
+ const available = addSkill.skills;
1658
+ let skillsLabel = null;
1659
+ if (selected && selected.length > 0) {
1660
+ skillsLabel = formatList(selected.map(getSkillDisplayName), 3);
1661
+ } else if (available && available.length > 0) {
1662
+ skillsLabel = `${available.length} available`;
1663
+ }
1664
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
1665
+ /* @__PURE__ */ jsx6(Header, { title }),
1666
+ source ? /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: `Source: ${sourceLabel}` }) : null,
1667
+ skillsLabel ? /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: `Skills: ${skillsLabel}` }) : null
1668
+ ] });
1669
+ }
1400
1670
 
1401
- // src/installer/files.ts
1402
- import { cp, lstat, mkdir, readdir as readdir2, readlink, rm as rm3, symlink } from "fs/promises";
1403
- import { platform } from "os";
1404
- import { join as join5, relative as relative2, resolve as resolve2 } from "path";
1405
- var EXCLUDE_FILES = /* @__PURE__ */ new Set(["README.md", "metadata.json"]);
1406
- var isExcluded = (name) => {
1407
- if (EXCLUDE_FILES.has(name)) return true;
1408
- if (name.startsWith("_")) return true;
1409
- return false;
1410
- };
1411
- async function createSymlink(target, linkPath) {
1412
- try {
1671
+ // src/tui/ui/spinner.ts
1672
+ import React4 from "react";
1673
+ var spinnerFrames = ["|", "/", "-", "\\"];
1674
+ function useSpinnerFrame(active = true, interval = 100) {
1675
+ const [index, setIndex] = React4.useState(0);
1676
+ React4.useEffect(() => {
1677
+ if (!active) return void 0;
1678
+ const timer = setInterval(() => {
1679
+ setIndex((prev) => (prev + 1) % spinnerFrames.length);
1680
+ }, interval);
1681
+ return () => clearInterval(timer);
1682
+ }, [active, interval]);
1683
+ return spinnerFrames[index] ?? "|";
1684
+ }
1685
+
1686
+ // src/tui/utils/skill-selection.ts
1687
+ import { basename as basename3 } from "path";
1688
+ function matchesSkillName(skill, input) {
1689
+ const normalized = input.toLowerCase();
1690
+ const byName = skill.name.toLowerCase() === normalized;
1691
+ const byPath = basename3(skill.path).toLowerCase() === normalized;
1692
+ return byName || byPath;
1693
+ }
1694
+ function autoSelect(skills, options) {
1695
+ if (options.skill && options.skill.length > 0) {
1696
+ const selected = skills.filter((s) => options.skill?.some((name) => matchesSkillName(s, name)));
1697
+ if (selected.length === 0) {
1698
+ return {
1699
+ status: "prompt",
1700
+ message: `No matching skills found for: ${options.skill.join(", ")}`
1701
+ };
1702
+ }
1703
+ return { status: "selected", skills: selected };
1704
+ }
1705
+ if (skills.length === 1) {
1706
+ return { status: "selected", skills };
1707
+ }
1708
+ if (options.yes) {
1709
+ return { status: "selected", skills };
1710
+ }
1711
+ return { status: "prompt" };
1712
+ }
1713
+
1714
+ // src/tui/screens/AddBundleSelect.tsx
1715
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1716
+ function groupByRepo(skills) {
1717
+ const map = /* @__PURE__ */ new Map();
1718
+ for (const skill of skills) {
1719
+ if (!skill.repoOwner || !skill.repoName) continue;
1720
+ const key = `${skill.repoOwner}/${skill.repoName}`;
1721
+ const list = map.get(key) ?? [];
1722
+ list.push(skill);
1723
+ map.set(key, list);
1724
+ }
1725
+ return map;
1726
+ }
1727
+ function findMatchingSkill(discovered, entry) {
1728
+ const targets = [entry.name, entry.skillSlug].filter((t) => Boolean(t)).map((t) => t.toLowerCase());
1729
+ for (const skill of discovered) {
1730
+ const candidates = [skill.name.toLowerCase(), basename4(skill.path).toLowerCase()];
1731
+ if (candidates.some((c) => targets.includes(c))) {
1732
+ return skill;
1733
+ }
1734
+ }
1735
+ return void 0;
1736
+ }
1737
+ function AddBundleSelectScreen() {
1738
+ const {
1739
+ invocation,
1740
+ addSkill,
1741
+ updateAddSkill,
1742
+ navigateTo,
1743
+ setFlash,
1744
+ setBackHandler,
1745
+ resetTo,
1746
+ setInvocation,
1747
+ resetAddSkill,
1748
+ setLastSource
1749
+ } = useNavigation();
1750
+ const [status, setStatus] = React5.useState(
1751
+ addSkill.skills && addSkill.skills.length > 0 ? "ready" : "loading"
1752
+ );
1753
+ const [error, setError] = React5.useState(null);
1754
+ const [bundleName, setBundleName] = React5.useState(null);
1755
+ const [listMode, setListMode] = React5.useState(false);
1756
+ const [showLoading, setShowLoading] = React5.useState(false);
1757
+ const spinner = useSpinnerFrame(status === "loading");
1758
+ const slug = invocation.source;
1759
+ const options = invocation.options;
1760
+ React5.useEffect(() => {
1761
+ let cancelled = false;
1762
+ const load = async () => {
1763
+ if (!slug) {
1764
+ setError("Missing bundle slug.");
1765
+ setStatus("error");
1766
+ return;
1767
+ }
1768
+ if (addSkill.skills && addSkill.skills.length > 0) {
1769
+ setStatus("ready");
1770
+ return;
1771
+ }
1772
+ setStatus("loading");
1773
+ const tempDirs2 = [];
1774
+ try {
1775
+ const bundle = await fetchBundle(slug);
1776
+ if (cancelled) return;
1777
+ setBundleName(bundle.bundle.name);
1778
+ const cloneable = bundle.skills.filter((s) => s.repoOwner && s.repoName);
1779
+ const skippedCount = bundle.skills.length - cloneable.length;
1780
+ if (cloneable.length === 0) {
1781
+ throw new Error("No installable skills found in this bundle.");
1782
+ }
1783
+ const grouped = groupByRepo(cloneable);
1784
+ const allSkills = [];
1785
+ const warnings = [];
1786
+ for (const [repo, entries] of grouped) {
1787
+ if (cancelled) break;
1788
+ const repoUrl = `https://github.com/${repo}.git`;
1789
+ let tempDir;
1790
+ try {
1791
+ tempDir = await cloneRepo(repoUrl);
1792
+ } catch {
1793
+ warnings.push(`Failed to clone ${repo}`);
1794
+ continue;
1795
+ }
1796
+ tempDirs2.push(tempDir);
1797
+ let discovered;
1798
+ try {
1799
+ discovered = await discoverSkills(tempDir);
1800
+ } catch {
1801
+ warnings.push(`Failed to discover skills in ${repo}`);
1802
+ continue;
1803
+ }
1804
+ for (const entry of entries) {
1805
+ if (cancelled) break;
1806
+ const match = findMatchingSkill(discovered, entry);
1807
+ if (match) {
1808
+ allSkills.push(match);
1809
+ } else {
1810
+ warnings.push(`Could not find skill "${entry.name}" in ${repo}`);
1811
+ }
1812
+ }
1813
+ }
1814
+ if (cancelled) {
1815
+ for (const dir of tempDirs2) {
1816
+ try {
1817
+ await cleanupTempDir(dir);
1818
+ } catch {
1819
+ }
1820
+ }
1821
+ return;
1822
+ }
1823
+ if (allSkills.length === 0) {
1824
+ for (const dir of tempDirs2) {
1825
+ try {
1826
+ await cleanupTempDir(dir);
1827
+ } catch {
1828
+ }
1829
+ }
1830
+ const detail = warnings.length > 0 ? `
1831
+ ${warnings.join("\n")}` : "";
1832
+ throw new Error(`No skills could be resolved from this bundle.${detail}`);
1833
+ }
1834
+ if (skippedCount > 0) {
1835
+ warnings.push(`${skippedCount} skill(s) skipped (missing repo info)`);
1836
+ }
1837
+ if (warnings.length > 0) {
1838
+ setFlash(warnings.join(". "));
1839
+ }
1840
+ updateAddSkill({
1841
+ tempDir: tempDirs2[0] ?? null,
1842
+ skills: allSkills
1843
+ });
1844
+ if (options.list) {
1845
+ setListMode(true);
1846
+ setStatus("list");
1847
+ return;
1848
+ }
1849
+ const autoSelection = autoSelect(allSkills, options);
1850
+ if (autoSelection.status === "selected") {
1851
+ updateAddSkill({
1852
+ selectedSkills: autoSelection.skills,
1853
+ securityBySkillName: void 0,
1854
+ securityScanRows: void 0,
1855
+ securityAccepted: void 0
1856
+ });
1857
+ navigateTo("add-security-scan");
1858
+ return;
1859
+ }
1860
+ if (autoSelection.status === "error") {
1861
+ throw new Error(autoSelection.message);
1862
+ }
1863
+ if (autoSelection.status === "prompt" && autoSelection.message) {
1864
+ setFlash(autoSelection.message);
1865
+ }
1866
+ setStatus("ready");
1867
+ } catch (err) {
1868
+ if (cancelled) return;
1869
+ for (const dir of tempDirs2) {
1870
+ try {
1871
+ await cleanupTempDir(dir);
1872
+ } catch {
1873
+ }
1874
+ }
1875
+ updateAddSkill({ tempDir: null, skills: void 0, selectedSkills: void 0 });
1876
+ setError(err instanceof Error ? err.message : "Failed to load bundle");
1877
+ setStatus("error");
1878
+ }
1879
+ };
1880
+ load();
1881
+ return () => {
1882
+ cancelled = true;
1883
+ };
1884
+ }, [slug, addSkill.skills, updateAddSkill, navigateTo, options, setFlash]);
1885
+ React5.useEffect(() => {
1886
+ setBackHandler(() => {
1887
+ setLastSource(null);
1888
+ resetAddSkill();
1889
+ setInvocation({ intent: "none", options: {} });
1890
+ resetTo("main");
1891
+ return true;
1892
+ });
1893
+ return () => {
1894
+ setBackHandler(null);
1895
+ };
1896
+ }, [resetTo, setBackHandler, resetAddSkill, setInvocation, setLastSource]);
1897
+ React5.useEffect(() => {
1898
+ if (status !== "loading") {
1899
+ setShowLoading(false);
1900
+ return;
1901
+ }
1902
+ const timer = setTimeout(() => {
1903
+ setShowLoading(true);
1904
+ }, 150);
1905
+ return () => clearTimeout(timer);
1906
+ }, [status]);
1907
+ if (status === "loading" && !showLoading) {
1908
+ return /* @__PURE__ */ jsx7(Box6, { padding: 1 });
1909
+ }
1910
+ if (status === "loading") {
1911
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
1912
+ /* @__PURE__ */ jsx7(AddFlowHeader, { title: bundleName ? `Bundle: ${bundleName}` : "Loading bundle" }),
1913
+ /* @__PURE__ */ jsxs6(Text6, { children: [
1914
+ spinner,
1915
+ " ",
1916
+ bundleName ? "Cloning skills..." : `Fetching bundle "${slug}"...`
1917
+ ] })
1918
+ ] });
1919
+ }
1920
+ if (status === "error") {
1921
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
1922
+ /* @__PURE__ */ jsx7(AddFlowHeader, { title: "Unable to load bundle" }),
1923
+ /* @__PURE__ */ jsx7(Text6, { color: "red", children: error }),
1924
+ /* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: BACK_QUIT_HINT }) })
1925
+ ] });
1926
+ }
1927
+ const skills = addSkill.skills ?? [];
1928
+ if (listMode || status === "list") {
1929
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
1930
+ /* @__PURE__ */ jsx7(AddFlowHeader, { title: `${bundleName ?? "Bundle"} (${skills.length} skills)` }),
1931
+ skills.map((skill) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
1932
+ /* @__PURE__ */ jsx7(Text6, { children: getSkillDisplayName(skill) }),
1933
+ skill.description ? /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: skill.description }) : null
1934
+ ] }, skill.name)),
1935
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: BACK_QUIT_HINT })
1936
+ ] });
1937
+ }
1938
+ if (skills.length === 0) {
1939
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
1940
+ /* @__PURE__ */ jsx7(AddFlowHeader, { title: "No skills found" }),
1941
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: BACK_QUIT_HINT })
1942
+ ] });
1943
+ }
1944
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
1945
+ /* @__PURE__ */ jsx7(AddFlowHeader, { title: `${bundleName ?? "Bundle"} - Select skills` }),
1946
+ /* @__PURE__ */ jsx7(
1947
+ MultiSelect,
1948
+ {
1949
+ items: skills.map((skill) => ({
1950
+ value: skill,
1951
+ label: getSkillDisplayName(skill),
1952
+ hint: skill.description && skill.description.length > 60 ? `${skill.description.slice(0, 57)}...` : skill.description
1953
+ })),
1954
+ initialSelected: addSkill.selectedSkills ?? skills,
1955
+ onSubmit: (values) => {
1956
+ if (values.length === 0) {
1957
+ setFlash("Select at least one skill.");
1958
+ return;
1959
+ }
1960
+ updateAddSkill({
1961
+ selectedSkills: values,
1962
+ targetAgents: void 0,
1963
+ installGlobally: void 0,
1964
+ installMode: void 0,
1965
+ planLines: void 0,
1966
+ securityBySkillName: void 0,
1967
+ securityScanRows: void 0,
1968
+ securityAccepted: void 0
1969
+ });
1970
+ navigateTo("add-security-scan");
1971
+ }
1972
+ }
1973
+ )
1974
+ ] });
1975
+ }
1976
+
1977
+ // src/tui/screens/AddConfirm.tsx
1978
+ import { join as join9 } from "path";
1979
+ import { Box as Box12, Text as Text12 } from "ink";
1980
+ import React9 from "react";
1981
+
1982
+ // src/flows/plan-summary.ts
1983
+ import chalk from "chalk";
1984
+
1985
+ // src/installer/install.ts
1986
+ import { access, mkdir as mkdir2, rm as rm4, writeFile } from "fs/promises";
1987
+ import { basename as basename5, join as join7 } from "path";
1988
+
1989
+ // src/installer/files.ts
1990
+ import { cp, lstat, mkdir, readdir as readdir2, readlink, rm as rm3, symlink } from "fs/promises";
1991
+ import { platform } from "os";
1992
+ import { join as join5, relative as relative2, resolve as resolve2 } from "path";
1993
+ var EXCLUDE_FILES = /* @__PURE__ */ new Set(["README.md", "metadata.json"]);
1994
+ var isExcluded = (name) => {
1995
+ if (EXCLUDE_FILES.has(name)) return true;
1996
+ if (name.startsWith("_")) return true;
1997
+ return false;
1998
+ };
1999
+ async function createSymlink(target, linkPath) {
2000
+ try {
1413
2001
  const resolvedTarget = resolve2(target);
1414
2002
  const resolvedLinkPath = resolve2(linkPath);
1415
2003
  if (resolvedTarget === resolvedLinkPath) {
@@ -1542,7 +2130,7 @@ async function installSkillForAgent(skill, agentType, options = {}) {
1542
2130
  error: `Agent ${agent.displayName} does not support global installation`
1543
2131
  };
1544
2132
  }
1545
- const rawSkillName = skill.name || basename3(skill.path);
2133
+ const rawSkillName = skill.name || basename5(skill.path);
1546
2134
  const { canonicalBase, canonicalDir, agentBase, agentDir } = getInstallTargets(
1547
2135
  rawSkillName,
1548
2136
  agentType,
@@ -2721,41 +3309,26 @@ async function scanSkillsForSecurity(skills, options = {}) {
2721
3309
  }
2722
3310
 
2723
3311
  // src/tui/controls/SelectMenu.tsx
2724
- import { Box as Box5 } from "ink";
3312
+ import { Box as Box9 } from "ink";
2725
3313
  import SelectInput from "ink-select-input";
2726
3314
 
2727
3315
  // src/tui/ui/Divider.tsx
2728
- import { Box as Box3, Text as Text3 } from "ink";
2729
- import { jsx as jsx4 } from "react/jsx-runtime";
3316
+ import { Box as Box7, Text as Text7 } from "ink";
3317
+ import { jsx as jsx8 } from "react/jsx-runtime";
2730
3318
  function Divider() {
2731
- return /* @__PURE__ */ jsx4(Box3, { children: /* @__PURE__ */ jsx4(Text3, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) });
3319
+ return /* @__PURE__ */ jsx8(Box7, { children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) });
2732
3320
  }
2733
3321
 
2734
3322
  // src/tui/ui/HelpBar.tsx
2735
- import { Box as Box4, Text as Text4 } from "ink";
2736
-
2737
- // src/tui/ui/hints.ts
2738
- var TEXT_INPUT_HINT = "Ctrl+D to clear, Esc to go back";
2739
- var MENU_HINT = "Use \u2191\u2193 to navigate, Enter to select, m for main, q/esc to quit";
2740
- var SINGLE_SELECT_HINT = "Use \u2191\u2193 to navigate, Enter to continue, m for main, q/esc to quit";
2741
- var MULTI_SELECT_HINT = "Space to toggle, s to select all, Enter to continue, m for main, q/esc to quit";
2742
- var BACK_QUIT_HINT = "Press \u2190 to go back, q/esc to quit";
2743
- var FIND_SKILLS_HINT = "Space to select one or more skills, then Enter to install.";
2744
- var FIND_RESULTS_HINT = "Space to toggle, s to select all, i for info, Enter to install, q/esc to quit";
2745
- var UPDATE_HINT_NEEDS_ONLY = "Showing needs update only. u to show all, s to select all, m for main, q/esc to quit";
2746
- var UPDATE_HINT_ALL = "u to show needs update only, s to select all, m for main, q/esc to quit";
2747
- var UPDATE_EMPTY_HINT = "Press u to show all, m for main, q/esc to quit";
2748
- var SCAN_SKILLS_HINT = "Use \u2191\u2193 to navigate, i for info, Enter for actions, m for main, q/esc to quit";
2749
-
2750
- // src/tui/ui/HelpBar.tsx
2751
- import { jsx as jsx5 } from "react/jsx-runtime";
3323
+ import { Box as Box8, Text as Text8 } from "ink";
3324
+ import { jsx as jsx9 } from "react/jsx-runtime";
2752
3325
  function HelpBar({ text = MENU_HINT }) {
2753
- return /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text4, { dimColor: true, children: text }) });
3326
+ return /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: text }) });
2754
3327
  }
2755
3328
 
2756
3329
  // src/tui/ui/SelectItem.tsx
2757
- import { Text as Text5 } from "ink";
2758
- import { jsx as jsx6 } from "react/jsx-runtime";
3330
+ import { Text as Text9 } from "ink";
3331
+ import { jsx as jsx10 } from "react/jsx-runtime";
2759
3332
  function SelectItem({ label }) {
2760
3333
  const l = label.toLowerCase();
2761
3334
  let color;
@@ -2764,11 +3337,11 @@ function SelectItem({ label }) {
2764
3337
  else if (l.includes("failed \u2717") || l.includes("inactive \u2717")) color = "red";
2765
3338
  else if (l.includes("sent \u2713") || l.includes("success \u2713") || l.includes("active \u2713"))
2766
3339
  color = "green";
2767
- return /* @__PURE__ */ jsx6(Text5, { color, children: label });
3340
+ return /* @__PURE__ */ jsx10(Text9, { color, children: label });
2768
3341
  }
2769
3342
 
2770
3343
  // src/tui/controls/SelectMenu.tsx
2771
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
3344
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2772
3345
  var SelectInputTyped = SelectInput;
2773
3346
  function SelectMenu({
2774
3347
  items,
@@ -2778,9 +3351,9 @@ function SelectMenu({
2778
3351
  itemComponent = SelectItem,
2779
3352
  limit
2780
3353
  }) {
2781
- return /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", children: [
2782
- showDivider && /* @__PURE__ */ jsx7(Divider, {}),
2783
- /* @__PURE__ */ jsx7(
3354
+ return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
3355
+ showDivider && /* @__PURE__ */ jsx11(Divider, {}),
3356
+ /* @__PURE__ */ jsx11(
2784
3357
  SelectInputTyped,
2785
3358
  {
2786
3359
  items,
@@ -2789,17 +3362,17 @@ function SelectMenu({
2789
3362
  limit
2790
3363
  }
2791
3364
  ),
2792
- /* @__PURE__ */ jsx7(HelpBar, { text: hint })
3365
+ /* @__PURE__ */ jsx11(HelpBar, { text: hint })
2793
3366
  ] });
2794
3367
  }
2795
3368
 
2796
3369
  // src/tui/hooks/useTextInput.tsx
2797
- import { useInput } from "ink";
2798
- import React3 from "react";
3370
+ import { useInput as useInput2 } from "ink";
3371
+ import React6 from "react";
2799
3372
  function useTextInput({ onClear, disabled = false }) {
2800
3373
  const { setTextInputActive, setTextInputEscMode } = useNavigation();
2801
- const skipNextChangeRef = React3.useRef(false);
2802
- React3.useEffect(() => {
3374
+ const skipNextChangeRef = React6.useRef(false);
3375
+ React6.useEffect(() => {
2803
3376
  if (disabled) {
2804
3377
  setTextInputActive(false);
2805
3378
  setTextInputEscMode("back");
@@ -2812,17 +3385,17 @@ function useTextInput({ onClear, disabled = false }) {
2812
3385
  setTextInputEscMode("back");
2813
3386
  };
2814
3387
  }, [disabled, setTextInputActive, setTextInputEscMode]);
2815
- const clearValue = React3.useCallback(() => {
3388
+ const clearValue = React6.useCallback(() => {
2816
3389
  skipNextChangeRef.current = true;
2817
3390
  onClear();
2818
3391
  }, [onClear]);
2819
- useInput((input, key) => {
3392
+ useInput2((input, key) => {
2820
3393
  if (disabled) return;
2821
3394
  if (key.ctrl && input === "d" || input === "") {
2822
3395
  clearValue();
2823
3396
  }
2824
3397
  });
2825
- const wrapOnChange = React3.useCallback(
3398
+ const wrapOnChange = React6.useCallback(
2826
3399
  (handler) => (next) => {
2827
3400
  if (skipNextChangeRef.current) {
2828
3401
  skipNextChangeRef.current = false;
@@ -2836,66 +3409,16 @@ function useTextInput({ onClear, disabled = false }) {
2836
3409
  return { wrapOnChange };
2837
3410
  }
2838
3411
 
2839
- // src/tui/ui/AddFlowHeader.tsx
2840
- import { Box as Box7, Text as Text7 } from "ink";
2841
-
2842
- // src/tui/ui/Header.tsx
2843
- import { Box as Box6, Text as Text6 } from "ink";
2844
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
2845
- function Header({ title }) {
2846
- return /* @__PURE__ */ jsx8(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text6, { bold: true, color: "cyan", children: title }) });
2847
- }
2848
-
2849
- // src/tui/ui/AddFlowHeader.tsx
2850
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
2851
- function AddFlowHeader({ title }) {
2852
- const { invocation, addSkill } = useNavigation();
2853
- const source = addSkill.source ?? invocation.source;
2854
- const cwd = process.cwd();
2855
- let sourceLabel = source ?? "";
2856
- if (addSkill.parsed?.type === "local" && addSkill.parsed.localPath) {
2857
- sourceLabel = shortenPath(addSkill.parsed.localPath, cwd);
2858
- }
2859
- const selected = addSkill.selectedSkills;
2860
- const available = addSkill.skills;
2861
- let skillsLabel = null;
2862
- if (selected && selected.length > 0) {
2863
- skillsLabel = formatList(selected.map(getSkillDisplayName), 3);
2864
- } else if (available && available.length > 0) {
2865
- skillsLabel = `${available.length} available`;
2866
- }
2867
- return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", marginBottom: 1, children: [
2868
- /* @__PURE__ */ jsx9(Header, { title }),
2869
- source ? /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: `Source: ${sourceLabel}` }) : null,
2870
- skillsLabel ? /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: `Skills: ${skillsLabel}` }) : null
2871
- ] });
2872
- }
2873
-
2874
- // src/tui/ui/spinner.ts
2875
- import React4 from "react";
2876
- var spinnerFrames = ["|", "/", "-", "\\"];
2877
- function useSpinnerFrame(active = true, interval = 100) {
2878
- const [index, setIndex] = React4.useState(0);
2879
- React4.useEffect(() => {
2880
- if (!active) return void 0;
2881
- const timer = setInterval(() => {
2882
- setIndex((prev) => (prev + 1) % spinnerFrames.length);
2883
- }, interval);
2884
- return () => clearInterval(timer);
2885
- }, [active, interval]);
2886
- return spinnerFrames[index] ?? "|";
2887
- }
2888
-
2889
3412
  // src/tui/screens/add-confirm-security.tsx
2890
- import { Box as Box9, Text as Text9 } from "ink";
3413
+ import { Box as Box11, Text as Text11 } from "ink";
2891
3414
  import TextInput from "ink-text-input";
2892
- import React6 from "react";
3415
+ import React8 from "react";
2893
3416
 
2894
3417
  // src/tui/controls/SingleSelect.tsx
2895
- import { Box as Box8, Text as Text8, useInput as useInput2 } from "ink";
2896
- import React5 from "react";
2897
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
2898
- var FILTER_THRESHOLD = 10;
3418
+ import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
3419
+ import React7 from "react";
3420
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
3421
+ var FILTER_THRESHOLD2 = 10;
2899
3422
  function SingleSelect({
2900
3423
  items,
2901
3424
  onSubmit,
@@ -2905,20 +3428,20 @@ function SingleSelect({
2905
3428
  hintMode = "active",
2906
3429
  initialValue
2907
3430
  }) {
2908
- const [cursor, setCursor] = React5.useState(() => {
3431
+ const [cursor, setCursor] = React7.useState(() => {
2909
3432
  if (initialValue === void 0) return 0;
2910
3433
  const index = items.findIndex((item) => item.value === initialValue);
2911
3434
  return index >= 0 ? index : 0;
2912
3435
  });
2913
- const [infoIndex, setInfoIndex] = React5.useState(null);
2914
- const [filter, setFilter] = React5.useState("");
2915
- const showFilter = enableFilter ?? items.length >= FILTER_THRESHOLD;
3436
+ const [infoIndex, setInfoIndex] = React7.useState(null);
3437
+ const [filter, setFilter] = React7.useState("");
3438
+ const showFilter = enableFilter ?? items.length >= FILTER_THRESHOLD2;
2916
3439
  const { setTextInputActive, setTextInputEscMode } = useNavigation();
2917
- const resetFocus = React5.useCallback(() => {
3440
+ const resetFocus = React7.useCallback(() => {
2918
3441
  setCursor(0);
2919
3442
  setInfoIndex(null);
2920
3443
  }, []);
2921
- React5.useEffect(() => {
3444
+ React7.useEffect(() => {
2922
3445
  if (!showFilter) return;
2923
3446
  setTextInputActive(true);
2924
3447
  setTextInputEscMode("back");
@@ -2927,7 +3450,7 @@ function SingleSelect({
2927
3450
  setTextInputEscMode("back");
2928
3451
  };
2929
3452
  }, [showFilter, setTextInputActive, setTextInputEscMode]);
2930
- const filteredItems = React5.useMemo(() => {
3453
+ const filteredItems = React7.useMemo(() => {
2931
3454
  if (!filter) return items.map((item, index) => ({ item, originalIndex: index }));
2932
3455
  const lowerFilter = filter.toLowerCase();
2933
3456
  return items.map((item, index) => ({ item, originalIndex: index })).filter(
@@ -2945,7 +3468,7 @@ function SingleSelect({
2945
3468
  if (value.length <= max) return value;
2946
3469
  return `${value.slice(0, max - 3)}...`;
2947
3470
  };
2948
- React5.useEffect(() => {
3471
+ React7.useEffect(() => {
2949
3472
  if (total === 0) {
2950
3473
  setCursor(0);
2951
3474
  return;
@@ -2954,7 +3477,7 @@ function SingleSelect({
2954
3477
  setCursor(total - 1);
2955
3478
  }
2956
3479
  }, [total, cursor]);
2957
- useInput2((input, key) => {
3480
+ useInput3((input, key) => {
2958
3481
  if (showFilter) {
2959
3482
  if (key.backspace || key.delete) {
2960
3483
  setFilter((prev) => prev.slice(0, -1));
@@ -2996,12 +3519,12 @@ function SingleSelect({
2996
3519
  });
2997
3520
  const filterHint = showFilter ? "Type to filter, " : "";
2998
3521
  const displayHint = filterHint + hint;
2999
- return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", children: [
3000
- showFilter && /* @__PURE__ */ jsxs6(Box8, { marginBottom: 1, children: [
3001
- /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "Filter: " }),
3002
- /* @__PURE__ */ jsx10(Text8, { children: filter || " " }),
3003
- /* @__PURE__ */ jsx10(Text8, { dimColor: true, inverse: true, children: " " }),
3004
- filter && /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
3522
+ return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", children: [
3523
+ showFilter && /* @__PURE__ */ jsxs8(Box10, { marginBottom: 1, children: [
3524
+ /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "Filter: " }),
3525
+ /* @__PURE__ */ jsx12(Text10, { children: filter || " " }),
3526
+ /* @__PURE__ */ jsx12(Text10, { dimColor: true, inverse: true, children: " " }),
3527
+ filter && /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
3005
3528
  " ",
3006
3529
  "(",
3007
3530
  filteredItems.length,
@@ -3010,35 +3533,35 @@ function SingleSelect({
3010
3533
  ")"
3011
3534
  ] })
3012
3535
  ] }),
3013
- total === 0 ? /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "No matches found" }) : visible.map(({ item, originalIndex }, index) => {
3536
+ total === 0 ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "No matches found" }) : visible.map(({ item, originalIndex }, index) => {
3014
3537
  const visibleIndex = windowStart + index;
3015
3538
  const isActive = visibleIndex === cursor;
3016
3539
  const pointer = isActive ? "\u276F" : " ";
3017
3540
  const color = item.disabled ? "gray" : isActive ? "cyan" : void 0;
3018
3541
  const showHint = hintMode === "all" || hintMode === "active" && isActive;
3019
- return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", children: [
3020
- /* @__PURE__ */ jsxs6(Text8, { color, children: [
3542
+ return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", children: [
3543
+ /* @__PURE__ */ jsxs8(Text10, { color, children: [
3021
3544
  pointer,
3022
3545
  " ",
3023
3546
  item.label
3024
3547
  ] }),
3025
- infoIndex === visibleIndex && item.info ? /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
3548
+ infoIndex === visibleIndex && item.info ? /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
3026
3549
  " ",
3027
3550
  " ",
3028
3551
  truncate(item.info)
3029
- ] }) : item.hint && showHint ? /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
3552
+ ] }) : item.hint && showHint ? /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
3030
3553
  " ",
3031
3554
  " ",
3032
3555
  item.hint
3033
3556
  ] }) : null
3034
3557
  ] }, `${item.label}-${originalIndex}`);
3035
3558
  }),
3036
- /* @__PURE__ */ jsx10(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: displayHint }) })
3559
+ /* @__PURE__ */ jsx12(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: displayHint }) })
3037
3560
  ] });
3038
3561
  }
3039
3562
 
3040
3563
  // src/tui/screens/add-confirm-security.tsx
3041
- import { Fragment, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
3564
+ import { Fragment, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3042
3565
  function formatSignalsBrief(signals) {
3043
3566
  if (signals.length === 0) return "None";
3044
3567
  const uniq = Array.from(new Set(signals.map((s) => s.id)));
@@ -3068,11 +3591,11 @@ function ManualSecurityGate({
3068
3591
  onInstall,
3069
3592
  onCancel
3070
3593
  }) {
3071
- const riskyScanRows = React6.useMemo(() => getRiskyScanRows(scanRows), [scanRows]);
3594
+ const riskyScanRows = React8.useMemo(() => getRiskyScanRows(scanRows), [scanRows]);
3072
3595
  if (manualView === "details") {
3073
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
3074
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "Skills with scan findings:" }),
3075
- /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: riskyScanRows.length === 0 ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "None." }) : /* @__PURE__ */ jsx11(
3596
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
3597
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Skills with scan findings:" }),
3598
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: riskyScanRows.length === 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "None." }) : /* @__PURE__ */ jsx13(
3076
3599
  SingleSelect,
3077
3600
  {
3078
3601
  items: riskyScanRows.map((row) => ({
@@ -3093,49 +3616,49 @@ function ManualSecurityGate({
3093
3616
  }
3094
3617
  if (manualView === "detail-skill" && selectedRow) {
3095
3618
  const riskColor = selectedRow.level === "critical" || selectedRow.level === "high" ? "red" : selectedRow.level === "medium" ? "yellow" : selectedRow.level === "low" ? "green" : "gray";
3096
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
3097
- /* @__PURE__ */ jsx11(Text9, { children: /* @__PURE__ */ jsx11(Text9, { bold: true, children: selectedRow.name }) }),
3098
- selectedRow.error ? /* @__PURE__ */ jsx11(Text9, { color: "red", children: selectedRow.error }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
3099
- /* @__PURE__ */ jsxs7(Text9, { children: [
3100
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "Verdict:" }),
3619
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
3620
+ /* @__PURE__ */ jsx13(Text11, { children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: selectedRow.name }) }),
3621
+ selectedRow.error ? /* @__PURE__ */ jsx13(Text11, { color: "red", children: selectedRow.error }) : /* @__PURE__ */ jsxs9(Fragment, { children: [
3622
+ /* @__PURE__ */ jsxs9(Text11, { children: [
3623
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Verdict:" }),
3101
3624
  " ",
3102
- /* @__PURE__ */ jsx11(Text9, { color: riskColor, children: selectedRow.verdict }),
3103
- /* @__PURE__ */ jsx11(
3104
- Text9,
3625
+ /* @__PURE__ */ jsx13(Text11, { color: riskColor, children: selectedRow.verdict }),
3626
+ /* @__PURE__ */ jsx13(
3627
+ Text11,
3105
3628
  {
3106
3629
  dimColor: true,
3107
3630
  children: ` \u2022 level=${selectedRow.level} score=${selectedRow.score} issues=${selectedRow.signals.length}`
3108
3631
  }
3109
3632
  )
3110
3633
  ] }),
3111
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `Top signals: ${formatSignalsBrief(selectedRow.signals)}` }),
3112
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `Ruleset: ${selectedRow.ruleset}` }),
3113
- selectedRow.truncated ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "Note: scan was truncated due to file/byte/signal limits." }) : null
3634
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `Top signals: ${formatSignalsBrief(selectedRow.signals)}` }),
3635
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `Ruleset: ${selectedRow.ruleset}` }),
3636
+ selectedRow.truncated ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Note: scan was truncated due to file/byte/signal limits." }) : null
3114
3637
  ] }),
3115
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `Path: ${selectedRow.path}` }),
3116
- !selectedRow.error && selectedRow.signals.length > 0 ? /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", marginTop: 1, children: [
3117
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `Findings (${selectedRow.signals.length}):` }),
3118
- selectedRow.signals.slice(0, 12).map((sig, idx) => /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
3638
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `Path: ${selectedRow.path}` }),
3639
+ !selectedRow.error && selectedRow.signals.length > 0 ? /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", marginTop: 1, children: [
3640
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `Findings (${selectedRow.signals.length}):` }),
3641
+ selectedRow.signals.slice(0, 12).map((sig, idx) => /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3119
3642
  "\u2022 ",
3120
3643
  " ",
3121
3644
  formatFindingLine(sig)
3122
3645
  ] }, `${sig.id}-${idx}`)),
3123
- selectedRow.signals.length > 12 ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `\u2026 ${selectedRow.signals.length - 12} more` }) : null
3646
+ selectedRow.signals.length > 12 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `\u2026 ${selectedRow.signals.length - 12} more` }) : null
3124
3647
  ] }) : null,
3125
- /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: BACK_QUIT_HINT }) })
3648
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: BACK_QUIT_HINT }) })
3126
3649
  ] });
3127
3650
  }
3128
3651
  if (manualView === "type-confirm") {
3129
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
3130
- /* @__PURE__ */ jsx11(Text9, { color: "red", children: "High risk patterns detected." }),
3131
- /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
3652
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
3653
+ /* @__PURE__ */ jsx13(Text11, { color: "red", children: "High risk patterns detected." }),
3654
+ /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3132
3655
  "If you still want to proceed, type ",
3133
- /* @__PURE__ */ jsx11(Text9, { bold: true, children: "install" }),
3656
+ /* @__PURE__ */ jsx13(Text11, { bold: true, children: "install" }),
3134
3657
  " and press Enter."
3135
3658
  ] }),
3136
- /* @__PURE__ */ jsxs7(Box9, { marginTop: 1, children: [
3137
- /* @__PURE__ */ jsx11(Text9, { color: "green", children: "> " }),
3138
- /* @__PURE__ */ jsx11(
3659
+ /* @__PURE__ */ jsxs9(Box11, { marginTop: 1, children: [
3660
+ /* @__PURE__ */ jsx13(Text11, { color: "green", children: "> " }),
3661
+ /* @__PURE__ */ jsx13(
3139
3662
  TextInput,
3140
3663
  {
3141
3664
  value: confirmText,
@@ -3152,22 +3675,22 @@ function ManualSecurityGate({
3152
3675
  }
3153
3676
  )
3154
3677
  ] }),
3155
- /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: TEXT_INPUT_HINT }) })
3678
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: TEXT_INPUT_HINT }) })
3156
3679
  ] });
3157
3680
  }
3158
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
3159
- /* @__PURE__ */ jsx11(Text9, { color: "red", children: "High risk patterns detected." }),
3160
- /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
3681
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
3682
+ /* @__PURE__ */ jsx13(Text11, { color: "red", children: "High risk patterns detected." }),
3683
+ /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3161
3684
  "Review the scan summary below (or open scan details). If you proceed, you will need to type",
3162
3685
  " ",
3163
- /* @__PURE__ */ jsx11(Text9, { bold: true, children: "install" }),
3686
+ /* @__PURE__ */ jsx13(Text11, { bold: true, children: "install" }),
3164
3687
  "."
3165
3688
  ] }),
3166
- /* @__PURE__ */ jsx11(Box9, { flexDirection: "column", marginTop: 1, children: riskyScanRows.slice(0, 6).map((row) => {
3689
+ /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginTop: 1, children: riskyScanRows.slice(0, 6).map((row) => {
3167
3690
  const level = row.level ?? "none";
3168
3691
  const top = row.error ? "" : ` top=${formatSignalsBrief(row.signals)}`;
3169
3692
  const suffix = row.error ? ` scan failed: ${row.error}` : ` score=${row.score} issues=${row.signals.length}${top}`;
3170
- return /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
3693
+ return /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3171
3694
  "\u2022",
3172
3695
  " ",
3173
3696
  row.name,
@@ -3176,7 +3699,7 @@ function ManualSecurityGate({
3176
3699
  suffix
3177
3700
  ] }, row.path);
3178
3701
  }) }),
3179
- /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(
3702
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(
3180
3703
  SelectMenu,
3181
3704
  {
3182
3705
  items: [
@@ -3203,7 +3726,7 @@ function ManualSecurityGate({
3203
3726
  }
3204
3727
 
3205
3728
  // src/tui/screens/AddConfirm.tsx
3206
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
3729
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
3207
3730
  function AddConfirmScreen() {
3208
3731
  const {
3209
3732
  invocation,
@@ -3216,13 +3739,13 @@ function AddConfirmScreen() {
3216
3739
  setBackHandler
3217
3740
  } = useNavigation();
3218
3741
  const options = invocation.options;
3219
- const [lines, setLines] = React7.useState([]);
3220
- const [scanStatus, setScanStatus] = React7.useState("idle");
3221
- const [requiresManual, setRequiresManual] = React7.useState(false);
3222
- const [confirmText, setConfirmText] = React7.useState("");
3223
- const [scanRows, setScanRows] = React7.useState([]);
3224
- const [manualView, setManualView] = React7.useState("menu");
3225
- const [selectedRow, setSelectedRow] = React7.useState(null);
3742
+ const [lines, setLines] = React9.useState([]);
3743
+ const [scanStatus, setScanStatus] = React9.useState("idle");
3744
+ const [requiresManual, setRequiresManual] = React9.useState(false);
3745
+ const [confirmText, setConfirmText] = React9.useState("");
3746
+ const [scanRows, setScanRows] = React9.useState([]);
3747
+ const [manualView, setManualView] = React9.useState("menu");
3748
+ const [selectedRow, setSelectedRow] = React9.useState(null);
3226
3749
  const spinner = useSpinnerFrame(scanStatus === "scanning");
3227
3750
  const { wrapOnChange } = useTextInput({
3228
3751
  onClear: () => {
@@ -3230,7 +3753,7 @@ function AddConfirmScreen() {
3230
3753
  },
3231
3754
  disabled: !requiresManual || manualView !== "type-confirm"
3232
3755
  });
3233
- React7.useEffect(() => {
3756
+ React9.useEffect(() => {
3234
3757
  const selectedSkills = addSkill.selectedSkills;
3235
3758
  const targetAgents2 = addSkill.targetAgents;
3236
3759
  const installGlobally = addSkill.installGlobally;
@@ -3301,7 +3824,7 @@ function AddConfirmScreen() {
3301
3824
  navigateTo,
3302
3825
  setFlash
3303
3826
  ]);
3304
- React7.useEffect(() => {
3827
+ React9.useEffect(() => {
3305
3828
  if (!requiresManual) {
3306
3829
  setBackHandler(null);
3307
3830
  return () => setBackHandler(null);
@@ -3332,7 +3855,7 @@ function AddConfirmScreen() {
3332
3855
  const canonicalBase = getCanonicalSkillsBase({ global: addSkill.installGlobally, cwd });
3333
3856
  const canonicalLabel = shortenPath(canonicalBase, cwd);
3334
3857
  const targetAgents = addSkill.targetAgents ?? [];
3335
- const keyedLines = React7.useMemo(() => {
3858
+ const keyedLines = React9.useMemo(() => {
3336
3859
  const counts = /* @__PURE__ */ new Map();
3337
3860
  return lines.map((line) => {
3338
3861
  const count = (counts.get(line) ?? 0) + 1;
@@ -3349,21 +3872,21 @@ function AddConfirmScreen() {
3349
3872
  }).filter((p) => p !== null)
3350
3873
  )
3351
3874
  ) : [];
3352
- return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", padding: 1, children: [
3353
- /* @__PURE__ */ jsx12(AddFlowHeader, { title: "Review plan" }),
3354
- /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", marginBottom: 1, children: [
3355
- /* @__PURE__ */ jsx12(Text10, { children: `Skills: ${skillsCount} \u2022 Agents: ${agentsCount}` }),
3356
- addSkill.targetAgents ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `Targets: ${formatList(addSkill.targetAgents.map((agent) => agents[agent].displayName))}` }) : null,
3357
- addSkill.installGlobally !== void 0 ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `Scope: ${scopeLabel}` }) : null,
3358
- addSkill.installMode ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `Mode: ${installModeLabel}` }) : null,
3359
- addSkill.installMode === "symlink" ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `Canonical base: ${canonicalLabel}` }) : null,
3360
- addSkill.installMode === "copy" && agentPaths.length > 0 ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: `Agent dirs: ${formatList(agentPaths, 3)}` }) : null
3875
+ return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
3876
+ /* @__PURE__ */ jsx14(AddFlowHeader, { title: "Review plan" }),
3877
+ /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", marginBottom: 1, children: [
3878
+ /* @__PURE__ */ jsx14(Text12, { children: `Skills: ${skillsCount} \u2022 Agents: ${agentsCount}` }),
3879
+ addSkill.targetAgents ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Targets: ${formatList(addSkill.targetAgents.map((agent) => agents[agent].displayName))}` }) : null,
3880
+ addSkill.installGlobally !== void 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Scope: ${scopeLabel}` }) : null,
3881
+ addSkill.installMode ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Mode: ${installModeLabel}` }) : null,
3882
+ addSkill.installMode === "symlink" ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Canonical base: ${canonicalLabel}` }) : null,
3883
+ addSkill.installMode === "copy" && agentPaths.length > 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Agent dirs: ${formatList(agentPaths, 3)}` }) : null
3361
3884
  ] }),
3362
- keyedLines.map(({ line, key }) => /* @__PURE__ */ jsx12(Text10, { children: line }, key)),
3363
- /* @__PURE__ */ jsx12(Box10, { marginTop: 1, children: scanStatus === "scanning" ? /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
3885
+ keyedLines.map(({ line, key }) => /* @__PURE__ */ jsx14(Text12, { children: line }, key)),
3886
+ /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: scanStatus === "scanning" ? /* @__PURE__ */ jsxs10(Text12, { dimColor: true, children: [
3364
3887
  spinner,
3365
3888
  " Scanning skills for suspicious patterns..."
3366
- ] }) : requiresManual ? /* @__PURE__ */ jsx12(
3889
+ ] }) : requiresManual ? /* @__PURE__ */ jsx14(
3367
3890
  ManualSecurityGate,
3368
3891
  {
3369
3892
  scanRows,
@@ -3378,7 +3901,7 @@ function AddConfirmScreen() {
3378
3901
  onInstall: () => navigateTo("add-install"),
3379
3902
  onCancel: () => resetTo("main")
3380
3903
  }
3381
- ) : /* @__PURE__ */ jsx12(
3904
+ ) : /* @__PURE__ */ jsx14(
3382
3905
  SelectMenu,
3383
3906
  {
3384
3907
  items: [
@@ -3399,12 +3922,12 @@ function AddConfirmScreen() {
3399
3922
  }
3400
3923
 
3401
3924
  // src/tui/screens/AddDocs.tsx
3402
- import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
3403
- import React9 from "react";
3925
+ import { Box as Box13, Text as Text13, useInput as useInput4 } from "ink";
3926
+ import React10 from "react";
3404
3927
 
3405
3928
  // src/docs/install.ts
3406
3929
  import { mkdir as mkdir3, stat as stat3 } from "fs/promises";
3407
- import { basename as basename4 } from "path";
3930
+ import { basename as basename6 } from "path";
3408
3931
 
3409
3932
  // src/docs/paths.ts
3410
3933
  import { join as join10 } from "path";
@@ -3449,7 +3972,7 @@ async function installDocs(sources, cwd = process.cwd()) {
3449
3972
  const docsBase = getDocsBase(cwd);
3450
3973
  await mkdir3(docsBase, { recursive: true });
3451
3974
  for (const source of sources) {
3452
- const slug = sanitizeDocName(source.name || basename4(source.url));
3975
+ const slug = sanitizeDocName(source.name || basename6(source.url));
3453
3976
  const targetPath = getDocPath(slug, cwd);
3454
3977
  if (!isPathSafe(docsBase, targetPath)) {
3455
3978
  results.push({
@@ -3577,353 +4100,151 @@ async function fetchRepoRefs(source) {
3577
4100
  }
3578
4101
  let branches = [];
3579
4102
  let tags = [];
3580
- try {
3581
- const [branchesResponse, tagsResponse] = await Promise.all([
3582
- fetch(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/branches?per_page=100`, {
3583
- headers
3584
- }),
3585
- fetch(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/tags?per_page=100`, {
3586
- headers
3587
- })
3588
- ]);
3589
- if (branchesResponse.ok) {
3590
- const data = await branchesResponse.json();
3591
- branches = data.map((item) => item.name).filter((value) => Boolean(value));
3592
- }
3593
- if (tagsResponse.ok) {
3594
- const data = await tagsResponse.json();
3595
- tags = data.map((item) => item.name).filter((value) => Boolean(value));
3596
- }
3597
- } catch {
3598
- }
3599
- return { defaultBranch, branches, tags };
3600
- }
3601
- function buildRefOptions(refs, includePrerelease) {
3602
- const options = [];
3603
- const branchSet = new Set(refs.branches);
3604
- branchSet.add(refs.defaultBranch);
3605
- const sortedTags = sortTags(refs.tags);
3606
- const stableTags = includePrerelease ? sortedTags : sortedTags.filter((tag) => !isPrerelease(tag));
3607
- const filteredBranches = includePrerelease ? Array.from(branchSet) : Array.from(branchSet).filter((branch) => !isPrerelease(branch));
3608
- const limitedTags = stableTags.slice(0, TAG_LIMIT);
3609
- const limitedBranches = filteredBranches.filter((branch) => branch !== refs.defaultBranch).sort().slice(0, BRANCH_LIMIT);
3610
- options.push({
3611
- label: `default branch (${refs.defaultBranch})`,
3612
- ref: refs.defaultBranch,
3613
- type: "branch"
3614
- });
3615
- for (const tag of limitedTags) {
3616
- options.push({ label: `tag: ${tag}`, ref: tag, type: "tag" });
3617
- }
3618
- for (const branch of limitedBranches) {
3619
- options.push({ label: `branch: ${branch}`, ref: branch, type: "branch" });
3620
- }
3621
- const extraTags = Math.max(0, stableTags.length - limitedTags.length);
3622
- const extraBranches = Math.max(0, filteredBranches.length - 1 - limitedBranches.length);
3623
- let note = "";
3624
- if (extraTags > 0) {
3625
- note += `Showing ${limitedTags.length} tags (${extraTags} more). `;
3626
- }
3627
- if (extraBranches > 0) {
3628
- note += `Showing ${limitedBranches.length} branches (${extraBranches} more).`;
3629
- }
3630
- return { options, note: note.trim() || void 0 };
3631
- }
3632
-
3633
- // src/docs/sources.ts
3634
- import { existsSync as existsSync4, readFileSync } from "fs";
3635
- import { dirname as dirname2, resolve as resolve4 } from "path";
3636
- import { fileURLToPath } from "url";
3637
- import matter2 from "gray-matter";
3638
- var __dirname = dirname2(fileURLToPath(import.meta.url));
3639
- var SOURCE_CANDIDATES = [
3640
- resolve4(__dirname, "..", "..", "data", "docs-sources.yml"),
3641
- resolve4(__dirname, "..", "data", "docs-sources.yml")
3642
- ];
3643
- var DOC_SOURCES = parseSources();
3644
- function getDocSources() {
3645
- return DOC_SOURCES.slice();
3646
- }
3647
- function parseSources() {
3648
- const sourcePath = SOURCE_CANDIDATES.find((path) => existsSync4(path));
3649
- if (!sourcePath) return [];
3650
- let raw = "";
3651
- try {
3652
- raw = readFileSync(sourcePath, "utf-8");
3653
- } catch {
3654
- return [];
3655
- }
3656
- const parsed = matter2(`---
3657
- ${raw}
3658
- ---`).data;
3659
- if (!Array.isArray(parsed)) return [];
3660
- return parsed.map((item) => normalizeSource(item)).filter((item) => Boolean(item)).sort((a, b) => a.name.localeCompare(b.name));
3661
- }
3662
- function normalizeSource(raw) {
3663
- if (!raw || typeof raw !== "object") return null;
3664
- const record = raw;
3665
- const name = typeof record.name === "string" ? record.name.trim() : "";
3666
- const type = normalizeType(record.type);
3667
- const url = typeof record.url === "string" ? record.url.trim() : "";
3668
- if (!name || !type || !url) {
3669
- return null;
3670
- }
3671
- const docs = typeof record.docs === "string" ? record.docs : void 0;
3672
- const ref = typeof record.ref === "string" ? record.ref : void 0;
3673
- const version2 = typeof record.version === "string" ? record.version : void 0;
3674
- const mode = normalizeMode(record.mode);
3675
- const allow = normalizeStringArray(record.allow);
3676
- const deny = normalizeStringArray(record.deny);
3677
- const depth = typeof record.depth === "number" ? record.depth : void 0;
3678
- const pages = typeof record.pages === "number" ? record.pages : void 0;
3679
- return {
3680
- name,
3681
- type,
3682
- url,
3683
- docs,
3684
- ref,
3685
- version: version2,
3686
- mode,
3687
- allow,
3688
- deny,
3689
- depth,
3690
- pages
3691
- };
3692
- }
3693
- function normalizeType(value) {
3694
- if (value === "github" || value === "web") return value;
3695
- return null;
3696
- }
3697
- function normalizeMode(value) {
3698
- if (value === "docs" || value === "repo") return value;
3699
- return void 0;
3700
- }
3701
- function normalizeStringArray(value) {
3702
- if (!Array.isArray(value)) return void 0;
3703
- const filtered = value.filter((entry) => typeof entry === "string");
3704
- return filtered.length > 0 ? filtered : void 0;
3705
- }
3706
-
3707
- // src/tui/controls/MultiSelect.tsx
3708
- import { Box as Box11, Text as Text11, useInput as useInput3 } from "ink";
3709
- import React8 from "react";
3710
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3711
- var FILTER_THRESHOLD2 = 10;
3712
- function MultiSelect({
3713
- items,
3714
- initialSelected = [],
3715
- onSubmit,
3716
- limit = 10,
3717
- hint = MULTI_SELECT_HINT,
3718
- enableFilter,
3719
- lockedSection,
3720
- hintMode = "all",
3721
- onSelectionChange
3722
- }) {
3723
- const [cursor, setCursor] = React8.useState(0);
3724
- const [infoIndex, setInfoIndex] = React8.useState(null);
3725
- const [filter, setFilter] = React8.useState("");
3726
- const [selected, setSelected] = React8.useState(
3727
- new Set(
3728
- initialSelected.length > 0 ? items.map((item, index) => ({ item, index })).filter(({ item }) => initialSelected.includes(item.value)).map(({ index }) => index) : []
3729
- )
3730
- );
3731
- const showFilter = enableFilter ?? items.length >= FILTER_THRESHOLD2;
3732
- const { setTextInputActive, setTextInputEscMode } = useNavigation();
3733
- const resetFocus = React8.useCallback(() => {
3734
- setCursor(0);
3735
- setInfoIndex(null);
3736
- }, []);
3737
- React8.useEffect(() => {
3738
- if (!showFilter) return;
3739
- setTextInputActive(true);
3740
- setTextInputEscMode("back");
3741
- return () => {
3742
- setTextInputActive(false);
3743
- setTextInputEscMode("back");
3744
- };
3745
- }, [showFilter, setTextInputActive, setTextInputEscMode]);
3746
- const filteredItems = React8.useMemo(() => {
3747
- if (!filter) return items.map((item, index) => ({ item, originalIndex: index }));
3748
- const lowerFilter = filter.toLowerCase();
3749
- return items.map((item, index) => ({ item, originalIndex: index })).filter(
3750
- ({ item }) => item.label.toLowerCase().includes(lowerFilter) || String(item.value).toLowerCase().includes(lowerFilter)
3751
- );
3752
- }, [items, filter]);
3753
- const total = filteredItems.length;
3754
- const maxItems = Math.max(5, Math.min(limit, total));
3755
- const windowStart = Math.min(
3756
- Math.max(0, cursor - Math.floor(maxItems / 2)),
3757
- Math.max(0, total - maxItems)
3758
- );
3759
- const visible = filteredItems.slice(windowStart, windowStart + maxItems);
3760
- const truncate = (value, max = 100) => {
3761
- if (value.length <= max) return value;
3762
- return `${value.slice(0, max - 3)}...`;
3763
- };
3764
- const getSelectedValues = React8.useCallback(
3765
- (nextSelected) => {
3766
- const lockedValues = lockedSection ? lockedSection.items.map((i) => i.value) : [];
3767
- const selectedValues = Array.from(nextSelected).map((index) => items[index]?.value).filter((value) => value !== void 0);
3768
- return [...lockedValues, ...selectedValues];
3769
- },
3770
- [items, lockedSection]
3771
- );
3772
- useInput3((input, key) => {
3773
- if (showFilter) {
3774
- if (key.backspace || key.delete) {
3775
- setFilter((prev) => prev.slice(0, -1));
3776
- resetFocus();
3777
- return;
3778
- }
3779
- if (input && input.length === 1 && !key.ctrl && !key.meta && !key.return && !key.tab && input !== " " && input !== "s" && input !== "S" && input !== "i" && input !== "I") {
3780
- setFilter((prev) => prev + input);
3781
- resetFocus();
3782
- return;
3783
- }
3784
- }
3785
- if (key.downArrow) {
3786
- setCursor((prev) => {
3787
- if (total === 0) return 0;
3788
- const next = (prev + 1) % total;
3789
- if (infoIndex !== null) {
3790
- setInfoIndex(null);
3791
- }
3792
- return next;
3793
- });
3794
- } else if (key.upArrow) {
3795
- setCursor((prev) => {
3796
- if (total === 0) return 0;
3797
- const next = (prev - 1 + total) % total;
3798
- if (infoIndex !== null) {
3799
- setInfoIndex(null);
3800
- }
3801
- return next;
3802
- });
3803
- } else if (input === " ") {
3804
- if (total === 0) return;
3805
- const currentFiltered = filteredItems[cursor];
3806
- if (!currentFiltered) return;
3807
- if (currentFiltered.item.disabled) return;
3808
- const originalIndex = currentFiltered.originalIndex;
3809
- setSelected((prev) => {
3810
- const next = new Set(prev);
3811
- if (next.has(originalIndex)) next.delete(originalIndex);
3812
- else next.add(originalIndex);
3813
- if (onSelectionChange) {
3814
- onSelectionChange(getSelectedValues(next));
3815
- }
3816
- return next;
3817
- });
3818
- } else if (input === "s" || input === "S") {
3819
- const selectableIndices = filteredItems.filter(({ item }) => !item.disabled).map(({ originalIndex }) => originalIndex);
3820
- setSelected((prev) => {
3821
- const allSelected = selectableIndices.length > 0 && selectableIndices.every((index) => prev.has(index));
3822
- if (allSelected) {
3823
- const next2 = new Set(prev);
3824
- for (const index of selectableIndices) {
3825
- next2.delete(index);
3826
- }
3827
- if (onSelectionChange) {
3828
- onSelectionChange(getSelectedValues(next2));
3829
- }
3830
- return next2;
3831
- }
3832
- const next = new Set(prev);
3833
- for (const index of selectableIndices) {
3834
- next.add(index);
3835
- }
3836
- if (onSelectionChange) {
3837
- onSelectionChange(getSelectedValues(next));
3838
- }
3839
- return next;
3840
- });
3841
- } else if (input === "i" || input === "I") {
3842
- setInfoIndex((prev) => prev === cursor ? null : cursor);
3843
- } else if (key.return) {
3844
- const lockedValues = lockedSection ? lockedSection.items.map((i) => i.value) : [];
3845
- const selectedValues = Array.from(selected).map((index) => items[index]?.value).filter((value) => value !== void 0);
3846
- onSubmit([...lockedValues, ...selectedValues]);
3847
- }
3848
- });
3849
- const filterHint = showFilter ? "Type to filter, " : "";
3850
- const displayHint = filterHint + hint;
3851
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
3852
- lockedSection && lockedSection.items.length > 0 && /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", marginBottom: 1, children: [
3853
- /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3854
- "\u2500\u2500 ",
3855
- lockedSection.title,
3856
- " \u2500\u2500"
3857
- ] }),
3858
- lockedSection.items.map((item) => /* @__PURE__ */ jsxs9(Text11, { color: "green", children: [
3859
- " ",
3860
- "\u2713 ",
3861
- item.label
3862
- ] }, String(item.value)))
3863
- ] }),
3864
- lockedSection && items.length > 0 && /* @__PURE__ */ jsx13(Box11, { marginBottom: 0, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "\u2500\u2500 Other agents \u2500\u2500" }) }),
3865
- showFilter && /* @__PURE__ */ jsxs9(Box11, { marginBottom: 1, marginTop: lockedSection ? 1 : 0, children: [
3866
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Filter: " }),
3867
- /* @__PURE__ */ jsx13(Text11, { children: filter || " " }),
3868
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, inverse: true, children: " " }),
3869
- filter && /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3870
- " ",
3871
- "(",
3872
- filteredItems.length,
3873
- "/",
3874
- items.length,
3875
- ")"
3876
- ] })
3877
- ] }),
3878
- total === 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "No matches found" }) : visible.map(({ item, originalIndex }, index) => {
3879
- const visibleIndex = windowStart + index;
3880
- const isActive = visibleIndex === cursor;
3881
- const isSelected = selected.has(originalIndex);
3882
- const marker = isSelected ? "\u25FC" : "\u25FB";
3883
- const pointer = isActive ? "\u276F" : " ";
3884
- const color = item.disabled ? "gray" : isActive ? "cyan" : void 0;
3885
- const showHint = hintMode === "all" || hintMode === "active" && isActive;
3886
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
3887
- /* @__PURE__ */ jsxs9(Text11, { color, children: [
3888
- pointer,
3889
- " ",
3890
- marker,
3891
- " ",
3892
- item.label
3893
- ] }),
3894
- infoIndex === visibleIndex && item.info ? /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3895
- " ",
3896
- " ",
3897
- truncate(item.info)
3898
- ] }) : item.hint && showHint ? /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3899
- " ",
3900
- " ",
3901
- item.hint
3902
- ] }) : null
3903
- ] }, `${item.label}-${originalIndex}`);
3904
- }),
3905
- /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: displayHint }) })
3906
- ] });
4103
+ try {
4104
+ const [branchesResponse, tagsResponse] = await Promise.all([
4105
+ fetch(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/branches?per_page=100`, {
4106
+ headers
4107
+ }),
4108
+ fetch(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/tags?per_page=100`, {
4109
+ headers
4110
+ })
4111
+ ]);
4112
+ if (branchesResponse.ok) {
4113
+ const data = await branchesResponse.json();
4114
+ branches = data.map((item) => item.name).filter((value) => Boolean(value));
4115
+ }
4116
+ if (tagsResponse.ok) {
4117
+ const data = await tagsResponse.json();
4118
+ tags = data.map((item) => item.name).filter((value) => Boolean(value));
4119
+ }
4120
+ } catch {
4121
+ }
4122
+ return { defaultBranch, branches, tags };
4123
+ }
4124
+ function buildRefOptions(refs, includePrerelease) {
4125
+ const options = [];
4126
+ const branchSet = new Set(refs.branches);
4127
+ branchSet.add(refs.defaultBranch);
4128
+ const sortedTags = sortTags(refs.tags);
4129
+ const stableTags = includePrerelease ? sortedTags : sortedTags.filter((tag) => !isPrerelease(tag));
4130
+ const filteredBranches = includePrerelease ? Array.from(branchSet) : Array.from(branchSet).filter((branch) => !isPrerelease(branch));
4131
+ const limitedTags = stableTags.slice(0, TAG_LIMIT);
4132
+ const limitedBranches = filteredBranches.filter((branch) => branch !== refs.defaultBranch).sort().slice(0, BRANCH_LIMIT);
4133
+ options.push({
4134
+ label: `default branch (${refs.defaultBranch})`,
4135
+ ref: refs.defaultBranch,
4136
+ type: "branch"
4137
+ });
4138
+ for (const tag of limitedTags) {
4139
+ options.push({ label: `tag: ${tag}`, ref: tag, type: "tag" });
4140
+ }
4141
+ for (const branch of limitedBranches) {
4142
+ options.push({ label: `branch: ${branch}`, ref: branch, type: "branch" });
4143
+ }
4144
+ const extraTags = Math.max(0, stableTags.length - limitedTags.length);
4145
+ const extraBranches = Math.max(0, filteredBranches.length - 1 - limitedBranches.length);
4146
+ let note = "";
4147
+ if (extraTags > 0) {
4148
+ note += `Showing ${limitedTags.length} tags (${extraTags} more). `;
4149
+ }
4150
+ if (extraBranches > 0) {
4151
+ note += `Showing ${limitedBranches.length} branches (${extraBranches} more).`;
4152
+ }
4153
+ return { options, note: note.trim() || void 0 };
4154
+ }
4155
+
4156
+ // src/docs/sources.ts
4157
+ import { existsSync as existsSync4, readFileSync } from "fs";
4158
+ import { dirname as dirname2, resolve as resolve4 } from "path";
4159
+ import { fileURLToPath } from "url";
4160
+ import matter2 from "gray-matter";
4161
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
4162
+ var SOURCE_CANDIDATES = [
4163
+ resolve4(__dirname, "..", "..", "data", "docs-sources.yml"),
4164
+ resolve4(__dirname, "..", "data", "docs-sources.yml")
4165
+ ];
4166
+ var DOC_SOURCES = parseSources();
4167
+ function getDocSources() {
4168
+ return DOC_SOURCES.slice();
4169
+ }
4170
+ function parseSources() {
4171
+ const sourcePath = SOURCE_CANDIDATES.find((path) => existsSync4(path));
4172
+ if (!sourcePath) return [];
4173
+ let raw = "";
4174
+ try {
4175
+ raw = readFileSync(sourcePath, "utf-8");
4176
+ } catch {
4177
+ return [];
4178
+ }
4179
+ const parsed = matter2(`---
4180
+ ${raw}
4181
+ ---`).data;
4182
+ if (!Array.isArray(parsed)) return [];
4183
+ return parsed.map((item) => normalizeSource(item)).filter((item) => Boolean(item)).sort((a, b) => a.name.localeCompare(b.name));
4184
+ }
4185
+ function normalizeSource(raw) {
4186
+ if (!raw || typeof raw !== "object") return null;
4187
+ const record = raw;
4188
+ const name = typeof record.name === "string" ? record.name.trim() : "";
4189
+ const type = normalizeType(record.type);
4190
+ const url = typeof record.url === "string" ? record.url.trim() : "";
4191
+ if (!name || !type || !url) {
4192
+ return null;
4193
+ }
4194
+ const docs = typeof record.docs === "string" ? record.docs : void 0;
4195
+ const ref = typeof record.ref === "string" ? record.ref : void 0;
4196
+ const version2 = typeof record.version === "string" ? record.version : void 0;
4197
+ const mode = normalizeMode(record.mode);
4198
+ const allow = normalizeStringArray(record.allow);
4199
+ const deny = normalizeStringArray(record.deny);
4200
+ const depth = typeof record.depth === "number" ? record.depth : void 0;
4201
+ const pages = typeof record.pages === "number" ? record.pages : void 0;
4202
+ return {
4203
+ name,
4204
+ type,
4205
+ url,
4206
+ docs,
4207
+ ref,
4208
+ version: version2,
4209
+ mode,
4210
+ allow,
4211
+ deny,
4212
+ depth,
4213
+ pages
4214
+ };
4215
+ }
4216
+ function normalizeType(value) {
4217
+ if (value === "github" || value === "web") return value;
4218
+ return null;
4219
+ }
4220
+ function normalizeMode(value) {
4221
+ if (value === "docs" || value === "repo") return value;
4222
+ return void 0;
4223
+ }
4224
+ function normalizeStringArray(value) {
4225
+ if (!Array.isArray(value)) return void 0;
4226
+ const filtered = value.filter((entry) => typeof entry === "string");
4227
+ return filtered.length > 0 ? filtered : void 0;
3907
4228
  }
3908
4229
 
3909
4230
  // src/tui/screens/AddDocs.tsx
3910
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
4231
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3911
4232
  var DOCS_HINT = "Space to toggle, Enter to install, m for main, q/esc to quit";
3912
4233
  var REF_HINT = "Enter to select, Ctrl+P to toggle prerelease, m for main, q/esc to quit";
3913
4234
  function AddDocsScreen() {
3914
4235
  const { navigateTo, setFlash, setBackHandler } = useNavigation();
3915
- const sources = React9.useMemo(() => getDocSources(), []);
3916
- const [status, setStatus] = React9.useState(sources.length === 0 ? "empty" : "select");
3917
- const [selected, setSelected] = React9.useState([]);
3918
- const [results, setResults] = React9.useState(null);
3919
- const [notice, setNotice] = React9.useState(null);
3920
- const [pendingSources, setPendingSources] = React9.useState([]);
3921
- const [currentIndex, setCurrentIndex] = React9.useState(0);
3922
- const [repoRefs, setRepoRefs] = React9.useState(null);
3923
- const [refStatus, setRefStatus] = React9.useState("idle");
3924
- const [refError, setRefError] = React9.useState(null);
3925
- const [refSelections, setRefSelections] = React9.useState(/* @__PURE__ */ new Map());
3926
- const [includePrerelease, setIncludePrerelease] = React9.useState(false);
4236
+ const sources = React10.useMemo(() => getDocSources(), []);
4237
+ const [status, setStatus] = React10.useState(sources.length === 0 ? "empty" : "select");
4238
+ const [selected, setSelected] = React10.useState([]);
4239
+ const [results, setResults] = React10.useState(null);
4240
+ const [notice, setNotice] = React10.useState(null);
4241
+ const [pendingSources, setPendingSources] = React10.useState([]);
4242
+ const [currentIndex, setCurrentIndex] = React10.useState(0);
4243
+ const [repoRefs, setRepoRefs] = React10.useState(null);
4244
+ const [refStatus, setRefStatus] = React10.useState("idle");
4245
+ const [refError, setRefError] = React10.useState(null);
4246
+ const [refSelections, setRefSelections] = React10.useState(/* @__PURE__ */ new Map());
4247
+ const [includePrerelease, setIncludePrerelease] = React10.useState(false);
3927
4248
  const spinner = useSpinnerFrame(status === "installing" || refStatus === "loading");
3928
4249
  useInput4((input, key) => {
3929
4250
  if (status !== "choose-ref") return;
@@ -3931,7 +4252,7 @@ function AddDocsScreen() {
3931
4252
  setIncludePrerelease((prev) => !prev);
3932
4253
  }
3933
4254
  });
3934
- React9.useEffect(() => {
4255
+ React10.useEffect(() => {
3935
4256
  let cancelled = false;
3936
4257
  const run = async () => {
3937
4258
  if (status !== "installing" || selected.length === 0) return;
@@ -3952,7 +4273,7 @@ function AddDocsScreen() {
3952
4273
  cancelled = true;
3953
4274
  };
3954
4275
  }, [status, selected, setFlash]);
3955
- React9.useEffect(() => {
4276
+ React10.useEffect(() => {
3956
4277
  if (status !== "choose-ref") {
3957
4278
  setBackHandler(null);
3958
4279
  return;
@@ -3969,7 +4290,7 @@ function AddDocsScreen() {
3969
4290
  setBackHandler(null);
3970
4291
  };
3971
4292
  }, [status, setBackHandler]);
3972
- React9.useEffect(() => {
4293
+ React10.useEffect(() => {
3973
4294
  let cancelled = false;
3974
4295
  const loadRefs = async () => {
3975
4296
  if (status !== "choose-ref") return;
@@ -4003,35 +4324,35 @@ function AddDocsScreen() {
4003
4324
  };
4004
4325
  }, [status, pendingSources, currentIndex]);
4005
4326
  if (status === "empty") {
4006
- return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
4007
- /* @__PURE__ */ jsx14(Header, { title: "Add docs" }),
4008
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No curated docs configured yet." }),
4009
- /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: BACK_QUIT_HINT }) })
4327
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4328
+ /* @__PURE__ */ jsx15(Header, { title: "Add docs" }),
4329
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "No curated docs configured yet." }),
4330
+ /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: BACK_QUIT_HINT }) })
4010
4331
  ] });
4011
4332
  }
4012
4333
  if (status === "choose-ref") {
4013
4334
  const source = pendingSources[currentIndex];
4014
4335
  if (!source) {
4015
- return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
4016
- /* @__PURE__ */ jsx14(Header, { title: "Select branch or tag" }),
4017
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No docs selected." })
4336
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4337
+ /* @__PURE__ */ jsx15(Header, { title: "Select branch or tag" }),
4338
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "No docs selected." })
4018
4339
  ] });
4019
4340
  }
4020
4341
  const built = repoRefs ? buildRefOptions(repoRefs, includePrerelease) : { options: [], note: "" };
4021
4342
  const prereleaseNote = includePrerelease ? "Showing prerelease refs." : "Stable only (Ctrl+P for prerelease).";
4022
- return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
4023
- /* @__PURE__ */ jsx14(Header, { title: "Select branch or tag" }),
4024
- /* @__PURE__ */ jsxs10(Box12, { marginBottom: 1, flexDirection: "column", children: [
4025
- /* @__PURE__ */ jsx14(Text12, { children: source.name }),
4026
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: source.docs ? `docs: ${source.docs}` : "full repo" }),
4027
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Tags are pinned; update docs will skip them." }),
4028
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Repo ${currentIndex + 1}/${pendingSources.length}` })
4343
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4344
+ /* @__PURE__ */ jsx15(Header, { title: "Select branch or tag" }),
4345
+ /* @__PURE__ */ jsxs11(Box13, { marginBottom: 1, flexDirection: "column", children: [
4346
+ /* @__PURE__ */ jsx15(Text13, { children: source.name }),
4347
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: source.docs ? `docs: ${source.docs}` : "full repo" }),
4348
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Tags are pinned; update docs will skip them." }),
4349
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: `Repo ${currentIndex + 1}/${pendingSources.length}` })
4029
4350
  ] }),
4030
- refError ? /* @__PURE__ */ jsx14(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text12, { color: "red", children: refError }) }) : null,
4031
- refStatus === "loading" ? /* @__PURE__ */ jsxs10(Text12, { children: [
4351
+ refError ? /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "red", children: refError }) }) : null,
4352
+ refStatus === "loading" ? /* @__PURE__ */ jsxs11(Text13, { children: [
4032
4353
  spinner,
4033
4354
  " Loading refs..."
4034
- ] }) : /* @__PURE__ */ jsx14(
4355
+ ] }) : /* @__PURE__ */ jsx15(
4035
4356
  SingleSelect,
4036
4357
  {
4037
4358
  items: built.options.map((option) => ({
@@ -4061,17 +4382,17 @@ function AddDocsScreen() {
4061
4382
  hintMode: "active"
4062
4383
  }
4063
4384
  ),
4064
- /* @__PURE__ */ jsxs10(Box12, { marginTop: 1, children: [
4065
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: prereleaseNote }),
4066
- built.note ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: built.note }) : null
4385
+ /* @__PURE__ */ jsxs11(Box13, { marginTop: 1, children: [
4386
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: prereleaseNote }),
4387
+ built.note ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: built.note }) : null
4067
4388
  ] })
4068
4389
  ] });
4069
4390
  }
4070
4391
  if (status === "installing") {
4071
4392
  const docsBase = shortenPath(getDocsBase(), process.cwd());
4072
- return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
4073
- /* @__PURE__ */ jsx14(Header, { title: "Installing docs" }),
4074
- /* @__PURE__ */ jsxs10(Text12, { children: [
4393
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4394
+ /* @__PURE__ */ jsx15(Header, { title: "Installing docs" }),
4395
+ /* @__PURE__ */ jsxs11(Text13, { children: [
4075
4396
  spinner,
4076
4397
  " Cloning ",
4077
4398
  selected.length,
@@ -4087,31 +4408,31 @@ function AddDocsScreen() {
4087
4408
  const skipped = results.filter((r) => r.status === "skipped");
4088
4409
  const failed = results.filter((r) => r.status === "failed");
4089
4410
  const cwd = process.cwd();
4090
- return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
4091
- /* @__PURE__ */ jsx14(Header, { title: "Docs installed" }),
4092
- installed.length > 0 ? /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", marginBottom: 1, children: [
4093
- /* @__PURE__ */ jsx14(Text12, { children: `Installed ${installed.length} repo${installed.length !== 1 ? "s" : ""}` }),
4094
- installed.map((item) => /* @__PURE__ */ jsxs10(Text12, { dimColor: true, children: [
4411
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4412
+ /* @__PURE__ */ jsx15(Header, { title: "Docs installed" }),
4413
+ installed.length > 0 ? /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", marginBottom: 1, children: [
4414
+ /* @__PURE__ */ jsx15(Text13, { children: `Installed ${installed.length} repo${installed.length !== 1 ? "s" : ""}` }),
4415
+ installed.map((item) => /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
4095
4416
  item.source.name,
4096
4417
  " -> ",
4097
4418
  shortenPath(item.path, cwd)
4098
4419
  ] }, `installed-${item.slug}`))
4099
4420
  ] }) : null,
4100
- skipped.length > 0 ? /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", marginBottom: 1, children: [
4101
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Skipped" }),
4102
- skipped.map((item) => /* @__PURE__ */ jsxs10(Text12, { dimColor: true, children: [
4421
+ skipped.length > 0 ? /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", marginBottom: 1, children: [
4422
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Skipped" }),
4423
+ skipped.map((item) => /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
4103
4424
  item.source.name,
4104
4425
  item.message ? ` (${item.message})` : ""
4105
4426
  ] }, `skipped-${item.slug}`))
4106
4427
  ] }) : null,
4107
- failed.length > 0 ? /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", marginBottom: 1, children: [
4108
- /* @__PURE__ */ jsx14(Text12, { color: "red", children: "Failed" }),
4109
- failed.map((item) => /* @__PURE__ */ jsxs10(Text12, { color: "red", children: [
4428
+ failed.length > 0 ? /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", marginBottom: 1, children: [
4429
+ /* @__PURE__ */ jsx15(Text13, { color: "red", children: "Failed" }),
4430
+ failed.map((item) => /* @__PURE__ */ jsxs11(Text13, { color: "red", children: [
4110
4431
  item.source.name,
4111
4432
  item.message ? ` (${item.message})` : ""
4112
4433
  ] }, `failed-${item.slug}`))
4113
4434
  ] }) : null,
4114
- /* @__PURE__ */ jsx14(
4435
+ /* @__PURE__ */ jsx15(
4115
4436
  SelectMenu,
4116
4437
  {
4117
4438
  items: [
@@ -4146,9 +4467,9 @@ function AddDocsScreen() {
4146
4467
  disabled: false
4147
4468
  };
4148
4469
  });
4149
- return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", padding: 1, children: [
4150
- /* @__PURE__ */ jsx14(Header, { title: "Select docs" }),
4151
- /* @__PURE__ */ jsx14(
4470
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4471
+ /* @__PURE__ */ jsx15(Header, { title: "Select docs" }),
4472
+ /* @__PURE__ */ jsx15(
4152
4473
  MultiSelect,
4153
4474
  {
4154
4475
  items,
@@ -4177,7 +4498,7 @@ function AddDocsScreen() {
4177
4498
  }
4178
4499
  }
4179
4500
  ),
4180
- notice ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text12, { color: "cyan", children: [
4501
+ notice ? /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text13, { color: "cyan", children: [
4181
4502
  "[i] ",
4182
4503
  notice
4183
4504
  ] }) }) : null
@@ -4185,8 +4506,8 @@ function AddDocsScreen() {
4185
4506
  }
4186
4507
 
4187
4508
  // src/tui/screens/AddInstall.tsx
4188
- import { Box as Box13, Text as Text13 } from "ink";
4189
- import React10 from "react";
4509
+ import { Box as Box14, Text as Text14 } from "ink";
4510
+ import React11 from "react";
4190
4511
 
4191
4512
  // src/skill-lock.ts
4192
4513
  import { createHash } from "crypto";
@@ -4571,12 +4892,16 @@ async function recordParsedTracking(skills, results, installGlobally, parsed, te
4571
4892
  const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue);
4572
4893
  if (hash) skillFolderHash = hash;
4573
4894
  }
4895
+ if (parsed.type === "well-known" && parsed.contentHash) {
4896
+ skillFolderHash = parsed.contentHash;
4897
+ }
4574
4898
  await addSkillToLock(
4575
4899
  displayName,
4576
4900
  {
4577
4901
  source: normalizedSource ?? parsed.url,
4578
4902
  sourceType: parsed.type,
4579
4903
  sourceUrl: parsed.url,
4904
+ ...parsed.type === "well-known" && parsed.licenseKey ? { licenseKey: parsed.licenseKey } : {},
4580
4905
  skillPath: skillPathValue,
4581
4906
  skillFolderHash,
4582
4907
  ref: parsed.ref
@@ -4646,7 +4971,10 @@ function formatResultSummary(results) {
4646
4971
  if (!firstResult) continue;
4647
4972
  if (firstResult.mode === "copy") {
4648
4973
  lines.push(`${chalk2.green("\u2713")} ${skillName} ${chalk2.dim("(copied)")}`);
4974
+ const seenPaths = /* @__PURE__ */ new Set();
4649
4975
  for (const r of skillResults) {
4976
+ if (seenPaths.has(r.path)) continue;
4977
+ seenPaths.add(r.path);
4650
4978
  const shortPath = shortenPath(r.path, cwd);
4651
4979
  lines.push(` ${chalk2.dim("\u2192")} ${shortPath}`);
4652
4980
  }
@@ -4678,14 +5006,14 @@ function formatResultSummary(results) {
4678
5006
  }
4679
5007
 
4680
5008
  // src/tui/screens/AddInstall.tsx
4681
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
5009
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
4682
5010
  function AddInstallScreen() {
4683
5011
  const { addSkill, updateAddSkill, navigateTo } = useNavigation();
4684
5012
  const spinner = useSpinnerFrame(true);
4685
- const [status, setStatus] = React10.useState("running");
4686
- const [error, setError] = React10.useState(null);
4687
- const didRun = React10.useRef(false);
4688
- React10.useEffect(() => {
5013
+ const [status, setStatus] = React11.useState("running");
5014
+ const [error, setError] = React11.useState(null);
5015
+ const didRun = React11.useRef(false);
5016
+ React11.useEffect(() => {
4689
5017
  let cancelled = false;
4690
5018
  const run = async () => {
4691
5019
  if (didRun.current) return;
@@ -4739,30 +5067,66 @@ function AddInstallScreen() {
4739
5067
  };
4740
5068
  }, [addSkill, updateAddSkill, navigateTo]);
4741
5069
  if (status === "error") {
4742
- return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4743
- /* @__PURE__ */ jsx15(AddFlowHeader, { title: "Install failed" }),
4744
- /* @__PURE__ */ jsx15(Text13, { color: "red", children: error }),
4745
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: BACK_QUIT_HINT })
5070
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", padding: 1, children: [
5071
+ /* @__PURE__ */ jsx16(AddFlowHeader, { title: "Install failed" }),
5072
+ /* @__PURE__ */ jsx16(Text14, { color: "red", children: error }),
5073
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: BACK_QUIT_HINT })
4746
5074
  ] });
4747
5075
  }
4748
- return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", padding: 1, children: [
4749
- /* @__PURE__ */ jsx15(AddFlowHeader, { title: "Installing skills" }),
4750
- /* @__PURE__ */ jsxs11(Text13, { children: [
5076
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", padding: 1, children: [
5077
+ /* @__PURE__ */ jsx16(AddFlowHeader, { title: "Installing skills" }),
5078
+ /* @__PURE__ */ jsxs12(Text14, { children: [
4751
5079
  spinner,
4752
5080
  " Installing..."
4753
5081
  ] })
4754
5082
  ] });
4755
5083
  }
4756
5084
 
5085
+ // src/tui/screens/AddLicenseKey.tsx
5086
+ import { Box as Box15, Text as Text15 } from "ink";
5087
+ import TextInput2 from "ink-text-input";
5088
+ import React12 from "react";
5089
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
5090
+ function AddLicenseKeyScreen() {
5091
+ const { invocation, addSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
5092
+ const [value, setValue] = React12.useState(
5093
+ addSkill.licenseKey ?? invocation.options.licenseKey ?? ""
5094
+ );
5095
+ const { wrapOnChange } = useTextInput({
5096
+ onClear: () => {
5097
+ setValue("");
5098
+ }
5099
+ });
5100
+ const onSubmit = (input) => {
5101
+ const trimmed = input.trim();
5102
+ if (!trimmed) {
5103
+ setFlash("Enter a license key.");
5104
+ return;
5105
+ }
5106
+ updateAddSkill({ licenseKey: trimmed });
5107
+ navigateTo("add-skill-select");
5108
+ };
5109
+ return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", padding: 1, children: [
5110
+ /* @__PURE__ */ jsx17(Header, { title: "License key" }),
5111
+ /* @__PURE__ */ jsx17(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text15, { children: "Enter the license key for this paid/private skill." }) }),
5112
+ /* @__PURE__ */ jsx17(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Tip: you can also pass it via --license-key." }) }),
5113
+ /* @__PURE__ */ jsxs13(Box15, { children: [
5114
+ /* @__PURE__ */ jsx17(Text15, { color: "green", children: "> " }),
5115
+ /* @__PURE__ */ jsx17(TextInput2, { value, onChange: wrapOnChange(setValue), onSubmit })
5116
+ ] }),
5117
+ /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: TEXT_INPUT_HINT }) })
5118
+ ] });
5119
+ }
5120
+
4757
5121
  // src/tui/screens/AddMode.tsx
4758
5122
  import { join as join12 } from "path";
4759
- import { Box as Box14 } from "ink";
4760
- import React11 from "react";
4761
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
5123
+ import { Box as Box16 } from "ink";
5124
+ import React13 from "react";
5125
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
4762
5126
  function AddModeScreen() {
4763
5127
  const { invocation, addSkill, updateAddSkill, navigateTo, navAction } = useNavigation();
4764
5128
  const options = invocation.options;
4765
- React11.useEffect(() => {
5129
+ React13.useEffect(() => {
4766
5130
  if (navAction === "pop") return;
4767
5131
  if (addSkill.installMode) {
4768
5132
  navigateTo("add-confirm");
@@ -4792,9 +5156,9 @@ function AddModeScreen() {
4792
5156
  { label: "Symlink (recommended)", value: "symlink", hint: symlinkHint },
4793
5157
  { label: "Copy to each agent", value: "copy", hint: copyHint }
4794
5158
  ];
4795
- return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", padding: 1, children: [
4796
- /* @__PURE__ */ jsx16(AddFlowHeader, { title: "Install mode" }),
4797
- /* @__PURE__ */ jsx16(
5159
+ return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", padding: 1, children: [
5160
+ /* @__PURE__ */ jsx18(AddFlowHeader, { title: "Install mode" }),
5161
+ /* @__PURE__ */ jsx18(
4798
5162
  SingleSelect,
4799
5163
  {
4800
5164
  items,
@@ -4810,9 +5174,9 @@ function AddModeScreen() {
4810
5174
 
4811
5175
  // src/tui/screens/AddResult.tsx
4812
5176
  import chalk3 from "chalk";
4813
- import { Box as Box15, Text as Text14 } from "ink";
4814
- import React12 from "react";
4815
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
5177
+ import { Box as Box17, Text as Text16 } from "ink";
5178
+ import React14 from "react";
5179
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
4816
5180
  function AddResultScreen() {
4817
5181
  const { addSkill, resetAddSkill, navigateTo } = useNavigation();
4818
5182
  const results = addSkill.installResults ?? [];
@@ -4820,7 +5184,7 @@ function AddResultScreen() {
4820
5184
  const failed = results.filter((r) => !r.success);
4821
5185
  const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
4822
5186
  const summary = successful.length > 0 ? formatResultSummary(successful) : null;
4823
- const summaryLines = React12.useMemo(() => {
5187
+ const summaryLines = React14.useMemo(() => {
4824
5188
  if (!summary) return [];
4825
5189
  const counts = /* @__PURE__ */ new Map();
4826
5190
  return summary.lines.map((line) => {
@@ -4829,15 +5193,15 @@ function AddResultScreen() {
4829
5193
  return { line, key: `${line}-${count}` };
4830
5194
  });
4831
5195
  }, [summary]);
4832
- return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", padding: 1, children: [
4833
- /* @__PURE__ */ jsx17(AddFlowHeader, { title: "Install results" }),
4834
- summary ? /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", marginBottom: 1, children: [
4835
- /* @__PURE__ */ jsx17(Text14, { children: summary.title }),
4836
- summaryLines.map(({ line, key }) => /* @__PURE__ */ jsx17(Text14, { children: line }, key))
5196
+ return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
5197
+ /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Install results" }),
5198
+ summary ? /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", marginBottom: 1, children: [
5199
+ /* @__PURE__ */ jsx19(Text16, { children: summary.title }),
5200
+ summaryLines.map(({ line, key }) => /* @__PURE__ */ jsx19(Text16, { children: line }, key))
4837
5201
  ] }) : null,
4838
- failed.length > 0 ? /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", marginBottom: 1, children: [
4839
- /* @__PURE__ */ jsx17(Text14, { color: "red", children: `Failed to install ${failed.length}` }),
4840
- failed.map((r) => /* @__PURE__ */ jsxs13(Text14, { children: [
5202
+ failed.length > 0 ? /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", marginBottom: 1, children: [
5203
+ /* @__PURE__ */ jsx19(Text16, { color: "red", children: `Failed to install ${failed.length}` }),
5204
+ failed.map((r) => /* @__PURE__ */ jsxs15(Text16, { children: [
4841
5205
  chalk3.red("\u2717"),
4842
5206
  " ",
4843
5207
  r.skill,
@@ -4847,15 +5211,15 @@ function AddResultScreen() {
4847
5211
  chalk3.dim(r.error)
4848
5212
  ] }, `${r.skill}-${r.agentId}`))
4849
5213
  ] }) : null,
4850
- successful.length > 0 ? /* @__PURE__ */ jsx17(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
5214
+ successful.length > 0 ? /* @__PURE__ */ jsx19(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
4851
5215
  "Installed to: ",
4852
5216
  formatList(successful.map((r) => r.agent))
4853
5217
  ] }) }) : null,
4854
- symlinkFailures.length > 0 ? /* @__PURE__ */ jsxs13(Box15, { marginBottom: 1, children: [
4855
- /* @__PURE__ */ jsx17(Text14, { color: "yellow", children: `Symlinks failed for: ${formatList(symlinkFailures.map((r) => r.agent))}` }),
4856
- /* @__PURE__ */ jsx17(Text14, { dimColor: true, children: "Files were copied instead." })
5218
+ symlinkFailures.length > 0 ? /* @__PURE__ */ jsxs15(Box17, { marginBottom: 1, children: [
5219
+ /* @__PURE__ */ jsx19(Text16, { color: "yellow", children: `Symlinks failed for: ${formatList(symlinkFailures.map((r) => r.agent))}` }),
5220
+ /* @__PURE__ */ jsx19(Text16, { dimColor: true, children: "Files were copied instead." })
4857
5221
  ] }) : null,
4858
- /* @__PURE__ */ jsx17(
5222
+ /* @__PURE__ */ jsx19(
4859
5223
  SelectMenu,
4860
5224
  {
4861
5225
  items: [
@@ -4878,13 +5242,13 @@ function AddResultScreen() {
4878
5242
  }
4879
5243
 
4880
5244
  // src/tui/screens/AddScope.tsx
4881
- import { Box as Box16 } from "ink";
4882
- import React13 from "react";
4883
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
5245
+ import { Box as Box18 } from "ink";
5246
+ import React15 from "react";
5247
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
4884
5248
  function AddScopeScreen() {
4885
5249
  const { invocation, addSkill, updateAddSkill, navigateTo, navAction } = useNavigation();
4886
5250
  const options = invocation.options;
4887
- React13.useEffect(() => {
5251
+ React15.useEffect(() => {
4888
5252
  if (navAction === "pop") return;
4889
5253
  if (addSkill.installGlobally !== void 0) {
4890
5254
  navigateTo("add-mode");
@@ -4907,9 +5271,9 @@ function AddScopeScreen() {
4907
5271
  const globalBase = getCanonicalSkillsBase({ global: true, cwd });
4908
5272
  const projectHint = `Project base (symlink): ${shortenPath(projectBase, cwd)}`;
4909
5273
  const globalHint = `Global base (symlink): ${shortenPath(globalBase, cwd)}`;
4910
- return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", padding: 1, children: [
4911
- /* @__PURE__ */ jsx18(AddFlowHeader, { title: "Install scope" }),
4912
- /* @__PURE__ */ jsx18(
5274
+ return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5275
+ /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Install scope" }),
5276
+ /* @__PURE__ */ jsx20(
4913
5277
  SingleSelect,
4914
5278
  {
4915
5279
  items: [
@@ -4931,23 +5295,23 @@ function AddScopeScreen() {
4931
5295
  }
4932
5296
 
4933
5297
  // src/tui/screens/AddSecurityScan.tsx
4934
- import { Box as Box17, Text as Text15 } from "ink";
4935
- import React14 from "react";
4936
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
5298
+ import { Box as Box19, Text as Text17 } from "ink";
5299
+ import React16 from "react";
5300
+ import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
4937
5301
  function AddSecurityScanScreen() {
4938
5302
  const { invocation, addSkill, updateAddSkill, navigateTo, resetTo, setFlash, setBackHandler } = useNavigation();
4939
5303
  const options = invocation.options;
4940
- const [status, setStatus] = React14.useState("scanning");
4941
- const [error, setError] = React14.useState(null);
4942
- const [manualView, setManualView] = React14.useState("menu");
4943
- const [confirmText, setConfirmText] = React14.useState("");
4944
- const [selectedRow, setSelectedRow] = React14.useState(null);
5304
+ const [status, setStatus] = React16.useState("scanning");
5305
+ const [error, setError] = React16.useState(null);
5306
+ const [manualView, setManualView] = React16.useState("menu");
5307
+ const [confirmText, setConfirmText] = React16.useState("");
5308
+ const [selectedRow, setSelectedRow] = React16.useState(null);
4945
5309
  const spinner = useSpinnerFrame(status === "scanning");
4946
5310
  const { wrapOnChange } = useTextInput({
4947
5311
  onClear: () => setConfirmText(""),
4948
5312
  disabled: status !== "risky" || manualView !== "type-confirm"
4949
5313
  });
4950
- React14.useEffect(() => {
5314
+ React16.useEffect(() => {
4951
5315
  const selectedSkills = addSkill.selectedSkills;
4952
5316
  if (!selectedSkills || selectedSkills.length === 0) {
4953
5317
  navigateTo("add-skill-select");
@@ -4987,7 +5351,7 @@ function AddSecurityScanScreen() {
4987
5351
  cancelled = true;
4988
5352
  };
4989
5353
  }, [addSkill.selectedSkills, navigateTo, updateAddSkill]);
4990
- React14.useEffect(() => {
5354
+ React16.useEffect(() => {
4991
5355
  if (status !== "risky") {
4992
5356
  setBackHandler(null);
4993
5357
  return () => setBackHandler(null);
@@ -5011,17 +5375,17 @@ function AddSecurityScanScreen() {
5011
5375
  return () => setBackHandler(null);
5012
5376
  }, [status, manualView, setBackHandler]);
5013
5377
  if (status === "error") {
5014
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
5015
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
5016
- /* @__PURE__ */ jsx19(Text15, { color: "red", children: error ?? "Scan failed" }),
5017
- /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text15, { dimColor: true, children: BACK_QUIT_HINT }) })
5378
+ return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
5379
+ /* @__PURE__ */ jsx21(AddFlowHeader, { title: "Security scan" }),
5380
+ /* @__PURE__ */ jsx21(Text17, { color: "red", children: error ?? "Scan failed" }),
5381
+ /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: BACK_QUIT_HINT }) })
5018
5382
  ] });
5019
5383
  }
5020
5384
  if (status === "scanning") {
5021
5385
  const count = addSkill.selectedSkills?.length ?? 0;
5022
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
5023
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
5024
- /* @__PURE__ */ jsxs15(Text15, { children: [
5386
+ return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
5387
+ /* @__PURE__ */ jsx21(AddFlowHeader, { title: "Security scan" }),
5388
+ /* @__PURE__ */ jsxs17(Text17, { children: [
5025
5389
  spinner,
5026
5390
  " Scanning ",
5027
5391
  count,
@@ -5032,9 +5396,9 @@ function AddSecurityScanScreen() {
5032
5396
  ] });
5033
5397
  }
5034
5398
  if (status === "risky") {
5035
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
5036
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
5037
- /* @__PURE__ */ jsx19(
5399
+ return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
5400
+ /* @__PURE__ */ jsx21(AddFlowHeader, { title: "Security scan" }),
5401
+ /* @__PURE__ */ jsx21(
5038
5402
  ManualSecurityGate,
5039
5403
  {
5040
5404
  scanRows: addSkill.securityScanRows ?? [],
@@ -5058,12 +5422,12 @@ function AddSecurityScanScreen() {
5058
5422
  ] });
5059
5423
  }
5060
5424
  if (options.yes) {
5061
- return /* @__PURE__ */ jsx19(Box17, { padding: 1 });
5425
+ return /* @__PURE__ */ jsx21(Box19, { padding: 1 });
5062
5426
  }
5063
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
5064
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
5065
- /* @__PURE__ */ jsx19(Text15, { dimColor: true, children: "Scan complete." }),
5066
- /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text15, { dimColor: true, children: BACK_QUIT_HINT }) })
5427
+ return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
5428
+ /* @__PURE__ */ jsx21(AddFlowHeader, { title: "Security scan" }),
5429
+ /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: "Scan complete." }),
5430
+ /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: BACK_QUIT_HINT }) })
5067
5431
  ] });
5068
5432
  }
5069
5433
 
@@ -5072,8 +5436,8 @@ import { existsSync as existsSync6 } from "fs";
5072
5436
  import { mkdir as mkdir6, mkdtemp as mkdtemp4, writeFile as writeFile4 } from "fs/promises";
5073
5437
  import { tmpdir as tmpdir5 } from "os";
5074
5438
  import { join as join15 } from "path";
5075
- import { Box as Box18, Text as Text16 } from "ink";
5076
- import React15 from "react";
5439
+ import { Box as Box20, Text as Text18 } from "ink";
5440
+ import React17 from "react";
5077
5441
 
5078
5442
  // src/mintlify.ts
5079
5443
  import matter3 from "gray-matter";
@@ -5346,7 +5710,18 @@ var WellKnownProvider = class {
5346
5710
  try {
5347
5711
  const parsed = new URL(baseUrl);
5348
5712
  const basePath = parsed.pathname.replace(/\/$/, "");
5713
+ const indexSuffix = `/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`;
5349
5714
  const urlsToTry = [
5715
+ // If the caller already passed the index.json URL, try it directly.
5716
+ ...basePath.endsWith(indexSuffix) ? [
5717
+ {
5718
+ indexUrl: `${parsed.protocol}//${parsed.host}${basePath}`,
5719
+ baseUrl: `${parsed.protocol}//${parsed.host}${basePath.slice(
5720
+ 0,
5721
+ Math.max(0, basePath.length - indexSuffix.length)
5722
+ )}`
5723
+ }
5724
+ ] : [],
5350
5725
  {
5351
5726
  indexUrl: `${parsed.protocol}//${parsed.host}${basePath}/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`,
5352
5727
  baseUrl: `${parsed.protocol}//${parsed.host}${basePath}`
@@ -5391,19 +5766,46 @@ var WellKnownProvider = class {
5391
5766
  const e = entry;
5392
5767
  if (typeof e.name !== "string" || !e.name) return false;
5393
5768
  if (typeof e.description !== "string" || !e.description) return false;
5394
- if (!Array.isArray(e.files) || e.files.length === 0) return false;
5395
5769
  const nameRegex = /^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/;
5396
5770
  if (!nameRegex.test(e.name) && e.name.length > 1) {
5397
5771
  if (e.name.length === 1 && !/^[a-z0-9]$/.test(e.name)) {
5398
5772
  return false;
5399
5773
  }
5400
5774
  }
5401
- for (const file of e.files) {
5402
- if (typeof file !== "string") return false;
5403
- if (file.startsWith("/") || file.startsWith("\\") || file.includes("..")) return false;
5775
+ if (Array.isArray(e.files) && e.files.length > 0) {
5776
+ for (const file of e.files) {
5777
+ if (typeof file !== "string") return false;
5778
+ if (file.startsWith("/") || file.startsWith("\\") || file.includes("..")) return false;
5779
+ }
5780
+ const hasSkillMd2 = e.files.some(
5781
+ (f) => typeof f === "string" && f.toLowerCase() === "skill.md"
5782
+ );
5783
+ if (!hasSkillMd2) return false;
5784
+ return true;
5785
+ }
5786
+ if (!e.content || typeof e.content !== "object") return false;
5787
+ const c = e.content;
5788
+ if (typeof c.endpoint !== "string" || !c.endpoint) return false;
5789
+ if (typeof c.method !== "string" || !c.method) return false;
5790
+ if (e.auth != null) {
5791
+ if (typeof e.auth !== "object") return false;
5792
+ const a = e.auth;
5793
+ if (typeof a.tokenHeader !== "string" || !a.tokenHeader) return false;
5794
+ if (a.tokenPrefix != null && typeof a.tokenPrefix !== "string") return false;
5795
+ } else {
5796
+ if (typeof c.tokenHeader !== "string" || !c.tokenHeader) return false;
5797
+ if (c.tokenPrefix != null && typeof c.tokenPrefix !== "string") return false;
5798
+ }
5799
+ if (e.verify != null) {
5800
+ if (typeof e.verify !== "object") return false;
5801
+ const v = e.verify;
5802
+ if (typeof v.endpoint !== "string" || !v.endpoint) return false;
5803
+ if (typeof v.method !== "string" || !v.method) return false;
5804
+ if (e.auth == null) {
5805
+ if (typeof v.tokenHeader !== "string" || !v.tokenHeader) return false;
5806
+ if (v.tokenPrefix != null && typeof v.tokenPrefix !== "string") return false;
5807
+ }
5404
5808
  }
5405
- const hasSkillMd2 = e.files.some((f) => typeof f === "string" && f.toLowerCase() === "skill.md");
5406
- if (!hasSkillMd2) return false;
5407
5809
  return true;
5408
5810
  }
5409
5811
  async fetchSkill(url) {
@@ -5450,6 +5852,9 @@ var WellKnownProvider = class {
5450
5852
  }
5451
5853
  async fetchSkillByEntry(baseUrl, entry) {
5452
5854
  try {
5855
+ if (!("files" in entry)) {
5856
+ return null;
5857
+ }
5453
5858
  const skillBaseUrl = `${baseUrl.replace(/\/$/, "")}/${this.WELL_KNOWN_PATH}/${entry.name}`;
5454
5859
  const skillMdUrl = `${skillBaseUrl}/SKILL.md`;
5455
5860
  const response = await fetch(skillMdUrl);
@@ -5610,9 +6015,82 @@ async function resolveRemoteSkill(url) {
5610
6015
  import { mkdir as mkdir5, mkdtemp as mkdtemp3, writeFile as writeFile3 } from "fs/promises";
5611
6016
  import { tmpdir as tmpdir4 } from "os";
5612
6017
  import { dirname as dirname4, join as join13 } from "path";
5613
- async function prepareWellKnownSkills(sourceUrl) {
5614
- const discovered = await wellKnownProvider.fetchAllSkills(sourceUrl);
5615
- if (discovered.length === 0) {
6018
+ var WellKnownLicenseKeyRequiredError = class extends Error {
6019
+ code = "LICENSE_KEY_REQUIRED";
6020
+ constructor() {
6021
+ super("A license key is required to install this skill.");
6022
+ }
6023
+ };
6024
+ function isStaticEntry(entry) {
6025
+ return "files" in entry;
6026
+ }
6027
+ function isAuthedEntry(entry) {
6028
+ return "content" in entry && !("files" in entry);
6029
+ }
6030
+ function resolveAuthForEndpoint(entry, endpoint) {
6031
+ if (entry.auth?.tokenHeader) {
6032
+ return { tokenHeader: entry.auth.tokenHeader, tokenPrefix: entry.auth.tokenPrefix ?? "" };
6033
+ }
6034
+ const spec = endpoint === "content" ? entry.content : entry.verify;
6035
+ const tokenHeader = spec?.tokenHeader;
6036
+ const tokenPrefix = spec?.tokenPrefix;
6037
+ if (typeof tokenHeader !== "string" || !tokenHeader) {
6038
+ throw new Error(
6039
+ "Invalid well-known index entry: missing auth.tokenHeader (or legacy tokenHeader)."
6040
+ );
6041
+ }
6042
+ return { tokenHeader, tokenPrefix: typeof tokenPrefix === "string" ? tokenPrefix : "" };
6043
+ }
6044
+ async function fetchAuthedManifest(entry, licenseKey) {
6045
+ const contentAuth = resolveAuthForEndpoint(entry, "content");
6046
+ const tokenValue = `${contentAuth.tokenPrefix}${licenseKey}`;
6047
+ if (entry.verify) {
6048
+ const verifyAuth = resolveAuthForEndpoint(entry, "verify");
6049
+ const verifyRes = await fetch(entry.verify.endpoint, {
6050
+ method: entry.verify.method,
6051
+ headers: { [verifyAuth.tokenHeader]: tokenValue }
6052
+ });
6053
+ if (!verifyRes.ok) {
6054
+ const text = await verifyRes.text().catch(() => "");
6055
+ let msg = text;
6056
+ try {
6057
+ const parsed = JSON.parse(text);
6058
+ if (typeof parsed?.error === "string" && parsed.error) {
6059
+ msg = parsed.error;
6060
+ }
6061
+ } catch {
6062
+ }
6063
+ throw new Error(msg || `License verification failed (${verifyRes.status}).`);
6064
+ }
6065
+ }
6066
+ const res = await fetch(entry.content.endpoint, {
6067
+ method: entry.content.method,
6068
+ headers: { [contentAuth.tokenHeader]: tokenValue }
6069
+ });
6070
+ if (!res.ok) {
6071
+ const text = await res.text().catch(() => "");
6072
+ throw new Error(text || `Unable to fetch skill content (${res.status}).`);
6073
+ }
6074
+ const json = await res.json();
6075
+ if (json && typeof json === "object" && "success" in json) {
6076
+ const wrapped = json;
6077
+ if (!wrapped.success) {
6078
+ throw new Error(wrapped.error || "Unable to fetch skill content.");
6079
+ }
6080
+ return wrapped.data;
6081
+ }
6082
+ return json;
6083
+ }
6084
+ async function prepareWellKnownSkills(sourceUrl, options) {
6085
+ const result = await wellKnownProvider.fetchIndex(sourceUrl);
6086
+ if (!result) {
6087
+ throw new Error(
6088
+ "No skills found at this URL. Make sure it exposes /.well-known/skills/index.json."
6089
+ );
6090
+ }
6091
+ const { index, resolvedBaseUrl } = result;
6092
+ const entries = index.skills ?? [];
6093
+ if (!Array.isArray(entries) || entries.length === 0) {
5616
6094
  throw new Error(
5617
6095
  "No skills found at this URL. Make sure it exposes /.well-known/skills/index.json."
5618
6096
  );
@@ -5623,33 +6101,86 @@ async function prepareWellKnownSkills(sourceUrl) {
5623
6101
  const originMap = /* @__PURE__ */ new Map();
5624
6102
  const skills = [];
5625
6103
  const sourceIdentifier = wellKnownProvider.getSourceIdentifier(sourceUrl);
5626
- for (const skill of discovered) {
5627
- const skillDir = join13(tempDir, skill.installName);
6104
+ let contentHash;
6105
+ const authedEntries = entries.filter(isAuthedEntry);
6106
+ const staticEntries = entries.filter(isStaticEntry);
6107
+ if (authedEntries.length > 0) {
6108
+ const licenseKey = options?.licenseKey?.trim();
6109
+ if (!licenseKey) {
6110
+ throw new WellKnownLicenseKeyRequiredError();
6111
+ }
6112
+ const prefixPerEntry = authedEntries.length > 1;
6113
+ for (const entry of authedEntries) {
6114
+ const manifest = await fetchAuthedManifest(entry, licenseKey);
6115
+ const m = manifest;
6116
+ if (!m || typeof m !== "object" || !m.files || typeof m.files !== "object") {
6117
+ throw new Error("Invalid manifest returned from content endpoint.");
6118
+ }
6119
+ if (typeof m.contentHash === "string" && m.contentHash) {
6120
+ contentHash = m.contentHash;
6121
+ }
6122
+ const base = prefixPerEntry ? join13(tempDir, entry.name) : tempDir;
6123
+ if (prefixPerEntry) {
6124
+ await mkdir5(base, { recursive: true });
6125
+ }
6126
+ for (const [filePath, fileContent] of Object.entries(m.files)) {
6127
+ if (typeof fileContent !== "string") continue;
6128
+ const targetPath = join13(base, filePath);
6129
+ if (!isPathSafe(base, targetPath)) {
6130
+ continue;
6131
+ }
6132
+ await mkdir5(dirname4(targetPath), { recursive: true });
6133
+ await writeFile3(targetPath, fileContent, "utf-8");
6134
+ }
6135
+ const discovered = await discoverSkills(base);
6136
+ for (const skill of discovered) {
6137
+ skills.push(skill);
6138
+ const displayName = getSkillDisplayName(skill);
6139
+ const relativeSkillDir = skill.path.startsWith(`${tempDir}/`) ? skill.path.slice(tempDir.length + 1) : null;
6140
+ const relativeSkillMd = relativeSkillDir ? `${relativeSkillDir}/SKILL.md` : null;
6141
+ originMap.set(displayName, {
6142
+ sourceType: "well-known",
6143
+ sourceUrl,
6144
+ source: sourceIdentifier,
6145
+ skillPath: relativeSkillMd ?? void 0
6146
+ });
6147
+ }
6148
+ }
6149
+ }
6150
+ for (const entry of staticEntries) {
6151
+ const fetched = await wellKnownProvider.fetchSkillByEntry(resolvedBaseUrl, entry);
6152
+ if (!fetched) continue;
6153
+ const skillDir = join13(tempDir, fetched.installName);
5628
6154
  await mkdir5(skillDir, { recursive: true });
5629
- for (const [filePath, content] of skill.files.entries()) {
6155
+ for (const [filePath, fileContent] of fetched.files.entries()) {
5630
6156
  const targetPath = join13(skillDir, filePath);
5631
6157
  if (!isPathSafe(skillDir, targetPath)) {
5632
6158
  throw new Error("Invalid file path in well-known skill index.");
5633
6159
  }
5634
6160
  await mkdir5(dirname4(targetPath), { recursive: true });
5635
- await writeFile3(targetPath, content, "utf-8");
6161
+ await writeFile3(targetPath, fileContent, "utf-8");
5636
6162
  }
5637
6163
  const localSkill = {
5638
- name: skill.installName,
5639
- description: skill.description,
6164
+ name: fetched.installName,
6165
+ description: fetched.description,
5640
6166
  path: skillDir,
5641
- rawContent: skill.content,
5642
- metadata: skill.metadata
6167
+ rawContent: fetched.content,
6168
+ metadata: fetched.metadata
5643
6169
  };
5644
6170
  skills.push(localSkill);
5645
6171
  originMap.set(getSkillDisplayName(localSkill), {
5646
6172
  sourceType: "well-known",
5647
- sourceUrl: skill.sourceUrl,
6173
+ sourceUrl: fetched.sourceUrl,
5648
6174
  source: sourceIdentifier,
5649
- skillPath: skill.sourceUrl
6175
+ skillPath: fetched.sourceUrl
5650
6176
  });
5651
6177
  }
5652
- return { tempDir, skills, originBySkillName: originMap };
6178
+ if (skills.length === 0) {
6179
+ throw new Error(
6180
+ "No skills found at this URL. Make sure it exposes /.well-known/skills/index.json."
6181
+ );
6182
+ }
6183
+ return { tempDir, skills, originBySkillName: originMap, contentHash };
5653
6184
  }
5654
6185
 
5655
6186
  // src/marketplace.ts
@@ -5930,36 +6461,8 @@ function resolvePluginSource(plugin, context) {
5930
6461
  return { kind: "unsupported", reason: "Unknown source type", overrides };
5931
6462
  }
5932
6463
 
5933
- // src/tui/utils/skill-selection.ts
5934
- import { basename as basename5 } from "path";
5935
- function matchesSkillName(skill, input) {
5936
- const normalized = input.toLowerCase();
5937
- const byName = skill.name.toLowerCase() === normalized;
5938
- const byPath = basename5(skill.path).toLowerCase() === normalized;
5939
- return byName || byPath;
5940
- }
5941
- function autoSelect(skills, options) {
5942
- if (options.skill && options.skill.length > 0) {
5943
- const selected = skills.filter((s) => options.skill?.some((name) => matchesSkillName(s, name)));
5944
- if (selected.length === 0) {
5945
- return {
5946
- status: "prompt",
5947
- message: `No matching skills found for: ${options.skill.join(", ")}`
5948
- };
5949
- }
5950
- return { status: "selected", skills: selected };
5951
- }
5952
- if (skills.length === 1) {
5953
- return { status: "selected", skills };
5954
- }
5955
- if (options.yes) {
5956
- return { status: "selected", skills };
5957
- }
5958
- return { status: "prompt" };
5959
- }
5960
-
5961
6464
  // src/tui/screens/AddSkillSelect.tsx
5962
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
6465
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
5963
6466
  function AddSkillSelectScreen() {
5964
6467
  const {
5965
6468
  invocation,
@@ -5973,16 +6476,16 @@ function AddSkillSelectScreen() {
5973
6476
  resetAddSkill,
5974
6477
  setLastSource
5975
6478
  } = useNavigation();
5976
- const [status, setStatus] = React15.useState(
6479
+ const [status, setStatus] = React17.useState(
5977
6480
  addSkill.skills && addSkill.skills.length > 0 ? "ready" : "loading"
5978
6481
  );
5979
- const [error, setError] = React15.useState(null);
5980
- const [listMode, setListMode] = React15.useState(false);
5981
- const [showLoading, setShowLoading] = React15.useState(false);
6482
+ const [error, setError] = React17.useState(null);
6483
+ const [listMode, setListMode] = React17.useState(false);
6484
+ const [showLoading, setShowLoading] = React17.useState(false);
5982
6485
  const spinner = useSpinnerFrame(status === "loading");
5983
6486
  const source = addSkill.source ?? invocation.source;
5984
6487
  const options = invocation.options;
5985
- React15.useEffect(() => {
6488
+ React17.useEffect(() => {
5986
6489
  let cancelled = false;
5987
6490
  const load = async () => {
5988
6491
  if (!source) {
@@ -6046,10 +6549,27 @@ function AddSkillSelectScreen() {
6046
6549
  return;
6047
6550
  }
6048
6551
  if (parsed.type === "well-known") {
6049
- const prepared = await prepareWellKnownSkills(parsed.url);
6552
+ const licenseKey = addSkill.licenseKey ?? options.licenseKey ?? parsed.licenseKey;
6553
+ let prepared;
6554
+ try {
6555
+ prepared = await prepareWellKnownSkills(parsed.url, { licenseKey });
6556
+ } catch (err) {
6557
+ if (err instanceof WellKnownLicenseKeyRequiredError) {
6558
+ updateAddSkill({ parsed });
6559
+ navigateTo("add-license-key");
6560
+ return;
6561
+ }
6562
+ throw err;
6563
+ }
6050
6564
  tempDirForCleanup = prepared.tempDir;
6565
+ const nextParsed = {
6566
+ ...parsed,
6567
+ ...licenseKey ? { licenseKey } : {},
6568
+ ...prepared.contentHash ? { contentHash: prepared.contentHash } : {}
6569
+ };
6051
6570
  updateAddSkill({
6052
- parsed,
6571
+ parsed: nextParsed,
6572
+ ...licenseKey ? { licenseKey } : {},
6053
6573
  tempDir: prepared.tempDir,
6054
6574
  skills: prepared.skills,
6055
6575
  originBySkillName: prepared.originBySkillName
@@ -6162,8 +6682,8 @@ function AddSkillSelectScreen() {
6162
6682
  return () => {
6163
6683
  cancelled = true;
6164
6684
  };
6165
- }, [source, addSkill.skills, updateAddSkill, navigateTo, options, setFlash]);
6166
- React15.useEffect(() => {
6685
+ }, [source, addSkill.skills, addSkill.licenseKey, updateAddSkill, navigateTo, options, setFlash]);
6686
+ React17.useEffect(() => {
6167
6687
  if (invocation.source) {
6168
6688
  setBackHandler(() => {
6169
6689
  setLastSource(invocation.source ?? null);
@@ -6179,7 +6699,7 @@ function AddSkillSelectScreen() {
6179
6699
  setBackHandler(null);
6180
6700
  };
6181
6701
  }, [invocation.source, resetTo, setBackHandler, resetAddSkill, setInvocation, setLastSource]);
6182
- React15.useEffect(() => {
6702
+ React17.useEffect(() => {
6183
6703
  if (status !== "loading") {
6184
6704
  setShowLoading(false);
6185
6705
  return;
@@ -6190,18 +6710,18 @@ function AddSkillSelectScreen() {
6190
6710
  return () => clearTimeout(timer);
6191
6711
  }, [status]);
6192
6712
  if (!source) {
6193
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
6194
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Add skills" }),
6195
- /* @__PURE__ */ jsx20(Text16, { children: `Missing source. ${BACK_QUIT_HINT}` })
6713
+ return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6714
+ /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Add skills" }),
6715
+ /* @__PURE__ */ jsx22(Text18, { children: `Missing source. ${BACK_QUIT_HINT}` })
6196
6716
  ] });
6197
6717
  }
6198
6718
  if (status === "loading" && !showLoading) {
6199
- return /* @__PURE__ */ jsx20(Box18, { padding: 1 });
6719
+ return /* @__PURE__ */ jsx22(Box20, { padding: 1 });
6200
6720
  }
6201
6721
  if (status === "loading") {
6202
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
6203
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Scanning skills" }),
6204
- /* @__PURE__ */ jsxs16(Text16, { children: [
6722
+ return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6723
+ /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Scanning skills" }),
6724
+ /* @__PURE__ */ jsxs18(Text18, { children: [
6205
6725
  spinner,
6206
6726
  " Fetching skills from ",
6207
6727
  source
@@ -6209,32 +6729,32 @@ function AddSkillSelectScreen() {
6209
6729
  ] });
6210
6730
  }
6211
6731
  if (status === "error") {
6212
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
6213
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Unable to load skills" }),
6214
- /* @__PURE__ */ jsx20(Text16, { color: "red", children: error }),
6215
- /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT }) })
6732
+ return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6733
+ /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Unable to load skills" }),
6734
+ /* @__PURE__ */ jsx22(Text18, { color: "red", children: error }),
6735
+ /* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: BACK_QUIT_HINT }) })
6216
6736
  ] });
6217
6737
  }
6218
6738
  const skills = addSkill.skills ?? [];
6219
6739
  if (listMode || status === "list") {
6220
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
6221
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: `Available skills (${skills.length})` }),
6222
- skills.map((skill) => /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", marginBottom: 1, children: [
6223
- /* @__PURE__ */ jsx20(Text16, { children: getSkillDisplayName(skill) }),
6224
- skill.description ? /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: skill.description }) : null
6740
+ return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6741
+ /* @__PURE__ */ jsx22(AddFlowHeader, { title: `Available skills (${skills.length})` }),
6742
+ skills.map((skill) => /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", marginBottom: 1, children: [
6743
+ /* @__PURE__ */ jsx22(Text18, { children: getSkillDisplayName(skill) }),
6744
+ skill.description ? /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: skill.description }) : null
6225
6745
  ] }, skill.name)),
6226
- /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT })
6746
+ /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: BACK_QUIT_HINT })
6227
6747
  ] });
6228
6748
  }
6229
6749
  if (skills.length === 0) {
6230
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
6231
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "No skills found" }),
6232
- /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT })
6750
+ return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6751
+ /* @__PURE__ */ jsx22(AddFlowHeader, { title: "No skills found" }),
6752
+ /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: BACK_QUIT_HINT })
6233
6753
  ] });
6234
6754
  }
6235
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
6236
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Select skills" }),
6237
- /* @__PURE__ */ jsx20(
6755
+ return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6756
+ /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Select skills" }),
6757
+ /* @__PURE__ */ jsx22(
6238
6758
  MultiSelect,
6239
6759
  {
6240
6760
  items: skills.map((skill) => ({
@@ -6266,22 +6786,22 @@ function AddSkillSelectScreen() {
6266
6786
  }
6267
6787
 
6268
6788
  // src/tui/screens/AddSource.tsx
6269
- import { Box as Box19, Text as Text17 } from "ink";
6270
- import TextInput2 from "ink-text-input";
6271
- import React16 from "react";
6272
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
6789
+ import { Box as Box21, Text as Text19 } from "ink";
6790
+ import TextInput3 from "ink-text-input";
6791
+ import React18 from "react";
6792
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
6273
6793
  function AddSourceScreen() {
6274
6794
  const { invocation, addSkill, updateAddSkill, navigateTo, setFlash, lastSource, setLastSource } = useNavigation();
6275
- const [value, setValue] = React16.useState(
6795
+ const [value, setValue] = React18.useState(
6276
6796
  addSkill.source ?? invocation.source ?? lastSource ?? ""
6277
6797
  );
6278
- const didAutofillRef = React16.useRef(false);
6798
+ const didAutofillRef = React18.useRef(false);
6279
6799
  const { wrapOnChange } = useTextInput({
6280
6800
  onClear: () => {
6281
6801
  setValue("");
6282
6802
  }
6283
6803
  });
6284
- React16.useEffect(() => {
6804
+ React18.useEffect(() => {
6285
6805
  const preset = invocation.source;
6286
6806
  if (!preset || didAutofillRef.current) return;
6287
6807
  didAutofillRef.current = true;
@@ -6298,31 +6818,31 @@ function AddSourceScreen() {
6298
6818
  updateAddSkill({ source: trimmed });
6299
6819
  navigateTo("add-skill-select");
6300
6820
  };
6301
- return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
6302
- /* @__PURE__ */ jsx21(Header, { title: "Add skills" }),
6303
- /* @__PURE__ */ jsx21(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text17, { children: "Where should we fetch skills from?" }) }),
6304
- /* @__PURE__ */ jsx21(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: "Examples: owner/repo, https://.../SKILL.md, ./local/path" }) }),
6305
- /* @__PURE__ */ jsxs17(Box19, { children: [
6306
- /* @__PURE__ */ jsx21(Text17, { color: "green", children: "> " }),
6307
- /* @__PURE__ */ jsx21(TextInput2, { value, onChange: wrapOnChange(setValue), onSubmit })
6821
+ return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6822
+ /* @__PURE__ */ jsx23(Header, { title: "Add skills" }),
6823
+ /* @__PURE__ */ jsx23(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx23(Text19, { children: "Where should we fetch skills from?" }) }),
6824
+ /* @__PURE__ */ jsx23(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "Examples: owner/repo, https://.../SKILL.md, ./local/path" }) }),
6825
+ /* @__PURE__ */ jsxs19(Box21, { children: [
6826
+ /* @__PURE__ */ jsx23(Text19, { color: "green", children: "> " }),
6827
+ /* @__PURE__ */ jsx23(TextInput3, { value, onChange: wrapOnChange(setValue), onSubmit })
6308
6828
  ] }),
6309
- /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: TEXT_INPUT_HINT }) })
6829
+ /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: TEXT_INPUT_HINT }) })
6310
6830
  ] });
6311
6831
  }
6312
6832
 
6313
6833
  // src/tui/screens/AddTargets.tsx
6314
- import { Box as Box20, Text as Text18 } from "ink";
6315
- import React17 from "react";
6316
- import { Fragment as Fragment2, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
6834
+ import { Box as Box22, Text as Text20 } from "ink";
6835
+ import React19 from "react";
6836
+ import { Fragment as Fragment2, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
6317
6837
  function AddTargetsScreen() {
6318
6838
  const { invocation, addSkill, updateAddSkill, navigateTo, setFlash, navAction } = useNavigation();
6319
- const [status, setStatus] = React17.useState("loading");
6320
- const [mode, setMode] = React17.useState("choice");
6321
- const [availableAgents, setAvailableAgents] = React17.useState([]);
6322
- const [lastSelected, setLastSelected] = React17.useState([]);
6323
- const [showLoading, setShowLoading] = React17.useState(false);
6839
+ const [status, setStatus] = React19.useState("loading");
6840
+ const [mode, setMode] = React19.useState("choice");
6841
+ const [availableAgents, setAvailableAgents] = React19.useState([]);
6842
+ const [lastSelected, setLastSelected] = React19.useState([]);
6843
+ const [showLoading, setShowLoading] = React19.useState(false);
6324
6844
  const spinner = useSpinnerFrame(status === "loading");
6325
- React17.useEffect(() => {
6845
+ React19.useEffect(() => {
6326
6846
  let cancelled = false;
6327
6847
  const run = async () => {
6328
6848
  setStatus("loading");
@@ -6371,7 +6891,7 @@ function AddTargetsScreen() {
6371
6891
  cancelled = true;
6372
6892
  };
6373
6893
  }, [invocation.options, updateAddSkill, navigateTo, addSkill.targetAgents, setFlash, navAction]);
6374
- React17.useEffect(() => {
6894
+ React19.useEffect(() => {
6375
6895
  if (status !== "loading") {
6376
6896
  setShowLoading(false);
6377
6897
  return;
@@ -6382,12 +6902,12 @@ function AddTargetsScreen() {
6382
6902
  return () => clearTimeout(timer);
6383
6903
  }, [status]);
6384
6904
  if (status === "loading" && !showLoading) {
6385
- return /* @__PURE__ */ jsx22(Box20, { padding: 1 });
6905
+ return /* @__PURE__ */ jsx24(Box22, { padding: 1 });
6386
6906
  }
6387
6907
  if (status === "loading") {
6388
- return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6389
- /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Detecting agents" }),
6390
- /* @__PURE__ */ jsxs18(Text18, { children: [
6908
+ return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", padding: 1, children: [
6909
+ /* @__PURE__ */ jsx24(AddFlowHeader, { title: "Detecting agents" }),
6910
+ /* @__PURE__ */ jsxs20(Text20, { children: [
6391
6911
  spinner,
6392
6912
  " Checking installed agents..."
6393
6913
  ] })
@@ -6409,9 +6929,9 @@ function AddTargetsScreen() {
6409
6929
  label: agents[agent].displayName,
6410
6930
  hint: agents[agent].skillsDir
6411
6931
  }));
6412
- return /* @__PURE__ */ jsx22(Box20, { flexDirection: "column", padding: 1, children: mode === "choice" ? /* @__PURE__ */ jsxs18(Fragment2, { children: [
6413
- /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Install to" }),
6414
- /* @__PURE__ */ jsx22(
6932
+ return /* @__PURE__ */ jsx24(Box22, { flexDirection: "column", padding: 1, children: mode === "choice" ? /* @__PURE__ */ jsxs20(Fragment2, { children: [
6933
+ /* @__PURE__ */ jsx24(AddFlowHeader, { title: "Install to" }),
6934
+ /* @__PURE__ */ jsx24(
6415
6935
  SingleSelect,
6416
6936
  {
6417
6937
  items: [
@@ -6444,9 +6964,9 @@ function AddTargetsScreen() {
6444
6964
  }
6445
6965
  }
6446
6966
  )
6447
- ] }) : /* @__PURE__ */ jsxs18(Fragment2, { children: [
6448
- /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Select agents" }),
6449
- /* @__PURE__ */ jsx22(
6967
+ ] }) : /* @__PURE__ */ jsxs20(Fragment2, { children: [
6968
+ /* @__PURE__ */ jsx24(AddFlowHeader, { title: "Select agents" }),
6969
+ /* @__PURE__ */ jsx24(
6450
6970
  MultiSelect,
6451
6971
  {
6452
6972
  items: selectableItems,
@@ -6478,9 +6998,9 @@ function AddTargetsScreen() {
6478
6998
  }
6479
6999
 
6480
7000
  // src/tui/screens/FindSkillResults.tsx
6481
- import { Box as Box21, Text as Text19 } from "ink";
6482
- import React18 from "react";
6483
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
7001
+ import { Box as Box23, Text as Text21 } from "ink";
7002
+ import React20 from "react";
7003
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
6484
7004
  var formatStars = (value) => {
6485
7005
  if (!value || value <= 0) return "";
6486
7006
  if (value >= 1e3) {
@@ -6509,8 +7029,8 @@ var truncateLabel = (value, max = 100) => {
6509
7029
  };
6510
7030
  function FindSkillResultsScreen() {
6511
7031
  const { findSkill, updateFindSkill, resetAddSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
6512
- const [status, setStatus] = React18.useState("ready");
6513
- const [error, setError] = React18.useState(null);
7032
+ const [status, setStatus] = React20.useState("ready");
7033
+ const [error, setError] = React20.useState(null);
6514
7034
  const spinner = useSpinnerFrame(status === "loading");
6515
7035
  const results = findSkill.results ?? [];
6516
7036
  const query = findSkill.query ?? "";
@@ -6548,36 +7068,36 @@ function FindSkillResultsScreen() {
6548
7068
  setStatus("error");
6549
7069
  }
6550
7070
  };
6551
- React18.useEffect(() => {
7071
+ React20.useEffect(() => {
6552
7072
  if (results.length === 0) {
6553
7073
  setStatus("ready");
6554
7074
  }
6555
7075
  }, [results.length]);
6556
7076
  if (status === "loading") {
6557
- return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6558
- /* @__PURE__ */ jsx23(Header, { title: "Preparing skills" }),
6559
- /* @__PURE__ */ jsxs19(Text19, { children: [
7077
+ return /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", padding: 1, children: [
7078
+ /* @__PURE__ */ jsx25(Header, { title: "Preparing skills" }),
7079
+ /* @__PURE__ */ jsxs21(Text21, { children: [
6560
7080
  spinner,
6561
7081
  " Cloning repositories..."
6562
7082
  ] })
6563
7083
  ] });
6564
7084
  }
6565
7085
  if (results.length === 0) {
6566
- return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6567
- /* @__PURE__ */ jsx23(Header, { title: "No results" }),
6568
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "No skills found." }),
6569
- /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: BACK_QUIT_HINT }) })
7086
+ return /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", padding: 1, children: [
7087
+ /* @__PURE__ */ jsx25(Header, { title: "No results" }),
7088
+ /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: "No skills found." }),
7089
+ /* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: BACK_QUIT_HINT }) })
6570
7090
  ] });
6571
7091
  }
6572
- return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6573
- /* @__PURE__ */ jsx23(Header, { title: "Select skills" }),
6574
- /* @__PURE__ */ jsxs19(Box21, { marginBottom: 1, flexDirection: "column", children: [
6575
- /* @__PURE__ */ jsx23(Text19, { children: `Query: ${query}` }),
6576
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `Mode: ${modeLabel} search` }),
6577
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: FIND_SKILLS_HINT })
7092
+ return /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", padding: 1, children: [
7093
+ /* @__PURE__ */ jsx25(Header, { title: "Select skills" }),
7094
+ /* @__PURE__ */ jsxs21(Box23, { marginBottom: 1, flexDirection: "column", children: [
7095
+ /* @__PURE__ */ jsx25(Text21, { children: `Query: ${query}` }),
7096
+ /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: `Mode: ${modeLabel} search` }),
7097
+ /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: FIND_SKILLS_HINT })
6578
7098
  ] }),
6579
- status === "error" ? /* @__PURE__ */ jsx23(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx23(Text19, { color: "red", children: error }) }) : null,
6580
- /* @__PURE__ */ jsx23(
7099
+ status === "error" ? /* @__PURE__ */ jsx25(Box23, { marginBottom: 1, children: /* @__PURE__ */ jsx25(Text21, { color: "red", children: error }) }) : null,
7100
+ /* @__PURE__ */ jsx25(
6581
7101
  MultiSelect,
6582
7102
  {
6583
7103
  items: results.map((result) => ({
@@ -6595,21 +7115,21 @@ function FindSkillResultsScreen() {
6595
7115
  }
6596
7116
 
6597
7117
  // src/tui/screens/FindSkillSearch.tsx
6598
- import { Box as Box22, Text as Text20, useInput as useInput5 } from "ink";
6599
- import TextInput3 from "ink-text-input";
6600
- import React19 from "react";
6601
- import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
7118
+ import { Box as Box24, Text as Text22, useInput as useInput5 } from "ink";
7119
+ import TextInput4 from "ink-text-input";
7120
+ import React21 from "react";
7121
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
6602
7122
  var MIN_QUERY_LENGTH = 2;
6603
7123
  function getDebounceMs(queryLength) {
6604
7124
  return Math.max(150, 350 - queryLength * 50);
6605
7125
  }
6606
7126
  function FindSkillSearchScreen() {
6607
7127
  const { findSkill, updateFindSkill, navigateTo, setFlash } = useNavigation();
6608
- const [value, setValue] = React19.useState(findSkill.query ?? "");
6609
- const [status, setStatus] = React19.useState("idle");
6610
- const [error, setError] = React19.useState(null);
6611
- const [preview, setPreview] = React19.useState([]);
6612
- const debounceRef = React19.useRef(null);
7128
+ const [value, setValue] = React21.useState(findSkill.query ?? "");
7129
+ const [status, setStatus] = React21.useState("idle");
7130
+ const [error, setError] = React21.useState(null);
7131
+ const [preview, setPreview] = React21.useState([]);
7132
+ const debounceRef = React21.useRef(null);
6613
7133
  const spinner = useSpinnerFrame(status === "searching" || status === "loading");
6614
7134
  const { wrapOnChange } = useTextInput({
6615
7135
  disabled: status === "loading",
@@ -6620,7 +7140,7 @@ function FindSkillSearchScreen() {
6620
7140
  updateFindSkill({ query: "" });
6621
7141
  }
6622
7142
  });
6623
- const triggerLiveSearch = React19.useCallback((query) => {
7143
+ const triggerLiveSearch = React21.useCallback((query) => {
6624
7144
  if (debounceRef.current) {
6625
7145
  clearTimeout(debounceRef.current);
6626
7146
  debounceRef.current = null;
@@ -6644,14 +7164,14 @@ function FindSkillSearchScreen() {
6644
7164
  }
6645
7165
  }, debounceMs);
6646
7166
  }, []);
6647
- React19.useEffect(() => {
7167
+ React21.useEffect(() => {
6648
7168
  return () => {
6649
7169
  if (debounceRef.current) {
6650
7170
  clearTimeout(debounceRef.current);
6651
7171
  }
6652
7172
  };
6653
7173
  }, []);
6654
- const goToLexicalResults = React19.useCallback(async () => {
7174
+ const goToLexicalResults = React21.useCallback(async () => {
6655
7175
  const query = value.trim();
6656
7176
  if (!query || query.length < MIN_QUERY_LENGTH) {
6657
7177
  setFlash(`Enter at least ${MIN_QUERY_LENGTH} characters.`);
@@ -6678,7 +7198,7 @@ function FindSkillSearchScreen() {
6678
7198
  setStatus("error");
6679
7199
  }
6680
7200
  }, [value, setFlash, updateFindSkill, navigateTo]);
6681
- const runSemanticSearch = React19.useCallback(async () => {
7201
+ const runSemanticSearch = React21.useCallback(async () => {
6682
7202
  const query = value.trim();
6683
7203
  if (!query) {
6684
7204
  setFlash("Enter a search term.");
@@ -6723,13 +7243,13 @@ function FindSkillSearchScreen() {
6723
7243
  });
6724
7244
  const showPreview = preview.length > 0 && status !== "loading";
6725
7245
  const showSearching = status === "searching" && value.trim().length >= MIN_QUERY_LENGTH;
6726
- return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", padding: 1, children: [
6727
- /* @__PURE__ */ jsx24(Header, { title: "Find skills" }),
6728
- /* @__PURE__ */ jsx24(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsx24(Text20, { children: "Find a skill to give your agent new capabilities." }) }),
6729
- /* @__PURE__ */ jsxs20(Box22, { children: [
6730
- /* @__PURE__ */ jsx24(Text20, { color: "green", children: "> " }),
6731
- /* @__PURE__ */ jsx24(
6732
- TextInput3,
7246
+ return /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", padding: 1, children: [
7247
+ /* @__PURE__ */ jsx26(Header, { title: "Find skills" }),
7248
+ /* @__PURE__ */ jsx26(Box24, { marginBottom: 1, children: /* @__PURE__ */ jsx26(Text22, { children: "Find a skill to give your agent new capabilities." }) }),
7249
+ /* @__PURE__ */ jsxs22(Box24, { children: [
7250
+ /* @__PURE__ */ jsx26(Text22, { color: "green", children: "> " }),
7251
+ /* @__PURE__ */ jsx26(
7252
+ TextInput4,
6733
7253
  {
6734
7254
  value,
6735
7255
  onChange: wrapOnChange((next) => {
@@ -6746,52 +7266,52 @@ function FindSkillSearchScreen() {
6746
7266
  }
6747
7267
  )
6748
7268
  ] }),
6749
- showSearching && !showPreview ? /* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
7269
+ showSearching && !showPreview ? /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs22(Text22, { dimColor: true, children: [
6750
7270
  spinner,
6751
7271
  " Searching..."
6752
7272
  ] }) }) : null,
6753
- showPreview ? /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", marginTop: 1, children: [
6754
- /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
7273
+ showPreview ? /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", marginTop: 1, children: [
7274
+ /* @__PURE__ */ jsxs22(Text22, { dimColor: true, children: [
6755
7275
  preview.length,
6756
7276
  " result",
6757
7277
  preview.length !== 1 ? "s" : "",
6758
7278
  " found:"
6759
7279
  ] }),
6760
- preview.slice(0, 3).map((result) => /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
7280
+ preview.slice(0, 3).map((result) => /* @__PURE__ */ jsxs22(Text22, { dimColor: true, children: [
6761
7281
  " ",
6762
7282
  "\u2022 ",
6763
7283
  result.name,
6764
7284
  result.repoOwner ? ` (${result.repoOwner}/${result.repoName})` : ""
6765
7285
  ] }, result.id)),
6766
- preview.length > 3 ? /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
7286
+ preview.length > 3 ? /* @__PURE__ */ jsxs22(Text22, { dimColor: true, children: [
6767
7287
  " ",
6768
7288
  "... and ",
6769
7289
  preview.length - 3,
6770
7290
  " more"
6771
7291
  ] }) : null
6772
7292
  ] }) : null,
6773
- status === "loading" ? /* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { children: [
7293
+ status === "loading" ? /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs22(Text22, { children: [
6774
7294
  spinner,
6775
7295
  " Running AI search..."
6776
7296
  ] }) }) : null,
6777
- status === "error" ? /* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text20, { color: "red", children: error }) }) : null,
6778
- /* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "Enter for fast results, Tab for AI search" }) }),
6779
- /* @__PURE__ */ jsx24(Box22, { children: /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: TEXT_INPUT_HINT }) })
7297
+ status === "error" ? /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsx26(Text22, { color: "red", children: error }) }) : null,
7298
+ /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: "Enter for fast results, Tab for AI search" }) }),
7299
+ /* @__PURE__ */ jsx26(Box24, { children: /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: TEXT_INPUT_HINT }) })
6780
7300
  ] });
6781
7301
  }
6782
7302
 
6783
7303
  // src/tui/screens/GetUrl.tsx
6784
- import { Box as Box23, Text as Text21, useApp } from "ink";
6785
- import React20 from "react";
6786
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
7304
+ import { Box as Box25, Text as Text23, useApp } from "ink";
7305
+ import React22 from "react";
7306
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
6787
7307
  function GetUrlScreen() {
6788
7308
  const { exit } = useApp();
6789
7309
  const { invocation } = useNavigation();
6790
7310
  const spinner = useSpinnerFrame(true);
6791
- const didRun = React20.useRef(false);
7311
+ const didRun = React22.useRef(false);
6792
7312
  const outputPath = invocation.options?.output ?? null;
6793
7313
  const outputFormat = invocation.options?.json ? "json" : "markdown";
6794
- React20.useEffect(() => {
7314
+ React22.useEffect(() => {
6795
7315
  let cancelled = false;
6796
7316
  const run = async () => {
6797
7317
  if (didRun.current) return;
@@ -6820,12 +7340,12 @@ function GetUrlScreen() {
6820
7340
  cancelled = true;
6821
7341
  };
6822
7342
  }, [exit, invocation]);
6823
- return /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", padding: 1, children: [
6824
- /* @__PURE__ */ jsx25(Header, { title: "Fetching URL" }),
6825
- invocation.source ? /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: `URL: ${invocation.source}` }) : null,
6826
- outputPath ? /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: `Output: ${outputPath}` }) : null,
6827
- outputFormat === "json" ? /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: "Format: json" }) : null,
6828
- /* @__PURE__ */ jsxs21(Text21, { children: [
7343
+ return /* @__PURE__ */ jsxs23(Box25, { flexDirection: "column", padding: 1, children: [
7344
+ /* @__PURE__ */ jsx27(Header, { title: "Fetching URL" }),
7345
+ invocation.source ? /* @__PURE__ */ jsx27(Text23, { dimColor: true, children: `URL: ${invocation.source}` }) : null,
7346
+ outputPath ? /* @__PURE__ */ jsx27(Text23, { dimColor: true, children: `Output: ${outputPath}` }) : null,
7347
+ outputFormat === "json" ? /* @__PURE__ */ jsx27(Text23, { dimColor: true, children: "Format: json" }) : null,
7348
+ /* @__PURE__ */ jsxs23(Text23, { children: [
6829
7349
  spinner,
6830
7350
  " Fetching markdown..."
6831
7351
  ] })
@@ -6833,12 +7353,12 @@ function GetUrlScreen() {
6833
7353
  }
6834
7354
 
6835
7355
  // src/tui/screens/ListSkills.tsx
6836
- import { Box as Box24, Text as Text22 } from "ink";
6837
- import React21 from "react";
7356
+ import { Box as Box26, Text as Text24 } from "ink";
7357
+ import React23 from "react";
6838
7358
 
6839
7359
  // src/installed-skills.ts
6840
7360
  import { lstat as lstat2, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
6841
- import { basename as basename6, join as join16 } from "path";
7361
+ import { basename as basename7, join as join16 } from "path";
6842
7362
  import matter8 from "gray-matter";
6843
7363
  function getAgentSkillsDir(agent, scope, cwd) {
6844
7364
  const config = agents[agent];
@@ -6926,7 +7446,7 @@ async function listSkillsForAgents(agentsList, scopes, cwd = process.cwd()) {
6926
7446
  }
6927
7447
  async function findSkillInstallations(skillName, scope, cwd = process.cwd()) {
6928
7448
  const installs = [];
6929
- const sanitized = basename6(getCanonicalPath(skillName, { global: scope === "global", cwd }));
7449
+ const sanitized = basename7(getCanonicalPath(skillName, { global: scope === "global", cwd }));
6930
7450
  if (!sanitized) {
6931
7451
  return installs;
6932
7452
  }
@@ -6949,12 +7469,12 @@ async function findSkillInstallations(skillName, scope, cwd = process.cwd()) {
6949
7469
  }
6950
7470
 
6951
7471
  // src/tui/screens/ListSkills.tsx
6952
- import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
7472
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
6953
7473
  function ListScreen() {
6954
7474
  const { navigateTo, setBackHandler } = useNavigation();
6955
- const [summaries, setSummaries] = React21.useState([]);
6956
- const [selectedAgent, setSelectedAgent] = React21.useState(null);
6957
- React21.useEffect(() => {
7475
+ const [summaries, setSummaries] = React23.useState([]);
7476
+ const [selectedAgent, setSelectedAgent] = React23.useState(null);
7477
+ React23.useEffect(() => {
6958
7478
  let cancelled = false;
6959
7479
  const load = async () => {
6960
7480
  const installed = await detectInstalledAgents();
@@ -6977,7 +7497,7 @@ function ListScreen() {
6977
7497
  cancelled = true;
6978
7498
  };
6979
7499
  }, []);
6980
- React21.useEffect(() => {
7500
+ React23.useEffect(() => {
6981
7501
  if (!selectedAgent) {
6982
7502
  setBackHandler(null);
6983
7503
  return;
@@ -6992,17 +7512,17 @@ function ListScreen() {
6992
7512
  }, [selectedAgent, setBackHandler]);
6993
7513
  if (selectedAgent) {
6994
7514
  const total = selectedAgent.projectSkills.length + selectedAgent.globalSkills.length;
6995
- return /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", padding: 1, children: [
6996
- /* @__PURE__ */ jsx26(Header, { title: `${agents[selectedAgent.agent].displayName} (${total})` }),
6997
- selectedAgent.projectSkills.length > 0 ? /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", marginBottom: 1, children: [
6998
- /* @__PURE__ */ jsx26(Text22, { children: "Project" }),
6999
- selectedAgent.projectSkills.map((name) => /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: name }, `p-${name}`))
7515
+ return /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", padding: 1, children: [
7516
+ /* @__PURE__ */ jsx28(Header, { title: `${agents[selectedAgent.agent].displayName} (${total})` }),
7517
+ selectedAgent.projectSkills.length > 0 ? /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", marginBottom: 1, children: [
7518
+ /* @__PURE__ */ jsx28(Text24, { children: "Project" }),
7519
+ selectedAgent.projectSkills.map((name) => /* @__PURE__ */ jsx28(Text24, { dimColor: true, children: name }, `p-${name}`))
7000
7520
  ] }) : null,
7001
- selectedAgent.globalSkills.length > 0 ? /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", marginBottom: 1, children: [
7002
- /* @__PURE__ */ jsx26(Text22, { children: "Global" }),
7003
- selectedAgent.globalSkills.map((name) => /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: name }, `g-${name}`))
7521
+ selectedAgent.globalSkills.length > 0 ? /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", marginBottom: 1, children: [
7522
+ /* @__PURE__ */ jsx28(Text24, { children: "Global" }),
7523
+ selectedAgent.globalSkills.map((name) => /* @__PURE__ */ jsx28(Text24, { dimColor: true, children: name }, `g-${name}`))
7004
7524
  ] }) : null,
7005
- /* @__PURE__ */ jsx26(
7525
+ /* @__PURE__ */ jsx28(
7006
7526
  SelectMenu,
7007
7527
  {
7008
7528
  items: [
@@ -7029,9 +7549,9 @@ function ListScreen() {
7029
7549
  hint: `${count} skill${count !== 1 ? "s" : ""}`
7030
7550
  };
7031
7551
  });
7032
- return /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", padding: 1, children: [
7033
- /* @__PURE__ */ jsx26(Header, { title: "Installed skills" }),
7034
- /* @__PURE__ */ jsx26(
7552
+ return /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", padding: 1, children: [
7553
+ /* @__PURE__ */ jsx28(Header, { title: "Installed skills" }),
7554
+ /* @__PURE__ */ jsx28(
7035
7555
  SelectMenu,
7036
7556
  {
7037
7557
  items,
@@ -7048,8 +7568,8 @@ function ListScreen() {
7048
7568
  }
7049
7569
 
7050
7570
  // src/tui/screens/MainMenu.tsx
7051
- import { Box as Box25 } from "ink";
7052
- import { jsx as jsx27 } from "react/jsx-runtime";
7571
+ import { Box as Box27 } from "ink";
7572
+ import { jsx as jsx29 } from "react/jsx-runtime";
7053
7573
  function MainMenu() {
7054
7574
  const { navigateTo, resetAddSkill, resetFindSkill, setInvocation } = useNavigation();
7055
7575
  const items = [
@@ -7063,7 +7583,7 @@ function MainMenu() {
7063
7583
  { label: "Update docs", value: "update-docs" },
7064
7584
  { label: "Exit", value: "exit" }
7065
7585
  ];
7066
- return /* @__PURE__ */ jsx27(Box25, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx27(
7586
+ return /* @__PURE__ */ jsx29(Box27, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx29(
7067
7587
  SelectMenu,
7068
7588
  {
7069
7589
  items,
@@ -7118,16 +7638,16 @@ function MainMenu() {
7118
7638
  // src/tui/screens/ManageSkills.tsx
7119
7639
  import { rm as rm5 } from "fs/promises";
7120
7640
  import chalk4 from "chalk";
7121
- import { Box as Box26, Text as Text23 } from "ink";
7122
- import React22 from "react";
7123
- import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
7641
+ import { Box as Box28, Text as Text25 } from "ink";
7642
+ import React24 from "react";
7643
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
7124
7644
  function ManageScreen() {
7125
7645
  const { navigateTo, setFlash, setBackHandler } = useNavigation();
7126
- const [summaries, setSummaries] = React22.useState([]);
7127
- const [selectedAgent, setSelectedAgent] = React22.useState(null);
7128
- const [selectedSkills, setSelectedSkills] = React22.useState(null);
7129
- const [isRemoving, setIsRemoving] = React22.useState(false);
7130
- React22.useEffect(() => {
7646
+ const [summaries, setSummaries] = React24.useState([]);
7647
+ const [selectedAgent, setSelectedAgent] = React24.useState(null);
7648
+ const [selectedSkills, setSelectedSkills] = React24.useState(null);
7649
+ const [isRemoving, setIsRemoving] = React24.useState(false);
7650
+ React24.useEffect(() => {
7131
7651
  let cancelled = false;
7132
7652
  const load = async () => {
7133
7653
  const installedAgents = await detectInstalledAgents();
@@ -7149,7 +7669,7 @@ function ManageScreen() {
7149
7669
  cancelled = true;
7150
7670
  };
7151
7671
  }, [setFlash]);
7152
- React22.useEffect(() => {
7672
+ React24.useEffect(() => {
7153
7673
  if (isRemoving) {
7154
7674
  setBackHandler(() => true);
7155
7675
  return () => setBackHandler(null);
@@ -7172,16 +7692,16 @@ function ManageScreen() {
7172
7692
  return () => setBackHandler(null);
7173
7693
  }, [isRemoving, selectedSkills, selectedAgent, setBackHandler]);
7174
7694
  if (summaries.length === 0) {
7175
- return /* @__PURE__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
7176
- /* @__PURE__ */ jsx28(Header, { title: "Remove skills" }),
7177
- /* @__PURE__ */ jsx28(Text23, { dimColor: true, children: "No skills found yet." }),
7178
- /* @__PURE__ */ jsx28(Text23, { dimColor: true, children: BACK_QUIT_HINT })
7695
+ return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7696
+ /* @__PURE__ */ jsx30(Header, { title: "Remove skills" }),
7697
+ /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: "No skills found yet." }),
7698
+ /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: BACK_QUIT_HINT })
7179
7699
  ] });
7180
7700
  }
7181
7701
  if (!selectedAgent) {
7182
- return /* @__PURE__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
7183
- /* @__PURE__ */ jsx28(Header, { title: "Select agent" }),
7184
- /* @__PURE__ */ jsx28(
7702
+ return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7703
+ /* @__PURE__ */ jsx30(Header, { title: "Select agent" }),
7704
+ /* @__PURE__ */ jsx30(
7185
7705
  SelectMenu,
7186
7706
  {
7187
7707
  items: summaries.map((summary) => ({
@@ -7198,9 +7718,9 @@ function ManageScreen() {
7198
7718
  ] });
7199
7719
  }
7200
7720
  if (!selectedSkills) {
7201
- return /* @__PURE__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
7202
- /* @__PURE__ */ jsx28(Header, { title: `Remove skills (${agents[selectedAgent.agent].displayName})` }),
7203
- /* @__PURE__ */ jsx28(
7721
+ return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7722
+ /* @__PURE__ */ jsx30(Header, { title: `Remove skills (${agents[selectedAgent.agent].displayName})` }),
7723
+ /* @__PURE__ */ jsx30(
7204
7724
  MultiSelect,
7205
7725
  {
7206
7726
  items: selectedAgent.skills.map((skill) => ({
@@ -7220,19 +7740,19 @@ function ManageScreen() {
7220
7740
  ] });
7221
7741
  }
7222
7742
  if (isRemoving) {
7223
- return /* @__PURE__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
7224
- /* @__PURE__ */ jsx28(Header, { title: "Removing skills" }),
7225
- /* @__PURE__ */ jsx28(Text23, { children: "Removing selected skills..." })
7743
+ return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7744
+ /* @__PURE__ */ jsx30(Header, { title: "Removing skills" }),
7745
+ /* @__PURE__ */ jsx30(Text25, { children: "Removing selected skills..." })
7226
7746
  ] });
7227
7747
  }
7228
- return /* @__PURE__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
7229
- /* @__PURE__ */ jsx28(Header, { title: "Confirm removal" }),
7230
- selectedSkills.map((skill) => /* @__PURE__ */ jsxs23(Text23, { children: [
7748
+ return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7749
+ /* @__PURE__ */ jsx30(Header, { title: "Confirm removal" }),
7750
+ selectedSkills.map((skill) => /* @__PURE__ */ jsxs25(Text25, { children: [
7231
7751
  formatSkillLabel(skill),
7232
7752
  " ",
7233
7753
  chalk4.dim(`(${skill.scope})`)
7234
7754
  ] }, `${skill.slug}-${skill.scope}`)),
7235
- /* @__PURE__ */ jsx28(
7755
+ /* @__PURE__ */ jsx30(
7236
7756
  SelectMenu,
7237
7757
  {
7238
7758
  items: [
@@ -7295,14 +7815,14 @@ function formatSkillLabel(skill) {
7295
7815
  }
7296
7816
 
7297
7817
  // src/tui/screens/MarketplacePlugins.tsx
7298
- import { Box as Box27, Text as Text24 } from "ink";
7299
- import React23 from "react";
7300
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
7818
+ import { Box as Box29, Text as Text26 } from "ink";
7819
+ import React25 from "react";
7820
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
7301
7821
  function MarketplacePluginScreen() {
7302
7822
  const { invocation, addSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
7303
7823
  const plugins = addSkill.marketplace?.plugins ?? [];
7304
7824
  const options = invocation.options;
7305
- React23.useEffect(() => {
7825
+ React25.useEffect(() => {
7306
7826
  if (plugins.length === 0) {
7307
7827
  return;
7308
7828
  }
@@ -7317,25 +7837,25 @@ function MarketplacePluginScreen() {
7317
7837
  }
7318
7838
  }, [plugins, options.yes, updateAddSkill, navigateTo, addSkill.marketplace]);
7319
7839
  if (plugins.length === 0) {
7320
- return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
7321
- /* @__PURE__ */ jsx29(AddFlowHeader, { title: "Marketplace plugins" }),
7322
- /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: "No plugins found." }),
7323
- /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: BACK_QUIT_HINT })
7840
+ return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7841
+ /* @__PURE__ */ jsx31(AddFlowHeader, { title: "Marketplace plugins" }),
7842
+ /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "No plugins found." }),
7843
+ /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT })
7324
7844
  ] });
7325
7845
  }
7326
7846
  if (options.list) {
7327
- return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
7328
- /* @__PURE__ */ jsx29(AddFlowHeader, { title: `Marketplace plugins (${plugins.length})` }),
7329
- plugins.map((plugin) => /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", marginBottom: 1, children: [
7330
- /* @__PURE__ */ jsx29(Text24, { children: plugin.name }),
7331
- plugin.description ? /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: plugin.description }) : null
7847
+ return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7848
+ /* @__PURE__ */ jsx31(AddFlowHeader, { title: `Marketplace plugins (${plugins.length})` }),
7849
+ plugins.map((plugin) => /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", marginBottom: 1, children: [
7850
+ /* @__PURE__ */ jsx31(Text26, { children: plugin.name }),
7851
+ plugin.description ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: plugin.description }) : null
7332
7852
  ] }, plugin.name)),
7333
- /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: BACK_QUIT_HINT })
7853
+ /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT })
7334
7854
  ] });
7335
7855
  }
7336
- return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
7337
- /* @__PURE__ */ jsx29(AddFlowHeader, { title: "Select plugins" }),
7338
- /* @__PURE__ */ jsx29(
7856
+ return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7857
+ /* @__PURE__ */ jsx31(AddFlowHeader, { title: "Select plugins" }),
7858
+ /* @__PURE__ */ jsx31(
7339
7859
  MultiSelect,
7340
7860
  {
7341
7861
  items: plugins.map((plugin) => ({
@@ -7362,16 +7882,16 @@ function MarketplacePluginScreen() {
7362
7882
  }
7363
7883
 
7364
7884
  // src/tui/screens/MarketplaceSkills.tsx
7365
- import { Box as Box28, Text as Text25 } from "ink";
7366
- import React24 from "react";
7885
+ import { Box as Box30, Text as Text27 } from "ink";
7886
+ import React26 from "react";
7367
7887
 
7368
7888
  // src/flows/marketplace.ts
7369
- import { dirname as dirname6, join as join17, relative as relative4 } from "path";
7889
+ import { dirname as dirname7, join as join17, relative as relative4 } from "path";
7370
7890
  function normalizeCandidatePath(basePath, candidate) {
7371
7891
  if (!candidate) return basePath;
7372
7892
  const cleaned = candidate.replace(/\\/g, "/");
7373
7893
  if (cleaned.endsWith(".md")) {
7374
- return join17(basePath, dirname6(cleaned));
7894
+ return join17(basePath, dirname7(cleaned));
7375
7895
  }
7376
7896
  return join17(basePath, cleaned);
7377
7897
  }
@@ -7523,16 +8043,16 @@ function buildOriginMap(skills) {
7523
8043
  }
7524
8044
 
7525
8045
  // src/tui/screens/MarketplaceSkills.tsx
7526
- import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
8046
+ import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
7527
8047
  function MarketplaceSkillScreen() {
7528
8048
  const { invocation, addSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
7529
8049
  const options = invocation.options;
7530
- const [status, setStatus] = React24.useState("loading");
7531
- const [error, setError] = React24.useState(null);
8050
+ const [status, setStatus] = React26.useState("loading");
8051
+ const [error, setError] = React26.useState(null);
7532
8052
  const spinner = useSpinnerFrame(status === "loading");
7533
8053
  const selectedPlugins = addSkill.marketplace?.selectedPlugins ?? [];
7534
8054
  const context = addSkill.marketplace?.context;
7535
- React24.useEffect(() => {
8055
+ React26.useEffect(() => {
7536
8056
  let cancelled = false;
7537
8057
  const load = async () => {
7538
8058
  if (!context || selectedPlugins.length === 0) {
@@ -7596,27 +8116,27 @@ function MarketplaceSkillScreen() {
7596
8116
  options.yes
7597
8117
  ]);
7598
8118
  if (status === "loading") {
7599
- return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7600
- /* @__PURE__ */ jsx30(AddFlowHeader, { title: "Scanning marketplace" }),
7601
- /* @__PURE__ */ jsxs25(Text25, { children: [
8119
+ return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8120
+ /* @__PURE__ */ jsx32(AddFlowHeader, { title: "Scanning marketplace" }),
8121
+ /* @__PURE__ */ jsxs27(Text27, { children: [
7602
8122
  spinner,
7603
8123
  " Discovering skills from plugins..."
7604
8124
  ] })
7605
8125
  ] });
7606
8126
  }
7607
8127
  if (status === "error") {
7608
- return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7609
- /* @__PURE__ */ jsx30(AddFlowHeader, { title: "Marketplace scan failed" }),
7610
- /* @__PURE__ */ jsx30(Text25, { color: "red", children: error }),
7611
- /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: BACK_QUIT_HINT })
8128
+ return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8129
+ /* @__PURE__ */ jsx32(AddFlowHeader, { title: "Marketplace scan failed" }),
8130
+ /* @__PURE__ */ jsx32(Text27, { color: "red", children: error }),
8131
+ /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT })
7612
8132
  ] });
7613
8133
  }
7614
8134
  const skills = addSkill.marketplace?.skills ?? [];
7615
8135
  const warnings = addSkill.marketplace?.warnings ?? [];
7616
- return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7617
- /* @__PURE__ */ jsx30(AddFlowHeader, { title: "Select skills" }),
7618
- warnings.length > 0 ? /* @__PURE__ */ jsx30(Box28, { flexDirection: "column", marginBottom: 1, children: warnings.map((warning) => /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: warning }, warning)) }) : null,
7619
- /* @__PURE__ */ jsx30(
8136
+ return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8137
+ /* @__PURE__ */ jsx32(AddFlowHeader, { title: "Select skills" }),
8138
+ warnings.length > 0 ? /* @__PURE__ */ jsx32(Box30, { flexDirection: "column", marginBottom: 1, children: warnings.map((warning) => /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: warning }, warning)) }) : null,
8139
+ /* @__PURE__ */ jsx32(
7620
8140
  MultiSelect,
7621
8141
  {
7622
8142
  items: skills.map((entry) => ({
@@ -7666,8 +8186,8 @@ function autoSelect2(skills, names, yes) {
7666
8186
  // src/tui/screens/ScanSkills.tsx
7667
8187
  import { rm as rm6 } from "fs/promises";
7668
8188
  import chalk5 from "chalk";
7669
- import { Box as Box29, Text as Text26 } from "ink";
7670
- import React25 from "react";
8189
+ import { Box as Box31, Text as Text28 } from "ink";
8190
+ import React27 from "react";
7671
8191
 
7672
8192
  // src/flows/scan-installed-skills.ts
7673
8193
  import { existsSync as existsSync7 } from "fs";
@@ -7890,17 +8410,17 @@ function removalTargetsForRow(row, cwd) {
7890
8410
  }
7891
8411
 
7892
8412
  // src/tui/screens/ScanSkills.tsx
7893
- import { Fragment as Fragment3, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
8413
+ import { Fragment as Fragment3, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
7894
8414
  function ScanSkillsScreen() {
7895
8415
  const { setFlash, setBackHandler } = useNavigation();
7896
- const [view, setView] = React25.useState("loading");
7897
- const [error, setError] = React25.useState(null);
7898
- const [rows, setRows] = React25.useState([]);
7899
- const [progress, setProgress] = React25.useState(null);
7900
- const [selected, setSelected] = React25.useState(null);
7901
- const [removeTargets, setRemoveTargets] = React25.useState(null);
8416
+ const [view, setView] = React27.useState("loading");
8417
+ const [error, setError] = React27.useState(null);
8418
+ const [rows, setRows] = React27.useState([]);
8419
+ const [progress, setProgress] = React27.useState(null);
8420
+ const [selected, setSelected] = React27.useState(null);
8421
+ const [removeTargets, setRemoveTargets] = React27.useState(null);
7902
8422
  const spinner = useSpinnerFrame(view === "running" || view === "removing");
7903
- React25.useEffect(() => {
8423
+ React27.useEffect(() => {
7904
8424
  let cancelled = false;
7905
8425
  const run = async () => {
7906
8426
  try {
@@ -7970,7 +8490,7 @@ function ScanSkillsScreen() {
7970
8490
  cancelled = true;
7971
8491
  };
7972
8492
  }, []);
7973
- React25.useEffect(() => {
8493
+ React27.useEffect(() => {
7974
8494
  if (view === "actions" || view === "confirm-remove" || view === "removing") {
7975
8495
  setBackHandler(() => {
7976
8496
  if (view === "confirm-remove") {
@@ -7990,26 +8510,26 @@ function ScanSkillsScreen() {
7990
8510
  setBackHandler(null);
7991
8511
  return () => setBackHandler(null);
7992
8512
  }, [view, setBackHandler]);
7993
- const riskyRows = React25.useMemo(() => rows.filter(isRisky), [rows]);
7994
- const summary = React25.useMemo(() => scanSummary(rows), [rows]);
8513
+ const riskyRows = React27.useMemo(() => rows.filter(isRisky), [rows]);
8514
+ const summary = React27.useMemo(() => scanSummary(rows), [rows]);
7995
8515
  if (view === "empty") {
7996
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7997
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7998
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
8516
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8517
+ /* @__PURE__ */ jsx33(Header, { title: "Scan skills" }),
8518
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
7999
8519
  ] });
8000
8520
  }
8001
8521
  if (view === "loading") {
8002
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8003
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
8004
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "Discovering skills..." })
8522
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8523
+ /* @__PURE__ */ jsx33(Header, { title: "Scan skills" }),
8524
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Discovering skills..." })
8005
8525
  ] });
8006
8526
  }
8007
8527
  if (view === "running") {
8008
8528
  const completed = progress?.completed ?? 0;
8009
8529
  const total = progress?.total ?? 0;
8010
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8011
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
8012
- /* @__PURE__ */ jsxs26(Text26, { children: [
8530
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8531
+ /* @__PURE__ */ jsx33(Header, { title: "Scan skills" }),
8532
+ /* @__PURE__ */ jsxs28(Text28, { children: [
8013
8533
  spinner,
8014
8534
  " Scanning ",
8015
8535
  completed,
@@ -8019,14 +8539,14 @@ function ScanSkillsScreen() {
8019
8539
  total === 1 ? "" : "s",
8020
8540
  "..."
8021
8541
  ] }),
8022
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT }) })
8542
+ /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
8023
8543
  ] });
8024
8544
  }
8025
8545
  if (view === "error") {
8026
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8027
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
8028
- /* @__PURE__ */ jsx31(Text26, { color: "red", children: error ?? "Scan failed" }),
8029
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT }) })
8546
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8547
+ /* @__PURE__ */ jsx33(Header, { title: "Scan skills" }),
8548
+ /* @__PURE__ */ jsx33(Text28, { color: "red", children: error ?? "Scan failed" }),
8549
+ /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
8030
8550
  ] });
8031
8551
  }
8032
8552
  if (view === "actions" && selected) {
@@ -8037,27 +8557,27 @@ function ScanSkillsScreen() {
8037
8557
  const top = selected.topSignals?.slice(0, 10) ?? [];
8038
8558
  const locationLabels = selected.skill.locations.map((l) => l.label);
8039
8559
  const riskColor = level === "critical" || level === "high" ? "red" : level === "medium" ? "yellow" : level === "low" ? "green" : "gray";
8040
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8041
- /* @__PURE__ */ jsx31(Header, { title: "Skill risk details" }),
8042
- /* @__PURE__ */ jsxs26(Text26, { children: [
8043
- /* @__PURE__ */ jsx31(Text26, { bold: true, children: selected.skill.name }),
8560
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8561
+ /* @__PURE__ */ jsx33(Header, { title: "Skill risk details" }),
8562
+ /* @__PURE__ */ jsxs28(Text28, { children: [
8563
+ /* @__PURE__ */ jsx33(Text28, { bold: true, children: selected.skill.name }),
8044
8564
  " ",
8045
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `(${selected.skill.slug})` })
8565
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `(${selected.skill.slug})` })
8046
8566
  ] }),
8047
- selected.error ? /* @__PURE__ */ jsx31(Text26, { color: "red", children: selected.error }) : /* @__PURE__ */ jsxs26(Fragment3, { children: [
8048
- /* @__PURE__ */ jsxs26(Text26, { children: [
8049
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "Verdict:" }),
8567
+ selected.error ? /* @__PURE__ */ jsx33(Text28, { color: "red", children: selected.error }) : /* @__PURE__ */ jsxs28(Fragment3, { children: [
8568
+ /* @__PURE__ */ jsxs28(Text28, { children: [
8569
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Verdict:" }),
8050
8570
  " ",
8051
- /* @__PURE__ */ jsx31(Text26, { color: riskColor, children: verdict }),
8052
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: ` \u2022 level=${level} score=${score} issues=${issues}` })
8571
+ /* @__PURE__ */ jsx33(Text28, { color: riskColor, children: verdict }),
8572
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: ` \u2022 level=${level} score=${score} issues=${issues}` })
8053
8573
  ] }),
8054
- top.length > 0 ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Top signals: ${top.join(", ")}` }) : null,
8055
- selected.ruleset ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Ruleset: ${selected.ruleset}` }) : null
8574
+ top.length > 0 ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Top signals: ${top.join(", ")}` }) : null,
8575
+ selected.ruleset ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Ruleset: ${selected.ruleset}` }) : null
8056
8576
  ] }),
8057
- selected.skill.description ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: selected.skill.description }) : null,
8058
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Path: ${selected.skill.path}` }),
8059
- locationLabels.length > 0 ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Locations: ${locationLabels.join(", ")}` }) : null,
8060
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(
8577
+ selected.skill.description ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: selected.skill.description }) : null,
8578
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Path: ${selected.skill.path}` }),
8579
+ locationLabels.length > 0 ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Locations: ${locationLabels.join(", ")}` }) : null,
8580
+ /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(
8061
8581
  SelectMenu,
8062
8582
  {
8063
8583
  items: [
@@ -8083,17 +8603,17 @@ function ScanSkillsScreen() {
8083
8603
  }
8084
8604
  if (view === "confirm-remove" && selected && removeTargets) {
8085
8605
  const title = `Remove ${selected.skill.name}`;
8086
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8087
- /* @__PURE__ */ jsx31(Header, { title }),
8088
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "These locations will be removed:" }),
8089
- /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginTop: 1, children: removeTargets.length === 0 ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "None found." }) : removeTargets.map((t) => /* @__PURE__ */ jsxs26(Text26, { children: [
8090
- /* @__PURE__ */ jsx31(Text26, { children: chalk5.red("\u2022") }),
8606
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8607
+ /* @__PURE__ */ jsx33(Header, { title }),
8608
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "These locations will be removed:" }),
8609
+ /* @__PURE__ */ jsx33(Box31, { flexDirection: "column", marginTop: 1, children: removeTargets.length === 0 ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "None found." }) : removeTargets.map((t) => /* @__PURE__ */ jsxs28(Text28, { children: [
8610
+ /* @__PURE__ */ jsx33(Text28, { children: chalk5.red("\u2022") }),
8091
8611
  " ",
8092
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: t.label }),
8612
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: t.label }),
8093
8613
  " ",
8094
8614
  t.path
8095
8615
  ] }, t.path)) }),
8096
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(
8616
+ /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(
8097
8617
  SelectMenu,
8098
8618
  {
8099
8619
  items: [
@@ -8141,33 +8661,33 @@ function ScanSkillsScreen() {
8141
8661
  ] });
8142
8662
  }
8143
8663
  if (view === "removing") {
8144
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8145
- /* @__PURE__ */ jsx31(Header, { title: "Removing skill" }),
8146
- /* @__PURE__ */ jsxs26(Text26, { children: [
8664
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8665
+ /* @__PURE__ */ jsx33(Header, { title: "Removing skill" }),
8666
+ /* @__PURE__ */ jsxs28(Text28, { children: [
8147
8667
  spinner,
8148
8668
  " Removing..."
8149
8669
  ] }),
8150
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT }) })
8670
+ /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
8151
8671
  ] });
8152
8672
  }
8153
8673
  if (riskyRows.length === 0) {
8154
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8155
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
8156
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
8674
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8675
+ /* @__PURE__ */ jsx33(Header, { title: "Scan skills" }),
8676
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
8157
8677
  ] });
8158
8678
  }
8159
- return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
8160
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
8161
- /* @__PURE__ */ jsxs26(Box29, { marginBottom: 1, flexDirection: "column", children: [
8162
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Scanned ${plural(summary.total, "skill")}.` }),
8163
- /* @__PURE__ */ jsxs26(Text26, { children: [
8164
- /* @__PURE__ */ jsx31(Text26, { color: "red", children: plural(summary.high, "high risk") }),
8165
- /* @__PURE__ */ jsx31(Text26, { children: ", " }),
8166
- /* @__PURE__ */ jsx31(Text26, { color: "yellow", children: plural(summary.medium, "medium risk") }),
8167
- /* @__PURE__ */ jsx31(Text26, { children: " detected." })
8679
+ return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8680
+ /* @__PURE__ */ jsx33(Header, { title: "Scan skills" }),
8681
+ /* @__PURE__ */ jsxs28(Box31, { marginBottom: 1, flexDirection: "column", children: [
8682
+ /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Scanned ${plural(summary.total, "skill")}.` }),
8683
+ /* @__PURE__ */ jsxs28(Text28, { children: [
8684
+ /* @__PURE__ */ jsx33(Text28, { color: "red", children: plural(summary.high, "high risk") }),
8685
+ /* @__PURE__ */ jsx33(Text28, { children: ", " }),
8686
+ /* @__PURE__ */ jsx33(Text28, { color: "yellow", children: plural(summary.medium, "medium risk") }),
8687
+ /* @__PURE__ */ jsx33(Text28, { children: " detected." })
8168
8688
  ] })
8169
8689
  ] }),
8170
- /* @__PURE__ */ jsx31(
8690
+ /* @__PURE__ */ jsx33(
8171
8691
  SingleSelect,
8172
8692
  {
8173
8693
  items: riskyRows.map((row) => ({
@@ -8188,8 +8708,8 @@ function ScanSkillsScreen() {
8188
8708
  }
8189
8709
 
8190
8710
  // src/tui/screens/UpdateDocs.tsx
8191
- import { Box as Box30, Text as Text27 } from "ink";
8192
- import React26 from "react";
8711
+ import { Box as Box32, Text as Text29 } from "ink";
8712
+ import React28 from "react";
8193
8713
 
8194
8714
  // src/docs/update.ts
8195
8715
  import { readdir as readdir6, stat as stat6 } from "fs/promises";
@@ -8255,12 +8775,12 @@ async function updateDocs(cwd = process.cwd()) {
8255
8775
  }
8256
8776
 
8257
8777
  // src/tui/screens/UpdateDocs.tsx
8258
- import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
8778
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
8259
8779
  function UpdateDocsScreen() {
8260
- const [status, setStatus] = React26.useState("running");
8261
- const [summary, setSummary] = React26.useState(null);
8780
+ const [status, setStatus] = React28.useState("running");
8781
+ const [summary, setSummary] = React28.useState(null);
8262
8782
  const spinner = useSpinnerFrame(status === "running");
8263
- React26.useEffect(() => {
8783
+ React28.useEffect(() => {
8264
8784
  let cancelled = false;
8265
8785
  const run = async () => {
8266
8786
  const output2 = await updateDocs();
@@ -8278,65 +8798,180 @@ function UpdateDocsScreen() {
8278
8798
  };
8279
8799
  }, []);
8280
8800
  if (status === "running") {
8281
- return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8282
- /* @__PURE__ */ jsx32(Header, { title: "Updating docs" }),
8283
- /* @__PURE__ */ jsxs27(Text27, { children: [
8801
+ return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
8802
+ /* @__PURE__ */ jsx34(Header, { title: "Updating docs" }),
8803
+ /* @__PURE__ */ jsxs29(Text29, { children: [
8284
8804
  spinner,
8285
8805
  " Pulling latest docs..."
8286
8806
  ] })
8287
8807
  ] });
8288
8808
  }
8289
8809
  if (status === "empty") {
8290
- return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8291
- /* @__PURE__ */ jsx32(Header, { title: "Update docs" }),
8292
- /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "No docs installed yet." }),
8293
- /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
8810
+ return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
8811
+ /* @__PURE__ */ jsx34(Header, { title: "Update docs" }),
8812
+ /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "No docs installed yet." }),
8813
+ /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: BACK_QUIT_HINT }) })
8294
8814
  ] });
8295
8815
  }
8296
8816
  if (!summary) {
8297
- return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8298
- /* @__PURE__ */ jsx32(Header, { title: "Update docs" }),
8299
- /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "Nothing to update." })
8817
+ return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
8818
+ /* @__PURE__ */ jsx34(Header, { title: "Update docs" }),
8819
+ /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "Nothing to update." })
8300
8820
  ] });
8301
8821
  }
8302
8822
  const cwd = process.cwd();
8303
- return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
8304
- /* @__PURE__ */ jsx32(Header, { title: "Docs update results" }),
8305
- summary.updated.length > 0 ? /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", marginBottom: 1, children: [
8306
- /* @__PURE__ */ jsx32(Text27, { children: `Updated ${summary.updated.length} repo${summary.updated.length !== 1 ? "s" : ""}` }),
8307
- summary.updated.map((item) => /* @__PURE__ */ jsxs27(Text27, { dimColor: true, children: [
8823
+ return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
8824
+ /* @__PURE__ */ jsx34(Header, { title: "Docs update results" }),
8825
+ summary.updated.length > 0 ? /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", marginBottom: 1, children: [
8826
+ /* @__PURE__ */ jsx34(Text29, { children: `Updated ${summary.updated.length} repo${summary.updated.length !== 1 ? "s" : ""}` }),
8827
+ summary.updated.map((item) => /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
8308
8828
  item.name,
8309
8829
  " \u2192 ",
8310
8830
  shortenPath(item.path, cwd)
8311
8831
  ] }, `updated-${item.name}`))
8312
8832
  ] }) : null,
8313
- summary.skipped.length > 0 ? /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", marginBottom: 1, children: [
8314
- /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "Skipped" }),
8315
- summary.skipped.map((item) => /* @__PURE__ */ jsxs27(Text27, { dimColor: true, children: [
8833
+ summary.skipped.length > 0 ? /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", marginBottom: 1, children: [
8834
+ /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "Skipped" }),
8835
+ summary.skipped.map((item) => /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
8316
8836
  item.name,
8317
8837
  item.message ? ` (${item.message})` : ""
8318
8838
  ] }, `skipped-${item.name}`))
8319
8839
  ] }) : null,
8320
- summary.failed.length > 0 ? /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", marginBottom: 1, children: [
8321
- /* @__PURE__ */ jsx32(Text27, { color: "red", children: "Failed" }),
8322
- summary.failed.map((item) => /* @__PURE__ */ jsxs27(Text27, { color: "red", children: [
8840
+ summary.failed.length > 0 ? /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", marginBottom: 1, children: [
8841
+ /* @__PURE__ */ jsx34(Text29, { color: "red", children: "Failed" }),
8842
+ summary.failed.map((item) => /* @__PURE__ */ jsxs29(Text29, { color: "red", children: [
8323
8843
  item.name,
8324
8844
  item.message ? ` (${item.message})` : ""
8325
8845
  ] }, `failed-${item.name}`))
8326
8846
  ] }) : null,
8327
- /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
8847
+ /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: BACK_QUIT_HINT }) })
8328
8848
  ] });
8329
8849
  }
8330
8850
 
8331
8851
  // src/tui/screens/UpdateSkills.tsx
8332
8852
  import chalk6 from "chalk";
8333
- import { Box as Box31, Text as Text28, useInput as useInput6 } from "ink";
8334
- import React27 from "react";
8853
+ import { Box as Box33, Text as Text30, useInput as useInput6 } from "ink";
8854
+ import React29 from "react";
8335
8855
 
8336
8856
  // src/flows/update-skills.ts
8337
- import { mkdir as mkdir7, mkdtemp as mkdtemp5, rm as rm7, stat as stat7, writeFile as writeFile5 } from "fs/promises";
8857
+ import { randomUUID } from "crypto";
8858
+ import { cp as cp2, mkdir as mkdir8, mkdtemp as mkdtemp6, rm as rm7, stat as stat7, writeFile as writeFile6 } from "fs/promises";
8859
+ import { homedir as homedir5, tmpdir as tmpdir7 } from "os";
8860
+ import { dirname as dirname9, join as join22 } from "path";
8861
+
8862
+ // src/flows/update-well-known-authed.ts
8863
+ import { mkdir as mkdir7, mkdtemp as mkdtemp5, writeFile as writeFile5 } from "fs/promises";
8338
8864
  import { tmpdir as tmpdir6 } from "os";
8339
- import { dirname as dirname7, join as join21 } from "path";
8865
+ import { dirname as dirname8, join as join21 } from "path";
8866
+ function resolveAuthedIndexAuth(entry, endpoint) {
8867
+ if (entry.auth?.tokenHeader) {
8868
+ return { tokenHeader: entry.auth.tokenHeader, tokenPrefix: entry.auth.tokenPrefix ?? "" };
8869
+ }
8870
+ const spec = endpoint === "content" ? entry.content : entry.verify;
8871
+ const tokenHeader = spec?.tokenHeader;
8872
+ const tokenPrefix = spec?.tokenPrefix;
8873
+ if (!tokenHeader) {
8874
+ throw new Error(
8875
+ "Invalid well-known index entry: missing auth.tokenHeader (or legacy tokenHeader)."
8876
+ );
8877
+ }
8878
+ return { tokenHeader, tokenPrefix: tokenPrefix ?? "" };
8879
+ }
8880
+ async function fetchWellKnownAuthedManifest(sourceUrl, licenseKey) {
8881
+ const result = await wellKnownProvider.fetchIndex(sourceUrl);
8882
+ if (!result) return null;
8883
+ const entry = result.index.skills.find(
8884
+ (e) => e && typeof e === "object" && "content" in e && !("files" in e)
8885
+ );
8886
+ if (!entry?.content?.endpoint) return null;
8887
+ const contentAuth = resolveAuthedIndexAuth(entry, "content");
8888
+ const tokenValue = `${contentAuth.tokenPrefix}${licenseKey}`;
8889
+ if (entry.verify?.endpoint) {
8890
+ const verifyAuth = resolveAuthedIndexAuth(entry, "verify");
8891
+ const verifyRes = await fetch(entry.verify.endpoint, {
8892
+ method: entry.verify.method,
8893
+ headers: { [verifyAuth.tokenHeader]: tokenValue }
8894
+ });
8895
+ if (!verifyRes.ok) {
8896
+ const text = await verifyRes.text().catch(() => "");
8897
+ let msg = text;
8898
+ try {
8899
+ const parsed = JSON.parse(text);
8900
+ if (typeof parsed?.error === "string" && parsed.error) {
8901
+ msg = parsed.error;
8902
+ }
8903
+ } catch {
8904
+ }
8905
+ throw new Error(msg || `License verification failed (${verifyRes.status}).`);
8906
+ }
8907
+ }
8908
+ const res = await fetch(entry.content.endpoint, {
8909
+ method: entry.content.method,
8910
+ headers: { [contentAuth.tokenHeader]: tokenValue }
8911
+ });
8912
+ if (!res.ok) {
8913
+ const text = await res.text().catch(() => "");
8914
+ throw new Error(text || `Unable to fetch skill content (${res.status}).`);
8915
+ }
8916
+ const json = await res.json();
8917
+ if (json && typeof json === "object" && "success" in json) {
8918
+ const wrapped = json;
8919
+ if (!wrapped.success) {
8920
+ throw new Error(wrapped.error || "Unable to fetch skill content.");
8921
+ }
8922
+ return wrapped.data;
8923
+ }
8924
+ return json;
8925
+ }
8926
+ async function updateFromWellKnownAuthed(target, helpers) {
8927
+ if (target.entry.sourceType !== "well-known") return false;
8928
+ if (!target.entry.licenseKey) return false;
8929
+ const sourceUrl = target.entry.sourceUrl || target.entry.source;
8930
+ const manifest = await fetchWellKnownAuthedManifest(sourceUrl, target.entry.licenseKey);
8931
+ if (!manifest?.files || typeof manifest.files !== "object") {
8932
+ return false;
8933
+ }
8934
+ if (manifest.contentHash) {
8935
+ target.latestHash = manifest.contentHash;
8936
+ if (target.entry.skillFolderHash && target.entry.skillFolderHash === manifest.contentHash) {
8937
+ target.status = "up-to-date";
8938
+ return false;
8939
+ }
8940
+ }
8941
+ const tempDir = await mkdtemp5(join21(tmpdir6(), "playbooks-well-known-"));
8942
+ registerTempDir(tempDir);
8943
+ try {
8944
+ await mkdir7(tempDir, { recursive: true });
8945
+ for (const [filePath, fileContent] of Object.entries(manifest.files)) {
8946
+ const targetPath = join21(tempDir, filePath);
8947
+ if (!isPathSafe(tempDir, targetPath)) continue;
8948
+ await mkdir7(dirname8(targetPath), { recursive: true });
8949
+ await writeFile5(targetPath, fileContent, "utf-8");
8950
+ }
8951
+ let sourceDir = null;
8952
+ if (target.entry.skillPath) {
8953
+ const normalized = target.entry.skillPath.replace(/\\/g, "/").replace(/^\/+/, "");
8954
+ const folder = normalized.toLowerCase().endsWith("skill.md") ? dirname8(normalized) : normalized;
8955
+ const candidate = join21(tempDir, folder);
8956
+ if (await helpers.pathExists(join21(candidate, "SKILL.md"))) {
8957
+ sourceDir = candidate;
8958
+ }
8959
+ }
8960
+ if (!sourceDir) {
8961
+ const discovered = await discoverSkills(tempDir);
8962
+ const match = discovered.find((s) => s.name === target.name) ?? null;
8963
+ sourceDir = match ? match.path : null;
8964
+ }
8965
+ if (!sourceDir) {
8966
+ return false;
8967
+ }
8968
+ return await helpers.applyUpdateFromDir(target.name, target.scope, sourceDir);
8969
+ } finally {
8970
+ await cleanupTempDir(tempDir);
8971
+ }
8972
+ }
8973
+
8974
+ // src/flows/update-skills.ts
8340
8975
  var repoTreeCache = /* @__PURE__ */ new Map();
8341
8976
  function normalizeSkillFolderPath(skillPath) {
8342
8977
  let folderPath = skillPath;
@@ -8404,6 +9039,23 @@ async function pathExists3(path) {
8404
9039
  return false;
8405
9040
  }
8406
9041
  }
9042
+ function getBackupRoot(scope) {
9043
+ const baseDir = scope === "global" ? homedir5() : process.cwd();
9044
+ return join22(baseDir, ".playbooks", "backups");
9045
+ }
9046
+ async function backupDirIfExists(input) {
9047
+ if (process.env.PLAYBOOKS_DISABLE_BACKUPS === "1") return;
9048
+ try {
9049
+ if (!await pathExists3(input.sourcePath)) return;
9050
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9051
+ const safeSkillName = sanitizeSkillName(input.skillName);
9052
+ const backupBase = join22(getBackupRoot(input.scope), stamp, input.scope, safeSkillName);
9053
+ const dest = join22(backupBase, `${input.label}-${randomUUID()}`);
9054
+ await mkdir8(dirname9(dest), { recursive: true });
9055
+ await cp2(input.sourcePath, dest, { recursive: true, dereference: false });
9056
+ } catch {
9057
+ }
9058
+ }
8407
9059
  async function applyUpdateFromDir(skillName, scope, sourceDir) {
8408
9060
  const canonicalPath = getCanonicalPath(skillName, { global: scope === "global" });
8409
9061
  const installs = await findSkillInstallations(skillName, scope);
@@ -8413,12 +9065,14 @@ async function applyUpdateFromDir(skillName, scope, sourceDir) {
8413
9065
  return false;
8414
9066
  }
8415
9067
  if (canonicalExists || hasSymlink) {
9068
+ await backupDirIfExists({ scope, skillName, sourcePath: canonicalPath, label: "canonical" });
8416
9069
  await rm7(canonicalPath, { recursive: true, force: true });
8417
9070
  await copySkillDirectory(sourceDir, canonicalPath);
8418
9071
  }
8419
9072
  const updateTargets = installs.filter((i) => !i.isSymlink && i.path !== canonicalPath);
8420
9073
  if (updateTargets.length > 0) {
8421
9074
  for (const install of updateTargets) {
9075
+ await backupDirIfExists({ scope, skillName, sourcePath: install.path, label: "install" });
8422
9076
  await rm7(install.path, { recursive: true, force: true });
8423
9077
  await copySkillDirectory(sourceDir, install.path);
8424
9078
  }
@@ -8431,8 +9085,8 @@ async function updateFromRepo(target) {
8431
9085
  let sourceDir = null;
8432
9086
  if (target.entry.skillPath) {
8433
9087
  const normalizedPath = target.entry.skillPath.replace(/\\/g, "/");
8434
- const skillDir = join21(tempDir, dirname7(normalizedPath));
8435
- if (await pathExists3(join21(skillDir, "SKILL.md"))) {
9088
+ const skillDir = join22(tempDir, dirname9(normalizedPath));
9089
+ if (await pathExists3(join22(skillDir, "SKILL.md"))) {
8436
9090
  sourceDir = skillDir;
8437
9091
  }
8438
9092
  }
@@ -8473,21 +9127,21 @@ async function updateFromRemote(target) {
8473
9127
  if (!content && !files) {
8474
9128
  return false;
8475
9129
  }
8476
- const tempDir = await mkdtemp5(join21(tmpdir6(), "playbooks-skill-"));
9130
+ const tempDir = await mkdtemp6(join22(tmpdir7(), "playbooks-skill-"));
8477
9131
  registerTempDir(tempDir);
8478
9132
  try {
8479
- await mkdir7(tempDir, { recursive: true });
9133
+ await mkdir8(tempDir, { recursive: true });
8480
9134
  if (files) {
8481
9135
  for (const [filePath, fileContent] of files.entries()) {
8482
- const targetPath = join21(tempDir, filePath);
9136
+ const targetPath = join22(tempDir, filePath);
8483
9137
  if (!isPathSafe(tempDir, targetPath)) {
8484
9138
  continue;
8485
9139
  }
8486
- await mkdir7(dirname7(targetPath), { recursive: true });
8487
- await writeFile5(targetPath, fileContent, "utf-8");
9140
+ await mkdir8(dirname9(targetPath), { recursive: true });
9141
+ await writeFile6(targetPath, fileContent, "utf-8");
8488
9142
  }
8489
9143
  } else if (content) {
8490
- await writeFile5(join21(tempDir, "SKILL.md"), content, "utf-8");
9144
+ await writeFile6(join22(tempDir, "SKILL.md"), content, "utf-8");
8491
9145
  }
8492
9146
  return await applyUpdateFromDir(target.name, target.scope, tempDir);
8493
9147
  } finally {
@@ -8495,6 +9149,10 @@ async function updateFromRemote(target) {
8495
9149
  }
8496
9150
  }
8497
9151
  async function updateTargetSkill(target) {
9152
+ if (target.entry.sourceType === "well-known" && target.entry.licenseKey) {
9153
+ const updated = await updateFromWellKnownAuthed(target, { applyUpdateFromDir, pathExists: pathExists3 });
9154
+ if (updated) return true;
9155
+ }
8498
9156
  if (target.entry.sourceType === "github" || target.entry.sourceType === "gitlab" || target.entry.sourceType === "git") {
8499
9157
  return await updateFromRepo(target);
8500
9158
  }
@@ -8584,15 +9242,15 @@ async function updateSkills(targets) {
8584
9242
  }
8585
9243
 
8586
9244
  // src/tui/screens/UpdateSkills.tsx
8587
- import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
9245
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
8588
9246
  function UpdateScreen() {
8589
9247
  const { invocation, navigateTo, setFlash } = useNavigation();
8590
- const [status, setStatus] = React27.useState("loading");
8591
- const [targets, setTargets] = React27.useState([]);
8592
- const [selected, setSelected] = React27.useState([]);
8593
- const [summary, setSummary] = React27.useState(null);
8594
- const [showOnlyNeeds, setShowOnlyNeeds] = React27.useState(false);
8595
- const [rateLimited, setRateLimited] = React27.useState(false);
9248
+ const [status, setStatus] = React29.useState("loading");
9249
+ const [targets, setTargets] = React29.useState([]);
9250
+ const [selected, setSelected] = React29.useState([]);
9251
+ const [summary, setSummary] = React29.useState(null);
9252
+ const [showOnlyNeeds, setShowOnlyNeeds] = React29.useState(false);
9253
+ const [rateLimited, setRateLimited] = React29.useState(false);
8596
9254
  const spinner = useSpinnerFrame(status === "running");
8597
9255
  useInput6((input) => {
8598
9256
  if (status !== "select") return;
@@ -8600,7 +9258,7 @@ function UpdateScreen() {
8600
9258
  setShowOnlyNeeds((prev) => !prev);
8601
9259
  }
8602
9260
  });
8603
- React27.useEffect(() => {
9261
+ React29.useEffect(() => {
8604
9262
  let cancelled = false;
8605
9263
  const load = async () => {
8606
9264
  const scopes = resolveScopes(invocation.options);
@@ -8642,7 +9300,7 @@ function UpdateScreen() {
8642
9300
  cancelled = true;
8643
9301
  };
8644
9302
  }, [invocation, setFlash]);
8645
- React27.useEffect(() => {
9303
+ React29.useEffect(() => {
8646
9304
  let cancelled = false;
8647
9305
  const run = async () => {
8648
9306
  if (status !== "running" || selected.length === 0) return;
@@ -8657,16 +9315,16 @@ function UpdateScreen() {
8657
9315
  };
8658
9316
  }, [status, selected]);
8659
9317
  if (status === "empty") {
8660
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8661
- /* @__PURE__ */ jsx33(Header, { title: "Update skills" }),
8662
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "No tracked skills to update yet." }),
8663
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Re-install a skill once to enable updates." })
9318
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9319
+ /* @__PURE__ */ jsx35(Header, { title: "Update skills" }),
9320
+ /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "No tracked skills to update yet." }),
9321
+ /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "Re-install a skill once to enable updates." })
8664
9322
  ] });
8665
9323
  }
8666
9324
  if (status === "loading") {
8667
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8668
- /* @__PURE__ */ jsx33(Header, { title: "Update skills" }),
8669
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Loading tracked skills..." })
9325
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9326
+ /* @__PURE__ */ jsx35(Header, { title: "Update skills" }),
9327
+ /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "Loading tracked skills..." })
8670
9328
  ] });
8671
9329
  }
8672
9330
  if (status === "select") {
@@ -8674,17 +9332,17 @@ function UpdateScreen() {
8674
9332
  const defaults = selected;
8675
9333
  const hint = showOnlyNeeds ? UPDATE_HINT_NEEDS_ONLY : UPDATE_HINT_ALL;
8676
9334
  if (showOnlyNeeds && visibleTargets.length === 0) {
8677
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8678
- /* @__PURE__ */ jsx33(Header, { title: "Select skills to update" }),
8679
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "No updates found." }),
8680
- rateLimited ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "GitHub rate limit hit. Some skills may be marked unknown." }) : null,
8681
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: UPDATE_EMPTY_HINT }) })
9335
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9336
+ /* @__PURE__ */ jsx35(Header, { title: "Select skills to update" }),
9337
+ /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "No updates found." }),
9338
+ rateLimited ? /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "GitHub rate limit hit. Some skills may be marked unknown." }) : null,
9339
+ /* @__PURE__ */ jsx35(Box33, { marginTop: 1, children: /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: UPDATE_EMPTY_HINT }) })
8682
9340
  ] });
8683
9341
  }
8684
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8685
- /* @__PURE__ */ jsx33(Header, { title: "Select skills to update" }),
8686
- rateLimited ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "GitHub rate limit hit. Some skills marked unknown." }) : null,
8687
- /* @__PURE__ */ jsx33(
9342
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9343
+ /* @__PURE__ */ jsx35(Header, { title: "Select skills to update" }),
9344
+ rateLimited ? /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "GitHub rate limit hit. Some skills marked unknown." }) : null,
9345
+ /* @__PURE__ */ jsx35(
8688
9346
  MultiSelect,
8689
9347
  {
8690
9348
  items: visibleTargets.map((target) => ({
@@ -8707,9 +9365,9 @@ function UpdateScreen() {
8707
9365
  ] });
8708
9366
  }
8709
9367
  if (status === "running") {
8710
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8711
- /* @__PURE__ */ jsx33(Header, { title: "Updating skills" }),
8712
- /* @__PURE__ */ jsxs28(Text28, { children: [
9368
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9369
+ /* @__PURE__ */ jsx35(Header, { title: "Updating skills" }),
9370
+ /* @__PURE__ */ jsxs30(Text30, { children: [
8713
9371
  spinner,
8714
9372
  " Updating ",
8715
9373
  selected.length,
@@ -8720,17 +9378,17 @@ function UpdateScreen() {
8720
9378
  ] });
8721
9379
  }
8722
9380
  if (!summary) {
8723
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8724
- /* @__PURE__ */ jsx33(Header, { title: "Update skills" }),
8725
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Nothing to update." })
9381
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9382
+ /* @__PURE__ */ jsx35(Header, { title: "Update skills" }),
9383
+ /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: "Nothing to update." })
8726
9384
  ] });
8727
9385
  }
8728
- return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8729
- /* @__PURE__ */ jsx33(Header, { title: "Update results" }),
8730
- summary.updated.length > 0 ? /* @__PURE__ */ jsx33(Text28, { children: `Updated ${summary.updated.length} skill${summary.updated.length !== 1 ? "s" : ""}` }) : null,
8731
- summary.skipped.length > 0 ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Skipped: ${summary.skipped.map(formatTargetLabel).join(", ")}` }) : null,
8732
- summary.failed.length > 0 ? /* @__PURE__ */ jsx33(Text28, { color: "red", children: `Failed: ${summary.failed.map(formatTargetLabel).join(", ")}` }) : null,
8733
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
9386
+ return /* @__PURE__ */ jsxs30(Box33, { flexDirection: "column", padding: 1, children: [
9387
+ /* @__PURE__ */ jsx35(Header, { title: "Update results" }),
9388
+ summary.updated.length > 0 ? /* @__PURE__ */ jsx35(Text30, { children: `Updated ${summary.updated.length} skill${summary.updated.length !== 1 ? "s" : ""}` }) : null,
9389
+ summary.skipped.length > 0 ? /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: `Skipped: ${summary.skipped.map(formatTargetLabel).join(", ")}` }) : null,
9390
+ summary.failed.length > 0 ? /* @__PURE__ */ jsx35(Text30, { color: "red", children: `Failed: ${summary.failed.map(formatTargetLabel).join(", ")}` }) : null,
9391
+ /* @__PURE__ */ jsx35(Box33, { marginTop: 1, children: /* @__PURE__ */ jsx35(Text30, { dimColor: true, children: BACK_QUIT_HINT }) })
8734
9392
  ] });
8735
9393
  }
8736
9394
  function resolveScopes(options) {
@@ -8759,61 +9417,65 @@ function sortTargets(a, b) {
8759
9417
  }
8760
9418
 
8761
9419
  // src/tui/ScreenRouter.tsx
8762
- import { Fragment as Fragment4, jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
9420
+ import { Fragment as Fragment4, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
8763
9421
  function ScreenRouter() {
8764
9422
  const { screen } = useNavigation();
8765
9423
  const render2 = (s) => {
8766
9424
  switch (s) {
8767
9425
  case "main":
8768
- return /* @__PURE__ */ jsx34(MainMenu, {});
9426
+ return /* @__PURE__ */ jsx36(MainMenu, {});
8769
9427
  case "add-source":
8770
- return /* @__PURE__ */ jsx34(AddSourceScreen, {});
9428
+ return /* @__PURE__ */ jsx36(AddSourceScreen, {});
8771
9429
  case "add-docs":
8772
- return /* @__PURE__ */ jsx34(AddDocsScreen, {});
9430
+ return /* @__PURE__ */ jsx36(AddDocsScreen, {});
8773
9431
  case "add-marketplace-plugins":
8774
- return /* @__PURE__ */ jsx34(MarketplacePluginScreen, {});
9432
+ return /* @__PURE__ */ jsx36(MarketplacePluginScreen, {});
8775
9433
  case "add-marketplace-skills":
8776
- return /* @__PURE__ */ jsx34(MarketplaceSkillScreen, {});
9434
+ return /* @__PURE__ */ jsx36(MarketplaceSkillScreen, {});
8777
9435
  case "find-skill-search":
8778
- return /* @__PURE__ */ jsx34(FindSkillSearchScreen, {});
9436
+ return /* @__PURE__ */ jsx36(FindSkillSearchScreen, {});
8779
9437
  case "find-skill-results":
8780
- return /* @__PURE__ */ jsx34(FindSkillResultsScreen, {});
9438
+ return /* @__PURE__ */ jsx36(FindSkillResultsScreen, {});
8781
9439
  case "scan-skills":
8782
- return /* @__PURE__ */ jsx34(ScanSkillsScreen, {});
9440
+ return /* @__PURE__ */ jsx36(ScanSkillsScreen, {});
8783
9441
  case "get-url":
8784
- return /* @__PURE__ */ jsx34(GetUrlScreen, {});
9442
+ return /* @__PURE__ */ jsx36(GetUrlScreen, {});
8785
9443
  case "add-skill-select":
8786
- return /* @__PURE__ */ jsx34(AddSkillSelectScreen, {});
9444
+ return /* @__PURE__ */ jsx36(AddSkillSelectScreen, {});
9445
+ case "add-bundle-select":
9446
+ return /* @__PURE__ */ jsx36(AddBundleSelectScreen, {});
9447
+ case "add-license-key":
9448
+ return /* @__PURE__ */ jsx36(AddLicenseKeyScreen, {});
8787
9449
  case "add-security-scan":
8788
- return /* @__PURE__ */ jsx34(AddSecurityScanScreen, {});
9450
+ return /* @__PURE__ */ jsx36(AddSecurityScanScreen, {});
8789
9451
  case "add-targets":
8790
- return /* @__PURE__ */ jsx34(AddTargetsScreen, {});
9452
+ return /* @__PURE__ */ jsx36(AddTargetsScreen, {});
8791
9453
  case "add-scope":
8792
- return /* @__PURE__ */ jsx34(AddScopeScreen, {});
9454
+ return /* @__PURE__ */ jsx36(AddScopeScreen, {});
8793
9455
  case "add-mode":
8794
- return /* @__PURE__ */ jsx34(AddModeScreen, {});
9456
+ return /* @__PURE__ */ jsx36(AddModeScreen, {});
8795
9457
  case "add-confirm":
8796
- return /* @__PURE__ */ jsx34(AddConfirmScreen, {});
9458
+ return /* @__PURE__ */ jsx36(AddConfirmScreen, {});
8797
9459
  case "add-install":
8798
- return /* @__PURE__ */ jsx34(AddInstallScreen, {});
9460
+ return /* @__PURE__ */ jsx36(AddInstallScreen, {});
8799
9461
  case "add-result":
8800
- return /* @__PURE__ */ jsx34(AddResultScreen, {});
9462
+ return /* @__PURE__ */ jsx36(AddResultScreen, {});
8801
9463
  case "list":
8802
- return /* @__PURE__ */ jsx34(ListScreen, {});
9464
+ return /* @__PURE__ */ jsx36(ListScreen, {});
8803
9465
  case "manage":
8804
- return /* @__PURE__ */ jsx34(ManageScreen, {});
9466
+ return /* @__PURE__ */ jsx36(ManageScreen, {});
8805
9467
  case "update":
8806
- return /* @__PURE__ */ jsx34(UpdateScreen, {});
9468
+ return /* @__PURE__ */ jsx36(UpdateScreen, {});
8807
9469
  case "update-docs":
8808
- return /* @__PURE__ */ jsx34(UpdateDocsScreen, {});
9470
+ return /* @__PURE__ */ jsx36(UpdateDocsScreen, {});
8809
9471
  default:
8810
9472
  return null;
8811
9473
  }
8812
9474
  };
8813
- return /* @__PURE__ */ jsxs29(Fragment4, { children: [
8814
- /* @__PURE__ */ jsx34(BrandHeader, {}),
9475
+ return /* @__PURE__ */ jsxs31(Fragment4, { children: [
9476
+ /* @__PURE__ */ jsx36(BrandHeader, {}),
8815
9477
  render2(screen),
8816
- /* @__PURE__ */ jsx34(FlashBar, { align: "center" })
9478
+ /* @__PURE__ */ jsx36(FlashBar, { align: "center" })
8817
9479
  ] });
8818
9480
  }
8819
9481
 
@@ -8893,14 +9555,14 @@ function useKeyboardShortcuts() {
8893
9555
  }
8894
9556
 
8895
9557
  // src/tui/App.tsx
8896
- import { jsx as jsx35 } from "react/jsx-runtime";
9558
+ import { jsx as jsx37 } from "react/jsx-runtime";
8897
9559
  function AppRoot() {
8898
9560
  useKeyboardShortcuts();
8899
- return /* @__PURE__ */ jsx35(ScreenRouter, {});
9561
+ return /* @__PURE__ */ jsx37(ScreenRouter, {});
8900
9562
  }
8901
9563
  function runApp(initialInvocation, initialScreen) {
8902
9564
  const { waitUntilExit } = render(
8903
- /* @__PURE__ */ jsx35(NavigationProvider, { initialInvocation, initialScreen, children: /* @__PURE__ */ jsx35(AppRoot, {}) })
9565
+ /* @__PURE__ */ jsx37(NavigationProvider, { initialInvocation, initialScreen, children: /* @__PURE__ */ jsx37(AppRoot, {}) })
8904
9566
  );
8905
9567
  return waitUntilExit();
8906
9568
  }
@@ -8914,7 +9576,7 @@ program.addHelpCommand();
8914
9576
  var applyAddSkillOptions = (cmd) => cmd.option("-g, --global", "Install globally (user-level) instead of project-level").option(
8915
9577
  "-a, --agent <agents...>",
8916
9578
  "Target agents to install to (claude-code, codex, cursor, opencode, and more)"
8917
- ).option("-s, --skill <skills...>", "Install specific skills by name").option("-l, --list", "List available skills in the repository without installing").option("-y, --yes", "Skip confirmation prompts").option("--all", "Install all skills to all agents without prompts (implies -y -g)");
9579
+ ).option("-s, --skill <skills...>", "Install specific skills by name").option("-l, --list", "List available skills in the repository without installing").option("-y, --yes", "Skip confirmation prompts").option("--license-key <key>", "License key for paid/private skills").option("--all", "Install all skills to all agents without prompts (implies -y -g)");
8918
9580
  function normalizeOptions(options) {
8919
9581
  const normalized = { ...options };
8920
9582
  if (normalized.all) {
@@ -8983,6 +9645,11 @@ applyAddSkillOptions(
8983
9645
  await launch({ intent: "add-skill", source, options }, initialAddSkillScreen(source));
8984
9646
  })
8985
9647
  );
9648
+ applyAddSkillOptions(
9649
+ addCmd.command("bundle <slug>").description("Add all skills from a bundle").action(async (slug, options) => {
9650
+ await launch({ intent: "add-bundle", source: slug, options }, "add-bundle-select");
9651
+ })
9652
+ );
8986
9653
  addCmd.command("docs").description("Add docs").action(async (options) => {
8987
9654
  await launch({ intent: "add-docs", options }, "add-docs");
8988
9655
  });