skillwiki 0.9.28 → 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.
- package/dist/{chunk-IP3RKVMF.js → chunk-JENSKJP6.js} +484 -149
- package/dist/cli.js +416 -94
- package/dist/skillwiki-mcp.js +1 -1
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -2
- package/skills/package.json +1 -1
- package/skills/.mcp.json +0 -7
|
@@ -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
|
|
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
|
|
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
|
|
4246
|
-
import { join as
|
|
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
|
|
4372
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4631
|
-
if (
|
|
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 =
|
|
4636
|
-
if (
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
4969
|
+
if (!existsSync10(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
4726
4970
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
4727
|
-
if (!
|
|
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 ?
|
|
4736
|
-
if (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 =
|
|
4750
|
-
if (
|
|
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 =
|
|
5004
|
+
const skillsDir = join22(home, ".claude", "skills");
|
|
4761
5005
|
const agentSkillDirs = [
|
|
4762
|
-
{ label: "~/.codex/skills/", path:
|
|
4763
|
-
{ label: "~/.agents/skills/", path:
|
|
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 =
|
|
4867
|
-
if (
|
|
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 (!
|
|
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 (!
|
|
4900
|
-
if (!
|
|
4901
|
-
if (!
|
|
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 =
|
|
4912
|
-
if (!
|
|
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(
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
5112
|
-
return check("pass", "fleet_identity", "Fleet identity", `Resolved ${
|
|
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 =
|
|
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
|
-
|
|
5120
|
-
|
|
5432
|
+
join22(home, "Library", "Logs", "wiki-pull.log"),
|
|
5433
|
+
join22(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
|
|
5121
5434
|
] : [
|
|
5122
|
-
|
|
5123
|
-
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
5164
|
-
if (!
|
|
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 =
|
|
5347
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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
|
|
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 ?
|
|
5502
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
5503
|
-
const filterPath = input.filterPath ??
|
|
5504
|
-
const packagedSnapshotPath =
|
|
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 ?? (
|
|
5819
|
+
const snapshotPath = input.snapshotScriptPath ?? (existsSync10(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
|
|
5507
5820
|
function snapshotLastStatusCheck() {
|
|
5508
|
-
const snapshotLog =
|
|
5821
|
+
const snapshotLog = join22(logDir, "wiki-snapshot.log");
|
|
5509
5822
|
try {
|
|
5510
|
-
const logContent =
|
|
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 =
|
|
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 =
|
|
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" &&
|
|
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" &&
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
5630
|
-
const c1 =
|
|
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 =
|
|
5994
|
+
const logFile = join22(logDir, "wiki-push.log");
|
|
5682
5995
|
let c3;
|
|
5683
5996
|
try {
|
|
5684
|
-
const logContent =
|
|
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 =
|
|
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 =
|
|
6065
|
+
const fetchLogFile = join22(logDir, "wiki-fetch.log");
|
|
5753
6066
|
let cFetch;
|
|
5754
6067
|
try {
|
|
5755
|
-
const logContent =
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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(
|
|
6209
|
+
results.push(join22(dir, entry.name));
|
|
5897
6210
|
} else if (entry.isDirectory()) {
|
|
5898
|
-
results.push(...findSkillMd(
|
|
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) =>
|
|
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() &&
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
6212
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
6360
|
-
await mkdir7(
|
|
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(
|
|
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 =
|
|
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 =
|
|
7029
|
-
await mkdir7(
|
|
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(
|
|
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(
|
|
7542
|
+
const projectCache = await readIfExists2(join25(vault, memoryCacheRelPath(project)));
|
|
7218
7543
|
if (projectCache) return projectCache;
|
|
7219
7544
|
}
|
|
7220
|
-
return readIfExists2(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
7908
|
-
import { existsSync as
|
|
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 ??
|
|
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 (!
|
|
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(
|
|
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 =
|
|
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);
|
|
@@ -8467,6 +8792,7 @@ export {
|
|
|
8467
8792
|
runOrphans,
|
|
8468
8793
|
extractCitationMarkers,
|
|
8469
8794
|
runAudit,
|
|
8795
|
+
findPlugin,
|
|
8470
8796
|
extractTaxonomy,
|
|
8471
8797
|
readLastOp,
|
|
8472
8798
|
appendLastOp,
|
|
@@ -8481,6 +8807,7 @@ export {
|
|
|
8481
8807
|
runIndexLinkFormat,
|
|
8482
8808
|
runDedup,
|
|
8483
8809
|
safeWritePage,
|
|
8810
|
+
runFrontmatterFix,
|
|
8484
8811
|
fixPathTooLong,
|
|
8485
8812
|
assessSourceIdentity,
|
|
8486
8813
|
runLint,
|
|
@@ -8491,8 +8818,16 @@ export {
|
|
|
8491
8818
|
runConfigPath,
|
|
8492
8819
|
writeCache,
|
|
8493
8820
|
triggerAutoUpdate,
|
|
8821
|
+
FLEET_REL_PATH,
|
|
8494
8822
|
runFleetValidate,
|
|
8495
8823
|
runFleetContext,
|
|
8824
|
+
loadFleetManifest,
|
|
8825
|
+
resolveFleetHostId,
|
|
8826
|
+
SATELLITE_STALE_MS,
|
|
8827
|
+
satelliteLatestRunPath,
|
|
8828
|
+
isFailedRunStatus,
|
|
8829
|
+
readSatelliteLatestRunFromText,
|
|
8830
|
+
evaluateSatelliteRunHealth,
|
|
8496
8831
|
runDoctor,
|
|
8497
8832
|
readCliPackageJson,
|
|
8498
8833
|
runProjectIndex,
|