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.
- package/dist/index.js +1760 -1093
- 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.
|
|
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/
|
|
1369
|
-
import {
|
|
1370
|
-
import { Box as
|
|
1371
|
-
import
|
|
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/
|
|
1395
|
-
import
|
|
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/
|
|
1398
|
-
import {
|
|
1399
|
-
|
|
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/
|
|
1402
|
-
import
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
};
|
|
1411
|
-
|
|
1412
|
-
|
|
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 ||
|
|
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
|
|
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
|
|
2729
|
-
import { jsx as
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
2758
|
-
import { jsx as
|
|
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__ */
|
|
3340
|
+
return /* @__PURE__ */ jsx10(Text9, { color, children: label });
|
|
2768
3341
|
}
|
|
2769
3342
|
|
|
2770
3343
|
// src/tui/controls/SelectMenu.tsx
|
|
2771
|
-
import { jsx as
|
|
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__ */
|
|
2782
|
-
showDivider && /* @__PURE__ */
|
|
2783
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
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 =
|
|
2802
|
-
|
|
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 =
|
|
3388
|
+
const clearValue = React6.useCallback(() => {
|
|
2816
3389
|
skipNextChangeRef.current = true;
|
|
2817
3390
|
onClear();
|
|
2818
3391
|
}, [onClear]);
|
|
2819
|
-
|
|
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 =
|
|
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
|
|
3413
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
2891
3414
|
import TextInput from "ink-text-input";
|
|
2892
|
-
import
|
|
3415
|
+
import React8 from "react";
|
|
2893
3416
|
|
|
2894
3417
|
// src/tui/controls/SingleSelect.tsx
|
|
2895
|
-
import { Box as
|
|
2896
|
-
import
|
|
2897
|
-
import { jsx as
|
|
2898
|
-
var
|
|
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] =
|
|
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] =
|
|
2914
|
-
const [filter, setFilter] =
|
|
2915
|
-
const showFilter = enableFilter ?? items.length >=
|
|
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 =
|
|
3440
|
+
const resetFocus = React7.useCallback(() => {
|
|
2918
3441
|
setCursor(0);
|
|
2919
3442
|
setInfoIndex(null);
|
|
2920
3443
|
}, []);
|
|
2921
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
3000
|
-
showFilter && /* @__PURE__ */
|
|
3001
|
-
/* @__PURE__ */
|
|
3002
|
-
/* @__PURE__ */
|
|
3003
|
-
/* @__PURE__ */
|
|
3004
|
-
filter && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
3020
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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 =
|
|
3594
|
+
const riskyScanRows = React8.useMemo(() => getRiskyScanRows(scanRows), [scanRows]);
|
|
3072
3595
|
if (manualView === "details") {
|
|
3073
|
-
return /* @__PURE__ */
|
|
3074
|
-
/* @__PURE__ */
|
|
3075
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3097
|
-
/* @__PURE__ */
|
|
3098
|
-
selectedRow.error ? /* @__PURE__ */
|
|
3099
|
-
/* @__PURE__ */
|
|
3100
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3103
|
-
/* @__PURE__ */
|
|
3104
|
-
|
|
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__ */
|
|
3112
|
-
/* @__PURE__ */
|
|
3113
|
-
selectedRow.truncated ? /* @__PURE__ */
|
|
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__ */
|
|
3116
|
-
!selectedRow.error && selectedRow.signals.length > 0 ? /* @__PURE__ */
|
|
3117
|
-
/* @__PURE__ */
|
|
3118
|
-
selectedRow.signals.slice(0, 12).map((sig, idx) => /* @__PURE__ */
|
|
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__ */
|
|
3646
|
+
selectedRow.signals.length > 12 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `\u2026 ${selectedRow.signals.length - 12} more` }) : null
|
|
3124
3647
|
] }) : null,
|
|
3125
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3130
|
-
/* @__PURE__ */
|
|
3131
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3656
|
+
/* @__PURE__ */ jsx13(Text11, { bold: true, children: "install" }),
|
|
3134
3657
|
" and press Enter."
|
|
3135
3658
|
] }),
|
|
3136
|
-
/* @__PURE__ */
|
|
3137
|
-
/* @__PURE__ */
|
|
3138
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3678
|
+
/* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: TEXT_INPUT_HINT }) })
|
|
3156
3679
|
] });
|
|
3157
3680
|
}
|
|
3158
|
-
return /* @__PURE__ */
|
|
3159
|
-
/* @__PURE__ */
|
|
3160
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3686
|
+
/* @__PURE__ */ jsx13(Text11, { bold: true, children: "install" }),
|
|
3164
3687
|
"."
|
|
3165
3688
|
] }),
|
|
3166
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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] =
|
|
3220
|
-
const [scanStatus, setScanStatus] =
|
|
3221
|
-
const [requiresManual, setRequiresManual] =
|
|
3222
|
-
const [confirmText, setConfirmText] =
|
|
3223
|
-
const [scanRows, setScanRows] =
|
|
3224
|
-
const [manualView, setManualView] =
|
|
3225
|
-
const [selectedRow, setSelectedRow] =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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__ */
|
|
3353
|
-
/* @__PURE__ */
|
|
3354
|
-
/* @__PURE__ */
|
|
3355
|
-
/* @__PURE__ */
|
|
3356
|
-
addSkill.targetAgents ? /* @__PURE__ */
|
|
3357
|
-
addSkill.installGlobally !== void 0 ? /* @__PURE__ */
|
|
3358
|
-
addSkill.installMode ? /* @__PURE__ */
|
|
3359
|
-
addSkill.installMode === "symlink" ? /* @__PURE__ */
|
|
3360
|
-
addSkill.installMode === "copy" && agentPaths.length > 0 ? /* @__PURE__ */
|
|
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__ */
|
|
3363
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
3403
|
-
import
|
|
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
|
|
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 ||
|
|
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
|
|
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 =
|
|
3916
|
-
const [status, setStatus] =
|
|
3917
|
-
const [selected, setSelected] =
|
|
3918
|
-
const [results, setResults] =
|
|
3919
|
-
const [notice, setNotice] =
|
|
3920
|
-
const [pendingSources, setPendingSources] =
|
|
3921
|
-
const [currentIndex, setCurrentIndex] =
|
|
3922
|
-
const [repoRefs, setRepoRefs] =
|
|
3923
|
-
const [refStatus, setRefStatus] =
|
|
3924
|
-
const [refError, setRefError] =
|
|
3925
|
-
const [refSelections, setRefSelections] =
|
|
3926
|
-
const [includePrerelease, setIncludePrerelease] =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
4007
|
-
/* @__PURE__ */
|
|
4008
|
-
/* @__PURE__ */
|
|
4009
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4016
|
-
/* @__PURE__ */
|
|
4017
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4023
|
-
/* @__PURE__ */
|
|
4024
|
-
/* @__PURE__ */
|
|
4025
|
-
/* @__PURE__ */
|
|
4026
|
-
/* @__PURE__ */
|
|
4027
|
-
/* @__PURE__ */
|
|
4028
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4031
|
-
refStatus === "loading" ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
4065
|
-
/* @__PURE__ */
|
|
4066
|
-
built.note ? /* @__PURE__ */
|
|
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__ */
|
|
4073
|
-
/* @__PURE__ */
|
|
4074
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4091
|
-
/* @__PURE__ */
|
|
4092
|
-
installed.length > 0 ? /* @__PURE__ */
|
|
4093
|
-
/* @__PURE__ */
|
|
4094
|
-
installed.map((item) => /* @__PURE__ */
|
|
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__ */
|
|
4101
|
-
/* @__PURE__ */
|
|
4102
|
-
skipped.map((item) => /* @__PURE__ */
|
|
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__ */
|
|
4108
|
-
/* @__PURE__ */
|
|
4109
|
-
failed.map((item) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
4150
|
-
/* @__PURE__ */
|
|
4151
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
4189
|
-
import
|
|
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
|
|
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] =
|
|
4686
|
-
const [error, setError] =
|
|
4687
|
-
const didRun =
|
|
4688
|
-
|
|
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__ */
|
|
4743
|
-
/* @__PURE__ */
|
|
4744
|
-
/* @__PURE__ */
|
|
4745
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4749
|
-
/* @__PURE__ */
|
|
4750
|
-
/* @__PURE__ */
|
|
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
|
|
4760
|
-
import
|
|
4761
|
-
import { jsx as
|
|
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
|
-
|
|
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__ */
|
|
4796
|
-
/* @__PURE__ */
|
|
4797
|
-
/* @__PURE__ */
|
|
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
|
|
4814
|
-
import
|
|
4815
|
-
import { jsx as
|
|
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 =
|
|
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__ */
|
|
4833
|
-
/* @__PURE__ */
|
|
4834
|
-
summary ? /* @__PURE__ */
|
|
4835
|
-
/* @__PURE__ */
|
|
4836
|
-
summaryLines.map(({ line, key }) => /* @__PURE__ */
|
|
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__ */
|
|
4839
|
-
/* @__PURE__ */
|
|
4840
|
-
failed.map((r) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
4855
|
-
/* @__PURE__ */
|
|
4856
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
4882
|
-
import
|
|
4883
|
-
import { jsx as
|
|
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
|
-
|
|
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__ */
|
|
4911
|
-
/* @__PURE__ */
|
|
4912
|
-
/* @__PURE__ */
|
|
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
|
|
4935
|
-
import
|
|
4936
|
-
import { jsx as
|
|
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] =
|
|
4941
|
-
const [error, setError] =
|
|
4942
|
-
const [manualView, setManualView] =
|
|
4943
|
-
const [confirmText, setConfirmText] =
|
|
4944
|
-
const [selectedRow, setSelectedRow] =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
5015
|
-
/* @__PURE__ */
|
|
5016
|
-
/* @__PURE__ */
|
|
5017
|
-
/* @__PURE__ */
|
|
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__ */
|
|
5023
|
-
/* @__PURE__ */
|
|
5024
|
-
/* @__PURE__ */
|
|
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__ */
|
|
5036
|
-
/* @__PURE__ */
|
|
5037
|
-
/* @__PURE__ */
|
|
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__ */
|
|
5425
|
+
return /* @__PURE__ */ jsx21(Box19, { padding: 1 });
|
|
5062
5426
|
}
|
|
5063
|
-
return /* @__PURE__ */
|
|
5064
|
-
/* @__PURE__ */
|
|
5065
|
-
/* @__PURE__ */
|
|
5066
|
-
/* @__PURE__ */
|
|
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
|
|
5076
|
-
import
|
|
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
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
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
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
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
|
-
|
|
5627
|
-
|
|
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,
|
|
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,
|
|
6161
|
+
await writeFile3(targetPath, fileContent, "utf-8");
|
|
5636
6162
|
}
|
|
5637
6163
|
const localSkill = {
|
|
5638
|
-
name:
|
|
5639
|
-
description:
|
|
6164
|
+
name: fetched.installName,
|
|
6165
|
+
description: fetched.description,
|
|
5640
6166
|
path: skillDir,
|
|
5641
|
-
rawContent:
|
|
5642
|
-
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:
|
|
6173
|
+
sourceUrl: fetched.sourceUrl,
|
|
5648
6174
|
source: sourceIdentifier,
|
|
5649
|
-
skillPath:
|
|
6175
|
+
skillPath: fetched.sourceUrl
|
|
5650
6176
|
});
|
|
5651
6177
|
}
|
|
5652
|
-
|
|
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
|
|
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] =
|
|
6479
|
+
const [status, setStatus] = React17.useState(
|
|
5977
6480
|
addSkill.skills && addSkill.skills.length > 0 ? "ready" : "loading"
|
|
5978
6481
|
);
|
|
5979
|
-
const [error, setError] =
|
|
5980
|
-
const [listMode, setListMode] =
|
|
5981
|
-
const [showLoading, setShowLoading] =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
6194
|
-
/* @__PURE__ */
|
|
6195
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6719
|
+
return /* @__PURE__ */ jsx22(Box20, { padding: 1 });
|
|
6200
6720
|
}
|
|
6201
6721
|
if (status === "loading") {
|
|
6202
|
-
return /* @__PURE__ */
|
|
6203
|
-
/* @__PURE__ */
|
|
6204
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6213
|
-
/* @__PURE__ */
|
|
6214
|
-
/* @__PURE__ */
|
|
6215
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6221
|
-
/* @__PURE__ */
|
|
6222
|
-
skills.map((skill) => /* @__PURE__ */
|
|
6223
|
-
/* @__PURE__ */
|
|
6224
|
-
skill.description ? /* @__PURE__ */
|
|
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__ */
|
|
6746
|
+
/* @__PURE__ */ jsx22(Text18, { dimColor: true, children: BACK_QUIT_HINT })
|
|
6227
6747
|
] });
|
|
6228
6748
|
}
|
|
6229
6749
|
if (skills.length === 0) {
|
|
6230
|
-
return /* @__PURE__ */
|
|
6231
|
-
/* @__PURE__ */
|
|
6232
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6236
|
-
/* @__PURE__ */
|
|
6237
|
-
/* @__PURE__ */
|
|
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
|
|
6270
|
-
import
|
|
6271
|
-
import
|
|
6272
|
-
import { jsx as
|
|
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] =
|
|
6795
|
+
const [value, setValue] = React18.useState(
|
|
6276
6796
|
addSkill.source ?? invocation.source ?? lastSource ?? ""
|
|
6277
6797
|
);
|
|
6278
|
-
const didAutofillRef =
|
|
6798
|
+
const didAutofillRef = React18.useRef(false);
|
|
6279
6799
|
const { wrapOnChange } = useTextInput({
|
|
6280
6800
|
onClear: () => {
|
|
6281
6801
|
setValue("");
|
|
6282
6802
|
}
|
|
6283
6803
|
});
|
|
6284
|
-
|
|
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__ */
|
|
6302
|
-
/* @__PURE__ */
|
|
6303
|
-
/* @__PURE__ */
|
|
6304
|
-
/* @__PURE__ */
|
|
6305
|
-
/* @__PURE__ */
|
|
6306
|
-
/* @__PURE__ */
|
|
6307
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
6315
|
-
import
|
|
6316
|
-
import { Fragment as Fragment2, jsx as
|
|
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] =
|
|
6320
|
-
const [mode, setMode] =
|
|
6321
|
-
const [availableAgents, setAvailableAgents] =
|
|
6322
|
-
const [lastSelected, setLastSelected] =
|
|
6323
|
-
const [showLoading, setShowLoading] =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
6905
|
+
return /* @__PURE__ */ jsx24(Box22, { padding: 1 });
|
|
6386
6906
|
}
|
|
6387
6907
|
if (status === "loading") {
|
|
6388
|
-
return /* @__PURE__ */
|
|
6389
|
-
/* @__PURE__ */
|
|
6390
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6413
|
-
/* @__PURE__ */
|
|
6414
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6448
|
-
/* @__PURE__ */
|
|
6449
|
-
/* @__PURE__ */
|
|
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
|
|
6482
|
-
import
|
|
6483
|
-
import { jsx as
|
|
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] =
|
|
6513
|
-
const [error, setError] =
|
|
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
|
-
|
|
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__ */
|
|
6558
|
-
/* @__PURE__ */
|
|
6559
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6567
|
-
/* @__PURE__ */
|
|
6568
|
-
/* @__PURE__ */
|
|
6569
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6573
|
-
/* @__PURE__ */
|
|
6574
|
-
/* @__PURE__ */
|
|
6575
|
-
/* @__PURE__ */
|
|
6576
|
-
/* @__PURE__ */
|
|
6577
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6580
|
-
/* @__PURE__ */
|
|
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
|
|
6599
|
-
import
|
|
6600
|
-
import
|
|
6601
|
-
import { jsx as
|
|
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] =
|
|
6609
|
-
const [status, setStatus] =
|
|
6610
|
-
const [error, setError] =
|
|
6611
|
-
const [preview, setPreview] =
|
|
6612
|
-
const debounceRef =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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__ */
|
|
6727
|
-
/* @__PURE__ */
|
|
6728
|
-
/* @__PURE__ */
|
|
6729
|
-
/* @__PURE__ */
|
|
6730
|
-
/* @__PURE__ */
|
|
6731
|
-
/* @__PURE__ */
|
|
6732
|
-
|
|
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__ */
|
|
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__ */
|
|
6754
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
6778
|
-
/* @__PURE__ */
|
|
6779
|
-
/* @__PURE__ */
|
|
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
|
|
6785
|
-
import
|
|
6786
|
-
import { jsx as
|
|
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 =
|
|
7311
|
+
const didRun = React22.useRef(false);
|
|
6792
7312
|
const outputPath = invocation.options?.output ?? null;
|
|
6793
7313
|
const outputFormat = invocation.options?.json ? "json" : "markdown";
|
|
6794
|
-
|
|
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__ */
|
|
6824
|
-
/* @__PURE__ */
|
|
6825
|
-
invocation.source ? /* @__PURE__ */
|
|
6826
|
-
outputPath ? /* @__PURE__ */
|
|
6827
|
-
outputFormat === "json" ? /* @__PURE__ */
|
|
6828
|
-
/* @__PURE__ */
|
|
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
|
|
6837
|
-
import
|
|
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
|
|
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 =
|
|
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
|
|
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] =
|
|
6956
|
-
const [selectedAgent, setSelectedAgent] =
|
|
6957
|
-
|
|
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
|
-
|
|
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__ */
|
|
6996
|
-
/* @__PURE__ */
|
|
6997
|
-
selectedAgent.projectSkills.length > 0 ? /* @__PURE__ */
|
|
6998
|
-
/* @__PURE__ */
|
|
6999
|
-
selectedAgent.projectSkills.map((name) => /* @__PURE__ */
|
|
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__ */
|
|
7002
|
-
/* @__PURE__ */
|
|
7003
|
-
selectedAgent.globalSkills.map((name) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
7033
|
-
/* @__PURE__ */
|
|
7034
|
-
/* @__PURE__ */
|
|
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
|
|
7052
|
-
import { jsx as
|
|
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__ */
|
|
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
|
|
7122
|
-
import
|
|
7123
|
-
import { jsx as
|
|
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] =
|
|
7127
|
-
const [selectedAgent, setSelectedAgent] =
|
|
7128
|
-
const [selectedSkills, setSelectedSkills] =
|
|
7129
|
-
const [isRemoving, setIsRemoving] =
|
|
7130
|
-
|
|
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
|
-
|
|
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__ */
|
|
7176
|
-
/* @__PURE__ */
|
|
7177
|
-
/* @__PURE__ */
|
|
7178
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7183
|
-
/* @__PURE__ */
|
|
7184
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7202
|
-
/* @__PURE__ */
|
|
7203
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7224
|
-
/* @__PURE__ */
|
|
7225
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7229
|
-
/* @__PURE__ */
|
|
7230
|
-
selectedSkills.map((skill) => /* @__PURE__ */
|
|
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__ */
|
|
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
|
|
7299
|
-
import
|
|
7300
|
-
import { jsx as
|
|
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
|
-
|
|
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__ */
|
|
7321
|
-
/* @__PURE__ */
|
|
7322
|
-
/* @__PURE__ */
|
|
7323
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7328
|
-
/* @__PURE__ */
|
|
7329
|
-
plugins.map((plugin) => /* @__PURE__ */
|
|
7330
|
-
/* @__PURE__ */
|
|
7331
|
-
plugin.description ? /* @__PURE__ */
|
|
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__ */
|
|
7853
|
+
/* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT })
|
|
7334
7854
|
] });
|
|
7335
7855
|
}
|
|
7336
|
-
return /* @__PURE__ */
|
|
7337
|
-
/* @__PURE__ */
|
|
7338
|
-
/* @__PURE__ */
|
|
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
|
|
7366
|
-
import
|
|
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
|
|
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,
|
|
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
|
|
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] =
|
|
7531
|
-
const [error, setError] =
|
|
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
|
-
|
|
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__ */
|
|
7600
|
-
/* @__PURE__ */
|
|
7601
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7609
|
-
/* @__PURE__ */
|
|
7610
|
-
/* @__PURE__ */
|
|
7611
|
-
/* @__PURE__ */
|
|
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__ */
|
|
7617
|
-
/* @__PURE__ */
|
|
7618
|
-
warnings.length > 0 ? /* @__PURE__ */
|
|
7619
|
-
/* @__PURE__ */
|
|
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
|
|
7670
|
-
import
|
|
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
|
|
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] =
|
|
7897
|
-
const [error, setError] =
|
|
7898
|
-
const [rows, setRows] =
|
|
7899
|
-
const [progress, setProgress] =
|
|
7900
|
-
const [selected, setSelected] =
|
|
7901
|
-
const [removeTargets, setRemoveTargets] =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
7994
|
-
const summary =
|
|
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__ */
|
|
7997
|
-
/* @__PURE__ */
|
|
7998
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8003
|
-
/* @__PURE__ */
|
|
8004
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8011
|
-
/* @__PURE__ */
|
|
8012
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
8027
|
-
/* @__PURE__ */
|
|
8028
|
-
/* @__PURE__ */
|
|
8029
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8041
|
-
/* @__PURE__ */
|
|
8042
|
-
/* @__PURE__ */
|
|
8043
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8565
|
+
/* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `(${selected.skill.slug})` })
|
|
8046
8566
|
] }),
|
|
8047
|
-
selected.error ? /* @__PURE__ */
|
|
8048
|
-
/* @__PURE__ */
|
|
8049
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8052
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8055
|
-
selected.ruleset ? /* @__PURE__ */
|
|
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__ */
|
|
8058
|
-
/* @__PURE__ */
|
|
8059
|
-
locationLabels.length > 0 ? /* @__PURE__ */
|
|
8060
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8087
|
-
/* @__PURE__ */
|
|
8088
|
-
/* @__PURE__ */
|
|
8089
|
-
/* @__PURE__ */
|
|
8090
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8612
|
+
/* @__PURE__ */ jsx33(Text28, { dimColor: true, children: t.label }),
|
|
8093
8613
|
" ",
|
|
8094
8614
|
t.path
|
|
8095
8615
|
] }, t.path)) }),
|
|
8096
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8145
|
-
/* @__PURE__ */
|
|
8146
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
8155
|
-
/* @__PURE__ */
|
|
8156
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8160
|
-
/* @__PURE__ */
|
|
8161
|
-
/* @__PURE__ */
|
|
8162
|
-
/* @__PURE__ */
|
|
8163
|
-
/* @__PURE__ */
|
|
8164
|
-
/* @__PURE__ */
|
|
8165
|
-
/* @__PURE__ */
|
|
8166
|
-
/* @__PURE__ */
|
|
8167
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
|
8192
|
-
import
|
|
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
|
|
8778
|
+
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
8259
8779
|
function UpdateDocsScreen() {
|
|
8260
|
-
const [status, setStatus] =
|
|
8261
|
-
const [summary, setSummary] =
|
|
8780
|
+
const [status, setStatus] = React28.useState("running");
|
|
8781
|
+
const [summary, setSummary] = React28.useState(null);
|
|
8262
8782
|
const spinner = useSpinnerFrame(status === "running");
|
|
8263
|
-
|
|
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__ */
|
|
8282
|
-
/* @__PURE__ */
|
|
8283
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8291
|
-
/* @__PURE__ */
|
|
8292
|
-
/* @__PURE__ */
|
|
8293
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8298
|
-
/* @__PURE__ */
|
|
8299
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8304
|
-
/* @__PURE__ */
|
|
8305
|
-
summary.updated.length > 0 ? /* @__PURE__ */
|
|
8306
|
-
/* @__PURE__ */
|
|
8307
|
-
summary.updated.map((item) => /* @__PURE__ */
|
|
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__ */
|
|
8314
|
-
/* @__PURE__ */
|
|
8315
|
-
summary.skipped.map((item) => /* @__PURE__ */
|
|
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__ */
|
|
8321
|
-
/* @__PURE__ */
|
|
8322
|
-
summary.failed.map((item) => /* @__PURE__ */
|
|
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__ */
|
|
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
|
|
8334
|
-
import
|
|
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 {
|
|
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
|
|
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 =
|
|
8435
|
-
if (await pathExists3(
|
|
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
|
|
9130
|
+
const tempDir = await mkdtemp6(join22(tmpdir7(), "playbooks-skill-"));
|
|
8477
9131
|
registerTempDir(tempDir);
|
|
8478
9132
|
try {
|
|
8479
|
-
await
|
|
9133
|
+
await mkdir8(tempDir, { recursive: true });
|
|
8480
9134
|
if (files) {
|
|
8481
9135
|
for (const [filePath, fileContent] of files.entries()) {
|
|
8482
|
-
const targetPath =
|
|
9136
|
+
const targetPath = join22(tempDir, filePath);
|
|
8483
9137
|
if (!isPathSafe(tempDir, targetPath)) {
|
|
8484
9138
|
continue;
|
|
8485
9139
|
}
|
|
8486
|
-
await
|
|
8487
|
-
await
|
|
9140
|
+
await mkdir8(dirname9(targetPath), { recursive: true });
|
|
9141
|
+
await writeFile6(targetPath, fileContent, "utf-8");
|
|
8488
9142
|
}
|
|
8489
9143
|
} else if (content) {
|
|
8490
|
-
await
|
|
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
|
|
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] =
|
|
8591
|
-
const [targets, setTargets] =
|
|
8592
|
-
const [selected, setSelected] =
|
|
8593
|
-
const [summary, setSummary] =
|
|
8594
|
-
const [showOnlyNeeds, setShowOnlyNeeds] =
|
|
8595
|
-
const [rateLimited, setRateLimited] =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
8661
|
-
/* @__PURE__ */
|
|
8662
|
-
/* @__PURE__ */
|
|
8663
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8668
|
-
/* @__PURE__ */
|
|
8669
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8678
|
-
/* @__PURE__ */
|
|
8679
|
-
/* @__PURE__ */
|
|
8680
|
-
rateLimited ? /* @__PURE__ */
|
|
8681
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8685
|
-
/* @__PURE__ */
|
|
8686
|
-
rateLimited ? /* @__PURE__ */
|
|
8687
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8711
|
-
/* @__PURE__ */
|
|
8712
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8724
|
-
/* @__PURE__ */
|
|
8725
|
-
/* @__PURE__ */
|
|
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__ */
|
|
8729
|
-
/* @__PURE__ */
|
|
8730
|
-
summary.updated.length > 0 ? /* @__PURE__ */
|
|
8731
|
-
summary.skipped.length > 0 ? /* @__PURE__ */
|
|
8732
|
-
summary.failed.length > 0 ? /* @__PURE__ */
|
|
8733
|
-
/* @__PURE__ */
|
|
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
|
|
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__ */
|
|
9426
|
+
return /* @__PURE__ */ jsx36(MainMenu, {});
|
|
8769
9427
|
case "add-source":
|
|
8770
|
-
return /* @__PURE__ */
|
|
9428
|
+
return /* @__PURE__ */ jsx36(AddSourceScreen, {});
|
|
8771
9429
|
case "add-docs":
|
|
8772
|
-
return /* @__PURE__ */
|
|
9430
|
+
return /* @__PURE__ */ jsx36(AddDocsScreen, {});
|
|
8773
9431
|
case "add-marketplace-plugins":
|
|
8774
|
-
return /* @__PURE__ */
|
|
9432
|
+
return /* @__PURE__ */ jsx36(MarketplacePluginScreen, {});
|
|
8775
9433
|
case "add-marketplace-skills":
|
|
8776
|
-
return /* @__PURE__ */
|
|
9434
|
+
return /* @__PURE__ */ jsx36(MarketplaceSkillScreen, {});
|
|
8777
9435
|
case "find-skill-search":
|
|
8778
|
-
return /* @__PURE__ */
|
|
9436
|
+
return /* @__PURE__ */ jsx36(FindSkillSearchScreen, {});
|
|
8779
9437
|
case "find-skill-results":
|
|
8780
|
-
return /* @__PURE__ */
|
|
9438
|
+
return /* @__PURE__ */ jsx36(FindSkillResultsScreen, {});
|
|
8781
9439
|
case "scan-skills":
|
|
8782
|
-
return /* @__PURE__ */
|
|
9440
|
+
return /* @__PURE__ */ jsx36(ScanSkillsScreen, {});
|
|
8783
9441
|
case "get-url":
|
|
8784
|
-
return /* @__PURE__ */
|
|
9442
|
+
return /* @__PURE__ */ jsx36(GetUrlScreen, {});
|
|
8785
9443
|
case "add-skill-select":
|
|
8786
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
9450
|
+
return /* @__PURE__ */ jsx36(AddSecurityScanScreen, {});
|
|
8789
9451
|
case "add-targets":
|
|
8790
|
-
return /* @__PURE__ */
|
|
9452
|
+
return /* @__PURE__ */ jsx36(AddTargetsScreen, {});
|
|
8791
9453
|
case "add-scope":
|
|
8792
|
-
return /* @__PURE__ */
|
|
9454
|
+
return /* @__PURE__ */ jsx36(AddScopeScreen, {});
|
|
8793
9455
|
case "add-mode":
|
|
8794
|
-
return /* @__PURE__ */
|
|
9456
|
+
return /* @__PURE__ */ jsx36(AddModeScreen, {});
|
|
8795
9457
|
case "add-confirm":
|
|
8796
|
-
return /* @__PURE__ */
|
|
9458
|
+
return /* @__PURE__ */ jsx36(AddConfirmScreen, {});
|
|
8797
9459
|
case "add-install":
|
|
8798
|
-
return /* @__PURE__ */
|
|
9460
|
+
return /* @__PURE__ */ jsx36(AddInstallScreen, {});
|
|
8799
9461
|
case "add-result":
|
|
8800
|
-
return /* @__PURE__ */
|
|
9462
|
+
return /* @__PURE__ */ jsx36(AddResultScreen, {});
|
|
8801
9463
|
case "list":
|
|
8802
|
-
return /* @__PURE__ */
|
|
9464
|
+
return /* @__PURE__ */ jsx36(ListScreen, {});
|
|
8803
9465
|
case "manage":
|
|
8804
|
-
return /* @__PURE__ */
|
|
9466
|
+
return /* @__PURE__ */ jsx36(ManageScreen, {});
|
|
8805
9467
|
case "update":
|
|
8806
|
-
return /* @__PURE__ */
|
|
9468
|
+
return /* @__PURE__ */ jsx36(UpdateScreen, {});
|
|
8807
9469
|
case "update-docs":
|
|
8808
|
-
return /* @__PURE__ */
|
|
9470
|
+
return /* @__PURE__ */ jsx36(UpdateDocsScreen, {});
|
|
8809
9471
|
default:
|
|
8810
9472
|
return null;
|
|
8811
9473
|
}
|
|
8812
9474
|
};
|
|
8813
|
-
return /* @__PURE__ */
|
|
8814
|
-
/* @__PURE__ */
|
|
9475
|
+
return /* @__PURE__ */ jsxs31(Fragment4, { children: [
|
|
9476
|
+
/* @__PURE__ */ jsx36(BrandHeader, {}),
|
|
8815
9477
|
render2(screen),
|
|
8816
|
-
/* @__PURE__ */
|
|
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
|
|
9558
|
+
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
8897
9559
|
function AppRoot() {
|
|
8898
9560
|
useKeyboardShortcuts();
|
|
8899
|
-
return /* @__PURE__ */
|
|
9561
|
+
return /* @__PURE__ */ jsx37(ScreenRouter, {});
|
|
8900
9562
|
}
|
|
8901
9563
|
function runApp(initialInvocation, initialScreen) {
|
|
8902
9564
|
const { waitUntilExit } = render(
|
|
8903
|
-
/* @__PURE__ */
|
|
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
|
});
|