skillwiki 0.9.52 → 0.9.55

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.
@@ -2852,7 +2852,7 @@ function buildCliSurface() {
2852
2852
  program.command("lint").option("--days <n>").option("--lines <n>").option("--log-threshold <n>").option("--fix").option("--only <bucket>").option("--summary").option("--examples <n>").option("--wiki <name>");
2853
2853
  program.command("config");
2854
2854
  program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
2855
- program.command("doctor");
2855
+ program.command("doctor").option("--check-snapshotter");
2856
2856
  program.command("status").option("--wiki <name>");
2857
2857
  program.command("archive").option("--wiki <name>").option("--cascade").option("--apply").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>");
2858
2858
  program.command("drift").option("--apply").option("--new <date>").option("--wiki <name>");
@@ -2887,7 +2887,7 @@ function buildCliSurface() {
2887
2887
  compoundCmd.command("list").requiredOption("--project <slug>").option("--wiki <name>");
2888
2888
  compoundCmd.command("delete").requiredOption("--project <slug>").option("--wiki <name>");
2889
2889
  const syncCmd = program.commands.find((c) => c.name() === "sync");
2890
- syncCmd.command("status").option("--wiki <name>").option("--include-stashes");
2890
+ syncCmd.command("status").option("--wiki <name>").option("--include-stashes").option("--include-remote-health").option("--check-snapshotter");
2891
2891
  syncCmd.command("push").option("--wiki <name>");
2892
2892
  syncCmd.command("pull").option("--wiki <name>");
2893
2893
  syncCmd.command("lock").option("--summary <text>").option("--ttl-minutes <n>").option("--force").option("--wiki <name>");
@@ -3100,7 +3100,7 @@ function extractSourceEntries(rawFm) {
3100
3100
  }
3101
3101
  return entries;
3102
3102
  }
3103
- var ERROR_ORDER = ["sensitive_content", "broken_wikilinks", "invalid_frontmatter", "raw_source_identity_conflict", "raw_dedup", "broken_sources", "tag_not_in_taxonomy", "path_too_long"];
3103
+ var ERROR_ORDER = ["sensitive_content", "conflict_markers", "broken_wikilinks", "invalid_frontmatter", "raw_source_identity_conflict", "raw_dedup", "broken_sources", "tag_not_in_taxonomy", "path_too_long"];
3104
3104
  var WARNING_ORDER = ["raw_body_duplicate", "raw_subdirectory_duplicate", "file_source_url", "index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "orphans", "compound_refs", "legacy_citation_style", "orphaned_citations", "duplicate_frontmatter", "frontmatter_yaml_invalid", "work_item_health", "orphaned_project_pages", "missing_overview", "missing_diagram"];
3105
3105
  var INFO_ORDER = ["bridges", "sparse_community", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "stale_sections", "cli_refs"];
3106
3106
  var KNOWN_BUCKETS = [...ERROR_ORDER, ...WARNING_ORDER, ...INFO_ORDER];
@@ -3448,6 +3448,38 @@ async function runFileSourceUrlOnly(input) {
3448
3448
  const match = remaining.size > 0 ? [{ kind: "file_source_url", items: [...remaining] }] : [];
3449
3449
  return outputForOnlyBucket(input, match, fixed, unresolved, readVault);
3450
3450
  }
3451
+ function scanConflictMarkerBlocks(path, text) {
3452
+ const findings = [];
3453
+ const lines = text.split(/\r?\n/);
3454
+ let inFence = false;
3455
+ let openLine = 0;
3456
+ let sawSeparator = false;
3457
+ for (let i = 0; i < lines.length; i += 1) {
3458
+ const line = lines[i];
3459
+ if (line.startsWith("```") || line.startsWith("~~~")) {
3460
+ inFence = !inFence;
3461
+ continue;
3462
+ }
3463
+ if (inFence) continue;
3464
+ if (line.startsWith("<<<<<<< ")) {
3465
+ openLine = i + 1;
3466
+ sawSeparator = false;
3467
+ continue;
3468
+ }
3469
+ if (line === "=======" && openLine > 0) {
3470
+ sawSeparator = true;
3471
+ continue;
3472
+ }
3473
+ if (line.startsWith(">>>>>>> ")) {
3474
+ if (openLine > 0 && sawSeparator) {
3475
+ findings.push({ path, line: openLine, message: "complete Git conflict-marker block" });
3476
+ }
3477
+ openLine = 0;
3478
+ sawSeparator = false;
3479
+ }
3480
+ }
3481
+ return findings;
3482
+ }
3451
3483
  async function runLint(input) {
3452
3484
  if (input.only && !KNOWN_BUCKETS.includes(input.only)) {
3453
3485
  return {
@@ -3545,10 +3577,12 @@ async function runLint(input) {
3545
3577
  {
3546
3578
  const allPageResults = await mapWithConcurrency(scan.allMarkdown, vaultIoConcurrency(), async (page) => {
3547
3579
  const sensitiveFlags2 = [];
3580
+ const conflictMarkers2 = [];
3548
3581
  let fmYamlInvalid2 = null;
3549
3582
  try {
3550
3583
  const text = await readPageCached(page, pageTextCache);
3551
3584
  sensitiveFlags2.push(...scanSensitiveContent(text, { file: page.relPath }));
3585
+ conflictMarkers2.push(...scanConflictMarkerBlocks(page.relPath, text));
3552
3586
  const fm = extractFrontmatter(text);
3553
3587
  if (!fm.ok && fm.error === "INVALID_FRONTMATTER") {
3554
3588
  const detail = fm.detail;
@@ -3557,10 +3591,12 @@ async function runLint(input) {
3557
3591
  }
3558
3592
  } catch {
3559
3593
  }
3560
- return { sensitiveFlags: sensitiveFlags2, fmYamlInvalid: fmYamlInvalid2 };
3594
+ return { sensitiveFlags: sensitiveFlags2, conflictMarkers: conflictMarkers2, fmYamlInvalid: fmYamlInvalid2 };
3561
3595
  });
3562
3596
  const sensitiveFlags = allPageResults.flatMap((result) => result.sensitiveFlags);
3563
3597
  if (sensitiveFlags.length > 0) buckets.sensitive_content = sensitiveFlags;
3598
+ const conflictMarkers = allPageResults.flatMap((result) => result.conflictMarkers);
3599
+ if (conflictMarkers.length > 0) buckets.conflict_markers = conflictMarkers;
3564
3600
  const fmYamlInvalid = allPageResults.map((result) => result.fmYamlInvalid).filter((item) => item !== null);
3565
3601
  if (fmYamlInvalid.length > 0) buckets.frontmatter_yaml_invalid = fmYamlInvalid;
3566
3602
  const subDirDupes = [];
@@ -4538,6 +4574,15 @@ async function loadFleetManifestAndHost(input) {
4538
4574
  identityStatus: "known"
4539
4575
  };
4540
4576
  }
4577
+ function snapshotterAliasForLocalHost(fleetLoad) {
4578
+ if (!fleetLoad?.manifest || !fleetLoad.hostId) return void 0;
4579
+ const snapshotterId = Object.entries(fleetLoad.manifest.hosts).find(([, h]) => h.role === "snapshotter")?.[0];
4580
+ if (!snapshotterId) return void 0;
4581
+ const profile = fleetLoad.manifest.hosts[snapshotterId]?.access?.from?.[fleetLoad.hostId];
4582
+ if (!profile || profile.status !== "configured" && profile.status !== "local") return void 0;
4583
+ const aliases = profile.ssh_aliases ?? [];
4584
+ return aliases.length > 0 ? aliases[0] : void 0;
4585
+ }
4541
4586
  function satelliteGateFromFleetLoad(load) {
4542
4587
  if (!load?.hostId) return { satelliteExpected: false };
4543
4588
  const host = load.manifest.hosts[load.hostId];
@@ -4794,8 +4839,8 @@ function safeUserName() {
4794
4839
  }
4795
4840
 
4796
4841
  // src/commands/doctor.ts
4797
- import { existsSync as existsSync11, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync8 } from "fs";
4798
- import { join as join22, resolve as resolve5 } from "path";
4842
+ import { existsSync as existsSync13, lstatSync, readlinkSync, readdirSync as readdirSync3, statSync, readFileSync as readFileSync10 } from "fs";
4843
+ import { join as join24, resolve as resolve5 } from "path";
4799
4844
  import { execSync as execSync2 } from "child_process";
4800
4845
  import { platform as platform2 } from "os";
4801
4846
 
@@ -4917,12 +4962,180 @@ function parseTomlScalar(rawValue) {
4917
4962
  return value;
4918
4963
  }
4919
4964
 
4920
- // src/utils/satellite-run-health.ts
4921
- import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
4965
+ // src/utils/conflict-markers.ts
4966
+ import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
4922
4967
  import { join as join20 } from "path";
4968
+ function scanConflictMarkerBlocksInText(relPath, text) {
4969
+ const findings = [];
4970
+ const lines = text.split(/\r?\n/);
4971
+ let inFence = false;
4972
+ let openLine = 0;
4973
+ let sawSeparator = false;
4974
+ for (let i = 0; i < lines.length; i += 1) {
4975
+ const line = lines[i];
4976
+ if (line.startsWith("```") || line.startsWith("~~~")) {
4977
+ inFence = !inFence;
4978
+ continue;
4979
+ }
4980
+ if (inFence) continue;
4981
+ if (line.startsWith("<<<<<<< ")) {
4982
+ openLine = i + 1;
4983
+ sawSeparator = false;
4984
+ continue;
4985
+ }
4986
+ if (line === "=======" && openLine > 0) {
4987
+ sawSeparator = true;
4988
+ continue;
4989
+ }
4990
+ if (line.startsWith(">>>>>>> ")) {
4991
+ if (openLine > 0 && sawSeparator) {
4992
+ findings.push({ path: relPath, line: openLine });
4993
+ }
4994
+ openLine = 0;
4995
+ sawSeparator = false;
4996
+ }
4997
+ }
4998
+ return findings;
4999
+ }
5000
+ var PRUNE_DIRS = /* @__PURE__ */ new Set([
5001
+ ".git",
5002
+ ".obsidian",
5003
+ ".skillwiki",
5004
+ ".claude",
5005
+ ".antigravitycli",
5006
+ ".playwright-cli"
5007
+ ]);
5008
+ function walkMarkdownFiles2(root, dir, rel, out) {
5009
+ let entries;
5010
+ try {
5011
+ entries = readdirSync2(dir, { withFileTypes: true });
5012
+ } catch {
5013
+ return;
5014
+ }
5015
+ for (const entry of entries) {
5016
+ if (entry.isDirectory()) {
5017
+ if (PRUNE_DIRS.has(entry.name)) continue;
5018
+ walkMarkdownFiles2(root, join20(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
5019
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
5020
+ out.push(rel ? `${rel}/${entry.name}` : entry.name);
5021
+ }
5022
+ }
5023
+ }
5024
+ function scanVaultConflictMarkers(vaultRoot) {
5025
+ if (!existsSync9(vaultRoot)) return [];
5026
+ const relPaths = [];
5027
+ walkMarkdownFiles2(vaultRoot, vaultRoot, "", relPaths);
5028
+ const all = [];
5029
+ for (const rel of relPaths) {
5030
+ let text;
5031
+ try {
5032
+ text = readFileSync6(join20(vaultRoot, rel), "utf8");
5033
+ } catch {
5034
+ continue;
5035
+ }
5036
+ all.push(...scanConflictMarkerBlocksInText(rel, text));
5037
+ }
5038
+ return all;
5039
+ }
5040
+
5041
+ // src/utils/remote-health.ts
5042
+ import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5043
+ import { join as join21 } from "path";
5044
+ import { execFileSync } from "child_process";
5045
+ var REMOTE_PROBE_TIMEOUT_MS = 3e3;
5046
+ var defaultExec = (file, args, cwd) => execFileSync(file, args, {
5047
+ cwd,
5048
+ encoding: "utf8",
5049
+ stdio: ["pipe", "pipe", "pipe"],
5050
+ timeout: REMOTE_PROBE_TIMEOUT_MS
5051
+ }).trim();
5052
+ var DEFAULT_WIKI_S3_REMOTE = "seaweed-wiki:cloud/wiki";
5053
+ function readWikiS3RemoteConfigured(home) {
5054
+ try {
5055
+ const content = readFileSync7(join21(home, ".skillwiki", ".env"), "utf8");
5056
+ for (const line of content.split(/\r?\n/)) {
5057
+ const trimmed = line.trim();
5058
+ if (!trimmed || trimmed.startsWith("#")) continue;
5059
+ const eq = trimmed.indexOf("=");
5060
+ if (eq <= 0) continue;
5061
+ const k = trimmed.slice(0, eq).trim();
5062
+ const v = trimmed.slice(eq + 1).trim();
5063
+ if (k === "WIKI_REMOTE" && v.length > 0) return v;
5064
+ }
5065
+ } catch {
5066
+ }
5067
+ return void 0;
5068
+ }
5069
+ function readWikiS3RemoteFromEnv(home) {
5070
+ return readWikiS3RemoteConfigured(home) ?? DEFAULT_WIKI_S3_REMOTE;
5071
+ }
5072
+ function probeGithubReachability(vaultPath, exec = defaultExec) {
5073
+ if (!existsSync10(join21(vaultPath, ".git"))) return "unknown";
5074
+ try {
5075
+ exec("git", ["remote", "get-url", "origin"], vaultPath);
5076
+ } catch {
5077
+ return "unknown";
5078
+ }
5079
+ try {
5080
+ const out = exec("git", ["ls-remote", "origin", "refs/heads/main"], vaultPath);
5081
+ if (out.length > 0) return "ok";
5082
+ return "unreachable";
5083
+ } catch {
5084
+ return "unreachable";
5085
+ }
5086
+ }
5087
+ function probeS3Reachability(remote, exec = defaultExec) {
5088
+ if (!remote) return "unknown";
5089
+ try {
5090
+ exec("rclone", ["lsf", remote, "--max-depth", "1", "--files-only"]);
5091
+ return "ok";
5092
+ } catch {
5093
+ return "unreachable";
5094
+ }
5095
+ }
5096
+ function probeSnapshotterSsh(sshAlias2, exec = defaultExec) {
5097
+ if (!sshAlias2) return "unknown";
5098
+ try {
5099
+ exec("ssh", [
5100
+ "-o",
5101
+ "BatchMode=yes",
5102
+ "-o",
5103
+ "ConnectTimeout=3",
5104
+ "-o",
5105
+ "StrictHostKeyChecking=accept-new",
5106
+ sshAlias2,
5107
+ "true"
5108
+ ]);
5109
+ return "ok";
5110
+ } catch {
5111
+ return "unreachable";
5112
+ }
5113
+ }
5114
+ function buildDegradedReasons(health) {
5115
+ const reasons = [];
5116
+ if (health.github === "unreachable") reasons.push("github_remote_unreachable");
5117
+ if (health.s3 === "unreachable") reasons.push("s3_remote_unreachable");
5118
+ if (health.snapshotter === "unreachable") reasons.push("snapshotter_host_unreachable");
5119
+ return reasons;
5120
+ }
5121
+ function probeRemoteHealth(input) {
5122
+ const exec = input.exec ?? defaultExec;
5123
+ const github = probeGithubReachability(input.vaultPath, exec);
5124
+ const s3Remote = input.s3Remote ?? readWikiS3RemoteFromEnv(input.home);
5125
+ const s3 = probeS3Reachability(s3Remote, exec);
5126
+ let snapshotter = "not_checked";
5127
+ if (input.checkSnapshotter && input.snapshotterAlias) {
5128
+ snapshotter = probeSnapshotterSsh(input.snapshotterAlias, exec);
5129
+ }
5130
+ return { github, s3, snapshotter };
5131
+ }
5132
+
5133
+ // src/utils/satellite-run-health.ts
5134
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5135
+ import { join as join22 } from "path";
4923
5136
  var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
4924
5137
  function satelliteLatestRunPath(vault) {
4925
- return join20(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
5138
+ return join22(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
4926
5139
  }
4927
5140
  function isFailedRunStatus(status) {
4928
5141
  return status === "fail" || status === "failure";
@@ -4944,9 +5157,9 @@ function readSatelliteLatestRunFromText(text) {
4944
5157
  }
4945
5158
  function readSatelliteLatestRun(vault) {
4946
5159
  const latestPath = satelliteLatestRunPath(vault);
4947
- if (!existsSync9(latestPath)) return null;
5160
+ if (!existsSync11(latestPath)) return null;
4948
5161
  try {
4949
- return parseLatestRunFile(readFileSync6(latestPath, "utf8"));
5162
+ return parseLatestRunFile(readFileSync8(latestPath, "utf8"));
4950
5163
  } catch {
4951
5164
  return null;
4952
5165
  }
@@ -4975,8 +5188,8 @@ function evaluateSatelliteRunHealth(vault, now) {
4975
5188
  // src/utils/s3-mount-health.ts
4976
5189
  import { execSync } from "child_process";
4977
5190
  import { platform } from "os";
4978
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
4979
- import { join as join21 } from "path";
5191
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
5192
+ import { join as join23 } from "path";
4980
5193
  var OS = platform();
4981
5194
  function findRcloneMountPid() {
4982
5195
  try {
@@ -5060,7 +5273,7 @@ function extractRcloneFs(args) {
5060
5273
  function getRcloneArgs(pid) {
5061
5274
  try {
5062
5275
  if (OS === "linux") {
5063
- const raw = readFileSync7(`/proc/${pid}/cmdline`);
5276
+ const raw = readFileSync9(`/proc/${pid}/cmdline`);
5064
5277
  return new TextDecoder().decode(raw).split("\0").filter(Boolean);
5065
5278
  } else {
5066
5279
  const out = execSync(`ps -o args= -p ${pid}`, {
@@ -5103,7 +5316,7 @@ function queryRcloneRC(rcAddr, fs) {
5103
5316
  function detectFuseMount(vaultPath) {
5104
5317
  try {
5105
5318
  if (OS === "linux") {
5106
- const mounts = readFileSync7("/proc/mounts", "utf8");
5319
+ const mounts = readFileSync9("/proc/mounts", "utf8");
5107
5320
  let best = null;
5108
5321
  for (const line of mounts.split("\n")) {
5109
5322
  const parts = line.split(" ");
@@ -5134,7 +5347,7 @@ function detectFuseMount(vaultPath) {
5134
5347
  return null;
5135
5348
  }
5136
5349
  function writeTest(dir) {
5137
- const testFile = join21(dir, `.doctor-write-test-${process.pid}.tmp`);
5350
+ const testFile = join23(dir, `.doctor-write-test-${process.pid}.tmp`);
5138
5351
  const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
5139
5352
  const start = Date.now();
5140
5353
  try {
@@ -5234,13 +5447,13 @@ function detectCliChannels(argv, home) {
5234
5447
  }
5235
5448
  const plugin = findPlugin(home);
5236
5449
  if (plugin) {
5237
- const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
5238
- if (existsSync11(pluginBin)) {
5450
+ const pluginBin = join24(plugin.installPath, "bin", "skillwiki");
5451
+ if (existsSync13(pluginBin)) {
5239
5452
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
5240
5453
  }
5241
5454
  }
5242
- const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
5243
- if (existsSync11(installBin)) {
5455
+ const installBin = join24(home, ".claude", "skills", "bin", "skillwiki");
5456
+ if (existsSync13(installBin)) {
5244
5457
  channels.push({ name: "install", path: installBin, isDevLink: false });
5245
5458
  }
5246
5459
  return channels;
@@ -5301,7 +5514,7 @@ function isDevSourceRun(argv) {
5301
5514
  }
5302
5515
  async function checkConfigFile(home) {
5303
5516
  const cfgPath = configPath(home);
5304
- if (!existsSync11(cfgPath)) {
5517
+ if (!existsSync13(cfgPath)) {
5305
5518
  return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
5306
5519
  }
5307
5520
  try {
@@ -5316,7 +5529,7 @@ function checkWikiPathExists(resolvedPath) {
5316
5529
  if (resolvedPath === void 0) {
5317
5530
  return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
5318
5531
  }
5319
- if (existsSync11(resolvedPath) && statSync(resolvedPath).isDirectory()) {
5532
+ if (existsSync13(resolvedPath) && statSync(resolvedPath).isDirectory()) {
5320
5533
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
5321
5534
  }
5322
5535
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
@@ -5325,13 +5538,13 @@ function checkVaultStructure(resolvedPath) {
5325
5538
  if (resolvedPath === void 0) {
5326
5539
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
5327
5540
  }
5328
- if (!existsSync11(resolvedPath)) {
5541
+ if (!existsSync13(resolvedPath)) {
5329
5542
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
5330
5543
  }
5331
5544
  const missing = [];
5332
- if (!existsSync11(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
5545
+ if (!existsSync13(join24(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
5333
5546
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
5334
- if (!existsSync11(join22(resolvedPath, dir))) missing.push(dir + "/");
5547
+ if (!existsSync13(join24(resolvedPath, dir))) missing.push(dir + "/");
5335
5548
  }
5336
5549
  if (missing.length === 0) {
5337
5550
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -5339,8 +5552,8 @@ function checkVaultStructure(resolvedPath) {
5339
5552
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
5340
5553
  }
5341
5554
  function checkSkillsInstalled(home, cwd) {
5342
- const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
5343
- if (srcDir && existsSync11(srcDir)) {
5555
+ const srcDir = cwd ? join24(cwd, "packages", "skills") : void 0;
5556
+ if (srcDir && existsSync13(srcDir)) {
5344
5557
  const found = findInstalledSkillMd(srcDir);
5345
5558
  if (found.length > 0) {
5346
5559
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
@@ -5353,8 +5566,8 @@ function checkSkillsInstalled(home, cwd) {
5353
5566
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
5354
5567
  }
5355
5568
  }
5356
- const skillsDir = join22(home, ".claude", "skills");
5357
- if (existsSync11(skillsDir)) {
5569
+ const skillsDir = join24(home, ".claude", "skills");
5570
+ if (existsSync13(skillsDir)) {
5358
5571
  const found = findInstalledSkillMd(skillsDir);
5359
5572
  if (found.length > 0) {
5360
5573
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
@@ -5364,10 +5577,10 @@ function checkSkillsInstalled(home, cwd) {
5364
5577
  }
5365
5578
  function checkDuplicateSkills(home) {
5366
5579
  const plugin = findPlugin(home);
5367
- const skillsDir = join22(home, ".claude", "skills");
5580
+ const skillsDir = join24(home, ".claude", "skills");
5368
5581
  const agentSkillDirs = [
5369
- { label: "~/.codex/skills/", path: join22(home, ".codex", "skills") },
5370
- { label: "~/.agents/skills/", path: join22(home, ".agents", "skills") }
5582
+ { label: "~/.codex/skills/", path: join24(home, ".codex", "skills") },
5583
+ { label: "~/.agents/skills/", path: join24(home, ".agents", "skills") }
5371
5584
  ];
5372
5585
  if (!plugin) {
5373
5586
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -5470,8 +5683,8 @@ async function checkProfiles(home) {
5470
5683
  }
5471
5684
  async function checkProjectLocalOverride(cwd) {
5472
5685
  const dir = cwd ?? process.cwd();
5473
- const envPath = join22(dir, ".skillwiki", ".env");
5474
- if (existsSync11(envPath)) {
5686
+ const envPath = join24(dir, ".skillwiki", ".env");
5687
+ if (existsSync13(envPath)) {
5475
5688
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
5476
5689
  }
5477
5690
  return check("pass", "project_local", "Project-local config", "None");
@@ -5480,7 +5693,7 @@ function checkVaultGitRemote(resolvedPath) {
5480
5693
  if (resolvedPath === void 0) {
5481
5694
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
5482
5695
  }
5483
- if (!existsSync11(join22(resolvedPath, ".git"))) {
5696
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5484
5697
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
5485
5698
  }
5486
5699
  try {
@@ -5503,9 +5716,9 @@ function checkObsidianTemplates(resolvedPath) {
5503
5716
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
5504
5717
  }
5505
5718
  const missing = [];
5506
- if (!existsSync11(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
5507
- if (!existsSync11(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5508
- if (!existsSync11(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5719
+ if (!existsSync13(join24(resolvedPath, "_Templates"))) missing.push("_Templates/");
5720
+ if (!existsSync13(join24(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5721
+ if (!existsSync13(join24(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5509
5722
  if (missing.length === 0) {
5510
5723
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
5511
5724
  }
@@ -5515,15 +5728,15 @@ function checkDotStoreClean(resolvedPath) {
5515
5728
  if (resolvedPath === void 0) {
5516
5729
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
5517
5730
  }
5518
- const rawDir = join22(resolvedPath, "raw");
5519
- if (!existsSync11(rawDir)) {
5731
+ const rawDir = join24(resolvedPath, "raw");
5732
+ if (!existsSync13(rawDir)) {
5520
5733
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
5521
5734
  }
5522
5735
  const found = [];
5523
5736
  (function walk2(dir, rel) {
5524
5737
  let entries;
5525
5738
  try {
5526
- entries = readdirSync2(dir, { withFileTypes: true });
5739
+ entries = readdirSync3(dir, { withFileTypes: true });
5527
5740
  } catch {
5528
5741
  return;
5529
5742
  }
@@ -5531,7 +5744,7 @@ function checkDotStoreClean(resolvedPath) {
5531
5744
  if (entry.name === ".DS_Store") {
5532
5745
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
5533
5746
  } else if (entry.isDirectory()) {
5534
- walk2(join22(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
5747
+ walk2(join24(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
5535
5748
  }
5536
5749
  }
5537
5750
  })(rawDir, "");
@@ -5540,11 +5753,29 @@ function checkDotStoreClean(resolvedPath) {
5540
5753
  }
5541
5754
  return check("info", "dsstore_clean", "No .DS_Store in raw/", `${found.length} .DS_Store file(s) found \u2014 remove with: find ${rawDir} -name .DS_Store -delete`);
5542
5755
  }
5756
+ function checkVaultConflictMarkers(resolvedPath) {
5757
+ if (resolvedPath === void 0) {
5758
+ return check("pass", "vault_conflict_markers", "Vault conflict markers", "No vault path \u2014 check skipped");
5759
+ }
5760
+ const findings = scanVaultConflictMarkers(resolvedPath);
5761
+ if (findings.length === 0) {
5762
+ return check("pass", "vault_conflict_markers", "Vault conflict markers", "No complete conflict-marker blocks");
5763
+ }
5764
+ const first = findings[0];
5765
+ const n = findings.length;
5766
+ const fileWord = n === 1 ? "file" : "files";
5767
+ return check(
5768
+ "error",
5769
+ "vault_conflict_markers",
5770
+ "Vault conflict markers",
5771
+ `${n} ${fileWord}, first: ${first.path}:${first.line}`
5772
+ );
5773
+ }
5543
5774
  function checkSyncLastPush(resolvedPath) {
5544
5775
  if (resolvedPath === void 0) {
5545
5776
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
5546
5777
  }
5547
- if (!existsSync11(join22(resolvedPath, ".git"))) {
5778
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5548
5779
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
5549
5780
  }
5550
5781
  let timestamp;
@@ -5592,7 +5823,7 @@ function checkVaultGitDirty(resolvedPath) {
5592
5823
  if (resolvedPath === void 0) {
5593
5824
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
5594
5825
  }
5595
- if (!existsSync11(join22(resolvedPath, ".git"))) {
5826
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5596
5827
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
5597
5828
  }
5598
5829
  try {
@@ -5660,7 +5891,7 @@ function remoteMainHash(resolvedPath) {
5660
5891
  }
5661
5892
  function checkStaleRemoteMain(resolvedPath) {
5662
5893
  if (resolvedPath === void 0) return void 0;
5663
- if (!existsSync11(join22(resolvedPath, ".git"))) return void 0;
5894
+ if (!existsSync13(join24(resolvedPath, ".git"))) return void 0;
5664
5895
  const localOrigin = gitRefHash(resolvedPath, "origin/main");
5665
5896
  if (!localOrigin) return void 0;
5666
5897
  const remoteMain = remoteMainHash(resolvedPath);
@@ -5672,11 +5903,101 @@ function checkStaleRemoteMain(resolvedPath) {
5672
5903
  `Remote main differs from local origin/main (${remoteMain.slice(0, 8)} != ${localOrigin.slice(0, 8)}) \u2014 run git fetch before trusting behind count`
5673
5904
  );
5674
5905
  }
5906
+ function checkVaultLocalGit(resolvedPath) {
5907
+ if (resolvedPath === void 0) {
5908
+ return check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved");
5909
+ }
5910
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5911
+ return check("warn", "vault_local_git", "Vault local git", "Not a git repository - sync features unavailable");
5912
+ }
5913
+ try {
5914
+ execSync2("git rev-parse --git-dir", {
5915
+ cwd: resolvedPath,
5916
+ encoding: "utf8",
5917
+ stdio: ["pipe", "pipe", "pipe"],
5918
+ timeout: 2e3
5919
+ });
5920
+ return check("pass", "vault_local_git", "Vault local git", "Git metadata readable");
5921
+ } catch {
5922
+ return check("error", "vault_local_git", "Vault local git", "Git metadata unreadable \u2014 local vault may be corrupt");
5923
+ }
5924
+ }
5925
+ function checkVaultGithubRemote(resolvedPath, exec) {
5926
+ if (resolvedPath === void 0) {
5927
+ return check("pass", "vault_github_remote", "Vault GitHub remote", "No vault path \u2014 check skipped");
5928
+ }
5929
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5930
+ return check("pass", "vault_github_remote", "Vault GitHub remote", "No git repo \u2014 check skipped");
5931
+ }
5932
+ const state = probeGithubReachability(resolvedPath, exec);
5933
+ if (state === "ok") {
5934
+ return check("pass", "vault_github_remote", "Vault GitHub remote", "git ls-remote origin main succeeded");
5935
+ }
5936
+ if (state === "unreachable") {
5937
+ return check("warn", "vault_github_remote", "Vault GitHub remote", "GitHub unreachable (ls-remote failed) \u2014 local vault still usable");
5938
+ }
5939
+ return check("pass", "vault_github_remote", "Vault GitHub remote", "No origin remote \u2014 network probe skipped");
5940
+ }
5941
+ function checkVaultS3Remote(home, exec) {
5942
+ const remote = readWikiS3RemoteConfigured(home);
5943
+ if (!remote) {
5944
+ return check("pass", "vault_s3_remote", "Vault S3 remote", "S3 remote not configured \u2014 check skipped");
5945
+ }
5946
+ const state = probeS3Reachability(remote, exec);
5947
+ if (state === "ok") {
5948
+ return check("pass", "vault_s3_remote", "Vault S3 remote", `rclone lsf ${remote} succeeded`);
5949
+ }
5950
+ if (state === "unreachable") {
5951
+ return check("warn", "vault_s3_remote", "Vault S3 remote", `S3 remote unreachable (${remote}) \u2014 local/GitHub work may continue`);
5952
+ }
5953
+ return check("pass", "vault_s3_remote", "Vault S3 remote", "S3 remote not configured \u2014 check skipped");
5954
+ }
5955
+ function checkVaultSnapshotterReachable(fleetLoad, checkSnapshotter, exec) {
5956
+ if (!checkSnapshotter) {
5957
+ return check("pass", "vault_snapshotter_reachable", "Vault snapshotter host", "Snapshotter SSH probe not requested \u2014 check skipped");
5958
+ }
5959
+ const alias = snapshotterAliasForLocalHost(fleetLoad);
5960
+ if (!alias) {
5961
+ return check("pass", "vault_snapshotter_reachable", "Vault snapshotter host", "No declared SSH alias from this host \u2014 check skipped");
5962
+ }
5963
+ const state = probeSnapshotterSsh(alias, exec);
5964
+ if (state === "ok") {
5965
+ return check("pass", "vault_snapshotter_reachable", "Vault snapshotter host", `SSH reachable via ${alias}`);
5966
+ }
5967
+ return check("warn", "vault_snapshotter_reachable", "Vault snapshotter host", `Snapshotter unreachable via ${alias} \u2014 not a local vault corruption signal`);
5968
+ }
5969
+ function checkVaultPromotionLag(resolvedPath) {
5970
+ if (resolvedPath === void 0) {
5971
+ return check("pass", "vault_promotion_lag", "Vault promotion lag", "No vault path \u2014 check skipped");
5972
+ }
5973
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5974
+ return check("pass", "vault_promotion_lag", "Vault promotion lag", "No git repo \u2014 check skipped");
5975
+ }
5976
+ try {
5977
+ const out = execSync2("git log -1 --format=%ct origin/main", {
5978
+ cwd: resolvedPath,
5979
+ encoding: "utf8",
5980
+ stdio: ["pipe", "pipe", "pipe"],
5981
+ timeout: 2e3
5982
+ }).trim();
5983
+ const ts = parseInt(out, 10);
5984
+ if (!Number.isFinite(ts) || ts <= 0) {
5985
+ return check("pass", "vault_promotion_lag", "Vault promotion lag", "origin/main timestamp unavailable \u2014 check skipped");
5986
+ }
5987
+ const ageHours = Math.floor((Date.now() / 1e3 - ts) / 3600);
5988
+ if (ageHours > 48) {
5989
+ return check("warn", "vault_promotion_lag", "Vault promotion lag", `Local origin/main snapshot is ${ageHours}h old \u2014 verify snapshotter/GitHub when online`);
5990
+ }
5991
+ return check("pass", "vault_promotion_lag", "Vault promotion lag", `origin/main age ${ageHours}h`);
5992
+ } catch {
5993
+ return check("pass", "vault_promotion_lag", "Vault promotion lag", "Could not read origin/main \u2014 check skipped");
5994
+ }
5995
+ }
5675
5996
  function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix, zeroDetail) {
5676
5997
  if (resolvedPath === void 0) {
5677
5998
  return check("pass", id, label, "No vault path \u2014 check skipped");
5678
5999
  }
5679
- if (!existsSync11(join22(resolvedPath, ".git"))) {
6000
+ if (!existsSync13(join24(resolvedPath, ".git"))) {
5680
6001
  return check("pass", id, label, "No git repo \u2014 check skipped");
5681
6002
  }
5682
6003
  if (!hasOriginMain(resolvedPath)) {
@@ -5704,7 +6025,7 @@ function checkSatelliteLastRun(vaultPath, satelliteExpected) {
5704
6025
  return check("pass", "satellite_job_last_run", "Satellite job last run", "No vault path \u2014 check skipped");
5705
6026
  }
5706
6027
  const latestPath = satelliteLatestRunPath(vaultPath);
5707
- if (!existsSync11(latestPath)) {
6028
+ if (!existsSync13(latestPath)) {
5708
6029
  return check("pass", "satellite_job_last_run", "Satellite job last run", "No latest-run.json \u2014 satellite has not run yet");
5709
6030
  }
5710
6031
  try {
@@ -5792,11 +6113,11 @@ async function checkFleetIdentity(input) {
5792
6113
  }
5793
6114
  function pullLogPaths(home) {
5794
6115
  const paths = platform2() === "darwin" ? [
5795
- join22(home, "Library", "Logs", "wiki-pull.log"),
5796
- join22(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
6116
+ join24(home, "Library", "Logs", "wiki-pull.log"),
6117
+ join24(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
5797
6118
  ] : [
5798
- join22(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
5799
- join22(home, "Library", "Logs", "wiki-pull.log")
6119
+ join24(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
6120
+ join24(home, "Library", "Logs", "wiki-pull.log")
5800
6121
  ];
5801
6122
  return [...new Set(paths)];
5802
6123
  }
@@ -5808,12 +6129,12 @@ function isRecentLogLine(line, nowMs) {
5808
6129
  return nowMs - ts <= 24 * 60 * 60 * 1e3;
5809
6130
  }
5810
6131
  function checkVaultGitPullFailures(home) {
5811
- const path = pullLogPaths(home).find((p) => existsSync11(p));
6132
+ const path = pullLogPaths(home).find((p) => existsSync13(p));
5812
6133
  if (!path) {
5813
6134
  return check("pass", "vault_git_pull_failures", "Vault pull failures", "No wiki-pull.log found \u2014 check skipped");
5814
6135
  }
5815
6136
  try {
5816
- const lines = readFileSync8(path, "utf8").split(/\r?\n/).filter(Boolean);
6137
+ const lines = readFileSync10(path, "utf8").split(/\r?\n/).filter(Boolean);
5817
6138
  const now = Date.now();
5818
6139
  const failures = lines.filter(
5819
6140
  (line) => isRecentLogLine(line, now) && /(pre-push pull failed|FAIL .*pull|FAIL .*rebase|cannot pull with rebase|unstaged changes)/i.test(line)
@@ -5836,8 +6157,8 @@ function checkS3MountPerf(resolvedPath) {
5836
6157
  return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
5837
6158
  }
5838
6159
  const mountPoint = fuse.mountPoint;
5839
- const conceptsDir = join22(resolvedPath, "concepts");
5840
- if (!existsSync11(conceptsDir)) {
6160
+ const conceptsDir = join24(resolvedPath, "concepts");
6161
+ if (!existsSync13(conceptsDir)) {
5841
6162
  return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
5842
6163
  }
5843
6164
  const start = Date.now();
@@ -6019,8 +6340,8 @@ function checkWriteTest(resolvedPath) {
6019
6340
  if (!fuse) {
6020
6341
  return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
6021
6342
  }
6022
- const conceptsDir = join22(resolvedPath, "concepts");
6023
- if (!existsSync11(conceptsDir)) {
6343
+ const conceptsDir = join24(resolvedPath, "concepts");
6344
+ if (!existsSync13(conceptsDir)) {
6024
6345
  return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
6025
6346
  }
6026
6347
  const result = writeTest(conceptsDir);
@@ -6106,7 +6427,7 @@ function checkVfsCacheHealth(resolvedPath) {
6106
6427
  }
6107
6428
  function readVaultSyncConfig(home) {
6108
6429
  try {
6109
- const content = readFileSync8(join22(home, ".skillwiki", ".env"), "utf8");
6430
+ const content = readFileSync10(join24(home, ".skillwiki", ".env"), "utf8");
6110
6431
  let installed = false;
6111
6432
  let role;
6112
6433
  let serviceScope;
@@ -6135,7 +6456,7 @@ function readVaultSyncConfig(home) {
6135
6456
  }
6136
6457
  function readKeyFromEnvFile(path, keys) {
6137
6458
  try {
6138
- const content = readFileSync8(path, "utf8");
6459
+ const content = readFileSync10(path, "utf8");
6139
6460
  for (const line of content.split(/\r?\n/)) {
6140
6461
  const trimmed = line.trim();
6141
6462
  if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
@@ -6157,7 +6478,7 @@ function resolveSnapshotGitWorktree(config) {
6157
6478
  if (fromProfile) return fromProfile;
6158
6479
  }
6159
6480
  const defaultPath = "/root/wiki-git";
6160
- return existsSync11(defaultPath) ? defaultPath : void 0;
6481
+ return existsSync13(defaultPath) ? defaultPath : void 0;
6161
6482
  }
6162
6483
  function vaultSyncChecks(input) {
6163
6484
  const os = input.os ?? platform2();
@@ -6174,16 +6495,16 @@ function vaultSyncChecks(input) {
6174
6495
  ];
6175
6496
  }
6176
6497
  const isMac = os === "darwin";
6177
- const logDir = input.logDir ?? (isMac ? join22(home, "Library", "Logs") : join22(home, ".local", "state", "vault-sync", "log"));
6178
- const shareDir = input.shareDir ?? (isMac ? join22(home, "Library", "Application Support", "vault-sync", "bin") : join22(home, ".local", "share", "vault-sync", "bin"));
6179
- const filterPath = input.filterPath ?? join22(home, ".config", "rclone", "wiki-push-filters.txt");
6180
- const packagedSnapshotPath = join22(shareDir, "wiki-snapshot.sh");
6498
+ const logDir = input.logDir ?? (isMac ? join24(home, "Library", "Logs") : join24(home, ".local", "state", "vault-sync", "log"));
6499
+ const shareDir = input.shareDir ?? (isMac ? join24(home, "Library", "Application Support", "vault-sync", "bin") : join24(home, ".local", "share", "vault-sync", "bin"));
6500
+ const filterPath = input.filterPath ?? join24(home, ".config", "rclone", "wiki-push-filters.txt");
6501
+ const packagedSnapshotPath = join24(shareDir, "wiki-snapshot.sh");
6181
6502
  const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
6182
- const snapshotPath = input.snapshotScriptPath ?? (existsSync11(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
6503
+ const snapshotPath = input.snapshotScriptPath ?? (existsSync13(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
6183
6504
  function snapshotLastStatusCheck() {
6184
- const snapshotLog = join22(logDir, "wiki-snapshot.log");
6505
+ const snapshotLog = join24(logDir, "wiki-snapshot.log");
6185
6506
  try {
6186
- const logContent = readFileSync8(snapshotLog, "utf8");
6507
+ const logContent = readFileSync10(snapshotLog, "utf8");
6187
6508
  const lines = logContent.trim().split("\n").filter(Boolean);
6188
6509
  if (lines.length === 0) {
6189
6510
  return check(
@@ -6228,14 +6549,14 @@ function vaultSyncChecks(input) {
6228
6549
  }
6229
6550
  }
6230
6551
  if (input.vaultSyncRole === "snapshotter") {
6231
- const c12 = existsSync11(snapshotPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found snapshot script: ${snapshotPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Snapshot script not found at ${snapshotPath}`);
6552
+ const c12 = existsSync13(snapshotPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found snapshot script: ${snapshotPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Snapshot script not found at ${snapshotPath}`);
6232
6553
  const serviceScope = input.vaultSyncServiceScope ?? "user";
6233
- const userTimerPath = join22(home, ".config", "systemd", "user", "wiki-snapshot.timer");
6554
+ const userTimerPath = join24(home, ".config", "systemd", "user", "wiki-snapshot.timer");
6234
6555
  const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
6235
6556
  let c22;
6236
- if (serviceScope === "user" && existsSync11(userTimerPath)) {
6557
+ if (serviceScope === "user" && existsSync13(userTimerPath)) {
6237
6558
  c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${userTimerPath}`);
6238
- } else if (serviceScope === "system" && existsSync11(systemTimerPath)) {
6559
+ } else if (serviceScope === "system" && existsSync13(systemTimerPath)) {
6239
6560
  c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${systemTimerPath}`);
6240
6561
  } else if (os !== "linux") {
6241
6562
  c22 = check("warn", "vault_sync_jobs_enabled", "Vault sync jobs enabled", "Snapshotter scheduler is Linux-only and no wiki-snapshot.timer file was found");
@@ -6267,7 +6588,7 @@ function vaultSyncChecks(input) {
6267
6588
  );
6268
6589
  let c52;
6269
6590
  try {
6270
- if (!existsSync11(snapshotPath)) {
6591
+ if (!existsSync13(snapshotPath)) {
6271
6592
  c52 = check(
6272
6593
  "error",
6273
6594
  "vault_sync_snapshot_guard",
@@ -6275,7 +6596,7 @@ function vaultSyncChecks(input) {
6275
6596
  `Snapshot script not found at ${snapshotPath}`
6276
6597
  );
6277
6598
  } else {
6278
- const content = readFileSync8(snapshotPath, "utf8");
6599
+ const content = readFileSync10(snapshotPath, "utf8");
6279
6600
  if (!content.includes("--max-delete")) {
6280
6601
  c52 = check(
6281
6602
  "error",
@@ -6302,8 +6623,8 @@ function vaultSyncChecks(input) {
6302
6623
  }
6303
6624
  return [c12, c22, c32, cFetch2, c42, c52];
6304
6625
  }
6305
- const pushScriptPath = join22(shareDir, "wiki-push.sh");
6306
- const c1 = existsSync11(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`);
6626
+ const pushScriptPath = join24(shareDir, "wiki-push.sh");
6627
+ const c1 = existsSync13(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`);
6307
6628
  let c2;
6308
6629
  try {
6309
6630
  if (isMac) {
@@ -6354,10 +6675,10 @@ function vaultSyncChecks(input) {
6354
6675
  "Scheduler check failed \u2014 run vault-sync-install"
6355
6676
  );
6356
6677
  }
6357
- const logFile = join22(logDir, "wiki-push.log");
6678
+ const logFile = join24(logDir, "wiki-push.log");
6358
6679
  let c3;
6359
6680
  try {
6360
- const logContent = readFileSync8(logFile, "utf8");
6681
+ const logContent = readFileSync10(logFile, "utf8");
6361
6682
  const lines = logContent.trim().split("\n").filter(Boolean);
6362
6683
  if (lines.length === 0) {
6363
6684
  c3 = check(
@@ -6413,7 +6734,7 @@ function vaultSyncChecks(input) {
6413
6734
  }
6414
6735
  }
6415
6736
  } catch {
6416
- c3 = existsSync11(logDir) ? check(
6737
+ c3 = existsSync13(logDir) ? check(
6417
6738
  "warn",
6418
6739
  "vault_sync_last_push_age",
6419
6740
  "Vault sync last push recency",
@@ -6425,10 +6746,10 @@ function vaultSyncChecks(input) {
6425
6746
  `Log directory not found at ${logDir}`
6426
6747
  );
6427
6748
  }
6428
- const fetchLogFile = join22(logDir, "wiki-fetch.log");
6749
+ const fetchLogFile = join24(logDir, "wiki-fetch.log");
6429
6750
  let cFetch;
6430
6751
  try {
6431
- const logContent = readFileSync8(fetchLogFile, "utf8");
6752
+ const logContent = readFileSync10(fetchLogFile, "utf8");
6432
6753
  const lines = logContent.trim().split("\n").filter(Boolean);
6433
6754
  if (lines.length === 0) {
6434
6755
  cFetch = check(
@@ -6472,7 +6793,7 @@ function vaultSyncChecks(input) {
6472
6793
  }
6473
6794
  let c4;
6474
6795
  try {
6475
- if (!existsSync11(filterPath)) {
6796
+ if (!existsSync13(filterPath)) {
6476
6797
  c4 = check(
6477
6798
  "error",
6478
6799
  "vault_sync_filter_present",
@@ -6480,7 +6801,7 @@ function vaultSyncChecks(input) {
6480
6801
  `Filter file not found at ${filterPath}`
6481
6802
  );
6482
6803
  } else {
6483
- const content = readFileSync8(filterPath, "utf8");
6804
+ const content = readFileSync10(filterPath, "utf8");
6484
6805
  const requiredExcludes = [
6485
6806
  "remotely-save/data.json",
6486
6807
  ".skillwiki/sync.lock",
@@ -6523,7 +6844,7 @@ function vaultSyncChecks(input) {
6523
6844
  );
6524
6845
  } else {
6525
6846
  try {
6526
- if (!existsSync11(snapshotPath)) {
6847
+ if (!existsSync13(snapshotPath)) {
6527
6848
  c5 = check(
6528
6849
  "error",
6529
6850
  "vault_sync_snapshot_guard",
@@ -6531,7 +6852,7 @@ function vaultSyncChecks(input) {
6531
6852
  `Snapshot script not found at ${snapshotPath}`
6532
6853
  );
6533
6854
  } else {
6534
- const content = readFileSync8(snapshotPath, "utf8");
6855
+ const content = readFileSync10(snapshotPath, "utf8");
6535
6856
  if (!content.includes("--max-delete")) {
6536
6857
  c5 = check(
6537
6858
  "error",
@@ -6563,33 +6884,33 @@ function findSkillMd(dir) {
6563
6884
  const results = [];
6564
6885
  let entries;
6565
6886
  try {
6566
- entries = readdirSync2(dir, { withFileTypes: true });
6887
+ entries = readdirSync3(dir, { withFileTypes: true });
6567
6888
  } catch {
6568
6889
  return results;
6569
6890
  }
6570
6891
  for (const entry of entries) {
6571
6892
  if (entry.isFile() && entry.name === "SKILL.md") {
6572
- results.push(join22(dir, entry.name));
6893
+ results.push(join24(dir, entry.name));
6573
6894
  } else if (entry.isDirectory()) {
6574
- results.push(...findSkillMd(join22(dir, entry.name)));
6895
+ results.push(...findSkillMd(join24(dir, entry.name)));
6575
6896
  }
6576
6897
  }
6577
6898
  return results;
6578
6899
  }
6579
6900
  function findInstalledSkillMd(dir) {
6580
- const directSkills = findSkillNames(dir).map((name) => join22(dir, name, "SKILL.md"));
6901
+ const directSkills = findSkillNames(dir).map((name) => join24(dir, name, "SKILL.md"));
6581
6902
  return directSkills.length > 0 ? directSkills : findSkillMd(dir);
6582
6903
  }
6583
6904
  function findSkillNames(dir) {
6584
6905
  const results = [];
6585
6906
  let entries;
6586
6907
  try {
6587
- entries = readdirSync2(dir, { withFileTypes: true });
6908
+ entries = readdirSync3(dir, { withFileTypes: true });
6588
6909
  } catch {
6589
6910
  return results;
6590
6911
  }
6591
6912
  for (const entry of entries) {
6592
- if (entry.isDirectory() && existsSync11(join22(dir, entry.name, "SKILL.md"))) {
6913
+ if (entry.isDirectory() && existsSync13(join24(dir, entry.name, "SKILL.md"))) {
6593
6914
  results.push(entry.name);
6594
6915
  }
6595
6916
  }
@@ -6633,7 +6954,7 @@ async function vaultMetrics(resolvedPath) {
6633
6954
  }
6634
6955
  let logLines = 0;
6635
6956
  try {
6636
- logLines = readFileSync8(join22(resolvedPath, "log.md"), "utf8").split("\n").length;
6957
+ logLines = readFileSync10(join24(resolvedPath, "log.md"), "utf8").split("\n").length;
6637
6958
  } catch {
6638
6959
  }
6639
6960
  return [
@@ -6690,7 +7011,13 @@ async function runDoctor(input) {
6690
7011
  checks.push(checkVaultGitAhead(gitCheckPath));
6691
7012
  checks.push(checkVaultGitBehind(gitCheckPath));
6692
7013
  checks.push(checkVaultGitPullFailures(input.home));
7014
+ checks.push(checkVaultLocalGit(gitCheckPath));
7015
+ checks.push(checkVaultGithubRemote(gitCheckPath, input.execProbe));
7016
+ checks.push(checkVaultS3Remote(input.home, input.execProbe));
7017
+ checks.push(checkVaultSnapshotterReachable(fleetLoad, input.checkSnapshotter, input.execProbe));
7018
+ checks.push(checkVaultPromotionLag(gitCheckPath));
6693
7019
  checks.push(checkDotStoreClean(resolvedPath));
7020
+ checks.push(checkVaultConflictMarkers(resolvedPath));
6694
7021
  checks.push(checkS3MountPerf(resolvedPath));
6695
7022
  checks.push(checkS3MountFreshness(resolvedPath));
6696
7023
  checks.push(checkRcloneFlagAudit(resolvedPath));
@@ -6735,7 +7062,7 @@ async function runDoctor(input) {
6735
7062
  }
6736
7063
 
6737
7064
  // src/utils/package-info.ts
6738
- import { readFileSync as readFileSync9 } from "fs";
7065
+ import { readFileSync as readFileSync11 } from "fs";
6739
7066
  function packageJsonCandidateUrls(baseUrl = import.meta.url) {
6740
7067
  return [
6741
7068
  new URL("../package.json", baseUrl),
@@ -6745,7 +7072,7 @@ function packageJsonCandidateUrls(baseUrl = import.meta.url) {
6745
7072
  function readCliPackageJson(baseUrl = import.meta.url) {
6746
7073
  for (const url of packageJsonCandidateUrls(baseUrl)) {
6747
7074
  try {
6748
- const pkg = JSON.parse(readFileSync9(url, "utf8"));
7075
+ const pkg = JSON.parse(readFileSync11(url, "utf8"));
6749
7076
  if (typeof pkg.version === "string") {
6750
7077
  return { ...pkg, version: pkg.version };
6751
7078
  }
@@ -6757,7 +7084,7 @@ function readCliPackageJson(baseUrl = import.meta.url) {
6757
7084
 
6758
7085
  // src/commands/project-index.ts
6759
7086
  import { readdir as readdir4, readFile as readFile16, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
6760
- import { join as join23, dirname as dirname8, basename as basename2 } from "path";
7087
+ import { join as join25, dirname as dirname8, basename as basename2 } from "path";
6761
7088
  var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
6762
7089
  var PROJECT_LOCAL_DIRS = ["requirements", "work", "architecture", "history"];
6763
7090
  async function scanMarkdownTree(rootAbs, rootRel) {
@@ -6769,7 +7096,7 @@ async function scanMarkdownTree(rootAbs, rootRel) {
6769
7096
  return found;
6770
7097
  }
6771
7098
  for (const entry of entries) {
6772
- const abs = join23(rootAbs, entry.name);
7099
+ const abs = join25(rootAbs, entry.name);
6773
7100
  const rel = `${rootRel}/${entry.name}`;
6774
7101
  if (entry.isDirectory()) {
6775
7102
  found.push(...await scanMarkdownTree(abs, rel));
@@ -6799,7 +7126,7 @@ function projectLocalType(slug, page, data) {
6799
7126
  }
6800
7127
  async function runProjectIndex(input) {
6801
7128
  const slug = input.slug;
6802
- const projectDir = join23(input.vault, "projects", slug);
7129
+ const projectDir = join25(input.vault, "projects", slug);
6803
7130
  try {
6804
7131
  await readdir4(projectDir);
6805
7132
  } catch {
@@ -6810,12 +7137,12 @@ async function runProjectIndex(input) {
6810
7137
  }
6811
7138
  const wikilinkPattern = `[[${slug}]]`;
6812
7139
  const entries = [];
6813
- const compoundDir = join23(input.vault, "projects", slug, "compound");
7140
+ const compoundDir = join25(input.vault, "projects", slug, "compound");
6814
7141
  try {
6815
7142
  const compoundFiles = await readdir4(compoundDir, { withFileTypes: true });
6816
7143
  for (const entry of compoundFiles) {
6817
7144
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
6818
- const filePath = join23(compoundDir, entry.name);
7145
+ const filePath = join25(compoundDir, entry.name);
6819
7146
  let text;
6820
7147
  try {
6821
7148
  text = await readFile16(filePath, "utf8");
@@ -6835,13 +7162,13 @@ async function runProjectIndex(input) {
6835
7162
  for (const dir of LAYER2_DIRS) {
6836
7163
  let files;
6837
7164
  try {
6838
- files = await readdir4(join23(input.vault, dir), { withFileTypes: true });
7165
+ files = await readdir4(join25(input.vault, dir), { withFileTypes: true });
6839
7166
  } catch {
6840
7167
  continue;
6841
7168
  }
6842
7169
  for (const entry of files) {
6843
7170
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
6844
- const filePath = join23(input.vault, dir, entry.name);
7171
+ const filePath = join25(input.vault, dir, entry.name);
6845
7172
  let text;
6846
7173
  try {
6847
7174
  text = await readFile16(filePath, "utf8");
@@ -6860,11 +7187,11 @@ async function runProjectIndex(input) {
6860
7187
  }
6861
7188
  }
6862
7189
  for (const dir of PROJECT_LOCAL_DIRS) {
6863
- const rootAbs = join23(projectDir, dir);
7190
+ const rootAbs = join25(projectDir, dir);
6864
7191
  const rootRel = `projects/${slug}/${dir}`;
6865
7192
  const pages = await scanMarkdownTree(rootAbs, rootRel);
6866
7193
  for (const page of pages) {
6867
- const filePath = join23(input.vault, page);
7194
+ const filePath = join25(input.vault, page);
6868
7195
  let text;
6869
7196
  try {
6870
7197
  text = await readFile16(filePath, "utf8");
@@ -6886,7 +7213,7 @@ async function runProjectIndex(input) {
6886
7213
  const tb = typeOrder[b.type] ?? 99;
6887
7214
  return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
6888
7215
  });
6889
- const indexPath = join23(projectDir, "knowledge.md");
7216
+ const indexPath = join25(projectDir, "knowledge.md");
6890
7217
  let existing = false;
6891
7218
  let stale = false;
6892
7219
  try {
@@ -6960,8 +7287,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
6960
7287
 
6961
7288
  // src/commands/observe.ts
6962
7289
  import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
6963
- import { existsSync as existsSync12, statSync as statSync2 } from "fs";
6964
- import { join as join24 } from "path";
7290
+ import { existsSync as existsSync14, statSync as statSync2 } from "fs";
7291
+ import { join as join26 } from "path";
6965
7292
  import { createHash as createHash5 } from "crypto";
6966
7293
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
6967
7294
  function slugify(text) {
@@ -6984,13 +7311,13 @@ async function runObserve(input) {
6984
7311
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
6985
7312
  };
6986
7313
  }
6987
- if (!existsSync12(input.vault) || !statSync2(input.vault).isDirectory()) {
7314
+ if (!existsSync14(input.vault) || !statSync2(input.vault).isDirectory()) {
6988
7315
  return {
6989
7316
  exitCode: ExitCode.VAULT_PATH_INVALID,
6990
7317
  result: err("VAULT_PATH_INVALID", { path: input.vault })
6991
7318
  };
6992
7319
  }
6993
- const transcriptsDir = join24(input.vault, "raw", "transcripts");
7320
+ const transcriptsDir = join26(input.vault, "raw", "transcripts");
6994
7321
  try {
6995
7322
  await mkdir6(transcriptsDir, { recursive: true });
6996
7323
  } catch {
@@ -7002,7 +7329,7 @@ async function runObserve(input) {
7002
7329
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7003
7330
  const slug = slugify(input.text);
7004
7331
  const fileName = `${today}-observation-${slug}.md`;
7005
- const filePath = join24(transcriptsDir, fileName);
7332
+ const filePath = join26(transcriptsDir, fileName);
7006
7333
  const body = `
7007
7334
  ${input.text.trim()}
7008
7335
  `;
@@ -7044,7 +7371,7 @@ ${input.text.trim()}
7044
7371
  // src/commands/memory.ts
7045
7372
  import { createHash as createHash6 } from "crypto";
7046
7373
  import { mkdir as mkdir7, readFile as readFile17, readdir as readdir5, stat as stat5, writeFile as writeFile8 } from "fs/promises";
7047
- import { basename as basename3, extname, join as join25, relative as relative4, sep as sep4 } from "path";
7374
+ import { basename as basename3, extname, join as join27, relative as relative4, sep as sep4 } from "path";
7048
7375
  async function runMemoryTopics(input) {
7049
7376
  const scan = await scanVault(input.vault);
7050
7377
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -7108,8 +7435,8 @@ async function runMemoryIndex(input) {
7108
7435
  }
7109
7436
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
7110
7437
  const relCachePath = memoryCacheRelPath(input.project);
7111
- const absCachePath = join25(input.vault, relCachePath);
7112
- await mkdir7(join25(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
7438
+ const absCachePath = join27(input.vault, relCachePath);
7439
+ await mkdir7(join27(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
7113
7440
  await writeFile8(absCachePath, `${JSON.stringify({
7114
7441
  generated_at: generatedAt,
7115
7442
  project: input.project,
@@ -7301,7 +7628,7 @@ async function buildMemoryIndexState(pages, project) {
7301
7628
  }
7302
7629
  async function checkMemoryIndex(vault, project, current) {
7303
7630
  const relCachePath = memoryCacheRelPath(project);
7304
- const cacheText = await readIfExists2(join25(vault, relCachePath));
7631
+ const cacheText = await readIfExists2(join27(vault, relCachePath));
7305
7632
  if (!cacheText) {
7306
7633
  return {
7307
7634
  ok: true,
@@ -7710,7 +8037,7 @@ async function walkImportFiles(dir, out) {
7710
8037
  const entries = await readdir5(dir, { withFileTypes: true });
7711
8038
  for (const entry of entries) {
7712
8039
  if (entry.name === ".git" || entry.name === "node_modules") continue;
7713
- const path = join25(dir, entry.name);
8040
+ const path = join27(dir, entry.name);
7714
8041
  if (entry.isDirectory()) {
7715
8042
  await walkImportFiles(path, out);
7716
8043
  } else if (entry.isFile() && isImportCandidate(path)) {
@@ -7777,8 +8104,8 @@ async function writeImportCapture(vault, entry, today) {
7777
8104
  const content = hiddenString(entry, "__content");
7778
8105
  const project = hiddenString(entry, "__project");
7779
8106
  const relPath = await availableImportPath(vault, entry.proposed_path);
7780
- const absPath = join25(vault, relPath);
7781
- await mkdir7(join25(vault, "raw", "transcripts"), { recursive: true });
8107
+ const absPath = join27(vault, relPath);
8108
+ await mkdir7(join27(vault, "raw", "transcripts"), { recursive: true });
7782
8109
  await writeFile8(absPath, renderImportCapture(entry, content, project, today), "utf8");
7783
8110
  const validation = await runValidate({ file: absPath });
7784
8111
  return {
@@ -7794,7 +8121,7 @@ async function availableImportPath(vault, proposed) {
7794
8121
  const stem = proposed.slice(0, -ext.length);
7795
8122
  let candidate = proposed;
7796
8123
  let i = 2;
7797
- while (await readIfExists2(join25(vault, candidate))) {
8124
+ while (await readIfExists2(join27(vault, candidate))) {
7798
8125
  candidate = `${stem}-${i}${ext}`;
7799
8126
  i++;
7800
8127
  }
@@ -7966,10 +8293,10 @@ function memoryCacheRelPath(project) {
7966
8293
  }
7967
8294
  async function readMemoryCache(vault, project) {
7968
8295
  if (project) {
7969
- const projectCache = await readIfExists2(join25(vault, memoryCacheRelPath(project)));
8296
+ const projectCache = await readIfExists2(join27(vault, memoryCacheRelPath(project)));
7970
8297
  if (projectCache) return projectCache;
7971
8298
  }
7972
- return readIfExists2(join25(vault, ".skillwiki", "memory-topics.json"));
8299
+ return readIfExists2(join27(vault, ".skillwiki", "memory-topics.json"));
7973
8300
  }
7974
8301
  function dedupePages(pages) {
7975
8302
  const seen = /* @__PURE__ */ new Set();
@@ -8056,7 +8383,7 @@ function slugify2(value) {
8056
8383
 
8057
8384
  // src/commands/query.ts
8058
8385
  import { readFile as readFile18, stat as stat6 } from "fs/promises";
8059
- import { join as join26 } from "path";
8386
+ import { join as join28 } from "path";
8060
8387
  var W_KEYWORD = 2;
8061
8388
  var W_SOURCE_OVERLAP = 4;
8062
8389
  var W_WIKILINK = 3;
@@ -8177,7 +8504,7 @@ function computeKeywordScore(terms, title, tags, body) {
8177
8504
  return score;
8178
8505
  }
8179
8506
  async function loadOrBuildGraph(vault) {
8180
- const graphPath = join26(vault, ".skillwiki", "graph.json");
8507
+ const graphPath = join28(vault, ".skillwiki", "graph.json");
8181
8508
  let needsBuild = false;
8182
8509
  try {
8183
8510
  const fileStat = await stat6(graphPath);
@@ -8206,7 +8533,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
8206
8533
  import { z as z2 } from "zod";
8207
8534
 
8208
8535
  // src/mcp/vault-resolve.ts
8209
- import { join as join27, resolve as resolve7 } from "path";
8536
+ import { join as join29, resolve as resolve7 } from "path";
8210
8537
 
8211
8538
  // src/mcp/allowlist.ts
8212
8539
  import { resolve as resolve6, sep as sep5 } from "path";
@@ -8268,7 +8595,7 @@ async function resolveMcpVault(input) {
8268
8595
  return ok({ vault: vaultPath, source });
8269
8596
  }
8270
8597
  function defaultGraphOut(vault) {
8271
- return join27(vault, ".skillwiki", "graph.json");
8598
+ return join29(vault, ".skillwiki", "graph.json");
8272
8599
  }
8273
8600
 
8274
8601
  // src/mcp/result-format.ts
@@ -8285,7 +8612,7 @@ function formatToolResult(payload) {
8285
8612
  // src/mcp/audit-log.ts
8286
8613
  import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
8287
8614
  import { homedir } from "os";
8288
- import { join as join28 } from "path";
8615
+ import { join as join30 } from "path";
8289
8616
  function auditEnabled() {
8290
8617
  const v = process.env.SKILLWIKI_MCP_AUDIT;
8291
8618
  if (v === "0" || v === "false") return false;
@@ -8297,7 +8624,7 @@ function auditSink() {
8297
8624
  function auditFilePath() {
8298
8625
  const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
8299
8626
  if (custom && custom.length > 0) return custom;
8300
- return join28(homedir(), ".skillwiki", "mcp-audit.jsonl");
8627
+ return join30(homedir(), ".skillwiki", "mcp-audit.jsonl");
8301
8628
  }
8302
8629
  function auditMcpToolCall(entry) {
8303
8630
  if (!auditEnabled()) return;
@@ -8307,7 +8634,7 @@ function auditMcpToolCall(entry) {
8307
8634
  return;
8308
8635
  }
8309
8636
  const path = auditFilePath();
8310
- mkdirSync4(join28(path, ".."), { recursive: true });
8637
+ mkdirSync4(join30(path, ".."), { recursive: true });
8311
8638
  appendFileSync(path, line, "utf8");
8312
8639
  }
8313
8640
  async function runMcpToolHandler(tool, input, fn) {
@@ -8528,7 +8855,7 @@ function registerMcpMutatingTools(server) {
8528
8855
 
8529
8856
  // src/mcp/resources.ts
8530
8857
  import { readFile as readFile20 } from "fs/promises";
8531
- import { join as join30 } from "path";
8858
+ import { join as join32 } from "path";
8532
8859
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
8533
8860
 
8534
8861
  // src/mcp/lint-bucket.ts
@@ -8657,8 +8984,8 @@ async function fetchQueryPreview(input) {
8657
8984
 
8658
8985
  // src/mcp/graph-html.ts
8659
8986
  import { readFile as readFile19 } from "fs/promises";
8660
- import { join as join29 } from "path";
8661
- import { existsSync as existsSync13 } from "fs";
8987
+ import { join as join31 } from "path";
8988
+ import { existsSync as existsSync15 } from "fs";
8662
8989
  var TYPE_COLORS = {
8663
8990
  entities: "#e74c3c",
8664
8991
  concepts: "#27ae60",
@@ -8726,9 +9053,9 @@ ${nodeSvg}
8726
9053
  return { html, node_count: nodes.length, edge_count: edges.length, truncated };
8727
9054
  }
8728
9055
  async function fetchGraphHtmlReport(input) {
8729
- const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
9056
+ const graphPath = input.graphPath ?? join31(input.vault, ".skillwiki", "graph.json");
8730
9057
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
8731
- if (!existsSync13(graphPath)) {
9058
+ if (!existsSync15(graphPath)) {
8732
9059
  return {
8733
9060
  exitCode: ExitCode.FILE_NOT_FOUND,
8734
9061
  result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
@@ -8800,7 +9127,7 @@ async function fetchStaleSummary(input) {
8800
9127
 
8801
9128
  // src/mcp/resources.ts
8802
9129
  async function readVaultFile(vault, rel) {
8803
- return readFile20(join30(vault, rel), "utf8");
9130
+ return readFile20(join32(vault, rel), "utf8");
8804
9131
  }
8805
9132
  async function tailLines(text, lines) {
8806
9133
  const parts = text.split(/\r?\n/);
@@ -8886,7 +9213,7 @@ function registerMcpResources(server) {
8886
9213
  if (!v.ok) {
8887
9214
  return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
8888
9215
  }
8889
- const path = join30(v.data.vault, ".skillwiki", "graph.json");
9216
+ const path = join32(v.data.vault, ".skillwiki", "graph.json");
8890
9217
  try {
8891
9218
  const raw = await readFile20(path, "utf8");
8892
9219
  const graph = JSON.parse(raw);
@@ -9251,10 +9578,13 @@ export {
9251
9578
  runConfigPath,
9252
9579
  writeCache,
9253
9580
  triggerAutoUpdate,
9581
+ buildDegradedReasons,
9582
+ probeRemoteHealth,
9254
9583
  FLEET_REL_PATH,
9255
9584
  runFleetValidate,
9256
9585
  runFleetContext,
9257
9586
  loadFleetManifestAndHost,
9587
+ snapshotterAliasForLocalHost,
9258
9588
  loadFleetManifest,
9259
9589
  resolveFleetHostId,
9260
9590
  SATELLITE_STALE_MS,