skillwiki 0.9.52 → 0.9.56
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.
- package/dist/auto-update-bg.js +28 -8
- package/dist/chunk-7I2TPIV5.js +154 -0
- package/dist/{chunk-D6T7GSHK.js → chunk-SE5URAJR.js} +480 -216
- package/dist/cli.js +56 -10
- package/dist/skillwiki-mcp.js +2 -2
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/dist/chunk-E6UWZ3S3.js +0 -62
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
CHECK_INTERVAL_MS,
|
|
5
|
-
CLI_DISABLE_FLAG,
|
|
6
|
-
DIST_TAG,
|
|
7
|
-
ENV_DISABLE_KEY,
|
|
8
|
-
normalizeDistTag,
|
|
3
|
+
latestFromCache,
|
|
9
4
|
semverGt
|
|
10
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7I2TPIV5.js";
|
|
11
6
|
|
|
12
7
|
// ../shared/src/exit-codes.ts
|
|
13
8
|
var ExitCode = {
|
|
@@ -2852,7 +2847,7 @@ function buildCliSurface() {
|
|
|
2852
2847
|
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
2848
|
program.command("config");
|
|
2854
2849
|
program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
|
|
2855
|
-
program.command("doctor");
|
|
2850
|
+
program.command("doctor").option("--check-snapshotter");
|
|
2856
2851
|
program.command("status").option("--wiki <name>");
|
|
2857
2852
|
program.command("archive").option("--wiki <name>").option("--cascade").option("--apply").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>");
|
|
2858
2853
|
program.command("drift").option("--apply").option("--new <date>").option("--wiki <name>");
|
|
@@ -2887,7 +2882,7 @@ function buildCliSurface() {
|
|
|
2887
2882
|
compoundCmd.command("list").requiredOption("--project <slug>").option("--wiki <name>");
|
|
2888
2883
|
compoundCmd.command("delete").requiredOption("--project <slug>").option("--wiki <name>");
|
|
2889
2884
|
const syncCmd = program.commands.find((c) => c.name() === "sync");
|
|
2890
|
-
syncCmd.command("status").option("--wiki <name>").option("--include-stashes");
|
|
2885
|
+
syncCmd.command("status").option("--wiki <name>").option("--include-stashes").option("--include-remote-health").option("--check-snapshotter");
|
|
2891
2886
|
syncCmd.command("push").option("--wiki <name>");
|
|
2892
2887
|
syncCmd.command("pull").option("--wiki <name>");
|
|
2893
2888
|
syncCmd.command("lock").option("--summary <text>").option("--ttl-minutes <n>").option("--force").option("--wiki <name>");
|
|
@@ -3100,7 +3095,7 @@ function extractSourceEntries(rawFm) {
|
|
|
3100
3095
|
}
|
|
3101
3096
|
return entries;
|
|
3102
3097
|
}
|
|
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"];
|
|
3098
|
+
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
3099
|
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
3100
|
var INFO_ORDER = ["bridges", "sparse_community", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "stale_sections", "cli_refs"];
|
|
3106
3101
|
var KNOWN_BUCKETS = [...ERROR_ORDER, ...WARNING_ORDER, ...INFO_ORDER];
|
|
@@ -3448,6 +3443,38 @@ async function runFileSourceUrlOnly(input) {
|
|
|
3448
3443
|
const match = remaining.size > 0 ? [{ kind: "file_source_url", items: [...remaining] }] : [];
|
|
3449
3444
|
return outputForOnlyBucket(input, match, fixed, unresolved, readVault);
|
|
3450
3445
|
}
|
|
3446
|
+
function scanConflictMarkerBlocks(path, text) {
|
|
3447
|
+
const findings = [];
|
|
3448
|
+
const lines = text.split(/\r?\n/);
|
|
3449
|
+
let inFence = false;
|
|
3450
|
+
let openLine = 0;
|
|
3451
|
+
let sawSeparator = false;
|
|
3452
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
3453
|
+
const line = lines[i];
|
|
3454
|
+
if (line.startsWith("```") || line.startsWith("~~~")) {
|
|
3455
|
+
inFence = !inFence;
|
|
3456
|
+
continue;
|
|
3457
|
+
}
|
|
3458
|
+
if (inFence) continue;
|
|
3459
|
+
if (line.startsWith("<<<<<<< ")) {
|
|
3460
|
+
openLine = i + 1;
|
|
3461
|
+
sawSeparator = false;
|
|
3462
|
+
continue;
|
|
3463
|
+
}
|
|
3464
|
+
if (line === "=======" && openLine > 0) {
|
|
3465
|
+
sawSeparator = true;
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
if (line.startsWith(">>>>>>> ")) {
|
|
3469
|
+
if (openLine > 0 && sawSeparator) {
|
|
3470
|
+
findings.push({ path, line: openLine, message: "complete Git conflict-marker block" });
|
|
3471
|
+
}
|
|
3472
|
+
openLine = 0;
|
|
3473
|
+
sawSeparator = false;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
return findings;
|
|
3477
|
+
}
|
|
3451
3478
|
async function runLint(input) {
|
|
3452
3479
|
if (input.only && !KNOWN_BUCKETS.includes(input.only)) {
|
|
3453
3480
|
return {
|
|
@@ -3545,10 +3572,12 @@ async function runLint(input) {
|
|
|
3545
3572
|
{
|
|
3546
3573
|
const allPageResults = await mapWithConcurrency(scan.allMarkdown, vaultIoConcurrency(), async (page) => {
|
|
3547
3574
|
const sensitiveFlags2 = [];
|
|
3575
|
+
const conflictMarkers2 = [];
|
|
3548
3576
|
let fmYamlInvalid2 = null;
|
|
3549
3577
|
try {
|
|
3550
3578
|
const text = await readPageCached(page, pageTextCache);
|
|
3551
3579
|
sensitiveFlags2.push(...scanSensitiveContent(text, { file: page.relPath }));
|
|
3580
|
+
conflictMarkers2.push(...scanConflictMarkerBlocks(page.relPath, text));
|
|
3552
3581
|
const fm = extractFrontmatter(text);
|
|
3553
3582
|
if (!fm.ok && fm.error === "INVALID_FRONTMATTER") {
|
|
3554
3583
|
const detail = fm.detail;
|
|
@@ -3557,10 +3586,12 @@ async function runLint(input) {
|
|
|
3557
3586
|
}
|
|
3558
3587
|
} catch {
|
|
3559
3588
|
}
|
|
3560
|
-
return { sensitiveFlags: sensitiveFlags2, fmYamlInvalid: fmYamlInvalid2 };
|
|
3589
|
+
return { sensitiveFlags: sensitiveFlags2, conflictMarkers: conflictMarkers2, fmYamlInvalid: fmYamlInvalid2 };
|
|
3561
3590
|
});
|
|
3562
3591
|
const sensitiveFlags = allPageResults.flatMap((result) => result.sensitiveFlags);
|
|
3563
3592
|
if (sensitiveFlags.length > 0) buckets.sensitive_content = sensitiveFlags;
|
|
3593
|
+
const conflictMarkers = allPageResults.flatMap((result) => result.conflictMarkers);
|
|
3594
|
+
if (conflictMarkers.length > 0) buckets.conflict_markers = conflictMarkers;
|
|
3564
3595
|
const fmYamlInvalid = allPageResults.map((result) => result.fmYamlInvalid).filter((item) => item !== null);
|
|
3565
3596
|
if (fmYamlInvalid.length > 0) buckets.frontmatter_yaml_invalid = fmYamlInvalid;
|
|
3566
3597
|
const subDirDupes = [];
|
|
@@ -4281,71 +4312,12 @@ async function runConfigPath(input) {
|
|
|
4281
4312
|
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync6(filePath), humanHint: filePath }) };
|
|
4282
4313
|
}
|
|
4283
4314
|
|
|
4284
|
-
// src/utils/auto-update.ts
|
|
4285
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
4286
|
-
import { join as join17, dirname as dirname7 } from "path";
|
|
4287
|
-
import { spawn } from "child_process";
|
|
4288
|
-
function cachePath(home) {
|
|
4289
|
-
return join17(home, ".skillwiki", CACHE_FILENAME);
|
|
4290
|
-
}
|
|
4291
|
-
function readCacheRaw(home) {
|
|
4292
|
-
try {
|
|
4293
|
-
const raw = readFileSync4(cachePath(home), "utf8");
|
|
4294
|
-
return JSON.parse(raw);
|
|
4295
|
-
} catch {
|
|
4296
|
-
return null;
|
|
4297
|
-
}
|
|
4298
|
-
}
|
|
4299
|
-
function readCache(home) {
|
|
4300
|
-
const cache = readCacheRaw(home);
|
|
4301
|
-
if (!cache) return { cache: null, hasUpdate: false, isStale: true };
|
|
4302
|
-
const isStale = Date.now() - cache.lastCheck >= CHECK_INTERVAL_MS;
|
|
4303
|
-
const hasUpdate = !!cache.latestVersion && semverGt(cache.latestVersion, cache.currentVersion);
|
|
4304
|
-
return { cache, hasUpdate, isStale };
|
|
4305
|
-
}
|
|
4306
|
-
function writeCache(home, cache) {
|
|
4307
|
-
const p = cachePath(home);
|
|
4308
|
-
mkdirSync3(dirname7(p), { recursive: true });
|
|
4309
|
-
writeFileSync3(p, JSON.stringify(cache, null, 2));
|
|
4310
|
-
}
|
|
4311
|
-
function latestFromCache(home, currentVersion) {
|
|
4312
|
-
const { cache } = readCache(home);
|
|
4313
|
-
if (!cache || !cache.latestVersion) return { hasUpdate: false, latest: null, distTag: DIST_TAG };
|
|
4314
|
-
const distTag = normalizeDistTag(cache.distTag);
|
|
4315
|
-
return {
|
|
4316
|
-
hasUpdate: semverGt(cache.latestVersion, currentVersion),
|
|
4317
|
-
latest: cache.latestVersion,
|
|
4318
|
-
distTag
|
|
4319
|
-
};
|
|
4320
|
-
}
|
|
4321
|
-
function distTagFromCache(home) {
|
|
4322
|
-
return normalizeDistTag(readCacheRaw(home)?.distTag);
|
|
4323
|
-
}
|
|
4324
|
-
function isDisabled() {
|
|
4325
|
-
return !!(process.env[ENV_DISABLE_KEY] || process.env.NODE_ENV === "test" || process.argv.includes(CLI_DISABLE_FLAG));
|
|
4326
|
-
}
|
|
4327
|
-
function triggerAutoUpdate(home, currentVersion) {
|
|
4328
|
-
if (isDisabled()) return;
|
|
4329
|
-
const { isStale } = readCache(home);
|
|
4330
|
-
if (!isStale) return;
|
|
4331
|
-
const distTag = distTagFromCache(home);
|
|
4332
|
-
const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
|
|
4333
|
-
if (!existsSync7(bgScript)) return;
|
|
4334
|
-
const child = spawn(process.execPath, [bgScript, home, currentVersion, distTag], {
|
|
4335
|
-
detached: true,
|
|
4336
|
-
stdio: "ignore"
|
|
4337
|
-
});
|
|
4338
|
-
child.on("error", () => {
|
|
4339
|
-
});
|
|
4340
|
-
child.unref();
|
|
4341
|
-
}
|
|
4342
|
-
|
|
4343
4315
|
// src/commands/fleet.ts
|
|
4344
4316
|
import { readFile as readFile14 } from "fs/promises";
|
|
4345
4317
|
import { hostname as nodeHostname, userInfo } from "os";
|
|
4346
|
-
import { join as
|
|
4318
|
+
import { join as join17 } from "path";
|
|
4347
4319
|
import yaml3 from "js-yaml";
|
|
4348
|
-
var FLEET_REL_PATH =
|
|
4320
|
+
var FLEET_REL_PATH = join17("projects", "llm-wiki", "architecture", "fleet.yaml");
|
|
4349
4321
|
async function runFleetValidate(input) {
|
|
4350
4322
|
const loaded = await loadFleetManifest(input.file);
|
|
4351
4323
|
if (!loaded.ok) {
|
|
@@ -4376,7 +4348,7 @@ async function runFleetContext(input) {
|
|
|
4376
4348
|
const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
|
|
4377
4349
|
const user = input.user ?? safeEnvValue(env.USER) ?? safeUserName();
|
|
4378
4350
|
const vault = input.vault ?? safeEnvValue(env.WIKI_PATH);
|
|
4379
|
-
const file = input.file ?? (vault ?
|
|
4351
|
+
const file = input.file ?? (vault ? join17(vault, FLEET_REL_PATH) : void 0);
|
|
4380
4352
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4381
4353
|
const loaded = file ? await loadFleetManifest(file) : { ok: false, error: "FILE_NOT_FOUND" };
|
|
4382
4354
|
if (!loaded.ok) {
|
|
@@ -4496,7 +4468,7 @@ function fleetContextEnv(input) {
|
|
|
4496
4468
|
const home = input.home ?? env.HOME ?? "";
|
|
4497
4469
|
const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
|
|
4498
4470
|
const vault = input.vault ?? safeEnvValue(env.WIKI_PATH);
|
|
4499
|
-
const file = input.file ?? (vault ?
|
|
4471
|
+
const file = input.file ?? (vault ? join17(vault, FLEET_REL_PATH) : void 0);
|
|
4500
4472
|
return { env, home, osHostname, vault, file };
|
|
4501
4473
|
}
|
|
4502
4474
|
async function loadFleetManifestAndHost(input) {
|
|
@@ -4538,6 +4510,15 @@ async function loadFleetManifestAndHost(input) {
|
|
|
4538
4510
|
identityStatus: "known"
|
|
4539
4511
|
};
|
|
4540
4512
|
}
|
|
4513
|
+
function snapshotterAliasForLocalHost(fleetLoad) {
|
|
4514
|
+
if (!fleetLoad?.manifest || !fleetLoad.hostId) return void 0;
|
|
4515
|
+
const snapshotterId = Object.entries(fleetLoad.manifest.hosts).find(([, h]) => h.role === "snapshotter")?.[0];
|
|
4516
|
+
if (!snapshotterId) return void 0;
|
|
4517
|
+
const profile = fleetLoad.manifest.hosts[snapshotterId]?.access?.from?.[fleetLoad.hostId];
|
|
4518
|
+
if (!profile || profile.status !== "configured" && profile.status !== "local") return void 0;
|
|
4519
|
+
const aliases = profile.ssh_aliases ?? [];
|
|
4520
|
+
return aliases.length > 0 ? aliases[0] : void 0;
|
|
4521
|
+
}
|
|
4541
4522
|
function satelliteGateFromFleetLoad(load) {
|
|
4542
4523
|
if (!load?.hostId) return { satelliteExpected: false };
|
|
4543
4524
|
const host = load.manifest.hosts[load.hostId];
|
|
@@ -4621,7 +4602,7 @@ async function resolveFleetHostId(input) {
|
|
|
4621
4602
|
}
|
|
4622
4603
|
trace.push({ source: "AGENT_HOST_ID", status: "unset" });
|
|
4623
4604
|
if (input.home) {
|
|
4624
|
-
const dotenv = await parseDotenvFile(
|
|
4605
|
+
const dotenv = await parseDotenvFile(join17(input.home, ".skillwiki", ".env"));
|
|
4625
4606
|
if (dotenv.SKILLWIKI_HOST_ID) {
|
|
4626
4607
|
trace.push({ source: "~/.skillwiki/.env:SKILLWIKI_HOST_ID", status: "matched", value: dotenv.SKILLWIKI_HOST_ID });
|
|
4627
4608
|
return { hostId: dotenv.SKILLWIKI_HOST_ID, source: "~/.skillwiki/.env:SKILLWIKI_HOST_ID", trace };
|
|
@@ -4794,20 +4775,20 @@ function safeUserName() {
|
|
|
4794
4775
|
}
|
|
4795
4776
|
|
|
4796
4777
|
// src/commands/doctor.ts
|
|
4797
|
-
import { existsSync as
|
|
4798
|
-
import { join as
|
|
4778
|
+
import { existsSync as existsSync12, lstatSync, readlinkSync, readdirSync as readdirSync3, statSync, readFileSync as readFileSync9 } from "fs";
|
|
4779
|
+
import { join as join23, resolve as resolve5 } from "path";
|
|
4799
4780
|
import { execSync as execSync2 } from "child_process";
|
|
4800
4781
|
import { platform as platform2 } from "os";
|
|
4801
4782
|
|
|
4802
4783
|
// src/utils/plugin-registry.ts
|
|
4803
|
-
import { existsSync as
|
|
4804
|
-
import { join as
|
|
4805
|
-
var REGISTRY_PATH =
|
|
4806
|
-
var CODEX_CONFIG_PATH =
|
|
4784
|
+
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync4 } from "fs";
|
|
4785
|
+
import { join as join18 } from "path";
|
|
4786
|
+
var REGISTRY_PATH = join18(".claude", "plugins", "installed_plugins.json");
|
|
4787
|
+
var CODEX_CONFIG_PATH = join18(".codex", "config.toml");
|
|
4807
4788
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
4808
4789
|
function readInstalledPlugins(home) {
|
|
4809
4790
|
try {
|
|
4810
|
-
const raw =
|
|
4791
|
+
const raw = readFileSync4(join18(home, REGISTRY_PATH), "utf8");
|
|
4811
4792
|
return JSON.parse(raw);
|
|
4812
4793
|
} catch {
|
|
4813
4794
|
return null;
|
|
@@ -4843,8 +4824,8 @@ function findPluginInstallations(home, key = PLUGIN_KEY) {
|
|
|
4843
4824
|
function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
4844
4825
|
const config = readCodexPluginConfig(home, key, marketplace);
|
|
4845
4826
|
if (!config?.enabled) return null;
|
|
4846
|
-
const cacheRoot =
|
|
4847
|
-
if (!
|
|
4827
|
+
const cacheRoot = join18(home, ".codex", "plugins", "cache", marketplace, pluginName);
|
|
4828
|
+
if (!existsSync7(cacheRoot)) return null;
|
|
4848
4829
|
let versions;
|
|
4849
4830
|
try {
|
|
4850
4831
|
versions = readdirSync(cacheRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -4859,7 +4840,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
|
4859
4840
|
key,
|
|
4860
4841
|
pluginName,
|
|
4861
4842
|
marketplace,
|
|
4862
|
-
installPath:
|
|
4843
|
+
installPath: join18(cacheRoot, version),
|
|
4863
4844
|
version,
|
|
4864
4845
|
sourceType: config.sourceType,
|
|
4865
4846
|
source: config.source
|
|
@@ -4876,7 +4857,7 @@ function parsePluginKey(key) {
|
|
|
4876
4857
|
function readCodexPluginConfig(home, key, marketplace) {
|
|
4877
4858
|
let raw;
|
|
4878
4859
|
try {
|
|
4879
|
-
raw =
|
|
4860
|
+
raw = readFileSync4(join18(home, CODEX_CONFIG_PATH), "utf8");
|
|
4880
4861
|
} catch {
|
|
4881
4862
|
return null;
|
|
4882
4863
|
}
|
|
@@ -4917,12 +4898,180 @@ function parseTomlScalar(rawValue) {
|
|
|
4917
4898
|
return value;
|
|
4918
4899
|
}
|
|
4919
4900
|
|
|
4920
|
-
// src/utils/
|
|
4901
|
+
// src/utils/conflict-markers.ts
|
|
4902
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
4903
|
+
import { join as join19 } from "path";
|
|
4904
|
+
function scanConflictMarkerBlocksInText(relPath, text) {
|
|
4905
|
+
const findings = [];
|
|
4906
|
+
const lines = text.split(/\r?\n/);
|
|
4907
|
+
let inFence = false;
|
|
4908
|
+
let openLine = 0;
|
|
4909
|
+
let sawSeparator = false;
|
|
4910
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
4911
|
+
const line = lines[i];
|
|
4912
|
+
if (line.startsWith("```") || line.startsWith("~~~")) {
|
|
4913
|
+
inFence = !inFence;
|
|
4914
|
+
continue;
|
|
4915
|
+
}
|
|
4916
|
+
if (inFence) continue;
|
|
4917
|
+
if (line.startsWith("<<<<<<< ")) {
|
|
4918
|
+
openLine = i + 1;
|
|
4919
|
+
sawSeparator = false;
|
|
4920
|
+
continue;
|
|
4921
|
+
}
|
|
4922
|
+
if (line === "=======" && openLine > 0) {
|
|
4923
|
+
sawSeparator = true;
|
|
4924
|
+
continue;
|
|
4925
|
+
}
|
|
4926
|
+
if (line.startsWith(">>>>>>> ")) {
|
|
4927
|
+
if (openLine > 0 && sawSeparator) {
|
|
4928
|
+
findings.push({ path: relPath, line: openLine });
|
|
4929
|
+
}
|
|
4930
|
+
openLine = 0;
|
|
4931
|
+
sawSeparator = false;
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
return findings;
|
|
4935
|
+
}
|
|
4936
|
+
var PRUNE_DIRS = /* @__PURE__ */ new Set([
|
|
4937
|
+
".git",
|
|
4938
|
+
".obsidian",
|
|
4939
|
+
".skillwiki",
|
|
4940
|
+
".claude",
|
|
4941
|
+
".antigravitycli",
|
|
4942
|
+
".playwright-cli"
|
|
4943
|
+
]);
|
|
4944
|
+
function walkMarkdownFiles2(root, dir, rel, out) {
|
|
4945
|
+
let entries;
|
|
4946
|
+
try {
|
|
4947
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
4948
|
+
} catch {
|
|
4949
|
+
return;
|
|
4950
|
+
}
|
|
4951
|
+
for (const entry of entries) {
|
|
4952
|
+
if (entry.isDirectory()) {
|
|
4953
|
+
if (PRUNE_DIRS.has(entry.name)) continue;
|
|
4954
|
+
walkMarkdownFiles2(root, join19(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
|
|
4955
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
4956
|
+
out.push(rel ? `${rel}/${entry.name}` : entry.name);
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
function scanVaultConflictMarkers(vaultRoot) {
|
|
4961
|
+
if (!existsSync8(vaultRoot)) return [];
|
|
4962
|
+
const relPaths = [];
|
|
4963
|
+
walkMarkdownFiles2(vaultRoot, vaultRoot, "", relPaths);
|
|
4964
|
+
const all = [];
|
|
4965
|
+
for (const rel of relPaths) {
|
|
4966
|
+
let text;
|
|
4967
|
+
try {
|
|
4968
|
+
text = readFileSync5(join19(vaultRoot, rel), "utf8");
|
|
4969
|
+
} catch {
|
|
4970
|
+
continue;
|
|
4971
|
+
}
|
|
4972
|
+
all.push(...scanConflictMarkerBlocksInText(rel, text));
|
|
4973
|
+
}
|
|
4974
|
+
return all;
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4977
|
+
// src/utils/remote-health.ts
|
|
4921
4978
|
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
4922
4979
|
import { join as join20 } from "path";
|
|
4980
|
+
import { execFileSync } from "child_process";
|
|
4981
|
+
var REMOTE_PROBE_TIMEOUT_MS = 3e3;
|
|
4982
|
+
var defaultExec = (file, args, cwd) => execFileSync(file, args, {
|
|
4983
|
+
cwd,
|
|
4984
|
+
encoding: "utf8",
|
|
4985
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4986
|
+
timeout: REMOTE_PROBE_TIMEOUT_MS
|
|
4987
|
+
}).trim();
|
|
4988
|
+
var DEFAULT_WIKI_S3_REMOTE = "seaweed-wiki:cloud/wiki";
|
|
4989
|
+
function readWikiS3RemoteConfigured(home) {
|
|
4990
|
+
try {
|
|
4991
|
+
const content = readFileSync6(join20(home, ".skillwiki", ".env"), "utf8");
|
|
4992
|
+
for (const line of content.split(/\r?\n/)) {
|
|
4993
|
+
const trimmed = line.trim();
|
|
4994
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
4995
|
+
const eq = trimmed.indexOf("=");
|
|
4996
|
+
if (eq <= 0) continue;
|
|
4997
|
+
const k = trimmed.slice(0, eq).trim();
|
|
4998
|
+
const v = trimmed.slice(eq + 1).trim();
|
|
4999
|
+
if (k === "WIKI_REMOTE" && v.length > 0) return v;
|
|
5000
|
+
}
|
|
5001
|
+
} catch {
|
|
5002
|
+
}
|
|
5003
|
+
return void 0;
|
|
5004
|
+
}
|
|
5005
|
+
function readWikiS3RemoteFromEnv(home) {
|
|
5006
|
+
return readWikiS3RemoteConfigured(home) ?? DEFAULT_WIKI_S3_REMOTE;
|
|
5007
|
+
}
|
|
5008
|
+
function probeGithubReachability(vaultPath, exec = defaultExec) {
|
|
5009
|
+
if (!existsSync9(join20(vaultPath, ".git"))) return "unknown";
|
|
5010
|
+
try {
|
|
5011
|
+
exec("git", ["remote", "get-url", "origin"], vaultPath);
|
|
5012
|
+
} catch {
|
|
5013
|
+
return "unknown";
|
|
5014
|
+
}
|
|
5015
|
+
try {
|
|
5016
|
+
const out = exec("git", ["ls-remote", "origin", "refs/heads/main"], vaultPath);
|
|
5017
|
+
if (out.length > 0) return "ok";
|
|
5018
|
+
return "unreachable";
|
|
5019
|
+
} catch {
|
|
5020
|
+
return "unreachable";
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
5023
|
+
function probeS3Reachability(remote, exec = defaultExec) {
|
|
5024
|
+
if (!remote) return "unknown";
|
|
5025
|
+
try {
|
|
5026
|
+
exec("rclone", ["lsf", remote, "--max-depth", "1", "--files-only"]);
|
|
5027
|
+
return "ok";
|
|
5028
|
+
} catch {
|
|
5029
|
+
return "unreachable";
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
function probeSnapshotterSsh(sshAlias2, exec = defaultExec) {
|
|
5033
|
+
if (!sshAlias2) return "unknown";
|
|
5034
|
+
try {
|
|
5035
|
+
exec("ssh", [
|
|
5036
|
+
"-o",
|
|
5037
|
+
"BatchMode=yes",
|
|
5038
|
+
"-o",
|
|
5039
|
+
"ConnectTimeout=3",
|
|
5040
|
+
"-o",
|
|
5041
|
+
"StrictHostKeyChecking=accept-new",
|
|
5042
|
+
sshAlias2,
|
|
5043
|
+
"true"
|
|
5044
|
+
]);
|
|
5045
|
+
return "ok";
|
|
5046
|
+
} catch {
|
|
5047
|
+
return "unreachable";
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
function buildDegradedReasons(health) {
|
|
5051
|
+
const reasons = [];
|
|
5052
|
+
if (health.github === "unreachable") reasons.push("github_remote_unreachable");
|
|
5053
|
+
if (health.s3 === "unreachable") reasons.push("s3_remote_unreachable");
|
|
5054
|
+
if (health.snapshotter === "unreachable") reasons.push("snapshotter_host_unreachable");
|
|
5055
|
+
return reasons;
|
|
5056
|
+
}
|
|
5057
|
+
function probeRemoteHealth(input) {
|
|
5058
|
+
const exec = input.exec ?? defaultExec;
|
|
5059
|
+
const github = probeGithubReachability(input.vaultPath, exec);
|
|
5060
|
+
const s3Remote = input.s3Remote ?? readWikiS3RemoteFromEnv(input.home);
|
|
5061
|
+
const s3 = probeS3Reachability(s3Remote, exec);
|
|
5062
|
+
let snapshotter = "not_checked";
|
|
5063
|
+
if (input.checkSnapshotter && input.snapshotterAlias) {
|
|
5064
|
+
snapshotter = probeSnapshotterSsh(input.snapshotterAlias, exec);
|
|
5065
|
+
}
|
|
5066
|
+
return { github, s3, snapshotter };
|
|
5067
|
+
}
|
|
5068
|
+
|
|
5069
|
+
// src/utils/satellite-run-health.ts
|
|
5070
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
5071
|
+
import { join as join21 } from "path";
|
|
4923
5072
|
var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
|
|
4924
5073
|
function satelliteLatestRunPath(vault) {
|
|
4925
|
-
return
|
|
5074
|
+
return join21(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
|
|
4926
5075
|
}
|
|
4927
5076
|
function isFailedRunStatus(status) {
|
|
4928
5077
|
return status === "fail" || status === "failure";
|
|
@@ -4944,9 +5093,9 @@ function readSatelliteLatestRunFromText(text) {
|
|
|
4944
5093
|
}
|
|
4945
5094
|
function readSatelliteLatestRun(vault) {
|
|
4946
5095
|
const latestPath = satelliteLatestRunPath(vault);
|
|
4947
|
-
if (!
|
|
5096
|
+
if (!existsSync10(latestPath)) return null;
|
|
4948
5097
|
try {
|
|
4949
|
-
return parseLatestRunFile(
|
|
5098
|
+
return parseLatestRunFile(readFileSync7(latestPath, "utf8"));
|
|
4950
5099
|
} catch {
|
|
4951
5100
|
return null;
|
|
4952
5101
|
}
|
|
@@ -4975,8 +5124,8 @@ function evaluateSatelliteRunHealth(vault, now) {
|
|
|
4975
5124
|
// src/utils/s3-mount-health.ts
|
|
4976
5125
|
import { execSync } from "child_process";
|
|
4977
5126
|
import { platform } from "os";
|
|
4978
|
-
import { readFileSync as
|
|
4979
|
-
import { join as
|
|
5127
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
|
|
5128
|
+
import { join as join22 } from "path";
|
|
4980
5129
|
var OS = platform();
|
|
4981
5130
|
function findRcloneMountPid() {
|
|
4982
5131
|
try {
|
|
@@ -5060,7 +5209,7 @@ function extractRcloneFs(args) {
|
|
|
5060
5209
|
function getRcloneArgs(pid) {
|
|
5061
5210
|
try {
|
|
5062
5211
|
if (OS === "linux") {
|
|
5063
|
-
const raw =
|
|
5212
|
+
const raw = readFileSync8(`/proc/${pid}/cmdline`);
|
|
5064
5213
|
return new TextDecoder().decode(raw).split("\0").filter(Boolean);
|
|
5065
5214
|
} else {
|
|
5066
5215
|
const out = execSync(`ps -o args= -p ${pid}`, {
|
|
@@ -5103,7 +5252,7 @@ function queryRcloneRC(rcAddr, fs) {
|
|
|
5103
5252
|
function detectFuseMount(vaultPath) {
|
|
5104
5253
|
try {
|
|
5105
5254
|
if (OS === "linux") {
|
|
5106
|
-
const mounts =
|
|
5255
|
+
const mounts = readFileSync8("/proc/mounts", "utf8");
|
|
5107
5256
|
let best = null;
|
|
5108
5257
|
for (const line of mounts.split("\n")) {
|
|
5109
5258
|
const parts = line.split(" ");
|
|
@@ -5134,11 +5283,11 @@ function detectFuseMount(vaultPath) {
|
|
|
5134
5283
|
return null;
|
|
5135
5284
|
}
|
|
5136
5285
|
function writeTest(dir) {
|
|
5137
|
-
const testFile =
|
|
5286
|
+
const testFile = join22(dir, `.doctor-write-test-${process.pid}.tmp`);
|
|
5138
5287
|
const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
|
|
5139
5288
|
const start = Date.now();
|
|
5140
5289
|
try {
|
|
5141
|
-
|
|
5290
|
+
writeFileSync3(testFile, payload, "utf8");
|
|
5142
5291
|
} catch (e) {
|
|
5143
5292
|
return { success: false, writeMs: Date.now() - start, readMs: 0, size: 0, error: `write failed: ${e.message}` };
|
|
5144
5293
|
}
|
|
@@ -5234,13 +5383,13 @@ function detectCliChannels(argv, home) {
|
|
|
5234
5383
|
}
|
|
5235
5384
|
const plugin = findPlugin(home);
|
|
5236
5385
|
if (plugin) {
|
|
5237
|
-
const pluginBin =
|
|
5238
|
-
if (
|
|
5386
|
+
const pluginBin = join23(plugin.installPath, "bin", "skillwiki");
|
|
5387
|
+
if (existsSync12(pluginBin)) {
|
|
5239
5388
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
5240
5389
|
}
|
|
5241
5390
|
}
|
|
5242
|
-
const installBin =
|
|
5243
|
-
if (
|
|
5391
|
+
const installBin = join23(home, ".claude", "skills", "bin", "skillwiki");
|
|
5392
|
+
if (existsSync12(installBin)) {
|
|
5244
5393
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
5245
5394
|
}
|
|
5246
5395
|
return channels;
|
|
@@ -5301,7 +5450,7 @@ function isDevSourceRun(argv) {
|
|
|
5301
5450
|
}
|
|
5302
5451
|
async function checkConfigFile(home) {
|
|
5303
5452
|
const cfgPath = configPath(home);
|
|
5304
|
-
if (!
|
|
5453
|
+
if (!existsSync12(cfgPath)) {
|
|
5305
5454
|
return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
|
|
5306
5455
|
}
|
|
5307
5456
|
try {
|
|
@@ -5316,7 +5465,7 @@ function checkWikiPathExists(resolvedPath) {
|
|
|
5316
5465
|
if (resolvedPath === void 0) {
|
|
5317
5466
|
return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5318
5467
|
}
|
|
5319
|
-
if (
|
|
5468
|
+
if (existsSync12(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
|
5320
5469
|
return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
5321
5470
|
}
|
|
5322
5471
|
return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
@@ -5325,13 +5474,13 @@ function checkVaultStructure(resolvedPath) {
|
|
|
5325
5474
|
if (resolvedPath === void 0) {
|
|
5326
5475
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5327
5476
|
}
|
|
5328
|
-
if (!
|
|
5477
|
+
if (!existsSync12(resolvedPath)) {
|
|
5329
5478
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
5330
5479
|
}
|
|
5331
5480
|
const missing = [];
|
|
5332
|
-
if (!
|
|
5481
|
+
if (!existsSync12(join23(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
5333
5482
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
5334
|
-
if (!
|
|
5483
|
+
if (!existsSync12(join23(resolvedPath, dir))) missing.push(dir + "/");
|
|
5335
5484
|
}
|
|
5336
5485
|
if (missing.length === 0) {
|
|
5337
5486
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -5339,8 +5488,8 @@ function checkVaultStructure(resolvedPath) {
|
|
|
5339
5488
|
return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
|
|
5340
5489
|
}
|
|
5341
5490
|
function checkSkillsInstalled(home, cwd) {
|
|
5342
|
-
const srcDir = cwd ?
|
|
5343
|
-
if (srcDir &&
|
|
5491
|
+
const srcDir = cwd ? join23(cwd, "packages", "skills") : void 0;
|
|
5492
|
+
if (srcDir && existsSync12(srcDir)) {
|
|
5344
5493
|
const found = findInstalledSkillMd(srcDir);
|
|
5345
5494
|
if (found.length > 0) {
|
|
5346
5495
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
|
|
@@ -5353,8 +5502,8 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
5353
5502
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
|
|
5354
5503
|
}
|
|
5355
5504
|
}
|
|
5356
|
-
const skillsDir =
|
|
5357
|
-
if (
|
|
5505
|
+
const skillsDir = join23(home, ".claude", "skills");
|
|
5506
|
+
if (existsSync12(skillsDir)) {
|
|
5358
5507
|
const found = findInstalledSkillMd(skillsDir);
|
|
5359
5508
|
if (found.length > 0) {
|
|
5360
5509
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
|
|
@@ -5364,10 +5513,10 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
5364
5513
|
}
|
|
5365
5514
|
function checkDuplicateSkills(home) {
|
|
5366
5515
|
const plugin = findPlugin(home);
|
|
5367
|
-
const skillsDir =
|
|
5516
|
+
const skillsDir = join23(home, ".claude", "skills");
|
|
5368
5517
|
const agentSkillDirs = [
|
|
5369
|
-
{ label: "~/.codex/skills/", path:
|
|
5370
|
-
{ label: "~/.agents/skills/", path:
|
|
5518
|
+
{ label: "~/.codex/skills/", path: join23(home, ".codex", "skills") },
|
|
5519
|
+
{ label: "~/.agents/skills/", path: join23(home, ".agents", "skills") }
|
|
5371
5520
|
];
|
|
5372
5521
|
if (!plugin) {
|
|
5373
5522
|
return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
|
|
@@ -5470,8 +5619,8 @@ async function checkProfiles(home) {
|
|
|
5470
5619
|
}
|
|
5471
5620
|
async function checkProjectLocalOverride(cwd) {
|
|
5472
5621
|
const dir = cwd ?? process.cwd();
|
|
5473
|
-
const envPath =
|
|
5474
|
-
if (
|
|
5622
|
+
const envPath = join23(dir, ".skillwiki", ".env");
|
|
5623
|
+
if (existsSync12(envPath)) {
|
|
5475
5624
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
5476
5625
|
}
|
|
5477
5626
|
return check("pass", "project_local", "Project-local config", "None");
|
|
@@ -5480,7 +5629,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
5480
5629
|
if (resolvedPath === void 0) {
|
|
5481
5630
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5482
5631
|
}
|
|
5483
|
-
if (!
|
|
5632
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5484
5633
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
5485
5634
|
}
|
|
5486
5635
|
try {
|
|
@@ -5503,9 +5652,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
5503
5652
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5504
5653
|
}
|
|
5505
5654
|
const missing = [];
|
|
5506
|
-
if (!
|
|
5507
|
-
if (!
|
|
5508
|
-
if (!
|
|
5655
|
+
if (!existsSync12(join23(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
5656
|
+
if (!existsSync12(join23(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
5657
|
+
if (!existsSync12(join23(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
5509
5658
|
if (missing.length === 0) {
|
|
5510
5659
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
5511
5660
|
}
|
|
@@ -5515,15 +5664,15 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
5515
5664
|
if (resolvedPath === void 0) {
|
|
5516
5665
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5517
5666
|
}
|
|
5518
|
-
const rawDir =
|
|
5519
|
-
if (!
|
|
5667
|
+
const rawDir = join23(resolvedPath, "raw");
|
|
5668
|
+
if (!existsSync12(rawDir)) {
|
|
5520
5669
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
5521
5670
|
}
|
|
5522
5671
|
const found = [];
|
|
5523
5672
|
(function walk2(dir, rel) {
|
|
5524
5673
|
let entries;
|
|
5525
5674
|
try {
|
|
5526
|
-
entries =
|
|
5675
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
5527
5676
|
} catch {
|
|
5528
5677
|
return;
|
|
5529
5678
|
}
|
|
@@ -5531,7 +5680,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
5531
5680
|
if (entry.name === ".DS_Store") {
|
|
5532
5681
|
found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
|
|
5533
5682
|
} else if (entry.isDirectory()) {
|
|
5534
|
-
walk2(
|
|
5683
|
+
walk2(join23(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
5535
5684
|
}
|
|
5536
5685
|
}
|
|
5537
5686
|
})(rawDir, "");
|
|
@@ -5540,11 +5689,29 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
5540
5689
|
}
|
|
5541
5690
|
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
5691
|
}
|
|
5692
|
+
function checkVaultConflictMarkers(resolvedPath) {
|
|
5693
|
+
if (resolvedPath === void 0) {
|
|
5694
|
+
return check("pass", "vault_conflict_markers", "Vault conflict markers", "No vault path \u2014 check skipped");
|
|
5695
|
+
}
|
|
5696
|
+
const findings = scanVaultConflictMarkers(resolvedPath);
|
|
5697
|
+
if (findings.length === 0) {
|
|
5698
|
+
return check("pass", "vault_conflict_markers", "Vault conflict markers", "No complete conflict-marker blocks");
|
|
5699
|
+
}
|
|
5700
|
+
const first = findings[0];
|
|
5701
|
+
const n = findings.length;
|
|
5702
|
+
const fileWord = n === 1 ? "file" : "files";
|
|
5703
|
+
return check(
|
|
5704
|
+
"error",
|
|
5705
|
+
"vault_conflict_markers",
|
|
5706
|
+
"Vault conflict markers",
|
|
5707
|
+
`${n} ${fileWord}, first: ${first.path}:${first.line}`
|
|
5708
|
+
);
|
|
5709
|
+
}
|
|
5543
5710
|
function checkSyncLastPush(resolvedPath) {
|
|
5544
5711
|
if (resolvedPath === void 0) {
|
|
5545
5712
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5546
5713
|
}
|
|
5547
|
-
if (!
|
|
5714
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5548
5715
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
5549
5716
|
}
|
|
5550
5717
|
let timestamp;
|
|
@@ -5592,7 +5759,7 @@ function checkVaultGitDirty(resolvedPath) {
|
|
|
5592
5759
|
if (resolvedPath === void 0) {
|
|
5593
5760
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
|
|
5594
5761
|
}
|
|
5595
|
-
if (!
|
|
5762
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5596
5763
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
|
|
5597
5764
|
}
|
|
5598
5765
|
try {
|
|
@@ -5660,7 +5827,7 @@ function remoteMainHash(resolvedPath) {
|
|
|
5660
5827
|
}
|
|
5661
5828
|
function checkStaleRemoteMain(resolvedPath) {
|
|
5662
5829
|
if (resolvedPath === void 0) return void 0;
|
|
5663
|
-
if (!
|
|
5830
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) return void 0;
|
|
5664
5831
|
const localOrigin = gitRefHash(resolvedPath, "origin/main");
|
|
5665
5832
|
if (!localOrigin) return void 0;
|
|
5666
5833
|
const remoteMain = remoteMainHash(resolvedPath);
|
|
@@ -5672,11 +5839,101 @@ function checkStaleRemoteMain(resolvedPath) {
|
|
|
5672
5839
|
`Remote main differs from local origin/main (${remoteMain.slice(0, 8)} != ${localOrigin.slice(0, 8)}) \u2014 run git fetch before trusting behind count`
|
|
5673
5840
|
);
|
|
5674
5841
|
}
|
|
5842
|
+
function checkVaultLocalGit(resolvedPath) {
|
|
5843
|
+
if (resolvedPath === void 0) {
|
|
5844
|
+
return check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5845
|
+
}
|
|
5846
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5847
|
+
return check("warn", "vault_local_git", "Vault local git", "Not a git repository - sync features unavailable");
|
|
5848
|
+
}
|
|
5849
|
+
try {
|
|
5850
|
+
execSync2("git rev-parse --git-dir", {
|
|
5851
|
+
cwd: resolvedPath,
|
|
5852
|
+
encoding: "utf8",
|
|
5853
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5854
|
+
timeout: 2e3
|
|
5855
|
+
});
|
|
5856
|
+
return check("pass", "vault_local_git", "Vault local git", "Git metadata readable");
|
|
5857
|
+
} catch {
|
|
5858
|
+
return check("error", "vault_local_git", "Vault local git", "Git metadata unreadable \u2014 local vault may be corrupt");
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
function checkVaultGithubRemote(resolvedPath, exec) {
|
|
5862
|
+
if (resolvedPath === void 0) {
|
|
5863
|
+
return check("pass", "vault_github_remote", "Vault GitHub remote", "No vault path \u2014 check skipped");
|
|
5864
|
+
}
|
|
5865
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5866
|
+
return check("pass", "vault_github_remote", "Vault GitHub remote", "No git repo \u2014 check skipped");
|
|
5867
|
+
}
|
|
5868
|
+
const state = probeGithubReachability(resolvedPath, exec);
|
|
5869
|
+
if (state === "ok") {
|
|
5870
|
+
return check("pass", "vault_github_remote", "Vault GitHub remote", "git ls-remote origin main succeeded");
|
|
5871
|
+
}
|
|
5872
|
+
if (state === "unreachable") {
|
|
5873
|
+
return check("warn", "vault_github_remote", "Vault GitHub remote", "GitHub unreachable (ls-remote failed) \u2014 local vault still usable");
|
|
5874
|
+
}
|
|
5875
|
+
return check("pass", "vault_github_remote", "Vault GitHub remote", "No origin remote \u2014 network probe skipped");
|
|
5876
|
+
}
|
|
5877
|
+
function checkVaultS3Remote(home, exec) {
|
|
5878
|
+
const remote = readWikiS3RemoteConfigured(home);
|
|
5879
|
+
if (!remote) {
|
|
5880
|
+
return check("pass", "vault_s3_remote", "Vault S3 remote", "S3 remote not configured \u2014 check skipped");
|
|
5881
|
+
}
|
|
5882
|
+
const state = probeS3Reachability(remote, exec);
|
|
5883
|
+
if (state === "ok") {
|
|
5884
|
+
return check("pass", "vault_s3_remote", "Vault S3 remote", `rclone lsf ${remote} succeeded`);
|
|
5885
|
+
}
|
|
5886
|
+
if (state === "unreachable") {
|
|
5887
|
+
return check("warn", "vault_s3_remote", "Vault S3 remote", `S3 remote unreachable (${remote}) \u2014 local/GitHub work may continue`);
|
|
5888
|
+
}
|
|
5889
|
+
return check("pass", "vault_s3_remote", "Vault S3 remote", "S3 remote not configured \u2014 check skipped");
|
|
5890
|
+
}
|
|
5891
|
+
function checkVaultSnapshotterReachable(fleetLoad, checkSnapshotter, exec) {
|
|
5892
|
+
if (!checkSnapshotter) {
|
|
5893
|
+
return check("pass", "vault_snapshotter_reachable", "Vault snapshotter host", "Snapshotter SSH probe not requested \u2014 check skipped");
|
|
5894
|
+
}
|
|
5895
|
+
const alias = snapshotterAliasForLocalHost(fleetLoad);
|
|
5896
|
+
if (!alias) {
|
|
5897
|
+
return check("pass", "vault_snapshotter_reachable", "Vault snapshotter host", "No declared SSH alias from this host \u2014 check skipped");
|
|
5898
|
+
}
|
|
5899
|
+
const state = probeSnapshotterSsh(alias, exec);
|
|
5900
|
+
if (state === "ok") {
|
|
5901
|
+
return check("pass", "vault_snapshotter_reachable", "Vault snapshotter host", `SSH reachable via ${alias}`);
|
|
5902
|
+
}
|
|
5903
|
+
return check("warn", "vault_snapshotter_reachable", "Vault snapshotter host", `Snapshotter unreachable via ${alias} \u2014 not a local vault corruption signal`);
|
|
5904
|
+
}
|
|
5905
|
+
function checkVaultPromotionLag(resolvedPath) {
|
|
5906
|
+
if (resolvedPath === void 0) {
|
|
5907
|
+
return check("pass", "vault_promotion_lag", "Vault promotion lag", "No vault path \u2014 check skipped");
|
|
5908
|
+
}
|
|
5909
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5910
|
+
return check("pass", "vault_promotion_lag", "Vault promotion lag", "No git repo \u2014 check skipped");
|
|
5911
|
+
}
|
|
5912
|
+
try {
|
|
5913
|
+
const out = execSync2("git log -1 --format=%ct origin/main", {
|
|
5914
|
+
cwd: resolvedPath,
|
|
5915
|
+
encoding: "utf8",
|
|
5916
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5917
|
+
timeout: 2e3
|
|
5918
|
+
}).trim();
|
|
5919
|
+
const ts = parseInt(out, 10);
|
|
5920
|
+
if (!Number.isFinite(ts) || ts <= 0) {
|
|
5921
|
+
return check("pass", "vault_promotion_lag", "Vault promotion lag", "origin/main timestamp unavailable \u2014 check skipped");
|
|
5922
|
+
}
|
|
5923
|
+
const ageHours = Math.floor((Date.now() / 1e3 - ts) / 3600);
|
|
5924
|
+
if (ageHours > 48) {
|
|
5925
|
+
return check("warn", "vault_promotion_lag", "Vault promotion lag", `Local origin/main snapshot is ${ageHours}h old \u2014 verify snapshotter/GitHub when online`);
|
|
5926
|
+
}
|
|
5927
|
+
return check("pass", "vault_promotion_lag", "Vault promotion lag", `origin/main age ${ageHours}h`);
|
|
5928
|
+
} catch {
|
|
5929
|
+
return check("pass", "vault_promotion_lag", "Vault promotion lag", "Could not read origin/main \u2014 check skipped");
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5675
5932
|
function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix, zeroDetail) {
|
|
5676
5933
|
if (resolvedPath === void 0) {
|
|
5677
5934
|
return check("pass", id, label, "No vault path \u2014 check skipped");
|
|
5678
5935
|
}
|
|
5679
|
-
if (!
|
|
5936
|
+
if (!existsSync12(join23(resolvedPath, ".git"))) {
|
|
5680
5937
|
return check("pass", id, label, "No git repo \u2014 check skipped");
|
|
5681
5938
|
}
|
|
5682
5939
|
if (!hasOriginMain(resolvedPath)) {
|
|
@@ -5704,7 +5961,7 @@ function checkSatelliteLastRun(vaultPath, satelliteExpected) {
|
|
|
5704
5961
|
return check("pass", "satellite_job_last_run", "Satellite job last run", "No vault path \u2014 check skipped");
|
|
5705
5962
|
}
|
|
5706
5963
|
const latestPath = satelliteLatestRunPath(vaultPath);
|
|
5707
|
-
if (!
|
|
5964
|
+
if (!existsSync12(latestPath)) {
|
|
5708
5965
|
return check("pass", "satellite_job_last_run", "Satellite job last run", "No latest-run.json \u2014 satellite has not run yet");
|
|
5709
5966
|
}
|
|
5710
5967
|
try {
|
|
@@ -5792,11 +6049,11 @@ async function checkFleetIdentity(input) {
|
|
|
5792
6049
|
}
|
|
5793
6050
|
function pullLogPaths(home) {
|
|
5794
6051
|
const paths = platform2() === "darwin" ? [
|
|
5795
|
-
|
|
5796
|
-
|
|
6052
|
+
join23(home, "Library", "Logs", "wiki-pull.log"),
|
|
6053
|
+
join23(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
|
|
5797
6054
|
] : [
|
|
5798
|
-
|
|
5799
|
-
|
|
6055
|
+
join23(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
|
|
6056
|
+
join23(home, "Library", "Logs", "wiki-pull.log")
|
|
5800
6057
|
];
|
|
5801
6058
|
return [...new Set(paths)];
|
|
5802
6059
|
}
|
|
@@ -5808,12 +6065,12 @@ function isRecentLogLine(line, nowMs) {
|
|
|
5808
6065
|
return nowMs - ts <= 24 * 60 * 60 * 1e3;
|
|
5809
6066
|
}
|
|
5810
6067
|
function checkVaultGitPullFailures(home) {
|
|
5811
|
-
const path = pullLogPaths(home).find((p) =>
|
|
6068
|
+
const path = pullLogPaths(home).find((p) => existsSync12(p));
|
|
5812
6069
|
if (!path) {
|
|
5813
6070
|
return check("pass", "vault_git_pull_failures", "Vault pull failures", "No wiki-pull.log found \u2014 check skipped");
|
|
5814
6071
|
}
|
|
5815
6072
|
try {
|
|
5816
|
-
const lines =
|
|
6073
|
+
const lines = readFileSync9(path, "utf8").split(/\r?\n/).filter(Boolean);
|
|
5817
6074
|
const now = Date.now();
|
|
5818
6075
|
const failures = lines.filter(
|
|
5819
6076
|
(line) => isRecentLogLine(line, now) && /(pre-push pull failed|FAIL .*pull|FAIL .*rebase|cannot pull with rebase|unstaged changes)/i.test(line)
|
|
@@ -5836,8 +6093,8 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
5836
6093
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
5837
6094
|
}
|
|
5838
6095
|
const mountPoint = fuse.mountPoint;
|
|
5839
|
-
const conceptsDir =
|
|
5840
|
-
if (!
|
|
6096
|
+
const conceptsDir = join23(resolvedPath, "concepts");
|
|
6097
|
+
if (!existsSync12(conceptsDir)) {
|
|
5841
6098
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
5842
6099
|
}
|
|
5843
6100
|
const start = Date.now();
|
|
@@ -6019,8 +6276,8 @@ function checkWriteTest(resolvedPath) {
|
|
|
6019
6276
|
if (!fuse) {
|
|
6020
6277
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
6021
6278
|
}
|
|
6022
|
-
const conceptsDir =
|
|
6023
|
-
if (!
|
|
6279
|
+
const conceptsDir = join23(resolvedPath, "concepts");
|
|
6280
|
+
if (!existsSync12(conceptsDir)) {
|
|
6024
6281
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
6025
6282
|
}
|
|
6026
6283
|
const result = writeTest(conceptsDir);
|
|
@@ -6106,7 +6363,7 @@ function checkVfsCacheHealth(resolvedPath) {
|
|
|
6106
6363
|
}
|
|
6107
6364
|
function readVaultSyncConfig(home) {
|
|
6108
6365
|
try {
|
|
6109
|
-
const content =
|
|
6366
|
+
const content = readFileSync9(join23(home, ".skillwiki", ".env"), "utf8");
|
|
6110
6367
|
let installed = false;
|
|
6111
6368
|
let role;
|
|
6112
6369
|
let serviceScope;
|
|
@@ -6135,7 +6392,7 @@ function readVaultSyncConfig(home) {
|
|
|
6135
6392
|
}
|
|
6136
6393
|
function readKeyFromEnvFile(path, keys) {
|
|
6137
6394
|
try {
|
|
6138
|
-
const content =
|
|
6395
|
+
const content = readFileSync9(path, "utf8");
|
|
6139
6396
|
for (const line of content.split(/\r?\n/)) {
|
|
6140
6397
|
const trimmed = line.trim();
|
|
6141
6398
|
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
|
|
@@ -6157,7 +6414,7 @@ function resolveSnapshotGitWorktree(config) {
|
|
|
6157
6414
|
if (fromProfile) return fromProfile;
|
|
6158
6415
|
}
|
|
6159
6416
|
const defaultPath = "/root/wiki-git";
|
|
6160
|
-
return
|
|
6417
|
+
return existsSync12(defaultPath) ? defaultPath : void 0;
|
|
6161
6418
|
}
|
|
6162
6419
|
function vaultSyncChecks(input) {
|
|
6163
6420
|
const os = input.os ?? platform2();
|
|
@@ -6174,16 +6431,16 @@ function vaultSyncChecks(input) {
|
|
|
6174
6431
|
];
|
|
6175
6432
|
}
|
|
6176
6433
|
const isMac = os === "darwin";
|
|
6177
|
-
const logDir = input.logDir ?? (isMac ?
|
|
6178
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
6179
|
-
const filterPath = input.filterPath ??
|
|
6180
|
-
const packagedSnapshotPath =
|
|
6434
|
+
const logDir = input.logDir ?? (isMac ? join23(home, "Library", "Logs") : join23(home, ".local", "state", "vault-sync", "log"));
|
|
6435
|
+
const shareDir = input.shareDir ?? (isMac ? join23(home, "Library", "Application Support", "vault-sync", "bin") : join23(home, ".local", "share", "vault-sync", "bin"));
|
|
6436
|
+
const filterPath = input.filterPath ?? join23(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
6437
|
+
const packagedSnapshotPath = join23(shareDir, "wiki-snapshot.sh");
|
|
6181
6438
|
const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
6182
|
-
const snapshotPath = input.snapshotScriptPath ?? (
|
|
6439
|
+
const snapshotPath = input.snapshotScriptPath ?? (existsSync12(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
|
|
6183
6440
|
function snapshotLastStatusCheck() {
|
|
6184
|
-
const snapshotLog =
|
|
6441
|
+
const snapshotLog = join23(logDir, "wiki-snapshot.log");
|
|
6185
6442
|
try {
|
|
6186
|
-
const logContent =
|
|
6443
|
+
const logContent = readFileSync9(snapshotLog, "utf8");
|
|
6187
6444
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
6188
6445
|
if (lines.length === 0) {
|
|
6189
6446
|
return check(
|
|
@@ -6228,14 +6485,14 @@ function vaultSyncChecks(input) {
|
|
|
6228
6485
|
}
|
|
6229
6486
|
}
|
|
6230
6487
|
if (input.vaultSyncRole === "snapshotter") {
|
|
6231
|
-
const c12 =
|
|
6488
|
+
const c12 = existsSync12(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
6489
|
const serviceScope = input.vaultSyncServiceScope ?? "user";
|
|
6233
|
-
const userTimerPath =
|
|
6490
|
+
const userTimerPath = join23(home, ".config", "systemd", "user", "wiki-snapshot.timer");
|
|
6234
6491
|
const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
|
|
6235
6492
|
let c22;
|
|
6236
|
-
if (serviceScope === "user" &&
|
|
6493
|
+
if (serviceScope === "user" && existsSync12(userTimerPath)) {
|
|
6237
6494
|
c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${userTimerPath}`);
|
|
6238
|
-
} else if (serviceScope === "system" &&
|
|
6495
|
+
} else if (serviceScope === "system" && existsSync12(systemTimerPath)) {
|
|
6239
6496
|
c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${systemTimerPath}`);
|
|
6240
6497
|
} else if (os !== "linux") {
|
|
6241
6498
|
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 +6524,7 @@ function vaultSyncChecks(input) {
|
|
|
6267
6524
|
);
|
|
6268
6525
|
let c52;
|
|
6269
6526
|
try {
|
|
6270
|
-
if (!
|
|
6527
|
+
if (!existsSync12(snapshotPath)) {
|
|
6271
6528
|
c52 = check(
|
|
6272
6529
|
"error",
|
|
6273
6530
|
"vault_sync_snapshot_guard",
|
|
@@ -6275,7 +6532,7 @@ function vaultSyncChecks(input) {
|
|
|
6275
6532
|
`Snapshot script not found at ${snapshotPath}`
|
|
6276
6533
|
);
|
|
6277
6534
|
} else {
|
|
6278
|
-
const content =
|
|
6535
|
+
const content = readFileSync9(snapshotPath, "utf8");
|
|
6279
6536
|
if (!content.includes("--max-delete")) {
|
|
6280
6537
|
c52 = check(
|
|
6281
6538
|
"error",
|
|
@@ -6302,8 +6559,8 @@ function vaultSyncChecks(input) {
|
|
|
6302
6559
|
}
|
|
6303
6560
|
return [c12, c22, c32, cFetch2, c42, c52];
|
|
6304
6561
|
}
|
|
6305
|
-
const pushScriptPath =
|
|
6306
|
-
const c1 =
|
|
6562
|
+
const pushScriptPath = join23(shareDir, "wiki-push.sh");
|
|
6563
|
+
const c1 = existsSync12(pushScriptPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${pushScriptPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Script not found at ${pushScriptPath} \u2014 run vault-sync-install`);
|
|
6307
6564
|
let c2;
|
|
6308
6565
|
try {
|
|
6309
6566
|
if (isMac) {
|
|
@@ -6354,10 +6611,10 @@ function vaultSyncChecks(input) {
|
|
|
6354
6611
|
"Scheduler check failed \u2014 run vault-sync-install"
|
|
6355
6612
|
);
|
|
6356
6613
|
}
|
|
6357
|
-
const logFile =
|
|
6614
|
+
const logFile = join23(logDir, "wiki-push.log");
|
|
6358
6615
|
let c3;
|
|
6359
6616
|
try {
|
|
6360
|
-
const logContent =
|
|
6617
|
+
const logContent = readFileSync9(logFile, "utf8");
|
|
6361
6618
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
6362
6619
|
if (lines.length === 0) {
|
|
6363
6620
|
c3 = check(
|
|
@@ -6413,7 +6670,7 @@ function vaultSyncChecks(input) {
|
|
|
6413
6670
|
}
|
|
6414
6671
|
}
|
|
6415
6672
|
} catch {
|
|
6416
|
-
c3 =
|
|
6673
|
+
c3 = existsSync12(logDir) ? check(
|
|
6417
6674
|
"warn",
|
|
6418
6675
|
"vault_sync_last_push_age",
|
|
6419
6676
|
"Vault sync last push recency",
|
|
@@ -6425,10 +6682,10 @@ function vaultSyncChecks(input) {
|
|
|
6425
6682
|
`Log directory not found at ${logDir}`
|
|
6426
6683
|
);
|
|
6427
6684
|
}
|
|
6428
|
-
const fetchLogFile =
|
|
6685
|
+
const fetchLogFile = join23(logDir, "wiki-fetch.log");
|
|
6429
6686
|
let cFetch;
|
|
6430
6687
|
try {
|
|
6431
|
-
const logContent =
|
|
6688
|
+
const logContent = readFileSync9(fetchLogFile, "utf8");
|
|
6432
6689
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
6433
6690
|
if (lines.length === 0) {
|
|
6434
6691
|
cFetch = check(
|
|
@@ -6472,7 +6729,7 @@ function vaultSyncChecks(input) {
|
|
|
6472
6729
|
}
|
|
6473
6730
|
let c4;
|
|
6474
6731
|
try {
|
|
6475
|
-
if (!
|
|
6732
|
+
if (!existsSync12(filterPath)) {
|
|
6476
6733
|
c4 = check(
|
|
6477
6734
|
"error",
|
|
6478
6735
|
"vault_sync_filter_present",
|
|
@@ -6480,7 +6737,7 @@ function vaultSyncChecks(input) {
|
|
|
6480
6737
|
`Filter file not found at ${filterPath}`
|
|
6481
6738
|
);
|
|
6482
6739
|
} else {
|
|
6483
|
-
const content =
|
|
6740
|
+
const content = readFileSync9(filterPath, "utf8");
|
|
6484
6741
|
const requiredExcludes = [
|
|
6485
6742
|
"remotely-save/data.json",
|
|
6486
6743
|
".skillwiki/sync.lock",
|
|
@@ -6523,7 +6780,7 @@ function vaultSyncChecks(input) {
|
|
|
6523
6780
|
);
|
|
6524
6781
|
} else {
|
|
6525
6782
|
try {
|
|
6526
|
-
if (!
|
|
6783
|
+
if (!existsSync12(snapshotPath)) {
|
|
6527
6784
|
c5 = check(
|
|
6528
6785
|
"error",
|
|
6529
6786
|
"vault_sync_snapshot_guard",
|
|
@@ -6531,7 +6788,7 @@ function vaultSyncChecks(input) {
|
|
|
6531
6788
|
`Snapshot script not found at ${snapshotPath}`
|
|
6532
6789
|
);
|
|
6533
6790
|
} else {
|
|
6534
|
-
const content =
|
|
6791
|
+
const content = readFileSync9(snapshotPath, "utf8");
|
|
6535
6792
|
if (!content.includes("--max-delete")) {
|
|
6536
6793
|
c5 = check(
|
|
6537
6794
|
"error",
|
|
@@ -6563,33 +6820,33 @@ function findSkillMd(dir) {
|
|
|
6563
6820
|
const results = [];
|
|
6564
6821
|
let entries;
|
|
6565
6822
|
try {
|
|
6566
|
-
entries =
|
|
6823
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
6567
6824
|
} catch {
|
|
6568
6825
|
return results;
|
|
6569
6826
|
}
|
|
6570
6827
|
for (const entry of entries) {
|
|
6571
6828
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
6572
|
-
results.push(
|
|
6829
|
+
results.push(join23(dir, entry.name));
|
|
6573
6830
|
} else if (entry.isDirectory()) {
|
|
6574
|
-
results.push(...findSkillMd(
|
|
6831
|
+
results.push(...findSkillMd(join23(dir, entry.name)));
|
|
6575
6832
|
}
|
|
6576
6833
|
}
|
|
6577
6834
|
return results;
|
|
6578
6835
|
}
|
|
6579
6836
|
function findInstalledSkillMd(dir) {
|
|
6580
|
-
const directSkills = findSkillNames(dir).map((name) =>
|
|
6837
|
+
const directSkills = findSkillNames(dir).map((name) => join23(dir, name, "SKILL.md"));
|
|
6581
6838
|
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
6582
6839
|
}
|
|
6583
6840
|
function findSkillNames(dir) {
|
|
6584
6841
|
const results = [];
|
|
6585
6842
|
let entries;
|
|
6586
6843
|
try {
|
|
6587
|
-
entries =
|
|
6844
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
6588
6845
|
} catch {
|
|
6589
6846
|
return results;
|
|
6590
6847
|
}
|
|
6591
6848
|
for (const entry of entries) {
|
|
6592
|
-
if (entry.isDirectory() &&
|
|
6849
|
+
if (entry.isDirectory() && existsSync12(join23(dir, entry.name, "SKILL.md"))) {
|
|
6593
6850
|
results.push(entry.name);
|
|
6594
6851
|
}
|
|
6595
6852
|
}
|
|
@@ -6633,7 +6890,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
6633
6890
|
}
|
|
6634
6891
|
let logLines = 0;
|
|
6635
6892
|
try {
|
|
6636
|
-
logLines =
|
|
6893
|
+
logLines = readFileSync9(join23(resolvedPath, "log.md"), "utf8").split("\n").length;
|
|
6637
6894
|
} catch {
|
|
6638
6895
|
}
|
|
6639
6896
|
return [
|
|
@@ -6690,7 +6947,13 @@ async function runDoctor(input) {
|
|
|
6690
6947
|
checks.push(checkVaultGitAhead(gitCheckPath));
|
|
6691
6948
|
checks.push(checkVaultGitBehind(gitCheckPath));
|
|
6692
6949
|
checks.push(checkVaultGitPullFailures(input.home));
|
|
6950
|
+
checks.push(checkVaultLocalGit(gitCheckPath));
|
|
6951
|
+
checks.push(checkVaultGithubRemote(gitCheckPath, input.execProbe));
|
|
6952
|
+
checks.push(checkVaultS3Remote(input.home, input.execProbe));
|
|
6953
|
+
checks.push(checkVaultSnapshotterReachable(fleetLoad, input.checkSnapshotter, input.execProbe));
|
|
6954
|
+
checks.push(checkVaultPromotionLag(gitCheckPath));
|
|
6693
6955
|
checks.push(checkDotStoreClean(resolvedPath));
|
|
6956
|
+
checks.push(checkVaultConflictMarkers(resolvedPath));
|
|
6694
6957
|
checks.push(checkS3MountPerf(resolvedPath));
|
|
6695
6958
|
checks.push(checkS3MountFreshness(resolvedPath));
|
|
6696
6959
|
checks.push(checkRcloneFlagAudit(resolvedPath));
|
|
@@ -6735,7 +6998,7 @@ async function runDoctor(input) {
|
|
|
6735
6998
|
}
|
|
6736
6999
|
|
|
6737
7000
|
// src/utils/package-info.ts
|
|
6738
|
-
import { readFileSync as
|
|
7001
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6739
7002
|
function packageJsonCandidateUrls(baseUrl = import.meta.url) {
|
|
6740
7003
|
return [
|
|
6741
7004
|
new URL("../package.json", baseUrl),
|
|
@@ -6745,7 +7008,7 @@ function packageJsonCandidateUrls(baseUrl = import.meta.url) {
|
|
|
6745
7008
|
function readCliPackageJson(baseUrl = import.meta.url) {
|
|
6746
7009
|
for (const url of packageJsonCandidateUrls(baseUrl)) {
|
|
6747
7010
|
try {
|
|
6748
|
-
const pkg = JSON.parse(
|
|
7011
|
+
const pkg = JSON.parse(readFileSync10(url, "utf8"));
|
|
6749
7012
|
if (typeof pkg.version === "string") {
|
|
6750
7013
|
return { ...pkg, version: pkg.version };
|
|
6751
7014
|
}
|
|
@@ -6757,7 +7020,7 @@ function readCliPackageJson(baseUrl = import.meta.url) {
|
|
|
6757
7020
|
|
|
6758
7021
|
// src/commands/project-index.ts
|
|
6759
7022
|
import { readdir as readdir4, readFile as readFile16, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
6760
|
-
import { join as
|
|
7023
|
+
import { join as join24, dirname as dirname7, basename as basename2 } from "path";
|
|
6761
7024
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
6762
7025
|
var PROJECT_LOCAL_DIRS = ["requirements", "work", "architecture", "history"];
|
|
6763
7026
|
async function scanMarkdownTree(rootAbs, rootRel) {
|
|
@@ -6769,7 +7032,7 @@ async function scanMarkdownTree(rootAbs, rootRel) {
|
|
|
6769
7032
|
return found;
|
|
6770
7033
|
}
|
|
6771
7034
|
for (const entry of entries) {
|
|
6772
|
-
const abs =
|
|
7035
|
+
const abs = join24(rootAbs, entry.name);
|
|
6773
7036
|
const rel = `${rootRel}/${entry.name}`;
|
|
6774
7037
|
if (entry.isDirectory()) {
|
|
6775
7038
|
found.push(...await scanMarkdownTree(abs, rel));
|
|
@@ -6799,7 +7062,7 @@ function projectLocalType(slug, page, data) {
|
|
|
6799
7062
|
}
|
|
6800
7063
|
async function runProjectIndex(input) {
|
|
6801
7064
|
const slug = input.slug;
|
|
6802
|
-
const projectDir =
|
|
7065
|
+
const projectDir = join24(input.vault, "projects", slug);
|
|
6803
7066
|
try {
|
|
6804
7067
|
await readdir4(projectDir);
|
|
6805
7068
|
} catch {
|
|
@@ -6810,12 +7073,12 @@ async function runProjectIndex(input) {
|
|
|
6810
7073
|
}
|
|
6811
7074
|
const wikilinkPattern = `[[${slug}]]`;
|
|
6812
7075
|
const entries = [];
|
|
6813
|
-
const compoundDir =
|
|
7076
|
+
const compoundDir = join24(input.vault, "projects", slug, "compound");
|
|
6814
7077
|
try {
|
|
6815
7078
|
const compoundFiles = await readdir4(compoundDir, { withFileTypes: true });
|
|
6816
7079
|
for (const entry of compoundFiles) {
|
|
6817
7080
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6818
|
-
const filePath =
|
|
7081
|
+
const filePath = join24(compoundDir, entry.name);
|
|
6819
7082
|
let text;
|
|
6820
7083
|
try {
|
|
6821
7084
|
text = await readFile16(filePath, "utf8");
|
|
@@ -6835,13 +7098,13 @@ async function runProjectIndex(input) {
|
|
|
6835
7098
|
for (const dir of LAYER2_DIRS) {
|
|
6836
7099
|
let files;
|
|
6837
7100
|
try {
|
|
6838
|
-
files = await readdir4(
|
|
7101
|
+
files = await readdir4(join24(input.vault, dir), { withFileTypes: true });
|
|
6839
7102
|
} catch {
|
|
6840
7103
|
continue;
|
|
6841
7104
|
}
|
|
6842
7105
|
for (const entry of files) {
|
|
6843
7106
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6844
|
-
const filePath =
|
|
7107
|
+
const filePath = join24(input.vault, dir, entry.name);
|
|
6845
7108
|
let text;
|
|
6846
7109
|
try {
|
|
6847
7110
|
text = await readFile16(filePath, "utf8");
|
|
@@ -6860,11 +7123,11 @@ async function runProjectIndex(input) {
|
|
|
6860
7123
|
}
|
|
6861
7124
|
}
|
|
6862
7125
|
for (const dir of PROJECT_LOCAL_DIRS) {
|
|
6863
|
-
const rootAbs =
|
|
7126
|
+
const rootAbs = join24(projectDir, dir);
|
|
6864
7127
|
const rootRel = `projects/${slug}/${dir}`;
|
|
6865
7128
|
const pages = await scanMarkdownTree(rootAbs, rootRel);
|
|
6866
7129
|
for (const page of pages) {
|
|
6867
|
-
const filePath =
|
|
7130
|
+
const filePath = join24(input.vault, page);
|
|
6868
7131
|
let text;
|
|
6869
7132
|
try {
|
|
6870
7133
|
text = await readFile16(filePath, "utf8");
|
|
@@ -6886,7 +7149,7 @@ async function runProjectIndex(input) {
|
|
|
6886
7149
|
const tb = typeOrder[b.type] ?? 99;
|
|
6887
7150
|
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
6888
7151
|
});
|
|
6889
|
-
const indexPath =
|
|
7152
|
+
const indexPath = join24(projectDir, "knowledge.md");
|
|
6890
7153
|
let existing = false;
|
|
6891
7154
|
let stale = false;
|
|
6892
7155
|
try {
|
|
@@ -6930,7 +7193,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
6930
7193
|
}
|
|
6931
7194
|
if (input.apply) {
|
|
6932
7195
|
try {
|
|
6933
|
-
await mkdir5(
|
|
7196
|
+
await mkdir5(dirname7(indexPath), { recursive: true });
|
|
6934
7197
|
await writeFile6(indexPath, body, "utf8");
|
|
6935
7198
|
} catch (e) {
|
|
6936
7199
|
return {
|
|
@@ -6960,8 +7223,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
6960
7223
|
|
|
6961
7224
|
// src/commands/observe.ts
|
|
6962
7225
|
import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
|
|
6963
|
-
import { existsSync as
|
|
6964
|
-
import { join as
|
|
7226
|
+
import { existsSync as existsSync13, statSync as statSync2 } from "fs";
|
|
7227
|
+
import { join as join25 } from "path";
|
|
6965
7228
|
import { createHash as createHash5 } from "crypto";
|
|
6966
7229
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
6967
7230
|
function slugify(text) {
|
|
@@ -6984,13 +7247,13 @@ async function runObserve(input) {
|
|
|
6984
7247
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
6985
7248
|
};
|
|
6986
7249
|
}
|
|
6987
|
-
if (!
|
|
7250
|
+
if (!existsSync13(input.vault) || !statSync2(input.vault).isDirectory()) {
|
|
6988
7251
|
return {
|
|
6989
7252
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
6990
7253
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
6991
7254
|
};
|
|
6992
7255
|
}
|
|
6993
|
-
const transcriptsDir =
|
|
7256
|
+
const transcriptsDir = join25(input.vault, "raw", "transcripts");
|
|
6994
7257
|
try {
|
|
6995
7258
|
await mkdir6(transcriptsDir, { recursive: true });
|
|
6996
7259
|
} catch {
|
|
@@ -7002,7 +7265,7 @@ async function runObserve(input) {
|
|
|
7002
7265
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7003
7266
|
const slug = slugify(input.text);
|
|
7004
7267
|
const fileName = `${today}-observation-${slug}.md`;
|
|
7005
|
-
const filePath =
|
|
7268
|
+
const filePath = join25(transcriptsDir, fileName);
|
|
7006
7269
|
const body = `
|
|
7007
7270
|
${input.text.trim()}
|
|
7008
7271
|
`;
|
|
@@ -7044,7 +7307,7 @@ ${input.text.trim()}
|
|
|
7044
7307
|
// src/commands/memory.ts
|
|
7045
7308
|
import { createHash as createHash6 } from "crypto";
|
|
7046
7309
|
import { mkdir as mkdir7, readFile as readFile17, readdir as readdir5, stat as stat5, writeFile as writeFile8 } from "fs/promises";
|
|
7047
|
-
import { basename as basename3, extname, join as
|
|
7310
|
+
import { basename as basename3, extname, join as join26, relative as relative4, sep as sep4 } from "path";
|
|
7048
7311
|
async function runMemoryTopics(input) {
|
|
7049
7312
|
const scan = await scanVault(input.vault);
|
|
7050
7313
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
@@ -7108,8 +7371,8 @@ async function runMemoryIndex(input) {
|
|
|
7108
7371
|
}
|
|
7109
7372
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
7110
7373
|
const relCachePath = memoryCacheRelPath(input.project);
|
|
7111
|
-
const absCachePath =
|
|
7112
|
-
await mkdir7(
|
|
7374
|
+
const absCachePath = join26(input.vault, relCachePath);
|
|
7375
|
+
await mkdir7(join26(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
|
|
7113
7376
|
await writeFile8(absCachePath, `${JSON.stringify({
|
|
7114
7377
|
generated_at: generatedAt,
|
|
7115
7378
|
project: input.project,
|
|
@@ -7301,7 +7564,7 @@ async function buildMemoryIndexState(pages, project) {
|
|
|
7301
7564
|
}
|
|
7302
7565
|
async function checkMemoryIndex(vault, project, current) {
|
|
7303
7566
|
const relCachePath = memoryCacheRelPath(project);
|
|
7304
|
-
const cacheText = await readIfExists2(
|
|
7567
|
+
const cacheText = await readIfExists2(join26(vault, relCachePath));
|
|
7305
7568
|
if (!cacheText) {
|
|
7306
7569
|
return {
|
|
7307
7570
|
ok: true,
|
|
@@ -7710,7 +7973,7 @@ async function walkImportFiles(dir, out) {
|
|
|
7710
7973
|
const entries = await readdir5(dir, { withFileTypes: true });
|
|
7711
7974
|
for (const entry of entries) {
|
|
7712
7975
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
7713
|
-
const path =
|
|
7976
|
+
const path = join26(dir, entry.name);
|
|
7714
7977
|
if (entry.isDirectory()) {
|
|
7715
7978
|
await walkImportFiles(path, out);
|
|
7716
7979
|
} else if (entry.isFile() && isImportCandidate(path)) {
|
|
@@ -7777,8 +8040,8 @@ async function writeImportCapture(vault, entry, today) {
|
|
|
7777
8040
|
const content = hiddenString(entry, "__content");
|
|
7778
8041
|
const project = hiddenString(entry, "__project");
|
|
7779
8042
|
const relPath = await availableImportPath(vault, entry.proposed_path);
|
|
7780
|
-
const absPath =
|
|
7781
|
-
await mkdir7(
|
|
8043
|
+
const absPath = join26(vault, relPath);
|
|
8044
|
+
await mkdir7(join26(vault, "raw", "transcripts"), { recursive: true });
|
|
7782
8045
|
await writeFile8(absPath, renderImportCapture(entry, content, project, today), "utf8");
|
|
7783
8046
|
const validation = await runValidate({ file: absPath });
|
|
7784
8047
|
return {
|
|
@@ -7794,7 +8057,7 @@ async function availableImportPath(vault, proposed) {
|
|
|
7794
8057
|
const stem = proposed.slice(0, -ext.length);
|
|
7795
8058
|
let candidate = proposed;
|
|
7796
8059
|
let i = 2;
|
|
7797
|
-
while (await readIfExists2(
|
|
8060
|
+
while (await readIfExists2(join26(vault, candidate))) {
|
|
7798
8061
|
candidate = `${stem}-${i}${ext}`;
|
|
7799
8062
|
i++;
|
|
7800
8063
|
}
|
|
@@ -7966,10 +8229,10 @@ function memoryCacheRelPath(project) {
|
|
|
7966
8229
|
}
|
|
7967
8230
|
async function readMemoryCache(vault, project) {
|
|
7968
8231
|
if (project) {
|
|
7969
|
-
const projectCache = await readIfExists2(
|
|
8232
|
+
const projectCache = await readIfExists2(join26(vault, memoryCacheRelPath(project)));
|
|
7970
8233
|
if (projectCache) return projectCache;
|
|
7971
8234
|
}
|
|
7972
|
-
return readIfExists2(
|
|
8235
|
+
return readIfExists2(join26(vault, ".skillwiki", "memory-topics.json"));
|
|
7973
8236
|
}
|
|
7974
8237
|
function dedupePages(pages) {
|
|
7975
8238
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8056,7 +8319,7 @@ function slugify2(value) {
|
|
|
8056
8319
|
|
|
8057
8320
|
// src/commands/query.ts
|
|
8058
8321
|
import { readFile as readFile18, stat as stat6 } from "fs/promises";
|
|
8059
|
-
import { join as
|
|
8322
|
+
import { join as join27 } from "path";
|
|
8060
8323
|
var W_KEYWORD = 2;
|
|
8061
8324
|
var W_SOURCE_OVERLAP = 4;
|
|
8062
8325
|
var W_WIKILINK = 3;
|
|
@@ -8177,7 +8440,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
8177
8440
|
return score;
|
|
8178
8441
|
}
|
|
8179
8442
|
async function loadOrBuildGraph(vault) {
|
|
8180
|
-
const graphPath =
|
|
8443
|
+
const graphPath = join27(vault, ".skillwiki", "graph.json");
|
|
8181
8444
|
let needsBuild = false;
|
|
8182
8445
|
try {
|
|
8183
8446
|
const fileStat = await stat6(graphPath);
|
|
@@ -8206,7 +8469,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
8206
8469
|
import { z as z2 } from "zod";
|
|
8207
8470
|
|
|
8208
8471
|
// src/mcp/vault-resolve.ts
|
|
8209
|
-
import { join as
|
|
8472
|
+
import { join as join28, resolve as resolve7 } from "path";
|
|
8210
8473
|
|
|
8211
8474
|
// src/mcp/allowlist.ts
|
|
8212
8475
|
import { resolve as resolve6, sep as sep5 } from "path";
|
|
@@ -8268,7 +8531,7 @@ async function resolveMcpVault(input) {
|
|
|
8268
8531
|
return ok({ vault: vaultPath, source });
|
|
8269
8532
|
}
|
|
8270
8533
|
function defaultGraphOut(vault) {
|
|
8271
|
-
return
|
|
8534
|
+
return join28(vault, ".skillwiki", "graph.json");
|
|
8272
8535
|
}
|
|
8273
8536
|
|
|
8274
8537
|
// src/mcp/result-format.ts
|
|
@@ -8283,9 +8546,9 @@ function formatToolResult(payload) {
|
|
|
8283
8546
|
}
|
|
8284
8547
|
|
|
8285
8548
|
// src/mcp/audit-log.ts
|
|
8286
|
-
import { appendFileSync, mkdirSync as
|
|
8549
|
+
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
8287
8550
|
import { homedir } from "os";
|
|
8288
|
-
import { join as
|
|
8551
|
+
import { join as join29 } from "path";
|
|
8289
8552
|
function auditEnabled() {
|
|
8290
8553
|
const v = process.env.SKILLWIKI_MCP_AUDIT;
|
|
8291
8554
|
if (v === "0" || v === "false") return false;
|
|
@@ -8297,7 +8560,7 @@ function auditSink() {
|
|
|
8297
8560
|
function auditFilePath() {
|
|
8298
8561
|
const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
|
|
8299
8562
|
if (custom && custom.length > 0) return custom;
|
|
8300
|
-
return
|
|
8563
|
+
return join29(homedir(), ".skillwiki", "mcp-audit.jsonl");
|
|
8301
8564
|
}
|
|
8302
8565
|
function auditMcpToolCall(entry) {
|
|
8303
8566
|
if (!auditEnabled()) return;
|
|
@@ -8307,7 +8570,7 @@ function auditMcpToolCall(entry) {
|
|
|
8307
8570
|
return;
|
|
8308
8571
|
}
|
|
8309
8572
|
const path = auditFilePath();
|
|
8310
|
-
|
|
8573
|
+
mkdirSync3(join29(path, ".."), { recursive: true });
|
|
8311
8574
|
appendFileSync(path, line, "utf8");
|
|
8312
8575
|
}
|
|
8313
8576
|
async function runMcpToolHandler(tool, input, fn) {
|
|
@@ -8528,7 +8791,7 @@ function registerMcpMutatingTools(server) {
|
|
|
8528
8791
|
|
|
8529
8792
|
// src/mcp/resources.ts
|
|
8530
8793
|
import { readFile as readFile20 } from "fs/promises";
|
|
8531
|
-
import { join as
|
|
8794
|
+
import { join as join31 } from "path";
|
|
8532
8795
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8533
8796
|
|
|
8534
8797
|
// src/mcp/lint-bucket.ts
|
|
@@ -8657,8 +8920,8 @@ async function fetchQueryPreview(input) {
|
|
|
8657
8920
|
|
|
8658
8921
|
// src/mcp/graph-html.ts
|
|
8659
8922
|
import { readFile as readFile19 } from "fs/promises";
|
|
8660
|
-
import { join as
|
|
8661
|
-
import { existsSync as
|
|
8923
|
+
import { join as join30 } from "path";
|
|
8924
|
+
import { existsSync as existsSync14 } from "fs";
|
|
8662
8925
|
var TYPE_COLORS = {
|
|
8663
8926
|
entities: "#e74c3c",
|
|
8664
8927
|
concepts: "#27ae60",
|
|
@@ -8726,9 +8989,9 @@ ${nodeSvg}
|
|
|
8726
8989
|
return { html, node_count: nodes.length, edge_count: edges.length, truncated };
|
|
8727
8990
|
}
|
|
8728
8991
|
async function fetchGraphHtmlReport(input) {
|
|
8729
|
-
const graphPath = input.graphPath ??
|
|
8992
|
+
const graphPath = input.graphPath ?? join30(input.vault, ".skillwiki", "graph.json");
|
|
8730
8993
|
const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
|
|
8731
|
-
if (!
|
|
8994
|
+
if (!existsSync14(graphPath)) {
|
|
8732
8995
|
return {
|
|
8733
8996
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
8734
8997
|
result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
|
|
@@ -8800,7 +9063,7 @@ async function fetchStaleSummary(input) {
|
|
|
8800
9063
|
|
|
8801
9064
|
// src/mcp/resources.ts
|
|
8802
9065
|
async function readVaultFile(vault, rel) {
|
|
8803
|
-
return readFile20(
|
|
9066
|
+
return readFile20(join31(vault, rel), "utf8");
|
|
8804
9067
|
}
|
|
8805
9068
|
async function tailLines(text, lines) {
|
|
8806
9069
|
const parts = text.split(/\r?\n/);
|
|
@@ -8886,7 +9149,7 @@ function registerMcpResources(server) {
|
|
|
8886
9149
|
if (!v.ok) {
|
|
8887
9150
|
return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
|
|
8888
9151
|
}
|
|
8889
|
-
const path =
|
|
9152
|
+
const path = join31(v.data.vault, ".skillwiki", "graph.json");
|
|
8890
9153
|
try {
|
|
8891
9154
|
const raw = await readFile20(path, "utf8");
|
|
8892
9155
|
const graph = JSON.parse(raw);
|
|
@@ -9249,12 +9512,13 @@ export {
|
|
|
9249
9512
|
runConfigSet,
|
|
9250
9513
|
runConfigList,
|
|
9251
9514
|
runConfigPath,
|
|
9252
|
-
|
|
9253
|
-
|
|
9515
|
+
buildDegradedReasons,
|
|
9516
|
+
probeRemoteHealth,
|
|
9254
9517
|
FLEET_REL_PATH,
|
|
9255
9518
|
runFleetValidate,
|
|
9256
9519
|
runFleetContext,
|
|
9257
9520
|
loadFleetManifestAndHost,
|
|
9521
|
+
snapshotterAliasForLocalHost,
|
|
9258
9522
|
loadFleetManifest,
|
|
9259
9523
|
resolveFleetHostId,
|
|
9260
9524
|
SATELLITE_STALE_MS,
|