skillwiki 0.9.51 → 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.
@@ -1823,9 +1823,9 @@ async function runStale(input) {
1823
1823
  if (input.project && !project.includes(input.project)) return null;
1824
1824
  let inferred = false;
1825
1825
  if (input.forceScan && !kind) {
1826
- const basename3 = t.relPath.split("/").pop();
1827
- if (!LOOP_CYCLE_PATTERN.test(basename3)) {
1828
- const m = basename3.match(KIND_FROM_FILENAME);
1826
+ const basename4 = t.relPath.split("/").pop();
1827
+ if (!LOOP_CYCLE_PATTERN.test(basename4)) {
1828
+ const m = basename4.match(KIND_FROM_FILENAME);
1829
1829
  if (m) {
1830
1830
  kind = m[1];
1831
1831
  inferred = true;
@@ -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,11 +7084,49 @@ 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 } 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"];
7089
+ var PROJECT_LOCAL_DIRS = ["requirements", "work", "architecture", "history"];
7090
+ async function scanMarkdownTree(rootAbs, rootRel) {
7091
+ const found = [];
7092
+ let entries;
7093
+ try {
7094
+ entries = await readdir4(rootAbs, { withFileTypes: true });
7095
+ } catch {
7096
+ return found;
7097
+ }
7098
+ for (const entry of entries) {
7099
+ const abs = join25(rootAbs, entry.name);
7100
+ const rel = `${rootRel}/${entry.name}`;
7101
+ if (entry.isDirectory()) {
7102
+ found.push(...await scanMarkdownTree(abs, rel));
7103
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
7104
+ found.push(rel);
7105
+ }
7106
+ }
7107
+ return found;
7108
+ }
7109
+ function projectLocalType(slug, page, data) {
7110
+ if (page.startsWith(`projects/${slug}/requirements/`)) return "requirement";
7111
+ if (page.startsWith(`projects/${slug}/work/`)) {
7112
+ if (typeof data.kind === "string") return data.kind;
7113
+ const name = basename2(page, ".md");
7114
+ if (name === "spec" || name === "plan" || name === "retro") return name;
7115
+ return "work";
7116
+ }
7117
+ if (page.startsWith(`projects/${slug}/architecture/`)) {
7118
+ return typeof data.type === "string" ? data.type : "architecture";
7119
+ }
7120
+ if (page.startsWith(`projects/${slug}/history/`)) {
7121
+ if (typeof data.kind === "string") return data.kind;
7122
+ if (typeof data.type === "string") return data.type;
7123
+ return "history";
7124
+ }
7125
+ return typeof data.type === "string" ? data.type : "project";
7126
+ }
6762
7127
  async function runProjectIndex(input) {
6763
7128
  const slug = input.slug;
6764
- const projectDir = join23(input.vault, "projects", slug);
7129
+ const projectDir = join25(input.vault, "projects", slug);
6765
7130
  try {
6766
7131
  await readdir4(projectDir);
6767
7132
  } catch {
@@ -6772,12 +7137,12 @@ async function runProjectIndex(input) {
6772
7137
  }
6773
7138
  const wikilinkPattern = `[[${slug}]]`;
6774
7139
  const entries = [];
6775
- const compoundDir = join23(input.vault, "projects", slug, "compound");
7140
+ const compoundDir = join25(input.vault, "projects", slug, "compound");
6776
7141
  try {
6777
7142
  const compoundFiles = await readdir4(compoundDir, { withFileTypes: true });
6778
7143
  for (const entry of compoundFiles) {
6779
7144
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
6780
- const filePath = join23(compoundDir, entry.name);
7145
+ const filePath = join25(compoundDir, entry.name);
6781
7146
  let text;
6782
7147
  try {
6783
7148
  text = await readFile16(filePath, "utf8");
@@ -6797,13 +7162,13 @@ async function runProjectIndex(input) {
6797
7162
  for (const dir of LAYER2_DIRS) {
6798
7163
  let files;
6799
7164
  try {
6800
- files = await readdir4(join23(input.vault, dir), { withFileTypes: true });
7165
+ files = await readdir4(join25(input.vault, dir), { withFileTypes: true });
6801
7166
  } catch {
6802
7167
  continue;
6803
7168
  }
6804
7169
  for (const entry of files) {
6805
7170
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
6806
- const filePath = join23(input.vault, dir, entry.name);
7171
+ const filePath = join25(input.vault, dir, entry.name);
6807
7172
  let text;
6808
7173
  try {
6809
7174
  text = await readFile16(filePath, "utf8");
@@ -6821,13 +7186,34 @@ async function runProjectIndex(input) {
6821
7186
  });
6822
7187
  }
6823
7188
  }
6824
- const typeOrder = { entity: 0, concept: 1, comparison: 2, query: 3, summary: 4, meta: 5, pattern: 6, gotcha: 7, lesson: 8, antipattern: 9, compound: 10 };
7189
+ for (const dir of PROJECT_LOCAL_DIRS) {
7190
+ const rootAbs = join25(projectDir, dir);
7191
+ const rootRel = `projects/${slug}/${dir}`;
7192
+ const pages = await scanMarkdownTree(rootAbs, rootRel);
7193
+ for (const page of pages) {
7194
+ const filePath = join25(input.vault, page);
7195
+ let text;
7196
+ try {
7197
+ text = await readFile16(filePath, "utf8");
7198
+ } catch {
7199
+ continue;
7200
+ }
7201
+ const fm = extractFrontmatter(text);
7202
+ if (!fm.ok) continue;
7203
+ entries.push({
7204
+ page,
7205
+ type: projectLocalType(slug, page, fm.data),
7206
+ title: typeof fm.data.title === "string" ? fm.data.title : basename2(page, ".md")
7207
+ });
7208
+ }
7209
+ }
7210
+ const typeOrder = { entity: 0, concept: 1, comparison: 2, query: 3, summary: 4, meta: 5, requirement: 6, spec: 7, plan: 8, retro: 9, architecture: 10, pattern: 11, gotcha: 12, lesson: 13, antipattern: 14, compound: 15, work: 16, history: 17 };
6825
7211
  entries.sort((a, b) => {
6826
7212
  const ta = typeOrder[a.type] ?? 99;
6827
7213
  const tb = typeOrder[b.type] ?? 99;
6828
7214
  return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
6829
7215
  });
6830
- const indexPath = join23(projectDir, "knowledge.md");
7216
+ const indexPath = join25(projectDir, "knowledge.md");
6831
7217
  let existing = false;
6832
7218
  let stale = false;
6833
7219
  try {
@@ -6901,8 +7287,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
6901
7287
 
6902
7288
  // src/commands/observe.ts
6903
7289
  import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
6904
- import { existsSync as existsSync12, statSync as statSync2 } from "fs";
6905
- import { join as join24 } from "path";
7290
+ import { existsSync as existsSync14, statSync as statSync2 } from "fs";
7291
+ import { join as join26 } from "path";
6906
7292
  import { createHash as createHash5 } from "crypto";
6907
7293
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
6908
7294
  function slugify(text) {
@@ -6925,13 +7311,13 @@ async function runObserve(input) {
6925
7311
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
6926
7312
  };
6927
7313
  }
6928
- if (!existsSync12(input.vault) || !statSync2(input.vault).isDirectory()) {
7314
+ if (!existsSync14(input.vault) || !statSync2(input.vault).isDirectory()) {
6929
7315
  return {
6930
7316
  exitCode: ExitCode.VAULT_PATH_INVALID,
6931
7317
  result: err("VAULT_PATH_INVALID", { path: input.vault })
6932
7318
  };
6933
7319
  }
6934
- const transcriptsDir = join24(input.vault, "raw", "transcripts");
7320
+ const transcriptsDir = join26(input.vault, "raw", "transcripts");
6935
7321
  try {
6936
7322
  await mkdir6(transcriptsDir, { recursive: true });
6937
7323
  } catch {
@@ -6943,7 +7329,7 @@ async function runObserve(input) {
6943
7329
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6944
7330
  const slug = slugify(input.text);
6945
7331
  const fileName = `${today}-observation-${slug}.md`;
6946
- const filePath = join24(transcriptsDir, fileName);
7332
+ const filePath = join26(transcriptsDir, fileName);
6947
7333
  const body = `
6948
7334
  ${input.text.trim()}
6949
7335
  `;
@@ -6985,7 +7371,7 @@ ${input.text.trim()}
6985
7371
  // src/commands/memory.ts
6986
7372
  import { createHash as createHash6 } from "crypto";
6987
7373
  import { mkdir as mkdir7, readFile as readFile17, readdir as readdir5, stat as stat5, writeFile as writeFile8 } from "fs/promises";
6988
- import { basename as basename2, 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";
6989
7375
  async function runMemoryTopics(input) {
6990
7376
  const scan = await scanVault(input.vault);
6991
7377
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -7049,8 +7435,8 @@ async function runMemoryIndex(input) {
7049
7435
  }
7050
7436
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
7051
7437
  const relCachePath = memoryCacheRelPath(input.project);
7052
- const absCachePath = join25(input.vault, relCachePath);
7053
- 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 });
7054
7440
  await writeFile8(absCachePath, `${JSON.stringify({
7055
7441
  generated_at: generatedAt,
7056
7442
  project: input.project,
@@ -7242,7 +7628,7 @@ async function buildMemoryIndexState(pages, project) {
7242
7628
  }
7243
7629
  async function checkMemoryIndex(vault, project, current) {
7244
7630
  const relCachePath = memoryCacheRelPath(project);
7245
- const cacheText = await readIfExists2(join25(vault, relCachePath));
7631
+ const cacheText = await readIfExists2(join27(vault, relCachePath));
7246
7632
  if (!cacheText) {
7247
7633
  return {
7248
7634
  ok: true,
@@ -7651,7 +8037,7 @@ async function walkImportFiles(dir, out) {
7651
8037
  const entries = await readdir5(dir, { withFileTypes: true });
7652
8038
  for (const entry of entries) {
7653
8039
  if (entry.name === ".git" || entry.name === "node_modules") continue;
7654
- const path = join25(dir, entry.name);
8040
+ const path = join27(dir, entry.name);
7655
8041
  if (entry.isDirectory()) {
7656
8042
  await walkImportFiles(path, out);
7657
8043
  } else if (entry.isFile() && isImportCandidate(path)) {
@@ -7700,7 +8086,7 @@ async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
7700
8086
  }
7701
8087
  const redacted = redactSensitiveContent(extracted, { file });
7702
8088
  const privacy = redacted.findings.length > 0 ? "sensitive" : "local";
7703
- const sourceSlug = slugify2(basename2(file, extname(file)));
8089
+ const sourceSlug = slugify2(basename3(file, extname(file)));
7704
8090
  const relSource = relative4(sourceRoot, file).split(sep4).join("/");
7705
8091
  const entry = {
7706
8092
  ...baseEntry,
@@ -7718,8 +8104,8 @@ async function writeImportCapture(vault, entry, today) {
7718
8104
  const content = hiddenString(entry, "__content");
7719
8105
  const project = hiddenString(entry, "__project");
7720
8106
  const relPath = await availableImportPath(vault, entry.proposed_path);
7721
- const absPath = join25(vault, relPath);
7722
- await mkdir7(join25(vault, "raw", "transcripts"), { recursive: true });
8107
+ const absPath = join27(vault, relPath);
8108
+ await mkdir7(join27(vault, "raw", "transcripts"), { recursive: true });
7723
8109
  await writeFile8(absPath, renderImportCapture(entry, content, project, today), "utf8");
7724
8110
  const validation = await runValidate({ file: absPath });
7725
8111
  return {
@@ -7735,7 +8121,7 @@ async function availableImportPath(vault, proposed) {
7735
8121
  const stem = proposed.slice(0, -ext.length);
7736
8122
  let candidate = proposed;
7737
8123
  let i = 2;
7738
- while (await readIfExists2(join25(vault, candidate))) {
8124
+ while (await readIfExists2(join27(vault, candidate))) {
7739
8125
  candidate = `${stem}-${i}${ext}`;
7740
8126
  i++;
7741
8127
  }
@@ -7759,7 +8145,7 @@ function renderImportCapture(entry, content, project, today) {
7759
8145
  `source_paths: ["${entry.source_path.replaceAll('"', '\\"')}"]`,
7760
8146
  "---",
7761
8147
  "",
7762
- `# Imported Memory: ${basename2(entry.source_path, extname(entry.source_path))}`,
8148
+ `# Imported Memory: ${basename3(entry.source_path, extname(entry.source_path))}`,
7763
8149
  "",
7764
8150
  `Source kind: ${entry.source_kind}`,
7765
8151
  "",
@@ -7774,7 +8160,7 @@ function hiddenString(entry, key) {
7774
8160
  }
7775
8161
  function classifyImportSource(file) {
7776
8162
  const rel = file.split(sep4).join("/");
7777
- const name = basename2(file);
8163
+ const name = basename3(file);
7778
8164
  if (rel.includes("/.codex/memories/")) return "codex-memory";
7779
8165
  if (rel.includes("/.codex/rules/")) return "codex-rule";
7780
8166
  if (rel.includes("/.claude/") && name === "napkin.md") return "napkin";
@@ -7907,10 +8293,10 @@ function memoryCacheRelPath(project) {
7907
8293
  }
7908
8294
  async function readMemoryCache(vault, project) {
7909
8295
  if (project) {
7910
- const projectCache = await readIfExists2(join25(vault, memoryCacheRelPath(project)));
8296
+ const projectCache = await readIfExists2(join27(vault, memoryCacheRelPath(project)));
7911
8297
  if (projectCache) return projectCache;
7912
8298
  }
7913
- return readIfExists2(join25(vault, ".skillwiki", "memory-topics.json"));
8299
+ return readIfExists2(join27(vault, ".skillwiki", "memory-topics.json"));
7914
8300
  }
7915
8301
  function dedupePages(pages) {
7916
8302
  const seen = /* @__PURE__ */ new Set();
@@ -7997,7 +8383,7 @@ function slugify2(value) {
7997
8383
 
7998
8384
  // src/commands/query.ts
7999
8385
  import { readFile as readFile18, stat as stat6 } from "fs/promises";
8000
- import { join as join26 } from "path";
8386
+ import { join as join28 } from "path";
8001
8387
  var W_KEYWORD = 2;
8002
8388
  var W_SOURCE_OVERLAP = 4;
8003
8389
  var W_WIKILINK = 3;
@@ -8118,7 +8504,7 @@ function computeKeywordScore(terms, title, tags, body) {
8118
8504
  return score;
8119
8505
  }
8120
8506
  async function loadOrBuildGraph(vault) {
8121
- const graphPath = join26(vault, ".skillwiki", "graph.json");
8507
+ const graphPath = join28(vault, ".skillwiki", "graph.json");
8122
8508
  let needsBuild = false;
8123
8509
  try {
8124
8510
  const fileStat = await stat6(graphPath);
@@ -8147,7 +8533,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
8147
8533
  import { z as z2 } from "zod";
8148
8534
 
8149
8535
  // src/mcp/vault-resolve.ts
8150
- import { join as join27, resolve as resolve7 } from "path";
8536
+ import { join as join29, resolve as resolve7 } from "path";
8151
8537
 
8152
8538
  // src/mcp/allowlist.ts
8153
8539
  import { resolve as resolve6, sep as sep5 } from "path";
@@ -8209,7 +8595,7 @@ async function resolveMcpVault(input) {
8209
8595
  return ok({ vault: vaultPath, source });
8210
8596
  }
8211
8597
  function defaultGraphOut(vault) {
8212
- return join27(vault, ".skillwiki", "graph.json");
8598
+ return join29(vault, ".skillwiki", "graph.json");
8213
8599
  }
8214
8600
 
8215
8601
  // src/mcp/result-format.ts
@@ -8226,7 +8612,7 @@ function formatToolResult(payload) {
8226
8612
  // src/mcp/audit-log.ts
8227
8613
  import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
8228
8614
  import { homedir } from "os";
8229
- import { join as join28 } from "path";
8615
+ import { join as join30 } from "path";
8230
8616
  function auditEnabled() {
8231
8617
  const v = process.env.SKILLWIKI_MCP_AUDIT;
8232
8618
  if (v === "0" || v === "false") return false;
@@ -8238,7 +8624,7 @@ function auditSink() {
8238
8624
  function auditFilePath() {
8239
8625
  const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
8240
8626
  if (custom && custom.length > 0) return custom;
8241
- return join28(homedir(), ".skillwiki", "mcp-audit.jsonl");
8627
+ return join30(homedir(), ".skillwiki", "mcp-audit.jsonl");
8242
8628
  }
8243
8629
  function auditMcpToolCall(entry) {
8244
8630
  if (!auditEnabled()) return;
@@ -8248,7 +8634,7 @@ function auditMcpToolCall(entry) {
8248
8634
  return;
8249
8635
  }
8250
8636
  const path = auditFilePath();
8251
- mkdirSync4(join28(path, ".."), { recursive: true });
8637
+ mkdirSync4(join30(path, ".."), { recursive: true });
8252
8638
  appendFileSync(path, line, "utf8");
8253
8639
  }
8254
8640
  async function runMcpToolHandler(tool, input, fn) {
@@ -8469,7 +8855,7 @@ function registerMcpMutatingTools(server) {
8469
8855
 
8470
8856
  // src/mcp/resources.ts
8471
8857
  import { readFile as readFile20 } from "fs/promises";
8472
- import { join as join30 } from "path";
8858
+ import { join as join32 } from "path";
8473
8859
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
8474
8860
 
8475
8861
  // src/mcp/lint-bucket.ts
@@ -8598,8 +8984,8 @@ async function fetchQueryPreview(input) {
8598
8984
 
8599
8985
  // src/mcp/graph-html.ts
8600
8986
  import { readFile as readFile19 } from "fs/promises";
8601
- import { join as join29 } from "path";
8602
- import { existsSync as existsSync13 } from "fs";
8987
+ import { join as join31 } from "path";
8988
+ import { existsSync as existsSync15 } from "fs";
8603
8989
  var TYPE_COLORS = {
8604
8990
  entities: "#e74c3c",
8605
8991
  concepts: "#27ae60",
@@ -8667,9 +9053,9 @@ ${nodeSvg}
8667
9053
  return { html, node_count: nodes.length, edge_count: edges.length, truncated };
8668
9054
  }
8669
9055
  async function fetchGraphHtmlReport(input) {
8670
- const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
9056
+ const graphPath = input.graphPath ?? join31(input.vault, ".skillwiki", "graph.json");
8671
9057
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
8672
- if (!existsSync13(graphPath)) {
9058
+ if (!existsSync15(graphPath)) {
8673
9059
  return {
8674
9060
  exitCode: ExitCode.FILE_NOT_FOUND,
8675
9061
  result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
@@ -8741,7 +9127,7 @@ async function fetchStaleSummary(input) {
8741
9127
 
8742
9128
  // src/mcp/resources.ts
8743
9129
  async function readVaultFile(vault, rel) {
8744
- return readFile20(join30(vault, rel), "utf8");
9130
+ return readFile20(join32(vault, rel), "utf8");
8745
9131
  }
8746
9132
  async function tailLines(text, lines) {
8747
9133
  const parts = text.split(/\r?\n/);
@@ -8827,7 +9213,7 @@ function registerMcpResources(server) {
8827
9213
  if (!v.ok) {
8828
9214
  return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
8829
9215
  }
8830
- const path = join30(v.data.vault, ".skillwiki", "graph.json");
9216
+ const path = join32(v.data.vault, ".skillwiki", "graph.json");
8831
9217
  try {
8832
9218
  const raw = await readFile20(path, "utf8");
8833
9219
  const graph = JSON.parse(raw);
@@ -9192,10 +9578,13 @@ export {
9192
9578
  runConfigPath,
9193
9579
  writeCache,
9194
9580
  triggerAutoUpdate,
9581
+ buildDegradedReasons,
9582
+ probeRemoteHealth,
9195
9583
  FLEET_REL_PATH,
9196
9584
  runFleetValidate,
9197
9585
  runFleetContext,
9198
9586
  loadFleetManifestAndHost,
9587
+ snapshotterAliasForLocalHost,
9199
9588
  loadFleetManifest,
9200
9589
  resolveFleetHostId,
9201
9590
  SATELLITE_STALE_MS,