skillwiki 0.9.46 → 0.9.48
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.
|
@@ -659,15 +659,48 @@ import { writeFile as writeFile2, mkdir } from "fs/promises";
|
|
|
659
659
|
import { dirname } from "path";
|
|
660
660
|
|
|
661
661
|
// src/utils/vault.ts
|
|
662
|
+
import { existsSync, readFileSync } from "fs";
|
|
662
663
|
import { readFile as readFile2, readdir, stat } from "fs/promises";
|
|
663
664
|
import { join as join2, relative as relative2, sep as sep2 } from "path";
|
|
664
665
|
var TYPED_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
665
666
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
666
|
-
var DEFAULT_IO_CONCURRENCY =
|
|
667
|
+
var DEFAULT_IO_CONCURRENCY = 1;
|
|
667
668
|
function vaultIoConcurrency() {
|
|
668
669
|
const raw = Number.parseInt(process.env.SKILLWIKI_VAULT_IO_CONCURRENCY ?? "", 10);
|
|
669
670
|
return Number.isFinite(raw) && raw > 0 ? Math.min(raw, 64) : DEFAULT_IO_CONCURRENCY;
|
|
670
671
|
}
|
|
672
|
+
function decodeProcMountPath(value) {
|
|
673
|
+
return value.replace(/\\040/g, " ");
|
|
674
|
+
}
|
|
675
|
+
function isRcloneFuseVault(root) {
|
|
676
|
+
let mounts;
|
|
677
|
+
try {
|
|
678
|
+
mounts = readFileSync("/proc/mounts", "utf8");
|
|
679
|
+
} catch {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
return mounts.split(/\r?\n/).some((line) => {
|
|
683
|
+
const parts = line.split(" ");
|
|
684
|
+
if (parts.length < 3) return false;
|
|
685
|
+
const mountPoint = decodeProcMountPath(parts[1]);
|
|
686
|
+
const fsType = parts[2];
|
|
687
|
+
return fsType === "fuse.rclone" && (root === mountPoint || root.startsWith(`${mountPoint}/`));
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
function resolveReadOnlyVaultRoot(root) {
|
|
691
|
+
if (/^(1|true|yes)$/i.test(process.env.SKILLWIKI_DISABLE_VAULT_READ_MIRROR ?? "")) {
|
|
692
|
+
return { root, mirrored: false };
|
|
693
|
+
}
|
|
694
|
+
const explicitMirror = process.env.SKILLWIKI_VAULT_READ_MIRROR;
|
|
695
|
+
if (explicitMirror && existsSync(join2(explicitMirror, "SCHEMA.md"))) {
|
|
696
|
+
return { root: explicitMirror, mirrored: explicitMirror !== root };
|
|
697
|
+
}
|
|
698
|
+
const siblingMirror = `${root}-git`;
|
|
699
|
+
if (isRcloneFuseVault(root) && existsSync(join2(siblingMirror, "SCHEMA.md"))) {
|
|
700
|
+
return { root: siblingMirror, mirrored: true };
|
|
701
|
+
}
|
|
702
|
+
return { root, mirrored: false };
|
|
703
|
+
}
|
|
671
704
|
async function mapWithConcurrency(items, limit, mapper) {
|
|
672
705
|
const out = new Array(items.length);
|
|
673
706
|
let next = 0;
|
|
@@ -1334,7 +1367,7 @@ function hasWikilinkCitations(body) {
|
|
|
1334
1367
|
}
|
|
1335
1368
|
|
|
1336
1369
|
// src/utils/raw-source.ts
|
|
1337
|
-
import { existsSync } from "fs";
|
|
1370
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1338
1371
|
import { stat as stat2 } from "fs/promises";
|
|
1339
1372
|
import { join as join4 } from "path";
|
|
1340
1373
|
function normalizeRawSourceTarget(entry) {
|
|
@@ -1355,7 +1388,7 @@ function rawSourceTargetCandidates(vault, target) {
|
|
|
1355
1388
|
return [...new Set(candidates)];
|
|
1356
1389
|
}
|
|
1357
1390
|
function rawSourceTargetExistsSync(vault, target) {
|
|
1358
|
-
return rawSourceTargetCandidates(vault, target).some((candidate) =>
|
|
1391
|
+
return rawSourceTargetCandidates(vault, target).some((candidate) => existsSync2(candidate));
|
|
1359
1392
|
}
|
|
1360
1393
|
async function rawSourceTargetExists(vault, target) {
|
|
1361
1394
|
for (const candidate of rawSourceTargetCandidates(vault, target)) {
|
|
@@ -1438,8 +1471,8 @@ async function findVaultRoot(start) {
|
|
|
1438
1471
|
function stripWikilink(s) {
|
|
1439
1472
|
return s.replace(/^\[\[/, "").replace(/(?:\|[^\[\]]*)?\]\]$/, "").trim();
|
|
1440
1473
|
}
|
|
1441
|
-
async function validateCompoundReferences(vault) {
|
|
1442
|
-
const scan = await scanVault(vault);
|
|
1474
|
+
async function validateCompoundReferences(vault, existingScan, pageTextCache) {
|
|
1475
|
+
const scan = existingScan ? ok(existingScan) : await scanVault(vault);
|
|
1443
1476
|
if (!scan.ok) return scan;
|
|
1444
1477
|
const slugToPage = /* @__PURE__ */ new Map();
|
|
1445
1478
|
const pathToPage = /* @__PURE__ */ new Map();
|
|
@@ -1449,7 +1482,7 @@ async function validateCompoundReferences(vault) {
|
|
|
1449
1482
|
}
|
|
1450
1483
|
const findings = [];
|
|
1451
1484
|
for (const cp of scan.data.compound) {
|
|
1452
|
-
const text = await
|
|
1485
|
+
const text = await readPageCached(cp, pageTextCache);
|
|
1453
1486
|
const fm = extractFrontmatter(text);
|
|
1454
1487
|
if (!fm.ok) continue;
|
|
1455
1488
|
const projectRaw = fm.data.project;
|
|
@@ -1464,7 +1497,7 @@ async function validateCompoundReferences(vault) {
|
|
|
1464
1497
|
findings.push({ compound: cp.relPath, work_item: wi, kind: "missing", detail: `no work item found for [[${target}]]` });
|
|
1465
1498
|
continue;
|
|
1466
1499
|
}
|
|
1467
|
-
const wiFm = extractFrontmatter(await
|
|
1500
|
+
const wiFm = extractFrontmatter(await readPageCached(resolved, pageTextCache));
|
|
1468
1501
|
if (wiFm.ok && wiFm.data.project) {
|
|
1469
1502
|
const wiProj = stripWikilink(String(wiFm.data.project));
|
|
1470
1503
|
if (wiProj !== projSlug) {
|
|
@@ -1666,7 +1699,7 @@ function parseExpiryAnnotations(content, pagePath) {
|
|
|
1666
1699
|
}
|
|
1667
1700
|
|
|
1668
1701
|
// src/utils/last-op.ts
|
|
1669
|
-
import { readFileSync, writeFileSync, mkdirSync, unlinkSync, existsSync as
|
|
1702
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, unlinkSync, existsSync as existsSync3 } from "fs";
|
|
1670
1703
|
import { join as join8 } from "path";
|
|
1671
1704
|
var LAST_OP_DIR = ".skillwiki";
|
|
1672
1705
|
var LAST_OP_FILE = "last-op.json";
|
|
@@ -1675,9 +1708,9 @@ function lastOpPath(vault) {
|
|
|
1675
1708
|
}
|
|
1676
1709
|
function readLastOp(vault) {
|
|
1677
1710
|
const p = lastOpPath(vault);
|
|
1678
|
-
if (!
|
|
1711
|
+
if (!existsSync3(p)) return [];
|
|
1679
1712
|
try {
|
|
1680
|
-
const raw =
|
|
1713
|
+
const raw = readFileSync2(p, "utf8");
|
|
1681
1714
|
const parsed = JSON.parse(raw);
|
|
1682
1715
|
if (!Array.isArray(parsed)) {
|
|
1683
1716
|
unlinkSync(p);
|
|
@@ -1696,7 +1729,7 @@ function appendLastOp(vault, entry) {
|
|
|
1696
1729
|
const existing = readLastOp(vault);
|
|
1697
1730
|
existing.push(entry);
|
|
1698
1731
|
const dir = join8(vault, LAST_OP_DIR);
|
|
1699
|
-
if (!
|
|
1732
|
+
if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
1700
1733
|
writeFileSync(lastOpPath(vault), JSON.stringify(existing, null, 2), "utf8");
|
|
1701
1734
|
}
|
|
1702
1735
|
function clearLastOp(vault) {
|
|
@@ -2142,7 +2175,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
|
|
|
2142
2175
|
|
|
2143
2176
|
// src/commands/dedup.ts
|
|
2144
2177
|
import { createHash as createHash2 } from "crypto";
|
|
2145
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
2178
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2146
2179
|
import { dirname as dirname4, join as join12, resolve as resolve3 } from "path";
|
|
2147
2180
|
|
|
2148
2181
|
// src/utils/rclone.ts
|
|
@@ -2262,7 +2295,7 @@ async function runDedup(input) {
|
|
|
2262
2295
|
}
|
|
2263
2296
|
}
|
|
2264
2297
|
for (const page of scan.allMarkdown.filter((p) => !p.relPath.startsWith("raw/"))) {
|
|
2265
|
-
const text =
|
|
2298
|
+
const text = readFileSync3(join12(input.vault, page.relPath), "utf-8");
|
|
2266
2299
|
let updated = text;
|
|
2267
2300
|
let changed = false;
|
|
2268
2301
|
for (const [oldPath, newPath] of replacements) {
|
|
@@ -2379,14 +2412,14 @@ function buildSafeEntries(vault, duplicates, unsafe) {
|
|
|
2379
2412
|
return entries;
|
|
2380
2413
|
}
|
|
2381
2414
|
function hashRawBody(vault, relPath) {
|
|
2382
|
-
const text =
|
|
2415
|
+
const text = readFileSync3(join12(vault, relPath), "utf-8");
|
|
2383
2416
|
const split = splitFrontmatter(text);
|
|
2384
2417
|
const body = split.ok ? split.data.body : text;
|
|
2385
2418
|
return createHash2("sha256").update(body).digest("hex");
|
|
2386
2419
|
}
|
|
2387
2420
|
function readManifest(path) {
|
|
2388
2421
|
try {
|
|
2389
|
-
const parsed = JSON.parse(
|
|
2422
|
+
const parsed = JSON.parse(readFileSync3(path, "utf-8"));
|
|
2390
2423
|
if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {
|
|
2391
2424
|
return err("INVALID_FRONTMATTER", { message: "dedup manifest must have version 1 and entries[]" });
|
|
2392
2425
|
}
|
|
@@ -2553,7 +2586,7 @@ ${newBody}`;
|
|
|
2553
2586
|
}
|
|
2554
2587
|
|
|
2555
2588
|
// src/commands/lint.ts
|
|
2556
|
-
import { existsSync as
|
|
2589
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2557
2590
|
import { readFile as readFile12, readdir as readdir3 } from "fs/promises";
|
|
2558
2591
|
import { createHash as createHash4 } from "crypto";
|
|
2559
2592
|
import { join as join15, relative as relative3, sep as sep3 } from "path";
|
|
@@ -2613,7 +2646,7 @@ async function runRawBodyDedup(vault, scan, pageTextCache) {
|
|
|
2613
2646
|
}
|
|
2614
2647
|
|
|
2615
2648
|
// src/commands/path-too-long.ts
|
|
2616
|
-
import { existsSync as
|
|
2649
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2617
2650
|
import { mkdir as mkdir4, readFile as readFile11, rename as rename4, unlink as unlink2 } from "fs/promises";
|
|
2618
2651
|
import { dirname as dirname6, join as join14, posix, resolve as resolve4 } from "path";
|
|
2619
2652
|
var MAX_PATH_LENGTH = 240;
|
|
@@ -2729,7 +2762,7 @@ async function resolveFixTarget(vault, original, preferred, maxLength) {
|
|
|
2729
2762
|
for (const candidate of candidateRelPaths(preferred, maxLength)) {
|
|
2730
2763
|
if (candidate === original || candidate.length > maxLength) continue;
|
|
2731
2764
|
const candidatePath = join14(vault, candidate);
|
|
2732
|
-
if (!
|
|
2765
|
+
if (!existsSync4(candidatePath)) return { relPath: candidate, mode: "rename" };
|
|
2733
2766
|
if (await hasSameContent(join14(vault, original), candidatePath)) {
|
|
2734
2767
|
return { relPath: candidate, mode: "dedupe" };
|
|
2735
2768
|
}
|
|
@@ -3126,6 +3159,9 @@ ${match.length === 0 ? "0 violations" : match.map((b) => ` ${b.kind}: ${b.items
|
|
|
3126
3159
|
result: ok(input.summary ? summarizeLintOutput(output, input.examplesLimit) : output)
|
|
3127
3160
|
};
|
|
3128
3161
|
}
|
|
3162
|
+
function lintReadVault(input) {
|
|
3163
|
+
return input.fix ? input.vault : resolveReadOnlyVaultRoot(input.vault).root;
|
|
3164
|
+
}
|
|
3129
3165
|
function recomputeRawSha256IfPresent(content) {
|
|
3130
3166
|
const split = splitFrontmatter(content);
|
|
3131
3167
|
if (!split.ok) return content;
|
|
@@ -3188,19 +3224,20 @@ async function walkMarkdownFiles(absDir, vaultRoot) {
|
|
|
3188
3224
|
return pages;
|
|
3189
3225
|
}
|
|
3190
3226
|
async function collectCliRefsPages(vault) {
|
|
3191
|
-
if (!
|
|
3227
|
+
if (!existsSync5(join15(vault, "SCHEMA.md"))) {
|
|
3192
3228
|
return err("VAULT_PATH_INVALID", { root: vault, reason: "SCHEMA.md missing" });
|
|
3193
3229
|
}
|
|
3194
3230
|
const pages = [];
|
|
3195
3231
|
for (const dir of CLI_REFS_TYPED_DIRS) {
|
|
3196
3232
|
const absDir = join15(vault, dir);
|
|
3197
|
-
if (!
|
|
3233
|
+
if (!existsSync5(absDir)) continue;
|
|
3198
3234
|
pages.push(...await walkMarkdownFiles(absDir, vault));
|
|
3199
3235
|
}
|
|
3200
3236
|
return ok(pages);
|
|
3201
3237
|
}
|
|
3202
3238
|
async function runCliRefsOnly(input) {
|
|
3203
|
-
const
|
|
3239
|
+
const lintVault = lintReadVault(input);
|
|
3240
|
+
const pages = await collectCliRefsPages(lintVault);
|
|
3204
3241
|
if (!pages.ok) {
|
|
3205
3242
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: pages };
|
|
3206
3243
|
}
|
|
@@ -3358,7 +3395,8 @@ async function applyFileSourceUrlFix(input, scan, fileSourceUrlFlags, fileSource
|
|
|
3358
3395
|
return remaining;
|
|
3359
3396
|
}
|
|
3360
3397
|
async function runFileSourceUrlOnly(input) {
|
|
3361
|
-
const
|
|
3398
|
+
const lintVault = lintReadVault(input);
|
|
3399
|
+
const scanResult = await scanVault(lintVault);
|
|
3362
3400
|
if (!scanResult.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
|
|
3363
3401
|
const pageTextCache = /* @__PURE__ */ new Map();
|
|
3364
3402
|
const fixed = [];
|
|
@@ -3389,10 +3427,11 @@ async function runLint(input) {
|
|
|
3389
3427
|
return runFileSourceUrlOnly(input);
|
|
3390
3428
|
}
|
|
3391
3429
|
const shouldFix = (bucket) => !!input.fix && (!input.only || input.only === bucket);
|
|
3430
|
+
const lintVault = lintReadVault(input);
|
|
3392
3431
|
const buckets = {};
|
|
3393
3432
|
const fixed = [];
|
|
3394
3433
|
const unresolved = [];
|
|
3395
|
-
const scanResult = await scanVault(
|
|
3434
|
+
const scanResult = await scanVault(lintVault);
|
|
3396
3435
|
if (!scanResult.ok) {
|
|
3397
3436
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
|
|
3398
3437
|
}
|
|
@@ -3406,64 +3445,64 @@ async function runLint(input) {
|
|
|
3406
3445
|
}
|
|
3407
3446
|
});
|
|
3408
3447
|
}
|
|
3409
|
-
const links = await runLinks({ vault:
|
|
3448
|
+
const links = await runLinks({ vault: lintVault, scan, pageTextCache });
|
|
3410
3449
|
if (links.result.ok && links.result.data.broken.length > 0) buckets.broken_wikilinks = links.result.data.broken;
|
|
3411
3450
|
if (!links.result.ok && links.result.error === "INVALID_FRONTMATTER") {
|
|
3412
3451
|
buckets.invalid_frontmatter = [links.result.detail ?? {}];
|
|
3413
3452
|
}
|
|
3414
|
-
const tags = await runTagAudit({ vault:
|
|
3453
|
+
const tags = await runTagAudit({ vault: lintVault, scan, pageTextCache });
|
|
3415
3454
|
if (tags.result.ok && tags.result.data.violations.length > 0) buckets.tag_not_in_taxonomy = tags.result.data.violations;
|
|
3416
3455
|
if (!tags.result.ok && tags.result.error === "INVALID_FRONTMATTER") {
|
|
3417
3456
|
buckets.invalid_frontmatter = [...buckets.invalid_frontmatter ?? [], tags.result.detail ?? {}];
|
|
3418
3457
|
}
|
|
3419
|
-
const idx = await runIndexCheck({ vault:
|
|
3458
|
+
const idx = await runIndexCheck({ vault: lintVault, scan });
|
|
3420
3459
|
if (idx.result.ok && (idx.result.data.missing_from_index.length > 0 || idx.result.data.ghost_entries.length > 0)) {
|
|
3421
3460
|
buckets.index_incomplete = [{
|
|
3422
3461
|
missing_from_index: idx.result.data.missing_from_index,
|
|
3423
3462
|
ghost_entries: idx.result.data.ghost_entries
|
|
3424
3463
|
}];
|
|
3425
3464
|
}
|
|
3426
|
-
const linkFmt = await runIndexLinkFormat({ vault:
|
|
3465
|
+
const linkFmt = await runIndexLinkFormat({ vault: lintVault });
|
|
3427
3466
|
if (linkFmt.result.ok && linkFmt.result.data.markdown_links.length > 0) {
|
|
3428
3467
|
buckets.index_link_format = linkFmt.result.data.markdown_links;
|
|
3429
3468
|
}
|
|
3430
|
-
const staleResult = await runStale({ vault:
|
|
3469
|
+
const staleResult = await runStale({ vault: lintVault, days: input.days, scan, pageTextCache });
|
|
3431
3470
|
if (staleResult.result.ok) {
|
|
3432
3471
|
const st = staleResult.result.data;
|
|
3433
3472
|
const staleList = [...st.stale_transcripts.map((t) => t.path), ...(st.unclaimed_transcripts ?? []).map((t) => t.path), ...st.incomplete_work_items.map((w) => w.path), ...(st.done_work_items ?? []).map((w) => w.path)];
|
|
3434
3473
|
if (staleList.length > 0) buckets.stale_page = staleList;
|
|
3435
3474
|
}
|
|
3436
|
-
const pagesize = await runPagesize({ vault:
|
|
3475
|
+
const pagesize = await runPagesize({ vault: lintVault, lines: input.lines, scan, pageTextCache });
|
|
3437
3476
|
if (pagesize.result.ok && pagesize.result.data.oversized.length > 0) buckets.page_too_large = pagesize.result.data.oversized;
|
|
3438
|
-
const rotate = await runLogRotate({ vault:
|
|
3477
|
+
const rotate = await runLogRotate({ vault: lintVault, threshold: input.logThreshold, apply: false });
|
|
3439
3478
|
if (rotate.result.ok && rotate.exitCode === ExitCode.LOG_ROTATE_NEEDED) {
|
|
3440
3479
|
buckets.log_rotate_needed = [{ entries: rotate.result.data.entries, threshold: rotate.result.data.threshold }];
|
|
3441
3480
|
}
|
|
3442
|
-
const orphans = await runOrphans({ vault:
|
|
3481
|
+
const orphans = await runOrphans({ vault: lintVault, scan, pageTextCache });
|
|
3443
3482
|
if (orphans.result.ok) {
|
|
3444
3483
|
if (orphans.result.data.orphans.length > 0) buckets.orphans = orphans.result.data.orphans;
|
|
3445
3484
|
if (orphans.result.data.bridges.length > 0) buckets.bridges = orphans.result.data.bridges;
|
|
3446
3485
|
}
|
|
3447
|
-
const sparse = await runSparseCommunity({ vault:
|
|
3486
|
+
const sparse = await runSparseCommunity({ vault: lintVault, scan, pageTextCache });
|
|
3448
3487
|
if (sparse.result.ok && sparse.result.data.communities.length > 0) {
|
|
3449
3488
|
buckets.sparse_community = sparse.result.data.communities;
|
|
3450
3489
|
}
|
|
3451
|
-
const topicMap = await runTopicMapCheck({ vault:
|
|
3490
|
+
const topicMap = await runTopicMapCheck({ vault: lintVault, scan });
|
|
3452
3491
|
if (topicMap.result.ok && topicMap.result.data.recommended) {
|
|
3453
3492
|
buckets.topic_map_recommended = [{ page_count: topicMap.result.data.page_count, threshold: topicMap.result.data.threshold }];
|
|
3454
3493
|
}
|
|
3455
|
-
const dedup = await runDedup({ vault:
|
|
3494
|
+
const dedup = await runDedup({ vault: lintVault, scan, pageTextCache });
|
|
3456
3495
|
if (dedup.result.ok && dedup.result.data.duplicates.length > 0) buckets.raw_dedup = dedup.result.data.duplicates;
|
|
3457
|
-
const bodyDedup = await runRawBodyDedup(
|
|
3496
|
+
const bodyDedup = await runRawBodyDedup(lintVault, scan, pageTextCache);
|
|
3458
3497
|
if (bodyDedup.result.ok && bodyDedup.result.data.duplicates.length > 0) {
|
|
3459
3498
|
buckets.raw_body_duplicate = bodyDedup.result.data.duplicates.map((d) => ({
|
|
3460
3499
|
body_hash: d.bodyHash.slice(0, 12),
|
|
3461
3500
|
files: d.files.map((f) => `${f.relPath} (sha256: ${f.sha256 ?? "none"})`)
|
|
3462
3501
|
}));
|
|
3463
3502
|
}
|
|
3464
|
-
const compoundRefs = await validateCompoundReferences(
|
|
3503
|
+
const compoundRefs = await validateCompoundReferences(lintVault, scan, pageTextCache);
|
|
3465
3504
|
if (compoundRefs.ok && compoundRefs.data.length > 0) buckets.compound_refs = compoundRefs.data;
|
|
3466
|
-
const pathCheck = await runPathTooLong({ vault:
|
|
3505
|
+
const pathCheck = await runPathTooLong({ vault: lintVault, scan });
|
|
3467
3506
|
if (pathCheck.result.ok && pathCheck.result.data.violations.length > 0) buckets.path_too_long = pathCheck.result.data.violations;
|
|
3468
3507
|
const allPages = [...scan.typedKnowledge, ...scan.raw, ...scan.workItems, ...scan.compound];
|
|
3469
3508
|
const slugs = buildSlugMap(allPages);
|
|
@@ -3553,12 +3592,12 @@ async function runLint(input) {
|
|
|
3553
3592
|
for (const entry of sourcesEntries) {
|
|
3554
3593
|
const rawPath = normalizeRawSourceTarget(entry);
|
|
3555
3594
|
if (!rawPath) continue;
|
|
3556
|
-
if (!rawSourceTargetExistsSync(
|
|
3595
|
+
if (!rawSourceTargetExistsSync(lintVault, rawPath)) {
|
|
3557
3596
|
result.brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
|
|
3558
3597
|
}
|
|
3559
3598
|
}
|
|
3560
3599
|
for (const marker of extractCitationMarkers(body)) {
|
|
3561
|
-
if (!rawSourceTargetExistsSync(
|
|
3600
|
+
if (!rawSourceTargetExistsSync(lintVault, marker.target)) {
|
|
3562
3601
|
result.brokenSourceFlags.push(`${page.relPath}: ${marker.target}`);
|
|
3563
3602
|
}
|
|
3564
3603
|
}
|
|
@@ -3661,8 +3700,8 @@ async function runLint(input) {
|
|
|
3661
3700
|
const readKnowledgeContent = (slug) => {
|
|
3662
3701
|
const existing = knowledgeContentCache.get(slug);
|
|
3663
3702
|
if (existing) return existing;
|
|
3664
|
-
const knowledgePath = join15(
|
|
3665
|
-
const pending =
|
|
3703
|
+
const knowledgePath = join15(lintVault, "projects", slug, "knowledge.md");
|
|
3704
|
+
const pending = existsSync5(knowledgePath) ? readFile12(knowledgePath, "utf8").catch(() => null) : Promise.resolve(null);
|
|
3666
3705
|
knowledgeContentCache.set(slug, pending);
|
|
3667
3706
|
return pending;
|
|
3668
3707
|
};
|
|
@@ -4145,7 +4184,7 @@ ${split.data.body}`;
|
|
|
4145
4184
|
|
|
4146
4185
|
// src/commands/config.ts
|
|
4147
4186
|
import { readFile as readFile13 } from "fs/promises";
|
|
4148
|
-
import { existsSync as
|
|
4187
|
+
import { existsSync as existsSync6 } from "fs";
|
|
4149
4188
|
import { join as join16 } from "path";
|
|
4150
4189
|
function validateKey(key) {
|
|
4151
4190
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
@@ -4201,11 +4240,11 @@ async function runConfigList(input) {
|
|
|
4201
4240
|
}
|
|
4202
4241
|
async function runConfigPath(input) {
|
|
4203
4242
|
const filePath = configPath(input.home);
|
|
4204
|
-
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists:
|
|
4243
|
+
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync6(filePath), humanHint: filePath }) };
|
|
4205
4244
|
}
|
|
4206
4245
|
|
|
4207
4246
|
// src/utils/auto-update.ts
|
|
4208
|
-
import { readFileSync as
|
|
4247
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
4209
4248
|
import { join as join17, dirname as dirname7 } from "path";
|
|
4210
4249
|
import { spawn } from "child_process";
|
|
4211
4250
|
function cachePath(home) {
|
|
@@ -4213,7 +4252,7 @@ function cachePath(home) {
|
|
|
4213
4252
|
}
|
|
4214
4253
|
function readCacheRaw(home) {
|
|
4215
4254
|
try {
|
|
4216
|
-
const raw =
|
|
4255
|
+
const raw = readFileSync4(cachePath(home), "utf8");
|
|
4217
4256
|
return JSON.parse(raw);
|
|
4218
4257
|
} catch {
|
|
4219
4258
|
return null;
|
|
@@ -4253,7 +4292,7 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
4253
4292
|
if (!isStale) return;
|
|
4254
4293
|
const distTag = distTagFromCache(home);
|
|
4255
4294
|
const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
|
|
4256
|
-
if (!
|
|
4295
|
+
if (!existsSync7(bgScript)) return;
|
|
4257
4296
|
const child = spawn(process.execPath, [bgScript, home, currentVersion, distTag], {
|
|
4258
4297
|
detached: true,
|
|
4259
4298
|
stdio: "ignore"
|
|
@@ -4717,20 +4756,20 @@ function safeUserName() {
|
|
|
4717
4756
|
}
|
|
4718
4757
|
|
|
4719
4758
|
// src/commands/doctor.ts
|
|
4720
|
-
import { existsSync as
|
|
4759
|
+
import { existsSync as existsSync11, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync8 } from "fs";
|
|
4721
4760
|
import { join as join22, resolve as resolve5 } from "path";
|
|
4722
4761
|
import { execSync as execSync2 } from "child_process";
|
|
4723
4762
|
import { platform as platform2 } from "os";
|
|
4724
4763
|
|
|
4725
4764
|
// src/utils/plugin-registry.ts
|
|
4726
|
-
import { existsSync as
|
|
4765
|
+
import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync5 } from "fs";
|
|
4727
4766
|
import { join as join19 } from "path";
|
|
4728
4767
|
var REGISTRY_PATH = join19(".claude", "plugins", "installed_plugins.json");
|
|
4729
4768
|
var CODEX_CONFIG_PATH = join19(".codex", "config.toml");
|
|
4730
4769
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
4731
4770
|
function readInstalledPlugins(home) {
|
|
4732
4771
|
try {
|
|
4733
|
-
const raw =
|
|
4772
|
+
const raw = readFileSync5(join19(home, REGISTRY_PATH), "utf8");
|
|
4734
4773
|
return JSON.parse(raw);
|
|
4735
4774
|
} catch {
|
|
4736
4775
|
return null;
|
|
@@ -4767,7 +4806,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
|
4767
4806
|
const config = readCodexPluginConfig(home, key, marketplace);
|
|
4768
4807
|
if (!config?.enabled) return null;
|
|
4769
4808
|
const cacheRoot = join19(home, ".codex", "plugins", "cache", marketplace, pluginName);
|
|
4770
|
-
if (!
|
|
4809
|
+
if (!existsSync8(cacheRoot)) return null;
|
|
4771
4810
|
let versions;
|
|
4772
4811
|
try {
|
|
4773
4812
|
versions = readdirSync(cacheRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -4799,7 +4838,7 @@ function parsePluginKey(key) {
|
|
|
4799
4838
|
function readCodexPluginConfig(home, key, marketplace) {
|
|
4800
4839
|
let raw;
|
|
4801
4840
|
try {
|
|
4802
|
-
raw =
|
|
4841
|
+
raw = readFileSync5(join19(home, CODEX_CONFIG_PATH), "utf8");
|
|
4803
4842
|
} catch {
|
|
4804
4843
|
return null;
|
|
4805
4844
|
}
|
|
@@ -4841,7 +4880,7 @@ function parseTomlScalar(rawValue) {
|
|
|
4841
4880
|
}
|
|
4842
4881
|
|
|
4843
4882
|
// src/utils/satellite-run-health.ts
|
|
4844
|
-
import { existsSync as
|
|
4883
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
4845
4884
|
import { join as join20 } from "path";
|
|
4846
4885
|
var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
|
|
4847
4886
|
function satelliteLatestRunPath(vault) {
|
|
@@ -4867,9 +4906,9 @@ function readSatelliteLatestRunFromText(text) {
|
|
|
4867
4906
|
}
|
|
4868
4907
|
function readSatelliteLatestRun(vault) {
|
|
4869
4908
|
const latestPath = satelliteLatestRunPath(vault);
|
|
4870
|
-
if (!
|
|
4909
|
+
if (!existsSync9(latestPath)) return null;
|
|
4871
4910
|
try {
|
|
4872
|
-
return parseLatestRunFile(
|
|
4911
|
+
return parseLatestRunFile(readFileSync6(latestPath, "utf8"));
|
|
4873
4912
|
} catch {
|
|
4874
4913
|
return null;
|
|
4875
4914
|
}
|
|
@@ -4898,7 +4937,7 @@ function evaluateSatelliteRunHealth(vault, now) {
|
|
|
4898
4937
|
// src/utils/s3-mount-health.ts
|
|
4899
4938
|
import { execSync } from "child_process";
|
|
4900
4939
|
import { platform } from "os";
|
|
4901
|
-
import { readFileSync as
|
|
4940
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
|
|
4902
4941
|
import { join as join21 } from "path";
|
|
4903
4942
|
var OS = platform();
|
|
4904
4943
|
function findRcloneMountPid() {
|
|
@@ -4983,7 +5022,7 @@ function extractRcloneFs(args) {
|
|
|
4983
5022
|
function getRcloneArgs(pid) {
|
|
4984
5023
|
try {
|
|
4985
5024
|
if (OS === "linux") {
|
|
4986
|
-
const raw =
|
|
5025
|
+
const raw = readFileSync7(`/proc/${pid}/cmdline`);
|
|
4987
5026
|
return new TextDecoder().decode(raw).split("\0").filter(Boolean);
|
|
4988
5027
|
} else {
|
|
4989
5028
|
const out = execSync(`ps -o args= -p ${pid}`, {
|
|
@@ -5026,7 +5065,7 @@ function queryRcloneRC(rcAddr, fs) {
|
|
|
5026
5065
|
function detectFuseMount(vaultPath) {
|
|
5027
5066
|
try {
|
|
5028
5067
|
if (OS === "linux") {
|
|
5029
|
-
const mounts =
|
|
5068
|
+
const mounts = readFileSync7("/proc/mounts", "utf8");
|
|
5030
5069
|
let best = null;
|
|
5031
5070
|
for (const line of mounts.split("\n")) {
|
|
5032
5071
|
const parts = line.split(" ");
|
|
@@ -5158,12 +5197,12 @@ function detectCliChannels(argv, home) {
|
|
|
5158
5197
|
const plugin = findPlugin(home);
|
|
5159
5198
|
if (plugin) {
|
|
5160
5199
|
const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
|
|
5161
|
-
if (
|
|
5200
|
+
if (existsSync11(pluginBin)) {
|
|
5162
5201
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
5163
5202
|
}
|
|
5164
5203
|
}
|
|
5165
5204
|
const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
|
|
5166
|
-
if (
|
|
5205
|
+
if (existsSync11(installBin)) {
|
|
5167
5206
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
5168
5207
|
}
|
|
5169
5208
|
return channels;
|
|
@@ -5224,7 +5263,7 @@ function isDevSourceRun(argv) {
|
|
|
5224
5263
|
}
|
|
5225
5264
|
async function checkConfigFile(home) {
|
|
5226
5265
|
const cfgPath = configPath(home);
|
|
5227
|
-
if (!
|
|
5266
|
+
if (!existsSync11(cfgPath)) {
|
|
5228
5267
|
return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
|
|
5229
5268
|
}
|
|
5230
5269
|
try {
|
|
@@ -5239,7 +5278,7 @@ function checkWikiPathExists(resolvedPath) {
|
|
|
5239
5278
|
if (resolvedPath === void 0) {
|
|
5240
5279
|
return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5241
5280
|
}
|
|
5242
|
-
if (
|
|
5281
|
+
if (existsSync11(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
|
5243
5282
|
return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
5244
5283
|
}
|
|
5245
5284
|
return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
@@ -5248,13 +5287,13 @@ function checkVaultStructure(resolvedPath) {
|
|
|
5248
5287
|
if (resolvedPath === void 0) {
|
|
5249
5288
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5250
5289
|
}
|
|
5251
|
-
if (!
|
|
5290
|
+
if (!existsSync11(resolvedPath)) {
|
|
5252
5291
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
5253
5292
|
}
|
|
5254
5293
|
const missing = [];
|
|
5255
|
-
if (!
|
|
5294
|
+
if (!existsSync11(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
5256
5295
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
5257
|
-
if (!
|
|
5296
|
+
if (!existsSync11(join22(resolvedPath, dir))) missing.push(dir + "/");
|
|
5258
5297
|
}
|
|
5259
5298
|
if (missing.length === 0) {
|
|
5260
5299
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -5263,7 +5302,7 @@ function checkVaultStructure(resolvedPath) {
|
|
|
5263
5302
|
}
|
|
5264
5303
|
function checkSkillsInstalled(home, cwd) {
|
|
5265
5304
|
const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
|
|
5266
|
-
if (srcDir &&
|
|
5305
|
+
if (srcDir && existsSync11(srcDir)) {
|
|
5267
5306
|
const found = findInstalledSkillMd(srcDir);
|
|
5268
5307
|
if (found.length > 0) {
|
|
5269
5308
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
|
|
@@ -5277,7 +5316,7 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
5277
5316
|
}
|
|
5278
5317
|
}
|
|
5279
5318
|
const skillsDir = join22(home, ".claude", "skills");
|
|
5280
|
-
if (
|
|
5319
|
+
if (existsSync11(skillsDir)) {
|
|
5281
5320
|
const found = findInstalledSkillMd(skillsDir);
|
|
5282
5321
|
if (found.length > 0) {
|
|
5283
5322
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
|
|
@@ -5394,7 +5433,7 @@ async function checkProfiles(home) {
|
|
|
5394
5433
|
async function checkProjectLocalOverride(cwd) {
|
|
5395
5434
|
const dir = cwd ?? process.cwd();
|
|
5396
5435
|
const envPath = join22(dir, ".skillwiki", ".env");
|
|
5397
|
-
if (
|
|
5436
|
+
if (existsSync11(envPath)) {
|
|
5398
5437
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
5399
5438
|
}
|
|
5400
5439
|
return check("pass", "project_local", "Project-local config", "None");
|
|
@@ -5403,7 +5442,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
5403
5442
|
if (resolvedPath === void 0) {
|
|
5404
5443
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5405
5444
|
}
|
|
5406
|
-
if (!
|
|
5445
|
+
if (!existsSync11(join22(resolvedPath, ".git"))) {
|
|
5407
5446
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
5408
5447
|
}
|
|
5409
5448
|
try {
|
|
@@ -5426,9 +5465,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
5426
5465
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5427
5466
|
}
|
|
5428
5467
|
const missing = [];
|
|
5429
|
-
if (!
|
|
5430
|
-
if (!
|
|
5431
|
-
if (!
|
|
5468
|
+
if (!existsSync11(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
5469
|
+
if (!existsSync11(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
5470
|
+
if (!existsSync11(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
5432
5471
|
if (missing.length === 0) {
|
|
5433
5472
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
5434
5473
|
}
|
|
@@ -5439,7 +5478,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
5439
5478
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5440
5479
|
}
|
|
5441
5480
|
const rawDir = join22(resolvedPath, "raw");
|
|
5442
|
-
if (!
|
|
5481
|
+
if (!existsSync11(rawDir)) {
|
|
5443
5482
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
5444
5483
|
}
|
|
5445
5484
|
const found = [];
|
|
@@ -5467,7 +5506,7 @@ function checkSyncLastPush(resolvedPath) {
|
|
|
5467
5506
|
if (resolvedPath === void 0) {
|
|
5468
5507
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5469
5508
|
}
|
|
5470
|
-
if (!
|
|
5509
|
+
if (!existsSync11(join22(resolvedPath, ".git"))) {
|
|
5471
5510
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
5472
5511
|
}
|
|
5473
5512
|
let timestamp;
|
|
@@ -5515,7 +5554,7 @@ function checkVaultGitDirty(resolvedPath) {
|
|
|
5515
5554
|
if (resolvedPath === void 0) {
|
|
5516
5555
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
|
|
5517
5556
|
}
|
|
5518
|
-
if (!
|
|
5557
|
+
if (!existsSync11(join22(resolvedPath, ".git"))) {
|
|
5519
5558
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
|
|
5520
5559
|
}
|
|
5521
5560
|
try {
|
|
@@ -5583,7 +5622,7 @@ function remoteMainHash(resolvedPath) {
|
|
|
5583
5622
|
}
|
|
5584
5623
|
function checkStaleRemoteMain(resolvedPath) {
|
|
5585
5624
|
if (resolvedPath === void 0) return void 0;
|
|
5586
|
-
if (!
|
|
5625
|
+
if (!existsSync11(join22(resolvedPath, ".git"))) return void 0;
|
|
5587
5626
|
const localOrigin = gitRefHash(resolvedPath, "origin/main");
|
|
5588
5627
|
if (!localOrigin) return void 0;
|
|
5589
5628
|
const remoteMain = remoteMainHash(resolvedPath);
|
|
@@ -5599,7 +5638,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
|
|
|
5599
5638
|
if (resolvedPath === void 0) {
|
|
5600
5639
|
return check("pass", id, label, "No vault path \u2014 check skipped");
|
|
5601
5640
|
}
|
|
5602
|
-
if (!
|
|
5641
|
+
if (!existsSync11(join22(resolvedPath, ".git"))) {
|
|
5603
5642
|
return check("pass", id, label, "No git repo \u2014 check skipped");
|
|
5604
5643
|
}
|
|
5605
5644
|
if (!hasOriginMain(resolvedPath)) {
|
|
@@ -5627,7 +5666,7 @@ function checkSatelliteLastRun(vaultPath, satelliteExpected) {
|
|
|
5627
5666
|
return check("pass", "satellite_job_last_run", "Satellite job last run", "No vault path \u2014 check skipped");
|
|
5628
5667
|
}
|
|
5629
5668
|
const latestPath = satelliteLatestRunPath(vaultPath);
|
|
5630
|
-
if (!
|
|
5669
|
+
if (!existsSync11(latestPath)) {
|
|
5631
5670
|
return check("pass", "satellite_job_last_run", "Satellite job last run", "No latest-run.json \u2014 satellite has not run yet");
|
|
5632
5671
|
}
|
|
5633
5672
|
try {
|
|
@@ -5731,12 +5770,12 @@ function isRecentLogLine(line, nowMs) {
|
|
|
5731
5770
|
return nowMs - ts <= 24 * 60 * 60 * 1e3;
|
|
5732
5771
|
}
|
|
5733
5772
|
function checkVaultGitPullFailures(home) {
|
|
5734
|
-
const path = pullLogPaths(home).find((p) =>
|
|
5773
|
+
const path = pullLogPaths(home).find((p) => existsSync11(p));
|
|
5735
5774
|
if (!path) {
|
|
5736
5775
|
return check("pass", "vault_git_pull_failures", "Vault pull failures", "No wiki-pull.log found \u2014 check skipped");
|
|
5737
5776
|
}
|
|
5738
5777
|
try {
|
|
5739
|
-
const lines =
|
|
5778
|
+
const lines = readFileSync8(path, "utf8").split(/\r?\n/).filter(Boolean);
|
|
5740
5779
|
const now = Date.now();
|
|
5741
5780
|
const failures = lines.filter(
|
|
5742
5781
|
(line) => isRecentLogLine(line, now) && /(pre-push pull failed|FAIL .*pull|FAIL .*rebase|cannot pull with rebase|unstaged changes)/i.test(line)
|
|
@@ -5760,7 +5799,7 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
5760
5799
|
}
|
|
5761
5800
|
const mountPoint = fuse.mountPoint;
|
|
5762
5801
|
const conceptsDir = join22(resolvedPath, "concepts");
|
|
5763
|
-
if (!
|
|
5802
|
+
if (!existsSync11(conceptsDir)) {
|
|
5764
5803
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
5765
5804
|
}
|
|
5766
5805
|
const start = Date.now();
|
|
@@ -5943,7 +5982,7 @@ function checkWriteTest(resolvedPath) {
|
|
|
5943
5982
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
5944
5983
|
}
|
|
5945
5984
|
const conceptsDir = join22(resolvedPath, "concepts");
|
|
5946
|
-
if (!
|
|
5985
|
+
if (!existsSync11(conceptsDir)) {
|
|
5947
5986
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
5948
5987
|
}
|
|
5949
5988
|
const result = writeTest(conceptsDir);
|
|
@@ -6029,7 +6068,7 @@ function checkVfsCacheHealth(resolvedPath) {
|
|
|
6029
6068
|
}
|
|
6030
6069
|
function readVaultSyncConfig(home) {
|
|
6031
6070
|
try {
|
|
6032
|
-
const content =
|
|
6071
|
+
const content = readFileSync8(join22(home, ".skillwiki", ".env"), "utf8");
|
|
6033
6072
|
let installed = false;
|
|
6034
6073
|
let role;
|
|
6035
6074
|
let serviceScope;
|
|
@@ -6058,7 +6097,7 @@ function readVaultSyncConfig(home) {
|
|
|
6058
6097
|
}
|
|
6059
6098
|
function readKeyFromEnvFile(path, keys) {
|
|
6060
6099
|
try {
|
|
6061
|
-
const content =
|
|
6100
|
+
const content = readFileSync8(path, "utf8");
|
|
6062
6101
|
for (const line of content.split(/\r?\n/)) {
|
|
6063
6102
|
const trimmed = line.trim();
|
|
6064
6103
|
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
|
|
@@ -6080,7 +6119,7 @@ function resolveSnapshotGitWorktree(config) {
|
|
|
6080
6119
|
if (fromProfile) return fromProfile;
|
|
6081
6120
|
}
|
|
6082
6121
|
const defaultPath = "/root/wiki-git";
|
|
6083
|
-
return
|
|
6122
|
+
return existsSync11(defaultPath) ? defaultPath : void 0;
|
|
6084
6123
|
}
|
|
6085
6124
|
function vaultSyncChecks(input) {
|
|
6086
6125
|
const os = input.os ?? platform2();
|
|
@@ -6102,11 +6141,11 @@ function vaultSyncChecks(input) {
|
|
|
6102
6141
|
const filterPath = input.filterPath ?? join22(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
6103
6142
|
const packagedSnapshotPath = join22(shareDir, "wiki-snapshot.sh");
|
|
6104
6143
|
const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
6105
|
-
const snapshotPath = input.snapshotScriptPath ?? (
|
|
6144
|
+
const snapshotPath = input.snapshotScriptPath ?? (existsSync11(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
|
|
6106
6145
|
function snapshotLastStatusCheck() {
|
|
6107
6146
|
const snapshotLog = join22(logDir, "wiki-snapshot.log");
|
|
6108
6147
|
try {
|
|
6109
|
-
const logContent =
|
|
6148
|
+
const logContent = readFileSync8(snapshotLog, "utf8");
|
|
6110
6149
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
6111
6150
|
if (lines.length === 0) {
|
|
6112
6151
|
return check(
|
|
@@ -6151,14 +6190,14 @@ function vaultSyncChecks(input) {
|
|
|
6151
6190
|
}
|
|
6152
6191
|
}
|
|
6153
6192
|
if (input.vaultSyncRole === "snapshotter") {
|
|
6154
|
-
const c12 =
|
|
6193
|
+
const c12 = existsSync11(snapshotPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found snapshot script: ${snapshotPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Snapshot script not found at ${snapshotPath}`);
|
|
6155
6194
|
const serviceScope = input.vaultSyncServiceScope ?? "user";
|
|
6156
6195
|
const userTimerPath = join22(home, ".config", "systemd", "user", "wiki-snapshot.timer");
|
|
6157
6196
|
const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
|
|
6158
6197
|
let c22;
|
|
6159
|
-
if (serviceScope === "user" &&
|
|
6198
|
+
if (serviceScope === "user" && existsSync11(userTimerPath)) {
|
|
6160
6199
|
c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${userTimerPath}`);
|
|
6161
|
-
} else if (serviceScope === "system" &&
|
|
6200
|
+
} else if (serviceScope === "system" && existsSync11(systemTimerPath)) {
|
|
6162
6201
|
c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${systemTimerPath}`);
|
|
6163
6202
|
} else if (os !== "linux") {
|
|
6164
6203
|
c22 = check("warn", "vault_sync_jobs_enabled", "Vault sync jobs enabled", "Snapshotter scheduler is Linux-only and no wiki-snapshot.timer file was found");
|
|
@@ -6190,7 +6229,7 @@ function vaultSyncChecks(input) {
|
|
|
6190
6229
|
);
|
|
6191
6230
|
let c52;
|
|
6192
6231
|
try {
|
|
6193
|
-
if (!
|
|
6232
|
+
if (!existsSync11(snapshotPath)) {
|
|
6194
6233
|
c52 = check(
|
|
6195
6234
|
"error",
|
|
6196
6235
|
"vault_sync_snapshot_guard",
|
|
@@ -6198,7 +6237,7 @@ function vaultSyncChecks(input) {
|
|
|
6198
6237
|
`Snapshot script not found at ${snapshotPath}`
|
|
6199
6238
|
);
|
|
6200
6239
|
} else {
|
|
6201
|
-
const content =
|
|
6240
|
+
const content = readFileSync8(snapshotPath, "utf8");
|
|
6202
6241
|
if (!content.includes("--max-delete")) {
|
|
6203
6242
|
c52 = check(
|
|
6204
6243
|
"error",
|
|
@@ -6226,7 +6265,7 @@ function vaultSyncChecks(input) {
|
|
|
6226
6265
|
return [c12, c22, c32, cFetch2, c42, c52];
|
|
6227
6266
|
}
|
|
6228
6267
|
const pushScriptPath = join22(shareDir, "wiki-push.sh");
|
|
6229
|
-
const c1 =
|
|
6268
|
+
const c1 = existsSync11(pushScriptPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${pushScriptPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Script not found at ${pushScriptPath} \u2014 run vault-sync-install`);
|
|
6230
6269
|
let c2;
|
|
6231
6270
|
try {
|
|
6232
6271
|
if (isMac) {
|
|
@@ -6280,7 +6319,7 @@ function vaultSyncChecks(input) {
|
|
|
6280
6319
|
const logFile = join22(logDir, "wiki-push.log");
|
|
6281
6320
|
let c3;
|
|
6282
6321
|
try {
|
|
6283
|
-
const logContent =
|
|
6322
|
+
const logContent = readFileSync8(logFile, "utf8");
|
|
6284
6323
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
6285
6324
|
if (lines.length === 0) {
|
|
6286
6325
|
c3 = check(
|
|
@@ -6336,7 +6375,7 @@ function vaultSyncChecks(input) {
|
|
|
6336
6375
|
}
|
|
6337
6376
|
}
|
|
6338
6377
|
} catch {
|
|
6339
|
-
c3 =
|
|
6378
|
+
c3 = existsSync11(logDir) ? check(
|
|
6340
6379
|
"warn",
|
|
6341
6380
|
"vault_sync_last_push_age",
|
|
6342
6381
|
"Vault sync last push recency",
|
|
@@ -6351,7 +6390,7 @@ function vaultSyncChecks(input) {
|
|
|
6351
6390
|
const fetchLogFile = join22(logDir, "wiki-fetch.log");
|
|
6352
6391
|
let cFetch;
|
|
6353
6392
|
try {
|
|
6354
|
-
const logContent =
|
|
6393
|
+
const logContent = readFileSync8(fetchLogFile, "utf8");
|
|
6355
6394
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
6356
6395
|
if (lines.length === 0) {
|
|
6357
6396
|
cFetch = check(
|
|
@@ -6395,7 +6434,7 @@ function vaultSyncChecks(input) {
|
|
|
6395
6434
|
}
|
|
6396
6435
|
let c4;
|
|
6397
6436
|
try {
|
|
6398
|
-
if (!
|
|
6437
|
+
if (!existsSync11(filterPath)) {
|
|
6399
6438
|
c4 = check(
|
|
6400
6439
|
"error",
|
|
6401
6440
|
"vault_sync_filter_present",
|
|
@@ -6403,7 +6442,7 @@ function vaultSyncChecks(input) {
|
|
|
6403
6442
|
`Filter file not found at ${filterPath}`
|
|
6404
6443
|
);
|
|
6405
6444
|
} else {
|
|
6406
|
-
const content =
|
|
6445
|
+
const content = readFileSync8(filterPath, "utf8");
|
|
6407
6446
|
const requiredExcludes = [
|
|
6408
6447
|
"remotely-save/data.json",
|
|
6409
6448
|
".skillwiki/sync.lock",
|
|
@@ -6446,7 +6485,7 @@ function vaultSyncChecks(input) {
|
|
|
6446
6485
|
);
|
|
6447
6486
|
} else {
|
|
6448
6487
|
try {
|
|
6449
|
-
if (!
|
|
6488
|
+
if (!existsSync11(snapshotPath)) {
|
|
6450
6489
|
c5 = check(
|
|
6451
6490
|
"error",
|
|
6452
6491
|
"vault_sync_snapshot_guard",
|
|
@@ -6454,7 +6493,7 @@ function vaultSyncChecks(input) {
|
|
|
6454
6493
|
`Snapshot script not found at ${snapshotPath}`
|
|
6455
6494
|
);
|
|
6456
6495
|
} else {
|
|
6457
|
-
const content =
|
|
6496
|
+
const content = readFileSync8(snapshotPath, "utf8");
|
|
6458
6497
|
if (!content.includes("--max-delete")) {
|
|
6459
6498
|
c5 = check(
|
|
6460
6499
|
"error",
|
|
@@ -6512,7 +6551,7 @@ function findSkillNames(dir) {
|
|
|
6512
6551
|
return results;
|
|
6513
6552
|
}
|
|
6514
6553
|
for (const entry of entries) {
|
|
6515
|
-
if (entry.isDirectory() &&
|
|
6554
|
+
if (entry.isDirectory() && existsSync11(join22(dir, entry.name, "SKILL.md"))) {
|
|
6516
6555
|
results.push(entry.name);
|
|
6517
6556
|
}
|
|
6518
6557
|
}
|
|
@@ -6556,7 +6595,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
6556
6595
|
}
|
|
6557
6596
|
let logLines = 0;
|
|
6558
6597
|
try {
|
|
6559
|
-
logLines =
|
|
6598
|
+
logLines = readFileSync8(join22(resolvedPath, "log.md"), "utf8").split("\n").length;
|
|
6560
6599
|
} catch {
|
|
6561
6600
|
}
|
|
6562
6601
|
return [
|
|
@@ -6658,7 +6697,7 @@ async function runDoctor(input) {
|
|
|
6658
6697
|
}
|
|
6659
6698
|
|
|
6660
6699
|
// src/utils/package-info.ts
|
|
6661
|
-
import { readFileSync as
|
|
6700
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
6662
6701
|
function packageJsonCandidateUrls(baseUrl = import.meta.url) {
|
|
6663
6702
|
return [
|
|
6664
6703
|
new URL("../package.json", baseUrl),
|
|
@@ -6668,7 +6707,7 @@ function packageJsonCandidateUrls(baseUrl = import.meta.url) {
|
|
|
6668
6707
|
function readCliPackageJson(baseUrl = import.meta.url) {
|
|
6669
6708
|
for (const url of packageJsonCandidateUrls(baseUrl)) {
|
|
6670
6709
|
try {
|
|
6671
|
-
const pkg = JSON.parse(
|
|
6710
|
+
const pkg = JSON.parse(readFileSync9(url, "utf8"));
|
|
6672
6711
|
if (typeof pkg.version === "string") {
|
|
6673
6712
|
return { ...pkg, version: pkg.version };
|
|
6674
6713
|
}
|
|
@@ -6824,7 +6863,7 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
6824
6863
|
|
|
6825
6864
|
// src/commands/observe.ts
|
|
6826
6865
|
import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
|
|
6827
|
-
import { existsSync as
|
|
6866
|
+
import { existsSync as existsSync12, statSync as statSync2 } from "fs";
|
|
6828
6867
|
import { join as join24 } from "path";
|
|
6829
6868
|
import { createHash as createHash5 } from "crypto";
|
|
6830
6869
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
@@ -6848,7 +6887,7 @@ async function runObserve(input) {
|
|
|
6848
6887
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
6849
6888
|
};
|
|
6850
6889
|
}
|
|
6851
|
-
if (!
|
|
6890
|
+
if (!existsSync12(input.vault) || !statSync2(input.vault).isDirectory()) {
|
|
6852
6891
|
return {
|
|
6853
6892
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
6854
6893
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
@@ -8522,7 +8561,7 @@ async function fetchQueryPreview(input) {
|
|
|
8522
8561
|
// src/mcp/graph-html.ts
|
|
8523
8562
|
import { readFile as readFile19 } from "fs/promises";
|
|
8524
8563
|
import { join as join29 } from "path";
|
|
8525
|
-
import { existsSync as
|
|
8564
|
+
import { existsSync as existsSync13 } from "fs";
|
|
8526
8565
|
var TYPE_COLORS = {
|
|
8527
8566
|
entities: "#e74c3c",
|
|
8528
8567
|
concepts: "#27ae60",
|
|
@@ -8592,7 +8631,7 @@ ${nodeSvg}
|
|
|
8592
8631
|
async function fetchGraphHtmlReport(input) {
|
|
8593
8632
|
const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
|
|
8594
8633
|
const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
|
|
8595
|
-
if (!
|
|
8634
|
+
if (!existsSync13(graphPath)) {
|
|
8596
8635
|
return {
|
|
8597
8636
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
8598
8637
|
result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
|
package/dist/cli.js
CHANGED
package/dist/skillwiki-mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.48",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|