skillwiki 0.10.100 → 0.10.103

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.
@@ -40,7 +40,7 @@ import {
40
40
  snapshotterAliasForLocalHost,
41
41
  toUndirectedWeighted,
42
42
  writeDotenv
43
- } from "./chunk-B4QTKCEK.js";
43
+ } from "./chunk-PVIOU7BN.js";
44
44
  import {
45
45
  atomicWriteText,
46
46
  prepareTypedPage
@@ -1018,6 +1018,10 @@ import { join as join5 } from "path";
1018
1018
  function validateKey(key) {
1019
1019
  return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key) || isVaultSyncKey(key);
1020
1020
  }
1021
+ function displayConfigValue(key, value) {
1022
+ if (key === "SKILLWIKI_MCP_TOKEN") return value.length > 0 ? "TOKEN_SET" : "";
1023
+ return value;
1024
+ }
1021
1025
  function configPath(home) {
1022
1026
  return join5(home, ".skillwiki", ".env");
1023
1027
  }
@@ -1056,7 +1060,10 @@ async function runConfigSet(input) {
1056
1060
  }
1057
1061
  async function runConfigList(input) {
1058
1062
  const map = await parseDotenvFile(configPath(input.home));
1059
- const entries = Object.entries(map).map(([key, value]) => ({ key, value: value ?? "" }));
1063
+ const entries = Object.entries(map).map(([key, value]) => ({
1064
+ key,
1065
+ value: displayConfigValue(key, value ?? "")
1066
+ }));
1060
1067
  let profiles;
1061
1068
  if (input.profiles) {
1062
1069
  const defaultProfile = map["WIKI_DEFAULT"];
@@ -1078,23 +1085,66 @@ async function runConfigPath(input) {
1078
1085
  return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync2(filePath), humanHint: filePath }) };
1079
1086
  }
1080
1087
 
1088
+ // src/utils/mcp-auth-env.ts
1089
+ import { join as join6 } from "path";
1090
+ var MCP_URL_ENV = "SKILLWIKI_MCP_URL";
1091
+ var MCP_TOKEN_ENV = "SKILLWIKI_MCP_TOKEN";
1092
+ var MCP_HOST_ID_ENV = "SKILLWIKI_HOST_ID";
1093
+ function nonEmptyEnvValue(value) {
1094
+ if (typeof value !== "string") return void 0;
1095
+ const trimmed = value.trim();
1096
+ return trimmed.length > 0 ? trimmed : void 0;
1097
+ }
1098
+ function mcpAuthFromEnv(env) {
1099
+ return {
1100
+ url: nonEmptyEnvValue(env[MCP_URL_ENV]),
1101
+ token: nonEmptyEnvValue(env[MCP_TOKEN_ENV]),
1102
+ hostId: nonEmptyEnvValue(env[MCP_HOST_ID_ENV])
1103
+ };
1104
+ }
1105
+ async function resolveMcpAuthEnv(input) {
1106
+ const processAuth = mcpAuthFromEnv(input.env ?? process.env);
1107
+ const dotenv = await parseDotenvFile(join6(input.home, ".skillwiki", ".env"));
1108
+ const fileAuth = mcpAuthFromEnv(dotenv);
1109
+ return {
1110
+ url: processAuth.url ?? fileAuth.url,
1111
+ token: processAuth.token ?? fileAuth.token,
1112
+ hostId: processAuth.hostId ?? fileAuth.hostId
1113
+ };
1114
+ }
1115
+ async function mergeMcpAuthIntoEnv(home, env) {
1116
+ const auth = await resolveMcpAuthEnv({ home, env });
1117
+ const merged = { ...env };
1118
+ if (auth.url && !nonEmptyEnvValue(env[MCP_URL_ENV])) merged[MCP_URL_ENV] = auth.url;
1119
+ if (auth.token && !nonEmptyEnvValue(env[MCP_TOKEN_ENV])) merged[MCP_TOKEN_ENV] = auth.token;
1120
+ if (auth.hostId && !nonEmptyEnvValue(env[MCP_HOST_ID_ENV])) merged[MCP_HOST_ID_ENV] = auth.hostId;
1121
+ return merged;
1122
+ }
1123
+ function redactMcpSecret(text, secret) {
1124
+ if (!secret || secret.length === 0 || !text.includes(secret)) return text;
1125
+ return text.split(secret).join("[REDACTED]");
1126
+ }
1127
+ function isMcpOnlyLeaf(input) {
1128
+ return !input.resolvedPath && Boolean(input.token) && !input.vaultSyncInstalled;
1129
+ }
1130
+
1081
1131
  // src/doctor/runner.ts
1082
1132
  import { existsSync as existsSync18 } from "fs";
1083
1133
 
1084
1134
  // src/doctor/probes/environment.ts
1085
1135
  import { existsSync as existsSync4, lstatSync, readlinkSync } from "fs";
1086
- import { resolve as resolve2, join as join7 } from "path";
1136
+ import { resolve as resolve2, join as join8 } from "path";
1087
1137
  import { execSync } from "child_process";
1088
1138
 
1089
1139
  // src/utils/plugin-registry.ts
1090
1140
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2 } from "fs";
1091
- import { join as join6 } from "path";
1092
- var REGISTRY_PATH = join6(".claude", "plugins", "installed_plugins.json");
1093
- var CODEX_CONFIG_PATH = join6(".codex", "config.toml");
1141
+ import { join as join7 } from "path";
1142
+ var REGISTRY_PATH = join7(".claude", "plugins", "installed_plugins.json");
1143
+ var CODEX_CONFIG_PATH = join7(".codex", "config.toml");
1094
1144
  var PLUGIN_KEY = "skillwiki@llm-wiki";
