skillwiki 0.9.29 → 0.9.30

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.
@@ -62,7 +62,8 @@ var ExitCode = {
62
62
  SYNC_LOCK_HELD: 48,
63
63
  LOG_APPEND_LOCK_HELD: 49,
64
64
  FLEET_MANIFEST_INVALID: 50,
65
- SENSITIVE_CONTENT_DETECTED: 51
65
+ SENSITIVE_CONTENT_DETECTED: 51,
66
+ FLEET_SATELLITE_HEALTH_FAILED: 52
66
67
  };
67
68
 
68
69
  // ../shared/src/json-output.ts
@@ -2292,25 +2293,6 @@ async function defaultRcloneRunner(args) {
2292
2293
  }
2293
2294
  }
2294
2295
 
2295
- // src/commands/lint.ts
2296
- import { existsSync as existsSync4 } from "fs";
2297
- import { readFile as readFile12, readdir as readdir3 } from "fs/promises";
2298
- import { createHash as createHash4 } from "crypto";
2299
- import { join as join15, relative as relative3, sep as sep3 } from "path";
2300
-
2301
- // src/commands/sparse-community.ts
2302
- async function runSparseCommunity(input) {
2303
- const scan = await scanVault(input.vault);
2304
- if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
2305
- const adjacency = await buildWikilinkAdjacency(scan.data.typedKnowledge);
2306
- const communities = findSparseCommunities(adjacency, {
2307
- minSize: input.minSize,
2308
- maxCohesion: input.maxCohesion
2309
- });
2310
- const humanHint = communities.length === 0 ? "no sparse communities" : communities.map((c) => ` cohesion ${c.cohesion} (${c.size} pages): ${c.action}`).join("\n");
2311
- return { exitCode: ExitCode.OK, result: ok({ communities, humanHint }) };
2312
- }
2313
-
2314
2296
  // src/utils/safe-write.ts
2315
2297
  import { open, readFile as readFile10, rename as rename3, unlink, writeFile as writeFile5 } from "fs/promises";
2316
2298
  import { randomBytes } from "crypto";
@@ -2386,6 +2368,101 @@ async function safeWritePage(absPath, newContent, opts = {}) {
2386
2368
  }
2387
2369
  }
2388
2370
 
