skillwiki 0.10.100 → 0.10.102

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-7UK67FAZ.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";
@@ -3813,7 +3918,7 @@ var satelliteProbe = {
3813
3918
 
3814
3919
  // src/doctor/probes/metrics.ts
3815
3920
  import { readFileSync as readFileSync10 } from "fs";
3816
- import { join as join17 } from "path";
3921
+ import { join as join18 } from "path";
3817
3922
  var METRIC_TYPES = ["entities", "concepts", "comparisons", "queries", "meta"];
3818
3923
  function doctorReadOnlyScanRoot(resolvedPath) {
3819
3924
  return resolveReadOnlyVaultRoot(resolvedPath).root;
@@ -3857,7 +3962,7 @@ async function vaultMetrics(resolvedPath) {
3857
3962
  }
3858
3963
  let logLines = 0;
3859
3964
  try {
3860
- logLines = readFileSync10(join17(scanRoot, "log.md"), "utf8").split("\n").length;
3965
+ logLines = readFileSync10(join18(scanRoot, "log.md"), "utf8").split("\n").length;
3861
3966
  } catch {
3862
3967
  }
3863
3968
  return [
@@ -3993,18 +4098,18 @@ var fuseStalenessProbe = {
3993
4098
 
3994
4099
  // src/doctor/probes/activation-marker.ts
3995
4100
  import { existsSync as existsSync15, readFileSync as readFileSync11, readdirSync as readdirSync5 } from "fs";
3996
- import { join as join18 } from "path";
4101
+ import { join as join19 } from "path";
3997
4102
  var GROK_ACTIVATION_REFERENCE2 = "Read @~/.grok/skillwiki.md for SkillWiki activation context.";
3998
4103
  var STALE_GROK_ACTIVATION_REFERENCE2 = "Read @skillwiki.md";
3999
4104
  var ACTIVATION_FIX_HINT2 = "run `npm run install:activation` from the llm-wiki repo";
4000
4105
  function findGrokActivationTemplate2(home, cwd) {
4001
4106
  if (cwd) {
4002
- const src = join18(cwd, "packages", "skills", "using-skillwiki", "activation.md");
4107
+ const src = join19(cwd, "packages", "skills", "using-skillwiki", "activation.md");
4003
4108
  if (existsSync15(src)) return src;
4004
- const directSrc = join18(cwd, "skills", "using-skillwiki", "activation.md");
4109
+ const directSrc = join19(cwd, "skills", "using-skillwiki", "activation.md");
4005
4110
  if (existsSync15(directSrc)) return directSrc;
4006
4111
  }
4007
- const pluginsRoot = join18(home, ".grok", "installed-plugins");
4112
+ const pluginsRoot = join19(home, ".grok", "installed-plugins");
4008
4113
  if (!existsSync15(pluginsRoot)) return void 0;
4009
4114
  let entries;
4010
4115
  try {
@@ -4014,21 +4119,21 @@ function findGrokActivationTemplate2(home, cwd) {
4014
4119
  }
4015
4120
  for (const entry of entries) {
4016
4121
  if (!entry.isDirectory()) continue;
4017
- const root = join18(pluginsRoot, entry.name);
4122
+ const root = join19(pluginsRoot, entry.name);
4018
4123
  for (const rel of ["using-skillwiki/activation.md", "skills/using-skillwiki/activation.md"]) {
4019
- const candidate = join18(root, rel);
4124
+ const candidate = join19(root, rel);
4020
4125
  if (existsSync15(candidate)) return candidate;
4021
4126
  }
4022
4127
  }
4023
4128
  return void 0;
4024
4129
  }
4025
4130
  function checkActivationMarker(home, cwd) {
4026
- const grokDir = join18(home, ".grok");
4131
+ const grokDir = join19(home, ".grok");
4027
4132
  if (!existsSync15(grokDir)) {
4028
4133
  return check("pass", "activation_marker", "Activation marker", "Not a Grok host \u2014 check skipped");
4029
4134
  }
4030
- const activationPath = join18(grokDir, "skillwiki.md");
4031
- const agentsPath = join18(grokDir, "AGENTS.md");
4135
+ const activationPath = join19(grokDir, "skillwiki.md");
4136
+ const agentsPath = join19(grokDir, "AGENTS.md");
4032
4137
  const hasActivation = existsSync15(activationPath);
4033
4138
  const issues = [];
4034
4139
  if (!hasActivation) {
@@ -4085,7 +4190,7 @@ var activationMarkerProbe = {
4085
4190
 
4086
4191
  // src/doctor/probes/ds-store-noise.ts
4087
4192
  import { existsSync as existsSync16, readdirSync as readdirSync6 } from "fs";
4088
- import { join as join19 } from "path";
4193
+ import { join as join20 } from "path";
4089
4194
  var VAULT_TRACKED_DIRS = ["raw", "entities", "concepts", "comparisons", "queries", "meta", "_archive", "_Templates"];
4090
4195
  function checkDsStoreNoise(resolvedPath) {
4091
4196
  if (resolvedPath === void 0) {
@@ -4107,13 +4212,13 @@ function checkDsStoreNoise(resolvedPath) {
4107
4212
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
4108
4213
  } else if (entry.isDirectory()) {
4109
4214
  if (entry.name === ".git" || entry.name === "node_modules") continue;
4110
- walk(join19(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
4215
+ walk(join20(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
4111
4216
  }
4112
4217
  }
4113
4218
  }
4114
4219
  let scannedAny = false;
4115
4220
  for (const sub of VAULT_TRACKED_DIRS) {
4116
- const subDir = join19(resolvedPath, sub);
4221
+ const subDir = join20(resolvedPath, sub);
4117
4222
  if (existsSync16(subDir)) {
4118
4223
  scannedAny = true;
4119
4224
  walk(subDir, sub);
@@ -4143,17 +4248,14 @@ var dsStoreNoiseProbe = {
4143
4248
 
4144
4249
  // src/doctor/probes/mcp-handshake.ts
4145
4250
  import { existsSync as existsSync17 } from "fs";
4146
- import { join as join20 } from "path";
4251
+ import { join as join21 } from "path";
4147
4252
  var DEFAULT_MCP_URL = "https://wiki.karldigi.dev/mcp";
4148
- var URL_ENV = "SKILLWIKI_MCP_URL";
4149
- var TOKEN_ENV = "SKILLWIKI_MCP_TOKEN";
4150
4253
  function mcpEnv(ctx) {
4151
4254
  return ctx.input.env ?? process.env;
4152
4255
  }
4153
4256
  function checkMcpUrl(ctx) {
4154
- const env = mcpEnv(ctx);
4155
- const override = env[URL_ENV];
4156
- if (typeof override === "string" && override.trim().length > 0) {
4257
+ const creds = mcpAuthFromEnv(mcpEnv(ctx));
4258
+ if (creds.url) {
4157
4259
  return check("pass", "mcp_url_configured", "HTTP MCP URL", "configured via env override");
4158
4260
  }
4159
4261
  return check(
@@ -4164,21 +4266,20 @@ function checkMcpUrl(ctx) {
4164
4266
  );
4165
4267
  }
4166
4268
  function checkMcpCredential(ctx) {
4167
- const env = mcpEnv(ctx);
4168
- const value = env[TOKEN_ENV];
4169
- if (typeof value === "string" && value.trim().length > 0) {
4269
+ const creds = mcpAuthFromEnv(mcpEnv(ctx));
4270
+ if (creds.token) {
4170
4271
  return check("pass", "mcp_credential_present", "HTTP MCP auth", "present");
4171
4272
  }
4172
4273
  return check(
4173
4274
  "warn",
4174
4275
  "mcp_credential_present",
4175
4276
  "HTTP MCP auth",
4176
- "not set \u2014 set in process env or host Configure (value not shown)"
4277
+ "not set \u2014 set in process env, ~/.skillwiki/.env, or host Configure (value not shown)"
4177
4278
  );
4178
4279
  }
4179
4280
  function checkFrozenLeafWritePath(ctx) {
4180
4281
  const vault = ctx.resolvedPath;
4181
- if (!vault || !existsSync17(join20(vault, ".WIKI_GIT_FROZEN"))) {
4282
+ if (!vault || !existsSync17(join21(vault, ".WIKI_GIT_FROZEN"))) {
4182
4283
  return check(
4183
4284
  "pass",
4184
4285
  "mcp_frozen_leaf_write_path",
@@ -4210,8 +4311,7 @@ function versionLessThan(a, b) {
4210
4311
  return pa.patch < pb.patch;
4211
4312
  }
4212
4313
  function redactSecret(detail, secret) {
4213
- if (!secret || secret.length === 0 || !detail.includes(secret)) return detail;
4214
- return detail.split(secret).join("[REDACTED]");
4314
+ return redactMcpSecret(detail, secret);
4215
4315
  }
4216
4316
  function handshakeRow(status, detail, secret) {
4217
4317
  return check(status, HANDSHAKE_ID, HANDSHAKE_LABEL, redactSecret(detail, secret));
@@ -4269,9 +4369,10 @@ async function checkMcpHandshake(ctx) {
4269
4369
  return handshakeRow("pass", "not requested \u2014 check skipped");
4270
4370
  }
4271
4371
  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) {
4372
+ const creds = mcpAuthFromEnv(env);
4373
+ const url = creds.url ?? DEFAULT_MCP_URL;
4374
+ const token = creds.token ?? "";
4375
+ if (token.length === 0) {
4275
4376
  return handshakeRow("error", "auth not set \u2014 handshake not attempted (value not shown)");
4276
4377
  }
4277
4378
  const fetchFn = ctx.input.mcpFetch ?? globalThis.fetch;
@@ -4304,7 +4405,7 @@ async function checkMcpHandshake(ctx) {
4304
4405
  init.sessionId
4305
4406
  );
4306
4407
  const tools = toolNames(listed.result);
4307
- const frozen = Boolean(ctx.resolvedPath && existsSync17(join20(ctx.resolvedPath, ".WIKI_GIT_FROZEN")));
4408
+ const frozen = Boolean(ctx.resolvedPath && existsSync17(join21(ctx.resolvedPath, ".WIKI_GIT_FROZEN")));
4308
4409
  if (frozen && !tools.includes(WORKITEM_TOOL)) {
4309
4410
  return handshakeRow(
4310
4411
  "error",
@@ -4377,6 +4478,10 @@ var DoctorRunner = class {
4377
4478
  async run(input) {
4378
4479
  const devSourceRun = isDevSourceRun(input.argv);
4379
4480
  const vsConfig = readVaultSyncConfig(input.home);
4481
+ const baseEnv = input.env ?? process.env;
4482
+ const mergedEnv = await mergeMcpAuthIntoEnv(input.home, baseEnv);
4483
+ const auth = mcpAuthFromEnv(mergedEnv);
4484
+ const inputWithEnv = { ...input, env: mergedEnv };
4380
4485
  const resolved = await resolveRuntimePath({
4381
4486
  flag: void 0,
4382
4487
  envValue: input.envValue,
@@ -4385,9 +4490,14 @@ var DoctorRunner = class {
4385
4490
  });
4386
4491
  const resolvedPath = resolved.ok ? resolved.data.path : void 0;
4387
4492
  const gitCheckPath = vsConfig.role === "snapshotter" ? resolveSnapshotGitWorktree(input.home) ?? resolvedPath : resolvedPath;
4493
+ const mcpOnlyLeaf = isMcpOnlyLeaf({
4494
+ resolvedPath,
4495
+ token: auth.token,
4496
+ vaultSyncInstalled: vsConfig.installed
4497
+ });
4388
4498
  const fleetLoad = resolvedPath ? await loadFleetManifestAndHost({
4389
4499
  vault: resolvedPath,
4390
- env: { ...process.env, WIKI_PATH: input.envValue ?? resolvedPath },
4500
+ env: { ...mergedEnv, WIKI_PATH: input.envValue ?? resolvedPath },
4391
4501
  home: input.home,
4392
4502
  cwd: input.cwd,
4393
4503
  osHostname: process.env.HOSTNAME,
@@ -4396,7 +4506,7 @@ var DoctorRunner = class {
4396
4506
  const satelliteGate = satelliteGateFromFleetLoad(fleetLoad);
4397
4507
  const readOnlyScanRoot = resolvedPath ? doctorReadOnlyScanRoot(resolvedPath) : void 0;
4398
4508
  const ctx = {
4399
- input,
4509
+ input: inputWithEnv,
4400
4510
  devSourceRun,
4401
4511
  vsConfig,
4402
4512
  resolvedPath,
@@ -4404,13 +4514,18 @@ var DoctorRunner = class {
4404
4514
  gitCheckPath,
4405
4515
  fleetLoad,
4406
4516
  readOnlyScanRoot,
4407
- satelliteGate
4517
+ satelliteGate,
4518
+ mcpOnlyLeaf
4408
4519
  };
4409
- const checks = [];
4520
+ const rawChecks = [];
4410
4521
  for (const probe of this.probes) {
4411
4522
  const probeChecks = await probe.run(ctx);
4412
- checks.push(...probeChecks);
4523
+ rawChecks.push(...probeChecks);
4413
4524
  }
4525
+ const checks = rawChecks.map((c) => ({
4526
+ ...c,
4527
+ detail: redactMcpSecret(c.detail, auth.token)
4528
+ }));
4414
4529
  const summary = {
4415
4530
  pass: checks.filter((c) => c.status === "pass").length,
4416
4531
  info: checks.filter((c) => c.status === "info").length,
@@ -4429,7 +4544,7 @@ var DoctorRunner = class {
4429
4544
  if (summary.info > 0) summaryParts.push(`${summary.info} info`);
4430
4545
  summaryParts.push(`${summary.warn} warn`, `${summary.error} error`);
4431
4546
  lines.push(summaryParts.join(" \xB7 "));
4432
- const humanHint = lines.join("\n");
4547
+ const humanHint = redactMcpSecret(lines.join("\n"), auth.token);
4433
4548
  return { exitCode, result: ok({ checks, summary, humanHint }) };
4434
4549
  }
4435
4550
  };
@@ -4461,7 +4576,7 @@ function readCliPackageJson(baseUrl = import.meta.url) {
4461
4576
  // src/utils/vault-write-gates.ts
4462
4577
  import { execFileSync } from "child_process";
4463
4578
  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";
4579
+ import { join as join22, relative as relative2 } from "path";
4465
4580
  var DEFAULT_DIRTY_VOLUME_THRESHOLD = 50;
4466
4581
  var DEFAULT_CAPTURE_BUDGET = 20;
4467
4582
  var DEFAULT_NO_DECISION_STREAK = 3;
@@ -4517,7 +4632,7 @@ function isEventLedgerPath(rel) {
4517
4632
  return n === "meta/log-events" || n.startsWith("meta/log-events/");
4518
4633
  }
4519
4634
  function countEventLedgerFiles(vault) {
4520
- return listFilesRecursive(join21(vault, "meta", "log-events")).length;
4635
+ return listFilesRecursive(join22(vault, "meta", "log-events")).length;
4521
4636
  }
4522
4637
  function measureDirtyVolume(vault) {
4523
4638
  const empty = (extra = {}) => ({
@@ -4577,7 +4692,7 @@ function measureDirtyVolume(vault) {
4577
4692
  if (!rel) continue;
4578
4693
  if (code === "??") {
4579
4694
  untracked += 1;
4580
- const abs = join21(vault, rel);
4695
+ const abs = join22(vault, rel);
4581
4696
  if (existsSync19(abs) && statSync3(abs).isDirectory()) {
4582
4697
  const files = listFilesRecursive(abs);
4583
4698
  expanded += files.length;
@@ -4621,7 +4736,7 @@ function listFilesRecursive(dir) {
4621
4736
  }
4622
4737
  for (const name of entries) {
4623
4738
  if (name === ".git") continue;
4624
- const p = join21(dir, name);
4739
+ const p = join22(dir, name);
4625
4740
  try {
4626
4741
  const st = statSync3(p);
4627
4742
  if (st.isDirectory()) out.push(...listFilesRecursive(p));
@@ -4752,7 +4867,7 @@ function listProjectDayCaptures(vault, project, day) {
4752
4867
  return;
4753
4868
  }
4754
4869
  for (const name of entries) {
4755
- const abs = join21(dir, name);
4870
+ const abs = join22(dir, name);
4756
4871
  const rel = relBase ? `${relBase}/${name}` : name;
4757
4872
  try {
4758
4873
  const st = statSync3(abs);
@@ -4762,16 +4877,16 @@ function listProjectDayCaptures(vault, project, day) {
4762
4877
  }
4763
4878
  }
4764
4879
  };
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");
4880
+ walk(join22(vault, "raw", "transcripts"), "raw/transcripts");
4881
+ walk(join22(vault, "projects", slug, "raw", "transcripts"), `projects/${slug}/raw/transcripts`);
4882
+ walk(join22(vault, "projects", slug, "requirements"), `projects/${slug}/requirements`);
4883
+ const workRoot = join22(vault, "projects", slug, "work");
4769
4884
  if (existsSync19(workRoot)) {
4770
4885
  try {
4771
4886
  for (const name of readdirSync7(workRoot)) {
4772
4887
  if (!name.startsWith(day)) continue;
4773
4888
  if (!/investigate|pilot-q|research|cycle|dev-loop/.test(name)) continue;
4774
- const abs = join21(workRoot, name);
4889
+ const abs = join22(workRoot, name);
4775
4890
  if (statSync3(abs).isDirectory()) {
4776
4891
  for (const f of listFilesRecursive(abs)) {
4777
4892
  const rel = relative2(vault, f).replace(/\\/g, "/");
@@ -4877,7 +4992,7 @@ function runWritePreflight(input) {
4877
4992
  // src/commands/observe.ts
4878
4993
  import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
4879
4994
  import { existsSync as existsSync20, statSync as statSync4 } from "fs";
4880
- import { join as join22 } from "path";
4995
+ import { join as join23 } from "path";
4881
4996
  import { createHash as createHash2 } from "crypto";
4882
4997
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
4883
4998
  function slugify(text) {
@@ -4926,7 +5041,7 @@ async function runObserve(input) {
4926
5041
  };
4927
5042
  }
4928
5043
  }
4929
- const transcriptsDir = join22(input.vault, "raw", "transcripts");
5044
+ const transcriptsDir = join23(input.vault, "raw", "transcripts");
4930
5045
  try {
4931
5046
  await mkdir3(transcriptsDir, { recursive: true });
4932
5047
  } catch {
@@ -4938,7 +5053,7 @@ async function runObserve(input) {
4938
5053
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4939
5054
  const slug = slugify(input.text);
4940
5055
  const fileName = `${today}-observation-${slug}.md`;
4941
- const filePath = join22(transcriptsDir, fileName);
5056
+ const filePath = join23(transcriptsDir, fileName);
4942
5057
  const body = `
4943
5058
  ${input.text.trim()}
4944
5059
  `;
@@ -4980,7 +5095,7 @@ ${input.text.trim()}
4980
5095
  // src/commands/memory.ts
4981
5096
  import { createHash as createHash3 } from "crypto";
4982
5097
  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";
5098
+ import { basename as basename2, extname, join as join24, relative as relative3, sep as sep2 } from "path";
4984
5099
 
4985
5100
  // src/utils/memory-authority.ts
4986
5101
  var TIER_RANK = {
@@ -5099,8 +5214,8 @@ async function runMemoryIndex(input) {
5099
5214
  }
5100
5215
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
5101
5216
  const relCachePath = memoryCacheRelPath(input.project);
5102
- const absCachePath = join23(input.vault, relCachePath);
5103
- await mkdir4(join23(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
5217
+ const absCachePath = join24(input.vault, relCachePath);
5218
+ await mkdir4(join24(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
5104
5219
  await writeFile3(absCachePath, `${JSON.stringify({
5105
5220
  generated_at: generatedAt,
5106
5221
  project: input.project,
@@ -5292,7 +5407,7 @@ async function buildMemoryIndexState(pages, project) {
5292
5407
  }
5293
5408
  async function checkMemoryIndex(vault, project, current) {
5294
5409
  const relCachePath = memoryCacheRelPath(project);
5295
- const cacheText = await readIfExists(join23(vault, relCachePath));
5410
+ const cacheText = await readIfExists(join24(vault, relCachePath));
5296
5411
  if (!cacheText) {
5297
5412
  return {
5298
5413
  ok: true,
@@ -5701,7 +5816,7 @@ async function walkImportFiles(dir, out) {
5701
5816
  const entries = await readdir2(dir, { withFileTypes: true });
5702
5817
  for (const entry of entries) {
5703
5818
  if (entry.name === ".git" || entry.name === "node_modules") continue;
5704
- const path = join23(dir, entry.name);
5819
+ const path = join24(dir, entry.name);
5705
5820
  if (entry.isDirectory()) {
5706
5821
  await walkImportFiles(path, out);
5707
5822
  } else if (entry.isFile() && isImportCandidate(path)) {
@@ -5768,8 +5883,8 @@ async function writeImportCapture(vault, entry, today) {
5768
5883
  const content = hiddenString(entry, "__content");
5769
5884
  const project = hiddenString(entry, "__project");
5770
5885
  const relPath = await availableImportPath(vault, entry.proposed_path);
5771
- const absPath = join23(vault, relPath);
5772
- await mkdir4(join23(vault, "raw", "transcripts"), { recursive: true });
5886
+ const absPath = join24(vault, relPath);
5887
+ await mkdir4(join24(vault, "raw", "transcripts"), { recursive: true });
5773
5888
  await writeFile3(absPath, renderImportCapture(entry, content, project, today), "utf8");
5774
5889
  const validation = await runValidate({ file: absPath });
5775
5890
  return {
@@ -5785,7 +5900,7 @@ async function availableImportPath(vault, proposed) {
5785
5900
  const stem = proposed.slice(0, -ext.length);
5786
5901
  let candidate = proposed;
5787
5902
  let i = 2;
5788
- while (await readIfExists(join23(vault, candidate))) {
5903
+ while (await readIfExists(join24(vault, candidate))) {
5789
5904
  candidate = `${stem}-${i}${ext}`;
5790
5905
  i++;
5791
5906
  }
@@ -5970,10 +6085,10 @@ function memoryCacheRelPath(project) {
5970
6085
  }
5971
6086
  async function readMemoryCache(vault, project) {
5972
6087
  if (project) {
5973
- const projectCache = await readIfExists(join23(vault, memoryCacheRelPath(project)));
6088
+ const projectCache = await readIfExists(join24(vault, memoryCacheRelPath(project)));
5974
6089
  if (projectCache) return projectCache;
5975
6090
  }
5976
- return readIfExists(join23(vault, ".skillwiki", "memory-topics.json"));
6091
+ return readIfExists(join24(vault, ".skillwiki", "memory-topics.json"));
5977
6092
  }
5978
6093
  function dedupePages(pages) {
5979
6094
  const seen = /* @__PURE__ */ new Set();
@@ -6065,7 +6180,7 @@ function slugify2(value) {
6065
6180
 
6066
6181
  // src/commands/query.ts
6067
6182
  import { readFile as readFile9, stat as stat4 } from "fs/promises";
6068
- import { join as join25 } from "path";
6183
+ import { join as join26 } from "path";
6069
6184
 
6070
6185
  // src/utils/rrf.ts
6071
6186
  var RRF_K = 60;
@@ -6081,7 +6196,7 @@ function fuseRankings(lists, k = RRF_K) {
6081
6196
 
6082
6197
  // src/utils/vector-index.ts
6083
6198
  import { mkdir as mkdir5, readFile as readFile8, stat as stat3 } from "fs/promises";
6084
- import { join as join24 } from "path";
6199
+ import { join as join25 } from "path";
6085
6200
  import { existsSync as existsSync21 } from "fs";
6086
6201
  var VECTOR_INDEX_REL = ".skillwiki/vectors/index.json";
6087
6202
  var VECTOR_INDEX_SCHEMA = "skillwiki-tfidf-index/v1";
@@ -6096,7 +6211,7 @@ function extractPageTokens(text) {
6096
6211
  return tokenize(`${title} ${body}`);
6097
6212
  }
6098
6213
  function vectorIndexPath(vault) {
6099
- return join24(vault, ...VECTOR_INDEX_REL.split("/"));
6214
+ return join25(vault, ...VECTOR_INDEX_REL.split("/"));
6100
6215
  }
6101
6216
  function termFreq(tokens) {
6102
6217
  const tf = /* @__PURE__ */ new Map();
@@ -6151,7 +6266,7 @@ async function buildVectorIndex(vault, now = (/* @__PURE__ */ new Date()).toISOS
6151
6266
  docs
6152
6267
  };
6153
6268
  const dest = vectorIndexPath(vault);
6154
- await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
6269
+ await mkdir5(join25(vault, ".skillwiki", "vectors"), { recursive: true });
6155
6270
  await atomicWriteText(dest, `${JSON.stringify(index)}
6156
6271
  `);
6157
6272
  return ok(index);
@@ -6194,7 +6309,7 @@ async function reindexPageInVectorIndex(vault, pageRelPath, now = (/* @__PURE__
6194
6309
  if (!normalizedPage.endsWith(".md")) {
6195
6310
  return err("USAGE", { message: `Page must be a .md file: ${pageRelPath}` });
6196
6311
  }
6197
- const absPath = join24(vault, ...normalizedPage.split("/"));
6312
+ const absPath = join25(vault, ...normalizedPage.split("/"));
6198
6313
  let text;
6199
6314
  try {
6200
6315
  text = await readFile8(absPath, "utf8");
@@ -6279,7 +6394,7 @@ async function reindexPageInVectorIndex(vault, pageRelPath, now = (/* @__PURE__
6279
6394
  }
6280
6395
  }
6281
6396
  const dest = vectorIndexPath(vault);
6282
- await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
6397
+ await mkdir5(join25(vault, ".skillwiki", "vectors"), { recursive: true });
6283
6398
  await atomicWriteText(dest, `${JSON.stringify(index)}
6284
6399
  `);
6285
6400
  return ok({
@@ -6297,7 +6412,7 @@ async function pruneVectorIndex(vault, opts) {
6297
6412
  const orphans = [];
6298
6413
  for (const docKey of Object.keys(index.docs)) {
6299
6414
  const normalizedKey = docKey.split(/\\|\//).join("/");
6300
- const absPath = join24(vault, ...normalizedKey.split("/"));
6415
+ const absPath = join25(vault, ...normalizedKey.split("/"));
6301
6416
  if (!existsSync21(absPath)) {
6302
6417
  orphans.push(normalizedKey);
6303
6418
  }
@@ -6344,7 +6459,7 @@ async function pruneVectorIndex(vault, opts) {
6344
6459
  index.docs[docKey] = toTfIdf(termFreq(tokens), index.df, newPageCount);
6345
6460
  } else {
6346
6461
  try {
6347
- const text = await readFile8(join24(vault, ...docKey.split("/")), "utf8");
6462
+ const text = await readFile8(join25(vault, ...docKey.split("/")), "utf8");
6348
6463
  const tokens = extractPageTokens(text);
6349
6464
  index.docs[docKey] = toTfIdf(termFreq(tokens), index.df, newPageCount);
6350
6465
  } catch {
@@ -6354,7 +6469,7 @@ async function pruneVectorIndex(vault, opts) {
6354
6469
  }
6355
6470
  if (!opts?.dryRun) {
6356
6471
  const dest = vectorIndexPath(vault);
6357
- await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
6472
+ await mkdir5(join25(vault, ".skillwiki", "vectors"), { recursive: true });
6358
6473
  await atomicWriteText(dest, `${JSON.stringify(index)}
6359
6474
  `);
6360
6475
  }
@@ -6655,7 +6770,7 @@ function computeKeywordScore(terms, title, tags, body, relPath) {
6655
6770
  return score;
6656
6771
  }
6657
6772
  async function loadOrBuildGraph(vault) {
6658
- const graphPath = join25(vault, ".skillwiki", "graph.json");
6773
+ const graphPath = join26(vault, ".skillwiki", "graph.json");
6659
6774
  let needsBuild = false;
6660
6775
  try {
6661
6776
  const fileStat = await stat4(graphPath);
@@ -6744,7 +6859,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6744
6859
  import { z } from "zod";
6745
6860
 
6746
6861
  // src/mcp/vault-resolve.ts
6747
- import { join as join26, resolve as resolve4 } from "path";
6862
+ import { join as join27, resolve as resolve4 } from "path";
6748
6863
 
6749
6864
  // src/mcp/allowlist.ts
6750
6865
  import { resolve as resolve3, sep as sep3 } from "path";
@@ -6806,7 +6921,7 @@ async function resolveMcpVault(input) {
6806
6921
  return ok({ vault: vaultPath, source });
6807
6922
  }
6808
6923
  function defaultGraphOut(vault) {
6809
- return join26(vault, ".skillwiki", "graph.json");
6924
+ return join27(vault, ".skillwiki", "graph.json");
6810
6925
  }
6811
6926
 
6812
6927
  // src/mcp/result-format.ts
@@ -6823,7 +6938,7 @@ function formatToolResult(payload) {
6823
6938
  // src/mcp/audit-log.ts
6824
6939
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
6825
6940
  import { homedir } from "os";
6826
- import { join as join27 } from "path";
6941
+ import { join as join28 } from "path";
6827
6942
  function auditEnabled() {
6828
6943
  const v = process.env.SKILLWIKI_MCP_AUDIT;
6829
6944
  if (v === "0" || v === "false") return false;
@@ -6835,7 +6950,7 @@ function auditSink() {
6835
6950
  function auditFilePath() {
6836
6951
  const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
6837
6952
  if (custom && custom.length > 0) return custom;
6838
- return join27(homedir(), ".skillwiki", "mcp-audit.jsonl");
6953
+ return join28(homedir(), ".skillwiki", "mcp-audit.jsonl");
6839
6954
  }
6840
6955
  function auditMcpToolCall(entry) {
6841
6956
  if (!auditEnabled()) return;
@@ -6845,7 +6960,7 @@ function auditMcpToolCall(entry) {
6845
6960
  return;
6846
6961
  }
6847
6962
  const path = auditFilePath();
6848
- mkdirSync2(join27(path, ".."), { recursive: true });
6963
+ mkdirSync2(join28(path, ".."), { recursive: true });
6849
6964
  appendFileSync(path, line, "utf8");
6850
6965
  }
6851
6966
  async function runMcpToolHandler(tool, input, fn) {
@@ -7120,7 +7235,7 @@ function registerMcpMutatingTools(server) {
7120
7235
 
7121
7236
  // src/mcp/resources.ts
7122
7237
  import { readFile as readFile11 } from "fs/promises";
7123
- import { join as join29 } from "path";
7238
+ import { join as join30 } from "path";
7124
7239
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7125
7240
 
7126
7241
  // src/mcp/lint-bucket.ts
@@ -7250,7 +7365,7 @@ async function fetchQueryPreview(input) {
7250
7365
 
7251
7366
  // src/mcp/graph-html.ts
7252
7367
  import { readFile as readFile10 } from "fs/promises";
7253
- import { join as join28 } from "path";
7368
+ import { join as join29 } from "path";
7254
7369
  import { existsSync as existsSync22 } from "fs";
7255
7370
  var TYPE_COLORS = {
7256
7371
  entities: "#e74c3c",
@@ -7319,7 +7434,7 @@ ${nodeSvg}
7319
7434
  return { html, node_count: nodes.length, edge_count: edges.length, truncated };
7320
7435
  }
7321
7436
  async function fetchGraphHtmlReport(input) {
7322
- const graphPath = input.graphPath ?? join28(input.vault, ".skillwiki", "graph.json");
7437
+ const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
7323
7438
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
7324
7439
  if (!existsSync22(graphPath)) {
7325
7440
  return {
@@ -7393,7 +7508,7 @@ async function fetchStaleSummary(input) {
7393
7508
 
7394
7509
  // src/mcp/resources.ts
7395
7510
  async function readVaultFile(vault, rel) {
7396
- return readFile11(join29(vault, rel), "utf8");
7511
+ return readFile11(join30(vault, rel), "utf8");
7397
7512
  }
7398
7513
  async function tailLines(text, lines) {
7399
7514
  const parts = text.split(/\r?\n/);
@@ -7479,7 +7594,7 @@ function registerMcpResources(server) {
7479
7594
  if (!v.ok) {
7480
7595
  return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
7481
7596
  }
7482
- const path = join29(v.data.vault, ".skillwiki", "graph.json");
7597
+ const path = join30(v.data.vault, ".skillwiki", "graph.json");
7483
7598
  try {
7484
7599
  const raw = await readFile11(path, "utf8");
7485
7600
  const graph = JSON.parse(raw);
@@ -7830,6 +7945,12 @@ export {
7830
7945
  renderProjectIndex,
7831
7946
  runProjectIndex,
7832
7947
  scanConflictMarkerBlocksInText,
7948
+ MCP_URL_ENV,
7949
+ MCP_TOKEN_ENV,
7950
+ MCP_HOST_ID_ENV,
7951
+ nonEmptyEnvValue,
7952
+ mergeMcpAuthIntoEnv,
7953
+ redactMcpSecret,
7833
7954
  configPath,
7834
7955
  runConfigGet,
7835
7956
  runConfigSet,
@@ -7844,6 +7965,7 @@ export {
7844
7965
  isFailedRunStatus,
7845
7966
  readSatelliteLatestRunFromText,
7846
7967
  evaluateSatelliteRunHealth,
7968
+ DEFAULT_MCP_URL,
7847
7969
  runDoctor,
7848
7970
  readCliPackageJson,
7849
7971
  DEFAULT_DIRTY_VOLUME_THRESHOLD,