1095
1145
  function readInstalledPlugins(home) {
1096
1146
  try {
1097
- const raw = readFileSync2(join6(home, REGISTRY_PATH), "utf8");
1147
+ const raw = readFileSync2(join7(home, REGISTRY_PATH), "utf8");
1098
1148
  return JSON.parse(raw);
1099
1149
  } catch {
1100
1150
  return null;
@@ -1130,7 +1180,7 @@ function findPluginInstallations(home, key = PLUGIN_KEY) {
1130
1180
  return installs;
1131
1181
  }
1132
1182
  function findGrokPlugin(home, key, pluginName, marketplace) {
1133
- const pluginsRoot = join6(home, ".grok", "installed-plugins");
1183
+ const pluginsRoot = join7(home, ".grok", "installed-plugins");
1134
1184
  if (!existsSync3(pluginsRoot)) return null;
1135
1185
  let entries;
1136
1186
  try {
@@ -1141,10 +1191,10 @@ function findGrokPlugin(home, key, pluginName, marketplace) {
1141
1191
  let best = null;
1142
1192
  for (const entry of entries) {
1143
1193
  if (!entry.isDirectory()) continue;
1144
- const installPath = join6(pluginsRoot, entry.name);
1194
+ const installPath = join7(pluginsRoot, entry.name);
1145
1195
  let manifest;
1146
1196
  try {
1147
- manifest = JSON.parse(readFileSync2(join6(installPath, ".claude-plugin", "plugin.json"), "utf8"));
1197
+ manifest = JSON.parse(readFileSync2(join7(installPath, ".claude-plugin", "plugin.json"), "utf8"));
1148
1198
  } catch {
1149
1199
  continue;
1150
1200
  }
@@ -1168,7 +1218,7 @@ function findGrokPlugin(home, key, pluginName, marketplace) {
1168
1218
  function findCodexPlugin(home, key, pluginName, marketplace) {
1169
1219
  const config = readCodexPluginConfig(home, key, marketplace);
1170
1220
  if (!config?.enabled) return null;
1171
- const cacheRoot = join6(home, ".codex", "plugins", "cache", marketplace, pluginName);
1221
+ const cacheRoot = join7(home, ".codex", "plugins", "cache", marketplace, pluginName);
1172
1222
  if (!existsSync3(cacheRoot)) return null;
1173
1223
  let versions;
1174
1224
  try {
@@ -1184,7 +1234,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
1184
1234
  key,
1185
1235
  pluginName,
1186
1236
  marketplace,
1187
- installPath: join6(cacheRoot, version),
1237
+ installPath: join7(cacheRoot, version),
1188
1238
  version,
1189
1239
  sourceType: config.sourceType,
1190
1240
  source: config.source
@@ -1201,7 +1251,7 @@ function parsePluginKey(key) {
1201
1251
  function readCodexPluginConfig(home, key, marketplace) {
1202
1252
  let raw;
1203
1253
  try {
1204
- raw = readFileSync2(join6(home, CODEX_CONFIG_PATH), "utf8");
1254
+ raw = readFileSync2(join7(home, CODEX_CONFIG_PATH), "utf8");
1205
1255
  } catch {
1206
1256
  return null;
1207
1257
  }
@@ -1243,9 +1293,14 @@ function parseTomlScalar(rawValue) {
1243
1293
  }
1244
1294
 
1245
1295
  // src/doctor/probes/helpers.ts
1296
+ var MCP_ONLY_LEAF_SKIP = "MCP-only leaf \u2014 check skipped";
1246
1297
  function check(status, id, label, detail) {
1247
1298
  return { id, label, status, detail };
1248
1299
  }
1300
+ function mcpOnlyOr(mcpOnlyLeaf, id, label, fallback) {
1301
+ if (mcpOnlyLeaf) return check("info", id, label, MCP_ONLY_LEAF_SKIP);
1302
+ return fallback;
1303
+ }
1249
1304
 
1250
1305
  // src/doctor/probes/environment.ts
1251
1306
  function checkNodeVersion() {
@@ -1273,12 +1328,12 @@ function detectCliChannels(argv, home) {
1273
1328
  }
1274
1329
  const plugin = findPlugin(home);
1275
1330
  if (plugin) {
1276
- const pluginBin = join7(plugin.installPath, "bin", "skillwiki");
1331
+ const pluginBin = join8(plugin.installPath, "bin", "skillwiki");
1277
1332
  if (existsSync4(pluginBin)) {
1278
1333
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
1279
1334
  }
1280
1335
  }
1281
- const installBin = join7(home, ".claude", "skills", "bin", "skillwiki");
1336
+ const installBin = join8(home, ".claude", "skills", "bin", "skillwiki");
1282
1337
  if (existsSync4(installBin)) {
1283
1338
  channels.push({ name: "install", path: installBin, isDevLink: false });
1284
1339
  }
@@ -1370,7 +1425,7 @@ async function checkProfiles(home) {
1370
1425
  }
1371
1426
  async function checkProjectLocalOverride(cwd) {
1372
1427
  const dir = cwd ?? process.cwd();
1373
- const envPath = join7(dir, ".skillwiki", ".env");
1428
+ const envPath = join8(dir, ".skillwiki", ".env");
1374
1429
  if (existsSync4(envPath)) {
1375
1430
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
1376
1431
  }
@@ -1380,6 +1435,14 @@ function checkWikiPathSet(ctx) {
1380
1435
  if (ctx.resolvedPath) {
1381
1436
  return check("pass", "wiki_path_set", "WIKI_PATH configured", `Resolved via ${ctx.wikiPathSource ?? "unknown"}: ${ctx.resolvedPath}`);
1382
1437
  }
1438
+ if (ctx.mcpOnlyLeaf) {
1439
+ return check(
1440
+ "info",
1441
+ "wiki_path_set",
1442
+ "WIKI_PATH configured",
1443
+ "MCP-only leaf \u2014 no local vault; HTTP MCP handshake is the success gate"
1444
+ );
1445
+ }
1383
1446
  return check("error", "wiki_path_set", "WIKI_PATH configured", "No vault configured. Run `skillwiki init` or pass --vault.");
1384
1447
  }
1385
1448
  var environmentProbe = {
@@ -1398,41 +1461,56 @@ var environmentProbe = {
1398
1461
 
1399
1462
  // src/doctor/probes/vault-structure.ts
1400
1463
  import { existsSync as existsSync5, statSync as statSync2 } from "fs";
1401
- import { join as join8 } from "path";
1402
- function checkWikiPathExists(resolvedPath) {
1464
+ import { join as join9 } from "path";
1465
+ function checkWikiPathExists(resolvedPath, mcpOnlyLeaf) {
1403
1466
  if (resolvedPath === void 0) {
1404
- return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
1467
+ return mcpOnlyOr(
1468
+ mcpOnlyLeaf,
1469
+ "wiki_path_exists",
1470
+ "Vault directory exists",
1471
+ check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved")
1472
+ );
1405
1473
  }
1406
1474
  if (existsSync5(resolvedPath) && statSync2(resolvedPath).isDirectory()) {
1407
1475
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
1408
1476
  }
1409
1477
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
1410
1478
  }
1411
- function checkVaultStructure(resolvedPath) {
1479
+ function checkVaultStructure(resolvedPath, mcpOnlyLeaf) {
1412
1480
  if (resolvedPath === void 0) {
1413
- return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
1481
+ return mcpOnlyOr(
1482
+ mcpOnlyLeaf,
1483
+ "vault_structure",
1484
+ "Vault structure valid",
1485
+ check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved")
1486
+ );
1414
1487
  }
1415
1488
  if (!existsSync5(resolvedPath)) {
1416
1489
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
1417
1490
  }
1418
1491
  const missing = [];
1419
- if (!existsSync5(join8(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
1492
+ if (!existsSync5(join9(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
1420
1493
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
1421
- if (!existsSync5(join8(resolvedPath, dir))) missing.push(dir + "/");
1494
+ if (!existsSync5(join9(resolvedPath, dir))) missing.push(dir + "/");
1422
1495
  }
1423
1496
  if (missing.length === 0) {
1424
1497
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
1425
1498
  }
1426
1499
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
1427
1500
  }
1428
- function checkObsidianTemplates(resolvedPath) {
1501
+ function checkObsidianTemplates(resolvedPath, mcpOnlyLeaf) {
1429
1502
  if (resolvedPath === void 0) {
1430
- return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
1503
+ return mcpOnlyOr(
1504
+ mcpOnlyLeaf,
1505
+ "obsidian_templates",
1506
+ "Obsidian templates",
1507
+ check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved")
1508
+ );
1431
1509
  }
1432
1510
  const missing = [];
1433
- if (!existsSync5(join8(resolvedPath, "_Templates"))) missing.push("_Templates/");
1434
- if (!existsSync5(join8(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
1435
- if (!existsSync5(join8(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
1511
+ if (!existsSync5(join9(resolvedPath, "_Templates"))) missing.push("_Templates/");
1512
+ if (!existsSync5(join9(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
1513
+ if (!existsSync5(join9(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
1436
1514
  if (missing.length === 0) {
1437
1515
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
1438
1516
  }
@@ -1442,23 +1520,28 @@ var vaultStructureProbe = {
1442
1520
  id: "vault_structure",
1443
1521
  run(ctx) {
1444
1522
  return [
1445
- checkWikiPathExists(ctx.resolvedPath),
1446
- checkVaultStructure(ctx.resolvedPath),
1447
- checkObsidianTemplates(ctx.resolvedPath)
1523
+ checkWikiPathExists(ctx.resolvedPath, ctx.mcpOnlyLeaf),
1524
+ checkVaultStructure(ctx.resolvedPath, ctx.mcpOnlyLeaf),
1525
+ checkObsidianTemplates(ctx.resolvedPath, ctx.mcpOnlyLeaf)
1448
1526
  ];
1449
1527
  }
1450
1528
  };
1451
1529
 
1452
1530
  // src/doctor/probes/git-fleet.ts
1453
1531
  import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
1454
- import { join as join9 } from "path";
1532
+ import { join as join10 } from "path";
1455
1533
  import { execSync as execSync2 } from "child_process";
1456
1534
  import { platform } from "os";
1457
- function checkVaultGitRemote(resolvedPath) {
1535
+ function checkVaultGitRemote(resolvedPath, mcpOnlyLeaf = false) {
1458
1536
  if (resolvedPath === void 0) {
1459
- return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
1537
+ return mcpOnlyOr(
1538
+ mcpOnlyLeaf,
1539
+ "vault_git_remote",
1540
+ "Vault git remote",
1541
+ check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved")
1542
+ );
1460
1543
  }
1461
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1544
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1462
1545
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
1463
1546
  }
1464
1547
  try {
@@ -1497,11 +1580,16 @@ async function checkFleetIdentity(input) {
1497
1580
  const detail = load.warnings.length > 0 ? load.warnings.join("; ") : "Fleet identity is unresolved";
1498
1581
  return check("warn", "fleet_identity", "Fleet identity", detail);
1499
1582
  }
1500
- function checkSyncLastPush(resolvedPath) {
1583
+ function checkSyncLastPush(resolvedPath, mcpOnlyLeaf = false) {
1501
1584
  if (resolvedPath === void 0) {
1502
- return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
1585
+ return mcpOnlyOr(
1586
+ mcpOnlyLeaf,
1587
+ "sync_last_push",
1588
+ "Vault sync recency",
1589
+ check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved")
1590
+ );
1503
1591
  }
1504
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1592
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1505
1593
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
1506
1594
  }
1507
1595
  let timestamp;
@@ -1550,7 +1638,7 @@ function checkVaultGitDirty(resolvedPath) {
1550
1638
  if (resolvedPath === void 0) {
1551
1639
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
1552
1640
  }
1553
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1641
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1554
1642
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
1555
1643
  }
1556
1644
  try {
@@ -1597,7 +1685,7 @@ function remoteMainHash(resolvedPath) {
1597
1685
  }
1598
1686
  function checkStaleRemoteMain(resolvedPath) {
1599
1687
  if (resolvedPath === void 0) return void 0;
1600
- if (!existsSync6(join9(resolvedPath, ".git"))) return void 0;
1688
+ if (!existsSync6(join10(resolvedPath, ".git"))) return void 0;
1601
1689
  const localOrigin = gitRefHash(resolvedPath, "origin/main");
1602
1690
  if (!localOrigin) return void 0;
1603
1691
  const remoteMain = remoteMainHash(resolvedPath);
@@ -1613,7 +1701,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
1613
1701
  if (resolvedPath === void 0) {
1614
1702
  return check("pass", id, label, "No vault path \u2014 check skipped");
1615
1703
  }
1616
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1704
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1617
1705
  return check("pass", id, label, "No git repo \u2014 check skipped");
1618
1706
  }
1619
1707
  if (!hasOriginMain(resolvedPath)) {
@@ -1658,11 +1746,11 @@ function checkVaultGitBehind(resolvedPath) {
1658
1746
  }
1659
1747
  function pullLogPaths(home) {
1660
1748
  const paths = platform() === "darwin" ? [
1661
- join9(home, "Library", "Logs", "wiki-pull.log"),
1662
- join9(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
1749
+ join10(home, "Library", "Logs", "wiki-pull.log"),
1750
+ join10(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
1663
1751
  ] : [
1664
- join9(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
1665
- join9(home, "Library", "Logs", "wiki-pull.log")
1752
+ join10(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
1753
+ join10(home, "Library", "Logs", "wiki-pull.log")
1666
1754
  ];
1667
1755
  return [...new Set(paths)];
1668
1756
  }
@@ -1693,11 +1781,16 @@ function checkVaultGitPullFailures(home) {
1693
1781
  return check("warn", "vault_git_pull_failures", "Vault pull failures", `Could not read ${path}`);
1694
1782
  }
1695
1783
  }
1696
- function checkVaultLocalGit(resolvedPath) {
1784
+ function checkVaultLocalGit(resolvedPath, mcpOnlyLeaf = false) {
1697
1785
  if (resolvedPath === void 0) {
1698
- return check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved");
1786
+ return mcpOnlyOr(
1787
+ mcpOnlyLeaf,
1788
+ "vault_local_git",
1789
+ "Vault local git",
1790
+ check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved")
1791
+ );
1699
1792
  }
1700
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1793
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1701
1794
  return check("warn", "vault_local_git", "Vault local git", "Not a git repository - sync features unavailable");
1702
1795
  }
1703
1796
  try {
@@ -1716,7 +1809,7 @@ function checkVaultGithubRemote(resolvedPath, exec) {
1716
1809
  if (resolvedPath === void 0) {
1717
1810
  return check("pass", "vault_github_remote", "Vault GitHub remote", "No vault path \u2014 check skipped");
1718
1811
  }
1719
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1812
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1720
1813
  return check("pass", "vault_github_remote", "Vault GitHub remote", "No git repo \u2014 check skipped");
1721
1814
  }
1722
1815
  const state = probeGithubReachability(resolvedPath, exec);
@@ -1760,7 +1853,7 @@ function checkVaultPromotionLag(resolvedPath) {
1760
1853
  if (resolvedPath === void 0) {
1761
1854
  return check("pass", "vault_promotion_lag", "Vault promotion lag", "No vault path \u2014 check skipped");
1762
1855
  }
1763
- if (!existsSync6(join9(resolvedPath, ".git"))) {
1856
+ if (!existsSync6(join10(resolvedPath, ".git"))) {
1764
1857
  return check("pass", "vault_promotion_lag", "Vault promotion lag", "No git repo \u2014 check skipped");
1765
1858
  }
1766
1859
  try {
@@ -1787,7 +1880,7 @@ var gitFleetProbe = {
1787
1880
  id: "git_fleet",
1788
1881
  async run(ctx) {
1789
1882
  return [
1790
- checkVaultGitRemote(ctx.gitCheckPath),
1883
+ checkVaultGitRemote(ctx.gitCheckPath, ctx.mcpOnlyLeaf),
1791
1884
  await checkFleetIdentity({
1792
1885
  vaultPath: ctx.resolvedPath,
1793
1886
  home: ctx.input.home,
@@ -1795,12 +1888,12 @@ var gitFleetProbe = {
1795
1888
  envValue: ctx.input.envValue,
1796
1889
  fleetLoad: ctx.fleetLoad
1797
1890
  }),
1798
- checkSyncLastPush(ctx.gitCheckPath),
1891
+ checkSyncLastPush(ctx.gitCheckPath, ctx.mcpOnlyLeaf),
1799
1892
  checkVaultGitDirty(ctx.gitCheckPath),
1800
1893
  checkVaultGitAhead(ctx.gitCheckPath),
1801
1894
  checkVaultGitBehind(ctx.gitCheckPath),
1802
1895
  checkVaultGitPullFailures(ctx.input.home),
1803
- checkVaultLocalGit(ctx.gitCheckPath),
1896
+ checkVaultLocalGit(ctx.gitCheckPath, ctx.mcpOnlyLeaf),
1804
1897
  checkVaultGithubRemote(ctx.gitCheckPath, ctx.input.execProbe),
1805
1898
  checkVaultS3Remote(ctx.input.home, ctx.input.execProbe, ctx.input.env ?? process.env),
1806
1899
  checkVaultSnapshotterReachable(ctx.fleetLoad, ctx.input.checkSnapshotter, ctx.input.execProbe),
@@ -1811,11 +1904,11 @@ var gitFleetProbe = {
1811
1904
 
1812
1905
  // src/doctor/probes/hygiene.ts
1813
1906
  import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
1814
- import { join as join11 } from "path";
1907
+ import { join as join12 } from "path";
1815
1908
 
1816
1909
  // src/utils/conflict-markers.ts
1817
1910
  import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
1818
- import { join as join10 } from "path";
1911
+ import { join as join11 } from "path";
1819
1912
  function scanConflictMarkerBlocksInText(relPath, text) {
1820
1913
  const findings = [];
1821
1914
  const lines = text.split(/\r?\n/);
@@ -1866,7 +1959,7 @@ function walkMarkdownFiles(root, dir, rel, out) {
1866
1959
  for (const entry of entries) {
1867
1960
  if (entry.isDirectory()) {
1868
1961
  if (PRUNE_DIRS.has(entry.name)) continue;
1869
- walkMarkdownFiles(root, join10(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
1962
+ walkMarkdownFiles(root, join11(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
1870
1963
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
1871
1964
  out.push(rel ? `${rel}/${entry.name}` : entry.name);
1872
1965
  }
@@ -1880,7 +1973,7 @@ function scanVaultConflictMarkers(vaultRoot) {
1880
1973
  for (const rel of relPaths) {
1881
1974
  let text;
1882
1975
  try {
1883
- text = readFileSync4(join10(vaultRoot, rel), "utf8");
1976
+ text = readFileSync4(join11(vaultRoot, rel), "utf8");
1884
1977
  } catch {
1885
1978
  continue;
1886
1979
  }
@@ -1890,11 +1983,16 @@ function scanVaultConflictMarkers(vaultRoot) {
1890
1983
  }
1891
1984
 
1892
1985
  // src/doctor/probes/hygiene.ts
1893
- function checkDotStoreClean(resolvedPath) {
1986
+ function checkDotStoreClean(resolvedPath, mcpOnlyLeaf = false) {
1894
1987
  if (resolvedPath === void 0) {
1895
- return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
1988
+ return mcpOnlyOr(
1989
+ mcpOnlyLeaf,
1990
+ "dsstore_clean",
1991
+ "No .DS_Store in raw/",
1992
+ check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved")
1993
+ );
1896
1994
  }
1897
- const rawDir = join11(resolvedPath, "raw");
1995
+ const rawDir = join12(resolvedPath, "raw");
1898
1996
  if (!existsSync8(rawDir)) {
1899
1997
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
1900
1998
  }
@@ -1910,7 +2008,7 @@ function checkDotStoreClean(resolvedPath) {
1910
2008
  if (entry.name === ".DS_Store") {
1911
2009
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
1912
2010
  } else if (entry.isDirectory()) {
1913
- walk(join11(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
2011
+ walk(join12(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
1914
2012
  }
1915
2013
  }
1916
2014
  })(rawDir, "");
@@ -1939,7 +2037,7 @@ function checkVaultConflictMarkers(resolvedPath) {
1939
2037
  }
1940
2038
  function githubSyncedGitRoot(gitRoot) {
1941
2039
  if (gitRoot === void 0) return { ok: false, reason: "No vault path \u2014 check skipped" };
1942
- if (!existsSync8(join11(gitRoot, ".git"))) return { ok: false, reason: "Not a git repository \u2014 check skipped" };
2040
+ if (!existsSync8(join12(gitRoot, ".git"))) return { ok: false, reason: "Not a git repository \u2014 check skipped" };
1943
2041
  if (!git(gitRoot, ["remote"])) return { ok: false, reason: "No git remote \u2014 not GitHub-synced, check skipped" };
1944
2042
  return { ok: true, gitRoot };
1945
2043
  }
@@ -1949,7 +2047,7 @@ function checkVaultGitignoreHygiene(synced) {
1949
2047
  }
1950
2048
  let content = "";
1951
2049
  try {
1952
- content = readFileSync5(join11(synced.gitRoot, ".gitignore"), "utf8");
2050
+ content = readFileSync5(join12(synced.gitRoot, ".gitignore"), "utf8");
1953
2051
  } catch {
1954
2052
  content = "";
1955
2053
  }
@@ -1987,7 +2085,7 @@ var hygieneProbe = {
1987
2085
  run(ctx) {
1988
2086
  const synced = githubSyncedGitRoot(ctx.gitCheckPath);
1989
2087
  return [
1990
- checkDotStoreClean(ctx.readOnlyScanRoot),
2088
+ checkDotStoreClean(ctx.readOnlyScanRoot, ctx.mcpOnlyLeaf),
1991
2089
  checkVaultConflictMarkers(ctx.readOnlyScanRoot),
1992
2090
  checkVaultGitignoreHygiene(synced),
1993
2091
  checkTrackedHygieneScratch(synced)
@@ -1997,14 +2095,14 @@ var hygieneProbe = {
1997
2095
 
1998
2096
  // src/doctor/probes/s3-mount-health.ts
1999
2097
  import { existsSync as existsSync10 } from "fs";
2000
- import { join as join13 } from "path";
2098
+ import { join as join14 } from "path";
2001
2099
  import { execSync as execSync4 } from "child_process";
2002
2100
 
2003
2101
  // src/utils/s3-mount-health.ts
2004
2102
  import { execSync as execSync3 } from "child_process";
2005
2103
  import { platform as platform2 } from "os";
2006
2104
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, readFileSync as readFile6 } from "fs";
2007
- import { join as join12 } from "path";
2105
+ import { join as join13 } from "path";
2008
2106
  var OS = platform2();
2009
2107
  function findRcloneMountPid() {
2010
2108
  try {
@@ -2162,7 +2260,7 @@ function detectFuseMount(vaultPath) {
2162
2260
  return null;
2163
2261
  }
2164
2262
  function writeTest(dir) {
2165
- const testFile = join12(dir, `.doctor-write-test-${process.pid}.tmp`);
2263
+ const testFile = join13(dir, `.doctor-write-test-${process.pid}.tmp`);
2166
2264
  const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
2167
2265
  const start = Date.now();
2168
2266
  try {
@@ -2243,7 +2341,7 @@ function checkS3MountPerf(resolvedPath) {
2243
2341
  return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
2244
2342
  }
2245
2343
  const mountPoint = fuse.mountPoint;
2246
- const conceptsDir = join13(resolvedPath, "concepts");
2344
+ const conceptsDir = join14(resolvedPath, "concepts");
2247
2345
  if (!existsSync10(conceptsDir)) {
2248
2346
  return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
2249
2347
  }
@@ -2394,7 +2492,10 @@ function checkRcloneFlagAudit(resolvedPath) {
2394
2492
  }
2395
2493
  return check("pass", "rclone_flags", "rclone VFS flags", `PID ${pid}: all critical flags at safe values`);
2396
2494
  }
2397
- function checkRcloneVersion(resolvedPath, vaultSyncInstalled, pushEnabled = true) {
2495
+ function checkRcloneVersion(resolvedPath, vaultSyncInstalled, pushEnabled = true, mcpOnlyLeaf = false) {
2496
+ if (mcpOnlyLeaf) {
2497
+ return check("pass", "rclone_version", "rclone version", "MCP-only leaf \u2014 rclone not required");
2498
+ }
2398
2499
  const fuse = resolvedPath ? detectFuseMount(resolvedPath) : null;
2399
2500
  if (pushEnabled === false && !fuse) {
2400
2501
  return check("pass", "rclone_version", "rclone version", "push not required \u2014 check skipped");
@@ -2429,7 +2530,7 @@ function checkWriteTest(resolvedPath) {
2429
2530
  if (!fuse) {
2430
2531
  return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
2431
2532
  }
2432
- const conceptsDir = join13(resolvedPath, "concepts");
2533
+ const conceptsDir = join14(resolvedPath, "concepts");
2433
2534
  if (!existsSync10(conceptsDir)) {
2434
2535
  return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
2435
2536
  }
@@ -2521,7 +2622,7 @@ var s3MountHealthProbe = {
2521
2622
  checkS3MountPerf(ctx.resolvedPath),
2522
2623
  checkS3MountFreshness(ctx.resolvedPath),
2523
2624
  checkRcloneFlagAudit(ctx.resolvedPath),
2524
- checkRcloneVersion(ctx.resolvedPath, ctx.vsConfig.installed, ctx.vsConfig.pushEnabled !== false),
2625
+ checkRcloneVersion(ctx.resolvedPath, ctx.vsConfig.installed, ctx.vsConfig.pushEnabled !== false, ctx.mcpOnlyLeaf),
2525
2626
  checkWriteTest(ctx.resolvedPath),
2526
2627
  checkVfsCacheHealth(ctx.resolvedPath)
2527
2628
  ];
@@ -2530,7 +2631,7 @@ var s3MountHealthProbe = {
2530
2631
 
2531
2632
  // src/doctor/probes/skills-plugins.ts
2532
2633
  import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
2533
- import { join as join14 } from "path";
2634
+ import { join as join15 } from "path";
2534
2635
  function findSkillMd(dir) {
2535
2636
  const results = [];
2536
2637
  let entries;
@@ -2541,9 +2642,9 @@ function findSkillMd(dir) {
2541
2642
  }
2542
2643
  for (const entry of entries) {
2543
2644
  if (entry.isFile() && entry.name === "SKILL.md") {
2544
- results.push(join14(dir, entry.name));
2645
+ results.push(join15(dir, entry.name));
2545
2646
  } else if (entry.isDirectory()) {
2546
- results.push(...findSkillMd(join14(dir, entry.name)));
2647
+ results.push(...findSkillMd(join15(dir, entry.name)));
2547
2648
  }
2548
2649
  }
2549
2650
  return results;
@@ -2557,18 +2658,18 @@ function findSkillNames(dir) {
2557
2658
  return results;
2558
2659
  }
2559
2660
  for (const entry of entries) {
2560
- if (entry.isDirectory() && existsSync11(join14(dir, entry.name, "SKILL.md"))) {
2661
+ if (entry.isDirectory() && existsSync11(join15(dir, entry.name, "SKILL.md"))) {
2561
2662
  results.push(entry.name);
2562
2663
  }
2563
2664
  }
2564
2665
  return results;
2565
2666
  }
2566
2667
  function findInstalledSkillMd(dir) {
2567
- const directSkills = findSkillNames(dir).map((name) => join14(dir, name, "SKILL.md"));
2668
+ const directSkills = findSkillNames(dir).map((name) => join15(dir, name, "SKILL.md"));
2568
2669
  return directSkills.length > 0 ? directSkills : findSkillMd(dir);
2569
2670
  }
2570
2671
  function checkSkillsInstalled(home, cwd) {
2571
- const srcDir = cwd ? join14(cwd, "packages", "skills") : void 0;
2672
+ const srcDir = cwd ? join15(cwd, "packages", "skills") : void 0;
2572
2673
  if (srcDir && existsSync11(srcDir)) {
2573
2674
  const found = findInstalledSkillMd(srcDir);
2574
2675
  if (found.length > 0) {
@@ -2582,7 +2683,7 @@ function checkSkillsInstalled(home, cwd) {
2582
2683
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
2583
2684
  }
2584
2685
  }
2585
- const skillsDir = join14(home, ".claude", "skills");
2686
+ const skillsDir = join15(home, ".claude", "skills");
2586
2687
  if (existsSync11(skillsDir)) {
2587
2688
  const found = findInstalledSkillMd(skillsDir);
2588
2689
  if (found.length > 0) {
@@ -2593,10 +2694,10 @@ function checkSkillsInstalled(home, cwd) {
2593
2694
  }
2594
2695
  function checkDuplicateSkills(home) {
2595
2696
  const plugin = findPlugin(home);
2596
- const skillsDir = join14(home, ".claude", "skills");
2697
+ const skillsDir = join15(home, ".claude", "skills");
2597
2698
  const agentSkillDirs = [
2598
- { label: "~/.codex/skills/", path: join14(home, ".codex", "skills") },
2599
- { label: "~/.agents/skills/", path: join14(home, ".agents", "skills") }
2699
+ { label: "~/.codex/skills/", path: join15(home, ".codex", "skills") },
2700
+ { label: "~/.agents/skills/", path: join15(home, ".agents", "skills") }
2600
2701
  ];
2601
2702
  if (!plugin) {
2602
2703
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -2629,10 +2730,10 @@ var STALE_GROK_ACTIVATION_REFERENCE = "Read @skillwiki.md";
2629
2730
  var ACTIVATION_FIX_HINT = "run `npm run install:activation` from the llm-wiki repo";
2630
2731
  function findGrokActivationTemplate(home, cwd) {
2631
2732
  if (cwd) {
2632
- const src = join14(cwd, "packages", "skills", "using-skillwiki", "activation.md");
2733
+ const src = join15(cwd, "packages", "skills", "using-skillwiki", "activation.md");
2633
2734
  if (existsSync11(src)) return src;
2634
2735
  }
2635
- const pluginsRoot = join14(home, ".grok", "installed-plugins");
2736
+ const pluginsRoot = join15(home, ".grok", "installed-plugins");
2636
2737
  if (!existsSync11(pluginsRoot)) return void 0;
2637
2738
  let entries;
2638
2739
  try {
@@ -2642,21 +2743,21 @@ function findGrokActivationTemplate(home, cwd) {
2642
2743
  }
2643
2744
  for (const entry of entries) {
2644
2745
  if (!entry.isDirectory()) continue;
2645
- const root = join14(pluginsRoot, entry.name);
2746
+ const root = join15(pluginsRoot, entry.name);
2646
2747
  for (const rel of ["using-skillwiki/activation.md", "skills/using-skillwiki/activation.md"]) {
2647
- const candidate = join14(root, rel);
2748
+ const candidate = join15(root, rel);
2648
2749
  if (existsSync11(candidate)) return candidate;
2649
2750
  }
2650
2751
  }
2651
2752
  return void 0;
2652
2753
  }
2653
2754
  function checkGrokActivation(home, cwd) {
2654
- const grokDir = join14(home, ".grok");
2755
+ const grokDir = join15(home, ".grok");
2655
2756
  if (!existsSync11(grokDir)) {
2656
2757
  return check("pass", "activation_grok", "Grok activation", "Not a Grok host");
2657
2758
  }
2658
- const activationPath = join14(grokDir, "skillwiki.md");
2659
- const agentsPath = join14(grokDir, "AGENTS.md");
2759
+ const activationPath = join15(grokDir, "skillwiki.md");
2760
+ const agentsPath = join15(grokDir, "AGENTS.md");
2660
2761
  const hasActivation = existsSync11(activationPath);
2661
2762
  const issues = [];
2662
2763
  if (!hasActivation) {
@@ -2771,7 +2872,7 @@ var skillsPluginsProbe = {
2771
2872
 
2772
2873
  // src/doctor/probes/vault-sync.ts
2773
2874
  import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2774
- import { join as join15 } from "path";
2875
+ import { join as join16 } from "path";
2775
2876
  import { execSync as execSync5 } from "child_process";
2776
2877
  import { platform as platform3 } from "os";
2777
2878
  function readPushResultState(stateFile) {
@@ -2800,7 +2901,7 @@ function readPushResultState(stateFile) {
2800
2901
  }
2801
2902
  function readVaultSyncConfig(home) {
2802
2903
  try {
2803
- const content = readFileSync8(join15(home, ".skillwiki", ".env"), "utf8");
2904
+ const content = readFileSync8(join16(home, ".skillwiki", ".env"), "utf8");
2804
2905
  let installed = false;
2805
2906
  let role;
2806
2907
  let serviceScope;
@@ -3103,7 +3204,7 @@ function snapshotterHealthChecks(scope, logDir, env) {
3103
3204
  let completionOutcome = "unknown";
3104
3205
  const logRecords = fixture ? fixture.log_records : (() => {
3105
3206
  try {
3106
- const content = readFileSync8(join15(logDir, "wiki-snapshot.log"), "utf8");
3207
+ const content = readFileSync8(join16(logDir, "wiki-snapshot.log"), "utf8");
3107
3208
  return content.split(/\r?\n/).filter(Boolean);
3108
3209
  } catch {
3109
3210
  return [];
@@ -3281,11 +3382,11 @@ function vaultSyncChecks(input) {
3281
3382
  ];
3282
3383
  }
3283
3384
  const isMac = os === "darwin";
3284
- const logDir = input.logDir ?? (isMac ? join15(home, "Library", "Logs") : join15(home, ".local", "state", "vault-sync", "log"));
3285
- const cacheDir = input.cacheDir ?? (isMac ? join15(home, "Library", "Caches", "vault-sync") : join15(home, ".cache", "vault-sync"));
3286
- const shareDir = input.shareDir ?? (isMac ? join15(home, "Library", "Application Support", "vault-sync", "bin") : join15(home, ".local", "share", "vault-sync", "bin"));
3287
- const filterPath = input.filterPath ?? join15(home, ".config", "rclone", "wiki-push-filters.txt");
3288
- const packagedSnapshotPath = join15(shareDir, "wiki-snapshot.sh");
3385
+ const logDir = input.logDir ?? (isMac ? join16(home, "Library", "Logs") : join16(home, ".local", "state", "vault-sync", "log"));
3386
+ const cacheDir = input.cacheDir ?? (isMac ? join16(home, "Library", "Caches", "vault-sync") : join16(home, ".cache", "vault-sync"));
3387
+ const shareDir = input.shareDir ?? (isMac ? join16(home, "Library", "Application Support", "vault-sync", "bin") : join16(home, ".local", "share", "vault-sync", "bin"));
3388
+ const filterPath = input.filterPath ?? join16(home, ".config", "rclone", "wiki-push-filters.txt");
3389
+ const packagedSnapshotPath = join16(shareDir, "wiki-snapshot.sh");
3289
3390
  const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
3290
3391
  const snapshotPath = input.snapshotScriptPath ?? (existsSync12(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
3291
3392
  if (input.vaultSyncRole === "snapshotter") {
@@ -3342,9 +3443,9 @@ function vaultSyncChecks(input) {
3342
3443
  return [c12, ...healthChecks, cFetch2, c42, c52];
3343
3444
  }
3344
3445
  const env = input.env ?? process.env;
3345
- const fetchLogFile = join15(logDir, "wiki-fetch.log");
3446
+ const fetchLogFile = join16(logDir, "wiki-fetch.log");
3346
3447
  if (input.pushEnabled === false) {
3347
- const fetchHelperPath = join15(shareDir, "wiki-fetch-notify.sh");
3448
+ const fetchHelperPath = join16(shareDir, "wiki-fetch-notify.sh");
3348
3449
  const c12 = existsSync12(fetchHelperPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${fetchHelperPath}`) : check(
3349
3450
  "error",
3350
3451
  "vault_sync_installed",
@@ -3379,7 +3480,7 @@ function vaultSyncChecks(input) {
3379
3480
  );
3380
3481
  return [c12, c22, c32, cPushResult2, cFetch2, c42, c52];
3381
3482
  }
3382
- const pushScriptPath = join15(shareDir, "wiki-push.sh");
3483
+ const pushScriptPath = join16(shareDir, "wiki-push.sh");
3383
3484
  const c1 = existsSync12(pushScriptPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${pushScriptPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Script not found at ${pushScriptPath} \u2014 run vault-sync-install`);
3384
3485
  let c2;
3385
3486
  try {
@@ -3431,9 +3532,9 @@ function vaultSyncChecks(input) {
3431
3532
  "Scheduler check failed \u2014 run vault-sync-install"
3432
3533
  );
3433
3534
  }
3434
- const stateFile = join15(cacheDir, "wiki-push-result.state");
3535
+ const stateFile = join16(cacheDir, "wiki-push-result.state");
3435
3536
  const pushState = readPushResultState(stateFile);
3436
- const logFile = join15(logDir, "wiki-push.log");
3537
+ const logFile = join16(logDir, "wiki-push.log");
3437
3538
  let c3;
3438
3539
  if (pushState.exists && !pushState.malformed && pushState.result === "refused") {
3439
3540
  const reasonSuffix = pushState.reason ? `: ${pushState.reason}` : "";
@@ -3583,7 +3684,7 @@ function checkVaultSyncPullHelper(home, env) {
3583
3684
  );
3584
3685
  }
3585
3686
  function isGitDir(path) {
3586
- return Boolean(path && existsSync12(join15(path, ".git")));
3687
+ return Boolean(path && existsSync12(join16(path, ".git")));
3587
3688
  }
3588
3689
  function checkVaultSyncReviewRequiredJournals(ctx) {
3589
3690
  const live = ctx.resolvedPath;
@@ -3660,7 +3761,11 @@ var vaultSyncProbe = {
3660
3761
  snapshotScriptPath: ctx.vsConfig.snapshotScript,
3661
3762
  env: ctx.input.env ?? process.env
3662
3763
  }));
3663
- checks.push(checkVaultSyncPullHelper(ctx.input.home, ctx.input.env ?? process.env));
3764
+ if (!ctx.vsConfig.installed) {
3765
+ checks.push(check("pass", "vault_sync_pull_helper", "Vault-sync pull helper", "vault-sync not installed \u2014 check skipped"));
3766
+ } else {
3767
+ checks.push(checkVaultSyncPullHelper(ctx.input.home, ctx.input.env ?? process.env));
3768
+ }
3664
3769
  checks.push(checkVaultSyncReviewRequiredJournals(ctx));
3665
3770
  checks.push(checkSnapshotWorktreeUnmerged(ctx));
3666
3771
  return checks;
@@ -3674,10 +3779,10 @@ import { platform as platform4 } from "os";
3674
3779
 
3675
3780
  // src/utils/satellite-run-health.ts
3676
3781
  import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
3677
- import { join as join16 } from "path";
3782
+ import { join as join17 } from "path";
3678
3783
  var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
3679
3784
  function satelliteLatestRunPath(vault) {
3680
- return join16(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
3785
+ return join17(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
3681
3786
  }
3682
3787
  function isFailedRunStatus(status) {
3683
3788
  return status === "fail" || status === "failure";
@@ -3689,7 +3794,9 @@ function parseLatestRunFile(text) {
3689
3794
  if (!status) return null;
3690
3795
  const finishedAt = typeof parsed.finished_at === "string" && parsed.finished_at.length > 0 ? parsed.finished_at : void 0;
3691
3796
  const failureClass = parsed.failure_class != null && String(parsed.failure_class).length > 0 ? String(parsed.failure_class) : void 0;
3692
- return { status, finishedAt, failureClass };
3797
+ const runDate = typeof parsed.run_date === "string" && parsed.run_date.length > 0 ? parsed.run_date : void 0;
3798
+ const selectedCandidateCount = typeof parsed.selected_candidate_count === "number" && Number.isInteger(parsed.selected_candidate_count) && parsed.selected_candidate_count >= 0 ? parsed.selected_candidate_count : void 0;
3799
+ return { status, runDate, selectedCandidateCount, finishedAt, failureClass };
3693
3800
  } catch {
3694
3801
  return null;
3695
3802
  }
@@ -3697,17 +3804,21 @@ function parseLatestRunFile(text) {
3697
3804
  function readSatelliteLatestRunFromText(text) {
3698
3805
  return parseLatestRunFile(text);
3699
3806
  }
3700
- function readSatelliteLatestRun(vault) {
3701
- const latestPath = satelliteLatestRunPath(vault);
3702
- if (!existsSync13(latestPath)) return null;
3807
+ function readSatelliteLatestRunAt(path) {
3808
+ if (!existsSync13(path)) return null;
3703
3809
  try {
3704
- return parseLatestRunFile(readFileSync9(latestPath, "utf8"));
3810
+ return parseLatestRunFile(readFileSync9(path, "utf8"));
3705
3811
  } catch {
3706
3812
  return null;
3707
3813
  }
3708
3814
  }
3709
3815
  function evaluateSatelliteRunHealth(vault, now) {
3710
- const run = readSatelliteLatestRun(vault);
3816
+ return evaluateSatelliteRunHealthAt(satelliteLatestRunPath(vault), now);
3817
+ }
3818
+ function evaluateSatelliteRunHealthAt(path, now) {
3819
+ return evaluateSatelliteRunHealthState(readSatelliteLatestRunAt(path), now);
3820
+ }
3821
+ function evaluateSatelliteRunHealthState(run, now) {
3711
3822
  if (!run) {
3712
3823
  return { failed: false, stale: false };
3713
3824
  }
@@ -3813,7 +3924,7 @@ var satelliteProbe = {
3813
3924
 
3814
3925
  // src/doctor/probes/metrics.ts
3815
3926
  import { readFileSync as readFileSync10 } from "fs";
3816
- import { join as join17 } from "path";
3927
+ import { join as join18 } from "path";
3817
3928
  var METRIC_TYPES = ["entities", "concepts", "comparisons", "queries", "meta"];
3818
3929
  function doctorReadOnlyScanRoot(resolvedPath) {
3819
3930
  return resolveReadOnlyVaultRoot(resolvedPath).root;
@@ -3857,7 +3968,7 @@ async function vaultMetrics(resolvedPath) {
3857
3968
  }
3858
3969
  let logLines = 0;
3859
3970
  try {
3860
- logLines = readFileSync10(join17(scanRoot, "log.md"), "utf8").split("\n").length;
3971
+ logLines = readFileSync10(join18(scanRoot, "log.md"), "utf8").split("\n").length;
3861
3972
  } catch {
3862
3973
  }
3863
3974
  return [
@@ -3993,18 +4104,18 @@ var fuseStalenessProbe = {
3993
4104
 
3994
4105
  // src/doctor/probes/activation-marker.ts
3995
4106
  import { existsSync as existsSync15, readFileSync as readFileSync11, readdirSync as readdirSync5 } from "fs";
3996
- import { join as join18 } from "path";
4107
+ import { join as join19 } from "path";
3997
4108
  var GROK_ACTIVATION_REFERENCE2 = "Read @~/.grok/skillwiki.md for SkillWiki activation context.";
3998
4109
  var STALE_GROK_ACTIVATION_REFERENCE2 = "Read @skillwiki.md";
3999
4110
  var ACTIVATION_FIX_HINT2 = "run `npm run install:activation` from the llm-wiki repo";
4000
4111
  function findGrokActivationTemplate2(home, cwd) {
4001
4112
  if (cwd) {
4002
- const src = join18(cwd, "packages", "skills", "using-skillwiki", "activation.md");
4113
+ const src = join19(cwd, "packages", "skills", "using-skillwiki", "activation.md");
4003
4114
  if (existsSync15(src)) return src;
4004
- const directSrc = join18(cwd, "skills", "using-skillwiki", "activation.md");
4115
+ const directSrc = join19(cwd, "skills", "using-skillwiki", "activation.md");
4005
4116
  if (existsSync15(directSrc)) return directSrc;
4006
4117
  }
4007
- const pluginsRoot = join18(home, ".grok", "installed-plugins");
4118
+ const pluginsRoot = join19(home, ".grok", "installed-plugins");
4008
4119
  if (!existsSync15(pluginsRoot)) return void 0;
4009
4120
  let entries;
4010
4121
  try {
@@ -4014,21 +4125,21 @@ function findGrokActivationTemplate2(home, cwd) {
4014
4125
  }
4015
4126
  for (const entry of entries) {
4016
4127
  if (!entry.isDirectory()) continue;
4017
- const root = join18(pluginsRoot, entry.name);
4128
+ const root = join19(pluginsRoot, entry.name);
4018
4129
  for (const rel of ["using-skillwiki/activation.md", "skills/using-skillwiki/activation.md"]) {
4019
- const candidate = join18(root, rel);
4130
+ const candidate = join19(root, rel);
4020
4131
  if (existsSync15(candidate)) return candidate;
4021
4132
  }
4022
4133
  }
4023
4134
  return void 0;
4024
4135
  }
4025
4136
  function checkActivationMarker(home, cwd) {
4026
- const grokDir = join18(home, ".grok");
4137
+ const grokDir = join19(home, ".grok");
4027
4138
  if (!existsSync15(grokDir)) {
4028
4139
  return check("pass", "activation_marker", "Activation marker", "Not a Grok host \u2014 check skipped");
4029
4140
  }
4030
- const activationPath = join18(grokDir, "skillwiki.md");
4031
- const agentsPath = join18(grokDir, "AGENTS.md");
4141
+ const activationPath = join19(grokDir, "skillwiki.md");
4142
+ const agentsPath = join19(grokDir, "AGENTS.md");
4032
4143
  const hasActivation = existsSync15(activationPath);
4033
4144
  const issues = [];
4034
4145
  if (!hasActivation) {
@@ -4085,7 +4196,7 @@ var activationMarkerProbe = {
4085
4196
 
4086
4197
  // src/doctor/probes/ds-store-noise.ts
4087
4198
  import { existsSync as existsSync16, readdirSync as readdirSync6 } from "fs";
4088
- import { join as join19 } from "path";
4199
+ import { join as join20 } from "path";
4089
4200
  var VAULT_TRACKED_DIRS = ["raw", "entities", "concepts", "comparisons", "queries", "meta", "_archive", "_Templates"];
4090
4201
  function checkDsStoreNoise(resolvedPath) {
4091
4202
  if (resolvedPath === void 0) {
@@ -4107,13 +4218,13 @@ function checkDsStoreNoise(resolvedPath) {
4107
4218
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
4108
4219
  } else if (entry.isDirectory()) {
4109
4220
  if (entry.name === ".git" || entry.name === "node_modules") continue;
4110
- walk(join19(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
4221
+ walk(join20(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
4111
4222
  }
4112
4223
  }
4113
4224
  }
4114
4225
  let scannedAny = false;
4115
4226
  for (const sub of VAULT_TRACKED_DIRS) {
4116
- const subDir = join19(resolvedPath, sub);
4227
+ const subDir = join20(resolvedPath, sub);
4117
4228
  if (existsSync16(subDir)) {
4118
4229
  scannedAny = true;
4119
4230
  walk(subDir, sub);
@@ -4143,17 +4254,14 @@ var dsStoreNoiseProbe = {
4143
4254
 
4144
4255
  // src/doctor/probes/mcp-handshake.ts
4145
4256
  import { existsSync as existsSync17 } from "fs";
4146
- import { join as join20 } from "path";
4257
+ import { join as join21 } from "path";
4147
4258
  var DEFAULT_MCP_URL = "https://wiki.karldigi.dev/mcp";
4148
- var URL_ENV = "SKILLWIKI_MCP_URL";
4149
- var TOKEN_ENV = "SKILLWIKI_MCP_TOKEN";
4150
4259
  function mcpEnv(ctx) {
4151
4260
  return ctx.input.env ?? process.env;
4152
4261
  }
4153
4262
  function checkMcpUrl(ctx) {
4154
- const env = mcpEnv(ctx);
4155
- const override = env[URL_ENV];
4156
- if (typeof override === "string" && override.trim().length > 0) {
4263
+ const creds = mcpAuthFromEnv(mcpEnv(ctx));
4264
+ if (creds.url) {
4157
4265
  return check("pass", "mcp_url_configured", "HTTP MCP URL", "configured via env override");
4158
4266
  }
4159
4267
  return check(
@@ -4164,21 +4272,20 @@ function checkMcpUrl(ctx) {
4164
4272
  );
4165
4273
  }
4166
4274
  function checkMcpCredential(ctx) {
4167
- const env = mcpEnv(ctx);
4168
- const value = env[TOKEN_ENV];
4169
- if (typeof value === "string" && value.trim().length > 0) {
4275
+ const creds = mcpAuthFromEnv(mcpEnv(ctx));
4276
+ if (creds.token) {
4170
4277
  return check("pass", "mcp_credential_present", "HTTP MCP auth", "present");
4171
4278
  }
4172
4279
  return check(
4173
4280
  "warn",
4174
4281
  "mcp_credential_present",
4175
4282
  "HTTP MCP auth",
4176
- "not set \u2014 set in process env or host Configure (value not shown)"
4283
+ "not set \u2014 set in process env, ~/.skillwiki/.env, or host Configure (value not shown)"
4177
4284
  );
4178
4285
  }
4179
4286
  function checkFrozenLeafWritePath(ctx) {
4180
4287
  const vault = ctx.resolvedPath;
4181
- if (!vault || !existsSync17(join20(vault, ".WIKI_GIT_FROZEN"))) {
4288
+ if (!vault || !existsSync17(join21(vault, ".WIKI_GIT_FROZEN"))) {
4182
4289
  return check(
4183
4290
  "pass",
4184
4291
  "mcp_frozen_leaf_write_path",
@@ -4210,8 +4317,7 @@ function versionLessThan(a, b) {
4210
4317
  return pa.patch < pb.patch;
4211
4318
  }
4212
4319
  function redactSecret(detail, secret) {
4213
- if (!secret || secret.length === 0 || !detail.includes(secret)) return detail;
4214
- return detail.split(secret).join("[REDACTED]");
4320
+ return redactMcpSecret(detail, secret);
4215
4321
  }
4216
4322
  function handshakeRow(status, detail, secret) {
4217
4323
  return check(status, HANDSHAKE_ID, HANDSHAKE_LABEL, redactSecret(detail, secret));
@@ -4269,9 +4375,10 @@ async function checkMcpHandshake(ctx) {
4269
4375
  return handshakeRow("pass", "not requested \u2014 check skipped");
4270
4376
  }
4271
4377
  const env = mcpEnv(ctx);
4272
- const url = typeof env[URL_ENV] === "string" && env[URL_ENV].trim().length > 0 ? env[URL_ENV].trim() : DEFAULT_MCP_URL;
4273
- const token = typeof env[TOKEN_ENV] === "string" ? env[TOKEN_ENV] : "";
4274
- if (token.trim().length === 0) {
4378
+ const creds = mcpAuthFromEnv(env);
4379
+ const url = creds.url ?? DEFAULT_MCP_URL;
4380
+ const token = creds.token ?? "";
4381
+ if (token.length === 0) {
4275
4382
  return handshakeRow("error", "auth not set \u2014 handshake not attempted (value not shown)");
4276
4383
  }
4277
4384
  const fetchFn = ctx.input.mcpFetch ?? globalThis.fetch;
@@ -4304,7 +4411,7 @@ async function checkMcpHandshake(ctx) {
4304
4411
  init.sessionId
4305
4412
  );
4306
4413
  const tools = toolNames(listed.result);
4307
- const frozen = Boolean(ctx.resolvedPath && existsSync17(join20(ctx.resolvedPath, ".WIKI_GIT_FROZEN")));
4414
+ const frozen = Boolean(ctx.resolvedPath && existsSync17(join21(ctx.resolvedPath, ".WIKI_GIT_FROZEN")));
4308
4415
  if (frozen && !tools.includes(WORKITEM_TOOL)) {
4309
4416
  return handshakeRow(
4310
4417
  "error",
@@ -4377,6 +4484,10 @@ var DoctorRunner = class {
4377
4484
  async run(input) {
4378
4485
  const devSourceRun = isDevSourceRun(input.argv);
4379
4486
  const vsConfig = readVaultSyncConfig(input.home);
4487
+ const baseEnv = input.env ?? process.env;
4488
+ const mergedEnv = await mergeMcpAuthIntoEnv(input.home, baseEnv);
4489
+ const auth = mcpAuthFromEnv(mergedEnv);
4490
+ const inputWithEnv = { ...input, env: mergedEnv };
4380
4491
  const resolved = await resolveRuntimePath({
4381
4492
  flag: void 0,
4382
4493
  envValue: input.envValue,
@@ -4385,9 +4496,14 @@ var DoctorRunner = class {
4385
4496
  });
4386
4497
  const resolvedPath = resolved.ok ? resolved.data.path : void 0;
4387
4498
  const gitCheckPath = vsConfig.role === "snapshotter" ? resolveSnapshotGitWorktree(input.home) ?? resolvedPath : resolvedPath;
4499
+ const mcpOnlyLeaf = isMcpOnlyLeaf({
4500
+ resolvedPath,
4501
+ token: auth.token,
4502
+ vaultSyncInstalled: vsConfig.installed
4503
+ });
4388
4504
  const fleetLoad = resolvedPath ? await loadFleetManifestAndHost({
4389
4505
  vault: resolvedPath,
4390
- env: { ...process.env, WIKI_PATH: input.envValue ?? resolvedPath },
4506
+ env: { ...mergedEnv, WIKI_PATH: input.envValue ?? resolvedPath },
4391
4507
  home: input.home,
4392
4508
  cwd: input.cwd,
4393
4509
  osHostname: process.env.HOSTNAME,
@@ -4396,7 +4512,7 @@ var DoctorRunner = class {
4396
4512
  const satelliteGate = satelliteGateFromFleetLoad(fleetLoad);
4397
4513
  const readOnlyScanRoot = resolvedPath ? doctorReadOnlyScanRoot(resolvedPath) : void 0;
4398
4514
  const ctx = {
4399
- input,
4515
+ input: inputWithEnv,
4400
4516
  devSourceRun,
4401
4517
  vsConfig,
4402
4518
  resolvedPath,
@@ -4404,13 +4520,18 @@ var DoctorRunner = class {
4404
4520
  gitCheckPath,
4405
4521
  fleetLoad,
4406
4522
  readOnlyScanRoot,
4407
- satelliteGate
4523
+ satelliteGate,
4524
+ mcpOnlyLeaf
4408
4525
  };
4409
- const checks = [];
4526
+ const rawChecks = [];
4410
4527
  for (const probe of this.probes) {
4411
4528
  const probeChecks = await probe.run(ctx);
4412
- checks.push(...probeChecks);
4529
+ rawChecks.push(...probeChecks);
4413
4530
  }
4531
+ const checks = rawChecks.map((c) => ({
4532
+ ...c,
4533
+ detail: redactMcpSecret(c.detail, auth.token)
4534
+ }));
4414
4535
  const summary = {
4415
4536
  pass: checks.filter((c) => c.status === "pass").length,
4416
4537
  info: checks.filter((c) => c.status === "info").length,
@@ -4429,7 +4550,7 @@ var DoctorRunner = class {
4429
4550
  if (summary.info > 0) summaryParts.push(`${summary.info} info`);
4430
4551
  summaryParts.push(`${summary.warn} warn`, `${summary.error} error`);
4431
4552
  lines.push(summaryParts.join(" \xB7 "));
4432
- const humanHint = lines.join("\n");
4553
+ const humanHint = redactMcpSecret(lines.join("\n"), auth.token);
4433
4554
  return { exitCode, result: ok({ checks, summary, humanHint }) };
4434
4555
  }
4435
4556
  };
@@ -4461,7 +4582,7 @@ function readCliPackageJson(baseUrl = import.meta.url) {
4461
4582
  // src/utils/vault-write-gates.ts
4462
4583
  import { execFileSync } from "child_process";
4463
4584
  import { existsSync as existsSync19, readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync3 } from "fs";
4464
- import { join as join21, relative as relative2 } from "path";
4585
+ import { join as join22, relative as relative2 } from "path";
4465
4586
  var DEFAULT_DIRTY_VOLUME_THRESHOLD = 50;
4466
4587
  var DEFAULT_CAPTURE_BUDGET = 20;
4467
4588
  var DEFAULT_NO_DECISION_STREAK = 3;
@@ -4517,7 +4638,7 @@ function isEventLedgerPath(rel) {
4517
4638
  return n === "meta/log-events" || n.startsWith("meta/log-events/");
4518
4639
  }
4519
4640
  function countEventLedgerFiles(vault) {
4520
- return listFilesRecursive(join21(vault, "meta", "log-events")).length;
4641
+ return listFilesRecursive(join22(vault, "meta", "log-events")).length;
4521
4642
  }
4522
4643
  function measureDirtyVolume(vault) {
4523
4644
  const empty = (extra = {}) => ({
@@ -4577,7 +4698,7 @@ function measureDirtyVolume(vault) {
4577
4698
  if (!rel) continue;
4578
4699
  if (code === "??") {
4579
4700
  untracked += 1;
4580
- const abs = join21(vault, rel);
4701
+ const abs = join22(vault, rel);
4581
4702
  if (existsSync19(abs) && statSync3(abs).isDirectory()) {
4582
4703
  const files = listFilesRecursive(abs);
4583
4704
  expanded += files.length;
@@ -4621,7 +4742,7 @@ function listFilesRecursive(dir) {
4621
4742
  }
4622
4743
  for (const name of entries) {
4623
4744
  if (name === ".git") continue;
4624
- const p = join21(dir, name);
4745
+ const p = join22(dir, name);
4625
4746
  try {
4626
4747
  const st = statSync3(p);
4627
4748
  if (st.isDirectory()) out.push(...listFilesRecursive(p));
@@ -4752,7 +4873,7 @@ function listProjectDayCaptures(vault, project, day) {
4752
4873
  return;
4753
4874
  }
4754
4875
  for (const name of entries) {
4755
- const abs = join21(dir, name);
4876
+ const abs = join22(dir, name);
4756
4877
  const rel = relBase ? `${relBase}/${name}` : name;
4757
4878
  try {
4758
4879
  const st = statSync3(abs);
@@ -4762,16 +4883,16 @@ function listProjectDayCaptures(vault, project, day) {
4762
4883
  }
4763
4884
  }
4764
4885
  };
4765
- walk(join21(vault, "raw", "transcripts"), "raw/transcripts");
4766
- walk(join21(vault, "projects", slug, "raw", "transcripts"), `projects/${slug}/raw/transcripts`);
4767
- walk(join21(vault, "projects", slug, "requirements"), `projects/${slug}/requirements`);
4768
- const workRoot = join21(vault, "projects", slug, "work");
4886
+ walk(join22(vault, "raw", "transcripts"), "raw/transcripts");
4887
+ walk(join22(vault, "projects", slug, "raw", "transcripts"), `projects/${slug}/raw/transcripts`);
4888
+ walk(join22(vault, "projects", slug, "requirements"), `projects/${slug}/requirements`);
4889
+ const workRoot = join22(vault, "projects", slug, "work");
4769
4890
  if (existsSync19(workRoot)) {
4770
4891
  try {
4771
4892
  for (const name of readdirSync7(workRoot)) {
4772
4893
  if (!name.startsWith(day)) continue;
4773
4894
  if (!/investigate|pilot-q|research|cycle|dev-loop/.test(name)) continue;
4774
- const abs = join21(workRoot, name);
4895
+ const abs = join22(workRoot, name);
4775
4896
  if (statSync3(abs).isDirectory()) {
4776
4897
  for (const f of listFilesRecursive(abs)) {
4777
4898
  const rel = relative2(vault, f).replace(/\\/g, "/");
@@ -4877,7 +4998,7 @@ function runWritePreflight(input) {
4877
4998
  // src/commands/observe.ts
4878
4999
  import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
4879
5000
  import { existsSync as existsSync20, statSync as statSync4 } from "fs";
4880
- import { join as join22 } from "path";
5001
+ import { join as join23 } from "path";
4881
5002
  import { createHash as createHash2 } from "crypto";
4882
5003
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
4883
5004
  function slugify(text) {
@@ -4926,7 +5047,7 @@ async function runObserve(input) {
4926
5047
  };
4927
5048
  }
4928
5049
  }
4929
- const transcriptsDir = join22(input.vault, "raw", "transcripts");
5050
+ const transcriptsDir = join23(input.vault, "raw", "transcripts");
4930
5051
  try {
4931
5052
  await mkdir3(transcriptsDir, { recursive: true });
4932
5053
  } catch {
@@ -4938,7 +5059,7 @@ async function runObserve(input) {
4938
5059
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4939
5060
  const slug = slugify(input.text);
4940
5061
  const fileName = `${today}-observation-${slug}.md`;
4941
- const filePath = join22(transcriptsDir, fileName);
5062
+ const filePath = join23(transcriptsDir, fileName);
4942
5063
  const body = `
4943
5064
  ${input.text.trim()}
4944
5065
  `;
@@ -4980,7 +5101,7 @@ ${input.text.trim()}
4980
5101
  // src/commands/memory.ts
4981
5102
  import { createHash as createHash3 } from "crypto";
4982
5103
  import { mkdir as mkdir4, readFile as readFile7, readdir as readdir2, stat as stat2, writeFile as writeFile3 } from "fs/promises";
4983
- import { basename as basename2, extname, join as join23, relative as relative3, sep as sep2 } from "path";
5104
+ import { basename as basename2, extname, join as join24, relative as relative3, sep as sep2 } from "path";
4984
5105
 
4985
5106
  // src/utils/memory-authority.ts
4986
5107
  var TIER_RANK = {
@@ -5099,8 +5220,8 @@ async function runMemoryIndex(input) {
5099
5220
  }
5100
5221
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
5101
5222
  const relCachePath = memoryCacheRelPath(input.project);
5102
- const absCachePath = join23(input.vault, relCachePath);
5103
- await mkdir4(join23(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
5223
+ const absCachePath = join24(input.vault, relCachePath);
5224
+ await mkdir4(join24(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
5104
5225
  await writeFile3(absCachePath, `${JSON.stringify({
5105
5226
  generated_at: generatedAt,
5106
5227
  project: input.project,
@@ -5292,7 +5413,7 @@ async function buildMemoryIndexState(pages, project) {
5292
5413
  }
5293
5414
  async function checkMemoryIndex(vault, project, current) {
5294
5415
  const relCachePath = memoryCacheRelPath(project);
5295
- const cacheText = await readIfExists(join23(vault, relCachePath));
5416
+ const cacheText = await readIfExists(join24(vault, relCachePath));
5296
5417
  if (!cacheText) {
5297
5418
  return {
5298
5419
  ok: true,
@@ -5701,7 +5822,7 @@ async function walkImportFiles(dir, out) {
5701
5822
  const entries = await readdir2(dir, { withFileTypes: true });
5702
5823
  for (const entry of entries) {
5703
5824
  if (entry.name === ".git" || entry.name === "node_modules") continue;
5704
- const path = join23(dir, entry.name);
5825
+ const path = join24(dir, entry.name);
5705
5826
  if (entry.isDirectory()) {
5706
5827
  await walkImportFiles(path, out);
5707
5828
  } else if (entry.isFile() && isImportCandidate(path)) {
@@ -5768,8 +5889,8 @@ async function writeImportCapture(vault, entry, today) {
5768
5889
  const content = hiddenString(entry, "__content");
5769
5890
  const project = hiddenString(entry, "__project");
5770
5891
  const relPath = await availableImportPath(vault, entry.proposed_path);
5771
- const absPath = join23(vault, relPath);
5772
- await mkdir4(join23(vault, "raw", "transcripts"), { recursive: true });
5892
+ const absPath = join24(vault, relPath);
5893
+ await mkdir4(join24(vault, "raw", "transcripts"), { recursive: true });
5773
5894
  await writeFile3(absPath, renderImportCapture(entry, content, project, today), "utf8");
5774
5895
  const validation = await runValidate({ file: absPath });
5775
5896
  return {
@@ -5785,7 +5906,7 @@ async function availableImportPath(vault, proposed) {
5785
5906
  const stem = proposed.slice(0, -ext.length);
5786
5907
  let candidate = proposed;
5787
5908
  let i = 2;
5788
- while (await readIfExists(join23(vault, candidate))) {
5909
+ while (await readIfExists(join24(vault, candidate))) {
5789
5910
  candidate = `${stem}-${i}${ext}`;
5790
5911
  i++;
5791
5912
  }
@@ -5970,10 +6091,10 @@ function memoryCacheRelPath(project) {
5970
6091
  }
5971
6092
  async function readMemoryCache(vault, project) {
5972
6093
  if (project) {
5973
- const projectCache = await readIfExists(join23(vault, memoryCacheRelPath(project)));
6094
+ const projectCache = await readIfExists(join24(vault, memoryCacheRelPath(project)));
5974
6095
  if (projectCache) return projectCache;
5975
6096
  }
5976
- return readIfExists(join23(vault, ".skillwiki", "memory-topics.json"));
6097
+ return readIfExists(join24(vault, ".skillwiki", "memory-topics.json"));
5977
6098
  }
5978
6099
  function dedupePages(pages) {
5979
6100
  const seen = /* @__PURE__ */ new Set();
@@ -6065,7 +6186,7 @@ function slugify2(value) {
6065
6186
 
6066
6187
  // src/commands/query.ts
6067
6188
  import { readFile as readFile9, stat as stat4 } from "fs/promises";
6068
- import { join as join25 } from "path";
6189
+ import { join as join26 } from "path";
6069
6190
 
6070
6191
  // src/utils/rrf.ts
6071
6192
  var RRF_K = 60;
@@ -6081,7 +6202,7 @@ function fuseRankings(lists, k = RRF_K) {
6081
6202
 
6082
6203
  // src/utils/vector-index.ts
6083
6204
  import { mkdir as mkdir5, readFile as readFile8, stat as stat3 } from "fs/promises";
6084
- import { join as join24 } from "path";
6205
+ import { join as join25 } from "path";
6085
6206
  import { existsSync as existsSync21 } from "fs";
6086
6207
  var VECTOR_INDEX_REL = ".skillwiki/vectors/index.json";
6087
6208
  var VECTOR_INDEX_SCHEMA = "skillwiki-tfidf-index/v1";
@@ -6096,7 +6217,7 @@ function extractPageTokens(text) {
6096
6217
  return tokenize(`${title} ${body}`);
6097
6218
  }
6098
6219
  function vectorIndexPath(vault) {
6099
- return join24(vault, ...VECTOR_INDEX_REL.split("/"));
6220
+ return join25(vault, ...VECTOR_INDEX_REL.split("/"));
6100
6221
  }
6101
6222
  function termFreq(tokens) {
6102
6223
  const tf = /* @__PURE__ */ new Map();
@@ -6151,7 +6272,7 @@ async function buildVectorIndex(vault, now = (/* @__PURE__ */ new Date()).toISOS
6151
6272
  docs
6152
6273
  };
6153
6274
  const dest = vectorIndexPath(vault);
6154
- await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
6275
+ await mkdir5(join25(vault, ".skillwiki", "vectors"), { recursive: true });
6155
6276
  await atomicWriteText(dest, `${JSON.stringify(index)}
6156
6277
  `);
6157
6278
  return ok(index);
@@ -6194,7 +6315,7 @@ async function reindexPageInVectorIndex(vault, pageRelPath, now = (/* @__PURE__
6194
6315
  if (!normalizedPage.endsWith(".md")) {
6195
6316
  return err("USAGE", { message: `Page must be a .md file: ${pageRelPath}` });
6196
6317
  }
6197
- const absPath = join24(vault, ...normalizedPage.split("/"));
6318
+ const absPath = join25(vault, ...normalizedPage.split("/"));
6198
6319
  let text;
6199
6320
  try {
6200
6321
  text = await readFile8(absPath, "utf8");
@@ -6279,7 +6400,7 @@ async function reindexPageInVectorIndex(vault, pageRelPath, now = (/* @__PURE__
6279
6400
  }
6280
6401
  }
6281
6402
  const dest = vectorIndexPath(vault);
6282
- await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
6403
+ await mkdir5(join25(vault, ".skillwiki", "vectors"), { recursive: true });
6283
6404
  await atomicWriteText(dest, `${JSON.stringify(index)}
6284
6405
  `);
6285
6406
  return ok({
@@ -6297,7 +6418,7 @@ async function pruneVectorIndex(vault, opts) {
6297
6418
  const orphans = [];
6298
6419
  for (const docKey of Object.keys(index.docs)) {
6299
6420
  const normalizedKey = docKey.split(/\\|\//).join("/");
6300
- const absPath = join24(vault, ...normalizedKey.split("/"));
6421
+ const absPath = join25(vault, ...normalizedKey.split("/"));
6301
6422
  if (!existsSync21(absPath)) {
6302
6423
  orphans.push(normalizedKey);
6303
6424
  }
@@ -6344,7 +6465,7 @@ async function pruneVectorIndex(vault, opts) {
6344
6465
  index.docs[docKey] = toTfIdf(termFreq(tokens), index.df, newPageCount);
6345
6466
  } else {
6346
6467
  try {
6347
- const text = await readFile8(join24(vault, ...docKey.split("/")), "utf8");
6468
+ const text = await readFile8(join25(vault, ...docKey.split("/")), "utf8");
6348
6469
  const tokens = extractPageTokens(text);
6349
6470
  index.docs[docKey] = toTfIdf(termFreq(tokens), index.df, newPageCount);
6350
6471
  } catch {
@@ -6354,7 +6475,7 @@ async function pruneVectorIndex(vault, opts) {
6354
6475
  }
6355
6476
  if (!opts?.dryRun) {
6356
6477
  const dest = vectorIndexPath(vault);
6357
- await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
6478
+ await mkdir5(join25(vault, ".skillwiki", "vectors"), { recursive: true });
6358
6479
  await atomicWriteText(dest, `${JSON.stringify(index)}
6359
6480
  `);
6360
6481
  }
@@ -6655,7 +6776,7 @@ function computeKeywordScore(terms, title, tags, body, relPath) {
6655
6776
  return score;
6656
6777
  }
6657
6778
  async function loadOrBuildGraph(vault) {
6658
- const graphPath = join25(vault, ".skillwiki", "graph.json");
6779
+ const graphPath = join26(vault, ".skillwiki", "graph.json");
6659
6780
  let needsBuild = false;
6660
6781
  try {
6661
6782
  const fileStat = await stat4(graphPath);
@@ -6744,7 +6865,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6744
6865
  import { z } from "zod";
6745
6866
 
6746
6867
  // src/mcp/vault-resolve.ts
6747
- import { join as join26, resolve as resolve4 } from "path";
6868
+ import { join as join27, resolve as resolve4 } from "path";
6748
6869
 
6749
6870
  // src/mcp/allowlist.ts
6750
6871
  import { resolve as resolve3, sep as sep3 } from "path";
@@ -6806,7 +6927,7 @@ async function resolveMcpVault(input) {
6806
6927
  return ok({ vault: vaultPath, source });
6807
6928
  }
6808
6929
  function defaultGraphOut(vault) {
6809
- return join26(vault, ".skillwiki", "graph.json");
6930
+ return join27(vault, ".skillwiki", "graph.json");
6810
6931
  }
6811
6932
 
6812
6933
  // src/mcp/result-format.ts
@@ -6823,7 +6944,7 @@ function formatToolResult(payload) {
6823
6944
  // src/mcp/audit-log.ts
6824
6945
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
6825
6946
  import { homedir } from "os";
6826
- import { join as join27 } from "path";
6947
+ import { join as join28 } from "path";
6827
6948
  function auditEnabled() {
6828
6949
  const v = process.env.SKILLWIKI_MCP_AUDIT;
6829
6950
  if (v === "0" || v === "false") return false;
@@ -6835,7 +6956,7 @@ function auditSink() {
6835
6956
  function auditFilePath() {
6836
6957
  const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
6837
6958
  if (custom && custom.length > 0) return custom;
6838
- return join27(homedir(), ".skillwiki", "mcp-audit.jsonl");
6959
+ return join28(homedir(), ".skillwiki", "mcp-audit.jsonl");
6839
6960
  }
6840
6961
  function auditMcpToolCall(entry) {
6841
6962
  if (!auditEnabled()) return;
@@ -6845,7 +6966,7 @@ function auditMcpToolCall(entry) {
6845
6966
  return;
6846
6967
  }
6847
6968
  const path = auditFilePath();
6848
- mkdirSync2(join27(path, ".."), { recursive: true });
6969
+ mkdirSync2(join28(path, ".."), { recursive: true });
6849
6970
  appendFileSync(path, line, "utf8");
6850
6971
  }
6851
6972
  async function runMcpToolHandler(tool, input, fn) {
@@ -7120,7 +7241,7 @@ function registerMcpMutatingTools(server) {
7120
7241
 
7121
7242
  // src/mcp/resources.ts
7122
7243
  import { readFile as readFile11 } from "fs/promises";
7123
- import { join as join29 } from "path";
7244
+ import { join as join30 } from "path";
7124
7245
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7125
7246
 
7126
7247
  // src/mcp/lint-bucket.ts
@@ -7250,7 +7371,7 @@ async function fetchQueryPreview(input) {
7250
7371
 
7251
7372
  // src/mcp/graph-html.ts
7252
7373
  import { readFile as readFile10 } from "fs/promises";
7253
- import { join as join28 } from "path";
7374
+ import { join as join29 } from "path";
7254
7375
  import { existsSync as existsSync22 } from "fs";
7255
7376
  var TYPE_COLORS = {
7256
7377
  entities: "#e74c3c",
@@ -7319,7 +7440,7 @@ ${nodeSvg}
7319
7440
  return { html, node_count: nodes.length, edge_count: edges.length, truncated };
7320
7441
  }
7321
7442
  async function fetchGraphHtmlReport(input) {
7322
- const graphPath = input.graphPath ?? join28(input.vault, ".skillwiki", "graph.json");
7443
+ const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
7323
7444
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
7324
7445
  if (!existsSync22(graphPath)) {
7325
7446
  return {
@@ -7393,7 +7514,7 @@ async function fetchStaleSummary(input) {
7393
7514
 
7394
7515
  // src/mcp/resources.ts
7395
7516
  async function readVaultFile(vault, rel) {
7396
- return readFile11(join29(vault, rel), "utf8");
7517
+ return readFile11(join30(vault, rel), "utf8");
7397
7518
  }
7398
7519
  async function tailLines(text, lines) {
7399
7520
  const parts = text.split(/\r?\n/);
@@ -7479,7 +7600,7 @@ function registerMcpResources(server) {
7479
7600
  if (!v.ok) {
7480
7601
  return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
7481
7602
  }
7482
- const path = join29(v.data.vault, ".skillwiki", "graph.json");
7603
+ const path = join30(v.data.vault, ".skillwiki", "graph.json");
7483
7604
  try {
7484
7605
  const raw = await readFile11(path, "utf8");
7485
7606
  const graph = JSON.parse(raw);
@@ -7830,6 +7951,12 @@ export {
7830
7951
  renderProjectIndex,
7831
7952
  runProjectIndex,
7832
7953
  scanConflictMarkerBlocksInText,
7954
+ MCP_URL_ENV,
7955
+ MCP_TOKEN_ENV,
7956
+ MCP_HOST_ID_ENV,
7957
+ nonEmptyEnvValue,
7958
+ mergeMcpAuthIntoEnv,
7959
+ redactMcpSecret,
7833
7960
  configPath,
7834
7961
  runConfigGet,
7835
7962
  runConfigSet,
@@ -7843,7 +7970,9 @@ export {
7843
7970
  satelliteLatestRunPath,
7844
7971
  isFailedRunStatus,
7845
7972
  readSatelliteLatestRunFromText,
7846
- evaluateSatelliteRunHealth,
7973
+ readSatelliteLatestRunAt,
7974
+ evaluateSatelliteRunHealthState,
7975
+ DEFAULT_MCP_URL,
7847
7976
  runDoctor,
7848
7977
  readCliPackageJson,
7849
7978
  DEFAULT_DIRTY_VOLUME_THRESHOLD,