2371
+ // src/commands/frontmatter-fix.ts
2372
+ function isoToday() {
2373
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2374
+ }
2375
+ function fixFrontmatter(rawFm) {
2376
+ const additions = [];
2377
+ if (!/^created:/m.test(rawFm)) additions.push(`created: ${isoToday()}`);
2378
+ if (!/^updated:/m.test(rawFm)) additions.push(`updated: ${isoToday()}`);
2379
+ if (!/^tags:/m.test(rawFm)) additions.push("tags: []");
2380
+ if (!/^sources:/m.test(rawFm)) additions.push("sources: []");
2381
+ if (!/^provenance:/m.test(rawFm)) additions.push("provenance: research");
2382
+ if (additions.length === 0) return rawFm;
2383
+ return rawFm.trimEnd() + "\n" + additions.join("\n") + "\n";
2384
+ }
2385
+ function removeOrphanTagsLines(body) {
2386
+ return body.split("\n").filter((line) => !/^tags:\s*\[/.test(line.trim())).join("\n");
2387
+ }
2388
+ async function runFrontmatterFix(input) {
2389
+ const scan = await scanVault(input.vault);
2390
+ if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
2391
+ const fixed = [];
2392
+ const skipped = [];
2393
+ let unchanged = 0;
2394
+ for (const page of scan.data.typedKnowledge) {
2395
+ const text = await readPage(page);
2396
+ const split = splitFrontmatter(text);
2397
+ if (!split.ok) {
2398
+ skipped.push(page.relPath);
2399
+ continue;
2400
+ }
2401
+ const { rawFrontmatter, body } = split.data;
2402
+ const newFm = fixFrontmatter(rawFrontmatter);
2403
+ const newBody = removeOrphanTagsLines(body);
2404
+ const newText = `---
2405
+ ${newFm}
2406
+ ---
2407
+ ${newBody}`;
2408
+ if (newText === text) {
2409
+ unchanged++;
2410
+ continue;
2411
+ }
2412
+ if (!input.dryRun) {
2413
+ const w = await safeWritePage(page.absPath, newText);
2414
+ if (!w.ok) {
2415
+ skipped.push(page.relPath);
2416
+ continue;
2417
+ }
2418
+ }
2419
+ fixed.push(page.relPath);
2420
+ }
2421
+ const exitCode = fixed.length > 0 ? ExitCode.MIGRATION_APPLIED : ExitCode.OK;
2422
+ const hintLines = [`scanned: ${fixed.length + skipped.length + unchanged}`];
2423
+ if (fixed.length > 0) hintLines.push(`fixed: ${fixed.length}`);
2424
+ if (skipped.length > 0) hintLines.push(`skipped (parse error): ${skipped.length}`);
2425
+ if (unchanged > 0) hintLines.push(`unchanged: ${unchanged}`);
2426
+ if (input.dryRun && fixed.length > 0) hintLines.push("(dry run \u2014 no files written)");
2427
+ if (!input.dryRun && fixed.length > 0) {
2428
+ appendLastOp(input.vault, {
2429
+ operation: "frontmatter-fix",
2430
+ summary: `normalized frontmatter on ${fixed.length} page(s)`,
2431
+ files: fixed,
2432
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2433
+ });
2434
+ }
2435
+ return {
2436
+ exitCode,
2437
+ result: ok({
2438
+ scanned: fixed.length + skipped.length + unchanged,
2439
+ fixed,
2440
+ skipped,
2441
+ unchanged,
2442
+ humanHint: hintLines.join("\n")
2443
+ })
2444
+ };
2445
+ }
2446
+
2447
+ // src/commands/lint.ts
2448
+ import { existsSync as existsSync4 } from "fs";
2449
+ import { readFile as readFile12, readdir as readdir3 } from "fs/promises";
2450
+ import { createHash as createHash4 } from "crypto";
2451
+ import { join as join15, relative as relative3, sep as sep3 } from "path";
2452
+
2453
+ // src/commands/sparse-community.ts
2454
+ async function runSparseCommunity(input) {
2455
+ const scan = await scanVault(input.vault);
2456
+ if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
2457
+ const adjacency = await buildWikilinkAdjacency(scan.data.typedKnowledge);
2458
+ const communities = findSparseCommunities(adjacency, {
2459
+ minSize: input.minSize,
2460
+ maxCohesion: input.maxCohesion
2461
+ });
2462
+ const humanHint = communities.length === 0 ? "no sparse communities" : communities.map((c) => ` cohesion ${c.cohesion} (${c.size} pages): ${c.action}`).join("\n");
2463
+ return { exitCode: ExitCode.OK, result: ok({ communities, humanHint }) };
2464
+ }
2465
+
2389
2466
  // src/commands/raw-body-dedup.ts
2390
2467
  import { createHash as createHash3 } from "crypto";
2391
2468
  async function runRawBodyDedup(vault) {
@@ -2684,6 +2761,7 @@ function buildCliSurface() {
2684
2761
  const fleetCmd = program.commands.find((c) => c.name() === "fleet");
2685
2762
  fleetCmd.command("validate");
2686
2763
  fleetCmd.command("context").option("--file <path>").option("--host-id <id>");
2764
+ fleetCmd.command("health").option("--file <path>").option("--host-id <id>").option("--json");
2687
2765
  const surface = /* @__PURE__ */ new Map();
2688
2766
  const rootFlags = new Set(program.options.map((o) => o.long ?? o.short).filter((f) => f != null));
2689
2767
  function walk2(cmd, prefix, parentFlags) {
@@ -2862,7 +2940,7 @@ function extractSourceEntries(rawFm) {
2862
2940
  return entries;
2863
2941
  }
2864
2942
  var ERROR_ORDER = ["sensitive_content", "broken_wikilinks", "invalid_frontmatter", "raw_source_identity_conflict", "raw_dedup", "broken_sources", "tag_not_in_taxonomy", "path_too_long"];
2865
- 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", "work_item_health", "orphaned_project_pages", "missing_overview", "missing_diagram"];
2943
+ 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"];
2866
2944
  var INFO_ORDER = ["bridges", "sparse_community", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "stale_sections", "cli_refs"];
2867
2945
  var KNOWN_BUCKETS = [...ERROR_ORDER, ...WARNING_ORDER, ...INFO_ORDER];
2868
2946
  var CLI_REFS_TYPED_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
@@ -3082,6 +3160,20 @@ async function runLint(input) {
3082
3160
  }
3083
3161
  }
3084
3162
  if (sensitiveFlags.length > 0) buckets.sensitive_content = sensitiveFlags;
3163
+ const fmYamlInvalid = [];
3164
+ for (const page of scan.data.allMarkdown) {
3165
+ try {
3166
+ const text = await readPage(page);
3167
+ const fm = extractFrontmatter(text);
3168
+ if (!fm.ok && fm.error === "INVALID_FRONTMATTER") {
3169
+ const detail = fm.detail;
3170
+ const message = detail?.message ?? "invalid YAML";
3171
+ fmYamlInvalid.push({ path: page.relPath, message });
3172
+ }
3173
+ } catch {
3174
+ }
3175
+ }
3176
+ if (fmYamlInvalid.length > 0) buckets.frontmatter_yaml_invalid = fmYamlInvalid;
3085
3177
  const subDirDupes = [];
3086
3178
  const flatStems = /* @__PURE__ */ new Map();
3087
3179
  const deepFiles = [];
@@ -3658,6 +3750,50 @@ ${newBody}`;
3658
3750
  else delete buckets.path_too_long;
3659
3751
  }
3660
3752
  }
3753
+ if (shouldFix("frontmatter_yaml_invalid") && buckets.frontmatter_yaml_invalid) {
3754
+ const invalidItems = buckets.frontmatter_yaml_invalid;
3755
+ const remaining = [];
3756
+ for (const item of invalidItems) {
3757
+ const page = scan.data.allMarkdown.find((p) => p.relPath === item.path);
3758
+ if (!page) {
3759
+ unresolved.push(item.path);
3760
+ remaining.push(item);
3761
+ continue;
3762
+ }
3763
+ try {
3764
+ const text = await readPage(page);
3765
+ const split = splitFrontmatter(text);
3766
+ if (!split.ok) {
3767
+ unresolved.push(item.path);
3768
+ remaining.push(item);
3769
+ continue;
3770
+ }
3771
+ const newFm = fixFrontmatter(split.data.rawFrontmatter);
3772
+ const newText = `---
3773
+ ${newFm}
3774
+ ---
3775
+ ${split.data.body}`;
3776
+ const recheck = extractFrontmatter(newText);
3777
+ if (!recheck.ok) {
3778
+ unresolved.push(item.path);
3779
+ remaining.push(item);
3780
+ continue;
3781
+ }
3782
+ const w = await safeWritePage(page.absPath, newText, { minBodyRatio: null });
3783
+ if (!w.ok) {
3784
+ unresolved.push(item.path);
3785
+ remaining.push(item);
3786
+ continue;
3787
+ }
3788
+ fixed.push(item.path);
3789
+ } catch {
3790
+ unresolved.push(item.path);
3791
+ remaining.push(item);
3792
+ }
3793
+ }
3794
+ if (remaining.length > 0) buckets.frontmatter_yaml_invalid = remaining;
3795
+ else delete buckets.frontmatter_yaml_invalid;
3796
+ }
3661
3797
  }
3662
3798
  const errorOut = ERROR_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
3663
3799
  const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
@@ -3905,7 +4041,7 @@ async function runFleetContext(input) {
3905
4041
  })
3906
4042
  };
3907
4043
  }
3908
- const resolved = await resolveHostId({
4044
+ const resolved = await resolveFleetHostId({
3909
4045
  manifest: loaded.manifest,
3910
4046
  hostId: input.hostId,
3911
4047
  env,
@@ -3992,6 +4128,59 @@ async function runFleetContext(input) {
3992
4128
  })
3993
4129
  };
3994
4130
  }
4131
+ function fleetContextEnv(input) {
4132
+ const env = input.env ?? process.env;
4133
+ const home = input.home ?? env.HOME ?? "";
4134
+ const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
4135
+ const vault = input.vault ?? safeEnvValue(env.WIKI_PATH);
4136
+ const file = input.file ?? (vault ? join18(vault, FLEET_REL_PATH) : void 0);
4137
+ return { env, home, osHostname, vault, file };
4138
+ }
4139
+ async function loadFleetManifestAndHost(input) {
4140
+ const { env, home, osHostname, file } = fleetContextEnv(input);
4141
+ if (!file) return null;
4142
+ const loaded = await loadFleetManifest(file);
4143
+ if (!loaded.ok) return null;
4144
+ const resolved = await resolveFleetHostId({
4145
+ manifest: loaded.manifest,
4146
+ hostId: input.hostId,
4147
+ env,
4148
+ home,
4149
+ osHostname
4150
+ });
4151
+ if (!resolved.hostId) {
4152
+ return {
4153
+ manifest: loaded.manifest,
4154
+ hostId: void 0,
4155
+ source: resolved.source,
4156
+ warnings: ["host identity is unresolved"],
4157
+ identityStatus: "unknown"
4158
+ };
4159
+ }
4160
+ if (!loaded.manifest.hosts[resolved.hostId]) {
4161
+ const source = resolved.source ?? "unknown";
4162
+ return {
4163
+ manifest: loaded.manifest,
4164
+ hostId: resolved.hostId,
4165
+ source: resolved.source,
4166
+ warnings: [`resolved host id \`${resolved.hostId}\` from ${source} is not in fleet.yaml`],
4167
+ identityStatus: "invalid"
4168
+ };
4169
+ }
4170
+ return {
4171
+ manifest: loaded.manifest,
4172
+ hostId: resolved.hostId,
4173
+ source: resolved.source,
4174
+ warnings: [],
4175
+ identityStatus: "known"
4176
+ };
4177
+ }
4178
+ function satelliteGateFromFleetLoad(load) {
4179
+ if (!load?.hostId) return { satelliteExpected: false };
4180
+ const host = load.manifest.hosts[load.hostId];
4181
+ if (!host) return { satelliteExpected: false };
4182
+ return { satelliteExpected: host.maintenance?.skillwiki_satellite?.enabled === true };
4183
+ }
3995
4184
  async function loadFleetManifest(file) {
3996
4185
  let text;
3997
4186
  try {
@@ -4051,7 +4240,7 @@ function fleetWarnings(manifest) {
4051
4240
  function findSnapshotter(manifest) {
4052
4241
  return Object.entries(manifest.hosts).find(([, host]) => host.role === "snapshotter")?.[0];
4053
4242
  }
4054
- async function resolveHostId(input) {
4243
+ async function resolveFleetHostId(input) {
4055
4244
  const trace = [];
4056
4245
  if (input.hostId) {
4057
4246
  trace.push({ source: "--host-id", status: "matched", value: input.hostId });
@@ -4242,8 +4431,8 @@ function safeUserName() {
4242
4431
  }
4243
4432
 
4244
4433
  // src/commands/doctor.ts
4245
- import { existsSync as existsSync9, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync6 } from "fs";
4246
- import { join as join21, resolve as resolve5 } from "path";
4434
+ import { existsSync as existsSync10, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync7 } from "fs";
4435
+ import { join as join22, resolve as resolve5 } from "path";
4247
4436
  import { execSync as execSync2 } from "child_process";
4248
4437
  import { platform as platform2 } from "os";
4249
4438
 
@@ -4365,11 +4554,66 @@ function parseTomlScalar(rawValue) {
4365
4554
  return value;
4366
4555
  }
4367
4556
 
4557
+ // src/utils/satellite-run-health.ts
4558
+ import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
4559
+ import { join as join20 } from "path";
4560
+ var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
4561
+ function satelliteLatestRunPath(vault) {
4562
+ return join20(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
4563
+ }
4564
+ function isFailedRunStatus(status) {
4565
+ return status === "fail" || status === "failure";
4566
+ }
4567
+ function parseLatestRunFile(text) {
4568
+ try {
4569
+ const parsed = JSON.parse(text);
4570
+ const status = typeof parsed.status === "string" ? parsed.status : "";
4571
+ if (!status) return null;
4572
+ const finishedAt = typeof parsed.finished_at === "string" && parsed.finished_at.length > 0 ? parsed.finished_at : void 0;
4573
+ const failureClass = parsed.failure_class != null && String(parsed.failure_class).length > 0 ? String(parsed.failure_class) : void 0;
4574
+ return { status, finishedAt, failureClass };
4575
+ } catch {
4576
+ return null;
4577
+ }
4578
+ }
4579
+ function readSatelliteLatestRunFromText(text) {
4580
+ return parseLatestRunFile(text);
4581
+ }
4582
+ function readSatelliteLatestRun(vault) {
4583
+ const latestPath = satelliteLatestRunPath(vault);
4584
+ if (!existsSync8(latestPath)) return null;
4585
+ try {
4586
+ return parseLatestRunFile(readFileSync5(latestPath, "utf8"));
4587
+ } catch {
4588
+ return null;
4589
+ }
4590
+ }
4591
+ function evaluateSatelliteRunHealth(vault, now) {
4592
+ const run = readSatelliteLatestRun(vault);
4593
+ if (!run) {
4594
+ return { failed: false, stale: false };
4595
+ }
4596
+ const failed = isFailedRunStatus(run.status);
4597
+ let stale = false;
4598
+ if (!failed && run.finishedAt) {
4599
+ const ts = Date.parse(run.finishedAt);
4600
+ if (Number.isFinite(ts) && now.getTime() - ts > SATELLITE_STALE_MS) {
4601
+ stale = true;
4602
+ }
4603
+ }
4604
+ return {
4605
+ failed,
4606
+ stale,
4607
+ failureClass: run.failureClass,
4608
+ finishedAt: run.finishedAt
4609
+ };
4610
+ }
4611
+
4368
4612
  // src/utils/s3-mount-health.ts
4369
4613
  import { execSync } from "child_process";
4370
4614
  import { platform } from "os";
4371
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
4372
- import { join as join20 } from "path";
4615
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
4616
+ import { join as join21 } from "path";
4373
4617
  var OS = platform();
4374
4618
  function findRcloneMountPid() {
4375
4619
  try {
@@ -4453,7 +4697,7 @@ function extractRcloneFs(args) {
4453
4697
  function getRcloneArgs(pid) {
4454
4698
  try {
4455
4699
  if (OS === "linux") {
4456
- const raw = readFileSync5(`/proc/${pid}/cmdline`);
4700
+ const raw = readFileSync6(`/proc/${pid}/cmdline`);
4457
4701
  return new TextDecoder().decode(raw).split("\0").filter(Boolean);
4458
4702
  } else {
4459
4703
  const out = execSync(`ps -o args= -p ${pid}`, {
@@ -4496,7 +4740,7 @@ function queryRcloneRC(rcAddr, fs) {
4496
4740
  function detectFuseMount(vaultPath) {
4497
4741
  try {
4498
4742
  if (OS === "linux") {
4499
- const mounts = readFileSync5("/proc/mounts", "utf8");
4743
+ const mounts = readFileSync6("/proc/mounts", "utf8");
4500
4744
  let best = null;
4501
4745
  for (const line of mounts.split("\n")) {
4502
4746
  const parts = line.split(" ");
@@ -4527,7 +4771,7 @@ function detectFuseMount(vaultPath) {
4527
4771
  return null;
4528
4772
  }
4529
4773
  function writeTest(dir) {
4530
- const testFile = join20(dir, `.doctor-write-test-${process.pid}.tmp`);
4774
+ const testFile = join21(dir, `.doctor-write-test-${process.pid}.tmp`);
4531
4775
  const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
4532
4776
  const start = Date.now();
4533
4777
  try {
@@ -4627,13 +4871,13 @@ function detectCliChannels(argv, home) {
4627
4871
  }
4628
4872
  const plugin = findPlugin(home);
4629
4873
  if (plugin) {
4630
- const pluginBin = join21(plugin.installPath, "bin", "skillwiki");
4631
- if (existsSync9(pluginBin)) {
4874
+ const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
4875
+ if (existsSync10(pluginBin)) {
4632
4876
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
4633
4877
  }
4634
4878
  }
4635
- const installBin = join21(home, ".claude", "skills", "bin", "skillwiki");
4636
- if (existsSync9(installBin)) {
4879
+ const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
4880
+ if (existsSync10(installBin)) {
4637
4881
  channels.push({ name: "install", path: installBin, isDevLink: false });
4638
4882
  }
4639
4883
  return channels;
@@ -4694,7 +4938,7 @@ function isDevSourceRun(argv) {
4694
4938
  }
4695
4939
  async function checkConfigFile(home) {
4696
4940
  const cfgPath = configPath(home);
4697
- if (!existsSync9(cfgPath)) {
4941
+ if (!existsSync10(cfgPath)) {
4698
4942
  return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
4699
4943
  }
4700
4944
  try {
@@ -4709,7 +4953,7 @@ function checkWikiPathExists(resolvedPath) {
4709
4953
  if (resolvedPath === void 0) {
4710
4954
  return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
4711
4955
  }
4712
- if (existsSync9(resolvedPath) && statSync(resolvedPath).isDirectory()) {
4956
+ if (existsSync10(resolvedPath) && statSync(resolvedPath).isDirectory()) {
4713
4957
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
4714
4958
  }
4715
4959
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
@@ -4718,13 +4962,13 @@ function checkVaultStructure(resolvedPath) {
4718
4962
  if (resolvedPath === void 0) {
4719
4963
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
4720
4964
  }
4721
- if (!existsSync9(resolvedPath)) {
4965
+ if (!existsSync10(resolvedPath)) {
4722
4966
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
4723
4967
  }
4724
4968
  const missing = [];
4725
- if (!existsSync9(join21(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
4969
+ if (!existsSync10(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
4726
4970
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
4727
- if (!existsSync9(join21(resolvedPath, dir))) missing.push(dir + "/");
4971
+ if (!existsSync10(join22(resolvedPath, dir))) missing.push(dir + "/");
4728
4972
  }
4729
4973
  if (missing.length === 0) {
4730
4974
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -4732,8 +4976,8 @@ function checkVaultStructure(resolvedPath) {
4732
4976
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
4733
4977
  }
4734
4978
  function checkSkillsInstalled(home, cwd) {
4735
- const srcDir = cwd ? join21(cwd, "packages", "skills") : void 0;
4736
- if (srcDir && existsSync9(srcDir)) {
4979
+ const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
4980
+ if (srcDir && existsSync10(srcDir)) {
4737
4981
  const found = findInstalledSkillMd(srcDir);
4738
4982
  if (found.length > 0) {
4739
4983
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
@@ -4746,8 +4990,8 @@ function checkSkillsInstalled(home, cwd) {
4746
4990
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
4747
4991
  }
4748
4992
  }
4749
- const skillsDir = join21(home, ".claude", "skills");
4750
- if (existsSync9(skillsDir)) {
4993
+ const skillsDir = join22(home, ".claude", "skills");
4994
+ if (existsSync10(skillsDir)) {
4751
4995
  const found = findInstalledSkillMd(skillsDir);
4752
4996
  if (found.length > 0) {
4753
4997
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
@@ -4757,10 +5001,10 @@ function checkSkillsInstalled(home, cwd) {
4757
5001
  }
4758
5002
  function checkDuplicateSkills(home) {
4759
5003
  const plugin = findPlugin(home);
4760
- const skillsDir = join21(home, ".claude", "skills");
5004
+ const skillsDir = join22(home, ".claude", "skills");
4761
5005
  const agentSkillDirs = [
4762
- { label: "~/.codex/skills/", path: join21(home, ".codex", "skills") },
4763
- { label: "~/.agents/skills/", path: join21(home, ".agents", "skills") }
5006
+ { label: "~/.codex/skills/", path: join22(home, ".codex", "skills") },
5007
+ { label: "~/.agents/skills/", path: join22(home, ".agents", "skills") }
4764
5008
  ];
4765
5009
  if (!plugin) {
4766
5010
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -4863,8 +5107,8 @@ async function checkProfiles(home) {
4863
5107
  }
4864
5108
  async function checkProjectLocalOverride(cwd) {
4865
5109
  const dir = cwd ?? process.cwd();
4866
- const envPath = join21(dir, ".skillwiki", ".env");
4867
- if (existsSync9(envPath)) {
5110
+ const envPath = join22(dir, ".skillwiki", ".env");
5111
+ if (existsSync10(envPath)) {
4868
5112
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
4869
5113
  }
4870
5114
  return check("pass", "project_local", "Project-local config", "None");
@@ -4873,7 +5117,7 @@ function checkVaultGitRemote(resolvedPath) {
4873
5117
  if (resolvedPath === void 0) {
4874
5118
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
4875
5119
  }
4876
- if (!existsSync9(join21(resolvedPath, ".git"))) {
5120
+ if (!existsSync10(join22(resolvedPath, ".git"))) {
4877
5121
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
4878
5122
  }
4879
5123
  try {
@@ -4896,9 +5140,9 @@ function checkObsidianTemplates(resolvedPath) {
4896
5140
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
4897
5141
  }
4898
5142
  const missing = [];
4899
- if (!existsSync9(join21(resolvedPath, "_Templates"))) missing.push("_Templates/");
4900
- if (!existsSync9(join21(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
4901
- if (!existsSync9(join21(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5143
+ if (!existsSync10(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
5144
+ if (!existsSync10(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5145
+ if (!existsSync10(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
4902
5146
  if (missing.length === 0) {
4903
5147
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
4904
5148
  }
@@ -4908,8 +5152,8 @@ function checkDotStoreClean(resolvedPath) {
4908
5152
  if (resolvedPath === void 0) {
4909
5153
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
4910
5154
  }
4911
- const rawDir = join21(resolvedPath, "raw");
4912
- if (!existsSync9(rawDir)) {
5155
+ const rawDir = join22(resolvedPath, "raw");
5156
+ if (!existsSync10(rawDir)) {
4913
5157
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
4914
5158
  }
4915
5159
  const found = [];
@@ -4924,7 +5168,7 @@ function checkDotStoreClean(resolvedPath) {
4924
5168
  if (entry.name === ".DS_Store") {
4925
5169
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
4926
5170
  } else if (entry.isDirectory()) {
4927
- walk2(join21(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
5171
+ walk2(join22(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
4928
5172
  }
4929
5173
  }
4930
5174
  })(rawDir, "");
@@ -4937,7 +5181,7 @@ function checkSyncLastPush(resolvedPath) {
4937
5181
  if (resolvedPath === void 0) {
4938
5182
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
4939
5183
  }
4940
- if (!existsSync9(join21(resolvedPath, ".git"))) {
5184
+ if (!existsSync10(join22(resolvedPath, ".git"))) {
4941
5185
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
4942
5186
  }
4943
5187
  let timestamp;
@@ -4985,7 +5229,7 @@ function checkVaultGitDirty(resolvedPath) {
4985
5229
  if (resolvedPath === void 0) {
4986
5230
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
4987
5231
  }
4988
- if (!existsSync9(join21(resolvedPath, ".git"))) {
5232
+ if (!existsSync10(join22(resolvedPath, ".git"))) {
4989
5233
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
4990
5234
  }
4991
5235
  try {
@@ -5053,7 +5297,7 @@ function remoteMainHash(resolvedPath) {
5053
5297
  }
5054
5298
  function checkStaleRemoteMain(resolvedPath) {
5055
5299
  if (resolvedPath === void 0) return void 0;
5056
- if (!existsSync9(join21(resolvedPath, ".git"))) return void 0;
5300
+ if (!existsSync10(join22(resolvedPath, ".git"))) return void 0;
5057
5301
  const localOrigin = gitRefHash(resolvedPath, "origin/main");
5058
5302
  if (!localOrigin) return void 0;
5059
5303
  const remoteMain = remoteMainHash(resolvedPath);
@@ -5069,7 +5313,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
5069
5313
  if (resolvedPath === void 0) {
5070
5314
  return check("pass", id, label, "No vault path \u2014 check skipped");
5071
5315
  }
5072
- if (!existsSync9(join21(resolvedPath, ".git"))) {
5316
+ if (!existsSync10(join22(resolvedPath, ".git"))) {
5073
5317
  return check("pass", id, label, "No git repo \u2014 check skipped");
5074
5318
  }
5075
5319
  if (!hasOriginMain(resolvedPath)) {
@@ -5089,11 +5333,84 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
5089
5333
  return check("warn", id, label, "Could not compare HEAD with origin/main");
5090
5334
  }
5091
5335
  }
5336
+ function checkSatelliteLastRun(vaultPath, satelliteExpected) {
5337
+ if (!satelliteExpected) {
5338
+ return check("pass", "satellite_job_last_run", "Satellite job last run", "Satellite job not expected on this host");
5339
+ }
5340
+ if (vaultPath === void 0) {
5341
+ return check("pass", "satellite_job_last_run", "Satellite job last run", "No vault path \u2014 check skipped");
5342
+ }
5343
+ const latestPath = satelliteLatestRunPath(vaultPath);
5344
+ if (!existsSync10(latestPath)) {
5345
+ return check("pass", "satellite_job_last_run", "Satellite job last run", "No latest-run.json \u2014 satellite has not run yet");
5346
+ }
5347
+ try {
5348
+ const health = evaluateSatelliteRunHealth(vaultPath, /* @__PURE__ */ new Date());
5349
+ if (health.failed) {
5350
+ const fc = health.failureClass;
5351
+ const detail = fc ? `Last satellite run failed (failure_class: ${fc})` : "Last satellite run failed";
5352
+ return check("error", "satellite_job_last_run", "Satellite job last run", detail);
5353
+ }
5354
+ if (health.stale && health.finishedAt) {
5355
+ return check(
5356
+ "warn",
5357
+ "satellite_job_last_run",
5358
+ "Satellite job last run",
5359
+ `Last run finished_at is older than 26h (${health.finishedAt})`
5360
+ );
5361
+ }
5362
+ return check(
5363
+ "pass",
5364
+ "satellite_job_last_run",
5365
+ "Satellite job last run",
5366
+ health.finishedAt ? `Last run ok (finished_at ${health.finishedAt})` : "Last run ok"
5367
+ );
5368
+ } catch {
5369
+ return check("warn", "satellite_job_last_run", "Satellite job last run", `Could not read ${latestPath}`);
5370
+ }
5371
+ }
5372
+ function defaultSatelliteTimerDeps() {
5373
+ return {
5374
+ platform: () => platform2(),
5375
+ systemctlIsActive: (unit) => {
5376
+ try {
5377
+ return execSync2(`systemctl is-active ${unit}`, {
5378
+ encoding: "utf8",
5379
+ timeout: 2e3,
5380
+ stdio: ["pipe", "pipe", "pipe"]
5381
+ }).trim();
5382
+ } catch {
5383
+ return void 0;
5384
+ }
5385
+ }
5386
+ };
5387
+ }
5388
+ function checkSatelliteTimer(satelliteExpected, deps = defaultSatelliteTimerDeps()) {
5389
+ if (!satelliteExpected) {
5390
+ return check("pass", "satellite_job_timer", "Satellite job timer", "Satellite job not expected on this host");
5391
+ }
5392
+ if (deps.platform() !== "linux") {
5393
+ return check("pass", "satellite_job_timer", "Satellite job timer", "Timer check skipped \u2014 Linux only");
5394
+ }
5395
+ const out = deps.systemctlIsActive("agent-memory-trends.timer");
5396
+ if (out === void 0) {
5397
+ return check("pass", "satellite_job_timer", "Satellite job timer", "systemctl unavailable");
5398
+ }
5399
+ if (out === "active") {
5400
+ return check("pass", "satellite_job_timer", "Satellite job timer", "systemd: agent-memory-trends.timer active");
5401
+ }
5402
+ return check(
5403
+ "error",
5404
+ "satellite_job_timer",
5405
+ "Satellite job timer",
5406
+ `systemd: agent-memory-trends.timer is ${out || "not active"}`
5407
+ );
5408
+ }
5092
5409
  async function checkFleetIdentity(input) {
5093
5410
  if (!input.vaultPath) {
5094
5411
  return check("pass", "fleet_identity", "Fleet identity", "No vault path \u2014 check skipped");
5095
5412
  }
5096
- const r = await runFleetContext({
5413
+ const load = input.fleetLoad !== void 0 ? input.fleetLoad : await loadFleetManifestAndHost({
5097
5414
  vault: input.vaultPath,
5098
5415
  env: { ...process.env, WIKI_PATH: input.envValue ?? input.vaultPath },
5099
5416
  home: input.home,
@@ -5101,26 +5418,22 @@ async function checkFleetIdentity(input) {
5101
5418
  osHostname: process.env.HOSTNAME,
5102
5419
  user: process.env.USER
5103
5420
  });
5104
- if (!r.result.ok) {
5105
- return check("warn", "fleet_identity", "Fleet identity", "Could not evaluate fleet identity");
5106
- }
5107
- const data = r.result.data;
5108
- if (!data.manifest_loaded) {
5421
+ if (!load) {
5109
5422
  return check("pass", "fleet_identity", "Fleet identity", "Fleet manifest unavailable \u2014 check skipped");
5110
5423
  }
5111
- if (data.identity_status === "known") {
5112
- return check("pass", "fleet_identity", "Fleet identity", `Resolved ${data.host_id ?? "unknown"} via ${data.source ?? "unknown"}`);
5424
+ if (load.identityStatus === "known") {
5425
+ return check("pass", "fleet_identity", "Fleet identity", `Resolved ${load.hostId ?? "unknown"} via ${load.source ?? "unknown"}`);
5113
5426
  }
5114
- const detail = data.warnings.length > 0 ? data.warnings.join("; ") : "Fleet identity is unresolved";
5427
+ const detail = load.warnings.length > 0 ? load.warnings.join("; ") : "Fleet identity is unresolved";
5115
5428
  return check("warn", "fleet_identity", "Fleet identity", detail);
5116
5429
  }
5117
5430
  function pullLogPaths(home) {
5118
5431
  const paths = platform2() === "darwin" ? [
5119
- join21(home, "Library", "Logs", "wiki-pull.log"),
5120
- join21(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
5432
+ join22(home, "Library", "Logs", "wiki-pull.log"),
5433
+ join22(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
5121
5434
  ] : [
5122
- join21(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
5123
- join21(home, "Library", "Logs", "wiki-pull.log")
5435
+ join22(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
5436
+ join22(home, "Library", "Logs", "wiki-pull.log")
5124
5437
  ];
5125
5438
  return [...new Set(paths)];
5126
5439
  }
@@ -5132,12 +5445,12 @@ function isRecentLogLine(line, nowMs) {
5132
5445
  return nowMs - ts <= 24 * 60 * 60 * 1e3;
5133
5446
  }
5134
5447
  function checkVaultGitPullFailures(home) {
5135
- const path = pullLogPaths(home).find((p) => existsSync9(p));
5448
+ const path = pullLogPaths(home).find((p) => existsSync10(p));
5136
5449
  if (!path) {
5137
5450
  return check("pass", "vault_git_pull_failures", "Vault pull failures", "No wiki-pull.log found \u2014 check skipped");
5138
5451
  }
5139
5452
  try {
5140
- const lines = readFileSync6(path, "utf8").split(/\r?\n/).filter(Boolean);
5453
+ const lines = readFileSync7(path, "utf8").split(/\r?\n/).filter(Boolean);
5141
5454
  const now = Date.now();
5142
5455
  const failures = lines.filter(
5143
5456
  (line) => isRecentLogLine(line, now) && /(pre-push pull failed|FAIL .*pull|FAIL .*rebase|cannot pull with rebase|unstaged changes)/i.test(line)
@@ -5160,8 +5473,8 @@ function checkS3MountPerf(resolvedPath) {
5160
5473
  return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
5161
5474
  }
5162
5475
  const mountPoint = fuse.mountPoint;
5163
- const conceptsDir = join21(resolvedPath, "concepts");
5164
- if (!existsSync9(conceptsDir)) {
5476
+ const conceptsDir = join22(resolvedPath, "concepts");
5477
+ if (!existsSync10(conceptsDir)) {
5165
5478
  return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
5166
5479
  }
5167
5480
  const start = Date.now();
@@ -5343,8 +5656,8 @@ function checkWriteTest(resolvedPath) {
5343
5656
  if (!fuse) {
5344
5657
  return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
5345
5658
  }
5346
- const conceptsDir = join21(resolvedPath, "concepts");
5347
- if (!existsSync9(conceptsDir)) {
5659
+ const conceptsDir = join22(resolvedPath, "concepts");
5660
+ if (!existsSync10(conceptsDir)) {
5348
5661
  return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
5349
5662
  }
5350
5663
  const result = writeTest(conceptsDir);
@@ -5430,7 +5743,7 @@ function checkVfsCacheHealth(resolvedPath) {
5430
5743
  }
5431
5744
  function readVaultSyncConfig(home) {
5432
5745
  try {
5433
- const content = readFileSync6(join21(home, ".skillwiki", ".env"), "utf8");
5746
+ const content = readFileSync7(join22(home, ".skillwiki", ".env"), "utf8");
5434
5747
  let installed = false;
5435
5748
  let role;
5436
5749
  let serviceScope;
@@ -5459,7 +5772,7 @@ function readVaultSyncConfig(home) {
5459
5772
  }
5460
5773
  function readKeyFromEnvFile(path, keys) {
5461
5774
  try {
5462
- const content = readFileSync6(path, "utf8");
5775
+ const content = readFileSync7(path, "utf8");
5463
5776
  for (const line of content.split(/\r?\n/)) {
5464
5777
  const trimmed = line.trim();
5465
5778
  if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
@@ -5481,7 +5794,7 @@ function resolveSnapshotGitWorktree(config) {
5481
5794
  if (fromProfile) return fromProfile;
5482
5795
  }
5483
5796
  const defaultPath = "/root/wiki-git";
5484
- return existsSync9(defaultPath) ? defaultPath : void 0;
5797
+ return existsSync10(defaultPath) ? defaultPath : void 0;
5485
5798
  }
5486
5799
  function vaultSyncChecks(input) {
5487
5800
  const os = input.os ?? platform2();
@@ -5498,16 +5811,16 @@ function vaultSyncChecks(input) {
5498
5811
  ];
5499
5812
  }
5500
5813
  const isMac = os === "darwin";
5501
- const logDir = input.logDir ?? (isMac ? join21(home, "Library", "Logs") : join21(home, ".local", "state", "vault-sync", "log"));
5502
- const shareDir = input.shareDir ?? (isMac ? join21(home, "Library", "Application Support", "vault-sync", "bin") : join21(home, ".local", "share", "vault-sync", "bin"));
5503
- const filterPath = input.filterPath ?? join21(home, ".config", "rclone", "wiki-push-filters.txt");
5504
- const packagedSnapshotPath = join21(shareDir, "wiki-snapshot.sh");
5814
+ const logDir = input.logDir ?? (isMac ? join22(home, "Library", "Logs") : join22(home, ".local", "state", "vault-sync", "log"));
5815
+ const shareDir = input.shareDir ?? (isMac ? join22(home, "Library", "Application Support", "vault-sync", "bin") : join22(home, ".local", "share", "vault-sync", "bin"));
5816
+ const filterPath = input.filterPath ?? join22(home, ".config", "rclone", "wiki-push-filters.txt");
5817
+ const packagedSnapshotPath = join22(shareDir, "wiki-snapshot.sh");
5505
5818
  const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
5506
- const snapshotPath = input.snapshotScriptPath ?? (existsSync9(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
5819
+ const snapshotPath = input.snapshotScriptPath ?? (existsSync10(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
5507
5820
  function snapshotLastStatusCheck() {
5508
- const snapshotLog = join21(logDir, "wiki-snapshot.log");
5821
+ const snapshotLog = join22(logDir, "wiki-snapshot.log");
5509
5822
  try {
5510
- const logContent = readFileSync6(snapshotLog, "utf8");
5823
+ const logContent = readFileSync7(snapshotLog, "utf8");
5511
5824
  const lines = logContent.trim().split("\n").filter(Boolean);
5512
5825
  if (lines.length === 0) {
5513
5826
  return check(
@@ -5552,14 +5865,14 @@ function vaultSyncChecks(input) {
5552
5865
  }
5553
5866
  }
5554
5867
  if (input.vaultSyncRole === "snapshotter") {
5555
- const c12 = existsSync9(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}`);
5868
+ const c12 = existsSync10(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}`);
5556
5869
  const serviceScope = input.vaultSyncServiceScope ?? "user";
5557
- const userTimerPath = join21(home, ".config", "systemd", "user", "wiki-snapshot.timer");
5870
+ const userTimerPath = join22(home, ".config", "systemd", "user", "wiki-snapshot.timer");
5558
5871
  const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
5559
5872
  let c22;
5560
- if (serviceScope === "user" && existsSync9(userTimerPath)) {
5873
+ if (serviceScope === "user" && existsSync10(userTimerPath)) {
5561
5874
  c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${userTimerPath}`);
5562
- } else if (serviceScope === "system" && existsSync9(systemTimerPath)) {
5875
+ } else if (serviceScope === "system" && existsSync10(systemTimerPath)) {
5563
5876
  c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${systemTimerPath}`);
5564
5877
  } else if (os !== "linux") {
5565
5878
  c22 = check("warn", "vault_sync_jobs_enabled", "Vault sync jobs enabled", "Snapshotter scheduler is Linux-only and no wiki-snapshot.timer file was found");
@@ -5591,7 +5904,7 @@ function vaultSyncChecks(input) {
5591
5904
  );
5592
5905
  let c52;
5593
5906
  try {
5594
- if (!existsSync9(snapshotPath)) {
5907
+ if (!existsSync10(snapshotPath)) {
5595
5908
  c52 = check(
5596
5909
  "error",
5597
5910
  "vault_sync_snapshot_guard",
@@ -5599,7 +5912,7 @@ function vaultSyncChecks(input) {
5599
5912
  `Snapshot script not found at ${snapshotPath}`
5600
5913
  );
5601
5914
  } else {
5602
- const content = readFileSync6(snapshotPath, "utf8");
5915
+ const content = readFileSync7(snapshotPath, "utf8");
5603
5916
  if (!content.includes("--max-delete")) {
5604
5917
  c52 = check(
5605
5918
  "error",
@@ -5626,8 +5939,8 @@ function vaultSyncChecks(input) {
5626
5939
  }
5627
5940
  return [c12, c22, c32, cFetch2, c42, c52];
5628
5941
  }
5629
- const pushScriptPath = join21(shareDir, "wiki-push.sh");
5630
- const c1 = existsSync9(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`);
5942
+ const pushScriptPath = join22(shareDir, "wiki-push.sh");
5943
+ const c1 = existsSync10(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`);
5631
5944
  let c2;
5632
5945
  try {
5633
5946
  if (isMac) {
@@ -5678,10 +5991,10 @@ function vaultSyncChecks(input) {
5678
5991
  "Scheduler check failed \u2014 run vault-sync-install"
5679
5992
  );
5680
5993
  }
5681
- const logFile = join21(logDir, "wiki-push.log");
5994
+ const logFile = join22(logDir, "wiki-push.log");
5682
5995
  let c3;
5683
5996
  try {
5684
- const logContent = readFileSync6(logFile, "utf8");
5997
+ const logContent = readFileSync7(logFile, "utf8");
5685
5998
  const lines = logContent.trim().split("\n").filter(Boolean);
5686
5999
  if (lines.length === 0) {
5687
6000
  c3 = check(
@@ -5737,7 +6050,7 @@ function vaultSyncChecks(input) {
5737
6050
  }
5738
6051
  }
5739
6052
  } catch {
5740
- c3 = existsSync9(logDir) ? check(
6053
+ c3 = existsSync10(logDir) ? check(
5741
6054
  "warn",
5742
6055
  "vault_sync_last_push_age",
5743
6056
  "Vault sync last push recency",
@@ -5749,10 +6062,10 @@ function vaultSyncChecks(input) {
5749
6062
  `Log directory not found at ${logDir}`
5750
6063
  );
5751
6064
  }
5752
- const fetchLogFile = join21(logDir, "wiki-fetch.log");
6065
+ const fetchLogFile = join22(logDir, "wiki-fetch.log");
5753
6066
  let cFetch;
5754
6067
  try {
5755
- const logContent = readFileSync6(fetchLogFile, "utf8");
6068
+ const logContent = readFileSync7(fetchLogFile, "utf8");
5756
6069
  const lines = logContent.trim().split("\n").filter(Boolean);
5757
6070
  if (lines.length === 0) {
5758
6071
  cFetch = check(
@@ -5796,7 +6109,7 @@ function vaultSyncChecks(input) {
5796
6109
  }
5797
6110
  let c4;
5798
6111
  try {
5799
- if (!existsSync9(filterPath)) {
6112
+ if (!existsSync10(filterPath)) {
5800
6113
  c4 = check(
5801
6114
  "error",
5802
6115
  "vault_sync_filter_present",
@@ -5804,7 +6117,7 @@ function vaultSyncChecks(input) {
5804
6117
  `Filter file not found at ${filterPath}`
5805
6118
  );
5806
6119
  } else {
5807
- const content = readFileSync6(filterPath, "utf8");
6120
+ const content = readFileSync7(filterPath, "utf8");
5808
6121
  const requiredExcludes = [
5809
6122
  "remotely-save/data.json",
5810
6123
  ".skillwiki/sync.lock",
@@ -5847,7 +6160,7 @@ function vaultSyncChecks(input) {
5847
6160
  );
5848
6161
  } else {
5849
6162
  try {
5850
- if (!existsSync9(snapshotPath)) {
6163
+ if (!existsSync10(snapshotPath)) {
5851
6164
  c5 = check(
5852
6165
  "error",
5853
6166
  "vault_sync_snapshot_guard",
@@ -5855,7 +6168,7 @@ function vaultSyncChecks(input) {
5855
6168
  `Snapshot script not found at ${snapshotPath}`
5856
6169
  );
5857
6170
  } else {
5858
- const content = readFileSync6(snapshotPath, "utf8");
6171
+ const content = readFileSync7(snapshotPath, "utf8");
5859
6172
  if (!content.includes("--max-delete")) {
5860
6173
  c5 = check(
5861
6174
  "error",
@@ -5893,15 +6206,15 @@ function findSkillMd(dir) {
5893
6206
  }
5894
6207
  for (const entry of entries) {
5895
6208
  if (entry.isFile() && entry.name === "SKILL.md") {
5896
- results.push(join21(dir, entry.name));
6209
+ results.push(join22(dir, entry.name));
5897
6210
  } else if (entry.isDirectory()) {
5898
- results.push(...findSkillMd(join21(dir, entry.name)));
6211
+ results.push(...findSkillMd(join22(dir, entry.name)));
5899
6212
  }
5900
6213
  }
5901
6214
  return results;
5902
6215
  }
5903
6216
  function findInstalledSkillMd(dir) {
5904
- const directSkills = findSkillNames(dir).map((name) => join21(dir, name, "SKILL.md"));
6217
+ const directSkills = findSkillNames(dir).map((name) => join22(dir, name, "SKILL.md"));
5905
6218
  return directSkills.length > 0 ? directSkills : findSkillMd(dir);
5906
6219
  }
5907
6220
  function findSkillNames(dir) {
@@ -5913,7 +6226,7 @@ function findSkillNames(dir) {
5913
6226
  return results;
5914
6227
  }
5915
6228
  for (const entry of entries) {
5916
- if (entry.isDirectory() && existsSync9(join21(dir, entry.name, "SKILL.md"))) {
6229
+ if (entry.isDirectory() && existsSync10(join22(dir, entry.name, "SKILL.md"))) {
5917
6230
  results.push(entry.name);
5918
6231
  }
5919
6232
  }
@@ -5957,7 +6270,7 @@ async function vaultMetrics(resolvedPath) {
5957
6270
  }
5958
6271
  let logLines = 0;
5959
6272
  try {
5960
- logLines = readFileSync6(join21(resolvedPath, "log.md"), "utf8").split("\n").length;
6273
+ logLines = readFileSync7(join22(resolvedPath, "log.md"), "utf8").split("\n").length;
5961
6274
  } catch {
5962
6275
  }
5963
6276
  return [
@@ -5989,11 +6302,20 @@ async function runDoctor(input) {
5989
6302
  checks.push(checkVaultStructure(resolvedPath));
5990
6303
  checks.push(checkObsidianTemplates(resolvedPath));
5991
6304
  checks.push(checkVaultGitRemote(gitCheckPath));
6305
+ const fleetLoad = resolvedPath ? await loadFleetManifestAndHost({
6306
+ vault: resolvedPath,
6307
+ env: { ...process.env, WIKI_PATH: input.envValue ?? resolvedPath },
6308
+ home: input.home,
6309
+ cwd: input.cwd,
6310
+ osHostname: process.env.HOSTNAME,
6311
+ user: process.env.USER
6312
+ }) : null;
5992
6313
  checks.push(await checkFleetIdentity({
5993
6314
  vaultPath: resolvedPath,
5994
6315
  home: input.home,
5995
6316
  cwd: input.cwd,
5996
- envValue: input.envValue
6317
+ envValue: input.envValue,
6318
+ fleetLoad
5997
6319
  }));
5998
6320
  checks.push(checkSyncLastPush(gitCheckPath));
5999
6321
  checks.push(checkVaultGitDirty(gitCheckPath));
@@ -6018,6 +6340,9 @@ async function runDoctor(input) {
6018
6340
  vaultSyncServiceScope: vsConfig.serviceScope,
6019
6341
  snapshotScriptPath: vsConfig.snapshotScript
6020
6342
  }));
6343
+ const satelliteGate = satelliteGateFromFleetLoad(fleetLoad);
6344
+ checks.push(checkSatelliteLastRun(resolvedPath, satelliteGate.satelliteExpected));
6345
+ checks.push(checkSatelliteTimer(satelliteGate.satelliteExpected));
6021
6346
  checks.push(...await vaultMetrics(resolvedPath));
6022
6347
  const summary = {
6023
6348
  pass: checks.filter((c) => c.status === "pass").length,
@@ -6042,7 +6367,7 @@ async function runDoctor(input) {
6042
6367
  }
6043
6368
 
6044
6369
  // src/utils/package-info.ts
6045
- import { readFileSync as readFileSync7 } from "fs";
6370
+ import { readFileSync as readFileSync8 } from "fs";
6046
6371
  function packageJsonCandidateUrls(baseUrl = import.meta.url) {
6047
6372
  return [
6048
6373
  new URL("../package.json", baseUrl),
@@ -6052,7 +6377,7 @@ function packageJsonCandidateUrls(baseUrl = import.meta.url) {
6052
6377
  function readCliPackageJson(baseUrl = import.meta.url) {
6053
6378
  for (const url of packageJsonCandidateUrls(baseUrl)) {
6054
6379
  try {
6055
- const pkg = JSON.parse(readFileSync7(url, "utf8"));
6380
+ const pkg = JSON.parse(readFileSync8(url, "utf8"));
6056
6381
  if (typeof pkg.version === "string") {
6057
6382
  return { ...pkg, version: pkg.version };
6058
6383
  }
@@ -6064,11 +6389,11 @@ function readCliPackageJson(baseUrl = import.meta.url) {
6064
6389
 
6065
6390
  // src/commands/project-index.ts
6066
6391
  import { readdir as readdir4, readFile as readFile16, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
6067
- import { join as join22, dirname as dirname8 } from "path";
6392
+ import { join as join23, dirname as dirname8 } from "path";
6068
6393
  var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
6069
6394
  async function runProjectIndex(input) {
6070
6395
  const slug = input.slug;
6071
- const projectDir = join22(input.vault, "projects", slug);
6396
+ const projectDir = join23(input.vault, "projects", slug);
6072
6397
  try {
6073
6398
  await readdir4(projectDir);
6074
6399
  } catch {
@@ -6079,12 +6404,12 @@ async function runProjectIndex(input) {
6079
6404
  }
6080
6405
  const wikilinkPattern = `[[${slug}]]`;
6081
6406
  const entries = [];
6082
- const compoundDir = join22(input.vault, "projects", slug, "compound");
6407
+ const compoundDir = join23(input.vault, "projects", slug, "compound");
6083
6408
  try {
6084
6409
  const compoundFiles = await readdir4(compoundDir, { withFileTypes: true });
6085
6410
  for (const entry of compoundFiles) {
6086
6411
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
6087
- const filePath = join22(compoundDir, entry.name);
6412
+ const filePath = join23(compoundDir, entry.name);
6088
6413
  let text;
6089
6414
  try {
6090
6415
  text = await readFile16(filePath, "utf8");
@@ -6104,13 +6429,13 @@ async function runProjectIndex(input) {
6104
6429
  for (const dir of LAYER2_DIRS) {
6105
6430
  let files;
6106
6431
  try {
6107
- files = await readdir4(join22(input.vault, dir), { withFileTypes: true });
6432
+ files = await readdir4(join23(input.vault, dir), { withFileTypes: true });
6108
6433
  } catch {
6109
6434
  continue;
6110
6435
  }
6111
6436
  for (const entry of files) {
6112
6437
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
6113
- const filePath = join22(input.vault, dir, entry.name);
6438
+ const filePath = join23(input.vault, dir, entry.name);
6114
6439
  let text;
6115
6440
  try {
6116
6441
  text = await readFile16(filePath, "utf8");
@@ -6134,7 +6459,7 @@ async function runProjectIndex(input) {
6134
6459
  const tb = typeOrder[b.type] ?? 99;
6135
6460
  return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
6136
6461
  });
6137
- const indexPath = join22(projectDir, "knowledge.md");
6462
+ const indexPath = join23(projectDir, "knowledge.md");
6138
6463
  let existing = false;
6139
6464
  let stale = false;
6140
6465
  try {
@@ -6208,8 +6533,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
6208
6533
 
6209
6534
  // src/commands/observe.ts
6210
6535
  import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
6211
- import { existsSync as existsSync10, statSync as statSync2 } from "fs";
6212
- import { join as join23 } from "path";
6536
+ import { existsSync as existsSync11, statSync as statSync2 } from "fs";
6537
+ import { join as join24 } from "path";
6213
6538
  import { createHash as createHash5 } from "crypto";
6214
6539
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
6215
6540
  function slugify(text) {
@@ -6232,13 +6557,13 @@ async function runObserve(input) {
6232
6557
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
6233
6558
  };
6234
6559
  }
6235
- if (!existsSync10(input.vault) || !statSync2(input.vault).isDirectory()) {
6560
+ if (!existsSync11(input.vault) || !statSync2(input.vault).isDirectory()) {
6236
6561
  return {
6237
6562
  exitCode: ExitCode.VAULT_PATH_INVALID,
6238
6563
  result: err("VAULT_PATH_INVALID", { path: input.vault })
6239
6564
  };
6240
6565
  }
6241
- const transcriptsDir = join23(input.vault, "raw", "transcripts");
6566
+ const transcriptsDir = join24(input.vault, "raw", "transcripts");
6242
6567
  try {
6243
6568
  await mkdir6(transcriptsDir, { recursive: true });
6244
6569
  } catch {
@@ -6250,7 +6575,7 @@ async function runObserve(input) {
6250
6575
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6251
6576
  const slug = slugify(input.text);
6252
6577
  const fileName = `${today}-observation-${slug}.md`;
6253
- const filePath = join23(transcriptsDir, fileName);
6578
+ const filePath = join24(transcriptsDir, fileName);
6254
6579
  const body = `
6255
6580
  ${input.text.trim()}
6256
6581
  `;
@@ -6292,7 +6617,7 @@ ${input.text.trim()}
6292
6617
  // src/commands/memory.ts
6293
6618
  import { createHash as createHash6 } from "crypto";
6294
6619
  import { mkdir as mkdir7, readFile as readFile17, readdir as readdir5, stat as stat5, writeFile as writeFile8 } from "fs/promises";
6295
- import { basename as basename2, extname, join as join24, relative as relative4, sep as sep4 } from "path";
6620
+ import { basename as basename2, extname, join as join25, relative as relative4, sep as sep4 } from "path";
6296
6621
  async function runMemoryTopics(input) {
6297
6622
  const scan = await scanVault(input.vault);
6298
6623
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -6356,8 +6681,8 @@ async function runMemoryIndex(input) {
6356
6681
  }
6357
6682
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
6358
6683
  const relCachePath = memoryCacheRelPath(input.project);
6359
- const absCachePath = join24(input.vault, relCachePath);
6360
- await mkdir7(join24(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
6684
+ const absCachePath = join25(input.vault, relCachePath);
6685
+ await mkdir7(join25(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
6361
6686
  await writeFile8(absCachePath, `${JSON.stringify({
6362
6687
  generated_at: generatedAt,
6363
6688
  project: input.project,
@@ -6549,7 +6874,7 @@ async function buildMemoryIndexState(pages, project) {
6549
6874
  }
6550
6875
  async function checkMemoryIndex(vault, project, current) {
6551
6876
  const relCachePath = memoryCacheRelPath(project);
6552
- const cacheText = await readIfExists2(join24(vault, relCachePath));
6877
+ const cacheText = await readIfExists2(join25(vault, relCachePath));
6553
6878
  if (!cacheText) {
6554
6879
  return {
6555
6880
  ok: true,
@@ -6958,7 +7283,7 @@ async function walkImportFiles(dir, out) {
6958
7283
  const entries = await readdir5(dir, { withFileTypes: true });
6959
7284
  for (const entry of entries) {
6960
7285
  if (entry.name === ".git" || entry.name === "node_modules") continue;
6961
- const path = join24(dir, entry.name);
7286
+ const path = join25(dir, entry.name);
6962
7287
  if (entry.isDirectory()) {
6963
7288
  await walkImportFiles(path, out);
6964
7289
  } else if (entry.isFile() && isImportCandidate(path)) {
@@ -7025,8 +7350,8 @@ async function writeImportCapture(vault, entry, today) {
7025
7350
  const content = hiddenString(entry, "__content");
7026
7351
  const project = hiddenString(entry, "__project");
7027
7352
  const relPath = await availableImportPath(vault, entry.proposed_path);
7028
- const absPath = join24(vault, relPath);
7029
- await mkdir7(join24(vault, "raw", "transcripts"), { recursive: true });
7353
+ const absPath = join25(vault, relPath);
7354
+ await mkdir7(join25(vault, "raw", "transcripts"), { recursive: true });
7030
7355
  await writeFile8(absPath, renderImportCapture(entry, content, project, today), "utf8");
7031
7356
  const validation = await runValidate({ file: absPath });
7032
7357
  return {
@@ -7042,7 +7367,7 @@ async function availableImportPath(vault, proposed) {
7042
7367
  const stem = proposed.slice(0, -ext.length);
7043
7368
  let candidate = proposed;
7044
7369
  let i = 2;
7045
- while (await readIfExists2(join24(vault, candidate))) {
7370
+ while (await readIfExists2(join25(vault, candidate))) {
7046
7371
  candidate = `${stem}-${i}${ext}`;
7047
7372
  i++;
7048
7373
  }
@@ -7214,10 +7539,10 @@ function memoryCacheRelPath(project) {
7214
7539
  }
7215
7540
  async function readMemoryCache(vault, project) {
7216
7541
  if (project) {
7217
- const projectCache = await readIfExists2(join24(vault, memoryCacheRelPath(project)));
7542
+ const projectCache = await readIfExists2(join25(vault, memoryCacheRelPath(project)));
7218
7543
  if (projectCache) return projectCache;
7219
7544
  }
7220
- return readIfExists2(join24(vault, ".skillwiki", "memory-topics.json"));
7545
+ return readIfExists2(join25(vault, ".skillwiki", "memory-topics.json"));
7221
7546
  }
7222
7547
  function dedupePages(pages) {
7223
7548
  const seen = /* @__PURE__ */ new Set();
@@ -7304,7 +7629,7 @@ function slugify2(value) {
7304
7629
 
7305
7630
  // src/commands/query.ts
7306
7631
  import { readFile as readFile18, stat as stat6 } from "fs/promises";
7307
- import { join as join25 } from "path";
7632
+ import { join as join26 } from "path";
7308
7633
  var W_KEYWORD = 2;
7309
7634
  var W_SOURCE_OVERLAP = 4;
7310
7635
  var W_WIKILINK = 3;
@@ -7425,7 +7750,7 @@ function computeKeywordScore(terms, title, tags, body) {
7425
7750
  return score;
7426
7751
  }
7427
7752
  async function loadOrBuildGraph(vault) {
7428
- const graphPath = join25(vault, ".skillwiki", "graph.json");
7753
+ const graphPath = join26(vault, ".skillwiki", "graph.json");
7429
7754
  let needsBuild = false;
7430
7755
  try {
7431
7756
  const fileStat = await stat6(graphPath);
@@ -7454,7 +7779,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
7454
7779
  import { z as z2 } from "zod";
7455
7780
 
7456
7781
  // src/mcp/vault-resolve.ts
7457
- import { join as join26, resolve as resolve7 } from "path";
7782
+ import { join as join27, resolve as resolve7 } from "path";
7458
7783
 
7459
7784
  // src/mcp/allowlist.ts
7460
7785
  import { resolve as resolve6, sep as sep5 } from "path";
@@ -7515,7 +7840,7 @@ async function resolveMcpVault(input) {
7515
7840
  return ok({ vault: vaultPath, source });
7516
7841
  }
7517
7842
  function defaultGraphOut(vault) {
7518
- return join26(vault, ".skillwiki", "graph.json");
7843
+ return join27(vault, ".skillwiki", "graph.json");
7519
7844
  }
7520
7845
 
7521
7846
  // src/mcp/result-format.ts
@@ -7532,7 +7857,7 @@ function formatToolResult(payload) {
7532
7857
  // src/mcp/audit-log.ts
7533
7858
  import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
7534
7859
  import { homedir } from "os";
7535
- import { join as join27 } from "path";
7860
+ import { join as join28 } from "path";
7536
7861
  function auditEnabled() {
7537
7862
  const v = process.env.SKILLWIKI_MCP_AUDIT;
7538
7863
  if (v === "0" || v === "false") return false;
@@ -7544,7 +7869,7 @@ function auditSink() {
7544
7869
  function auditFilePath() {
7545
7870
  const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
7546
7871
  if (custom && custom.length > 0) return custom;
7547
- return join27(homedir(), ".skillwiki", "mcp-audit.jsonl");
7872
+ return join28(homedir(), ".skillwiki", "mcp-audit.jsonl");
7548
7873
  }
7549
7874
  function auditMcpToolCall(entry) {
7550
7875
  if (!auditEnabled()) return;
@@ -7554,7 +7879,7 @@ function auditMcpToolCall(entry) {
7554
7879
  return;
7555
7880
  }
7556
7881
  const path = auditFilePath();
7557
- mkdirSync4(join27(path, ".."), { recursive: true });
7882
+ mkdirSync4(join28(path, ".."), { recursive: true });
7558
7883
  appendFileSync(path, line, "utf8");
7559
7884
  }
7560
7885
  async function runMcpToolHandler(tool, input, fn) {
@@ -7775,7 +8100,7 @@ function registerMcpMutatingTools(server) {
7775
8100
 
7776
8101
  // src/mcp/resources.ts
7777
8102
  import { readFile as readFile20 } from "fs/promises";
7778
- import { join as join29 } from "path";
8103
+ import { join as join30 } from "path";
7779
8104
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7780
8105
 
7781
8106
  // src/mcp/lint-bucket.ts
@@ -7904,8 +8229,8 @@ async function fetchQueryPreview(input) {
7904
8229
 
7905
8230
  // src/mcp/graph-html.ts
7906
8231
  import { readFile as readFile19 } from "fs/promises";
7907
- import { join as join28 } from "path";
7908
- import { existsSync as existsSync11 } from "fs";
8232
+ import { join as join29 } from "path";
8233
+ import { existsSync as existsSync12 } from "fs";
7909
8234
  var TYPE_COLORS = {
7910
8235
  entities: "#e74c3c",
7911
8236
  concepts: "#27ae60",
@@ -7973,9 +8298,9 @@ ${nodeSvg}
7973
8298
  return { html, node_count: nodes.length, edge_count: edges.length, truncated };
7974
8299
  }
7975
8300
  async function fetchGraphHtmlReport(input) {
7976
- const graphPath = input.graphPath ?? join28(input.vault, ".skillwiki", "graph.json");
8301
+ const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
7977
8302
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
7978
- if (!existsSync11(graphPath)) {
8303
+ if (!existsSync12(graphPath)) {
7979
8304
  return {
7980
8305
  exitCode: ExitCode.FILE_NOT_FOUND,
7981
8306
  result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
@@ -8047,7 +8372,7 @@ async function fetchStaleSummary(input) {
8047
8372
 
8048
8373
  // src/mcp/resources.ts
8049
8374
  async function readVaultFile(vault, rel) {
8050
- return readFile20(join29(vault, rel), "utf8");
8375
+ return readFile20(join30(vault, rel), "utf8");
8051
8376
  }
8052
8377
  async function tailLines(text, lines) {
8053
8378
  const parts = text.split(/\r?\n/);
@@ -8133,7 +8458,7 @@ function registerMcpResources(server) {
8133
8458
  if (!v.ok) {
8134
8459
  return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
8135
8460
  }
8136
- const path = join29(v.data.vault, ".skillwiki", "graph.json");
8461
+ const path = join30(v.data.vault, ".skillwiki", "graph.json");
8137
8462
  try {
8138
8463
  const raw = await readFile20(path, "utf8");
8139
8464
  const graph = JSON.parse(raw);
@@ -8482,6 +8807,7 @@ export {
8482
8807
  runIndexLinkFormat,
8483
8808
  runDedup,
8484
8809
  safeWritePage,
8810
+ runFrontmatterFix,
8485
8811
  fixPathTooLong,
8486
8812
  assessSourceIdentity,
8487
8813
  runLint,
@@ -8492,8 +8818,16 @@ export {
8492
8818
  runConfigPath,
8493
8819
  writeCache,
8494
8820
  triggerAutoUpdate,
8821
+ FLEET_REL_PATH,
8495
8822
  runFleetValidate,
8496
8823
  runFleetContext,
8824
+ loadFleetManifest,
8825
+ resolveFleetHostId,
8826
+ SATELLITE_STALE_MS,
8827
+ satelliteLatestRunPath,
8828
+ isFailedRunStatus,
8829
+ readSatelliteLatestRunFromText,
8830
+ evaluateSatelliteRunHealth,
8497
8831
  runDoctor,
8498
8832
  readCliPackageJson,
8499
8833
  runProjectIndex,