skillwiki 0.9.47 → 0.9.49

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,6 +659,7 @@ 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"];
@@ -668,6 +669,40 @@ 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 isRcloneFuseVaultFromMounts(root, mounts) {
676
+ return mounts.split(/\r?\n/).some((line) => {
677
+ const parts = line.split(" ");
678
+ if (parts.length < 3) return false;
679
+ const mountPoint = decodeProcMountPath(parts[1]);
680
+ const fsType = parts[2];
681
+ return fsType === "fuse.rclone" && (root === mountPoint || root.startsWith(`${mountPoint}/`));
682
+ });
683
+ }
684
+ function resolveReadOnlyVaultRootWithMounts(root, mounts) {
685
+ if (/^(1|true|yes)$/i.test(process.env.SKILLWIKI_DISABLE_VAULT_READ_MIRROR ?? "")) {
686
+ return { root, mirrored: false };
687
+ }
688
+ const explicitMirror = process.env.SKILLWIKI_VAULT_READ_MIRROR;
689
+ if (explicitMirror && existsSync(join2(explicitMirror, "SCHEMA.md"))) {
690
+ return { root: explicitMirror, mirrored: explicitMirror !== root };
691
+ }
692
+ const siblingMirror = `${root}-git`;
693
+ if (isRcloneFuseVaultFromMounts(root, mounts) && existsSync(join2(siblingMirror, "SCHEMA.md"))) {
694
+ return { root: siblingMirror, mirrored: true };
695
+ }
696
+ return { root, mirrored: false };
697
+ }
698
+ function resolveReadOnlyVaultRoot(root) {
699
+ let mounts = "";
700
+ try {
701
+ mounts = readFileSync("/proc/mounts", "utf8");
702
+ } catch {
703
+ }
704
+ return resolveReadOnlyVaultRootWithMounts(root, mounts);
705
+ }
671
706
  async function mapWithConcurrency(items, limit, mapper) {
672
707
  const out = new Array(items.length);
673
708
  let next = 0;
@@ -1334,7 +1369,7 @@ function hasWikilinkCitations(body) {
1334
1369
  }
1335
1370
 
1336
1371
  // src/utils/raw-source.ts
1337
- import { existsSync } from "fs";
1372
+ import { existsSync as existsSync2 } from "fs";
1338
1373
  import { stat as stat2 } from "fs/promises";
1339
1374
  import { join as join4 } from "path";
1340
1375
  function normalizeRawSourceTarget(entry) {
@@ -1355,7 +1390,7 @@ function rawSourceTargetCandidates(vault, target) {
1355
1390
  return [...new Set(candidates)];
1356
1391
  }
1357
1392
  function rawSourceTargetExistsSync(vault, target) {
1358
- return rawSourceTargetCandidates(vault, target).some((candidate) => existsSync(candidate));
1393
+ return rawSourceTargetCandidates(vault, target).some((candidate) => existsSync2(candidate));
1359
1394
  }
1360
1395
  async function rawSourceTargetExists(vault, target) {
1361
1396
  for (const candidate of rawSourceTargetCandidates(vault, target)) {
@@ -1666,7 +1701,7 @@ function parseExpiryAnnotations(content, pagePath) {
1666
1701
  }
1667
1702
 
1668
1703
  // src/utils/last-op.ts
1669
- import { readFileSync, writeFileSync, mkdirSync, unlinkSync, existsSync as existsSync2 } from "fs";
1704
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync, unlinkSync, existsSync as existsSync3 } from "fs";
1670
1705
  import { join as join8 } from "path";
1671
1706
  var LAST_OP_DIR = ".skillwiki";
1672
1707
  var LAST_OP_FILE = "last-op.json";
@@ -1675,9 +1710,9 @@ function lastOpPath(vault) {
1675
1710
  }
1676
1711
  function readLastOp(vault) {
1677
1712
  const p = lastOpPath(vault);
1678
- if (!existsSync2(p)) return [];
1713
+ if (!existsSync3(p)) return [];
1679
1714
  try {
1680
- const raw = readFileSync(p, "utf8");
1715
+ const raw = readFileSync2(p, "utf8");
1681
1716
  const parsed = JSON.parse(raw);
1682
1717
  if (!Array.isArray(parsed)) {
1683
1718
  unlinkSync(p);
@@ -1696,7 +1731,7 @@ function appendLastOp(vault, entry) {
1696
1731
  const existing = readLastOp(vault);
1697
1732
  existing.push(entry);
1698
1733
  const dir = join8(vault, LAST_OP_DIR);
1699
- if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
1734
+ if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
1700
1735
  writeFileSync(lastOpPath(vault), JSON.stringify(existing, null, 2), "utf8");
1701
1736
  }
1702
1737
  function clearLastOp(vault) {
@@ -2142,7 +2177,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
2142
2177
 
2143
2178
  // src/commands/dedup.ts
2144
2179
  import { createHash as createHash2 } from "crypto";
2145
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
2180
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
2146
2181
  import { dirname as dirname4, join as join12, resolve as resolve3 } from "path";
2147
2182
 
2148
2183
  // src/utils/rclone.ts
@@ -2262,7 +2297,7 @@ async function runDedup(input) {
2262
2297
  }
2263
2298
  }
2264
2299
  for (const page of scan.allMarkdown.filter((p) => !p.relPath.startsWith("raw/"))) {
2265
- const text = readFileSync2(join12(input.vault, page.relPath), "utf-8");
2300
+ const text = readFileSync3(join12(input.vault, page.relPath), "utf-8");
2266
2301
  let updated = text;
2267
2302
  let changed = false;
2268
2303
  for (const [oldPath, newPath] of replacements) {
@@ -2379,14 +2414,14 @@ function buildSafeEntries(vault, duplicates, unsafe) {
2379
2414
  return entries;
2380
2415
  }
2381
2416
  function hashRawBody(vault, relPath) {
2382
- const text = readFileSync2(join12(vault, relPath), "utf-8");
2417
+ const text = readFileSync3(join12(vault, relPath), "utf-8");
2383
2418
  const split = splitFrontmatter(text);
2384
2419
  const body = split.ok ? split.data.body : text;
2385
2420
  return createHash2("sha256").update(body).digest("hex");
2386
2421
  }
2387
2422
  function readManifest(path) {
2388
2423
  try {
2389
- const parsed = JSON.parse(readFileSync2(path, "utf-8"));
2424
+ const parsed = JSON.parse(readFileSync3(path, "utf-8"));
2390
2425
  if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {
2391
2426
  return err("INVALID_FRONTMATTER", { message: "dedup manifest must have version 1 and entries[]" });
2392
2427
  }
@@ -2553,7 +2588,7 @@ ${newBody}`;
2553
2588
  }
2554
2589
 
2555
2590
  // src/commands/lint.ts
2556
- import { existsSync as existsSync4 } from "fs";
2591
+ import { existsSync as existsSync5 } from "fs";
2557
2592
  import { readFile as readFile12, readdir as readdir3 } from "fs/promises";
2558
2593
  import { createHash as createHash4 } from "crypto";
2559
2594
  import { join as join15, relative as relative3, sep as sep3 } from "path";
@@ -2613,7 +2648,7 @@ async function runRawBodyDedup(vault, scan, pageTextCache) {
2613
2648
  }
2614
2649
 
2615
2650
  // src/commands/path-too-long.ts
2616
- import { existsSync as existsSync3 } from "fs";
2651
+ import { existsSync as existsSync4 } from "fs";
2617
2652
  import { mkdir as mkdir4, readFile as readFile11, rename as rename4, unlink as unlink2 } from "fs/promises";
2618
2653
  import { dirname as dirname6, join as join14, posix, resolve as resolve4 } from "path";
2619
2654
  var MAX_PATH_LENGTH = 240;
@@ -2729,7 +2764,7 @@ async function resolveFixTarget(vault, original, preferred, maxLength) {
2729
2764
  for (const candidate of candidateRelPaths(preferred, maxLength)) {
2730
2765
  if (candidate === original || candidate.length > maxLength) continue;
2731
2766
  const candidatePath = join14(vault, candidate);
2732
- if (!existsSync3(candidatePath)) return { relPath: candidate, mode: "rename" };
2767
+ if (!existsSync4(candidatePath)) return { relPath: candidate, mode: "rename" };
2733
2768
  if (await hasSameContent(join14(vault, original), candidatePath)) {
2734
2769
  return { relPath: candidate, mode: "dedupe" };
2735
2770
  }
@@ -3126,6 +3161,9 @@ ${match.length === 0 ? "0 violations" : match.map((b) => ` ${b.kind}: ${b.items
3126
3161
  result: ok(input.summary ? summarizeLintOutput(output, input.examplesLimit) : output)
3127
3162
  };
3128
3163
  }
3164
+ function lintReadVault(input) {
3165
+ return input.fix ? input.vault : resolveReadOnlyVaultRoot(input.vault).root;
3166
+ }
3129
3167
  function recomputeRawSha256IfPresent(content) {
3130
3168
  const split = splitFrontmatter(content);
3131
3169
  if (!split.ok) return content;
@@ -3188,19 +3226,20 @@ async function walkMarkdownFiles(absDir, vaultRoot) {
3188
3226
  return pages;
3189
3227
  }
3190
3228
  async function collectCliRefsPages(vault) {
3191
- if (!existsSync4(join15(vault, "SCHEMA.md"))) {
3229
+ if (!existsSync5(join15(vault, "SCHEMA.md"))) {
3192
3230
  return err("VAULT_PATH_INVALID", { root: vault, reason: "SCHEMA.md missing" });
3193
3231
  }
3194
3232
  const pages = [];
3195
3233
  for (const dir of CLI_REFS_TYPED_DIRS) {
3196
3234
  const absDir = join15(vault, dir);
3197
- if (!existsSync4(absDir)) continue;
3235
+ if (!existsSync5(absDir)) continue;
3198
3236
  pages.push(...await walkMarkdownFiles(absDir, vault));
3199
3237
  }
3200
3238
  return ok(pages);
3201
3239
  }
3202
3240
  async function runCliRefsOnly(input) {
3203
- const pages = await collectCliRefsPages(input.vault);
3241
+ const lintVault = lintReadVault(input);
3242
+ const pages = await collectCliRefsPages(lintVault);
3204
3243
  if (!pages.ok) {
3205
3244
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: pages };
3206
3245
  }
@@ -3358,7 +3397,8 @@ async function applyFileSourceUrlFix(input, scan, fileSourceUrlFlags, fileSource
3358
3397
  return remaining;
3359
3398
  }
3360
3399
  async function runFileSourceUrlOnly(input) {
3361
- const scanResult = await scanVault(input.vault);
3400
+ const lintVault = lintReadVault(input);
3401
+ const scanResult = await scanVault(lintVault);
3362
3402
  if (!scanResult.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
3363
3403
  const pageTextCache = /* @__PURE__ */ new Map();
3364
3404
  const fixed = [];
@@ -3389,10 +3429,11 @@ async function runLint(input) {
3389
3429
  return runFileSourceUrlOnly(input);
3390
3430
  }
3391
3431
  const shouldFix = (bucket) => !!input.fix && (!input.only || input.only === bucket);
3432
+ const lintVault = lintReadVault(input);
3392
3433
  const buckets = {};
3393
3434
  const fixed = [];
3394
3435
  const unresolved = [];
3395
- const scanResult = await scanVault(input.vault);
3436
+ const scanResult = await scanVault(lintVault);
3396
3437
  if (!scanResult.ok) {
3397
3438
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
3398
3439
  }
@@ -3406,64 +3447,64 @@ async function runLint(input) {
3406
3447
  }
3407
3448
  });
3408
3449
  }
3409
- const links = await runLinks({ vault: input.vault, scan, pageTextCache });
3450
+ const links = await runLinks({ vault: lintVault, scan, pageTextCache });
3410
3451
  if (links.result.ok && links.result.data.broken.length > 0) buckets.broken_wikilinks = links.result.data.broken;
3411
3452
  if (!links.result.ok && links.result.error === "INVALID_FRONTMATTER") {
3412
3453
  buckets.invalid_frontmatter = [links.result.detail ?? {}];
3413
3454
  }
3414
- const tags = await runTagAudit({ vault: input.vault, scan, pageTextCache });
3455
+ const tags = await runTagAudit({ vault: lintVault, scan, pageTextCache });
3415
3456
  if (tags.result.ok && tags.result.data.violations.length > 0) buckets.tag_not_in_taxonomy = tags.result.data.violations;
3416
3457
  if (!tags.result.ok && tags.result.error === "INVALID_FRONTMATTER") {
3417
3458
  buckets.invalid_frontmatter = [...buckets.invalid_frontmatter ?? [], tags.result.detail ?? {}];
3418
3459
  }
3419
- const idx = await runIndexCheck({ vault: input.vault, scan });
3460
+ const idx = await runIndexCheck({ vault: lintVault, scan });
3420
3461
  if (idx.result.ok && (idx.result.data.missing_from_index.length > 0 || idx.result.data.ghost_entries.length > 0)) {
3421
3462
  buckets.index_incomplete = [{
3422
3463
  missing_from_index: idx.result.data.missing_from_index,
3423
3464
  ghost_entries: idx.result.data.ghost_entries
3424
3465
  }];
3425
3466
  }
3426
- const linkFmt = await runIndexLinkFormat({ vault: input.vault });
3467
+ const linkFmt = await runIndexLinkFormat({ vault: lintVault });
3427
3468
  if (linkFmt.result.ok && linkFmt.result.data.markdown_links.length > 0) {
3428
3469
  buckets.index_link_format = linkFmt.result.data.markdown_links;
3429
3470
  }
3430
- const staleResult = await runStale({ vault: input.vault, days: input.days, scan, pageTextCache });
3471
+ const staleResult = await runStale({ vault: lintVault, days: input.days, scan, pageTextCache });
3431
3472
  if (staleResult.result.ok) {
3432
3473
  const st = staleResult.result.data;
3433
3474
  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
3475
  if (staleList.length > 0) buckets.stale_page = staleList;
3435
3476
  }
3436
- const pagesize = await runPagesize({ vault: input.vault, lines: input.lines, scan, pageTextCache });
3477
+ const pagesize = await runPagesize({ vault: lintVault, lines: input.lines, scan, pageTextCache });
3437
3478
  if (pagesize.result.ok && pagesize.result.data.oversized.length > 0) buckets.page_too_large = pagesize.result.data.oversized;
3438
- const rotate = await runLogRotate({ vault: input.vault, threshold: input.logThreshold, apply: false });
3479
+ const rotate = await runLogRotate({ vault: lintVault, threshold: input.logThreshold, apply: false });
3439
3480
  if (rotate.result.ok && rotate.exitCode === ExitCode.LOG_ROTATE_NEEDED) {
3440
3481
  buckets.log_rotate_needed = [{ entries: rotate.result.data.entries, threshold: rotate.result.data.threshold }];
3441
3482
  }
3442
- const orphans = await runOrphans({ vault: input.vault, scan, pageTextCache });
3483
+ const orphans = await runOrphans({ vault: lintVault, scan, pageTextCache });
3443
3484
  if (orphans.result.ok) {
3444
3485
  if (orphans.result.data.orphans.length > 0) buckets.orphans = orphans.result.data.orphans;
3445
3486
  if (orphans.result.data.bridges.length > 0) buckets.bridges = orphans.result.data.bridges;
3446
3487
  }
3447
- const sparse = await runSparseCommunity({ vault: input.vault, scan, pageTextCache });
3488
+ const sparse = await runSparseCommunity({ vault: lintVault, scan, pageTextCache });
3448
3489
  if (sparse.result.ok && sparse.result.data.communities.length > 0) {
3449
3490
  buckets.sparse_community = sparse.result.data.communities;
3450
3491
  }
3451
- const topicMap = await runTopicMapCheck({ vault: input.vault, scan });
3492
+ const topicMap = await runTopicMapCheck({ vault: lintVault, scan });
3452
3493
  if (topicMap.result.ok && topicMap.result.data.recommended) {
3453
3494
  buckets.topic_map_recommended = [{ page_count: topicMap.result.data.page_count, threshold: topicMap.result.data.threshold }];
3454
3495
  }
3455
- const dedup = await runDedup({ vault: input.vault, scan, pageTextCache });
3496
+ const dedup = await runDedup({ vault: lintVault, scan, pageTextCache });
3456
3497
  if (dedup.result.ok && dedup.result.data.duplicates.length > 0) buckets.raw_dedup = dedup.result.data.duplicates;
3457
- const bodyDedup = await runRawBodyDedup(input.vault, scan, pageTextCache);
3498
+ const bodyDedup = await runRawBodyDedup(lintVault, scan, pageTextCache);
3458
3499
  if (bodyDedup.result.ok && bodyDedup.result.data.duplicates.length > 0) {
3459
3500
  buckets.raw_body_duplicate = bodyDedup.result.data.duplicates.map((d) => ({
3460
3501
  body_hash: d.bodyHash.slice(0, 12),
3461
3502
  files: d.files.map((f) => `${f.relPath} (sha256: ${f.sha256 ?? "none"})`)
3462
3503
  }));
3463
3504
  }
3464
- const compoundRefs = await validateCompoundReferences(input.vault, scan, pageTextCache);
3505
+ const compoundRefs = await validateCompoundReferences(lintVault, scan, pageTextCache);
3465
3506
  if (compoundRefs.ok && compoundRefs.data.length > 0) buckets.compound_refs = compoundRefs.data;
3466
- const pathCheck = await runPathTooLong({ vault: input.vault, scan });
3507
+ const pathCheck = await runPathTooLong({ vault: lintVault, scan });
3467
3508
  if (pathCheck.result.ok && pathCheck.result.data.violations.length > 0) buckets.path_too_long = pathCheck.result.data.violations;
3468
3509
  const allPages = [...scan.typedKnowledge, ...scan.raw, ...scan.workItems, ...scan.compound];
3469
3510
  const slugs = buildSlugMap(allPages);
@@ -3553,12 +3594,12 @@ async function runLint(input) {
3553
3594
  for (const entry of sourcesEntries) {
3554
3595
  const rawPath = normalizeRawSourceTarget(entry);
3555
3596
  if (!rawPath) continue;
3556
- if (!rawSourceTargetExistsSync(input.vault, rawPath)) {
3597
+ if (!rawSourceTargetExistsSync(lintVault, rawPath)) {
3557
3598
  result.brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
3558
3599
  }
3559
3600
  }
3560
3601
  for (const marker of extractCitationMarkers(body)) {
3561
- if (!rawSourceTargetExistsSync(input.vault, marker.target)) {
3602
+ if (!rawSourceTargetExistsSync(lintVault, marker.target)) {
3562
3603
  result.brokenSourceFlags.push(`${page.relPath}: ${marker.target}`);
3563
3604
  }
3564
3605
  }
@@ -3661,8 +3702,8 @@ async function runLint(input) {
3661
3702
  const readKnowledgeContent = (slug) => {
3662
3703
  const existing = knowledgeContentCache.get(slug);
3663
3704
  if (existing) return existing;
3664
- const knowledgePath = join15(input.vault, "projects", slug, "knowledge.md");
3665
- const pending = existsSync4(knowledgePath) ? readFile12(knowledgePath, "utf8").catch(() => null) : Promise.resolve(null);
3705
+ const knowledgePath = join15(lintVault, "projects", slug, "knowledge.md");
3706
+ const pending = existsSync5(knowledgePath) ? readFile12(knowledgePath, "utf8").catch(() => null) : Promise.resolve(null);
3666
3707
  knowledgeContentCache.set(slug, pending);
3667
3708
  return pending;
3668
3709
  };
@@ -4145,7 +4186,7 @@ ${split.data.body}`;
4145
4186
 
4146
4187
  // src/commands/config.ts
4147
4188
  import { readFile as readFile13 } from "fs/promises";
4148
- import { existsSync as existsSync5 } from "fs";
4189
+ import { existsSync as existsSync6 } from "fs";
4149
4190
  import { join as join16 } from "path";
4150
4191
  function validateKey(key) {
4151
4192
  return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
@@ -4201,11 +4242,11 @@ async function runConfigList(input) {
4201
4242
  }
4202
4243
  async function runConfigPath(input) {
4203
4244
  const filePath = configPath(input.home);
4204
- return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync5(filePath), humanHint: filePath }) };
4245
+ return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync6(filePath), humanHint: filePath }) };
4205
4246
  }
4206
4247
 
4207
4248
  // src/utils/auto-update.ts
4208
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
4249
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
4209
4250
  import { join as join17, dirname as dirname7 } from "path";
4210
4251
  import { spawn } from "child_process";
4211
4252
  function cachePath(home) {
@@ -4213,7 +4254,7 @@ function cachePath(home) {
4213
4254
  }
4214
4255
  function readCacheRaw(home) {
4215
4256
  try {
4216
- const raw = readFileSync3(cachePath(home), "utf8");
4257
+ const raw = readFileSync4(cachePath(home), "utf8");
4217
4258
  return JSON.parse(raw);
4218
4259
  } catch {
4219
4260
  return null;
@@ -4253,7 +4294,7 @@ function triggerAutoUpdate(home, currentVersion) {
4253
4294
  if (!isStale) return;
4254
4295
  const distTag = distTagFromCache(home);
4255
4296
  const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
4256
- if (!existsSync6(bgScript)) return;
4297
+ if (!existsSync7(bgScript)) return;
4257
4298
  const child = spawn(process.execPath, [bgScript, home, currentVersion, distTag], {
4258
4299
  detached: true,
4259
4300
  stdio: "ignore"
@@ -4717,20 +4758,20 @@ function safeUserName() {
4717
4758
  }
4718
4759
 
4719
4760
  // src/commands/doctor.ts
4720
- import { existsSync as existsSync10, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync7 } from "fs";
4761
+ import { existsSync as existsSync11, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync, readFileSync as readFileSync8 } from "fs";
4721
4762
  import { join as join22, resolve as resolve5 } from "path";
4722
4763
  import { execSync as execSync2 } from "child_process";
4723
4764
  import { platform as platform2 } from "os";
4724
4765
 
4725
4766
  // src/utils/plugin-registry.ts
4726
- import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync4 } from "fs";
4767
+ import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync5 } from "fs";
4727
4768
  import { join as join19 } from "path";
4728
4769
  var REGISTRY_PATH = join19(".claude", "plugins", "installed_plugins.json");
4729
4770
  var CODEX_CONFIG_PATH = join19(".codex", "config.toml");
4730
4771
  var PLUGIN_KEY = "skillwiki@llm-wiki";
4731
4772
  function readInstalledPlugins(home) {
4732
4773
  try {
4733
- const raw = readFileSync4(join19(home, REGISTRY_PATH), "utf8");
4774
+ const raw = readFileSync5(join19(home, REGISTRY_PATH), "utf8");
4734
4775
  return JSON.parse(raw);
4735
4776
  } catch {
4736
4777
  return null;
@@ -4767,7 +4808,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
4767
4808
  const config = readCodexPluginConfig(home, key, marketplace);
4768
4809
  if (!config?.enabled) return null;
4769
4810
  const cacheRoot = join19(home, ".codex", "plugins", "cache", marketplace, pluginName);
4770
- if (!existsSync7(cacheRoot)) return null;
4811
+ if (!existsSync8(cacheRoot)) return null;
4771
4812
  let versions;
4772
4813
  try {
4773
4814
  versions = readdirSync(cacheRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -4799,7 +4840,7 @@ function parsePluginKey(key) {
4799
4840
  function readCodexPluginConfig(home, key, marketplace) {
4800
4841
  let raw;
4801
4842
  try {
4802
- raw = readFileSync4(join19(home, CODEX_CONFIG_PATH), "utf8");
4843
+ raw = readFileSync5(join19(home, CODEX_CONFIG_PATH), "utf8");
4803
4844
  } catch {
4804
4845
  return null;
4805
4846
  }
@@ -4841,7 +4882,7 @@ function parseTomlScalar(rawValue) {
4841
4882
  }
4842
4883
 
4843
4884
  // src/utils/satellite-run-health.ts
4844
- import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
4885
+ import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
4845
4886
  import { join as join20 } from "path";
4846
4887
  var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
4847
4888
  function satelliteLatestRunPath(vault) {
@@ -4867,9 +4908,9 @@ function readSatelliteLatestRunFromText(text) {
4867
4908
  }
4868
4909
  function readSatelliteLatestRun(vault) {
4869
4910
  const latestPath = satelliteLatestRunPath(vault);
4870
- if (!existsSync8(latestPath)) return null;
4911
+ if (!existsSync9(latestPath)) return null;
4871
4912
  try {
4872
- return parseLatestRunFile(readFileSync5(latestPath, "utf8"));
4913
+ return parseLatestRunFile(readFileSync6(latestPath, "utf8"));
4873
4914
  } catch {
4874
4915
  return null;
4875
4916
  }
@@ -4898,7 +4939,7 @@ function evaluateSatelliteRunHealth(vault, now) {
4898
4939
  // src/utils/s3-mount-health.ts
4899
4940
  import { execSync } from "child_process";
4900
4941
  import { platform } from "os";
4901
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
4942
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readFileSync as readFile15 } from "fs";
4902
4943
  import { join as join21 } from "path";
4903
4944
  var OS = platform();
4904
4945
  function findRcloneMountPid() {
@@ -4983,7 +5024,7 @@ function extractRcloneFs(args) {
4983
5024
  function getRcloneArgs(pid) {
4984
5025
  try {
4985
5026
  if (OS === "linux") {
4986
- const raw = readFileSync6(`/proc/${pid}/cmdline`);
5027
+ const raw = readFileSync7(`/proc/${pid}/cmdline`);
4987
5028
  return new TextDecoder().decode(raw).split("\0").filter(Boolean);
4988
5029
  } else {
4989
5030
  const out = execSync(`ps -o args= -p ${pid}`, {
@@ -5026,7 +5067,7 @@ function queryRcloneRC(rcAddr, fs) {
5026
5067
  function detectFuseMount(vaultPath) {
5027
5068
  try {
5028
5069
  if (OS === "linux") {
5029
- const mounts = readFileSync6("/proc/mounts", "utf8");
5070
+ const mounts = readFileSync7("/proc/mounts", "utf8");
5030
5071
  let best = null;
5031
5072
  for (const line of mounts.split("\n")) {
5032
5073
  const parts = line.split(" ");
@@ -5158,12 +5199,12 @@ function detectCliChannels(argv, home) {
5158
5199
  const plugin = findPlugin(home);
5159
5200
  if (plugin) {
5160
5201
  const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
5161
- if (existsSync10(pluginBin)) {
5202
+ if (existsSync11(pluginBin)) {
5162
5203
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
5163
5204
  }
5164
5205
  }
5165
5206
  const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
5166
- if (existsSync10(installBin)) {
5207
+ if (existsSync11(installBin)) {
5167
5208
  channels.push({ name: "install", path: installBin, isDevLink: false });
5168
5209
  }
5169
5210
  return channels;
@@ -5224,7 +5265,7 @@ function isDevSourceRun(argv) {
5224
5265
  }
5225
5266
  async function checkConfigFile(home) {
5226
5267
  const cfgPath = configPath(home);
5227
- if (!existsSync10(cfgPath)) {
5268
+ if (!existsSync11(cfgPath)) {
5228
5269
  return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
5229
5270
  }
5230
5271
  try {
@@ -5239,7 +5280,7 @@ function checkWikiPathExists(resolvedPath) {
5239
5280
  if (resolvedPath === void 0) {
5240
5281
  return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
5241
5282
  }
5242
- if (existsSync10(resolvedPath) && statSync(resolvedPath).isDirectory()) {
5283
+ if (existsSync11(resolvedPath) && statSync(resolvedPath).isDirectory()) {
5243
5284
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
5244
5285
  }
5245
5286
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
@@ -5248,13 +5289,13 @@ function checkVaultStructure(resolvedPath) {
5248
5289
  if (resolvedPath === void 0) {
5249
5290
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
5250
5291
  }
5251
- if (!existsSync10(resolvedPath)) {
5292
+ if (!existsSync11(resolvedPath)) {
5252
5293
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
5253
5294
  }
5254
5295
  const missing = [];
5255
- if (!existsSync10(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
5296
+ if (!existsSync11(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
5256
5297
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
5257
- if (!existsSync10(join22(resolvedPath, dir))) missing.push(dir + "/");
5298
+ if (!existsSync11(join22(resolvedPath, dir))) missing.push(dir + "/");
5258
5299
  }
5259
5300
  if (missing.length === 0) {
5260
5301
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -5263,7 +5304,7 @@ function checkVaultStructure(resolvedPath) {
5263
5304
  }
5264
5305
  function checkSkillsInstalled(home, cwd) {
5265
5306
  const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
5266
- if (srcDir && existsSync10(srcDir)) {
5307
+ if (srcDir && existsSync11(srcDir)) {
5267
5308
  const found = findInstalledSkillMd(srcDir);
5268
5309
  if (found.length > 0) {
5269
5310
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
@@ -5277,7 +5318,7 @@ function checkSkillsInstalled(home, cwd) {
5277
5318
  }
5278
5319
  }
5279
5320
  const skillsDir = join22(home, ".claude", "skills");
5280
- if (existsSync10(skillsDir)) {
5321
+ if (existsSync11(skillsDir)) {
5281
5322
  const found = findInstalledSkillMd(skillsDir);
5282
5323
  if (found.length > 0) {
5283
5324
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
@@ -5394,7 +5435,7 @@ async function checkProfiles(home) {
5394
5435
  async function checkProjectLocalOverride(cwd) {
5395
5436
  const dir = cwd ?? process.cwd();
5396
5437
  const envPath = join22(dir, ".skillwiki", ".env");
5397
- if (existsSync10(envPath)) {
5438
+ if (existsSync11(envPath)) {
5398
5439
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
5399
5440
  }
5400
5441
  return check("pass", "project_local", "Project-local config", "None");
@@ -5403,7 +5444,7 @@ function checkVaultGitRemote(resolvedPath) {
5403
5444
  if (resolvedPath === void 0) {
5404
5445
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
5405
5446
  }
5406
- if (!existsSync10(join22(resolvedPath, ".git"))) {
5447
+ if (!existsSync11(join22(resolvedPath, ".git"))) {
5407
5448
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
5408
5449
  }
5409
5450
  try {
@@ -5426,9 +5467,9 @@ function checkObsidianTemplates(resolvedPath) {
5426
5467
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
5427
5468
  }
5428
5469
  const missing = [];
5429
- if (!existsSync10(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
5430
- if (!existsSync10(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5431
- if (!existsSync10(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5470
+ if (!existsSync11(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
5471
+ if (!existsSync11(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5472
+ if (!existsSync11(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5432
5473
  if (missing.length === 0) {
5433
5474
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
5434
5475
  }
@@ -5439,7 +5480,7 @@ function checkDotStoreClean(resolvedPath) {
5439
5480
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
5440
5481
  }
5441
5482
  const rawDir = join22(resolvedPath, "raw");
5442
- if (!existsSync10(rawDir)) {
5483
+ if (!existsSync11(rawDir)) {
5443
5484
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
5444
5485
  }
5445
5486
  const found = [];
@@ -5467,7 +5508,7 @@ function checkSyncLastPush(resolvedPath) {
5467
5508
  if (resolvedPath === void 0) {
5468
5509
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
5469
5510
  }
5470
- if (!existsSync10(join22(resolvedPath, ".git"))) {
5511
+ if (!existsSync11(join22(resolvedPath, ".git"))) {
5471
5512
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
5472
5513
  }
5473
5514
  let timestamp;
@@ -5515,7 +5556,7 @@ function checkVaultGitDirty(resolvedPath) {
5515
5556
  if (resolvedPath === void 0) {
5516
5557
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
5517
5558
  }
5518
- if (!existsSync10(join22(resolvedPath, ".git"))) {
5559
+ if (!existsSync11(join22(resolvedPath, ".git"))) {
5519
5560
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
5520
5561
  }
5521
5562
  try {
@@ -5583,7 +5624,7 @@ function remoteMainHash(resolvedPath) {
5583
5624
  }
5584
5625
  function checkStaleRemoteMain(resolvedPath) {
5585
5626
  if (resolvedPath === void 0) return void 0;
5586
- if (!existsSync10(join22(resolvedPath, ".git"))) return void 0;
5627
+ if (!existsSync11(join22(resolvedPath, ".git"))) return void 0;
5587
5628
  const localOrigin = gitRefHash(resolvedPath, "origin/main");
5588
5629
  if (!localOrigin) return void 0;
5589
5630
  const remoteMain = remoteMainHash(resolvedPath);
@@ -5599,7 +5640,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
5599
5640
  if (resolvedPath === void 0) {
5600
5641
  return check("pass", id, label, "No vault path \u2014 check skipped");
5601
5642
  }
5602
- if (!existsSync10(join22(resolvedPath, ".git"))) {
5643
+ if (!existsSync11(join22(resolvedPath, ".git"))) {
5603
5644
  return check("pass", id, label, "No git repo \u2014 check skipped");
5604
5645
  }
5605
5646
  if (!hasOriginMain(resolvedPath)) {
@@ -5627,7 +5668,7 @@ function checkSatelliteLastRun(vaultPath, satelliteExpected) {
5627
5668
  return check("pass", "satellite_job_last_run", "Satellite job last run", "No vault path \u2014 check skipped");
5628
5669
  }
5629
5670
  const latestPath = satelliteLatestRunPath(vaultPath);
5630
- if (!existsSync10(latestPath)) {
5671
+ if (!existsSync11(latestPath)) {
5631
5672
  return check("pass", "satellite_job_last_run", "Satellite job last run", "No latest-run.json \u2014 satellite has not run yet");
5632
5673
  }
5633
5674
  try {
@@ -5731,12 +5772,12 @@ function isRecentLogLine(line, nowMs) {
5731
5772
  return nowMs - ts <= 24 * 60 * 60 * 1e3;
5732
5773
  }
5733
5774
  function checkVaultGitPullFailures(home) {
5734
- const path = pullLogPaths(home).find((p) => existsSync10(p));
5775
+ const path = pullLogPaths(home).find((p) => existsSync11(p));
5735
5776
  if (!path) {
5736
5777
  return check("pass", "vault_git_pull_failures", "Vault pull failures", "No wiki-pull.log found \u2014 check skipped");
5737
5778
  }
5738
5779
  try {
5739
- const lines = readFileSync7(path, "utf8").split(/\r?\n/).filter(Boolean);
5780
+ const lines = readFileSync8(path, "utf8").split(/\r?\n/).filter(Boolean);
5740
5781
  const now = Date.now();
5741
5782
  const failures = lines.filter(
5742
5783
  (line) => isRecentLogLine(line, now) && /(pre-push pull failed|FAIL .*pull|FAIL .*rebase|cannot pull with rebase|unstaged changes)/i.test(line)
@@ -5760,7 +5801,7 @@ function checkS3MountPerf(resolvedPath) {
5760
5801
  }
5761
5802
  const mountPoint = fuse.mountPoint;
5762
5803
  const conceptsDir = join22(resolvedPath, "concepts");
5763
- if (!existsSync10(conceptsDir)) {
5804
+ if (!existsSync11(conceptsDir)) {
5764
5805
  return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
5765
5806
  }
5766
5807
  const start = Date.now();
@@ -5943,7 +5984,7 @@ function checkWriteTest(resolvedPath) {
5943
5984
  return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
5944
5985
  }
5945
5986
  const conceptsDir = join22(resolvedPath, "concepts");
5946
- if (!existsSync10(conceptsDir)) {
5987
+ if (!existsSync11(conceptsDir)) {
5947
5988
  return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
5948
5989
  }
5949
5990
  const result = writeTest(conceptsDir);
@@ -6029,7 +6070,7 @@ function checkVfsCacheHealth(resolvedPath) {
6029
6070
  }
6030
6071
  function readVaultSyncConfig(home) {
6031
6072
  try {
6032
- const content = readFileSync7(join22(home, ".skillwiki", ".env"), "utf8");
6073
+ const content = readFileSync8(join22(home, ".skillwiki", ".env"), "utf8");
6033
6074
  let installed = false;
6034
6075
  let role;
6035
6076
  let serviceScope;
@@ -6058,7 +6099,7 @@ function readVaultSyncConfig(home) {
6058
6099
  }
6059
6100
  function readKeyFromEnvFile(path, keys) {
6060
6101
  try {
6061
- const content = readFileSync7(path, "utf8");
6102
+ const content = readFileSync8(path, "utf8");
6062
6103
  for (const line of content.split(/\r?\n/)) {
6063
6104
  const trimmed = line.trim();
6064
6105
  if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
@@ -6080,7 +6121,7 @@ function resolveSnapshotGitWorktree(config) {
6080
6121
  if (fromProfile) return fromProfile;
6081
6122
  }
6082
6123
  const defaultPath = "/root/wiki-git";
6083
- return existsSync10(defaultPath) ? defaultPath : void 0;
6124
+ return existsSync11(defaultPath) ? defaultPath : void 0;
6084
6125
  }
6085
6126
  function vaultSyncChecks(input) {
6086
6127
  const os = input.os ?? platform2();
@@ -6102,11 +6143,11 @@ function vaultSyncChecks(input) {
6102
6143
  const filterPath = input.filterPath ?? join22(home, ".config", "rclone", "wiki-push-filters.txt");
6103
6144
  const packagedSnapshotPath = join22(shareDir, "wiki-snapshot.sh");
6104
6145
  const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
6105
- const snapshotPath = input.snapshotScriptPath ?? (existsSync10(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
6146
+ const snapshotPath = input.snapshotScriptPath ?? (existsSync11(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
6106
6147
  function snapshotLastStatusCheck() {
6107
6148
  const snapshotLog = join22(logDir, "wiki-snapshot.log");
6108
6149
  try {
6109
- const logContent = readFileSync7(snapshotLog, "utf8");
6150
+ const logContent = readFileSync8(snapshotLog, "utf8");
6110
6151
  const lines = logContent.trim().split("\n").filter(Boolean);
6111
6152
  if (lines.length === 0) {
6112
6153
  return check(
@@ -6151,14 +6192,14 @@ function vaultSyncChecks(input) {
6151
6192
  }
6152
6193
  }
6153
6194
  if (input.vaultSyncRole === "snapshotter") {
6154
- 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}`);
6195
+ 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
6196
  const serviceScope = input.vaultSyncServiceScope ?? "user";
6156
6197
  const userTimerPath = join22(home, ".config", "systemd", "user", "wiki-snapshot.timer");
6157
6198
  const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
6158
6199
  let c22;
6159
- if (serviceScope === "user" && existsSync10(userTimerPath)) {
6200
+ if (serviceScope === "user" && existsSync11(userTimerPath)) {
6160
6201
  c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${userTimerPath}`);
6161
- } else if (serviceScope === "system" && existsSync10(systemTimerPath)) {
6202
+ } else if (serviceScope === "system" && existsSync11(systemTimerPath)) {
6162
6203
  c22 = check("pass", "vault_sync_jobs_enabled", "Vault sync jobs enabled", `Found: ${systemTimerPath}`);
6163
6204
  } else if (os !== "linux") {
6164
6205
  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 +6231,7 @@ function vaultSyncChecks(input) {
6190
6231
  );
6191
6232
  let c52;
6192
6233
  try {
6193
- if (!existsSync10(snapshotPath)) {
6234
+ if (!existsSync11(snapshotPath)) {
6194
6235
  c52 = check(
6195
6236
  "error",
6196
6237
  "vault_sync_snapshot_guard",
@@ -6198,7 +6239,7 @@ function vaultSyncChecks(input) {
6198
6239
  `Snapshot script not found at ${snapshotPath}`
6199
6240
  );
6200
6241
  } else {
6201
- const content = readFileSync7(snapshotPath, "utf8");
6242
+ const content = readFileSync8(snapshotPath, "utf8");
6202
6243
  if (!content.includes("--max-delete")) {
6203
6244
  c52 = check(
6204
6245
  "error",
@@ -6226,7 +6267,7 @@ function vaultSyncChecks(input) {
6226
6267
  return [c12, c22, c32, cFetch2, c42, c52];
6227
6268
  }
6228
6269
  const pushScriptPath = join22(shareDir, "wiki-push.sh");
6229
- 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`);
6270
+ 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
6271
  let c2;
6231
6272
  try {
6232
6273
  if (isMac) {
@@ -6280,7 +6321,7 @@ function vaultSyncChecks(input) {
6280
6321
  const logFile = join22(logDir, "wiki-push.log");
6281
6322
  let c3;
6282
6323
  try {
6283
- const logContent = readFileSync7(logFile, "utf8");
6324
+ const logContent = readFileSync8(logFile, "utf8");
6284
6325
  const lines = logContent.trim().split("\n").filter(Boolean);
6285
6326
  if (lines.length === 0) {
6286
6327
  c3 = check(
@@ -6336,7 +6377,7 @@ function vaultSyncChecks(input) {
6336
6377
  }
6337
6378
  }
6338
6379
  } catch {
6339
- c3 = existsSync10(logDir) ? check(
6380
+ c3 = existsSync11(logDir) ? check(
6340
6381
  "warn",
6341
6382
  "vault_sync_last_push_age",
6342
6383
  "Vault sync last push recency",
@@ -6351,7 +6392,7 @@ function vaultSyncChecks(input) {
6351
6392
  const fetchLogFile = join22(logDir, "wiki-fetch.log");
6352
6393
  let cFetch;
6353
6394
  try {
6354
- const logContent = readFileSync7(fetchLogFile, "utf8");
6395
+ const logContent = readFileSync8(fetchLogFile, "utf8");
6355
6396
  const lines = logContent.trim().split("\n").filter(Boolean);
6356
6397
  if (lines.length === 0) {
6357
6398
  cFetch = check(
@@ -6395,7 +6436,7 @@ function vaultSyncChecks(input) {
6395
6436
  }
6396
6437
  let c4;
6397
6438
  try {
6398
- if (!existsSync10(filterPath)) {
6439
+ if (!existsSync11(filterPath)) {
6399
6440
  c4 = check(
6400
6441
  "error",
6401
6442
  "vault_sync_filter_present",
@@ -6403,7 +6444,7 @@ function vaultSyncChecks(input) {
6403
6444
  `Filter file not found at ${filterPath}`
6404
6445
  );
6405
6446
  } else {
6406
- const content = readFileSync7(filterPath, "utf8");
6447
+ const content = readFileSync8(filterPath, "utf8");
6407
6448
  const requiredExcludes = [
6408
6449
  "remotely-save/data.json",
6409
6450
  ".skillwiki/sync.lock",
@@ -6446,7 +6487,7 @@ function vaultSyncChecks(input) {
6446
6487
  );
6447
6488
  } else {
6448
6489
  try {
6449
- if (!existsSync10(snapshotPath)) {
6490
+ if (!existsSync11(snapshotPath)) {
6450
6491
  c5 = check(
6451
6492
  "error",
6452
6493
  "vault_sync_snapshot_guard",
@@ -6454,7 +6495,7 @@ function vaultSyncChecks(input) {
6454
6495
  `Snapshot script not found at ${snapshotPath}`
6455
6496
  );
6456
6497
  } else {
6457
- const content = readFileSync7(snapshotPath, "utf8");
6498
+ const content = readFileSync8(snapshotPath, "utf8");
6458
6499
  if (!content.includes("--max-delete")) {
6459
6500
  c5 = check(
6460
6501
  "error",
@@ -6512,7 +6553,7 @@ function findSkillNames(dir) {
6512
6553
  return results;
6513
6554
  }
6514
6555
  for (const entry of entries) {
6515
- if (entry.isDirectory() && existsSync10(join22(dir, entry.name, "SKILL.md"))) {
6556
+ if (entry.isDirectory() && existsSync11(join22(dir, entry.name, "SKILL.md"))) {
6516
6557
  results.push(entry.name);
6517
6558
  }
6518
6559
  }
@@ -6556,7 +6597,7 @@ async function vaultMetrics(resolvedPath) {
6556
6597
  }
6557
6598
  let logLines = 0;
6558
6599
  try {
6559
- logLines = readFileSync7(join22(resolvedPath, "log.md"), "utf8").split("\n").length;
6600
+ logLines = readFileSync8(join22(resolvedPath, "log.md"), "utf8").split("\n").length;
6560
6601
  } catch {
6561
6602
  }
6562
6603
  return [
@@ -6658,7 +6699,7 @@ async function runDoctor(input) {
6658
6699
  }
6659
6700
 
6660
6701
  // src/utils/package-info.ts
6661
- import { readFileSync as readFileSync8 } from "fs";
6702
+ import { readFileSync as readFileSync9 } from "fs";
6662
6703
  function packageJsonCandidateUrls(baseUrl = import.meta.url) {
6663
6704
  return [
6664
6705
  new URL("../package.json", baseUrl),
@@ -6668,7 +6709,7 @@ function packageJsonCandidateUrls(baseUrl = import.meta.url) {
6668
6709
  function readCliPackageJson(baseUrl = import.meta.url) {
6669
6710
  for (const url of packageJsonCandidateUrls(baseUrl)) {
6670
6711
  try {
6671
- const pkg = JSON.parse(readFileSync8(url, "utf8"));
6712
+ const pkg = JSON.parse(readFileSync9(url, "utf8"));
6672
6713
  if (typeof pkg.version === "string") {
6673
6714
  return { ...pkg, version: pkg.version };
6674
6715
  }
@@ -6824,7 +6865,7 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
6824
6865
 
6825
6866
  // src/commands/observe.ts
6826
6867
  import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
6827
- import { existsSync as existsSync11, statSync as statSync2 } from "fs";
6868
+ import { existsSync as existsSync12, statSync as statSync2 } from "fs";
6828
6869
  import { join as join24 } from "path";
6829
6870
  import { createHash as createHash5 } from "crypto";
6830
6871
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
@@ -6848,7 +6889,7 @@ async function runObserve(input) {
6848
6889
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
6849
6890
  };
6850
6891
  }
6851
- if (!existsSync11(input.vault) || !statSync2(input.vault).isDirectory()) {
6892
+ if (!existsSync12(input.vault) || !statSync2(input.vault).isDirectory()) {
6852
6893
  return {
6853
6894
  exitCode: ExitCode.VAULT_PATH_INVALID,
6854
6895
  result: err("VAULT_PATH_INVALID", { path: input.vault })
@@ -8522,7 +8563,7 @@ async function fetchQueryPreview(input) {
8522
8563
  // src/mcp/graph-html.ts
8523
8564
  import { readFile as readFile19 } from "fs/promises";
8524
8565
  import { join as join29 } from "path";
8525
- import { existsSync as existsSync12 } from "fs";
8566
+ import { existsSync as existsSync13 } from "fs";
8526
8567
  var TYPE_COLORS = {
8527
8568
  entities: "#e74c3c",
8528
8569
  concepts: "#27ae60",
@@ -8592,7 +8633,7 @@ ${nodeSvg}
8592
8633
  async function fetchGraphHtmlReport(input) {
8593
8634
  const graphPath = input.graphPath ?? join29(input.vault, ".skillwiki", "graph.json");
8594
8635
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
8595
- if (!existsSync12(graphPath)) {
8636
+ if (!existsSync13(graphPath)) {
8596
8637
  return {
8597
8638
  exitCode: ExitCode.FILE_NOT_FOUND,
8598
8639
  result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
package/dist/cli.js CHANGED
@@ -75,7 +75,7 @@ import {
75
75
  triggerAutoUpdate,
76
76
  writeCache,
77
77
  writeDotenv
78
- } from "./chunk-MI4BZI7Q.js";
78
+ } from "./chunk-4Q7MANQI.js";
79
79
  import {
80
80
  normalizeDistTag
81
81
  } from "./chunk-E6UWZ3S3.js";
@@ -321,6 +321,8 @@ async function runInstall(input) {
321
321
  const hintLines2 = [
322
322
  `deferred to plugin: skillwiki@llm-wiki v${plugin.version}`,
323
323
  `plugin provides skills at ${plugin.installPath}`,
324
+ "Plugin-managed skills are not refreshed with `skillwiki install`.",
325
+ "Do not run `skillwiki install` just to refresh plugin-managed skills; update the active plugin channel instead.",
324
326
  `use --force to install CLI copies into ${input.target} anyway`
325
327
  ];
326
328
  return {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-MI4BZI7Q.js";
4
+ } from "./chunk-4Q7MANQI.js";
5
5
  import "./chunk-E6UWZ3S3.js";
6
6
 
7
7
  // src/mcp-entry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.47",
3
+ "version": "0.9.49",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.47",
3
+ "version": "0.9.49",
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.47",
3
+ "version": "0.9.49",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 18 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.9.47",
3
+ "version": "0.9.49",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -172,11 +172,13 @@ skillwiki has multiple distribution channels that can drift:
172
172
  | Channel | Location | Update Command |
173
173
  |---------|----------|----------------|
174
174
  | npm CLI | `/usr/local/bin/skillwiki` | `npm install -g skillwiki@latest` |
175
- | npm skills | `/usr/local/lib/node_modules/skillwiki/skills/` | `skillwiki install` (copies to `~/.claude/skills/`) |
175
+ | npm skills | `/usr/local/lib/node_modules/skillwiki/skills/` | `skillwiki install` only for standalone CLI skill copies; defers when the plugin channel is active |
176
176
  | Claude plugin | `~/.claude/plugins/cache/llm-wiki/` | `claude plugin update skillwiki@llm-wiki` |
177
177
  | Codex plugin | `~/.codex/plugins/cache/llm-wiki/` | `codex plugin marketplace upgrade llm-wiki`, then reinstall or restart Codex as needed |
178
178
  | Local git dev | source repo checkout | `npm link ./packages/cli` (from repo root) |
179
179
  **Check versions:** `skillwiki doctor` reports Plugin/CLI version mismatch warnings when installed channels disagree.
180
+ **Plugin channel rule:** Plugin-managed skills are not refreshed with `skillwiki install`. When Claude or Codex plugin is installed and enabled, the plugin cache is the skill provider; `skillwiki install` is only a legacy/standalone copier for `~/.claude/skills/`.
181
+ **Agent update rule:** Do not run `skillwiki install` just to refresh plugin-managed skills. If `skillwiki install` reports `deferred_to_plugin: true`, stop there and update the active plugin channel instead: Claude uses `claude plugin update skillwiki@llm-wiki`; Codex uses `codex plugin marketplace upgrade llm-wiki`, then reinstall or restart Codex as needed. Only use `skillwiki install --force` when the user explicitly wants duplicate CLI-managed copies under `~/.claude/skills/` and accepts that `skillwiki doctor` may report overlap.
180
182
  **Authoring rule:** `SKILL.md` frontmatter follows the Agent Skills schema: top-level `name` and `description` plus optional schema fields such as `metadata`. Do not put release version fields at the top level of `SKILL.md`; plugin and package release versions live in `plugin.json` and `package.json`.
181
183
  **Fix:** If developing locally, use the repo source plus `npm link`. If using released versions, update the relevant plugin or npm channel; do not infer release freshness from `SKILL.md` frontmatter.
182
184
 
@@ -172,11 +172,13 @@ skillwiki has multiple distribution channels that can drift:
172
172
  | Channel | Location | Update Command |
173
173
  |---------|----------|----------------|
174
174
  | npm CLI | `/usr/local/bin/skillwiki` | `npm install -g skillwiki@latest` |
175
- | npm skills | `/usr/local/lib/node_modules/skillwiki/skills/` | `skillwiki install` (copies to `~/.claude/skills/`) |
175
+ | npm skills | `/usr/local/lib/node_modules/skillwiki/skills/` | `skillwiki install` only for standalone CLI skill copies; defers when the plugin channel is active |
176
176
  | Claude plugin | `~/.claude/plugins/cache/llm-wiki/` | `claude plugin update skillwiki@llm-wiki` |
177
177
  | Codex plugin | `~/.codex/plugins/cache/llm-wiki/` | `codex plugin marketplace upgrade llm-wiki`, then reinstall or restart Codex as needed |
178
178
  | Local git dev | source repo checkout | `npm link ./packages/cli` (from repo root) |
179
179
  **Check versions:** `skillwiki doctor` reports Plugin/CLI version mismatch warnings when installed channels disagree.
180
+ **Plugin channel rule:** Plugin-managed skills are not refreshed with `skillwiki install`. When Claude or Codex plugin is installed and enabled, the plugin cache is the skill provider; `skillwiki install` is only a legacy/standalone copier for `~/.claude/skills/`.
181
+ **Agent update rule:** Do not run `skillwiki install` just to refresh plugin-managed skills. If `skillwiki install` reports `deferred_to_plugin: true`, stop there and update the active plugin channel instead: Claude uses `claude plugin update skillwiki@llm-wiki`; Codex uses `codex plugin marketplace upgrade llm-wiki`, then reinstall or restart Codex as needed. Only use `skillwiki install --force` when the user explicitly wants duplicate CLI-managed copies under `~/.claude/skills/` and accepts that `skillwiki doctor` may report overlap.
180
182
  **Authoring rule:** `SKILL.md` frontmatter follows the Agent Skills schema: top-level `name` and `description` plus optional schema fields such as `metadata`. Do not put release version fields at the top level of `SKILL.md`; plugin and package release versions live in `plugin.json` and `package.json`.
181
183
  **Fix:** If developing locally, use the repo source plus `npm link`. If using released versions, update the relevant plugin or npm channel; do not infer release freshness from `SKILL.md` frontmatter.
182
184