playbooks 0.1.15 → 0.1.17
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 +1226 -471
- 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.17",
|
|
12
12
|
description: "Install agent skills, MCPs and docs into your coding agents from any git repository.",
|
|
13
13
|
type: "module",
|
|
14
14
|
bin: {
|
|
@@ -1694,7 +1694,7 @@ import { readFile as readFile2, readdir as readdir3, realpath, stat as stat2 } f
|
|
|
1694
1694
|
import { join as join8, relative as relative3, sep as sep3 } from "path";
|
|
1695
1695
|
|
|
1696
1696
|
// src/scanner/static-scan/shared.ts
|
|
1697
|
-
var SKILL_STATIC_SCAN_RULESET_VERSION = "static-scan@2026-02-07.
|
|
1697
|
+
var SKILL_STATIC_SCAN_RULESET_VERSION = "static-scan@2026-02-07.5";
|
|
1698
1698
|
var clamp = (n, min, max) => Math.min(Math.max(n, min), max);
|
|
1699
1699
|
var levelFromScore = (score) => {
|
|
1700
1700
|
if (score >= 85) return "critical";
|
|
@@ -1719,7 +1719,7 @@ var isProbablyTextPath = (path) => {
|
|
|
1719
1719
|
if (base === "makefile" || base === "dockerfile" || base === "license" || base === "readme" || base === "readme.md") {
|
|
1720
1720
|
return true;
|
|
1721
1721
|
}
|
|
1722
|
-
return lower.endsWith(".md") || lower.endsWith(".txt") || lower.endsWith(".json") || lower.endsWith(".yaml") || lower.endsWith(".yml") || lower.endsWith(".toml") || lower.endsWith(".ini") || lower.endsWith(".env") || lower.endsWith(".sh") || lower.endsWith(".bash") || lower.endsWith(".zsh") || lower.endsWith(".ps1") || lower.endsWith(".py") || lower.endsWith(".rb") || lower.endsWith(".php") || lower.endsWith(".go") || lower.endsWith(".rs") || lower.endsWith(".java") || lower.endsWith(".ts") || lower.endsWith(".tsx") || lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs");
|
|
1722
|
+
return lower.endsWith(".md") || lower.endsWith(".txt") || lower.endsWith(".json") || lower.endsWith(".yaml") || lower.endsWith(".yml") || lower.endsWith(".toml") || lower.endsWith(".ini") || lower.endsWith(".env") || lower.endsWith(".sh") || lower.endsWith(".bash") || lower.endsWith(".zsh") || lower.endsWith(".ps1") || lower.endsWith(".py") || lower.endsWith(".rb") || lower.endsWith(".php") || lower.endsWith(".go") || lower.endsWith(".rs") || lower.endsWith(".java") || lower.endsWith(".ts") || lower.endsWith(".mts") || lower.endsWith(".cts") || lower.endsWith(".tsx") || lower.endsWith(".js") || lower.endsWith(".jsx") || lower.endsWith(".mjs") || lower.endsWith(".cjs");
|
|
1723
1723
|
};
|
|
1724
1724
|
var snippetFromIndex = (content, index, maxLen = 180) => {
|
|
1725
1725
|
const start = Math.max(0, index - 60);
|
|
@@ -1803,9 +1803,28 @@ async function collectSkillScanFiles(skillDir, options = {}) {
|
|
|
1803
1803
|
const baseRealWithSep = baseReal.endsWith(sep3) ? baseReal : `${baseReal}${sep3}`;
|
|
1804
1804
|
const files = [];
|
|
1805
1805
|
const visitedDirs = /* @__PURE__ */ new Set([baseReal]);
|
|
1806
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1806
1807
|
let truncated = false;
|
|
1807
1808
|
let bytes = 0;
|
|
1808
1809
|
let skipped = 0;
|
|
1810
|
+
const skillMdFull = join8(skillDir, "SKILL.md");
|
|
1811
|
+
const skillMdRel = "SKILL.md";
|
|
1812
|
+
try {
|
|
1813
|
+
if (files.length < maxFiles && isPathSafe(skillDir, skillMdFull) && isProbablyTextPath(skillMdRel)) {
|
|
1814
|
+
const s = await stat2(skillMdFull);
|
|
1815
|
+
const size = typeof s.size === "number" ? s.size : 0;
|
|
1816
|
+
if (size > 0 && size <= maxFileBytes && bytes + size <= maxTotalBytes) {
|
|
1817
|
+
const content = await readFile2(skillMdFull, "utf-8");
|
|
1818
|
+
const usedSize = size > 0 ? size : content.length;
|
|
1819
|
+
if (usedSize > 0 && usedSize <= maxFileBytes && bytes + usedSize <= maxTotalBytes) {
|
|
1820
|
+
bytes += usedSize;
|
|
1821
|
+
files.push({ path: skillMdRel, content, size: usedSize });
|
|
1822
|
+
seen.add(skillMdRel.toLowerCase());
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
} catch {
|
|
1827
|
+
}
|
|
1809
1828
|
const queue = [skillDir];
|
|
1810
1829
|
while (queue.length > 0) {
|
|
1811
1830
|
const current = queue.shift();
|
|
@@ -1813,6 +1832,7 @@ async function collectSkillScanFiles(skillDir, options = {}) {
|
|
|
1813
1832
|
let entries;
|
|
1814
1833
|
try {
|
|
1815
1834
|
entries = await readdir3(current, { withFileTypes: true });
|
|
1835
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
1816
1836
|
} catch {
|
|
1817
1837
|
continue;
|
|
1818
1838
|
}
|
|
@@ -1875,6 +1895,10 @@ async function collectSkillScanFiles(skillDir, options = {}) {
|
|
|
1875
1895
|
skipped += 1;
|
|
1876
1896
|
continue;
|
|
1877
1897
|
}
|
|
1898
|
+
if (seen.has(relPath.toLowerCase())) {
|
|
1899
|
+
skipped += 1;
|
|
1900
|
+
continue;
|
|
1901
|
+
}
|
|
1878
1902
|
if (!isProbablyTextPath(relPath)) {
|
|
1879
1903
|
skipped += 1;
|
|
1880
1904
|
continue;
|
|
@@ -1909,6 +1933,7 @@ async function collectSkillScanFiles(skillDir, options = {}) {
|
|
|
1909
1933
|
}
|
|
1910
1934
|
bytes += usedSize;
|
|
1911
1935
|
files.push({ path: relPath, content, size: usedSize });
|
|
1936
|
+
seen.add(relPath.toLowerCase());
|
|
1912
1937
|
} catch {
|
|
1913
1938
|
skipped += 1;
|
|
1914
1939
|
}
|
|
@@ -1918,6 +1943,329 @@ async function collectSkillScanFiles(skillDir, options = {}) {
|
|
|
1918
1943
|
return { files, truncated, skipped };
|
|
1919
1944
|
}
|
|
1920
1945
|
|
|
1946
|
+
// src/scanner/code-safety-scan.ts
|
|
1947
|
+
var isJsTsPath = (path) => {
|
|
1948
|
+
const lower = path.toLowerCase();
|
|
1949
|
+
return lower.endsWith(".js") || lower.endsWith(".ts") || lower.endsWith(".mjs") || lower.endsWith(".cjs") || lower.endsWith(".jsx") || lower.endsWith(".tsx") || lower.endsWith(".mts") || lower.endsWith(".cts");
|
|
1950
|
+
};
|
|
1951
|
+
var isTestLikePath = (path) => {
|
|
1952
|
+
const p = path.replace(/\\/g, "/").toLowerCase();
|
|
1953
|
+
if (p.includes("/__tests__/")) return true;
|
|
1954
|
+
if (p.includes("/tests/")) return true;
|
|
1955
|
+
if (p.includes("/test/")) return true;
|
|
1956
|
+
if (/\.(test|spec)\.[mc]?[jt]sx?$/.test(p)) return true;
|
|
1957
|
+
return false;
|
|
1958
|
+
};
|
|
1959
|
+
var NETWORK_RE = /\bfetch\s*\(|\baxios\b|\bgot\b|\bnode-fetch\b|\bhttp\.\s*request\b|\bhttps\.\s*request\b|\bXMLHttpRequest\b/i;
|
|
1960
|
+
var CHILD_PROCESS_RE = /\bchild_process\b|from\s+['"]node:child_process['"]|require\s*\(\s*['"]child_process['"]\s*\)/i;
|
|
1961
|
+
var EXEC_CALL_RE = /\b(exec|spawn|execFile|spawnSync|execSync|execFileSync)\s*\(/g;
|
|
1962
|
+
var EVAL_RE = /\beval\s*\(/g;
|
|
1963
|
+
var NEW_FUNCTION_RE = /\bnew\s+Function\s*\(/g;
|
|
1964
|
+
var PROCESS_ENV_RE = /\bprocess\.env\b/g;
|
|
1965
|
+
var READFILE_RE = /\breadFileSync\s*\(|\breadFile\s*\(/g;
|
|
1966
|
+
var BASE64_LITERAL_RE = /['"`]([A-Za-z0-9+/]{200,}={0,2})['"`]/g;
|
|
1967
|
+
var CRYPTO_MINING_RE = /\b(stratum\+tcp|stratum\+ssl|xmrig|cryptonight|coinhive)\b/i;
|
|
1968
|
+
var WS_PORT_RE = /new\s+WebSocket\s*\(\s*['"]ws:\/\/[^'"]+:(\d{2,5})[^'"]*['"]\s*\)/i;
|
|
1969
|
+
var WS_ALLOWED_PORTS = /* @__PURE__ */ new Set([80, 443, 3e3, 8080, 8443]);
|
|
1970
|
+
var mk = (file, content, id, opts, scoreKey) => {
|
|
1971
|
+
const line = Number.isFinite(opts.index) ? lineFromIndex(content, opts.index) : null;
|
|
1972
|
+
const snippet = snippetFromIndex(content, opts.index);
|
|
1973
|
+
return {
|
|
1974
|
+
scoreKey,
|
|
1975
|
+
signal: {
|
|
1976
|
+
id,
|
|
1977
|
+
file,
|
|
1978
|
+
dimension: opts.dimension,
|
|
1979
|
+
type: opts.type,
|
|
1980
|
+
severity: opts.severity,
|
|
1981
|
+
points: opts.points,
|
|
1982
|
+
line: line ?? null,
|
|
1983
|
+
snippet,
|
|
1984
|
+
reason: opts.reason
|
|
1985
|
+
}
|
|
1986
|
+
};
|
|
1987
|
+
};
|
|
1988
|
+
function scanCodeSafetyFile(file) {
|
|
1989
|
+
if (!isJsTsPath(file.path)) return [];
|
|
1990
|
+
const content = typeof file.content === "string" ? file.content : "";
|
|
1991
|
+
if (!content) return [];
|
|
1992
|
+
const out = [];
|
|
1993
|
+
const testLike = isTestLikePath(file.path);
|
|
1994
|
+
const urlLiterals = (() => {
|
|
1995
|
+
const urls = [];
|
|
1996
|
+
const re = /\b(?:https?:\/\/|wss?:\/\/|ws:\/\/)[^\s'"`)+]+/gi;
|
|
1997
|
+
for (let m = re.exec(content); m; m = re.exec(content)) {
|
|
1998
|
+
urls.push(m[0] ?? "");
|
|
1999
|
+
if (urls.length >= 10) break;
|
|
2000
|
+
}
|
|
2001
|
+
return urls;
|
|
2002
|
+
})();
|
|
2003
|
+
const hasRemoteUrlLiteral = (() => {
|
|
2004
|
+
for (const raw of urlLiterals) {
|
|
2005
|
+
try {
|
|
2006
|
+
const parsed = new URL(raw);
|
|
2007
|
+
const host = (parsed.hostname || "").toLowerCase();
|
|
2008
|
+
if (host === "localhost" || host === "0.0.0.0" || host === "::1" || host === "[::1]" || host === "127.0.0.1" || host.startsWith("127.")) {
|
|
2009
|
+
continue;
|
|
2010
|
+
}
|
|
2011
|
+
return true;
|
|
2012
|
+
} catch {
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
return false;
|
|
2016
|
+
})();
|
|
2017
|
+
const envKeys = (() => {
|
|
2018
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2019
|
+
const dotRe = /\bprocess\.env\.([A-Za-z_][A-Za-z0-9_]*)\b/g;
|
|
2020
|
+
const bracketRe = /\bprocess\.env\s*\[\s*['"]([^'"]+)['"]\s*\]/g;
|
|
2021
|
+
const destructureRe = /\{([^}]+)\}\s*=\s*process\.env\b/g;
|
|
2022
|
+
for (let m = dotRe.exec(content); m; m = dotRe.exec(content)) keys.add(m[1] ?? "");
|
|
2023
|
+
for (let m = bracketRe.exec(content); m; m = bracketRe.exec(content)) keys.add(m[1] ?? "");
|
|
2024
|
+
for (let m = destructureRe.exec(content); m; m = destructureRe.exec(content)) {
|
|
2025
|
+
const inner = (m[1] ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
2026
|
+
for (const part of inner) {
|
|
2027
|
+
const k = part.split(":")[0]?.trim() ?? "";
|
|
2028
|
+
if (k) keys.add(k);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
return Array.from(keys);
|
|
2032
|
+
})();
|
|
2033
|
+
const hasAnyEnvAccess = /\bprocess\.env\b/.test(content);
|
|
2034
|
+
const hasSensitiveEnvAccess = (() => {
|
|
2035
|
+
if (!hasAnyEnvAccess) return false;
|
|
2036
|
+
if (envKeys.length === 0) return true;
|
|
2037
|
+
const safe = /* @__PURE__ */ new Set([
|
|
2038
|
+
"HOME",
|
|
2039
|
+
"USERPROFILE",
|
|
2040
|
+
"PATH",
|
|
2041
|
+
"PWD",
|
|
2042
|
+
"SHELL",
|
|
2043
|
+
"TERM",
|
|
2044
|
+
"TMPDIR",
|
|
2045
|
+
"TEMP",
|
|
2046
|
+
"TMP",
|
|
2047
|
+
"PORT",
|
|
2048
|
+
"HOST",
|
|
2049
|
+
"HOSTNAME",
|
|
2050
|
+
"NODE_ENV",
|
|
2051
|
+
"CI",
|
|
2052
|
+
"HEADLESS",
|
|
2053
|
+
"DEBUG"
|
|
2054
|
+
]);
|
|
2055
|
+
const sensitiveRe = /(token|secret|api[_-]?key|password|passwd|auth|session|private|cookie|jwt|bearer|ssh|key)/i;
|
|
2056
|
+
for (const raw of envKeys) {
|
|
2057
|
+
const k = raw.trim();
|
|
2058
|
+
if (!k) continue;
|
|
2059
|
+
if (safe.has(k.toUpperCase())) continue;
|
|
2060
|
+
if (sensitiveRe.test(k)) return true;
|
|
2061
|
+
}
|
|
2062
|
+
return false;
|
|
2063
|
+
})();
|
|
2064
|
+
if (CHILD_PROCESS_RE.test(content)) {
|
|
2065
|
+
EXEC_CALL_RE.lastIndex = 0;
|
|
2066
|
+
let m = EXEC_CALL_RE.exec(content);
|
|
2067
|
+
while (m) {
|
|
2068
|
+
const idx = m.index ?? 0;
|
|
2069
|
+
const fn = (m[1] ?? "").toLowerCase();
|
|
2070
|
+
if (fn === "exec" && prevNonWhitespaceChar(content, idx - 1) === ".") {
|
|
2071
|
+
m = EXEC_CALL_RE.exec(content);
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
out.push(
|
|
2075
|
+
mk(
|
|
2076
|
+
file.path,
|
|
2077
|
+
content,
|
|
2078
|
+
"dangerous_exec_child_process",
|
|
2079
|
+
{
|
|
2080
|
+
index: idx,
|
|
2081
|
+
dimension: "remote_exec",
|
|
2082
|
+
type: "suspicious",
|
|
2083
|
+
severity: "high",
|
|
2084
|
+
points: 55,
|
|
2085
|
+
reason: "Uses Node child_process execution APIs (exec/spawn), which can run arbitrary commands."
|
|
2086
|
+
},
|
|
2087
|
+
"codesafety|dim:remote_exec"
|
|
2088
|
+
)
|
|
2089
|
+
);
|
|
2090
|
+
break;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
EVAL_RE.lastIndex = 0;
|
|
2094
|
+
const evalM = EVAL_RE.exec(content);
|
|
2095
|
+
if (evalM) {
|
|
2096
|
+
out.push(
|
|
2097
|
+
mk(
|
|
2098
|
+
file.path,
|
|
2099
|
+
content,
|
|
2100
|
+
"dynamic_code_execution_eval",
|
|
2101
|
+
{
|
|
2102
|
+
index: evalM.index ?? 0,
|
|
2103
|
+
dimension: "remote_exec",
|
|
2104
|
+
type: "suspicious",
|
|
2105
|
+
severity: "high",
|
|
2106
|
+
points: 45,
|
|
2107
|
+
reason: "Uses eval(), which enables dynamic code execution and is high risk in skills."
|
|
2108
|
+
},
|
|
2109
|
+
"codesafety|dim:remote_exec"
|
|
2110
|
+
)
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
NEW_FUNCTION_RE.lastIndex = 0;
|
|
2114
|
+
const fnM = NEW_FUNCTION_RE.exec(content);
|
|
2115
|
+
if (fnM) {
|
|
2116
|
+
out.push(
|
|
2117
|
+
mk(
|
|
2118
|
+
file.path,
|
|
2119
|
+
content,
|
|
2120
|
+
"dynamic_code_execution_function",
|
|
2121
|
+
{
|
|
2122
|
+
index: fnM.index ?? 0,
|
|
2123
|
+
dimension: "remote_exec",
|
|
2124
|
+
type: "suspicious",
|
|
2125
|
+
severity: "high",
|
|
2126
|
+
points: 45,
|
|
2127
|
+
reason: "Uses new Function(), which enables dynamic code execution and is high risk in skills."
|
|
2128
|
+
},
|
|
2129
|
+
"codesafety|dim:remote_exec"
|
|
2130
|
+
)
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
if (NETWORK_RE.test(content) && hasRemoteUrlLiteral && hasSensitiveEnvAccess) {
|
|
2134
|
+
PROCESS_ENV_RE.lastIndex = 0;
|
|
2135
|
+
const envM = PROCESS_ENV_RE.exec(content);
|
|
2136
|
+
if (envM) {
|
|
2137
|
+
out.push(
|
|
2138
|
+
mk(
|
|
2139
|
+
file.path,
|
|
2140
|
+
content,
|
|
2141
|
+
"env_harvesting_and_network",
|
|
2142
|
+
{
|
|
2143
|
+
index: envM.index ?? 0,
|
|
2144
|
+
dimension: "secret_access",
|
|
2145
|
+
type: "secret-access",
|
|
2146
|
+
severity: "critical",
|
|
2147
|
+
points: 85,
|
|
2148
|
+
reason: "Reads process.env and also performs network activity, which can indicate token harvesting/exfiltration."
|
|
2149
|
+
},
|
|
2150
|
+
"codesafety|dim:secret_access"
|
|
2151
|
+
)
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
if (NETWORK_RE.test(content) && hasRemoteUrlLiteral) {
|
|
2156
|
+
READFILE_RE.lastIndex = 0;
|
|
2157
|
+
const rfM = READFILE_RE.exec(content);
|
|
2158
|
+
if (rfM) {
|
|
2159
|
+
out.push(
|
|
2160
|
+
mk(
|
|
2161
|
+
file.path,
|
|
2162
|
+
content,
|
|
2163
|
+
"file_read_and_network",
|
|
2164
|
+
{
|
|
2165
|
+
index: rfM.index ?? 0,
|
|
2166
|
+
dimension: "exfiltration",
|
|
2167
|
+
type: "exfiltration",
|
|
2168
|
+
severity: "high",
|
|
2169
|
+
points: 60,
|
|
2170
|
+
reason: "Reads local files and also performs network activity, which can indicate data exfiltration."
|
|
2171
|
+
},
|
|
2172
|
+
"codesafety|dim:exfiltration"
|
|
2173
|
+
)
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
const hexSeqRe = /(?:\\\\x[0-9a-fA-F]{2}){6,}/g;
|
|
2178
|
+
hexSeqRe.lastIndex = 0;
|
|
2179
|
+
const hexSeq = hexSeqRe.exec(content);
|
|
2180
|
+
if (hexSeq) {
|
|
2181
|
+
out.push(
|
|
2182
|
+
mk(
|
|
2183
|
+
file.path,
|
|
2184
|
+
content,
|
|
2185
|
+
"obfuscated_hex_escapes",
|
|
2186
|
+
{
|
|
2187
|
+
index: hexSeq.index ?? 0,
|
|
2188
|
+
dimension: "obfuscation",
|
|
2189
|
+
type: "obfuscation",
|
|
2190
|
+
severity: "medium",
|
|
2191
|
+
points: 25,
|
|
2192
|
+
reason: "Contains repeated \\\\xNN escape sequences, which can be used for obfuscation."
|
|
2193
|
+
},
|
|
2194
|
+
"codesafety|dim:obfuscation"
|
|
2195
|
+
)
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
const hasAtob = /\batob\s*\(/.test(content);
|
|
2199
|
+
const hasBufferFromBase64 = /\bBuffer\s*\.\s*from\s*\(/.test(content) && /['"]base64['"]/.test(content);
|
|
2200
|
+
if (hasAtob || hasBufferFromBase64) {
|
|
2201
|
+
BASE64_LITERAL_RE.lastIndex = 0;
|
|
2202
|
+
const b64m = BASE64_LITERAL_RE.exec(content);
|
|
2203
|
+
if (b64m) {
|
|
2204
|
+
out.push(
|
|
2205
|
+
mk(
|
|
2206
|
+
file.path,
|
|
2207
|
+
content,
|
|
2208
|
+
"obfuscated_large_base64_decode",
|
|
2209
|
+
{
|
|
2210
|
+
index: b64m.index ?? 0,
|
|
2211
|
+
dimension: "obfuscation",
|
|
2212
|
+
type: "obfuscation",
|
|
2213
|
+
severity: "high",
|
|
2214
|
+
points: 60,
|
|
2215
|
+
reason: "Decodes a large base64 payload, which is a common obfuscation technique."
|
|
2216
|
+
},
|
|
2217
|
+
"codesafety|dim:obfuscation"
|
|
2218
|
+
)
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
const cm = CRYPTO_MINING_RE.exec(content);
|
|
2223
|
+
if (cm) {
|
|
2224
|
+
out.push(
|
|
2225
|
+
mk(
|
|
2226
|
+
file.path,
|
|
2227
|
+
content,
|
|
2228
|
+
"crypto_mining_strings",
|
|
2229
|
+
{
|
|
2230
|
+
index: cm.index ?? 0,
|
|
2231
|
+
dimension: "remote_exec",
|
|
2232
|
+
type: "suspicious",
|
|
2233
|
+
severity: "critical",
|
|
2234
|
+
points: 90,
|
|
2235
|
+
reason: "Contains crypto-mining related strings (e.g. stratum/xmrig)."
|
|
2236
|
+
},
|
|
2237
|
+
"codesafety|dim:remote_exec"
|
|
2238
|
+
)
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
const ws = WS_PORT_RE.exec(content);
|
|
2242
|
+
if (ws) {
|
|
2243
|
+
const rawPort = Number(ws[1]);
|
|
2244
|
+
if (Number.isFinite(rawPort) && rawPort > 0 && rawPort <= 65535 && !WS_ALLOWED_PORTS.has(rawPort)) {
|
|
2245
|
+
out.push(
|
|
2246
|
+
mk(
|
|
2247
|
+
file.path,
|
|
2248
|
+
content,
|
|
2249
|
+
"suspicious_websocket_port",
|
|
2250
|
+
{
|
|
2251
|
+
index: ws.index ?? 0,
|
|
2252
|
+
dimension: "exfiltration",
|
|
2253
|
+
type: "suspicious",
|
|
2254
|
+
severity: "medium",
|
|
2255
|
+
points: 30,
|
|
2256
|
+
reason: `Connects to a WebSocket on uncommon port ${rawPort}, which can indicate covert C2/exfiltration.`
|
|
2257
|
+
},
|
|
2258
|
+
"codesafety|dim:exfiltration"
|
|
2259
|
+
)
|
|
2260
|
+
);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
if (testLike) {
|
|
2264
|
+
return out.map((c) => ({ ...c, scoreKey: null }));
|
|
2265
|
+
}
|
|
2266
|
+
return out;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
1921
2269
|
// src/scanner/static-scan/rules.ts
|
|
1922
2270
|
var RULES = [
|
|
1923
2271
|
{
|
|
@@ -2096,7 +2444,18 @@ var RULES = [
|
|
|
2096
2444
|
];
|
|
2097
2445
|
|
|
2098
2446
|
// src/scanner/static-scan/scan.ts
|
|
2099
|
-
function
|
|
2447
|
+
function isTestLikePath2(path) {
|
|
2448
|
+
const p = path.replace(/\\/g, "/").toLowerCase();
|
|
2449
|
+
if (p.includes("/__tests__/")) return true;
|
|
2450
|
+
if (p.includes("/tests/")) return true;
|
|
2451
|
+
if (p.includes("/test/")) return true;
|
|
2452
|
+
if (/\.(test|spec)\.[mc]?[jt]sx?$/.test(p)) return true;
|
|
2453
|
+
return false;
|
|
2454
|
+
}
|
|
2455
|
+
function scanSkillSafety(files, options = {}) {
|
|
2456
|
+
return scanSkillInternal(files, options, { includeCodeSafety: true });
|
|
2457
|
+
}
|
|
2458
|
+
function scanSkillInternal(files, options, { includeCodeSafety }) {
|
|
2100
2459
|
const maxFiles = options.maxFiles ?? 120;
|
|
2101
2460
|
const maxTotalBytes = options.maxTotalBytes ?? 12e5;
|
|
2102
2461
|
const maxFileBytes = options.maxFileBytes ?? 16e4;
|
|
@@ -2139,6 +2498,34 @@ function scanSkillStatic(files, options = {}) {
|
|
|
2139
2498
|
}
|
|
2140
2499
|
bytesScanned += size;
|
|
2141
2500
|
filesScanned += 1;
|
|
2501
|
+
const testLike = includeCodeSafety ? isTestLikePath2(file.path) : false;
|
|
2502
|
+
let codeHasDynamicExec = false;
|
|
2503
|
+
if (includeCodeSafety) {
|
|
2504
|
+
const codeCandidates = scanCodeSafetyFile({ path: file.path, content, size });
|
|
2505
|
+
codeCandidates.sort(
|
|
2506
|
+
(a, b) => b.signal.points - a.signal.points || a.signal.id.localeCompare(b.signal.id)
|
|
2507
|
+
);
|
|
2508
|
+
codeHasDynamicExec = codeCandidates.some(
|
|
2509
|
+
(c) => c.signal.id === "dynamic_code_execution_eval" || c.signal.id === "dynamic_code_execution_function"
|
|
2510
|
+
);
|
|
2511
|
+
for (const candidate of codeCandidates) {
|
|
2512
|
+
if (signals.length >= maxSignals) {
|
|
2513
|
+
truncated = true;
|
|
2514
|
+
break;
|
|
2515
|
+
}
|
|
2516
|
+
signals.push(candidate.signal);
|
|
2517
|
+
if (candidate.scoreKey && !scoreApplied.has(candidate.scoreKey)) {
|
|
2518
|
+
if (!testLike) {
|
|
2519
|
+
scoreApplied.add(candidate.scoreKey);
|
|
2520
|
+
dimensionScores[candidate.signal.dimension] = clamp(
|
|
2521
|
+
dimensionScores[candidate.signal.dimension] + candidate.signal.points,
|
|
2522
|
+
0,
|
|
2523
|
+
100
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2142
2529
|
for (const rule of RULES) {
|
|
2143
2530
|
if (signals.length >= maxSignals) {
|
|
2144
2531
|
truncated = true;
|
|
@@ -2219,7 +2606,8 @@ function scanSkillStatic(files, options = {}) {
|
|
|
2219
2606
|
snippet,
|
|
2220
2607
|
reason
|
|
2221
2608
|
});
|
|
2222
|
-
|
|
2609
|
+
const shouldScore = !(includeCodeSafety && (testLike || rule.id === "eval_exec" && codeHasDynamicExec));
|
|
2610
|
+
if (shouldScore && !scoreApplied.has(scoreKey)) {
|
|
2223
2611
|
scoreApplied.add(scoreKey);
|
|
2224
2612
|
dimensionScores[rule.dimension] = clamp(dimensionScores[rule.dimension] + points, 0, 100);
|
|
2225
2613
|
}
|
|
@@ -2259,7 +2647,7 @@ function scanSkillStatic(files, options = {}) {
|
|
|
2259
2647
|
// src/scanner/scan-skill-dir.ts
|
|
2260
2648
|
async function scanSkillDir(dir, options = {}) {
|
|
2261
2649
|
const collected = await collectSkillScanFiles(dir, options);
|
|
2262
|
-
const staticScan =
|
|
2650
|
+
const staticScan = scanSkillSafety(collected.files, options);
|
|
2263
2651
|
return {
|
|
2264
2652
|
dir,
|
|
2265
2653
|
staticScan,
|
|
@@ -4183,12 +4571,16 @@ async function recordParsedTracking(skills, results, installGlobally, parsed, te
|
|
|
4183
4571
|
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue);
|
|
4184
4572
|
if (hash) skillFolderHash = hash;
|
|
4185
4573
|
}
|
|
4574
|
+
if (parsed.type === "well-known" && parsed.contentHash) {
|
|
4575
|
+
skillFolderHash = parsed.contentHash;
|
|
4576
|
+
}
|
|
4186
4577
|
await addSkillToLock(
|
|
4187
4578
|
displayName,
|
|
4188
4579
|
{
|
|
4189
4580
|
source: normalizedSource ?? parsed.url,
|
|
4190
4581
|
sourceType: parsed.type,
|
|
4191
4582
|
sourceUrl: parsed.url,
|
|
4583
|
+
...parsed.type === "well-known" && parsed.licenseKey ? { licenseKey: parsed.licenseKey } : {},
|
|
4192
4584
|
skillPath: skillPathValue,
|
|
4193
4585
|
skillFolderHash,
|
|
4194
4586
|
ref: parsed.ref
|
|
@@ -4258,7 +4650,10 @@ function formatResultSummary(results) {
|
|
|
4258
4650
|
if (!firstResult) continue;
|
|
4259
4651
|
if (firstResult.mode === "copy") {
|
|
4260
4652
|
lines.push(`${chalk2.green("\u2713")} ${skillName} ${chalk2.dim("(copied)")}`);
|
|
4653
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
4261
4654
|
for (const r of skillResults) {
|
|
4655
|
+
if (seenPaths.has(r.path)) continue;
|
|
4656
|
+
seenPaths.add(r.path);
|
|
4262
4657
|
const shortPath = shortenPath(r.path, cwd);
|
|
4263
4658
|
lines.push(` ${chalk2.dim("\u2192")} ${shortPath}`);
|
|
4264
4659
|
}
|
|
@@ -4366,15 +4761,51 @@ function AddInstallScreen() {
|
|
|
4366
4761
|
] });
|
|
4367
4762
|
}
|
|
4368
4763
|
|
|
4369
|
-
// src/tui/screens/
|
|
4370
|
-
import {
|
|
4371
|
-
import
|
|
4764
|
+
// src/tui/screens/AddLicenseKey.tsx
|
|
4765
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
4766
|
+
import TextInput2 from "ink-text-input";
|
|
4372
4767
|
import React11 from "react";
|
|
4373
4768
|
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4769
|
+
function AddLicenseKeyScreen() {
|
|
4770
|
+
const { invocation, addSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
|
|
4771
|
+
const [value, setValue] = React11.useState(
|
|
4772
|
+
addSkill.licenseKey ?? invocation.options.licenseKey ?? ""
|
|
4773
|
+
);
|
|
4774
|
+
const { wrapOnChange } = useTextInput({
|
|
4775
|
+
onClear: () => {
|
|
4776
|
+
setValue("");
|
|
4777
|
+
}
|
|
4778
|
+
});
|
|
4779
|
+
const onSubmit = (input) => {
|
|
4780
|
+
const trimmed = input.trim();
|
|
4781
|
+
if (!trimmed) {
|
|
4782
|
+
setFlash("Enter a license key.");
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4785
|
+
updateAddSkill({ licenseKey: trimmed });
|
|
4786
|
+
navigateTo("add-skill-select");
|
|
4787
|
+
};
|
|
4788
|
+
return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", padding: 1, children: [
|
|
4789
|
+
/* @__PURE__ */ jsx16(Header, { title: "License key" }),
|
|
4790
|
+
/* @__PURE__ */ jsx16(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text14, { children: "Enter the license key for this paid/private skill." }) }),
|
|
4791
|
+
/* @__PURE__ */ jsx16(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Tip: you can also pass it via --license-key." }) }),
|
|
4792
|
+
/* @__PURE__ */ jsxs12(Box14, { children: [
|
|
4793
|
+
/* @__PURE__ */ jsx16(Text14, { color: "green", children: "> " }),
|
|
4794
|
+
/* @__PURE__ */ jsx16(TextInput2, { value, onChange: wrapOnChange(setValue), onSubmit })
|
|
4795
|
+
] }),
|
|
4796
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: TEXT_INPUT_HINT }) })
|
|
4797
|
+
] });
|
|
4798
|
+
}
|
|
4799
|
+
|
|
4800
|
+
// src/tui/screens/AddMode.tsx
|
|
4801
|
+
import { join as join12 } from "path";
|
|
4802
|
+
import { Box as Box15 } from "ink";
|
|
4803
|
+
import React12 from "react";
|
|
4804
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4374
4805
|
function AddModeScreen() {
|
|
4375
4806
|
const { invocation, addSkill, updateAddSkill, navigateTo, navAction } = useNavigation();
|
|
4376
4807
|
const options = invocation.options;
|
|
4377
|
-
|
|
4808
|
+
React12.useEffect(() => {
|
|
4378
4809
|
if (navAction === "pop") return;
|
|
4379
4810
|
if (addSkill.installMode) {
|
|
4380
4811
|
navigateTo("add-confirm");
|
|
@@ -4404,9 +4835,9 @@ function AddModeScreen() {
|
|
|
4404
4835
|
{ label: "Symlink (recommended)", value: "symlink", hint: symlinkHint },
|
|
4405
4836
|
{ label: "Copy to each agent", value: "copy", hint: copyHint }
|
|
4406
4837
|
];
|
|
4407
|
-
return /* @__PURE__ */
|
|
4408
|
-
/* @__PURE__ */
|
|
4409
|
-
/* @__PURE__ */
|
|
4838
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", padding: 1, children: [
|
|
4839
|
+
/* @__PURE__ */ jsx17(AddFlowHeader, { title: "Install mode" }),
|
|
4840
|
+
/* @__PURE__ */ jsx17(
|
|
4410
4841
|
SingleSelect,
|
|
4411
4842
|
{
|
|
4412
4843
|
items,
|
|
@@ -4422,9 +4853,9 @@ function AddModeScreen() {
|
|
|
4422
4853
|
|
|
4423
4854
|
// src/tui/screens/AddResult.tsx
|
|
4424
4855
|
import chalk3 from "chalk";
|
|
4425
|
-
import { Box as
|
|
4426
|
-
import
|
|
4427
|
-
import { jsx as
|
|
4856
|
+
import { Box as Box16, Text as Text15 } from "ink";
|
|
4857
|
+
import React13 from "react";
|
|
4858
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4428
4859
|
function AddResultScreen() {
|
|
4429
4860
|
const { addSkill, resetAddSkill, navigateTo } = useNavigation();
|
|
4430
4861
|
const results = addSkill.installResults ?? [];
|
|
@@ -4432,7 +4863,7 @@ function AddResultScreen() {
|
|
|
4432
4863
|
const failed = results.filter((r) => !r.success);
|
|
4433
4864
|
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
4434
4865
|
const summary = successful.length > 0 ? formatResultSummary(successful) : null;
|
|
4435
|
-
const summaryLines =
|
|
4866
|
+
const summaryLines = React13.useMemo(() => {
|
|
4436
4867
|
if (!summary) return [];
|
|
4437
4868
|
const counts = /* @__PURE__ */ new Map();
|
|
4438
4869
|
return summary.lines.map((line) => {
|
|
@@ -4441,15 +4872,15 @@ function AddResultScreen() {
|
|
|
4441
4872
|
return { line, key: `${line}-${count}` };
|
|
4442
4873
|
});
|
|
4443
4874
|
}, [summary]);
|
|
4444
|
-
return /* @__PURE__ */
|
|
4445
|
-
/* @__PURE__ */
|
|
4446
|
-
summary ? /* @__PURE__ */
|
|
4447
|
-
/* @__PURE__ */
|
|
4448
|
-
summaryLines.map(({ line, key }) => /* @__PURE__ */
|
|
4875
|
+
return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", padding: 1, children: [
|
|
4876
|
+
/* @__PURE__ */ jsx18(AddFlowHeader, { title: "Install results" }),
|
|
4877
|
+
summary ? /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
4878
|
+
/* @__PURE__ */ jsx18(Text15, { children: summary.title }),
|
|
4879
|
+
summaryLines.map(({ line, key }) => /* @__PURE__ */ jsx18(Text15, { children: line }, key))
|
|
4449
4880
|
] }) : null,
|
|
4450
|
-
failed.length > 0 ? /* @__PURE__ */
|
|
4451
|
-
/* @__PURE__ */
|
|
4452
|
-
failed.map((r) => /* @__PURE__ */
|
|
4881
|
+
failed.length > 0 ? /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
4882
|
+
/* @__PURE__ */ jsx18(Text15, { color: "red", children: `Failed to install ${failed.length}` }),
|
|
4883
|
+
failed.map((r) => /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
4453
4884
|
chalk3.red("\u2717"),
|
|
4454
4885
|
" ",
|
|
4455
4886
|
r.skill,
|
|
@@ -4459,15 +4890,15 @@ function AddResultScreen() {
|
|
|
4459
4890
|
chalk3.dim(r.error)
|
|
4460
4891
|
] }, `${r.skill}-${r.agentId}`))
|
|
4461
4892
|
] }) : null,
|
|
4462
|
-
successful.length > 0 ? /* @__PURE__ */
|
|
4893
|
+
successful.length > 0 ? /* @__PURE__ */ jsx18(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
|
|
4463
4894
|
"Installed to: ",
|
|
4464
4895
|
formatList(successful.map((r) => r.agent))
|
|
4465
4896
|
] }) }) : null,
|
|
4466
|
-
symlinkFailures.length > 0 ? /* @__PURE__ */
|
|
4467
|
-
/* @__PURE__ */
|
|
4468
|
-
/* @__PURE__ */
|
|
4897
|
+
symlinkFailures.length > 0 ? /* @__PURE__ */ jsxs14(Box16, { marginBottom: 1, children: [
|
|
4898
|
+
/* @__PURE__ */ jsx18(Text15, { color: "yellow", children: `Symlinks failed for: ${formatList(symlinkFailures.map((r) => r.agent))}` }),
|
|
4899
|
+
/* @__PURE__ */ jsx18(Text15, { dimColor: true, children: "Files were copied instead." })
|
|
4469
4900
|
] }) : null,
|
|
4470
|
-
/* @__PURE__ */
|
|
4901
|
+
/* @__PURE__ */ jsx18(
|
|
4471
4902
|
SelectMenu,
|
|
4472
4903
|
{
|
|
4473
4904
|
items: [
|
|
@@ -4490,13 +4921,13 @@ function AddResultScreen() {
|
|
|
4490
4921
|
}
|
|
4491
4922
|
|
|
4492
4923
|
// src/tui/screens/AddScope.tsx
|
|
4493
|
-
import { Box as
|
|
4494
|
-
import
|
|
4495
|
-
import { jsx as
|
|
4924
|
+
import { Box as Box17 } from "ink";
|
|
4925
|
+
import React14 from "react";
|
|
4926
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4496
4927
|
function AddScopeScreen() {
|
|
4497
4928
|
const { invocation, addSkill, updateAddSkill, navigateTo, navAction } = useNavigation();
|
|
4498
4929
|
const options = invocation.options;
|
|
4499
|
-
|
|
4930
|
+
React14.useEffect(() => {
|
|
4500
4931
|
if (navAction === "pop") return;
|
|
4501
4932
|
if (addSkill.installGlobally !== void 0) {
|
|
4502
4933
|
navigateTo("add-mode");
|
|
@@ -4519,9 +4950,9 @@ function AddScopeScreen() {
|
|
|
4519
4950
|
const globalBase = getCanonicalSkillsBase({ global: true, cwd });
|
|
4520
4951
|
const projectHint = `Project base (symlink): ${shortenPath(projectBase, cwd)}`;
|
|
4521
4952
|
const globalHint = `Global base (symlink): ${shortenPath(globalBase, cwd)}`;
|
|
4522
|
-
return /* @__PURE__ */
|
|
4523
|
-
/* @__PURE__ */
|
|
4524
|
-
/* @__PURE__ */
|
|
4953
|
+
return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
|
|
4954
|
+
/* @__PURE__ */ jsx19(AddFlowHeader, { title: "Install scope" }),
|
|
4955
|
+
/* @__PURE__ */ jsx19(
|
|
4525
4956
|
SingleSelect,
|
|
4526
4957
|
{
|
|
4527
4958
|
items: [
|
|
@@ -4543,23 +4974,23 @@ function AddScopeScreen() {
|
|
|
4543
4974
|
}
|
|
4544
4975
|
|
|
4545
4976
|
// src/tui/screens/AddSecurityScan.tsx
|
|
4546
|
-
import { Box as
|
|
4547
|
-
import
|
|
4548
|
-
import { jsx as
|
|
4977
|
+
import { Box as Box18, Text as Text16 } from "ink";
|
|
4978
|
+
import React15 from "react";
|
|
4979
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4549
4980
|
function AddSecurityScanScreen() {
|
|
4550
4981
|
const { invocation, addSkill, updateAddSkill, navigateTo, resetTo, setFlash, setBackHandler } = useNavigation();
|
|
4551
4982
|
const options = invocation.options;
|
|
4552
|
-
const [status, setStatus] =
|
|
4553
|
-
const [error, setError] =
|
|
4554
|
-
const [manualView, setManualView] =
|
|
4555
|
-
const [confirmText, setConfirmText] =
|
|
4556
|
-
const [selectedRow, setSelectedRow] =
|
|
4983
|
+
const [status, setStatus] = React15.useState("scanning");
|
|
4984
|
+
const [error, setError] = React15.useState(null);
|
|
4985
|
+
const [manualView, setManualView] = React15.useState("menu");
|
|
4986
|
+
const [confirmText, setConfirmText] = React15.useState("");
|
|
4987
|
+
const [selectedRow, setSelectedRow] = React15.useState(null);
|
|
4557
4988
|
const spinner = useSpinnerFrame(status === "scanning");
|
|
4558
4989
|
const { wrapOnChange } = useTextInput({
|
|
4559
4990
|
onClear: () => setConfirmText(""),
|
|
4560
4991
|
disabled: status !== "risky" || manualView !== "type-confirm"
|
|
4561
4992
|
});
|
|
4562
|
-
|
|
4993
|
+
React15.useEffect(() => {
|
|
4563
4994
|
const selectedSkills = addSkill.selectedSkills;
|
|
4564
4995
|
if (!selectedSkills || selectedSkills.length === 0) {
|
|
4565
4996
|
navigateTo("add-skill-select");
|
|
@@ -4599,7 +5030,7 @@ function AddSecurityScanScreen() {
|
|
|
4599
5030
|
cancelled = true;
|
|
4600
5031
|
};
|
|
4601
5032
|
}, [addSkill.selectedSkills, navigateTo, updateAddSkill]);
|
|
4602
|
-
|
|
5033
|
+
React15.useEffect(() => {
|
|
4603
5034
|
if (status !== "risky") {
|
|
4604
5035
|
setBackHandler(null);
|
|
4605
5036
|
return () => setBackHandler(null);
|
|
@@ -4623,17 +5054,17 @@ function AddSecurityScanScreen() {
|
|
|
4623
5054
|
return () => setBackHandler(null);
|
|
4624
5055
|
}, [status, manualView, setBackHandler]);
|
|
4625
5056
|
if (status === "error") {
|
|
4626
|
-
return /* @__PURE__ */
|
|
4627
|
-
/* @__PURE__ */
|
|
4628
|
-
/* @__PURE__ */
|
|
4629
|
-
/* @__PURE__ */
|
|
5057
|
+
return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
|
|
5058
|
+
/* @__PURE__ */ jsx20(AddFlowHeader, { title: "Security scan" }),
|
|
5059
|
+
/* @__PURE__ */ jsx20(Text16, { color: "red", children: error ?? "Scan failed" }),
|
|
5060
|
+
/* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
4630
5061
|
] });
|
|
4631
5062
|
}
|
|
4632
5063
|
if (status === "scanning") {
|
|
4633
5064
|
const count = addSkill.selectedSkills?.length ?? 0;
|
|
4634
|
-
return /* @__PURE__ */
|
|
4635
|
-
/* @__PURE__ */
|
|
4636
|
-
/* @__PURE__ */
|
|
5065
|
+
return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
|
|
5066
|
+
/* @__PURE__ */ jsx20(AddFlowHeader, { title: "Security scan" }),
|
|
5067
|
+
/* @__PURE__ */ jsxs16(Text16, { children: [
|
|
4637
5068
|
spinner,
|
|
4638
5069
|
" Scanning ",
|
|
4639
5070
|
count,
|
|
@@ -4644,9 +5075,9 @@ function AddSecurityScanScreen() {
|
|
|
4644
5075
|
] });
|
|
4645
5076
|
}
|
|
4646
5077
|
if (status === "risky") {
|
|
4647
|
-
return /* @__PURE__ */
|
|
4648
|
-
/* @__PURE__ */
|
|
4649
|
-
/* @__PURE__ */
|
|
5078
|
+
return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
|
|
5079
|
+
/* @__PURE__ */ jsx20(AddFlowHeader, { title: "Security scan" }),
|
|
5080
|
+
/* @__PURE__ */ jsx20(
|
|
4650
5081
|
ManualSecurityGate,
|
|
4651
5082
|
{
|
|
4652
5083
|
scanRows: addSkill.securityScanRows ?? [],
|
|
@@ -4670,12 +5101,12 @@ function AddSecurityScanScreen() {
|
|
|
4670
5101
|
] });
|
|
4671
5102
|
}
|
|
4672
5103
|
if (options.yes) {
|
|
4673
|
-
return /* @__PURE__ */
|
|
5104
|
+
return /* @__PURE__ */ jsx20(Box18, { padding: 1 });
|
|
4674
5105
|
}
|
|
4675
|
-
return /* @__PURE__ */
|
|
4676
|
-
/* @__PURE__ */
|
|
4677
|
-
/* @__PURE__ */
|
|
4678
|
-
/* @__PURE__ */
|
|
5106
|
+
return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
|
|
5107
|
+
/* @__PURE__ */ jsx20(AddFlowHeader, { title: "Security scan" }),
|
|
5108
|
+
/* @__PURE__ */ jsx20(Text16, { dimColor: true, children: "Scan complete." }),
|
|
5109
|
+
/* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
4679
5110
|
] });
|
|
4680
5111
|
}
|
|
4681
5112
|
|
|
@@ -4684,8 +5115,8 @@ import { existsSync as existsSync6 } from "fs";
|
|
|
4684
5115
|
import { mkdir as mkdir6, mkdtemp as mkdtemp4, writeFile as writeFile4 } from "fs/promises";
|
|
4685
5116
|
import { tmpdir as tmpdir5 } from "os";
|
|
4686
5117
|
import { join as join15 } from "path";
|
|
4687
|
-
import { Box as
|
|
4688
|
-
import
|
|
5118
|
+
import { Box as Box19, Text as Text17 } from "ink";
|
|
5119
|
+
import React16 from "react";
|
|
4689
5120
|
|
|
4690
5121
|
// src/mintlify.ts
|
|
4691
5122
|
import matter3 from "gray-matter";
|
|
@@ -4958,7 +5389,18 @@ var WellKnownProvider = class {
|
|
|
4958
5389
|
try {
|
|
4959
5390
|
const parsed = new URL(baseUrl);
|
|
4960
5391
|
const basePath = parsed.pathname.replace(/\/$/, "");
|
|
5392
|
+
const indexSuffix = `/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`;
|
|
4961
5393
|
const urlsToTry = [
|
|
5394
|
+
// If the caller already passed the index.json URL, try it directly.
|
|
5395
|
+
...basePath.endsWith(indexSuffix) ? [
|
|
5396
|
+
{
|
|
5397
|
+
indexUrl: `${parsed.protocol}//${parsed.host}${basePath}`,
|
|
5398
|
+
baseUrl: `${parsed.protocol}//${parsed.host}${basePath.slice(
|
|
5399
|
+
0,
|
|
5400
|
+
Math.max(0, basePath.length - indexSuffix.length)
|
|
5401
|
+
)}`
|
|
5402
|
+
}
|
|
5403
|
+
] : [],
|
|
4962
5404
|
{
|
|
4963
5405
|
indexUrl: `${parsed.protocol}//${parsed.host}${basePath}/${this.WELL_KNOWN_PATH}/${this.INDEX_FILE}`,
|
|
4964
5406
|
baseUrl: `${parsed.protocol}//${parsed.host}${basePath}`
|
|
@@ -5003,19 +5445,46 @@ var WellKnownProvider = class {
|
|
|
5003
5445
|
const e = entry;
|
|
5004
5446
|
if (typeof e.name !== "string" || !e.name) return false;
|
|
5005
5447
|
if (typeof e.description !== "string" || !e.description) return false;
|
|
5006
|
-
if (!Array.isArray(e.files) || e.files.length === 0) return false;
|
|
5007
5448
|
const nameRegex = /^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
5008
5449
|
if (!nameRegex.test(e.name) && e.name.length > 1) {
|
|
5009
5450
|
if (e.name.length === 1 && !/^[a-z0-9]$/.test(e.name)) {
|
|
5010
5451
|
return false;
|
|
5011
5452
|
}
|
|
5012
5453
|
}
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5454
|
+
if (Array.isArray(e.files) && e.files.length > 0) {
|
|
5455
|
+
for (const file of e.files) {
|
|
5456
|
+
if (typeof file !== "string") return false;
|
|
5457
|
+
if (file.startsWith("/") || file.startsWith("\\") || file.includes("..")) return false;
|
|
5458
|
+
}
|
|
5459
|
+
const hasSkillMd2 = e.files.some(
|
|
5460
|
+
(f) => typeof f === "string" && f.toLowerCase() === "skill.md"
|
|
5461
|
+
);
|
|
5462
|
+
if (!hasSkillMd2) return false;
|
|
5463
|
+
return true;
|
|
5464
|
+
}
|
|
5465
|
+
if (!e.content || typeof e.content !== "object") return false;
|
|
5466
|
+
const c = e.content;
|
|
5467
|
+
if (typeof c.endpoint !== "string" || !c.endpoint) return false;
|
|
5468
|
+
if (typeof c.method !== "string" || !c.method) return false;
|
|
5469
|
+
if (e.auth != null) {
|
|
5470
|
+
if (typeof e.auth !== "object") return false;
|
|
5471
|
+
const a = e.auth;
|
|
5472
|
+
if (typeof a.tokenHeader !== "string" || !a.tokenHeader) return false;
|
|
5473
|
+
if (a.tokenPrefix != null && typeof a.tokenPrefix !== "string") return false;
|
|
5474
|
+
} else {
|
|
5475
|
+
if (typeof c.tokenHeader !== "string" || !c.tokenHeader) return false;
|
|
5476
|
+
if (c.tokenPrefix != null && typeof c.tokenPrefix !== "string") return false;
|
|
5477
|
+
}
|
|
5478
|
+
if (e.verify != null) {
|
|
5479
|
+
if (typeof e.verify !== "object") return false;
|
|
5480
|
+
const v = e.verify;
|
|
5481
|
+
if (typeof v.endpoint !== "string" || !v.endpoint) return false;
|
|
5482
|
+
if (typeof v.method !== "string" || !v.method) return false;
|
|
5483
|
+
if (e.auth == null) {
|
|
5484
|
+
if (typeof v.tokenHeader !== "string" || !v.tokenHeader) return false;
|
|
5485
|
+
if (v.tokenPrefix != null && typeof v.tokenPrefix !== "string") return false;
|
|
5486
|
+
}
|
|
5016
5487
|
}
|
|
5017
|
-
const hasSkillMd2 = e.files.some((f) => typeof f === "string" && f.toLowerCase() === "skill.md");
|
|
5018
|
-
if (!hasSkillMd2) return false;
|
|
5019
5488
|
return true;
|
|
5020
5489
|
}
|
|
5021
5490
|
async fetchSkill(url) {
|
|
@@ -5062,6 +5531,9 @@ var WellKnownProvider = class {
|
|
|
5062
5531
|
}
|
|
5063
5532
|
async fetchSkillByEntry(baseUrl, entry) {
|
|
5064
5533
|
try {
|
|
5534
|
+
if (!("files" in entry)) {
|
|
5535
|
+
return null;
|
|
5536
|
+
}
|
|
5065
5537
|
const skillBaseUrl = `${baseUrl.replace(/\/$/, "")}/${this.WELL_KNOWN_PATH}/${entry.name}`;
|
|
5066
5538
|
const skillMdUrl = `${skillBaseUrl}/SKILL.md`;
|
|
5067
5539
|
const response = await fetch(skillMdUrl);
|
|
@@ -5222,9 +5694,82 @@ async function resolveRemoteSkill(url) {
|
|
|
5222
5694
|
import { mkdir as mkdir5, mkdtemp as mkdtemp3, writeFile as writeFile3 } from "fs/promises";
|
|
5223
5695
|
import { tmpdir as tmpdir4 } from "os";
|
|
5224
5696
|
import { dirname as dirname4, join as join13 } from "path";
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5697
|
+
var WellKnownLicenseKeyRequiredError = class extends Error {
|
|
5698
|
+
code = "LICENSE_KEY_REQUIRED";
|
|
5699
|
+
constructor() {
|
|
5700
|
+
super("A license key is required to install this skill.");
|
|
5701
|
+
}
|
|
5702
|
+
};
|
|
5703
|
+
function isStaticEntry(entry) {
|
|
5704
|
+
return "files" in entry;
|
|
5705
|
+
}
|
|
5706
|
+
function isAuthedEntry(entry) {
|
|
5707
|
+
return "content" in entry && !("files" in entry);
|
|
5708
|
+
}
|
|
5709
|
+
function resolveAuthForEndpoint(entry, endpoint) {
|
|
5710
|
+
if (entry.auth?.tokenHeader) {
|
|
5711
|
+
return { tokenHeader: entry.auth.tokenHeader, tokenPrefix: entry.auth.tokenPrefix ?? "" };
|
|
5712
|
+
}
|
|
5713
|
+
const spec = endpoint === "content" ? entry.content : entry.verify;
|
|
5714
|
+
const tokenHeader = spec?.tokenHeader;
|
|
5715
|
+
const tokenPrefix = spec?.tokenPrefix;
|
|
5716
|
+
if (typeof tokenHeader !== "string" || !tokenHeader) {
|
|
5717
|
+
throw new Error(
|
|
5718
|
+
"Invalid well-known index entry: missing auth.tokenHeader (or legacy tokenHeader)."
|
|
5719
|
+
);
|
|
5720
|
+
}
|
|
5721
|
+
return { tokenHeader, tokenPrefix: typeof tokenPrefix === "string" ? tokenPrefix : "" };
|
|
5722
|
+
}
|
|
5723
|
+
async function fetchAuthedManifest(entry, licenseKey) {
|
|
5724
|
+
const contentAuth = resolveAuthForEndpoint(entry, "content");
|
|
5725
|
+
const tokenValue = `${contentAuth.tokenPrefix}${licenseKey}`;
|
|
5726
|
+
if (entry.verify) {
|
|
5727
|
+
const verifyAuth = resolveAuthForEndpoint(entry, "verify");
|
|
5728
|
+
const verifyRes = await fetch(entry.verify.endpoint, {
|
|
5729
|
+
method: entry.verify.method,
|
|
5730
|
+
headers: { [verifyAuth.tokenHeader]: tokenValue }
|
|
5731
|
+
});
|
|
5732
|
+
if (!verifyRes.ok) {
|
|
5733
|
+
const text = await verifyRes.text().catch(() => "");
|
|
5734
|
+
let msg = text;
|
|
5735
|
+
try {
|
|
5736
|
+
const parsed = JSON.parse(text);
|
|
5737
|
+
if (typeof parsed?.error === "string" && parsed.error) {
|
|
5738
|
+
msg = parsed.error;
|
|
5739
|
+
}
|
|
5740
|
+
} catch {
|
|
5741
|
+
}
|
|
5742
|
+
throw new Error(msg || `License verification failed (${verifyRes.status}).`);
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5745
|
+
const res = await fetch(entry.content.endpoint, {
|
|
5746
|
+
method: entry.content.method,
|
|
5747
|
+
headers: { [contentAuth.tokenHeader]: tokenValue }
|
|
5748
|
+
});
|
|
5749
|
+
if (!res.ok) {
|
|
5750
|
+
const text = await res.text().catch(() => "");
|
|
5751
|
+
throw new Error(text || `Unable to fetch skill content (${res.status}).`);
|
|
5752
|
+
}
|
|
5753
|
+
const json = await res.json();
|
|
5754
|
+
if (json && typeof json === "object" && "success" in json) {
|
|
5755
|
+
const wrapped = json;
|
|
5756
|
+
if (!wrapped.success) {
|
|
5757
|
+
throw new Error(wrapped.error || "Unable to fetch skill content.");
|
|
5758
|
+
}
|
|
5759
|
+
return wrapped.data;
|
|
5760
|
+
}
|
|
5761
|
+
return json;
|
|
5762
|
+
}
|
|
5763
|
+
async function prepareWellKnownSkills(sourceUrl, options) {
|
|
5764
|
+
const result = await wellKnownProvider.fetchIndex(sourceUrl);
|
|
5765
|
+
if (!result) {
|
|
5766
|
+
throw new Error(
|
|
5767
|
+
"No skills found at this URL. Make sure it exposes /.well-known/skills/index.json."
|
|
5768
|
+
);
|
|
5769
|
+
}
|
|
5770
|
+
const { index, resolvedBaseUrl } = result;
|
|
5771
|
+
const entries = index.skills ?? [];
|
|
5772
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
5228
5773
|
throw new Error(
|
|
5229
5774
|
"No skills found at this URL. Make sure it exposes /.well-known/skills/index.json."
|
|
5230
5775
|
);
|
|
@@ -5235,33 +5780,86 @@ async function prepareWellKnownSkills(sourceUrl) {
|
|
|
5235
5780
|
const originMap = /* @__PURE__ */ new Map();
|
|
5236
5781
|
const skills = [];
|
|
5237
5782
|
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(sourceUrl);
|
|
5238
|
-
|
|
5239
|
-
|
|
5783
|
+
let contentHash;
|
|
5784
|
+
const authedEntries = entries.filter(isAuthedEntry);
|
|
5785
|
+
const staticEntries = entries.filter(isStaticEntry);
|
|
5786
|
+
if (authedEntries.length > 0) {
|
|
5787
|
+
const licenseKey = options?.licenseKey?.trim();
|
|
5788
|
+
if (!licenseKey) {
|
|
5789
|
+
throw new WellKnownLicenseKeyRequiredError();
|
|
5790
|
+
}
|
|
5791
|
+
const prefixPerEntry = authedEntries.length > 1;
|
|
5792
|
+
for (const entry of authedEntries) {
|
|
5793
|
+
const manifest = await fetchAuthedManifest(entry, licenseKey);
|
|
5794
|
+
const m = manifest;
|
|
5795
|
+
if (!m || typeof m !== "object" || !m.files || typeof m.files !== "object") {
|
|
5796
|
+
throw new Error("Invalid manifest returned from content endpoint.");
|
|
5797
|
+
}
|
|
5798
|
+
if (typeof m.contentHash === "string" && m.contentHash) {
|
|
5799
|
+
contentHash = m.contentHash;
|
|
5800
|
+
}
|
|
5801
|
+
const base = prefixPerEntry ? join13(tempDir, entry.name) : tempDir;
|
|
5802
|
+
if (prefixPerEntry) {
|
|
5803
|
+
await mkdir5(base, { recursive: true });
|
|
5804
|
+
}
|
|
5805
|
+
for (const [filePath, fileContent] of Object.entries(m.files)) {
|
|
5806
|
+
if (typeof fileContent !== "string") continue;
|
|
5807
|
+
const targetPath = join13(base, filePath);
|
|
5808
|
+
if (!isPathSafe(base, targetPath)) {
|
|
5809
|
+
continue;
|
|
5810
|
+
}
|
|
5811
|
+
await mkdir5(dirname4(targetPath), { recursive: true });
|
|
5812
|
+
await writeFile3(targetPath, fileContent, "utf-8");
|
|
5813
|
+
}
|
|
5814
|
+
const discovered = await discoverSkills(base);
|
|
5815
|
+
for (const skill of discovered) {
|
|
5816
|
+
skills.push(skill);
|
|
5817
|
+
const displayName = getSkillDisplayName(skill);
|
|
5818
|
+
const relativeSkillDir = skill.path.startsWith(`${tempDir}/`) ? skill.path.slice(tempDir.length + 1) : null;
|
|
5819
|
+
const relativeSkillMd = relativeSkillDir ? `${relativeSkillDir}/SKILL.md` : null;
|
|
5820
|
+
originMap.set(displayName, {
|
|
5821
|
+
sourceType: "well-known",
|
|
5822
|
+
sourceUrl,
|
|
5823
|
+
source: sourceIdentifier,
|
|
5824
|
+
skillPath: relativeSkillMd ?? void 0
|
|
5825
|
+
});
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
for (const entry of staticEntries) {
|
|
5830
|
+
const fetched = await wellKnownProvider.fetchSkillByEntry(resolvedBaseUrl, entry);
|
|
5831
|
+
if (!fetched) continue;
|
|
5832
|
+
const skillDir = join13(tempDir, fetched.installName);
|
|
5240
5833
|
await mkdir5(skillDir, { recursive: true });
|
|
5241
|
-
for (const [filePath,
|
|
5834
|
+
for (const [filePath, fileContent] of fetched.files.entries()) {
|
|
5242
5835
|
const targetPath = join13(skillDir, filePath);
|
|
5243
5836
|
if (!isPathSafe(skillDir, targetPath)) {
|
|
5244
5837
|
throw new Error("Invalid file path in well-known skill index.");
|
|
5245
5838
|
}
|
|
5246
5839
|
await mkdir5(dirname4(targetPath), { recursive: true });
|
|
5247
|
-
await writeFile3(targetPath,
|
|
5840
|
+
await writeFile3(targetPath, fileContent, "utf-8");
|
|
5248
5841
|
}
|
|
5249
5842
|
const localSkill = {
|
|
5250
|
-
name:
|
|
5251
|
-
description:
|
|
5843
|
+
name: fetched.installName,
|
|
5844
|
+
description: fetched.description,
|
|
5252
5845
|
path: skillDir,
|
|
5253
|
-
rawContent:
|
|
5254
|
-
metadata:
|
|
5846
|
+
rawContent: fetched.content,
|
|
5847
|
+
metadata: fetched.metadata
|
|
5255
5848
|
};
|
|
5256
5849
|
skills.push(localSkill);
|
|
5257
5850
|
originMap.set(getSkillDisplayName(localSkill), {
|
|
5258
5851
|
sourceType: "well-known",
|
|
5259
|
-
sourceUrl:
|
|
5852
|
+
sourceUrl: fetched.sourceUrl,
|
|
5260
5853
|
source: sourceIdentifier,
|
|
5261
|
-
skillPath:
|
|
5854
|
+
skillPath: fetched.sourceUrl
|
|
5262
5855
|
});
|
|
5263
5856
|
}
|
|
5264
|
-
|
|
5857
|
+
if (skills.length === 0) {
|
|
5858
|
+
throw new Error(
|
|
5859
|
+
"No skills found at this URL. Make sure it exposes /.well-known/skills/index.json."
|
|
5860
|
+
);
|
|
5861
|
+
}
|
|
5862
|
+
return { tempDir, skills, originBySkillName: originMap, contentHash };
|
|
5265
5863
|
}
|
|
5266
5864
|
|
|
5267
5865
|
// src/marketplace.ts
|
|
@@ -5571,7 +6169,7 @@ function autoSelect(skills, options) {
|
|
|
5571
6169
|
}
|
|
5572
6170
|
|
|
5573
6171
|
// src/tui/screens/AddSkillSelect.tsx
|
|
5574
|
-
import { jsx as
|
|
6172
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
5575
6173
|
function AddSkillSelectScreen() {
|
|
5576
6174
|
const {
|
|
5577
6175
|
invocation,
|
|
@@ -5585,16 +6183,16 @@ function AddSkillSelectScreen() {
|
|
|
5585
6183
|
resetAddSkill,
|
|
5586
6184
|
setLastSource
|
|
5587
6185
|
} = useNavigation();
|
|
5588
|
-
const [status, setStatus] =
|
|
6186
|
+
const [status, setStatus] = React16.useState(
|
|
5589
6187
|
addSkill.skills && addSkill.skills.length > 0 ? "ready" : "loading"
|
|
5590
6188
|
);
|
|
5591
|
-
const [error, setError] =
|
|
5592
|
-
const [listMode, setListMode] =
|
|
5593
|
-
const [showLoading, setShowLoading] =
|
|
6189
|
+
const [error, setError] = React16.useState(null);
|
|
6190
|
+
const [listMode, setListMode] = React16.useState(false);
|
|
6191
|
+
const [showLoading, setShowLoading] = React16.useState(false);
|
|
5594
6192
|
const spinner = useSpinnerFrame(status === "loading");
|
|
5595
6193
|
const source = addSkill.source ?? invocation.source;
|
|
5596
6194
|
const options = invocation.options;
|
|
5597
|
-
|
|
6195
|
+
React16.useEffect(() => {
|
|
5598
6196
|
let cancelled = false;
|
|
5599
6197
|
const load = async () => {
|
|
5600
6198
|
if (!source) {
|
|
@@ -5658,10 +6256,27 @@ function AddSkillSelectScreen() {
|
|
|
5658
6256
|
return;
|
|
5659
6257
|
}
|
|
5660
6258
|
if (parsed.type === "well-known") {
|
|
5661
|
-
const
|
|
6259
|
+
const licenseKey = addSkill.licenseKey ?? options.licenseKey ?? parsed.licenseKey;
|
|
6260
|
+
let prepared;
|
|
6261
|
+
try {
|
|
6262
|
+
prepared = await prepareWellKnownSkills(parsed.url, { licenseKey });
|
|
6263
|
+
} catch (err) {
|
|
6264
|
+
if (err instanceof WellKnownLicenseKeyRequiredError) {
|
|
6265
|
+
updateAddSkill({ parsed });
|
|
6266
|
+
navigateTo("add-license-key");
|
|
6267
|
+
return;
|
|
6268
|
+
}
|
|
6269
|
+
throw err;
|
|
6270
|
+
}
|
|
5662
6271
|
tempDirForCleanup = prepared.tempDir;
|
|
6272
|
+
const nextParsed = {
|
|
6273
|
+
...parsed,
|
|
6274
|
+
...licenseKey ? { licenseKey } : {},
|
|
6275
|
+
...prepared.contentHash ? { contentHash: prepared.contentHash } : {}
|
|
6276
|
+
};
|
|
5663
6277
|
updateAddSkill({
|
|
5664
|
-
parsed,
|
|
6278
|
+
parsed: nextParsed,
|
|
6279
|
+
...licenseKey ? { licenseKey } : {},
|
|
5665
6280
|
tempDir: prepared.tempDir,
|
|
5666
6281
|
skills: prepared.skills,
|
|
5667
6282
|
originBySkillName: prepared.originBySkillName
|
|
@@ -5774,8 +6389,8 @@ function AddSkillSelectScreen() {
|
|
|
5774
6389
|
return () => {
|
|
5775
6390
|
cancelled = true;
|
|
5776
6391
|
};
|
|
5777
|
-
}, [source, addSkill.skills, updateAddSkill, navigateTo, options, setFlash]);
|
|
5778
|
-
|
|
6392
|
+
}, [source, addSkill.skills, addSkill.licenseKey, updateAddSkill, navigateTo, options, setFlash]);
|
|
6393
|
+
React16.useEffect(() => {
|
|
5779
6394
|
if (invocation.source) {
|
|
5780
6395
|
setBackHandler(() => {
|
|
5781
6396
|
setLastSource(invocation.source ?? null);
|
|
@@ -5791,7 +6406,7 @@ function AddSkillSelectScreen() {
|
|
|
5791
6406
|
setBackHandler(null);
|
|
5792
6407
|
};
|
|
5793
6408
|
}, [invocation.source, resetTo, setBackHandler, resetAddSkill, setInvocation, setLastSource]);
|
|
5794
|
-
|
|
6409
|
+
React16.useEffect(() => {
|
|
5795
6410
|
if (status !== "loading") {
|
|
5796
6411
|
setShowLoading(false);
|
|
5797
6412
|
return;
|
|
@@ -5802,18 +6417,18 @@ function AddSkillSelectScreen() {
|
|
|
5802
6417
|
return () => clearTimeout(timer);
|
|
5803
6418
|
}, [status]);
|
|
5804
6419
|
if (!source) {
|
|
5805
|
-
return /* @__PURE__ */
|
|
5806
|
-
/* @__PURE__ */
|
|
5807
|
-
/* @__PURE__ */
|
|
6420
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
|
|
6421
|
+
/* @__PURE__ */ jsx21(AddFlowHeader, { title: "Add skills" }),
|
|
6422
|
+
/* @__PURE__ */ jsx21(Text17, { children: `Missing source. ${BACK_QUIT_HINT}` })
|
|
5808
6423
|
] });
|
|
5809
6424
|
}
|
|
5810
6425
|
if (status === "loading" && !showLoading) {
|
|
5811
|
-
return /* @__PURE__ */
|
|
6426
|
+
return /* @__PURE__ */ jsx21(Box19, { padding: 1 });
|
|
5812
6427
|
}
|
|
5813
6428
|
if (status === "loading") {
|
|
5814
|
-
return /* @__PURE__ */
|
|
5815
|
-
/* @__PURE__ */
|
|
5816
|
-
/* @__PURE__ */
|
|
6429
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
|
|
6430
|
+
/* @__PURE__ */ jsx21(AddFlowHeader, { title: "Scanning skills" }),
|
|
6431
|
+
/* @__PURE__ */ jsxs17(Text17, { children: [
|
|
5817
6432
|
spinner,
|
|
5818
6433
|
" Fetching skills from ",
|
|
5819
6434
|
source
|
|
@@ -5821,32 +6436,32 @@ function AddSkillSelectScreen() {
|
|
|
5821
6436
|
] });
|
|
5822
6437
|
}
|
|
5823
6438
|
if (status === "error") {
|
|
5824
|
-
return /* @__PURE__ */
|
|
5825
|
-
/* @__PURE__ */
|
|
5826
|
-
/* @__PURE__ */
|
|
5827
|
-
/* @__PURE__ */
|
|
6439
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
|
|
6440
|
+
/* @__PURE__ */ jsx21(AddFlowHeader, { title: "Unable to load skills" }),
|
|
6441
|
+
/* @__PURE__ */ jsx21(Text17, { color: "red", children: error }),
|
|
6442
|
+
/* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
5828
6443
|
] });
|
|
5829
6444
|
}
|
|
5830
6445
|
const skills = addSkill.skills ?? [];
|
|
5831
6446
|
if (listMode || status === "list") {
|
|
5832
|
-
return /* @__PURE__ */
|
|
5833
|
-
/* @__PURE__ */
|
|
5834
|
-
skills.map((skill) => /* @__PURE__ */
|
|
5835
|
-
/* @__PURE__ */
|
|
5836
|
-
skill.description ? /* @__PURE__ */
|
|
6447
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
|
|
6448
|
+
/* @__PURE__ */ jsx21(AddFlowHeader, { title: `Available skills (${skills.length})` }),
|
|
6449
|
+
skills.map((skill) => /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", marginBottom: 1, children: [
|
|
6450
|
+
/* @__PURE__ */ jsx21(Text17, { children: getSkillDisplayName(skill) }),
|
|
6451
|
+
skill.description ? /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: skill.description }) : null
|
|
5837
6452
|
] }, skill.name)),
|
|
5838
|
-
/* @__PURE__ */
|
|
6453
|
+
/* @__PURE__ */ jsx21(Text17, { dimColor: true, children: BACK_QUIT_HINT })
|
|
5839
6454
|
] });
|
|
5840
6455
|
}
|
|
5841
6456
|
if (skills.length === 0) {
|
|
5842
|
-
return /* @__PURE__ */
|
|
5843
|
-
/* @__PURE__ */
|
|
5844
|
-
/* @__PURE__ */
|
|
6457
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
|
|
6458
|
+
/* @__PURE__ */ jsx21(AddFlowHeader, { title: "No skills found" }),
|
|
6459
|
+
/* @__PURE__ */ jsx21(Text17, { dimColor: true, children: BACK_QUIT_HINT })
|
|
5845
6460
|
] });
|
|
5846
6461
|
}
|
|
5847
|
-
return /* @__PURE__ */
|
|
5848
|
-
/* @__PURE__ */
|
|
5849
|
-
/* @__PURE__ */
|
|
6462
|
+
return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
|
|
6463
|
+
/* @__PURE__ */ jsx21(AddFlowHeader, { title: "Select skills" }),
|
|
6464
|
+
/* @__PURE__ */ jsx21(
|
|
5850
6465
|
MultiSelect,
|
|
5851
6466
|
{
|
|
5852
6467
|
items: skills.map((skill) => ({
|
|
@@ -5878,22 +6493,22 @@ function AddSkillSelectScreen() {
|
|
|
5878
6493
|
}
|
|
5879
6494
|
|
|
5880
6495
|
// src/tui/screens/AddSource.tsx
|
|
5881
|
-
import { Box as
|
|
5882
|
-
import
|
|
5883
|
-
import
|
|
5884
|
-
import { jsx as
|
|
6496
|
+
import { Box as Box20, Text as Text18 } from "ink";
|
|
6497
|
+
import TextInput3 from "ink-text-input";
|
|
6498
|
+
import React17 from "react";
|
|
6499
|
+
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
5885
6500
|
function AddSourceScreen() {
|
|
5886
6501
|
const { invocation, addSkill, updateAddSkill, navigateTo, setFlash, lastSource, setLastSource } = useNavigation();
|
|
5887
|
-
const [value, setValue] =
|
|
6502
|
+
const [value, setValue] = React17.useState(
|
|
5888
6503
|
addSkill.source ?? invocation.source ?? lastSource ?? ""
|
|
5889
6504
|
);
|
|
5890
|
-
const didAutofillRef =
|
|
6505
|
+
const didAutofillRef = React17.useRef(false);
|
|
5891
6506
|
const { wrapOnChange } = useTextInput({
|
|
5892
6507
|
onClear: () => {
|
|
5893
6508
|
setValue("");
|
|
5894
6509
|
}
|
|
5895
6510
|
});
|
|
5896
|
-
|
|
6511
|
+
React17.useEffect(() => {
|
|
5897
6512
|
const preset = invocation.source;
|
|
5898
6513
|
if (!preset || didAutofillRef.current) return;
|
|
5899
6514
|
didAutofillRef.current = true;
|
|
@@ -5910,31 +6525,31 @@ function AddSourceScreen() {
|
|
|
5910
6525
|
updateAddSkill({ source: trimmed });
|
|
5911
6526
|
navigateTo("add-skill-select");
|
|
5912
6527
|
};
|
|
5913
|
-
return /* @__PURE__ */
|
|
5914
|
-
/* @__PURE__ */
|
|
5915
|
-
/* @__PURE__ */
|
|
5916
|
-
/* @__PURE__ */
|
|
5917
|
-
/* @__PURE__ */
|
|
5918
|
-
/* @__PURE__ */
|
|
5919
|
-
/* @__PURE__ */
|
|
6528
|
+
return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
|
|
6529
|
+
/* @__PURE__ */ jsx22(Header, { title: "Add skills" }),
|
|
6530
|
+
/* @__PURE__ */ jsx22(Box20, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text18, { children: "Where should we fetch skills from?" }) }),
|
|
6531
|
+
/* @__PURE__ */ jsx22(Box20, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: "Examples: owner/repo, https://.../SKILL.md, ./local/path" }) }),
|
|
6532
|
+
/* @__PURE__ */ jsxs18(Box20, { children: [
|
|
6533
|
+
/* @__PURE__ */ jsx22(Text18, { color: "green", children: "> " }),
|
|
6534
|
+
/* @__PURE__ */ jsx22(TextInput3, { value, onChange: wrapOnChange(setValue), onSubmit })
|
|
5920
6535
|
] }),
|
|
5921
|
-
/* @__PURE__ */
|
|
6536
|
+
/* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text18, { dimColor: true, children: TEXT_INPUT_HINT }) })
|
|
5922
6537
|
] });
|
|
5923
6538
|
}
|
|
5924
6539
|
|
|
5925
6540
|
// src/tui/screens/AddTargets.tsx
|
|
5926
|
-
import { Box as
|
|
5927
|
-
import
|
|
5928
|
-
import { Fragment as Fragment2, jsx as
|
|
6541
|
+
import { Box as Box21, Text as Text19 } from "ink";
|
|
6542
|
+
import React18 from "react";
|
|
6543
|
+
import { Fragment as Fragment2, jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5929
6544
|
function AddTargetsScreen() {
|
|
5930
6545
|
const { invocation, addSkill, updateAddSkill, navigateTo, setFlash, navAction } = useNavigation();
|
|
5931
|
-
const [status, setStatus] =
|
|
5932
|
-
const [mode, setMode] =
|
|
5933
|
-
const [availableAgents, setAvailableAgents] =
|
|
5934
|
-
const [lastSelected, setLastSelected] =
|
|
5935
|
-
const [showLoading, setShowLoading] =
|
|
6546
|
+
const [status, setStatus] = React18.useState("loading");
|
|
6547
|
+
const [mode, setMode] = React18.useState("choice");
|
|
6548
|
+
const [availableAgents, setAvailableAgents] = React18.useState([]);
|
|
6549
|
+
const [lastSelected, setLastSelected] = React18.useState([]);
|
|
6550
|
+
const [showLoading, setShowLoading] = React18.useState(false);
|
|
5936
6551
|
const spinner = useSpinnerFrame(status === "loading");
|
|
5937
|
-
|
|
6552
|
+
React18.useEffect(() => {
|
|
5938
6553
|
let cancelled = false;
|
|
5939
6554
|
const run = async () => {
|
|
5940
6555
|
setStatus("loading");
|
|
@@ -5983,7 +6598,7 @@ function AddTargetsScreen() {
|
|
|
5983
6598
|
cancelled = true;
|
|
5984
6599
|
};
|
|
5985
6600
|
}, [invocation.options, updateAddSkill, navigateTo, addSkill.targetAgents, setFlash, navAction]);
|
|
5986
|
-
|
|
6601
|
+
React18.useEffect(() => {
|
|
5987
6602
|
if (status !== "loading") {
|
|
5988
6603
|
setShowLoading(false);
|
|
5989
6604
|
return;
|
|
@@ -5994,12 +6609,12 @@ function AddTargetsScreen() {
|
|
|
5994
6609
|
return () => clearTimeout(timer);
|
|
5995
6610
|
}, [status]);
|
|
5996
6611
|
if (status === "loading" && !showLoading) {
|
|
5997
|
-
return /* @__PURE__ */
|
|
6612
|
+
return /* @__PURE__ */ jsx23(Box21, { padding: 1 });
|
|
5998
6613
|
}
|
|
5999
6614
|
if (status === "loading") {
|
|
6000
|
-
return /* @__PURE__ */
|
|
6001
|
-
/* @__PURE__ */
|
|
6002
|
-
/* @__PURE__ */
|
|
6615
|
+
return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
|
|
6616
|
+
/* @__PURE__ */ jsx23(AddFlowHeader, { title: "Detecting agents" }),
|
|
6617
|
+
/* @__PURE__ */ jsxs19(Text19, { children: [
|
|
6003
6618
|
spinner,
|
|
6004
6619
|
" Checking installed agents..."
|
|
6005
6620
|
] })
|
|
@@ -6021,9 +6636,9 @@ function AddTargetsScreen() {
|
|
|
6021
6636
|
label: agents[agent].displayName,
|
|
6022
6637
|
hint: agents[agent].skillsDir
|
|
6023
6638
|
}));
|
|
6024
|
-
return /* @__PURE__ */
|
|
6025
|
-
/* @__PURE__ */
|
|
6026
|
-
/* @__PURE__ */
|
|
6639
|
+
return /* @__PURE__ */ jsx23(Box21, { flexDirection: "column", padding: 1, children: mode === "choice" ? /* @__PURE__ */ jsxs19(Fragment2, { children: [
|
|
6640
|
+
/* @__PURE__ */ jsx23(AddFlowHeader, { title: "Install to" }),
|
|
6641
|
+
/* @__PURE__ */ jsx23(
|
|
6027
6642
|
SingleSelect,
|
|
6028
6643
|
{
|
|
6029
6644
|
items: [
|
|
@@ -6056,9 +6671,9 @@ function AddTargetsScreen() {
|
|
|
6056
6671
|
}
|
|
6057
6672
|
}
|
|
6058
6673
|
)
|
|
6059
|
-
] }) : /* @__PURE__ */
|
|
6060
|
-
/* @__PURE__ */
|
|
6061
|
-
/* @__PURE__ */
|
|
6674
|
+
] }) : /* @__PURE__ */ jsxs19(Fragment2, { children: [
|
|
6675
|
+
/* @__PURE__ */ jsx23(AddFlowHeader, { title: "Select agents" }),
|
|
6676
|
+
/* @__PURE__ */ jsx23(
|
|
6062
6677
|
MultiSelect,
|
|
6063
6678
|
{
|
|
6064
6679
|
items: selectableItems,
|
|
@@ -6090,9 +6705,9 @@ function AddTargetsScreen() {
|
|
|
6090
6705
|
}
|
|
6091
6706
|
|
|
6092
6707
|
// src/tui/screens/FindSkillResults.tsx
|
|
6093
|
-
import { Box as
|
|
6094
|
-
import
|
|
6095
|
-
import { jsx as
|
|
6708
|
+
import { Box as Box22, Text as Text20 } from "ink";
|
|
6709
|
+
import React19 from "react";
|
|
6710
|
+
import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
6096
6711
|
var formatStars = (value) => {
|
|
6097
6712
|
if (!value || value <= 0) return "";
|
|
6098
6713
|
if (value >= 1e3) {
|
|
@@ -6121,8 +6736,8 @@ var truncateLabel = (value, max = 100) => {
|
|
|
6121
6736
|
};
|
|
6122
6737
|
function FindSkillResultsScreen() {
|
|
6123
6738
|
const { findSkill, updateFindSkill, resetAddSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
|
|
6124
|
-
const [status, setStatus] =
|
|
6125
|
-
const [error, setError] =
|
|
6739
|
+
const [status, setStatus] = React19.useState("ready");
|
|
6740
|
+
const [error, setError] = React19.useState(null);
|
|
6126
6741
|
const spinner = useSpinnerFrame(status === "loading");
|
|
6127
6742
|
const results = findSkill.results ?? [];
|
|
6128
6743
|
const query = findSkill.query ?? "";
|
|
@@ -6160,36 +6775,36 @@ function FindSkillResultsScreen() {
|
|
|
6160
6775
|
setStatus("error");
|
|
6161
6776
|
}
|
|
6162
6777
|
};
|
|
6163
|
-
|
|
6778
|
+
React19.useEffect(() => {
|
|
6164
6779
|
if (results.length === 0) {
|
|
6165
6780
|
setStatus("ready");
|
|
6166
6781
|
}
|
|
6167
6782
|
}, [results.length]);
|
|
6168
6783
|
if (status === "loading") {
|
|
6169
|
-
return /* @__PURE__ */
|
|
6170
|
-
/* @__PURE__ */
|
|
6171
|
-
/* @__PURE__ */
|
|
6784
|
+
return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", padding: 1, children: [
|
|
6785
|
+
/* @__PURE__ */ jsx24(Header, { title: "Preparing skills" }),
|
|
6786
|
+
/* @__PURE__ */ jsxs20(Text20, { children: [
|
|
6172
6787
|
spinner,
|
|
6173
6788
|
" Cloning repositories..."
|
|
6174
6789
|
] })
|
|
6175
6790
|
] });
|
|
6176
6791
|
}
|
|
6177
6792
|
if (results.length === 0) {
|
|
6178
|
-
return /* @__PURE__ */
|
|
6179
|
-
/* @__PURE__ */
|
|
6180
|
-
/* @__PURE__ */
|
|
6181
|
-
/* @__PURE__ */
|
|
6793
|
+
return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", padding: 1, children: [
|
|
6794
|
+
/* @__PURE__ */ jsx24(Header, { title: "No results" }),
|
|
6795
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "No skills found." }),
|
|
6796
|
+
/* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
6182
6797
|
] });
|
|
6183
6798
|
}
|
|
6184
|
-
return /* @__PURE__ */
|
|
6185
|
-
/* @__PURE__ */
|
|
6186
|
-
/* @__PURE__ */
|
|
6187
|
-
/* @__PURE__ */
|
|
6188
|
-
/* @__PURE__ */
|
|
6189
|
-
/* @__PURE__ */
|
|
6799
|
+
return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", padding: 1, children: [
|
|
6800
|
+
/* @__PURE__ */ jsx24(Header, { title: "Select skills" }),
|
|
6801
|
+
/* @__PURE__ */ jsxs20(Box22, { marginBottom: 1, flexDirection: "column", children: [
|
|
6802
|
+
/* @__PURE__ */ jsx24(Text20, { children: `Query: ${query}` }),
|
|
6803
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: `Mode: ${modeLabel} search` }),
|
|
6804
|
+
/* @__PURE__ */ jsx24(Text20, { dimColor: true, children: FIND_SKILLS_HINT })
|
|
6190
6805
|
] }),
|
|
6191
|
-
status === "error" ? /* @__PURE__ */
|
|
6192
|
-
/* @__PURE__ */
|
|
6806
|
+
status === "error" ? /* @__PURE__ */ jsx24(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsx24(Text20, { color: "red", children: error }) }) : null,
|
|
6807
|
+
/* @__PURE__ */ jsx24(
|
|
6193
6808
|
MultiSelect,
|
|
6194
6809
|
{
|
|
6195
6810
|
items: results.map((result) => ({
|
|
@@ -6207,21 +6822,21 @@ function FindSkillResultsScreen() {
|
|
|
6207
6822
|
}
|
|
6208
6823
|
|
|
6209
6824
|
// src/tui/screens/FindSkillSearch.tsx
|
|
6210
|
-
import { Box as
|
|
6211
|
-
import
|
|
6212
|
-
import
|
|
6213
|
-
import { jsx as
|
|
6825
|
+
import { Box as Box23, Text as Text21, useInput as useInput5 } from "ink";
|
|
6826
|
+
import TextInput4 from "ink-text-input";
|
|
6827
|
+
import React20 from "react";
|
|
6828
|
+
import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
6214
6829
|
var MIN_QUERY_LENGTH = 2;
|
|
6215
6830
|
function getDebounceMs(queryLength) {
|
|
6216
6831
|
return Math.max(150, 350 - queryLength * 50);
|
|
6217
6832
|
}
|
|
6218
6833
|
function FindSkillSearchScreen() {
|
|
6219
6834
|
const { findSkill, updateFindSkill, navigateTo, setFlash } = useNavigation();
|
|
6220
|
-
const [value, setValue] =
|
|
6221
|
-
const [status, setStatus] =
|
|
6222
|
-
const [error, setError] =
|
|
6223
|
-
const [preview, setPreview] =
|
|
6224
|
-
const debounceRef =
|
|
6835
|
+
const [value, setValue] = React20.useState(findSkill.query ?? "");
|
|
6836
|
+
const [status, setStatus] = React20.useState("idle");
|
|
6837
|
+
const [error, setError] = React20.useState(null);
|
|
6838
|
+
const [preview, setPreview] = React20.useState([]);
|
|
6839
|
+
const debounceRef = React20.useRef(null);
|
|
6225
6840
|
const spinner = useSpinnerFrame(status === "searching" || status === "loading");
|
|
6226
6841
|
const { wrapOnChange } = useTextInput({
|
|
6227
6842
|
disabled: status === "loading",
|
|
@@ -6232,7 +6847,7 @@ function FindSkillSearchScreen() {
|
|
|
6232
6847
|
updateFindSkill({ query: "" });
|
|
6233
6848
|
}
|
|
6234
6849
|
});
|
|
6235
|
-
const triggerLiveSearch =
|
|
6850
|
+
const triggerLiveSearch = React20.useCallback((query) => {
|
|
6236
6851
|
if (debounceRef.current) {
|
|
6237
6852
|
clearTimeout(debounceRef.current);
|
|
6238
6853
|
debounceRef.current = null;
|
|
@@ -6256,14 +6871,14 @@ function FindSkillSearchScreen() {
|
|
|
6256
6871
|
}
|
|
6257
6872
|
}, debounceMs);
|
|
6258
6873
|
}, []);
|
|
6259
|
-
|
|
6874
|
+
React20.useEffect(() => {
|
|
6260
6875
|
return () => {
|
|
6261
6876
|
if (debounceRef.current) {
|
|
6262
6877
|
clearTimeout(debounceRef.current);
|
|
6263
6878
|
}
|
|
6264
6879
|
};
|
|
6265
6880
|
}, []);
|
|
6266
|
-
const goToLexicalResults =
|
|
6881
|
+
const goToLexicalResults = React20.useCallback(async () => {
|
|
6267
6882
|
const query = value.trim();
|
|
6268
6883
|
if (!query || query.length < MIN_QUERY_LENGTH) {
|
|
6269
6884
|
setFlash(`Enter at least ${MIN_QUERY_LENGTH} characters.`);
|
|
@@ -6290,7 +6905,7 @@ function FindSkillSearchScreen() {
|
|
|
6290
6905
|
setStatus("error");
|
|
6291
6906
|
}
|
|
6292
6907
|
}, [value, setFlash, updateFindSkill, navigateTo]);
|
|
6293
|
-
const runSemanticSearch =
|
|
6908
|
+
const runSemanticSearch = React20.useCallback(async () => {
|
|
6294
6909
|
const query = value.trim();
|
|
6295
6910
|
if (!query) {
|
|
6296
6911
|
setFlash("Enter a search term.");
|
|
@@ -6335,13 +6950,13 @@ function FindSkillSearchScreen() {
|
|
|
6335
6950
|
});
|
|
6336
6951
|
const showPreview = preview.length > 0 && status !== "loading";
|
|
6337
6952
|
const showSearching = status === "searching" && value.trim().length >= MIN_QUERY_LENGTH;
|
|
6338
|
-
return /* @__PURE__ */
|
|
6339
|
-
/* @__PURE__ */
|
|
6340
|
-
/* @__PURE__ */
|
|
6341
|
-
/* @__PURE__ */
|
|
6342
|
-
/* @__PURE__ */
|
|
6343
|
-
/* @__PURE__ */
|
|
6344
|
-
|
|
6953
|
+
return /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", padding: 1, children: [
|
|
6954
|
+
/* @__PURE__ */ jsx25(Header, { title: "Find skills" }),
|
|
6955
|
+
/* @__PURE__ */ jsx25(Box23, { marginBottom: 1, children: /* @__PURE__ */ jsx25(Text21, { children: "Find a skill to give your agent new capabilities." }) }),
|
|
6956
|
+
/* @__PURE__ */ jsxs21(Box23, { children: [
|
|
6957
|
+
/* @__PURE__ */ jsx25(Text21, { color: "green", children: "> " }),
|
|
6958
|
+
/* @__PURE__ */ jsx25(
|
|
6959
|
+
TextInput4,
|
|
6345
6960
|
{
|
|
6346
6961
|
value,
|
|
6347
6962
|
onChange: wrapOnChange((next) => {
|
|
@@ -6358,52 +6973,52 @@ function FindSkillSearchScreen() {
|
|
|
6358
6973
|
}
|
|
6359
6974
|
)
|
|
6360
6975
|
] }),
|
|
6361
|
-
showSearching && !showPreview ? /* @__PURE__ */
|
|
6976
|
+
showSearching && !showPreview ? /* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text21, { dimColor: true, children: [
|
|
6362
6977
|
spinner,
|
|
6363
6978
|
" Searching..."
|
|
6364
6979
|
] }) }) : null,
|
|
6365
|
-
showPreview ? /* @__PURE__ */
|
|
6366
|
-
/* @__PURE__ */
|
|
6980
|
+
showPreview ? /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", marginTop: 1, children: [
|
|
6981
|
+
/* @__PURE__ */ jsxs21(Text21, { dimColor: true, children: [
|
|
6367
6982
|
preview.length,
|
|
6368
6983
|
" result",
|
|
6369
6984
|
preview.length !== 1 ? "s" : "",
|
|
6370
6985
|
" found:"
|
|
6371
6986
|
] }),
|
|
6372
|
-
preview.slice(0, 3).map((result) => /* @__PURE__ */
|
|
6987
|
+
preview.slice(0, 3).map((result) => /* @__PURE__ */ jsxs21(Text21, { dimColor: true, children: [
|
|
6373
6988
|
" ",
|
|
6374
6989
|
"\u2022 ",
|
|
6375
6990
|
result.name,
|
|
6376
6991
|
result.repoOwner ? ` (${result.repoOwner}/${result.repoName})` : ""
|
|
6377
6992
|
] }, result.id)),
|
|
6378
|
-
preview.length > 3 ? /* @__PURE__ */
|
|
6993
|
+
preview.length > 3 ? /* @__PURE__ */ jsxs21(Text21, { dimColor: true, children: [
|
|
6379
6994
|
" ",
|
|
6380
6995
|
"... and ",
|
|
6381
6996
|
preview.length - 3,
|
|
6382
6997
|
" more"
|
|
6383
6998
|
] }) : null
|
|
6384
6999
|
] }) : null,
|
|
6385
|
-
status === "loading" ? /* @__PURE__ */
|
|
7000
|
+
status === "loading" ? /* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text21, { children: [
|
|
6386
7001
|
spinner,
|
|
6387
7002
|
" Running AI search..."
|
|
6388
7003
|
] }) }) : null,
|
|
6389
|
-
status === "error" ? /* @__PURE__ */
|
|
6390
|
-
/* @__PURE__ */
|
|
6391
|
-
/* @__PURE__ */
|
|
7004
|
+
status === "error" ? /* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx25(Text21, { color: "red", children: error }) }) : null,
|
|
7005
|
+
/* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: "Enter for fast results, Tab for AI search" }) }),
|
|
7006
|
+
/* @__PURE__ */ jsx25(Box23, { children: /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: TEXT_INPUT_HINT }) })
|
|
6392
7007
|
] });
|
|
6393
7008
|
}
|
|
6394
7009
|
|
|
6395
7010
|
// src/tui/screens/GetUrl.tsx
|
|
6396
|
-
import { Box as
|
|
6397
|
-
import
|
|
6398
|
-
import { jsx as
|
|
7011
|
+
import { Box as Box24, Text as Text22, useApp } from "ink";
|
|
7012
|
+
import React21 from "react";
|
|
7013
|
+
import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
6399
7014
|
function GetUrlScreen() {
|
|
6400
7015
|
const { exit } = useApp();
|
|
6401
7016
|
const { invocation } = useNavigation();
|
|
6402
7017
|
const spinner = useSpinnerFrame(true);
|
|
6403
|
-
const didRun =
|
|
7018
|
+
const didRun = React21.useRef(false);
|
|
6404
7019
|
const outputPath = invocation.options?.output ?? null;
|
|
6405
7020
|
const outputFormat = invocation.options?.json ? "json" : "markdown";
|
|
6406
|
-
|
|
7021
|
+
React21.useEffect(() => {
|
|
6407
7022
|
let cancelled = false;
|
|
6408
7023
|
const run = async () => {
|
|
6409
7024
|
if (didRun.current) return;
|
|
@@ -6432,12 +7047,12 @@ function GetUrlScreen() {
|
|
|
6432
7047
|
cancelled = true;
|
|
6433
7048
|
};
|
|
6434
7049
|
}, [exit, invocation]);
|
|
6435
|
-
return /* @__PURE__ */
|
|
6436
|
-
/* @__PURE__ */
|
|
6437
|
-
invocation.source ? /* @__PURE__ */
|
|
6438
|
-
outputPath ? /* @__PURE__ */
|
|
6439
|
-
outputFormat === "json" ? /* @__PURE__ */
|
|
6440
|
-
/* @__PURE__ */
|
|
7050
|
+
return /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", padding: 1, children: [
|
|
7051
|
+
/* @__PURE__ */ jsx26(Header, { title: "Fetching URL" }),
|
|
7052
|
+
invocation.source ? /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: `URL: ${invocation.source}` }) : null,
|
|
7053
|
+
outputPath ? /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: `Output: ${outputPath}` }) : null,
|
|
7054
|
+
outputFormat === "json" ? /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: "Format: json" }) : null,
|
|
7055
|
+
/* @__PURE__ */ jsxs22(Text22, { children: [
|
|
6441
7056
|
spinner,
|
|
6442
7057
|
" Fetching markdown..."
|
|
6443
7058
|
] })
|
|
@@ -6445,8 +7060,8 @@ function GetUrlScreen() {
|
|
|
6445
7060
|
}
|
|
6446
7061
|
|
|
6447
7062
|
// src/tui/screens/ListSkills.tsx
|
|
6448
|
-
import { Box as
|
|
6449
|
-
import
|
|
7063
|
+
import { Box as Box25, Text as Text23 } from "ink";
|
|
7064
|
+
import React22 from "react";
|
|
6450
7065
|
|
|
6451
7066
|
// src/installed-skills.ts
|
|
6452
7067
|
import { lstat as lstat2, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
@@ -6561,12 +7176,12 @@ async function findSkillInstallations(skillName, scope, cwd = process.cwd()) {
|
|
|
6561
7176
|
}
|
|
6562
7177
|
|
|
6563
7178
|
// src/tui/screens/ListSkills.tsx
|
|
6564
|
-
import { jsx as
|
|
7179
|
+
import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
6565
7180
|
function ListScreen() {
|
|
6566
7181
|
const { navigateTo, setBackHandler } = useNavigation();
|
|
6567
|
-
const [summaries, setSummaries] =
|
|
6568
|
-
const [selectedAgent, setSelectedAgent] =
|
|
6569
|
-
|
|
7182
|
+
const [summaries, setSummaries] = React22.useState([]);
|
|
7183
|
+
const [selectedAgent, setSelectedAgent] = React22.useState(null);
|
|
7184
|
+
React22.useEffect(() => {
|
|
6570
7185
|
let cancelled = false;
|
|
6571
7186
|
const load = async () => {
|
|
6572
7187
|
const installed = await detectInstalledAgents();
|
|
@@ -6589,7 +7204,7 @@ function ListScreen() {
|
|
|
6589
7204
|
cancelled = true;
|
|
6590
7205
|
};
|
|
6591
7206
|
}, []);
|
|
6592
|
-
|
|
7207
|
+
React22.useEffect(() => {
|
|
6593
7208
|
if (!selectedAgent) {
|
|
6594
7209
|
setBackHandler(null);
|
|
6595
7210
|
return;
|
|
@@ -6604,17 +7219,17 @@ function ListScreen() {
|
|
|
6604
7219
|
}, [selectedAgent, setBackHandler]);
|
|
6605
7220
|
if (selectedAgent) {
|
|
6606
7221
|
const total = selectedAgent.projectSkills.length + selectedAgent.globalSkills.length;
|
|
6607
|
-
return /* @__PURE__ */
|
|
6608
|
-
/* @__PURE__ */
|
|
6609
|
-
selectedAgent.projectSkills.length > 0 ? /* @__PURE__ */
|
|
6610
|
-
/* @__PURE__ */
|
|
6611
|
-
selectedAgent.projectSkills.map((name) => /* @__PURE__ */
|
|
7222
|
+
return /* @__PURE__ */ jsxs23(Box25, { flexDirection: "column", padding: 1, children: [
|
|
7223
|
+
/* @__PURE__ */ jsx27(Header, { title: `${agents[selectedAgent.agent].displayName} (${total})` }),
|
|
7224
|
+
selectedAgent.projectSkills.length > 0 ? /* @__PURE__ */ jsxs23(Box25, { flexDirection: "column", marginBottom: 1, children: [
|
|
7225
|
+
/* @__PURE__ */ jsx27(Text23, { children: "Project" }),
|
|
7226
|
+
selectedAgent.projectSkills.map((name) => /* @__PURE__ */ jsx27(Text23, { dimColor: true, children: name }, `p-${name}`))
|
|
6612
7227
|
] }) : null,
|
|
6613
|
-
selectedAgent.globalSkills.length > 0 ? /* @__PURE__ */
|
|
6614
|
-
/* @__PURE__ */
|
|
6615
|
-
selectedAgent.globalSkills.map((name) => /* @__PURE__ */
|
|
7228
|
+
selectedAgent.globalSkills.length > 0 ? /* @__PURE__ */ jsxs23(Box25, { flexDirection: "column", marginBottom: 1, children: [
|
|
7229
|
+
/* @__PURE__ */ jsx27(Text23, { children: "Global" }),
|
|
7230
|
+
selectedAgent.globalSkills.map((name) => /* @__PURE__ */ jsx27(Text23, { dimColor: true, children: name }, `g-${name}`))
|
|
6616
7231
|
] }) : null,
|
|
6617
|
-
/* @__PURE__ */
|
|
7232
|
+
/* @__PURE__ */ jsx27(
|
|
6618
7233
|
SelectMenu,
|
|
6619
7234
|
{
|
|
6620
7235
|
items: [
|
|
@@ -6641,9 +7256,9 @@ function ListScreen() {
|
|
|
6641
7256
|
hint: `${count} skill${count !== 1 ? "s" : ""}`
|
|
6642
7257
|
};
|
|
6643
7258
|
});
|
|
6644
|
-
return /* @__PURE__ */
|
|
6645
|
-
/* @__PURE__ */
|
|
6646
|
-
/* @__PURE__ */
|
|
7259
|
+
return /* @__PURE__ */ jsxs23(Box25, { flexDirection: "column", padding: 1, children: [
|
|
7260
|
+
/* @__PURE__ */ jsx27(Header, { title: "Installed skills" }),
|
|
7261
|
+
/* @__PURE__ */ jsx27(
|
|
6647
7262
|
SelectMenu,
|
|
6648
7263
|
{
|
|
6649
7264
|
items,
|
|
@@ -6660,8 +7275,8 @@ function ListScreen() {
|
|
|
6660
7275
|
}
|
|
6661
7276
|
|
|
6662
7277
|
// src/tui/screens/MainMenu.tsx
|
|
6663
|
-
import { Box as
|
|
6664
|
-
import { jsx as
|
|
7278
|
+
import { Box as Box26 } from "ink";
|
|
7279
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
6665
7280
|
function MainMenu() {
|
|
6666
7281
|
const { navigateTo, resetAddSkill, resetFindSkill, setInvocation } = useNavigation();
|
|
6667
7282
|
const items = [
|
|
@@ -6675,7 +7290,7 @@ function MainMenu() {
|
|
|
6675
7290
|
{ label: "Update docs", value: "update-docs" },
|
|
6676
7291
|
{ label: "Exit", value: "exit" }
|
|
6677
7292
|
];
|
|
6678
|
-
return /* @__PURE__ */
|
|
7293
|
+
return /* @__PURE__ */ jsx28(Box26, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx28(
|
|
6679
7294
|
SelectMenu,
|
|
6680
7295
|
{
|
|
6681
7296
|
items,
|
|
@@ -6730,16 +7345,16 @@ function MainMenu() {
|
|
|
6730
7345
|
// src/tui/screens/ManageSkills.tsx
|
|
6731
7346
|
import { rm as rm5 } from "fs/promises";
|
|
6732
7347
|
import chalk4 from "chalk";
|
|
6733
|
-
import { Box as
|
|
6734
|
-
import
|
|
6735
|
-
import { jsx as
|
|
7348
|
+
import { Box as Box27, Text as Text24 } from "ink";
|
|
7349
|
+
import React23 from "react";
|
|
7350
|
+
import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
6736
7351
|
function ManageScreen() {
|
|
6737
7352
|
const { navigateTo, setFlash, setBackHandler } = useNavigation();
|
|
6738
|
-
const [summaries, setSummaries] =
|
|
6739
|
-
const [selectedAgent, setSelectedAgent] =
|
|
6740
|
-
const [selectedSkills, setSelectedSkills] =
|
|
6741
|
-
const [isRemoving, setIsRemoving] =
|
|
6742
|
-
|
|
7353
|
+
const [summaries, setSummaries] = React23.useState([]);
|
|
7354
|
+
const [selectedAgent, setSelectedAgent] = React23.useState(null);
|
|
7355
|
+
const [selectedSkills, setSelectedSkills] = React23.useState(null);
|
|
7356
|
+
const [isRemoving, setIsRemoving] = React23.useState(false);
|
|
7357
|
+
React23.useEffect(() => {
|
|
6743
7358
|
let cancelled = false;
|
|
6744
7359
|
const load = async () => {
|
|
6745
7360
|
const installedAgents = await detectInstalledAgents();
|
|
@@ -6761,7 +7376,7 @@ function ManageScreen() {
|
|
|
6761
7376
|
cancelled = true;
|
|
6762
7377
|
};
|
|
6763
7378
|
}, [setFlash]);
|
|
6764
|
-
|
|
7379
|
+
React23.useEffect(() => {
|
|
6765
7380
|
if (isRemoving) {
|
|
6766
7381
|
setBackHandler(() => true);
|
|
6767
7382
|
return () => setBackHandler(null);
|
|
@@ -6784,16 +7399,16 @@ function ManageScreen() {
|
|
|
6784
7399
|
return () => setBackHandler(null);
|
|
6785
7400
|
}, [isRemoving, selectedSkills, selectedAgent, setBackHandler]);
|
|
6786
7401
|
if (summaries.length === 0) {
|
|
6787
|
-
return /* @__PURE__ */
|
|
6788
|
-
/* @__PURE__ */
|
|
6789
|
-
/* @__PURE__ */
|
|
6790
|
-
/* @__PURE__ */
|
|
7402
|
+
return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
|
|
7403
|
+
/* @__PURE__ */ jsx29(Header, { title: "Remove skills" }),
|
|
7404
|
+
/* @__PURE__ */ jsx29(Text24, { dimColor: true, children: "No skills found yet." }),
|
|
7405
|
+
/* @__PURE__ */ jsx29(Text24, { dimColor: true, children: BACK_QUIT_HINT })
|
|
6791
7406
|
] });
|
|
6792
7407
|
}
|
|
6793
7408
|
if (!selectedAgent) {
|
|
6794
|
-
return /* @__PURE__ */
|
|
6795
|
-
/* @__PURE__ */
|
|
6796
|
-
/* @__PURE__ */
|
|
7409
|
+
return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
|
|
7410
|
+
/* @__PURE__ */ jsx29(Header, { title: "Select agent" }),
|
|
7411
|
+
/* @__PURE__ */ jsx29(
|
|
6797
7412
|
SelectMenu,
|
|
6798
7413
|
{
|
|
6799
7414
|
items: summaries.map((summary) => ({
|
|
@@ -6810,9 +7425,9 @@ function ManageScreen() {
|
|
|
6810
7425
|
] });
|
|
6811
7426
|
}
|
|
6812
7427
|
if (!selectedSkills) {
|
|
6813
|
-
return /* @__PURE__ */
|
|
6814
|
-
/* @__PURE__ */
|
|
6815
|
-
/* @__PURE__ */
|
|
7428
|
+
return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
|
|
7429
|
+
/* @__PURE__ */ jsx29(Header, { title: `Remove skills (${agents[selectedAgent.agent].displayName})` }),
|
|
7430
|
+
/* @__PURE__ */ jsx29(
|
|
6816
7431
|
MultiSelect,
|
|
6817
7432
|
{
|
|
6818
7433
|
items: selectedAgent.skills.map((skill) => ({
|
|
@@ -6832,19 +7447,19 @@ function ManageScreen() {
|
|
|
6832
7447
|
] });
|
|
6833
7448
|
}
|
|
6834
7449
|
if (isRemoving) {
|
|
6835
|
-
return /* @__PURE__ */
|
|
6836
|
-
/* @__PURE__ */
|
|
6837
|
-
/* @__PURE__ */
|
|
7450
|
+
return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
|
|
7451
|
+
/* @__PURE__ */ jsx29(Header, { title: "Removing skills" }),
|
|
7452
|
+
/* @__PURE__ */ jsx29(Text24, { children: "Removing selected skills..." })
|
|
6838
7453
|
] });
|
|
6839
7454
|
}
|
|
6840
|
-
return /* @__PURE__ */
|
|
6841
|
-
/* @__PURE__ */
|
|
6842
|
-
selectedSkills.map((skill) => /* @__PURE__ */
|
|
7455
|
+
return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
|
|
7456
|
+
/* @__PURE__ */ jsx29(Header, { title: "Confirm removal" }),
|
|
7457
|
+
selectedSkills.map((skill) => /* @__PURE__ */ jsxs24(Text24, { children: [
|
|
6843
7458
|
formatSkillLabel(skill),
|
|
6844
7459
|
" ",
|
|
6845
7460
|
chalk4.dim(`(${skill.scope})`)
|
|
6846
7461
|
] }, `${skill.slug}-${skill.scope}`)),
|
|
6847
|
-
/* @__PURE__ */
|
|
7462
|
+
/* @__PURE__ */ jsx29(
|
|
6848
7463
|
SelectMenu,
|
|
6849
7464
|
{
|
|
6850
7465
|
items: [
|
|
@@ -6907,14 +7522,14 @@ function formatSkillLabel(skill) {
|
|
|
6907
7522
|
}
|
|
6908
7523
|
|
|
6909
7524
|
// src/tui/screens/MarketplacePlugins.tsx
|
|
6910
|
-
import { Box as
|
|
6911
|
-
import
|
|
6912
|
-
import { jsx as
|
|
7525
|
+
import { Box as Box28, Text as Text25 } from "ink";
|
|
7526
|
+
import React24 from "react";
|
|
7527
|
+
import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
6913
7528
|
function MarketplacePluginScreen() {
|
|
6914
7529
|
const { invocation, addSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
|
|
6915
7530
|
const plugins = addSkill.marketplace?.plugins ?? [];
|
|
6916
7531
|
const options = invocation.options;
|
|
6917
|
-
|
|
7532
|
+
React24.useEffect(() => {
|
|
6918
7533
|
if (plugins.length === 0) {
|
|
6919
7534
|
return;
|
|
6920
7535
|
}
|
|
@@ -6929,25 +7544,25 @@ function MarketplacePluginScreen() {
|
|
|
6929
7544
|
}
|
|
6930
7545
|
}, [plugins, options.yes, updateAddSkill, navigateTo, addSkill.marketplace]);
|
|
6931
7546
|
if (plugins.length === 0) {
|
|
6932
|
-
return /* @__PURE__ */
|
|
6933
|
-
/* @__PURE__ */
|
|
6934
|
-
/* @__PURE__ */
|
|
6935
|
-
/* @__PURE__ */
|
|
7547
|
+
return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
|
|
7548
|
+
/* @__PURE__ */ jsx30(AddFlowHeader, { title: "Marketplace plugins" }),
|
|
7549
|
+
/* @__PURE__ */ jsx30(Text25, { dimColor: true, children: "No plugins found." }),
|
|
7550
|
+
/* @__PURE__ */ jsx30(Text25, { dimColor: true, children: BACK_QUIT_HINT })
|
|
6936
7551
|
] });
|
|
6937
7552
|
}
|
|
6938
7553
|
if (options.list) {
|
|
6939
|
-
return /* @__PURE__ */
|
|
6940
|
-
/* @__PURE__ */
|
|
6941
|
-
plugins.map((plugin) => /* @__PURE__ */
|
|
6942
|
-
/* @__PURE__ */
|
|
6943
|
-
plugin.description ? /* @__PURE__ */
|
|
7554
|
+
return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
|
|
7555
|
+
/* @__PURE__ */ jsx30(AddFlowHeader, { title: `Marketplace plugins (${plugins.length})` }),
|
|
7556
|
+
plugins.map((plugin) => /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", marginBottom: 1, children: [
|
|
7557
|
+
/* @__PURE__ */ jsx30(Text25, { children: plugin.name }),
|
|
7558
|
+
plugin.description ? /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: plugin.description }) : null
|
|
6944
7559
|
] }, plugin.name)),
|
|
6945
|
-
/* @__PURE__ */
|
|
7560
|
+
/* @__PURE__ */ jsx30(Text25, { dimColor: true, children: BACK_QUIT_HINT })
|
|
6946
7561
|
] });
|
|
6947
7562
|
}
|
|
6948
|
-
return /* @__PURE__ */
|
|
6949
|
-
/* @__PURE__ */
|
|
6950
|
-
/* @__PURE__ */
|
|
7563
|
+
return /* @__PURE__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
|
|
7564
|
+
/* @__PURE__ */ jsx30(AddFlowHeader, { title: "Select plugins" }),
|
|
7565
|
+
/* @__PURE__ */ jsx30(
|
|
6951
7566
|
MultiSelect,
|
|
6952
7567
|
{
|
|
6953
7568
|
items: plugins.map((plugin) => ({
|
|
@@ -6974,16 +7589,16 @@ function MarketplacePluginScreen() {
|
|
|
6974
7589
|
}
|
|
6975
7590
|
|
|
6976
7591
|
// src/tui/screens/MarketplaceSkills.tsx
|
|
6977
|
-
import { Box as
|
|
6978
|
-
import
|
|
7592
|
+
import { Box as Box29, Text as Text26 } from "ink";
|
|
7593
|
+
import React25 from "react";
|
|
6979
7594
|
|
|
6980
7595
|
// src/flows/marketplace.ts
|
|
6981
|
-
import { dirname as
|
|
7596
|
+
import { dirname as dirname7, join as join17, relative as relative4 } from "path";
|
|
6982
7597
|
function normalizeCandidatePath(basePath, candidate) {
|
|
6983
7598
|
if (!candidate) return basePath;
|
|
6984
7599
|
const cleaned = candidate.replace(/\\/g, "/");
|
|
6985
7600
|
if (cleaned.endsWith(".md")) {
|
|
6986
|
-
return join17(basePath,
|
|
7601
|
+
return join17(basePath, dirname7(cleaned));
|
|
6987
7602
|
}
|
|
6988
7603
|
return join17(basePath, cleaned);
|
|
6989
7604
|
}
|
|
@@ -7135,16 +7750,16 @@ function buildOriginMap(skills) {
|
|
|
7135
7750
|
}
|
|
7136
7751
|
|
|
7137
7752
|
// src/tui/screens/MarketplaceSkills.tsx
|
|
7138
|
-
import { jsx as
|
|
7753
|
+
import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
7139
7754
|
function MarketplaceSkillScreen() {
|
|
7140
7755
|
const { invocation, addSkill, updateAddSkill, navigateTo, setFlash } = useNavigation();
|
|
7141
7756
|
const options = invocation.options;
|
|
7142
|
-
const [status, setStatus] =
|
|
7143
|
-
const [error, setError] =
|
|
7757
|
+
const [status, setStatus] = React25.useState("loading");
|
|
7758
|
+
const [error, setError] = React25.useState(null);
|
|
7144
7759
|
const spinner = useSpinnerFrame(status === "loading");
|
|
7145
7760
|
const selectedPlugins = addSkill.marketplace?.selectedPlugins ?? [];
|
|
7146
7761
|
const context = addSkill.marketplace?.context;
|
|
7147
|
-
|
|
7762
|
+
React25.useEffect(() => {
|
|
7148
7763
|
let cancelled = false;
|
|
7149
7764
|
const load = async () => {
|
|
7150
7765
|
if (!context || selectedPlugins.length === 0) {
|
|
@@ -7208,27 +7823,27 @@ function MarketplaceSkillScreen() {
|
|
|
7208
7823
|
options.yes
|
|
7209
7824
|
]);
|
|
7210
7825
|
if (status === "loading") {
|
|
7211
|
-
return /* @__PURE__ */
|
|
7212
|
-
/* @__PURE__ */
|
|
7213
|
-
/* @__PURE__ */
|
|
7826
|
+
return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
|
|
7827
|
+
/* @__PURE__ */ jsx31(AddFlowHeader, { title: "Scanning marketplace" }),
|
|
7828
|
+
/* @__PURE__ */ jsxs26(Text26, { children: [
|
|
7214
7829
|
spinner,
|
|
7215
7830
|
" Discovering skills from plugins..."
|
|
7216
7831
|
] })
|
|
7217
7832
|
] });
|
|
7218
7833
|
}
|
|
7219
7834
|
if (status === "error") {
|
|
7220
|
-
return /* @__PURE__ */
|
|
7221
|
-
/* @__PURE__ */
|
|
7222
|
-
/* @__PURE__ */
|
|
7223
|
-
/* @__PURE__ */
|
|
7835
|
+
return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
|
|
7836
|
+
/* @__PURE__ */ jsx31(AddFlowHeader, { title: "Marketplace scan failed" }),
|
|
7837
|
+
/* @__PURE__ */ jsx31(Text26, { color: "red", children: error }),
|
|
7838
|
+
/* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT })
|
|
7224
7839
|
] });
|
|
7225
7840
|
}
|
|
7226
7841
|
const skills = addSkill.marketplace?.skills ?? [];
|
|
7227
7842
|
const warnings = addSkill.marketplace?.warnings ?? [];
|
|
7228
|
-
return /* @__PURE__ */
|
|
7229
|
-
/* @__PURE__ */
|
|
7230
|
-
warnings.length > 0 ? /* @__PURE__ */
|
|
7231
|
-
/* @__PURE__ */
|
|
7843
|
+
return /* @__PURE__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
|
|
7844
|
+
/* @__PURE__ */ jsx31(AddFlowHeader, { title: "Select skills" }),
|
|
7845
|
+
warnings.length > 0 ? /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginBottom: 1, children: warnings.map((warning) => /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: warning }, warning)) }) : null,
|
|
7846
|
+
/* @__PURE__ */ jsx31(
|
|
7232
7847
|
MultiSelect,
|
|
7233
7848
|
{
|
|
7234
7849
|
items: skills.map((entry) => ({
|
|
@@ -7278,8 +7893,8 @@ function autoSelect2(skills, names, yes) {
|
|
|
7278
7893
|
// src/tui/screens/ScanSkills.tsx
|
|
7279
7894
|
import { rm as rm6 } from "fs/promises";
|
|
7280
7895
|
import chalk5 from "chalk";
|
|
7281
|
-
import { Box as
|
|
7282
|
-
import
|
|
7896
|
+
import { Box as Box30, Text as Text27 } from "ink";
|
|
7897
|
+
import React26 from "react";
|
|
7283
7898
|
|
|
7284
7899
|
// src/flows/scan-installed-skills.ts
|
|
7285
7900
|
import { existsSync as existsSync7 } from "fs";
|
|
@@ -7502,17 +8117,17 @@ function removalTargetsForRow(row, cwd) {
|
|
|
7502
8117
|
}
|
|
7503
8118
|
|
|
7504
8119
|
// src/tui/screens/ScanSkills.tsx
|
|
7505
|
-
import { Fragment as Fragment3, jsx as
|
|
8120
|
+
import { Fragment as Fragment3, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
7506
8121
|
function ScanSkillsScreen() {
|
|
7507
8122
|
const { setFlash, setBackHandler } = useNavigation();
|
|
7508
|
-
const [view, setView] =
|
|
7509
|
-
const [error, setError] =
|
|
7510
|
-
const [rows, setRows] =
|
|
7511
|
-
const [progress, setProgress] =
|
|
7512
|
-
const [selected, setSelected] =
|
|
7513
|
-
const [removeTargets, setRemoveTargets] =
|
|
8123
|
+
const [view, setView] = React26.useState("loading");
|
|
8124
|
+
const [error, setError] = React26.useState(null);
|
|
8125
|
+
const [rows, setRows] = React26.useState([]);
|
|
8126
|
+
const [progress, setProgress] = React26.useState(null);
|
|
8127
|
+
const [selected, setSelected] = React26.useState(null);
|
|
8128
|
+
const [removeTargets, setRemoveTargets] = React26.useState(null);
|
|
7514
8129
|
const spinner = useSpinnerFrame(view === "running" || view === "removing");
|
|
7515
|
-
|
|
8130
|
+
React26.useEffect(() => {
|
|
7516
8131
|
let cancelled = false;
|
|
7517
8132
|
const run = async () => {
|
|
7518
8133
|
try {
|
|
@@ -7582,7 +8197,7 @@ function ScanSkillsScreen() {
|
|
|
7582
8197
|
cancelled = true;
|
|
7583
8198
|
};
|
|
7584
8199
|
}, []);
|
|
7585
|
-
|
|
8200
|
+
React26.useEffect(() => {
|
|
7586
8201
|
if (view === "actions" || view === "confirm-remove" || view === "removing") {
|
|
7587
8202
|
setBackHandler(() => {
|
|
7588
8203
|
if (view === "confirm-remove") {
|
|
@@ -7602,26 +8217,26 @@ function ScanSkillsScreen() {
|
|
|
7602
8217
|
setBackHandler(null);
|
|
7603
8218
|
return () => setBackHandler(null);
|
|
7604
8219
|
}, [view, setBackHandler]);
|
|
7605
|
-
const riskyRows =
|
|
7606
|
-
const summary =
|
|
8220
|
+
const riskyRows = React26.useMemo(() => rows.filter(isRisky), [rows]);
|
|
8221
|
+
const summary = React26.useMemo(() => scanSummary(rows), [rows]);
|
|
7607
8222
|
if (view === "empty") {
|
|
7608
|
-
return /* @__PURE__ */
|
|
7609
|
-
/* @__PURE__ */
|
|
7610
|
-
/* @__PURE__ */
|
|
8223
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8224
|
+
/* @__PURE__ */ jsx32(Header, { title: "Scan skills" }),
|
|
8225
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
|
|
7611
8226
|
] });
|
|
7612
8227
|
}
|
|
7613
8228
|
if (view === "loading") {
|
|
7614
|
-
return /* @__PURE__ */
|
|
7615
|
-
/* @__PURE__ */
|
|
7616
|
-
/* @__PURE__ */
|
|
8229
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8230
|
+
/* @__PURE__ */ jsx32(Header, { title: "Scan skills" }),
|
|
8231
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "Discovering skills..." })
|
|
7617
8232
|
] });
|
|
7618
8233
|
}
|
|
7619
8234
|
if (view === "running") {
|
|
7620
8235
|
const completed = progress?.completed ?? 0;
|
|
7621
8236
|
const total = progress?.total ?? 0;
|
|
7622
|
-
return /* @__PURE__ */
|
|
7623
|
-
/* @__PURE__ */
|
|
7624
|
-
/* @__PURE__ */
|
|
8237
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8238
|
+
/* @__PURE__ */ jsx32(Header, { title: "Scan skills" }),
|
|
8239
|
+
/* @__PURE__ */ jsxs27(Text27, { children: [
|
|
7625
8240
|
spinner,
|
|
7626
8241
|
" Scanning ",
|
|
7627
8242
|
completed,
|
|
@@ -7631,14 +8246,14 @@ function ScanSkillsScreen() {
|
|
|
7631
8246
|
total === 1 ? "" : "s",
|
|
7632
8247
|
"..."
|
|
7633
8248
|
] }),
|
|
7634
|
-
/* @__PURE__ */
|
|
8249
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
7635
8250
|
] });
|
|
7636
8251
|
}
|
|
7637
8252
|
if (view === "error") {
|
|
7638
|
-
return /* @__PURE__ */
|
|
7639
|
-
/* @__PURE__ */
|
|
7640
|
-
/* @__PURE__ */
|
|
7641
|
-
/* @__PURE__ */
|
|
8253
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8254
|
+
/* @__PURE__ */ jsx32(Header, { title: "Scan skills" }),
|
|
8255
|
+
/* @__PURE__ */ jsx32(Text27, { color: "red", children: error ?? "Scan failed" }),
|
|
8256
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
7642
8257
|
] });
|
|
7643
8258
|
}
|
|
7644
8259
|
if (view === "actions" && selected) {
|
|
@@ -7649,27 +8264,27 @@ function ScanSkillsScreen() {
|
|
|
7649
8264
|
const top = selected.topSignals?.slice(0, 10) ?? [];
|
|
7650
8265
|
const locationLabels = selected.skill.locations.map((l) => l.label);
|
|
7651
8266
|
const riskColor = level === "critical" || level === "high" ? "red" : level === "medium" ? "yellow" : level === "low" ? "green" : "gray";
|
|
7652
|
-
return /* @__PURE__ */
|
|
7653
|
-
/* @__PURE__ */
|
|
7654
|
-
/* @__PURE__ */
|
|
7655
|
-
/* @__PURE__ */
|
|
8267
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8268
|
+
/* @__PURE__ */ jsx32(Header, { title: "Skill risk details" }),
|
|
8269
|
+
/* @__PURE__ */ jsxs27(Text27, { children: [
|
|
8270
|
+
/* @__PURE__ */ jsx32(Text27, { bold: true, children: selected.skill.name }),
|
|
7656
8271
|
" ",
|
|
7657
|
-
/* @__PURE__ */
|
|
8272
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `(${selected.skill.slug})` })
|
|
7658
8273
|
] }),
|
|
7659
|
-
selected.error ? /* @__PURE__ */
|
|
7660
|
-
/* @__PURE__ */
|
|
7661
|
-
/* @__PURE__ */
|
|
8274
|
+
selected.error ? /* @__PURE__ */ jsx32(Text27, { color: "red", children: selected.error }) : /* @__PURE__ */ jsxs27(Fragment3, { children: [
|
|
8275
|
+
/* @__PURE__ */ jsxs27(Text27, { children: [
|
|
8276
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "Verdict:" }),
|
|
7662
8277
|
" ",
|
|
7663
|
-
/* @__PURE__ */
|
|
7664
|
-
/* @__PURE__ */
|
|
8278
|
+
/* @__PURE__ */ jsx32(Text27, { color: riskColor, children: verdict }),
|
|
8279
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: ` \u2022 level=${level} score=${score} issues=${issues}` })
|
|
7665
8280
|
] }),
|
|
7666
|
-
top.length > 0 ? /* @__PURE__ */
|
|
7667
|
-
selected.ruleset ? /* @__PURE__ */
|
|
8281
|
+
top.length > 0 ? /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `Top signals: ${top.join(", ")}` }) : null,
|
|
8282
|
+
selected.ruleset ? /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `Ruleset: ${selected.ruleset}` }) : null
|
|
7668
8283
|
] }),
|
|
7669
|
-
selected.skill.description ? /* @__PURE__ */
|
|
7670
|
-
/* @__PURE__ */
|
|
7671
|
-
locationLabels.length > 0 ? /* @__PURE__ */
|
|
7672
|
-
/* @__PURE__ */
|
|
8284
|
+
selected.skill.description ? /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: selected.skill.description }) : null,
|
|
8285
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `Path: ${selected.skill.path}` }),
|
|
8286
|
+
locationLabels.length > 0 ? /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `Locations: ${locationLabels.join(", ")}` }) : null,
|
|
8287
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(
|
|
7673
8288
|
SelectMenu,
|
|
7674
8289
|
{
|
|
7675
8290
|
items: [
|
|
@@ -7695,17 +8310,17 @@ function ScanSkillsScreen() {
|
|
|
7695
8310
|
}
|
|
7696
8311
|
if (view === "confirm-remove" && selected && removeTargets) {
|
|
7697
8312
|
const title = `Remove ${selected.skill.name}`;
|
|
7698
|
-
return /* @__PURE__ */
|
|
7699
|
-
/* @__PURE__ */
|
|
7700
|
-
/* @__PURE__ */
|
|
7701
|
-
/* @__PURE__ */
|
|
7702
|
-
/* @__PURE__ */
|
|
8313
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8314
|
+
/* @__PURE__ */ jsx32(Header, { title }),
|
|
8315
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "These locations will be removed:" }),
|
|
8316
|
+
/* @__PURE__ */ jsx32(Box30, { flexDirection: "column", marginTop: 1, children: removeTargets.length === 0 ? /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "None found." }) : removeTargets.map((t) => /* @__PURE__ */ jsxs27(Text27, { children: [
|
|
8317
|
+
/* @__PURE__ */ jsx32(Text27, { children: chalk5.red("\u2022") }),
|
|
7703
8318
|
" ",
|
|
7704
|
-
/* @__PURE__ */
|
|
8319
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: t.label }),
|
|
7705
8320
|
" ",
|
|
7706
8321
|
t.path
|
|
7707
8322
|
] }, t.path)) }),
|
|
7708
|
-
/* @__PURE__ */
|
|
8323
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(
|
|
7709
8324
|
SelectMenu,
|
|
7710
8325
|
{
|
|
7711
8326
|
items: [
|
|
@@ -7753,33 +8368,33 @@ function ScanSkillsScreen() {
|
|
|
7753
8368
|
] });
|
|
7754
8369
|
}
|
|
7755
8370
|
if (view === "removing") {
|
|
7756
|
-
return /* @__PURE__ */
|
|
7757
|
-
/* @__PURE__ */
|
|
7758
|
-
/* @__PURE__ */
|
|
8371
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8372
|
+
/* @__PURE__ */ jsx32(Header, { title: "Removing skill" }),
|
|
8373
|
+
/* @__PURE__ */ jsxs27(Text27, { children: [
|
|
7759
8374
|
spinner,
|
|
7760
8375
|
" Removing..."
|
|
7761
8376
|
] }),
|
|
7762
|
-
/* @__PURE__ */
|
|
8377
|
+
/* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
7763
8378
|
] });
|
|
7764
8379
|
}
|
|
7765
8380
|
if (riskyRows.length === 0) {
|
|
7766
|
-
return /* @__PURE__ */
|
|
7767
|
-
/* @__PURE__ */
|
|
7768
|
-
/* @__PURE__ */
|
|
8381
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8382
|
+
/* @__PURE__ */ jsx32(Header, { title: "Scan skills" }),
|
|
8383
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
|
|
7769
8384
|
] });
|
|
7770
8385
|
}
|
|
7771
|
-
return /* @__PURE__ */
|
|
7772
|
-
/* @__PURE__ */
|
|
7773
|
-
/* @__PURE__ */
|
|
7774
|
-
/* @__PURE__ */
|
|
7775
|
-
/* @__PURE__ */
|
|
7776
|
-
/* @__PURE__ */
|
|
7777
|
-
/* @__PURE__ */
|
|
7778
|
-
/* @__PURE__ */
|
|
7779
|
-
/* @__PURE__ */
|
|
8386
|
+
return /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
|
|
8387
|
+
/* @__PURE__ */ jsx32(Header, { title: "Scan skills" }),
|
|
8388
|
+
/* @__PURE__ */ jsxs27(Box30, { marginBottom: 1, flexDirection: "column", children: [
|
|
8389
|
+
/* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `Scanned ${plural(summary.total, "skill")}.` }),
|
|
8390
|
+
/* @__PURE__ */ jsxs27(Text27, { children: [
|
|
8391
|
+
/* @__PURE__ */ jsx32(Text27, { color: "red", children: plural(summary.high, "high risk") }),
|
|
8392
|
+
/* @__PURE__ */ jsx32(Text27, { children: ", " }),
|
|
8393
|
+
/* @__PURE__ */ jsx32(Text27, { color: "yellow", children: plural(summary.medium, "medium risk") }),
|
|
8394
|
+
/* @__PURE__ */ jsx32(Text27, { children: " detected." })
|
|
7780
8395
|
] })
|
|
7781
8396
|
] }),
|
|
7782
|
-
/* @__PURE__ */
|
|
8397
|
+
/* @__PURE__ */ jsx32(
|
|
7783
8398
|
SingleSelect,
|
|
7784
8399
|
{
|
|
7785
8400
|
items: riskyRows.map((row) => ({
|
|
@@ -7800,8 +8415,8 @@ function ScanSkillsScreen() {
|
|
|
7800
8415
|
}
|
|
7801
8416
|
|
|
7802
8417
|
// src/tui/screens/UpdateDocs.tsx
|
|
7803
|
-
import { Box as
|
|
7804
|
-
import
|
|
8418
|
+
import { Box as Box31, Text as Text28 } from "ink";
|
|
8419
|
+
import React27 from "react";
|
|
7805
8420
|
|
|
7806
8421
|
// src/docs/update.ts
|
|
7807
8422
|
import { readdir as readdir6, stat as stat6 } from "fs/promises";
|
|
@@ -7867,12 +8482,12 @@ async function updateDocs(cwd = process.cwd()) {
|
|
|
7867
8482
|
}
|
|
7868
8483
|
|
|
7869
8484
|
// src/tui/screens/UpdateDocs.tsx
|
|
7870
|
-
import { jsx as
|
|
8485
|
+
import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
7871
8486
|
function UpdateDocsScreen() {
|
|
7872
|
-
const [status, setStatus] =
|
|
7873
|
-
const [summary, setSummary] =
|
|
8487
|
+
const [status, setStatus] = React27.useState("running");
|
|
8488
|
+
const [summary, setSummary] = React27.useState(null);
|
|
7874
8489
|
const spinner = useSpinnerFrame(status === "running");
|
|
7875
|
-
|
|
8490
|
+
React27.useEffect(() => {
|
|
7876
8491
|
let cancelled = false;
|
|
7877
8492
|
const run = async () => {
|
|
7878
8493
|
const output2 = await updateDocs();
|
|
@@ -7890,65 +8505,180 @@ function UpdateDocsScreen() {
|
|
|
7890
8505
|
};
|
|
7891
8506
|
}, []);
|
|
7892
8507
|
if (status === "running") {
|
|
7893
|
-
return /* @__PURE__ */
|
|
7894
|
-
/* @__PURE__ */
|
|
7895
|
-
/* @__PURE__ */
|
|
8508
|
+
return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
|
|
8509
|
+
/* @__PURE__ */ jsx33(Header, { title: "Updating docs" }),
|
|
8510
|
+
/* @__PURE__ */ jsxs28(Text28, { children: [
|
|
7896
8511
|
spinner,
|
|
7897
8512
|
" Pulling latest docs..."
|
|
7898
8513
|
] })
|
|
7899
8514
|
] });
|
|
7900
8515
|
}
|
|
7901
8516
|
if (status === "empty") {
|
|
7902
|
-
return /* @__PURE__ */
|
|
7903
|
-
/* @__PURE__ */
|
|
7904
|
-
/* @__PURE__ */
|
|
7905
|
-
/* @__PURE__ */
|
|
8517
|
+
return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
|
|
8518
|
+
/* @__PURE__ */ jsx33(Header, { title: "Update docs" }),
|
|
8519
|
+
/* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "No docs installed yet." }),
|
|
8520
|
+
/* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
7906
8521
|
] });
|
|
7907
8522
|
}
|
|
7908
8523
|
if (!summary) {
|
|
7909
|
-
return /* @__PURE__ */
|
|
7910
|
-
/* @__PURE__ */
|
|
7911
|
-
/* @__PURE__ */
|
|
8524
|
+
return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
|
|
8525
|
+
/* @__PURE__ */ jsx33(Header, { title: "Update docs" }),
|
|
8526
|
+
/* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Nothing to update." })
|
|
7912
8527
|
] });
|
|
7913
8528
|
}
|
|
7914
8529
|
const cwd = process.cwd();
|
|
7915
|
-
return /* @__PURE__ */
|
|
7916
|
-
/* @__PURE__ */
|
|
7917
|
-
summary.updated.length > 0 ? /* @__PURE__ */
|
|
7918
|
-
/* @__PURE__ */
|
|
7919
|
-
summary.updated.map((item) => /* @__PURE__ */
|
|
8530
|
+
return /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
|
|
8531
|
+
/* @__PURE__ */ jsx33(Header, { title: "Docs update results" }),
|
|
8532
|
+
summary.updated.length > 0 ? /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", marginBottom: 1, children: [
|
|
8533
|
+
/* @__PURE__ */ jsx33(Text28, { children: `Updated ${summary.updated.length} repo${summary.updated.length !== 1 ? "s" : ""}` }),
|
|
8534
|
+
summary.updated.map((item) => /* @__PURE__ */ jsxs28(Text28, { dimColor: true, children: [
|
|
7920
8535
|
item.name,
|
|
7921
8536
|
" \u2192 ",
|
|
7922
8537
|
shortenPath(item.path, cwd)
|
|
7923
8538
|
] }, `updated-${item.name}`))
|
|
7924
8539
|
] }) : null,
|
|
7925
|
-
summary.skipped.length > 0 ? /* @__PURE__ */
|
|
7926
|
-
/* @__PURE__ */
|
|
7927
|
-
summary.skipped.map((item) => /* @__PURE__ */
|
|
8540
|
+
summary.skipped.length > 0 ? /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", marginBottom: 1, children: [
|
|
8541
|
+
/* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Skipped" }),
|
|
8542
|
+
summary.skipped.map((item) => /* @__PURE__ */ jsxs28(Text28, { dimColor: true, children: [
|
|
7928
8543
|
item.name,
|
|
7929
8544
|
item.message ? ` (${item.message})` : ""
|
|
7930
8545
|
] }, `skipped-${item.name}`))
|
|
7931
8546
|
] }) : null,
|
|
7932
|
-
summary.failed.length > 0 ? /* @__PURE__ */
|
|
7933
|
-
/* @__PURE__ */
|
|
7934
|
-
summary.failed.map((item) => /* @__PURE__ */
|
|
8547
|
+
summary.failed.length > 0 ? /* @__PURE__ */ jsxs28(Box31, { flexDirection: "column", marginBottom: 1, children: [
|
|
8548
|
+
/* @__PURE__ */ jsx33(Text28, { color: "red", children: "Failed" }),
|
|
8549
|
+
summary.failed.map((item) => /* @__PURE__ */ jsxs28(Text28, { color: "red", children: [
|
|
7935
8550
|
item.name,
|
|
7936
8551
|
item.message ? ` (${item.message})` : ""
|
|
7937
8552
|
] }, `failed-${item.name}`))
|
|
7938
8553
|
] }) : null,
|
|
7939
|
-
/* @__PURE__ */
|
|
8554
|
+
/* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
7940
8555
|
] });
|
|
7941
8556
|
}
|
|
7942
8557
|
|
|
7943
8558
|
// src/tui/screens/UpdateSkills.tsx
|
|
7944
8559
|
import chalk6 from "chalk";
|
|
7945
|
-
import { Box as
|
|
7946
|
-
import
|
|
8560
|
+
import { Box as Box32, Text as Text29, useInput as useInput6 } from "ink";
|
|
8561
|
+
import React28 from "react";
|
|
7947
8562
|
|
|
7948
8563
|
// src/flows/update-skills.ts
|
|
7949
|
-
import {
|
|
8564
|
+
import { randomUUID } from "crypto";
|
|
8565
|
+
import { cp as cp2, mkdir as mkdir8, mkdtemp as mkdtemp6, rm as rm7, stat as stat7, writeFile as writeFile6 } from "fs/promises";
|
|
8566
|
+
import { homedir as homedir5, tmpdir as tmpdir7 } from "os";
|
|
8567
|
+
import { dirname as dirname9, join as join22 } from "path";
|
|
8568
|
+
|
|
8569
|
+
// src/flows/update-well-known-authed.ts
|
|
8570
|
+
import { mkdir as mkdir7, mkdtemp as mkdtemp5, writeFile as writeFile5 } from "fs/promises";
|
|
7950
8571
|
import { tmpdir as tmpdir6 } from "os";
|
|
7951
|
-
import { dirname as
|
|
8572
|
+
import { dirname as dirname8, join as join21 } from "path";
|
|
8573
|
+
function resolveAuthedIndexAuth(entry, endpoint) {
|
|
8574
|
+
if (entry.auth?.tokenHeader) {
|
|
8575
|
+
return { tokenHeader: entry.auth.tokenHeader, tokenPrefix: entry.auth.tokenPrefix ?? "" };
|
|
8576
|
+
}
|
|
8577
|
+
const spec = endpoint === "content" ? entry.content : entry.verify;
|
|
8578
|
+
const tokenHeader = spec?.tokenHeader;
|
|
8579
|
+
const tokenPrefix = spec?.tokenPrefix;
|
|
8580
|
+
if (!tokenHeader) {
|
|
8581
|
+
throw new Error(
|
|
8582
|
+
"Invalid well-known index entry: missing auth.tokenHeader (or legacy tokenHeader)."
|
|
8583
|
+
);
|
|
8584
|
+
}
|
|
8585
|
+
return { tokenHeader, tokenPrefix: tokenPrefix ?? "" };
|
|
8586
|
+
}
|
|
8587
|
+
async function fetchWellKnownAuthedManifest(sourceUrl, licenseKey) {
|
|
8588
|
+
const result = await wellKnownProvider.fetchIndex(sourceUrl);
|
|
8589
|
+
if (!result) return null;
|
|
8590
|
+
const entry = result.index.skills.find(
|
|
8591
|
+
(e) => e && typeof e === "object" && "content" in e && !("files" in e)
|
|
8592
|
+
);
|
|
8593
|
+
if (!entry?.content?.endpoint) return null;
|
|
8594
|
+
const contentAuth = resolveAuthedIndexAuth(entry, "content");
|
|
8595
|
+
const tokenValue = `${contentAuth.tokenPrefix}${licenseKey}`;
|
|
8596
|
+
if (entry.verify?.endpoint) {
|
|
8597
|
+
const verifyAuth = resolveAuthedIndexAuth(entry, "verify");
|
|
8598
|
+
const verifyRes = await fetch(entry.verify.endpoint, {
|
|
8599
|
+
method: entry.verify.method,
|
|
8600
|
+
headers: { [verifyAuth.tokenHeader]: tokenValue }
|
|
8601
|
+
});
|
|
8602
|
+
if (!verifyRes.ok) {
|
|
8603
|
+
const text = await verifyRes.text().catch(() => "");
|
|
8604
|
+
let msg = text;
|
|
8605
|
+
try {
|
|
8606
|
+
const parsed = JSON.parse(text);
|
|
8607
|
+
if (typeof parsed?.error === "string" && parsed.error) {
|
|
8608
|
+
msg = parsed.error;
|
|
8609
|
+
}
|
|
8610
|
+
} catch {
|
|
8611
|
+
}
|
|
8612
|
+
throw new Error(msg || `License verification failed (${verifyRes.status}).`);
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
const res = await fetch(entry.content.endpoint, {
|
|
8616
|
+
method: entry.content.method,
|
|
8617
|
+
headers: { [contentAuth.tokenHeader]: tokenValue }
|
|
8618
|
+
});
|
|
8619
|
+
if (!res.ok) {
|
|
8620
|
+
const text = await res.text().catch(() => "");
|
|
8621
|
+
throw new Error(text || `Unable to fetch skill content (${res.status}).`);
|
|
8622
|
+
}
|
|
8623
|
+
const json = await res.json();
|
|
8624
|
+
if (json && typeof json === "object" && "success" in json) {
|
|
8625
|
+
const wrapped = json;
|
|
8626
|
+
if (!wrapped.success) {
|
|
8627
|
+
throw new Error(wrapped.error || "Unable to fetch skill content.");
|
|
8628
|
+
}
|
|
8629
|
+
return wrapped.data;
|
|
8630
|
+
}
|
|
8631
|
+
return json;
|
|
8632
|
+
}
|
|
8633
|
+
async function updateFromWellKnownAuthed(target, helpers) {
|
|
8634
|
+
if (target.entry.sourceType !== "well-known") return false;
|
|
8635
|
+
if (!target.entry.licenseKey) return false;
|
|
8636
|
+
const sourceUrl = target.entry.sourceUrl || target.entry.source;
|
|
8637
|
+
const manifest = await fetchWellKnownAuthedManifest(sourceUrl, target.entry.licenseKey);
|
|
8638
|
+
if (!manifest?.files || typeof manifest.files !== "object") {
|
|
8639
|
+
return false;
|
|
8640
|
+
}
|
|
8641
|
+
if (manifest.contentHash) {
|
|
8642
|
+
target.latestHash = manifest.contentHash;
|
|
8643
|
+
if (target.entry.skillFolderHash && target.entry.skillFolderHash === manifest.contentHash) {
|
|
8644
|
+
target.status = "up-to-date";
|
|
8645
|
+
return false;
|
|
8646
|
+
}
|
|
8647
|
+
}
|
|
8648
|
+
const tempDir = await mkdtemp5(join21(tmpdir6(), "playbooks-well-known-"));
|
|
8649
|
+
registerTempDir(tempDir);
|
|
8650
|
+
try {
|
|
8651
|
+
await mkdir7(tempDir, { recursive: true });
|
|
8652
|
+
for (const [filePath, fileContent] of Object.entries(manifest.files)) {
|
|
8653
|
+
const targetPath = join21(tempDir, filePath);
|
|
8654
|
+
if (!isPathSafe(tempDir, targetPath)) continue;
|
|
8655
|
+
await mkdir7(dirname8(targetPath), { recursive: true });
|
|
8656
|
+
await writeFile5(targetPath, fileContent, "utf-8");
|
|
8657
|
+
}
|
|
8658
|
+
let sourceDir = null;
|
|
8659
|
+
if (target.entry.skillPath) {
|
|
8660
|
+
const normalized = target.entry.skillPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
8661
|
+
const folder = normalized.toLowerCase().endsWith("skill.md") ? dirname8(normalized) : normalized;
|
|
8662
|
+
const candidate = join21(tempDir, folder);
|
|
8663
|
+
if (await helpers.pathExists(join21(candidate, "SKILL.md"))) {
|
|
8664
|
+
sourceDir = candidate;
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8667
|
+
if (!sourceDir) {
|
|
8668
|
+
const discovered = await discoverSkills(tempDir);
|
|
8669
|
+
const match = discovered.find((s) => s.name === target.name) ?? null;
|
|
8670
|
+
sourceDir = match ? match.path : null;
|
|
8671
|
+
}
|
|
8672
|
+
if (!sourceDir) {
|
|
8673
|
+
return false;
|
|
8674
|
+
}
|
|
8675
|
+
return await helpers.applyUpdateFromDir(target.name, target.scope, sourceDir);
|
|
8676
|
+
} finally {
|
|
8677
|
+
await cleanupTempDir(tempDir);
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
|
|
8681
|
+
// src/flows/update-skills.ts
|
|
7952
8682
|
var repoTreeCache = /* @__PURE__ */ new Map();
|
|
7953
8683
|
function normalizeSkillFolderPath(skillPath) {
|
|
7954
8684
|
let folderPath = skillPath;
|
|
@@ -8016,6 +8746,23 @@ async function pathExists3(path) {
|
|
|
8016
8746
|
return false;
|
|
8017
8747
|
}
|
|
8018
8748
|
}
|
|
8749
|
+
function getBackupRoot(scope) {
|
|
8750
|
+
const baseDir = scope === "global" ? homedir5() : process.cwd();
|
|
8751
|
+
return join22(baseDir, ".playbooks", "backups");
|
|
8752
|
+
}
|
|
8753
|
+
async function backupDirIfExists(input) {
|
|
8754
|
+
if (process.env.PLAYBOOKS_DISABLE_BACKUPS === "1") return;
|
|
8755
|
+
try {
|
|
8756
|
+
if (!await pathExists3(input.sourcePath)) return;
|
|
8757
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8758
|
+
const safeSkillName = sanitizeSkillName(input.skillName);
|
|
8759
|
+
const backupBase = join22(getBackupRoot(input.scope), stamp, input.scope, safeSkillName);
|
|
8760
|
+
const dest = join22(backupBase, `${input.label}-${randomUUID()}`);
|
|
8761
|
+
await mkdir8(dirname9(dest), { recursive: true });
|
|
8762
|
+
await cp2(input.sourcePath, dest, { recursive: true, dereference: false });
|
|
8763
|
+
} catch {
|
|
8764
|
+
}
|
|
8765
|
+
}
|
|
8019
8766
|
async function applyUpdateFromDir(skillName, scope, sourceDir) {
|
|
8020
8767
|
const canonicalPath = getCanonicalPath(skillName, { global: scope === "global" });
|
|
8021
8768
|
const installs = await findSkillInstallations(skillName, scope);
|
|
@@ -8025,12 +8772,14 @@ async function applyUpdateFromDir(skillName, scope, sourceDir) {
|
|
|
8025
8772
|
return false;
|
|
8026
8773
|
}
|
|
8027
8774
|
if (canonicalExists || hasSymlink) {
|
|
8775
|
+
await backupDirIfExists({ scope, skillName, sourcePath: canonicalPath, label: "canonical" });
|
|
8028
8776
|
await rm7(canonicalPath, { recursive: true, force: true });
|
|
8029
8777
|
await copySkillDirectory(sourceDir, canonicalPath);
|
|
8030
8778
|
}
|
|
8031
8779
|
const updateTargets = installs.filter((i) => !i.isSymlink && i.path !== canonicalPath);
|
|
8032
8780
|
if (updateTargets.length > 0) {
|
|
8033
8781
|
for (const install of updateTargets) {
|
|
8782
|
+
await backupDirIfExists({ scope, skillName, sourcePath: install.path, label: "install" });
|
|
8034
8783
|
await rm7(install.path, { recursive: true, force: true });
|
|
8035
8784
|
await copySkillDirectory(sourceDir, install.path);
|
|
8036
8785
|
}
|
|
@@ -8043,8 +8792,8 @@ async function updateFromRepo(target) {
|
|
|
8043
8792
|
let sourceDir = null;
|
|
8044
8793
|
if (target.entry.skillPath) {
|
|
8045
8794
|
const normalizedPath = target.entry.skillPath.replace(/\\/g, "/");
|
|
8046
|
-
const skillDir =
|
|
8047
|
-
if (await pathExists3(
|
|
8795
|
+
const skillDir = join22(tempDir, dirname9(normalizedPath));
|
|
8796
|
+
if (await pathExists3(join22(skillDir, "SKILL.md"))) {
|
|
8048
8797
|
sourceDir = skillDir;
|
|
8049
8798
|
}
|
|
8050
8799
|
}
|
|
@@ -8085,21 +8834,21 @@ async function updateFromRemote(target) {
|
|
|
8085
8834
|
if (!content && !files) {
|
|
8086
8835
|
return false;
|
|
8087
8836
|
}
|
|
8088
|
-
const tempDir = await
|
|
8837
|
+
const tempDir = await mkdtemp6(join22(tmpdir7(), "playbooks-skill-"));
|
|
8089
8838
|
registerTempDir(tempDir);
|
|
8090
8839
|
try {
|
|
8091
|
-
await
|
|
8840
|
+
await mkdir8(tempDir, { recursive: true });
|
|
8092
8841
|
if (files) {
|
|
8093
8842
|
for (const [filePath, fileContent] of files.entries()) {
|
|
8094
|
-
const targetPath =
|
|
8843
|
+
const targetPath = join22(tempDir, filePath);
|
|
8095
8844
|
if (!isPathSafe(tempDir, targetPath)) {
|
|
8096
8845
|
continue;
|
|
8097
8846
|
}
|
|
8098
|
-
await
|
|
8099
|
-
await
|
|
8847
|
+
await mkdir8(dirname9(targetPath), { recursive: true });
|
|
8848
|
+
await writeFile6(targetPath, fileContent, "utf-8");
|
|
8100
8849
|
}
|
|
8101
8850
|
} else if (content) {
|
|
8102
|
-
await
|
|
8851
|
+
await writeFile6(join22(tempDir, "SKILL.md"), content, "utf-8");
|
|
8103
8852
|
}
|
|
8104
8853
|
return await applyUpdateFromDir(target.name, target.scope, tempDir);
|
|
8105
8854
|
} finally {
|
|
@@ -8107,6 +8856,10 @@ async function updateFromRemote(target) {
|
|
|
8107
8856
|
}
|
|
8108
8857
|
}
|
|
8109
8858
|
async function updateTargetSkill(target) {
|
|
8859
|
+
if (target.entry.sourceType === "well-known" && target.entry.licenseKey) {
|
|
8860
|
+
const updated = await updateFromWellKnownAuthed(target, { applyUpdateFromDir, pathExists: pathExists3 });
|
|
8861
|
+
if (updated) return true;
|
|
8862
|
+
}
|
|
8110
8863
|
if (target.entry.sourceType === "github" || target.entry.sourceType === "gitlab" || target.entry.sourceType === "git") {
|
|
8111
8864
|
return await updateFromRepo(target);
|
|
8112
8865
|
}
|
|
@@ -8196,15 +8949,15 @@ async function updateSkills(targets) {
|
|
|
8196
8949
|
}
|
|
8197
8950
|
|
|
8198
8951
|
// src/tui/screens/UpdateSkills.tsx
|
|
8199
|
-
import { jsx as
|
|
8952
|
+
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
8200
8953
|
function UpdateScreen() {
|
|
8201
8954
|
const { invocation, navigateTo, setFlash } = useNavigation();
|
|
8202
|
-
const [status, setStatus] =
|
|
8203
|
-
const [targets, setTargets] =
|
|
8204
|
-
const [selected, setSelected] =
|
|
8205
|
-
const [summary, setSummary] =
|
|
8206
|
-
const [showOnlyNeeds, setShowOnlyNeeds] =
|
|
8207
|
-
const [rateLimited, setRateLimited] =
|
|
8955
|
+
const [status, setStatus] = React28.useState("loading");
|
|
8956
|
+
const [targets, setTargets] = React28.useState([]);
|
|
8957
|
+
const [selected, setSelected] = React28.useState([]);
|
|
8958
|
+
const [summary, setSummary] = React28.useState(null);
|
|
8959
|
+
const [showOnlyNeeds, setShowOnlyNeeds] = React28.useState(false);
|
|
8960
|
+
const [rateLimited, setRateLimited] = React28.useState(false);
|
|
8208
8961
|
const spinner = useSpinnerFrame(status === "running");
|
|
8209
8962
|
useInput6((input) => {
|
|
8210
8963
|
if (status !== "select") return;
|
|
@@ -8212,7 +8965,7 @@ function UpdateScreen() {
|
|
|
8212
8965
|
setShowOnlyNeeds((prev) => !prev);
|
|
8213
8966
|
}
|
|
8214
8967
|
});
|
|
8215
|
-
|
|
8968
|
+
React28.useEffect(() => {
|
|
8216
8969
|
let cancelled = false;
|
|
8217
8970
|
const load = async () => {
|
|
8218
8971
|
const scopes = resolveScopes(invocation.options);
|
|
@@ -8254,7 +9007,7 @@ function UpdateScreen() {
|
|
|
8254
9007
|
cancelled = true;
|
|
8255
9008
|
};
|
|
8256
9009
|
}, [invocation, setFlash]);
|
|
8257
|
-
|
|
9010
|
+
React28.useEffect(() => {
|
|
8258
9011
|
let cancelled = false;
|
|
8259
9012
|
const run = async () => {
|
|
8260
9013
|
if (status !== "running" || selected.length === 0) return;
|
|
@@ -8269,16 +9022,16 @@ function UpdateScreen() {
|
|
|
8269
9022
|
};
|
|
8270
9023
|
}, [status, selected]);
|
|
8271
9024
|
if (status === "empty") {
|
|
8272
|
-
return /* @__PURE__ */
|
|
8273
|
-
/* @__PURE__ */
|
|
8274
|
-
/* @__PURE__ */
|
|
8275
|
-
/* @__PURE__ */
|
|
9025
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9026
|
+
/* @__PURE__ */ jsx34(Header, { title: "Update skills" }),
|
|
9027
|
+
/* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "No tracked skills to update yet." }),
|
|
9028
|
+
/* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "Re-install a skill once to enable updates." })
|
|
8276
9029
|
] });
|
|
8277
9030
|
}
|
|
8278
9031
|
if (status === "loading") {
|
|
8279
|
-
return /* @__PURE__ */
|
|
8280
|
-
/* @__PURE__ */
|
|
8281
|
-
/* @__PURE__ */
|
|
9032
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9033
|
+
/* @__PURE__ */ jsx34(Header, { title: "Update skills" }),
|
|
9034
|
+
/* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "Loading tracked skills..." })
|
|
8282
9035
|
] });
|
|
8283
9036
|
}
|
|
8284
9037
|
if (status === "select") {
|
|
@@ -8286,17 +9039,17 @@ function UpdateScreen() {
|
|
|
8286
9039
|
const defaults = selected;
|
|
8287
9040
|
const hint = showOnlyNeeds ? UPDATE_HINT_NEEDS_ONLY : UPDATE_HINT_ALL;
|
|
8288
9041
|
if (showOnlyNeeds && visibleTargets.length === 0) {
|
|
8289
|
-
return /* @__PURE__ */
|
|
8290
|
-
/* @__PURE__ */
|
|
8291
|
-
/* @__PURE__ */
|
|
8292
|
-
rateLimited ? /* @__PURE__ */
|
|
8293
|
-
/* @__PURE__ */
|
|
9042
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9043
|
+
/* @__PURE__ */ jsx34(Header, { title: "Select skills to update" }),
|
|
9044
|
+
/* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "No updates found." }),
|
|
9045
|
+
rateLimited ? /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "GitHub rate limit hit. Some skills may be marked unknown." }) : null,
|
|
9046
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: UPDATE_EMPTY_HINT }) })
|
|
8294
9047
|
] });
|
|
8295
9048
|
}
|
|
8296
|
-
return /* @__PURE__ */
|
|
8297
|
-
/* @__PURE__ */
|
|
8298
|
-
rateLimited ? /* @__PURE__ */
|
|
8299
|
-
/* @__PURE__ */
|
|
9049
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9050
|
+
/* @__PURE__ */ jsx34(Header, { title: "Select skills to update" }),
|
|
9051
|
+
rateLimited ? /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "GitHub rate limit hit. Some skills marked unknown." }) : null,
|
|
9052
|
+
/* @__PURE__ */ jsx34(
|
|
8300
9053
|
MultiSelect,
|
|
8301
9054
|
{
|
|
8302
9055
|
items: visibleTargets.map((target) => ({
|
|
@@ -8319,9 +9072,9 @@ function UpdateScreen() {
|
|
|
8319
9072
|
] });
|
|
8320
9073
|
}
|
|
8321
9074
|
if (status === "running") {
|
|
8322
|
-
return /* @__PURE__ */
|
|
8323
|
-
/* @__PURE__ */
|
|
8324
|
-
/* @__PURE__ */
|
|
9075
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9076
|
+
/* @__PURE__ */ jsx34(Header, { title: "Updating skills" }),
|
|
9077
|
+
/* @__PURE__ */ jsxs29(Text29, { children: [
|
|
8325
9078
|
spinner,
|
|
8326
9079
|
" Updating ",
|
|
8327
9080
|
selected.length,
|
|
@@ -8332,17 +9085,17 @@ function UpdateScreen() {
|
|
|
8332
9085
|
] });
|
|
8333
9086
|
}
|
|
8334
9087
|
if (!summary) {
|
|
8335
|
-
return /* @__PURE__ */
|
|
8336
|
-
/* @__PURE__ */
|
|
8337
|
-
/* @__PURE__ */
|
|
9088
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9089
|
+
/* @__PURE__ */ jsx34(Header, { title: "Update skills" }),
|
|
9090
|
+
/* @__PURE__ */ jsx34(Text29, { dimColor: true, children: "Nothing to update." })
|
|
8338
9091
|
] });
|
|
8339
9092
|
}
|
|
8340
|
-
return /* @__PURE__ */
|
|
8341
|
-
/* @__PURE__ */
|
|
8342
|
-
summary.updated.length > 0 ? /* @__PURE__ */
|
|
8343
|
-
summary.skipped.length > 0 ? /* @__PURE__ */
|
|
8344
|
-
summary.failed.length > 0 ? /* @__PURE__ */
|
|
8345
|
-
/* @__PURE__ */
|
|
9093
|
+
return /* @__PURE__ */ jsxs29(Box32, { flexDirection: "column", padding: 1, children: [
|
|
9094
|
+
/* @__PURE__ */ jsx34(Header, { title: "Update results" }),
|
|
9095
|
+
summary.updated.length > 0 ? /* @__PURE__ */ jsx34(Text29, { children: `Updated ${summary.updated.length} skill${summary.updated.length !== 1 ? "s" : ""}` }) : null,
|
|
9096
|
+
summary.skipped.length > 0 ? /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: `Skipped: ${summary.skipped.map(formatTargetLabel).join(", ")}` }) : null,
|
|
9097
|
+
summary.failed.length > 0 ? /* @__PURE__ */ jsx34(Text29, { color: "red", children: `Failed: ${summary.failed.map(formatTargetLabel).join(", ")}` }) : null,
|
|
9098
|
+
/* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { dimColor: true, children: BACK_QUIT_HINT }) })
|
|
8346
9099
|
] });
|
|
8347
9100
|
}
|
|
8348
9101
|
function resolveScopes(options) {
|
|
@@ -8371,61 +9124,63 @@ function sortTargets(a, b) {
|
|
|
8371
9124
|
}
|
|
8372
9125
|
|
|
8373
9126
|
// src/tui/ScreenRouter.tsx
|
|
8374
|
-
import { Fragment as Fragment4, jsx as
|
|
9127
|
+
import { Fragment as Fragment4, jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
8375
9128
|
function ScreenRouter() {
|
|
8376
9129
|
const { screen } = useNavigation();
|
|
8377
9130
|
const render2 = (s) => {
|
|
8378
9131
|
switch (s) {
|
|
8379
9132
|
case "main":
|
|
8380
|
-
return /* @__PURE__ */
|
|
9133
|
+
return /* @__PURE__ */ jsx35(MainMenu, {});
|
|
8381
9134
|
case "add-source":
|
|
8382
|
-
return /* @__PURE__ */
|
|
9135
|
+
return /* @__PURE__ */ jsx35(AddSourceScreen, {});
|
|
8383
9136
|
case "add-docs":
|
|
8384
|
-
return /* @__PURE__ */
|
|
9137
|
+
return /* @__PURE__ */ jsx35(AddDocsScreen, {});
|
|
8385
9138
|
case "add-marketplace-plugins":
|
|
8386
|
-
return /* @__PURE__ */
|
|
9139
|
+
return /* @__PURE__ */ jsx35(MarketplacePluginScreen, {});
|
|
8387
9140
|
case "add-marketplace-skills":
|
|
8388
|
-
return /* @__PURE__ */
|
|
9141
|
+
return /* @__PURE__ */ jsx35(MarketplaceSkillScreen, {});
|
|
8389
9142
|
case "find-skill-search":
|
|
8390
|
-
return /* @__PURE__ */
|
|
9143
|
+
return /* @__PURE__ */ jsx35(FindSkillSearchScreen, {});
|
|
8391
9144
|
case "find-skill-results":
|
|
8392
|
-
return /* @__PURE__ */
|
|
9145
|
+
return /* @__PURE__ */ jsx35(FindSkillResultsScreen, {});
|
|
8393
9146
|
case "scan-skills":
|
|
8394
|
-
return /* @__PURE__ */
|
|
9147
|
+
return /* @__PURE__ */ jsx35(ScanSkillsScreen, {});
|
|
8395
9148
|
case "get-url":
|
|
8396
|
-
return /* @__PURE__ */
|
|
9149
|
+
return /* @__PURE__ */ jsx35(GetUrlScreen, {});
|
|
8397
9150
|
case "add-skill-select":
|
|
8398
|
-
return /* @__PURE__ */
|
|
9151
|
+
return /* @__PURE__ */ jsx35(AddSkillSelectScreen, {});
|
|
9152
|
+
case "add-license-key":
|
|
9153
|
+
return /* @__PURE__ */ jsx35(AddLicenseKeyScreen, {});
|
|
8399
9154
|
case "add-security-scan":
|
|
8400
|
-
return /* @__PURE__ */
|
|
9155
|
+
return /* @__PURE__ */ jsx35(AddSecurityScanScreen, {});
|
|
8401
9156
|
case "add-targets":
|
|
8402
|
-
return /* @__PURE__ */
|
|
9157
|
+
return /* @__PURE__ */ jsx35(AddTargetsScreen, {});
|
|
8403
9158
|
case "add-scope":
|
|
8404
|
-
return /* @__PURE__ */
|
|
9159
|
+
return /* @__PURE__ */ jsx35(AddScopeScreen, {});
|
|
8405
9160
|
case "add-mode":
|
|
8406
|
-
return /* @__PURE__ */
|
|
9161
|
+
return /* @__PURE__ */ jsx35(AddModeScreen, {});
|
|
8407
9162
|
case "add-confirm":
|
|
8408
|
-
return /* @__PURE__ */
|
|
9163
|
+
return /* @__PURE__ */ jsx35(AddConfirmScreen, {});
|
|
8409
9164
|
case "add-install":
|
|
8410
|
-
return /* @__PURE__ */
|
|
9165
|
+
return /* @__PURE__ */ jsx35(AddInstallScreen, {});
|
|
8411
9166
|
case "add-result":
|
|
8412
|
-
return /* @__PURE__ */
|
|
9167
|
+
return /* @__PURE__ */ jsx35(AddResultScreen, {});
|
|
8413
9168
|
case "list":
|
|
8414
|
-
return /* @__PURE__ */
|
|
9169
|
+
return /* @__PURE__ */ jsx35(ListScreen, {});
|
|
8415
9170
|
case "manage":
|
|
8416
|
-
return /* @__PURE__ */
|
|
9171
|
+
return /* @__PURE__ */ jsx35(ManageScreen, {});
|
|
8417
9172
|
case "update":
|
|
8418
|
-
return /* @__PURE__ */
|
|
9173
|
+
return /* @__PURE__ */ jsx35(UpdateScreen, {});
|
|
8419
9174
|
case "update-docs":
|
|
8420
|
-
return /* @__PURE__ */
|
|
9175
|
+
return /* @__PURE__ */ jsx35(UpdateDocsScreen, {});
|
|
8421
9176
|
default:
|
|
8422
9177
|
return null;
|
|
8423
9178
|
}
|
|
8424
9179
|
};
|
|
8425
|
-
return /* @__PURE__ */
|
|
8426
|
-
/* @__PURE__ */
|
|
9180
|
+
return /* @__PURE__ */ jsxs30(Fragment4, { children: [
|
|
9181
|
+
/* @__PURE__ */ jsx35(BrandHeader, {}),
|
|
8427
9182
|
render2(screen),
|
|
8428
|
-
/* @__PURE__ */
|
|
9183
|
+
/* @__PURE__ */ jsx35(FlashBar, { align: "center" })
|
|
8429
9184
|
] });
|
|
8430
9185
|
}
|
|
8431
9186
|
|
|
@@ -8505,14 +9260,14 @@ function useKeyboardShortcuts() {
|
|
|
8505
9260
|
}
|
|
8506
9261
|
|
|
8507
9262
|
// src/tui/App.tsx
|
|
8508
|
-
import { jsx as
|
|
9263
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
8509
9264
|
function AppRoot() {
|
|
8510
9265
|
useKeyboardShortcuts();
|
|
8511
|
-
return /* @__PURE__ */
|
|
9266
|
+
return /* @__PURE__ */ jsx36(ScreenRouter, {});
|
|
8512
9267
|
}
|
|
8513
9268
|
function runApp(initialInvocation, initialScreen) {
|
|
8514
9269
|
const { waitUntilExit } = render(
|
|
8515
|
-
/* @__PURE__ */
|
|
9270
|
+
/* @__PURE__ */ jsx36(NavigationProvider, { initialInvocation, initialScreen, children: /* @__PURE__ */ jsx36(AppRoot, {}) })
|
|
8516
9271
|
);
|
|
8517
9272
|
return waitUntilExit();
|
|
8518
9273
|
}
|
|
@@ -8526,7 +9281,7 @@ program.addHelpCommand();
|
|
|
8526
9281
|
var applyAddSkillOptions = (cmd) => cmd.option("-g, --global", "Install globally (user-level) instead of project-level").option(
|
|
8527
9282
|
"-a, --agent <agents...>",
|
|
8528
9283
|
"Target agents to install to (claude-code, codex, cursor, opencode, and more)"
|
|
8529
|
-
).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)");
|
|
9284
|
+
).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)");
|
|
8530
9285
|
function normalizeOptions(options) {
|
|
8531
9286
|
const normalized = { ...options };
|
|
8532
9287
|
if (normalized.all) {
|