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.
Files changed (2) hide show
  1. package/dist/index.js +1226 -471
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { program } from "commander";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "playbooks",
11
- version: "0.1.15",
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.3";
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 scanSkillStatic(files, options = {}) {
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
- if (!scoreApplied.has(scoreKey)) {
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 = scanSkillStatic(collected.files, options);
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/AddMode.tsx
4370
- import { join as join12 } from "path";
4371
- import { Box as Box14 } from "ink";
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
- React11.useEffect(() => {
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__ */ jsxs12(Box14, { flexDirection: "column", padding: 1, children: [
4408
- /* @__PURE__ */ jsx16(AddFlowHeader, { title: "Install mode" }),
4409
- /* @__PURE__ */ jsx16(
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 Box15, Text as Text14 } from "ink";
4426
- import React12 from "react";
4427
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
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 = React12.useMemo(() => {
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__ */ jsxs13(Box15, { flexDirection: "column", padding: 1, children: [
4445
- /* @__PURE__ */ jsx17(AddFlowHeader, { title: "Install results" }),
4446
- summary ? /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", marginBottom: 1, children: [
4447
- /* @__PURE__ */ jsx17(Text14, { children: summary.title }),
4448
- summaryLines.map(({ line, key }) => /* @__PURE__ */ jsx17(Text14, { children: line }, key))
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__ */ jsxs13(Box15, { flexDirection: "column", marginBottom: 1, children: [
4451
- /* @__PURE__ */ jsx17(Text14, { color: "red", children: `Failed to install ${failed.length}` }),
4452
- failed.map((r) => /* @__PURE__ */ jsxs13(Text14, { children: [
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__ */ jsx17(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
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__ */ jsxs13(Box15, { marginBottom: 1, children: [
4467
- /* @__PURE__ */ jsx17(Text14, { color: "yellow", children: `Symlinks failed for: ${formatList(symlinkFailures.map((r) => r.agent))}` }),
4468
- /* @__PURE__ */ jsx17(Text14, { dimColor: true, children: "Files were copied instead." })
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__ */ jsx17(
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 Box16 } from "ink";
4494
- import React13 from "react";
4495
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
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
- React13.useEffect(() => {
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__ */ jsxs14(Box16, { flexDirection: "column", padding: 1, children: [
4523
- /* @__PURE__ */ jsx18(AddFlowHeader, { title: "Install scope" }),
4524
- /* @__PURE__ */ jsx18(
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 Box17, Text as Text15 } from "ink";
4547
- import React14 from "react";
4548
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
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] = React14.useState("scanning");
4553
- const [error, setError] = React14.useState(null);
4554
- const [manualView, setManualView] = React14.useState("menu");
4555
- const [confirmText, setConfirmText] = React14.useState("");
4556
- const [selectedRow, setSelectedRow] = React14.useState(null);
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
- React14.useEffect(() => {
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
- React14.useEffect(() => {
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__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
4627
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
4628
- /* @__PURE__ */ jsx19(Text15, { color: "red", children: error ?? "Scan failed" }),
4629
- /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text15, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
4635
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
4636
- /* @__PURE__ */ jsxs15(Text15, { children: [
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__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
4648
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
4649
- /* @__PURE__ */ jsx19(
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__ */ jsx19(Box17, { padding: 1 });
5104
+ return /* @__PURE__ */ jsx20(Box18, { padding: 1 });
4674
5105
  }
4675
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", padding: 1, children: [
4676
- /* @__PURE__ */ jsx19(AddFlowHeader, { title: "Security scan" }),
4677
- /* @__PURE__ */ jsx19(Text15, { dimColor: true, children: "Scan complete." }),
4678
- /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text15, { dimColor: true, children: BACK_QUIT_HINT }) })
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 Box18, Text as Text16 } from "ink";
4688
- import React15 from "react";
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
- for (const file of e.files) {
5014
- if (typeof file !== "string") return false;
5015
- if (file.startsWith("/") || file.startsWith("\\") || file.includes("..")) return false;
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
- async function prepareWellKnownSkills(sourceUrl) {
5226
- const discovered = await wellKnownProvider.fetchAllSkills(sourceUrl);
5227
- if (discovered.length === 0) {
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
- for (const skill of discovered) {
5239
- const skillDir = join13(tempDir, skill.installName);
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, content] of skill.files.entries()) {
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, content, "utf-8");
5840
+ await writeFile3(targetPath, fileContent, "utf-8");
5248
5841
  }
5249
5842
  const localSkill = {
5250
- name: skill.installName,
5251
- description: skill.description,
5843
+ name: fetched.installName,
5844
+ description: fetched.description,
5252
5845
  path: skillDir,
5253
- rawContent: skill.content,
5254
- metadata: skill.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: skill.sourceUrl,
5852
+ sourceUrl: fetched.sourceUrl,
5260
5853
  source: sourceIdentifier,
5261
- skillPath: skill.sourceUrl
5854
+ skillPath: fetched.sourceUrl
5262
5855
  });
5263
5856
  }
5264
- return { tempDir, skills, originBySkillName: originMap };
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 jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
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] = React15.useState(
6186
+ const [status, setStatus] = React16.useState(
5589
6187
  addSkill.skills && addSkill.skills.length > 0 ? "ready" : "loading"
5590
6188
  );
5591
- const [error, setError] = React15.useState(null);
5592
- const [listMode, setListMode] = React15.useState(false);
5593
- const [showLoading, setShowLoading] = React15.useState(false);
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
- React15.useEffect(() => {
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 prepared = await prepareWellKnownSkills(parsed.url);
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
- React15.useEffect(() => {
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
- React15.useEffect(() => {
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__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5806
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Add skills" }),
5807
- /* @__PURE__ */ jsx20(Text16, { children: `Missing source. ${BACK_QUIT_HINT}` })
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__ */ jsx20(Box18, { padding: 1 });
6426
+ return /* @__PURE__ */ jsx21(Box19, { padding: 1 });
5812
6427
  }
5813
6428
  if (status === "loading") {
5814
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5815
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Scanning skills" }),
5816
- /* @__PURE__ */ jsxs16(Text16, { children: [
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__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5825
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Unable to load skills" }),
5826
- /* @__PURE__ */ jsx20(Text16, { color: "red", children: error }),
5827
- /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5833
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: `Available skills (${skills.length})` }),
5834
- skills.map((skill) => /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", marginBottom: 1, children: [
5835
- /* @__PURE__ */ jsx20(Text16, { children: getSkillDisplayName(skill) }),
5836
- skill.description ? /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: skill.description }) : null
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__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT })
6453
+ /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: BACK_QUIT_HINT })
5839
6454
  ] });
5840
6455
  }
5841
6456
  if (skills.length === 0) {
5842
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5843
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "No skills found" }),
5844
- /* @__PURE__ */ jsx20(Text16, { dimColor: true, children: BACK_QUIT_HINT })
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__ */ jsxs16(Box18, { flexDirection: "column", padding: 1, children: [
5848
- /* @__PURE__ */ jsx20(AddFlowHeader, { title: "Select skills" }),
5849
- /* @__PURE__ */ jsx20(
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 Box19, Text as Text17 } from "ink";
5882
- import TextInput2 from "ink-text-input";
5883
- import React16 from "react";
5884
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
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] = React16.useState(
6502
+ const [value, setValue] = React17.useState(
5888
6503
  addSkill.source ?? invocation.source ?? lastSource ?? ""
5889
6504
  );
5890
- const didAutofillRef = React16.useRef(false);
6505
+ const didAutofillRef = React17.useRef(false);
5891
6506
  const { wrapOnChange } = useTextInput({
5892
6507
  onClear: () => {
5893
6508
  setValue("");
5894
6509
  }
5895
6510
  });
5896
- React16.useEffect(() => {
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__ */ jsxs17(Box19, { flexDirection: "column", padding: 1, children: [
5914
- /* @__PURE__ */ jsx21(Header, { title: "Add skills" }),
5915
- /* @__PURE__ */ jsx21(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text17, { children: "Where should we fetch skills from?" }) }),
5916
- /* @__PURE__ */ jsx21(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: "Examples: owner/repo, https://.../SKILL.md, ./local/path" }) }),
5917
- /* @__PURE__ */ jsxs17(Box19, { children: [
5918
- /* @__PURE__ */ jsx21(Text17, { color: "green", children: "> " }),
5919
- /* @__PURE__ */ jsx21(TextInput2, { value, onChange: wrapOnChange(setValue), onSubmit })
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__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text17, { dimColor: true, children: TEXT_INPUT_HINT }) })
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 Box20, Text as Text18 } from "ink";
5927
- import React17 from "react";
5928
- import { Fragment as Fragment2, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
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] = React17.useState("loading");
5932
- const [mode, setMode] = React17.useState("choice");
5933
- const [availableAgents, setAvailableAgents] = React17.useState([]);
5934
- const [lastSelected, setLastSelected] = React17.useState([]);
5935
- const [showLoading, setShowLoading] = React17.useState(false);
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
- React17.useEffect(() => {
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
- React17.useEffect(() => {
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__ */ jsx22(Box20, { padding: 1 });
6612
+ return /* @__PURE__ */ jsx23(Box21, { padding: 1 });
5998
6613
  }
5999
6614
  if (status === "loading") {
6000
- return /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", padding: 1, children: [
6001
- /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Detecting agents" }),
6002
- /* @__PURE__ */ jsxs18(Text18, { children: [
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__ */ jsx22(Box20, { flexDirection: "column", padding: 1, children: mode === "choice" ? /* @__PURE__ */ jsxs18(Fragment2, { children: [
6025
- /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Install to" }),
6026
- /* @__PURE__ */ jsx22(
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__ */ jsxs18(Fragment2, { children: [
6060
- /* @__PURE__ */ jsx22(AddFlowHeader, { title: "Select agents" }),
6061
- /* @__PURE__ */ jsx22(
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 Box21, Text as Text19 } from "ink";
6094
- import React18 from "react";
6095
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
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] = React18.useState("ready");
6125
- const [error, setError] = React18.useState(null);
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
- React18.useEffect(() => {
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__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6170
- /* @__PURE__ */ jsx23(Header, { title: "Preparing skills" }),
6171
- /* @__PURE__ */ jsxs19(Text19, { children: [
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__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6179
- /* @__PURE__ */ jsx23(Header, { title: "No results" }),
6180
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "No skills found." }),
6181
- /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs19(Box21, { flexDirection: "column", padding: 1, children: [
6185
- /* @__PURE__ */ jsx23(Header, { title: "Select skills" }),
6186
- /* @__PURE__ */ jsxs19(Box21, { marginBottom: 1, flexDirection: "column", children: [
6187
- /* @__PURE__ */ jsx23(Text19, { children: `Query: ${query}` }),
6188
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: `Mode: ${modeLabel} search` }),
6189
- /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: FIND_SKILLS_HINT })
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__ */ jsx23(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx23(Text19, { color: "red", children: error }) }) : null,
6192
- /* @__PURE__ */ jsx23(
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 Box22, Text as Text20, useInput as useInput5 } from "ink";
6211
- import TextInput3 from "ink-text-input";
6212
- import React19 from "react";
6213
- import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
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] = React19.useState(findSkill.query ?? "");
6221
- const [status, setStatus] = React19.useState("idle");
6222
- const [error, setError] = React19.useState(null);
6223
- const [preview, setPreview] = React19.useState([]);
6224
- const debounceRef = React19.useRef(null);
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 = React19.useCallback((query) => {
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
- React19.useEffect(() => {
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 = React19.useCallback(async () => {
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 = React19.useCallback(async () => {
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__ */ jsxs20(Box22, { flexDirection: "column", padding: 1, children: [
6339
- /* @__PURE__ */ jsx24(Header, { title: "Find skills" }),
6340
- /* @__PURE__ */ jsx24(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsx24(Text20, { children: "Find a skill to give your agent new capabilities." }) }),
6341
- /* @__PURE__ */ jsxs20(Box22, { children: [
6342
- /* @__PURE__ */ jsx24(Text20, { color: "green", children: "> " }),
6343
- /* @__PURE__ */ jsx24(
6344
- TextInput3,
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__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
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__ */ jsxs20(Box22, { flexDirection: "column", marginTop: 1, children: [
6366
- /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
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__ */ jsxs20(Text20, { dimColor: true, children: [
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__ */ jsxs20(Text20, { dimColor: true, children: [
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__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { children: [
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__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text20, { color: "red", children: error }) }) : null,
6390
- /* @__PURE__ */ jsx24(Box22, { marginTop: 1, children: /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: "Enter for fast results, Tab for AI search" }) }),
6391
- /* @__PURE__ */ jsx24(Box22, { children: /* @__PURE__ */ jsx24(Text20, { dimColor: true, children: TEXT_INPUT_HINT }) })
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 Box23, Text as Text21, useApp } from "ink";
6397
- import React20 from "react";
6398
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
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 = React20.useRef(false);
7018
+ const didRun = React21.useRef(false);
6404
7019
  const outputPath = invocation.options?.output ?? null;
6405
7020
  const outputFormat = invocation.options?.json ? "json" : "markdown";
6406
- React20.useEffect(() => {
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__ */ jsxs21(Box23, { flexDirection: "column", padding: 1, children: [
6436
- /* @__PURE__ */ jsx25(Header, { title: "Fetching URL" }),
6437
- invocation.source ? /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: `URL: ${invocation.source}` }) : null,
6438
- outputPath ? /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: `Output: ${outputPath}` }) : null,
6439
- outputFormat === "json" ? /* @__PURE__ */ jsx25(Text21, { dimColor: true, children: "Format: json" }) : null,
6440
- /* @__PURE__ */ jsxs21(Text21, { children: [
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 Box24, Text as Text22 } from "ink";
6449
- import React21 from "react";
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 jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
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] = React21.useState([]);
6568
- const [selectedAgent, setSelectedAgent] = React21.useState(null);
6569
- React21.useEffect(() => {
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
- React21.useEffect(() => {
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__ */ jsxs22(Box24, { flexDirection: "column", padding: 1, children: [
6608
- /* @__PURE__ */ jsx26(Header, { title: `${agents[selectedAgent.agent].displayName} (${total})` }),
6609
- selectedAgent.projectSkills.length > 0 ? /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", marginBottom: 1, children: [
6610
- /* @__PURE__ */ jsx26(Text22, { children: "Project" }),
6611
- selectedAgent.projectSkills.map((name) => /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: name }, `p-${name}`))
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__ */ jsxs22(Box24, { flexDirection: "column", marginBottom: 1, children: [
6614
- /* @__PURE__ */ jsx26(Text22, { children: "Global" }),
6615
- selectedAgent.globalSkills.map((name) => /* @__PURE__ */ jsx26(Text22, { dimColor: true, children: name }, `g-${name}`))
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__ */ jsx26(
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__ */ jsxs22(Box24, { flexDirection: "column", padding: 1, children: [
6645
- /* @__PURE__ */ jsx26(Header, { title: "Installed skills" }),
6646
- /* @__PURE__ */ jsx26(
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 Box25 } from "ink";
6664
- import { jsx as jsx27 } from "react/jsx-runtime";
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__ */ jsx27(Box25, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx27(
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 Box26, Text as Text23 } from "ink";
6734
- import React22 from "react";
6735
- import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
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] = React22.useState([]);
6739
- const [selectedAgent, setSelectedAgent] = React22.useState(null);
6740
- const [selectedSkills, setSelectedSkills] = React22.useState(null);
6741
- const [isRemoving, setIsRemoving] = React22.useState(false);
6742
- React22.useEffect(() => {
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
- React22.useEffect(() => {
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__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
6788
- /* @__PURE__ */ jsx28(Header, { title: "Remove skills" }),
6789
- /* @__PURE__ */ jsx28(Text23, { dimColor: true, children: "No skills found yet." }),
6790
- /* @__PURE__ */ jsx28(Text23, { dimColor: true, children: BACK_QUIT_HINT })
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__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
6795
- /* @__PURE__ */ jsx28(Header, { title: "Select agent" }),
6796
- /* @__PURE__ */ jsx28(
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__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
6814
- /* @__PURE__ */ jsx28(Header, { title: `Remove skills (${agents[selectedAgent.agent].displayName})` }),
6815
- /* @__PURE__ */ jsx28(
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__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
6836
- /* @__PURE__ */ jsx28(Header, { title: "Removing skills" }),
6837
- /* @__PURE__ */ jsx28(Text23, { children: "Removing selected skills..." })
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__ */ jsxs23(Box26, { flexDirection: "column", padding: 1, children: [
6841
- /* @__PURE__ */ jsx28(Header, { title: "Confirm removal" }),
6842
- selectedSkills.map((skill) => /* @__PURE__ */ jsxs23(Text23, { children: [
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__ */ jsx28(
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 Box27, Text as Text24 } from "ink";
6911
- import React23 from "react";
6912
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
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
- React23.useEffect(() => {
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__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
6933
- /* @__PURE__ */ jsx29(AddFlowHeader, { title: "Marketplace plugins" }),
6934
- /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: "No plugins found." }),
6935
- /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: BACK_QUIT_HINT })
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__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
6940
- /* @__PURE__ */ jsx29(AddFlowHeader, { title: `Marketplace plugins (${plugins.length})` }),
6941
- plugins.map((plugin) => /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", marginBottom: 1, children: [
6942
- /* @__PURE__ */ jsx29(Text24, { children: plugin.name }),
6943
- plugin.description ? /* @__PURE__ */ jsx29(Text24, { dimColor: true, children: plugin.description }) : null
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__ */ jsx29(Text24, { dimColor: true, children: BACK_QUIT_HINT })
7560
+ /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: BACK_QUIT_HINT })
6946
7561
  ] });
6947
7562
  }
6948
- return /* @__PURE__ */ jsxs24(Box27, { flexDirection: "column", padding: 1, children: [
6949
- /* @__PURE__ */ jsx29(AddFlowHeader, { title: "Select plugins" }),
6950
- /* @__PURE__ */ jsx29(
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 Box28, Text as Text25 } from "ink";
6978
- import React24 from "react";
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 dirname6, join as join17, relative as relative4 } from "path";
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, dirname6(cleaned));
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 jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
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] = React24.useState("loading");
7143
- const [error, setError] = React24.useState(null);
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
- React24.useEffect(() => {
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__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7212
- /* @__PURE__ */ jsx30(AddFlowHeader, { title: "Scanning marketplace" }),
7213
- /* @__PURE__ */ jsxs25(Text25, { children: [
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__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7221
- /* @__PURE__ */ jsx30(AddFlowHeader, { title: "Marketplace scan failed" }),
7222
- /* @__PURE__ */ jsx30(Text25, { color: "red", children: error }),
7223
- /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: BACK_QUIT_HINT })
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__ */ jsxs25(Box28, { flexDirection: "column", padding: 1, children: [
7229
- /* @__PURE__ */ jsx30(AddFlowHeader, { title: "Select skills" }),
7230
- warnings.length > 0 ? /* @__PURE__ */ jsx30(Box28, { flexDirection: "column", marginBottom: 1, children: warnings.map((warning) => /* @__PURE__ */ jsx30(Text25, { dimColor: true, children: warning }, warning)) }) : null,
7231
- /* @__PURE__ */ jsx30(
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 Box29, Text as Text26 } from "ink";
7282
- import React25 from "react";
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 jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
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] = React25.useState("loading");
7509
- const [error, setError] = React25.useState(null);
7510
- const [rows, setRows] = React25.useState([]);
7511
- const [progress, setProgress] = React25.useState(null);
7512
- const [selected, setSelected] = React25.useState(null);
7513
- const [removeTargets, setRemoveTargets] = React25.useState(null);
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
- React25.useEffect(() => {
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
- React25.useEffect(() => {
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 = React25.useMemo(() => rows.filter(isRisky), [rows]);
7606
- const summary = React25.useMemo(() => scanSummary(rows), [rows]);
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7609
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7610
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7615
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7616
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "Discovering skills..." })
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7623
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7624
- /* @__PURE__ */ jsxs26(Text26, { children: [
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__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7639
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7640
- /* @__PURE__ */ jsx31(Text26, { color: "red", children: error ?? "Scan failed" }),
7641
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7653
- /* @__PURE__ */ jsx31(Header, { title: "Skill risk details" }),
7654
- /* @__PURE__ */ jsxs26(Text26, { children: [
7655
- /* @__PURE__ */ jsx31(Text26, { bold: true, children: selected.skill.name }),
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__ */ jsx31(Text26, { dimColor: true, children: `(${selected.skill.slug})` })
8272
+ /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: `(${selected.skill.slug})` })
7658
8273
  ] }),
7659
- selected.error ? /* @__PURE__ */ jsx31(Text26, { color: "red", children: selected.error }) : /* @__PURE__ */ jsxs26(Fragment3, { children: [
7660
- /* @__PURE__ */ jsxs26(Text26, { children: [
7661
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "Verdict:" }),
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__ */ jsx31(Text26, { color: riskColor, children: verdict }),
7664
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: ` \u2022 level=${level} score=${score} issues=${issues}` })
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__ */ jsx31(Text26, { dimColor: true, children: `Top signals: ${top.join(", ")}` }) : null,
7667
- selected.ruleset ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Ruleset: ${selected.ruleset}` }) : null
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__ */ jsx31(Text26, { dimColor: true, children: selected.skill.description }) : null,
7670
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Path: ${selected.skill.path}` }),
7671
- locationLabels.length > 0 ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Locations: ${locationLabels.join(", ")}` }) : null,
7672
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7699
- /* @__PURE__ */ jsx31(Header, { title }),
7700
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "These locations will be removed:" }),
7701
- /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginTop: 1, children: removeTargets.length === 0 ? /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: "None found." }) : removeTargets.map((t) => /* @__PURE__ */ jsxs26(Text26, { children: [
7702
- /* @__PURE__ */ jsx31(Text26, { children: chalk5.red("\u2022") }),
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__ */ jsx31(Text26, { dimColor: true, children: t.label }),
8319
+ /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: t.label }),
7705
8320
  " ",
7706
8321
  t.path
7707
8322
  ] }, t.path)) }),
7708
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7757
- /* @__PURE__ */ jsx31(Header, { title: "Removing skill" }),
7758
- /* @__PURE__ */ jsxs26(Text26, { children: [
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__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7767
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7768
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: rows.length === 0 ? "No skills found to scan (project + global)." : `Scanned ${rows.length} skill${rows.length === 1 ? "" : "s"}. No risks found.` })
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__ */ jsxs26(Box29, { flexDirection: "column", padding: 1, children: [
7772
- /* @__PURE__ */ jsx31(Header, { title: "Scan skills" }),
7773
- /* @__PURE__ */ jsxs26(Box29, { marginBottom: 1, flexDirection: "column", children: [
7774
- /* @__PURE__ */ jsx31(Text26, { dimColor: true, children: `Scanned ${plural(summary.total, "skill")}.` }),
7775
- /* @__PURE__ */ jsxs26(Text26, { children: [
7776
- /* @__PURE__ */ jsx31(Text26, { color: "red", children: plural(summary.high, "high risk") }),
7777
- /* @__PURE__ */ jsx31(Text26, { children: ", " }),
7778
- /* @__PURE__ */ jsx31(Text26, { color: "yellow", children: plural(summary.medium, "medium risk") }),
7779
- /* @__PURE__ */ jsx31(Text26, { children: " detected." })
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__ */ jsx31(
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 Box30, Text as Text27 } from "ink";
7804
- import React26 from "react";
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 jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
8485
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
7871
8486
  function UpdateDocsScreen() {
7872
- const [status, setStatus] = React26.useState("running");
7873
- const [summary, setSummary] = React26.useState(null);
8487
+ const [status, setStatus] = React27.useState("running");
8488
+ const [summary, setSummary] = React27.useState(null);
7874
8489
  const spinner = useSpinnerFrame(status === "running");
7875
- React26.useEffect(() => {
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__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
7894
- /* @__PURE__ */ jsx32(Header, { title: "Updating docs" }),
7895
- /* @__PURE__ */ jsxs27(Text27, { children: [
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__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
7903
- /* @__PURE__ */ jsx32(Header, { title: "Update docs" }),
7904
- /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "No docs installed yet." }),
7905
- /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
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__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
7910
- /* @__PURE__ */ jsx32(Header, { title: "Update docs" }),
7911
- /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "Nothing to update." })
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__ */ jsxs27(Box30, { flexDirection: "column", padding: 1, children: [
7916
- /* @__PURE__ */ jsx32(Header, { title: "Docs update results" }),
7917
- summary.updated.length > 0 ? /* @__PURE__ */ jsxs27(Box30, { flexDirection: "column", marginBottom: 1, children: [
7918
- /* @__PURE__ */ jsx32(Text27, { children: `Updated ${summary.updated.length} repo${summary.updated.length !== 1 ? "s" : ""}` }),
7919
- summary.updated.map((item) => /* @__PURE__ */ jsxs27(Text27, { dimColor: true, children: [
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__ */ jsxs27(Box30, { flexDirection: "column", marginBottom: 1, children: [
7926
- /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: "Skipped" }),
7927
- summary.skipped.map((item) => /* @__PURE__ */ jsxs27(Text27, { dimColor: true, children: [
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__ */ jsxs27(Box30, { flexDirection: "column", marginBottom: 1, children: [
7933
- /* @__PURE__ */ jsx32(Text27, { color: "red", children: "Failed" }),
7934
- summary.failed.map((item) => /* @__PURE__ */ jsxs27(Text27, { color: "red", children: [
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__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text27, { dimColor: true, children: BACK_QUIT_HINT }) })
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 Box31, Text as Text28, useInput as useInput6 } from "ink";
7946
- import React27 from "react";
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 { mkdir as mkdir7, mkdtemp as mkdtemp5, rm as rm7, stat as stat7, writeFile as writeFile5 } from "fs/promises";
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 dirname7, join as join21 } from "path";
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 = join21(tempDir, dirname7(normalizedPath));
8047
- if (await pathExists3(join21(skillDir, "SKILL.md"))) {
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 mkdtemp5(join21(tmpdir6(), "playbooks-skill-"));
8837
+ const tempDir = await mkdtemp6(join22(tmpdir7(), "playbooks-skill-"));
8089
8838
  registerTempDir(tempDir);
8090
8839
  try {
8091
- await mkdir7(tempDir, { recursive: true });
8840
+ await mkdir8(tempDir, { recursive: true });
8092
8841
  if (files) {
8093
8842
  for (const [filePath, fileContent] of files.entries()) {
8094
- const targetPath = join21(tempDir, filePath);
8843
+ const targetPath = join22(tempDir, filePath);
8095
8844
  if (!isPathSafe(tempDir, targetPath)) {
8096
8845
  continue;
8097
8846
  }
8098
- await mkdir7(dirname7(targetPath), { recursive: true });
8099
- await writeFile5(targetPath, fileContent, "utf-8");
8847
+ await mkdir8(dirname9(targetPath), { recursive: true });
8848
+ await writeFile6(targetPath, fileContent, "utf-8");
8100
8849
  }
8101
8850
  } else if (content) {
8102
- await writeFile5(join21(tempDir, "SKILL.md"), content, "utf-8");
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 jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
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] = React27.useState("loading");
8203
- const [targets, setTargets] = React27.useState([]);
8204
- const [selected, setSelected] = React27.useState([]);
8205
- const [summary, setSummary] = React27.useState(null);
8206
- const [showOnlyNeeds, setShowOnlyNeeds] = React27.useState(false);
8207
- const [rateLimited, setRateLimited] = React27.useState(false);
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
- React27.useEffect(() => {
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
- React27.useEffect(() => {
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8273
- /* @__PURE__ */ jsx33(Header, { title: "Update skills" }),
8274
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "No tracked skills to update yet." }),
8275
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Re-install a skill once to enable updates." })
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8280
- /* @__PURE__ */ jsx33(Header, { title: "Update skills" }),
8281
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Loading tracked skills..." })
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8290
- /* @__PURE__ */ jsx33(Header, { title: "Select skills to update" }),
8291
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "No updates found." }),
8292
- rateLimited ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "GitHub rate limit hit. Some skills may be marked unknown." }) : null,
8293
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: UPDATE_EMPTY_HINT }) })
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8297
- /* @__PURE__ */ jsx33(Header, { title: "Select skills to update" }),
8298
- rateLimited ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "GitHub rate limit hit. Some skills marked unknown." }) : null,
8299
- /* @__PURE__ */ jsx33(
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8323
- /* @__PURE__ */ jsx33(Header, { title: "Updating skills" }),
8324
- /* @__PURE__ */ jsxs28(Text28, { children: [
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8336
- /* @__PURE__ */ jsx33(Header, { title: "Update skills" }),
8337
- /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: "Nothing to update." })
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__ */ jsxs28(Box31, { flexDirection: "column", padding: 1, children: [
8341
- /* @__PURE__ */ jsx33(Header, { title: "Update results" }),
8342
- summary.updated.length > 0 ? /* @__PURE__ */ jsx33(Text28, { children: `Updated ${summary.updated.length} skill${summary.updated.length !== 1 ? "s" : ""}` }) : null,
8343
- summary.skipped.length > 0 ? /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: `Skipped: ${summary.skipped.map(formatTargetLabel).join(", ")}` }) : null,
8344
- summary.failed.length > 0 ? /* @__PURE__ */ jsx33(Text28, { color: "red", children: `Failed: ${summary.failed.map(formatTargetLabel).join(", ")}` }) : null,
8345
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(Text28, { dimColor: true, children: BACK_QUIT_HINT }) })
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 jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
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__ */ jsx34(MainMenu, {});
9133
+ return /* @__PURE__ */ jsx35(MainMenu, {});
8381
9134
  case "add-source":
8382
- return /* @__PURE__ */ jsx34(AddSourceScreen, {});
9135
+ return /* @__PURE__ */ jsx35(AddSourceScreen, {});
8383
9136
  case "add-docs":
8384
- return /* @__PURE__ */ jsx34(AddDocsScreen, {});
9137
+ return /* @__PURE__ */ jsx35(AddDocsScreen, {});
8385
9138
  case "add-marketplace-plugins":
8386
- return /* @__PURE__ */ jsx34(MarketplacePluginScreen, {});
9139
+ return /* @__PURE__ */ jsx35(MarketplacePluginScreen, {});
8387
9140
  case "add-marketplace-skills":
8388
- return /* @__PURE__ */ jsx34(MarketplaceSkillScreen, {});
9141
+ return /* @__PURE__ */ jsx35(MarketplaceSkillScreen, {});
8389
9142
  case "find-skill-search":
8390
- return /* @__PURE__ */ jsx34(FindSkillSearchScreen, {});
9143
+ return /* @__PURE__ */ jsx35(FindSkillSearchScreen, {});
8391
9144
  case "find-skill-results":
8392
- return /* @__PURE__ */ jsx34(FindSkillResultsScreen, {});
9145
+ return /* @__PURE__ */ jsx35(FindSkillResultsScreen, {});
8393
9146
  case "scan-skills":
8394
- return /* @__PURE__ */ jsx34(ScanSkillsScreen, {});
9147
+ return /* @__PURE__ */ jsx35(ScanSkillsScreen, {});
8395
9148
  case "get-url":
8396
- return /* @__PURE__ */ jsx34(GetUrlScreen, {});
9149
+ return /* @__PURE__ */ jsx35(GetUrlScreen, {});
8397
9150
  case "add-skill-select":
8398
- return /* @__PURE__ */ jsx34(AddSkillSelectScreen, {});
9151
+ return /* @__PURE__ */ jsx35(AddSkillSelectScreen, {});
9152
+ case "add-license-key":
9153
+ return /* @__PURE__ */ jsx35(AddLicenseKeyScreen, {});
8399
9154
  case "add-security-scan":
8400
- return /* @__PURE__ */ jsx34(AddSecurityScanScreen, {});
9155
+ return /* @__PURE__ */ jsx35(AddSecurityScanScreen, {});
8401
9156
  case "add-targets":
8402
- return /* @__PURE__ */ jsx34(AddTargetsScreen, {});
9157
+ return /* @__PURE__ */ jsx35(AddTargetsScreen, {});
8403
9158
  case "add-scope":
8404
- return /* @__PURE__ */ jsx34(AddScopeScreen, {});
9159
+ return /* @__PURE__ */ jsx35(AddScopeScreen, {});
8405
9160
  case "add-mode":
8406
- return /* @__PURE__ */ jsx34(AddModeScreen, {});
9161
+ return /* @__PURE__ */ jsx35(AddModeScreen, {});
8407
9162
  case "add-confirm":
8408
- return /* @__PURE__ */ jsx34(AddConfirmScreen, {});
9163
+ return /* @__PURE__ */ jsx35(AddConfirmScreen, {});
8409
9164
  case "add-install":
8410
- return /* @__PURE__ */ jsx34(AddInstallScreen, {});
9165
+ return /* @__PURE__ */ jsx35(AddInstallScreen, {});
8411
9166
  case "add-result":
8412
- return /* @__PURE__ */ jsx34(AddResultScreen, {});
9167
+ return /* @__PURE__ */ jsx35(AddResultScreen, {});
8413
9168
  case "list":
8414
- return /* @__PURE__ */ jsx34(ListScreen, {});
9169
+ return /* @__PURE__ */ jsx35(ListScreen, {});
8415
9170
  case "manage":
8416
- return /* @__PURE__ */ jsx34(ManageScreen, {});
9171
+ return /* @__PURE__ */ jsx35(ManageScreen, {});
8417
9172
  case "update":
8418
- return /* @__PURE__ */ jsx34(UpdateScreen, {});
9173
+ return /* @__PURE__ */ jsx35(UpdateScreen, {});
8419
9174
  case "update-docs":
8420
- return /* @__PURE__ */ jsx34(UpdateDocsScreen, {});
9175
+ return /* @__PURE__ */ jsx35(UpdateDocsScreen, {});
8421
9176
  default:
8422
9177
  return null;
8423
9178
  }
8424
9179
  };
8425
- return /* @__PURE__ */ jsxs29(Fragment4, { children: [
8426
- /* @__PURE__ */ jsx34(BrandHeader, {}),
9180
+ return /* @__PURE__ */ jsxs30(Fragment4, { children: [
9181
+ /* @__PURE__ */ jsx35(BrandHeader, {}),
8427
9182
  render2(screen),
8428
- /* @__PURE__ */ jsx34(FlashBar, { align: "center" })
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 jsx35 } from "react/jsx-runtime";
9263
+ import { jsx as jsx36 } from "react/jsx-runtime";
8509
9264
  function AppRoot() {
8510
9265
  useKeyboardShortcuts();
8511
- return /* @__PURE__ */ jsx35(ScreenRouter, {});
9266
+ return /* @__PURE__ */ jsx36(ScreenRouter, {});
8512
9267
  }
8513
9268
  function runApp(initialInvocation, initialScreen) {
8514
9269
  const { waitUntilExit } = render(
8515
- /* @__PURE__ */ jsx35(NavigationProvider, { initialInvocation, initialScreen, children: /* @__PURE__ */ jsx35(AppRoot, {}) })
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) {