skillwiki 0.10.7 → 0.10.8

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.
@@ -32,7 +32,7 @@ import {
32
32
  satelliteGateFromFleetLoad,
33
33
  snapshotterAliasForLocalHost,
34
34
  writeDotenv
35
- } from "./chunk-R6BKJWVC.js";
35
+ } from "./chunk-TYN2IHBY.js";
36
36
  import {
37
37
  CompoundSchema,
38
38
  ExitCode,
@@ -47,8 +47,9 @@ import {
47
47
  } from "./chunk-C5OLZRRM.js";
48
48
 
49
49
  // src/commands/log-append.ts
50
- import { readFile, stat } from "fs/promises";
51
- import { join as join3 } from "path";
50
+ import { readFile as readFile2, stat } from "fs/promises";
51
+ import { hostname } from "os";
52
+ import { join as join4 } from "path";
52
53
 
53
54
  // src/utils/last-op.ts
54
55
  import { readFileSync, writeFileSync, mkdirSync, unlinkSync, existsSync } from "fs";
@@ -159,36 +160,187 @@ function releaseLogLock(handle) {
159
160
  }
160
161
  }
161
162
 
163
+ // src/utils/log-events.ts
164
+ import { mkdir, open, readFile, readdir } from "fs/promises";
165
+ import { join as join3 } from "path";
166
+ function isPlainObject(value) {
167
+ return typeof value === "object" && value !== null && !Array.isArray(value);
168
+ }
169
+ function canonicalize(value) {
170
+ if (Array.isArray(value)) return value.map(canonicalize);
171
+ if (!isPlainObject(value)) return value;
172
+ const out = {};
173
+ for (const key of Object.keys(value).sort()) {
174
+ out[key] = canonicalize(value[key]);
175
+ }
176
+ return out;
177
+ }
178
+ function eventPathFor(event) {
179
+ const day = event.occurred_at.slice(0, 10);
180
+ return `meta/log-events/${day}/${event.operation_id}.json`;
181
+ }
182
+ function validateLogEvent(event) {
183
+ if (event.schema !== "skillwiki-log-event/v1") {
184
+ return err("SCHEME_REJECTED", { message: "invalid event schema" });
185
+ }
186
+ if (!/^[0-9a-f]{64}$/.test(event.operation_id)) {
187
+ return err("SCHEME_REJECTED", { message: "operation_id must be 64 hex chars" });
188
+ }
189
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(event.occurred_at)) {
190
+ return err("SCHEME_REJECTED", { message: "occurred_at must be UTC ISO with milliseconds" });
191
+ }
192
+ for (const field of ["host_id", "actor", "kind", "target", "note"]) {
193
+ const v = event[field];
194
+ if (typeof v !== "string" || v.trim().length === 0 || v.length > 500) {
195
+ return err("SCHEME_REJECTED", { message: `invalid ${field}` });
196
+ }
197
+ }
198
+ if (!isPlainObject(event.metadata)) {
199
+ return err("SCHEME_REJECTED", { message: "metadata must be a plain object" });
200
+ }
201
+ const sensitive = scanSensitiveContent(JSON.stringify(event));
202
+ if (sensitive.length > 0) {
203
+ return err("SENSITIVE_CONTENT_DETECTED", { findings: sensitive });
204
+ }
205
+ return ok({
206
+ schema: "skillwiki-log-event/v1",
207
+ operation_id: event.operation_id,
208
+ occurred_at: event.occurred_at,
209
+ host_id: event.host_id,
210
+ actor: event.actor,
211
+ kind: event.kind,
212
+ target: event.target,
213
+ note: event.note,
214
+ metadata: canonicalize(event.metadata)
215
+ });
216
+ }
217
+ function canonicalEventJson(event) {
218
+ const ordered = {
219
+ schema: event.schema,
220
+ operation_id: event.operation_id,
221
+ occurred_at: event.occurred_at,
222
+ host_id: event.host_id,
223
+ actor: event.actor,
224
+ kind: event.kind,
225
+ target: event.target,
226
+ note: event.note,
227
+ metadata: event.metadata
228
+ };
229
+ return `${JSON.stringify(ordered, null, 2)}
230
+ `;
231
+ }
232
+ async function writeLogEvent(vault, event) {
233
+ const validated = validateLogEvent(event);
234
+ if (!validated.ok) return validated;
235
+ const rel = eventPathFor(validated.data);
236
+ const abs = join3(vault, rel);
237
+ await mkdir(join3(vault, "meta", "log-events", validated.data.occurred_at.slice(0, 10)), {
238
+ recursive: true
239
+ });
240
+ const body = canonicalEventJson(validated.data);
241
+ try {
242
+ const handle = await open(abs, "wx");
243
+ try {
244
+ await handle.writeFile(body, "utf8");
245
+ } finally {
246
+ await handle.close();
247
+ }
248
+ return ok({ path: rel, created: true });
249
+ } catch (error) {
250
+ if (error.code === "EEXIST") {
251
+ let existing;
252
+ try {
253
+ existing = await readFile(abs, "utf8");
254
+ } catch (readErr) {
255
+ return err("WRITE_FAILED", { path: rel, message: String(readErr) });
256
+ }
257
+ if (existing === body) return ok({ path: rel, created: false });
258
+ return err("EVENT_IDENTITY_COLLISION", { path: rel });
259
+ }
260
+ return err("WRITE_FAILED", { path: rel, message: String(error) });
261
+ }
262
+ }
263
+ async function readLogEvents(vault) {
264
+ const root = join3(vault, "meta", "log-events");
265
+ let days;
266
+ try {
267
+ days = (await readdir(root, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name).sort();
268
+ } catch {
269
+ return ok([]);
270
+ }
271
+ const events = [];
272
+ for (const day of days) {
273
+ const files = (await readdir(join3(root, day))).filter((f) => f.endsWith(".json")).sort();
274
+ for (const file of files) {
275
+ const text = await readFile(join3(root, day, file), "utf8");
276
+ let parsed;
277
+ try {
278
+ parsed = JSON.parse(text);
279
+ } catch {
280
+ return err("SCHEME_REJECTED", { path: `meta/log-events/${day}/${file}`, message: "invalid JSON" });
281
+ }
282
+ const validated = validateLogEvent(parsed);
283
+ if (!validated.ok) return validated;
284
+ if (eventPathFor(validated.data) !== `meta/log-events/${day}/${file}`) {
285
+ return err("SCHEME_REJECTED", {
286
+ path: `meta/log-events/${day}/${file}`,
287
+ message: "path/identity mismatch"
288
+ });
289
+ }
290
+ events.push(validated.data);
291
+ }
292
+ }
293
+ events.sort((a, b) => {
294
+ if (a.occurred_at !== b.occurred_at) return a.occurred_at < b.occurred_at ? -1 : 1;
295
+ return a.operation_id < b.operation_id ? -1 : a.operation_id > b.operation_id ? 1 : 0;
296
+ });
297
+ return ok(events);
298
+ }
299
+
162
300
  // src/commands/log-append.ts
163
301
  var ENTRY_RE = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
164
- function operationMarker(operationId) {
302
+ function countLogEntries(logText) {
303
+ let count = 0;
304
+ for (const _ of logText.matchAll(ENTRY_RE)) count += 1;
305
+ return count;
306
+ }
307
+ function operationMarkers(operationId) {
165
308
  if (!/^[0-9a-f]{64}$/.test(operationId)) {
166
309
  return err("USAGE", { message: "operationId must be a SHA-256 hex string" });
167
310
  }
168
- return ok(`<!-- skillwiki-page-publish:${operationId} -->`);
311
+ return ok([
312
+ `<!-- skillwiki-log-op:${operationId} -->`,
313
+ `<!-- skillwiki-page-publish:${operationId} -->`
314
+ ]);
169
315
  }
170
- async function appendWhileLocked(logPath, content, marker) {
316
+ function preferredMarker(operationId, eventKind) {
317
+ if (!eventKind || eventKind === "page-publish") {
318
+ return `<!-- skillwiki-page-publish:${operationId} -->`;
319
+ }
320
+ return `<!-- skillwiki-log-op:${operationId} -->`;
321
+ }
322
+ async function appendWhileLocked(logPath, content, markers, writeMarker) {
171
323
  let logText;
172
324
  try {
173
- logText = await readFile(logPath, "utf8");
325
+ logText = await readFile2(logPath, "utf8");
174
326
  } catch {
175
327
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
176
328
  }
177
- const entriesBefore = [...logText.matchAll(ENTRY_RE)].length;
178
- if (marker && logText.includes(marker)) {
329
+ const entriesBefore = countLogEntries(logText);
330
+ if (markers?.some((marker) => logText.includes(marker))) {
179
331
  return {
180
332
  exitCode: ExitCode.OK,
181
333
  result: ok({
182
334
  entries_before: entriesBefore,
183
335
  entries_after: entriesBefore,
184
336
  appended: false,
185
- humanHint: `publication operation already appended (${entriesBefore} entries)`
337
+ humanHint: `operation already appended (${entriesBefore} entries)`
186
338
  })
187
339
  };
188
340
  }
189
341
  const body = logText.replace(/\s+$/, "");
190
- const appendedContent = marker ? `${content}
191
- ${marker}` : content;
342
+ const appendedContent = writeMarker ? `${content}
343
+ ${writeMarker}` : content;
192
344
  const written = await atomicWriteText(logPath, `${body}
193
345
 
194
346
  ${appendedContent}
@@ -209,7 +361,7 @@ ${appendedContent}
209
361
  }
210
362
  async function runLogAppend(input) {
211
363
  try {
212
- await stat(join3(input.vault, "SCHEMA.md"));
364
+ await stat(join4(input.vault, "SCHEMA.md"));
213
365
  } catch {
214
366
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
215
367
  }
@@ -224,11 +376,55 @@ async function runLogAppend(input) {
224
376
  result: err("SENSITIVE_CONTENT_DETECTED", { file: "log.md", findings: sensitive })
225
377
  };
226
378
  }
227
- let marker;
379
+ let markers;
380
+ let writeMarker;
228
381
  if (input.operationId !== void 0) {
229
- const operation = operationMarker(input.operationId);
382
+ const operation = operationMarkers(input.operationId);
230
383
  if (!operation.ok) return { exitCode: ExitCode.USAGE, result: operation };
231
- marker = operation.data;
384
+ markers = operation.data;
385
+ writeMarker = preferredMarker(input.operationId, input.eventKind);
386
+ }
387
+ let eventCreated;
388
+ let eventPath;
389
+ if (input.writeEvent === true && input.operationId) {
390
+ const day = input.eventMetadata?.day || content.match(/\[(\d{4}-\d{2}-\d{2})\]/)?.[1] || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
391
+ const event = await writeLogEvent(input.vault, {
392
+ schema: "skillwiki-log-event/v1",
393
+ operation_id: input.operationId,
394
+ occurred_at: `${day}T00:00:00.000Z`,
395
+ host_id: hostname() || "localhost",
396
+ actor: "skillwiki-cli",
397
+ kind: input.eventKind || "log-append",
398
+ target: input.eventTarget || "log.md",
399
+ note: input.eventNote || content.split("\n")[0].slice(0, 500),
400
+ metadata: {
401
+ ...input.eventMetadata || {}
402
+ }
403
+ });
404
+ if (!event.ok) {
405
+ if (event.error === "EVENT_IDENTITY_COLLISION") {
406
+ eventCreated = false;
407
+ eventPath = eventPathFor({
408
+ schema: "skillwiki-log-event/v1",
409
+ operation_id: input.operationId,
410
+ occurred_at: `${day}T00:00:00.000Z`,
411
+ host_id: "localhost",
412
+ actor: "skillwiki-cli",
413
+ kind: input.eventKind || "log-append",
414
+ target: input.eventTarget || "log.md",
415
+ note: "collision",
416
+ metadata: {}
417
+ });
418
+ } else {
419
+ return {
420
+ exitCode: event.error === "SENSITIVE_CONTENT_DETECTED" ? ExitCode.SENSITIVE_CONTENT_DETECTED : ExitCode.WRITE_FAILED,
421
+ result: event
422
+ };
423
+ }
424
+ } else {
425
+ eventCreated = event.data.created;
426
+ eventPath = event.data.path;
427
+ }
232
428
  }
233
429
  const acquired = await acquireLogLock(input.vault, input.strictLock ? { reclaimStale: false } : {});
234
430
  if (!acquired.ok) {
@@ -238,11 +434,11 @@ async function runLogAppend(input) {
238
434
  return { exitCode: ExitCode.LOG_APPEND_LOCK_HELD, result: err("LOG_APPEND_LOCK_HELD", { vault: input.vault }) };
239
435
  }
240
436
  const lockHandle = acquired.data;
241
- const logPath = join3(input.vault, "log.md");
437
+ const logPath = join4(input.vault, "log.md");
242
438
  let outcome;
243
439
  let released;
244
440
  try {
245
- outcome = await appendWhileLocked(logPath, content, marker);
441
+ outcome = await appendWhileLocked(logPath, content, markers, writeMarker);
246
442
  } catch (error) {
247
443
  outcome = {
248
444
  exitCode: ExitCode.WRITE_FAILED,
@@ -257,10 +453,16 @@ async function runLogAppend(input) {
257
453
  result: err("WRITE_FAILED", { stage: "log-unlock" })
258
454
  };
259
455
  }
260
- if (outcome === void 0) {
261
- return {
262
- exitCode: ExitCode.WRITE_FAILED,
263
- result: err("WRITE_FAILED", { stage: "log-append" })
456
+ if (outcome.result.ok) {
457
+ const alreadyApplied = !outcome.result.data.appended && eventCreated === false;
458
+ outcome = {
459
+ exitCode: outcome.exitCode,
460
+ result: ok({
461
+ ...outcome.result.data,
462
+ ...eventCreated !== void 0 ? { event_created: eventCreated } : {},
463
+ ...eventPath ? { event_path: eventPath } : {},
464
+ humanHint: alreadyApplied ? `operation already applied (${outcome.result.data.entries_before} entries)` : outcome.result.data.humanHint
465
+ })
264
466
  };
265
467
  }
266
468
  if (outcome.result.ok && outcome.result.data.appended && input.recordLastOp !== false) {
@@ -283,7 +485,7 @@ async function runLogAppend(input) {
283
485
 
284
486
  // src/utils/sync-lock.ts
285
487
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
286
- import { join as join4 } from "path";
488
+ import { join as join5 } from "path";
287
489
  import { createHash, randomBytes as randomBytes2 } from "crypto";
288
490
  function getEnvSessionId() {
289
491
  if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
@@ -306,7 +508,7 @@ function getCliSessionId(cwd) {
306
508
  return `cli-${getCwdHash(cwd)}`;
307
509
  }
308
510
  function lockPath(vault) {
309
- return join4(vault, ".skillwiki", "sync.lock");
511
+ return join5(vault, ".skillwiki", "sync.lock");
310
512
  }
311
513
  function readLock(vault) {
312
514
  const path = lockPath(vault);
@@ -325,7 +527,7 @@ function isStale(lock, now) {
325
527
  }
326
528
  function acquireLock(vault, opts = {}) {
327
529
  const path = lockPath(vault);
328
- const dir = join4(vault, ".skillwiki");
530
+ const dir = join5(vault, ".skillwiki");
329
531
  if (!existsSync3(dir)) {
330
532
  mkdirSync3(dir, { recursive: true });
331
533
  }
@@ -410,7 +612,7 @@ function acquireOwnedSyncLock(vault, opts) {
410
612
  };
411
613
  const path = lockPath(vault);
412
614
  try {
413
- mkdirSync3(join4(vault, ".skillwiki"), { recursive: true });
615
+ mkdirSync3(join5(vault, ".skillwiki"), { recursive: true });
414
616
  } catch (error) {
415
617
  return err("WRITE_FAILED", { path, message: String(error) });
416
618
  }
@@ -442,12 +644,12 @@ function releaseOwnedSyncLock(handle) {
442
644
 
443
645
  // src/commands/validate.ts
444
646
  import { createHash as createHash2 } from "crypto";
445
- import { readFile as readFile3 } from "fs/promises";
647
+ import { readFile as readFile4 } from "fs/promises";
446
648
  import { resolve, relative, sep } from "path";
447
649
 
448
650
  // src/utils/index-entry.ts
449
- import { readFile as readFile2 } from "fs/promises";
450
- import { join as join5 } from "path";
651
+ import { readFile as readFile3 } from "fs/promises";
652
+ import { join as join6 } from "path";
451
653
  var TYPE_SECTION = {
452
654
  entity: "Entities",
453
655
  concept: "Concepts",
@@ -491,10 +693,10 @@ function renderIndexUpsert(text, input) {
491
693
  });
492
694
  }
493
695
  async function upsertIndexEntry(input) {
494
- const path = join5(input.vault, "index.md");
696
+ const path = join6(input.vault, "index.md");
495
697
  let before = "";
496
698
  try {
497
- before = await readFile2(path, "utf8");
699
+ before = await readFile3(path, "utf8");
498
700
  } catch {
499
701
  before = "";
500
702
  }
@@ -529,7 +731,7 @@ var SCHEMAS = {
529
731
  async function runValidate(input) {
530
732
  let text;
531
733
  try {
532
- text = await readFile3(input.file, "utf8");
734
+ text = await readFile4(input.file, "utf8");
533
735
  } catch {
534
736
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.file }) };
535
737
  }
@@ -669,7 +871,7 @@ ${errors.map((e) => ` ${e.path}: ${e.message}`).join("\n")}` })
669
871
  }
670
872
 
671
873
  // src/commands/graph.ts
672
- import { writeFile, mkdir } from "fs/promises";
874
+ import { writeFile, mkdir as mkdir2 } from "fs/promises";
673
875
  import { dirname } from "path";
674
876
 
675
877
  // src/parsers/wikilinks.ts
@@ -824,7 +1026,7 @@ async function runGraphBuild(input) {
824
1026
  const adamicAdar = computeAdamicAdar(adjacency);
825
1027
  const edge_count = Object.values(adjacency).reduce((acc, arr) => acc + arr.length, 0);
826
1028
  try {
827
- await mkdir(dirname(input.out), { recursive: true });
1029
+ await mkdir2(dirname(input.out), { recursive: true });
828
1030
  await writeFile(input.out, JSON.stringify({ adjacency, adamicAdar }, null, 2));
829
1031
  } catch (e) {
830
1032
  return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { message: String(e) }) };
@@ -868,7 +1070,7 @@ function computeAdamicAdar(adj) {
868
1070
  }
869
1071
 
870
1072
  // src/utils/wiki-path.ts
871
- import { join as join6 } from "path";
1073
+ import { join as join7 } from "path";
872
1074
  async function resolveInitTimePath(input) {
873
1075
  const chain = [];
874
1076
  if (input.flag !== void 0 && input.flag.length > 0) {
@@ -881,27 +1083,27 @@ async function resolveInitTimePath(input) {
881
1083
  return { path: input.envValue, source: "env", ...input.explain ? { chain } : {} };
882
1084
  }
883
1085
  if (input.explain) chain.push({ source: "env", matched: false });
884
- const sw = await parseDotenvFile(join6(input.home, ".skillwiki", ".env"));
1086
+ const sw = await parseDotenvFile(join7(input.home, ".skillwiki", ".env"));
885
1087
  if (sw.WIKI_PATH !== void 0) {
886
1088
  if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: true, value: sw.WIKI_PATH });
887
1089
  return { path: sw.WIKI_PATH, source: "skillwiki-dotenv", ...input.explain ? { chain } : {} };
888
1090
  }
889
1091
  if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: false });
890
- const hermes = await parseDotenvFile(join6(input.home, ".hermes", ".env"));
1092
+ const hermes = await parseDotenvFile(join7(input.home, ".hermes", ".env"));
891
1093
  if (hermes.WIKI_PATH !== void 0) {
892
1094
  if (input.explain) chain.push({ source: "hermes-dotenv", matched: true, value: hermes.WIKI_PATH });
893
1095
  return { path: hermes.WIKI_PATH, source: "hermes-dotenv", ...input.explain ? { chain } : {} };
894
1096
  }
895
1097
  if (input.explain) chain.push({ source: "hermes-dotenv", matched: false });
896
1098
  if (input.cwd) {
897
- const projCfg = await parseDotenvFile(join6(input.cwd, ".skillwiki", ".env"));
1099
+ const projCfg = await parseDotenvFile(join7(input.cwd, ".skillwiki", ".env"));
898
1100
  if (projCfg.WIKI_PATH !== void 0) {
899
1101
  if (input.explain) chain.push({ source: "project-dotenv", matched: true, value: projCfg.WIKI_PATH });
900
1102
  return { path: projCfg.WIKI_PATH, source: "project-dotenv", ...input.explain ? { chain } : {} };
901
1103
  }
902
1104
  }
903
1105
  if (input.explain) chain.push({ source: "project-dotenv", matched: false });
904
- const fallback = join6(input.home, "wiki");
1106
+ const fallback = join7(input.home, "wiki");
905
1107
  if (input.explain) chain.push({ source: "default", matched: true, value: fallback });
906
1108
  return { path: fallback, source: "default", ...input.explain ? { chain } : {} };
907
1109
  }
@@ -912,7 +1114,7 @@ async function resolveRuntimePath(input) {
912
1114
  return ok({ path: input.flag, source: "flag", ...input.explain ? { chain } : {} });
913
1115
  }
914
1116
  if (input.explain) chain.push({ source: "flag", matched: false });
915
- const swGlobal = await parseDotenvFile(join6(input.home, ".skillwiki", ".env"));
1117
+ const swGlobal = await parseDotenvFile(join7(input.home, ".skillwiki", ".env"));
916
1118
  const wikiName = input.wiki;
917
1119
  if (wikiName !== void 0 && wikiName.length > 0) {
918
1120
  if (wikiName.toLowerCase() === "default") {
@@ -956,7 +1158,7 @@ async function resolveRuntimePath(input) {
956
1158
  }
957
1159
  if (input.explain) chain.push({ source: "env", matched: false });
958
1160
  if (input.cwd) {
959
- const projCfg = await parseDotenvFile(join6(input.cwd, ".skillwiki", ".env"));
1161
+ const projCfg = await parseDotenvFile(join7(input.cwd, ".skillwiki", ".env"));
960
1162
  if (projCfg.WIKI_PATH !== void 0) {
961
1163
  if (input.explain) chain.push({ source: "project-dotenv", matched: true, value: projCfg.WIKI_PATH });
962
1164
  return ok({ path: projCfg.WIKI_PATH, source: "project-dotenv", ...input.explain ? { chain } : {} });
@@ -1076,8 +1278,8 @@ function simulateRemoval(adj, removed) {
1076
1278
  }
1077
1279
 
1078
1280
  // src/commands/audit.ts
1079
- import { readFile as readFile4, stat as stat3 } from "fs/promises";
1080
- import { dirname as dirname2, resolve as resolve2, join as join8 } from "path";
1281
+ import { readFile as readFile5, stat as stat3 } from "fs/promises";
1282
+ import { dirname as dirname2, resolve as resolve2, join as join9 } from "path";
1081
1283
 
1082
1284
  // src/parsers/citations.ts
1083
1285
  var FENCE2 = /```[\s\S]*?```/g;
@@ -1189,7 +1391,7 @@ function hasWikilinkCitations(body) {
1189
1391
  // src/utils/raw-source.ts
1190
1392
  import { existsSync as existsSync4 } from "fs";
1191
1393
  import { stat as stat2 } from "fs/promises";
1192
- import { join as join7 } from "path";
1394
+ import { join as join8 } from "path";
1193
1395
  function normalizeRawSourceTarget(entry) {
1194
1396
  let target = entry.trim().replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
1195
1397
  target = target.replace(/^\^\[/, "").replace(/\]$/, "");
@@ -1199,11 +1401,11 @@ function normalizeRawSourceTarget(entry) {
1199
1401
  function rawSourceTargetCandidates(vault, target) {
1200
1402
  const normalized = normalizeRawSourceTarget(target);
1201
1403
  if (!normalized) return [];
1202
- const candidates = [join7(vault, normalized)];
1203
- if (!normalized.endsWith(".md")) candidates.push(join7(vault, `${normalized}.md`));
1404
+ const candidates = [join8(vault, normalized)];
1405
+ if (!normalized.endsWith(".md")) candidates.push(join8(vault, `${normalized}.md`));
1204
1406
  if (normalized.startsWith("raw/")) {
1205
- candidates.push(join7(vault, "_archive", normalized));
1206
- if (!normalized.endsWith(".md")) candidates.push(join7(vault, "_archive", `${normalized}.md`));
1407
+ candidates.push(join8(vault, "_archive", normalized));
1408
+ if (!normalized.endsWith(".md")) candidates.push(join8(vault, "_archive", `${normalized}.md`));
1207
1409
  }
1208
1410
  return [...new Set(candidates)];
1209
1411
  }
@@ -1225,7 +1427,7 @@ async function rawSourceTargetExists(vault, target) {
1225
1427
  async function runAudit(input) {
1226
1428
  let text;
1227
1429
  try {
1228
- text = await readFile4(input.file, "utf8");
1430
+ text = await readFile5(input.file, "utf8");
1229
1431
  } catch {
1230
1432
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.file }) };
1231
1433
  }
@@ -1278,7 +1480,7 @@ async function findVaultRoot(start) {
1278
1480
  let cur = start;
1279
1481
  for (let i = 0; i < 20; i++) {
1280
1482
  try {
1281
- await stat3(join8(cur, "SCHEMA.md"));
1483
+ await stat3(join9(cur, "SCHEMA.md"));
1282
1484
  return cur;
1283
1485
  } catch {
1284
1486
  }
@@ -1370,8 +1572,8 @@ ${broken.map((b) => ` ${b.page}:[[${b.slug}]] (line ${b.line})`).join("\n")}` }
1370
1572
  }
1371
1573
 
1372
1574
  // src/commands/tag-audit.ts
1373
- import { readFile as readFile5 } from "fs/promises";
1374
- import { join as join9 } from "path";
1575
+ import { readFile as readFile6 } from "fs/promises";
1576
+ import { join as join10 } from "path";
1375
1577
 
1376
1578
  // src/parsers/taxonomy.ts
1377
1579
  import yaml from "js-yaml";
@@ -1396,12 +1598,12 @@ function parseTaxonomyDocument(schemaText) {
1396
1598
  const nextHeading = /^#{1,2}[ \t]+/m.exec(unboundedTail);
1397
1599
  const sectionEnd = nextHeading?.index === void 0 ? schemaText.length : afterHeading + nextHeading.index;
1398
1600
  const sectionText = schemaText.slice(afterHeading, sectionEnd);
1399
- const open = /^```yaml[ \t]*\r?$/m.exec(sectionText);
1400
- if (!open || open.index === void 0) {
1601
+ const open2 = /^```yaml[ \t]*\r?$/m.exec(sectionText);
1602
+ if (!open2 || open2.index === void 0) {
1401
1603
  return err("NO_TAXONOMY_BLOCK", { message: "Fenced YAML taxonomy block not found" });
1402
1604
  }
1403
- const openStart = afterHeading + open.index;
1404
- const yamlStart = openStart + open[0].length + 1;
1605
+ const openStart = afterHeading + open2.index;
1606
+ const yamlStart = openStart + open2[0].length + 1;
1405
1607
  const afterOpen = schemaText.slice(yamlStart, sectionEnd);
1406
1608
  const close = /^```[ \t]*\r?$/m.exec(afterOpen);
1407
1609
  if (!close || close.index === void 0) {
@@ -1524,7 +1726,7 @@ async function runTagAudit(input) {
1524
1726
  const scanResult = input.scan ? ok(input.scan) : await scanVault(input.vault);
1525
1727
  if (!scanResult.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
1526
1728
  const scan = scanResult.data;
1527
- const schemaText = await readFile5(join9(input.vault, "SCHEMA.md"), "utf8");
1729
+ const schemaText = await readFile6(join10(input.vault, "SCHEMA.md"), "utf8");
1528
1730
  const tax = extractTaxonomy(schemaText);
1529
1731
  if (!tax.ok) return { exitCode: ExitCode.INVALID_FRONTMATTER, result: tax };
1530
1732
  const allowed = new Set(tax.data);
@@ -1556,8 +1758,8 @@ async function runTagAudit(input) {
1556
1758
  }
1557
1759
 
1558
1760
  // src/commands/index-check.ts
1559
- import { readFile as readFile6 } from "fs/promises";
1560
- import { join as join10 } from "path";
1761
+ import { readFile as readFile7 } from "fs/promises";
1762
+ import { join as join11 } from "path";
1561
1763
  function normalizeIndexTarget(raw) {
1562
1764
  return raw.replace(/\.md$/, "").replace(/^\.?\//, "");
1563
1765
  }
@@ -1569,7 +1771,7 @@ async function runIndexCheck(input) {
1569
1771
  }
1570
1772
  let indexText = "";
1571
1773
  try {
1572
- indexText = await readFile6(join10(input.vault, "index.md"), "utf8");
1774
+ indexText = await readFile7(join11(input.vault, "index.md"), "utf8");
1573
1775
  } catch {
1574
1776
  }
1575
1777
  const indexTargets = /* @__PURE__ */ new Set();
@@ -1623,20 +1825,20 @@ async function runIndexCheck(input) {
1623
1825
  }
1624
1826
 
1625
1827
  // src/commands/project-index.ts
1626
- import { readdir, readFile as readFile7, mkdir as mkdir2 } from "fs/promises";
1627
- import { join as join11, dirname as dirname3, basename } from "path";
1828
+ import { readdir as readdir2, readFile as readFile8, mkdir as mkdir3 } from "fs/promises";
1829
+ import { join as join12, dirname as dirname3, basename } from "path";
1628
1830
  var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
1629
1831
  var PROJECT_LOCAL_DIRS = ["requirements", "work", "architecture", "history"];
1630
1832
  async function scanMarkdownTree(rootAbs, rootRel) {
1631
1833
  const found = [];
1632
1834
  let entries;
1633
1835
  try {
1634
- entries = await readdir(rootAbs, { withFileTypes: true });
1836
+ entries = await readdir2(rootAbs, { withFileTypes: true });
1635
1837
  } catch {
1636
1838
  return found;
1637
1839
  }
1638
1840
  for (const entry of entries) {
1639
- const abs = join11(rootAbs, entry.name);
1841
+ const abs = join12(rootAbs, entry.name);
1640
1842
  const rel = `${rootRel}/${entry.name}`;
1641
1843
  if (entry.isDirectory()) {
1642
1844
  found.push(...await scanMarkdownTree(abs, rel));
@@ -1665,23 +1867,23 @@ function projectLocalType(slug, page, data) {
1665
1867
  return typeof data.type === "string" ? data.type : "project";
1666
1868
  }
1667
1869
  async function renderProjectIndex(vault, slug, opts = {}) {
1668
- const projectDir = join11(vault, "projects", slug);
1870
+ const projectDir = join12(vault, "projects", slug);
1669
1871
  try {
1670
- await readdir(projectDir);
1872
+ await readdir2(projectDir);
1671
1873
  } catch {
1672
1874
  return err("PROJECT_NOT_FOUND", { slug, path: projectDir });
1673
1875
  }
1674
1876
  const wikilinkPattern = `[[${slug}]]`;
1675
1877
  const entries = [];
1676
- const compoundDir = join11(vault, "projects", slug, "compound");
1878
+ const compoundDir = join12(vault, "projects", slug, "compound");
1677
1879
  try {
1678
- const compoundFiles = await readdir(compoundDir, { withFileTypes: true });
1880
+ const compoundFiles = await readdir2(compoundDir, { withFileTypes: true });
1679
1881
  for (const entry of compoundFiles) {
1680
1882
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1681
- const filePath = join11(compoundDir, entry.name);
1883
+ const filePath = join12(compoundDir, entry.name);
1682
1884
  let text;
1683
1885
  try {
1684
- text = await readFile7(filePath, "utf8");
1886
+ text = await readFile8(filePath, "utf8");
1685
1887
  } catch {
1686
1888
  continue;
1687
1889
  }
@@ -1698,16 +1900,16 @@ async function renderProjectIndex(vault, slug, opts = {}) {
1698
1900
  for (const dir of LAYER2_DIRS) {
1699
1901
  let files;
1700
1902
  try {
1701
- files = await readdir(join11(vault, dir), { withFileTypes: true });
1903
+ files = await readdir2(join12(vault, dir), { withFileTypes: true });
1702
1904
  } catch {
1703
1905
  continue;
1704
1906
  }
1705
1907
  for (const entry of files) {
1706
1908
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1707
- const filePath = join11(vault, dir, entry.name);
1909
+ const filePath = join12(vault, dir, entry.name);
1708
1910
  let text;
1709
1911
  try {
1710
- text = await readFile7(filePath, "utf8");
1912
+ text = await readFile8(filePath, "utf8");
1711
1913
  } catch {
1712
1914
  continue;
1713
1915
  }
@@ -1723,14 +1925,14 @@ async function renderProjectIndex(vault, slug, opts = {}) {
1723
1925
  }
1724
1926
  }
1725
1927
  for (const dir of PROJECT_LOCAL_DIRS) {
1726
- const rootAbs = join11(projectDir, dir);
1928
+ const rootAbs = join12(projectDir, dir);
1727
1929
  const rootRel = `projects/${slug}/${dir}`;
1728
1930
  const pages = await scanMarkdownTree(rootAbs, rootRel);
1729
1931
  for (const page of pages) {
1730
- const filePath = join11(vault, page);
1932
+ const filePath = join12(vault, page);
1731
1933
  let text;
1732
1934
  try {
1733
- text = await readFile7(filePath, "utf8");
1935
+ text = await readFile8(filePath, "utf8");
1734
1936
  } catch {
1735
1937
  continue;
1736
1938
  }
@@ -1803,7 +2005,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
1803
2005
  }
1804
2006
  async function runProjectIndex(input) {
1805
2007
  const slug = input.slug;
1806
- const projectDir = join11(input.vault, "projects", slug);
2008
+ const projectDir = join12(input.vault, "projects", slug);
1807
2009
  const rendered = await renderProjectIndex(input.vault, slug);
1808
2010
  if (!rendered.ok) {
1809
2011
  return {
@@ -1811,12 +2013,12 @@ async function runProjectIndex(input) {
1811
2013
  result: rendered
1812
2014
  };
1813
2015
  }
1814
- const indexPath = join11(projectDir, "knowledge.md");
2016
+ const indexPath = join12(projectDir, "knowledge.md");
1815
2017
  const entries = rendered.data.entries;
1816
2018
  let existing = false;
1817
2019
  let stale = false;
1818
2020
  try {
1819
- const existingText = await readFile7(indexPath, "utf8");
2021
+ const existingText = await readFile8(indexPath, "utf8");
1820
2022
  existing = true;
1821
2023
  const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
1822
2024
  const existingPages = new Set(existingEntries.map((l) => {
@@ -1829,7 +2031,7 @@ async function runProjectIndex(input) {
1829
2031
  }
1830
2032
  if (input.apply) {
1831
2033
  try {
1832
- await mkdir2(dirname3(indexPath), { recursive: true });
2034
+ await mkdir3(dirname3(indexPath), { recursive: true });
1833
2035
  } catch (e) {
1834
2036
  return {
1835
2037
  exitCode: ExitCode.WRITE_FAILED,
@@ -1861,8 +2063,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
1861
2063
  }
1862
2064
 
1863
2065
  // src/commands/stale.ts
1864
- import { readdir as readdir2, rename, mkdir as mkdir3, readFile as readFile8 } from "fs/promises";
1865
- import { join as join12 } from "path";
2066
+ import { readdir as readdir3, rename, mkdir as mkdir4, readFile as readFile9 } from "fs/promises";
2067
+ import { join as join13 } from "path";
1866
2068
 
1867
2069
  // src/parsers/expiry-annotations.ts
1868
2070
  var HEADING_RE = /^#{1,6}\s+(.+)$/;
@@ -1911,10 +2113,10 @@ async function runStale(input) {
1911
2113
  const archived = [];
1912
2114
  const workDirs = /* @__PURE__ */ new Map();
1913
2115
  const workDirsBySlug = /* @__PURE__ */ new Map();
1914
- const projectsDir = join12(input.vault, "projects");
2116
+ const projectsDir = join13(input.vault, "projects");
1915
2117
  let projectSlugs = [];
1916
2118
  try {
1917
- projectSlugs = (await readdir2(projectsDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
2119
+ projectSlugs = (await readdir3(projectsDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
1918
2120
  } catch {
1919
2121
  }
1920
2122
  if (input.project) {
@@ -1924,10 +2126,10 @@ async function runStale(input) {
1924
2126
  projectSlugs = [input.project];
1925
2127
  }
1926
2128
  for (const slug of projectSlugs) {
1927
- const workPath = join12(projectsDir, slug, "work");
2129
+ const workPath = join13(projectsDir, slug, "work");
1928
2130
  let entries;
1929
2131
  try {
1930
- entries = await readdir2(workPath, { withFileTypes: true });
2132
+ entries = await readdir3(workPath, { withFileTypes: true });
1931
2133
  } catch {
1932
2134
  continue;
1933
2135
  }
@@ -1935,11 +2137,11 @@ async function runStale(input) {
1935
2137
  for (const e of entries) {
1936
2138
  if (!e.isDirectory()) continue;
1937
2139
  const relDir = `projects/${slug}/work/${e.name}`;
1938
- const absDir = join12(workPath, e.name);
2140
+ const absDir = join13(workPath, e.name);
1939
2141
  let status = "";
1940
2142
  let files;
1941
2143
  try {
1942
- files = await readdir2(absDir);
2144
+ files = await readdir3(absDir);
1943
2145
  } catch {
1944
2146
  workDirs.set(relDir, "");
1945
2147
  slugDirs.set(e.name, "");
@@ -1948,7 +2150,7 @@ async function runStale(input) {
1948
2150
  for (const f of files) {
1949
2151
  if (!f.endsWith(".md")) continue;
1950
2152
  try {
1951
- const fm = extractFrontmatter(await readFile8(join12(absDir, f), "utf8"));
2153
+ const fm = extractFrontmatter(await readFile9(join13(absDir, f), "utf8"));
1952
2154
  if (fm.ok && typeof fm.data.status === "string") {
1953
2155
  status = fm.data.status;
1954
2156
  break;
@@ -2044,9 +2246,9 @@ async function runStale(input) {
2044
2246
  }
2045
2247
  }
2046
2248
  await mapWithConcurrency([...workDirs.keys()], vaultIoConcurrency(), async (relDir) => {
2047
- const specPath = join12(input.vault, relDir, "spec.md");
2249
+ const specPath = join13(input.vault, relDir, "spec.md");
2048
2250
  try {
2049
- const specContent = await readFile8(specPath, "utf8");
2251
+ const specContent = await readFile9(specPath, "utf8");
2050
2252
  const specFm = extractFrontmatter(specContent);
2051
2253
  if (specFm.ok && typeof specFm.data.source === "string") {
2052
2254
  const sourcePath = specFm.data.source;
@@ -2075,7 +2277,7 @@ async function runStale(input) {
2075
2277
  if (daysSince(dateStr) < input.days) continue;
2076
2278
  let files;
2077
2279
  try {
2078
- files = await readdir2(join12(input.vault, relDir));
2280
+ files = await readdir3(join13(input.vault, relDir));
2079
2281
  } catch {
2080
2282
  continue;
2081
2283
  }
@@ -2145,8 +2347,8 @@ async function runStale(input) {
2145
2347
  staleSections.push(...staleSectionResults.flat());
2146
2348
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2147
2349
  if (input.archive) {
2148
- const archiveDir = join12(input.vault, "_archive", today);
2149
- await mkdir3(archiveDir, { recursive: true });
2350
+ const archiveDir = join13(input.vault, "_archive", today);
2351
+ await mkdir4(archiveDir, { recursive: true });
2150
2352
  const citedRawPaths = /* @__PURE__ */ new Set();
2151
2353
  for (const page of scan.typedKnowledge) {
2152
2354
  const text = await readPageCached(page, input.pageTextCache).catch(() => "");
@@ -2161,9 +2363,9 @@ async function runStale(input) {
2161
2363
  }
2162
2364
  for (const t of staleTranscripts) {
2163
2365
  if (citedRawPaths.has(t.path) || citedRawPaths.has(t.path.replace(/\.md$/, ""))) continue;
2164
- const dest = join12(archiveDir, t.path.split("/").pop());
2366
+ const dest = join13(archiveDir, t.path.split("/").pop());
2165
2367
  try {
2166
- await rename(join12(input.vault, t.path), dest);
2368
+ await rename(join13(input.vault, t.path), dest);
2167
2369
  archived.push(t.path);
2168
2370
  } catch {
2169
2371
  }
@@ -2173,18 +2375,18 @@ async function runStale(input) {
2173
2375
  if (parts.length >= 4 && parts[0] === "projects") {
2174
2376
  const slug = parts[1];
2175
2377
  const itemName = parts[3];
2176
- const histDir = join12(input.vault, "projects", slug, "history", "archived-work");
2177
- await mkdir3(histDir, { recursive: true });
2178
- const dest = join12(histDir, itemName);
2378
+ const histDir = join13(input.vault, "projects", slug, "history", "archived-work");
2379
+ await mkdir4(histDir, { recursive: true });
2380
+ const dest = join13(histDir, itemName);
2179
2381
  try {
2180
- await rename(join12(input.vault, w.path), dest);
2382
+ await rename(join13(input.vault, w.path), dest);
2181
2383
  archived.push(w.path);
2182
2384
  } catch {
2183
2385
  }
2184
2386
  } else {
2185
- const dest = join12(archiveDir, w.path.replace(/\//g, "_"));
2387
+ const dest = join13(archiveDir, w.path.replace(/\//g, "_"));
2186
2388
  try {
2187
- await rename(join12(input.vault, w.path), dest);
2389
+ await rename(join13(input.vault, w.path), dest);
2188
2390
  archived.push(w.path);
2189
2391
  } catch {
2190
2392
  }
@@ -2239,19 +2441,19 @@ async function runPagesize(input) {
2239
2441
  }
2240
2442
 
2241
2443
  // src/commands/log-rotate.ts
2242
- import { readFile as readFile9, rename as rename2, writeFile as writeFile2, stat as stat4 } from "fs/promises";
2243
- import { join as join13 } from "path";
2444
+ import { readFile as readFile10, rename as rename2, writeFile as writeFile2, stat as stat4 } from "fs/promises";
2445
+ import { join as join14 } from "path";
2244
2446
  var ENTRY_RE2 = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
2245
2447
  async function runLogRotate(input) {
2246
2448
  try {
2247
- await stat4(join13(input.vault, "SCHEMA.md"));
2449
+ await stat4(join14(input.vault, "SCHEMA.md"));
2248
2450
  } catch {
2249
2451
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
2250
2452
  }
2251
- const logPath = join13(input.vault, "log.md");
2453
+ const logPath = join14(input.vault, "log.md");
2252
2454
  let logText;
2253
2455
  try {
2254
- logText = await readFile9(logPath, "utf8");
2456
+ logText = await readFile10(logPath, "utf8");
2255
2457
  } catch {
2256
2458
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
2257
2459
  }
@@ -2268,7 +2470,7 @@ async function runLogRotate(input) {
2268
2470
  }
2269
2471
  const newestYear = matches[matches.length - 1][1];
2270
2472
  const rotatedName = `log-${newestYear}.md`;
2271
- const rotatedPath = join13(input.vault, rotatedName);
2473
+ const rotatedPath = join14(input.vault, rotatedName);
2272
2474
  try {
2273
2475
  await rename2(logPath, rotatedPath);
2274
2476
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -2313,13 +2515,13 @@ async function runTopicMapCheck(input) {
2313
2515
  }
2314
2516
 
2315
2517
  // src/commands/index-link-format.ts
2316
- import { readFile as readFile10 } from "fs/promises";
2317
- import { join as join14 } from "path";
2518
+ import { readFile as readFile11 } from "fs/promises";
2519
+ import { join as join15 } from "path";
2318
2520
  var MD_LINK_RE = /\[[^\[\]]+\]\([^)]+\.md\)/;
2319
2521
  async function runIndexLinkFormat(input) {
2320
2522
  let text = "";
2321
2523
  try {
2322
- text = await readFile10(join14(input.vault, "index.md"), "utf8");
2524
+ text = await readFile11(join15(input.vault, "index.md"), "utf8");
2323
2525
  } catch {
2324
2526
  }
2325
2527
  const markdown_links = [];
@@ -2334,7 +2536,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
2334
2536
  // src/commands/dedup.ts
2335
2537
  import { createHash as createHash3 } from "crypto";
2336
2538
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
2337
- import { dirname as dirname4, join as join15, resolve as resolve3 } from "path";
2539
+ import { dirname as dirname4, join as join16, resolve as resolve3 } from "path";
2338
2540
 
2339
2541
  // src/utils/rclone.ts
2340
2542
  import { execFile } from "child_process";
@@ -2453,7 +2655,7 @@ async function runDedup(input) {
2453
2655
  }
2454
2656
  }
2455
2657
  for (const page of scan.allMarkdown.filter((p) => !p.relPath.startsWith("raw/"))) {
2456
- const text = readFileSync4(join15(input.vault, page.relPath), "utf-8");
2658
+ const text = readFileSync4(join16(input.vault, page.relPath), "utf-8");
2457
2659
  let updated = text;
2458
2660
  let changed = false;
2459
2661
  for (const [oldPath, newPath] of replacements) {
@@ -2471,12 +2673,12 @@ async function runDedup(input) {
2471
2673
  }
2472
2674
  }
2473
2675
  if (changed) {
2474
- writeFileSync4(join15(input.vault, page.relPath), updated);
2676
+ writeFileSync4(join16(input.vault, page.relPath), updated);
2475
2677
  rewired.push(page.relPath);
2476
2678
  }
2477
2679
  }
2478
2680
  for (const oldPath of replacements.keys()) {
2479
- const fullPath = join15(input.vault, oldPath);
2681
+ const fullPath = join16(input.vault, oldPath);
2480
2682
  try {
2481
2683
  unlinkSync4(fullPath);
2482
2684
  removed.push(oldPath);
@@ -2570,7 +2772,7 @@ function buildSafeEntries(vault, duplicates, unsafe) {
2570
2772
  return entries;
2571
2773
  }
2572
2774
  function hashRawBody(vault, relPath) {
2573
- const text = readFileSync4(join15(vault, relPath), "utf-8");
2775
+ const text = readFileSync4(join16(vault, relPath), "utf-8");
2574
2776
  const split = splitFrontmatter(text);
2575
2777
  const body = split.ok ? split.data.body : text;
2576
2778
  return createHash3("sha256").update(body).digest("hex");
@@ -2593,7 +2795,7 @@ async function planAndMaybePruneRemote(input, entries) {
2593
2795
  }
2594
2796
 
2595
2797
  // src/utils/safe-write.ts
2596
- import { readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
2798
+ import { readFile as readFile12, writeFile as writeFile3 } from "fs/promises";
2597
2799
  var DEFAULT_MIN_BODY_RATIO = 0.5;
2598
2800
  var DEFAULT_MIN_OLD_BODY_BYTES = 200;
2599
2801
  function bodyBytes(text) {
@@ -2603,7 +2805,7 @@ function bodyBytes(text) {
2603
2805
  }
2604
2806
  async function readIfExists(absPath) {
2605
2807
  try {
2606
- return await readFile11(absPath, "utf8");
2808
+ return await readFile12(absPath, "utf8");
2607
2809
  } catch (e) {
2608
2810
  if (e.code === "ENOENT") return null;
2609
2811
  throw e;
@@ -2727,9 +2929,9 @@ ${newBody}`;
2727
2929
 
2728
2930
  // src/commands/lint.ts
2729
2931
  import { existsSync as existsSync6 } from "fs";
2730
- import { readFile as readFile13, readdir as readdir3 } from "fs/promises";
2932
+ import { readFile as readFile14, readdir as readdir4 } from "fs/promises";
2731
2933
  import { createHash as createHash5 } from "crypto";
2732
- import { join as join17, relative as relative2, sep as sep2 } from "path";
2934
+ import { join as join18, relative as relative2, sep as sep2 } from "path";
2733
2935
 
2734
2936
  // src/commands/sparse-community.ts
2735
2937
  async function runSparseCommunity(input) {
@@ -2787,8 +2989,8 @@ async function runRawBodyDedup(vault, scan, pageTextCache) {
2787
2989
 
2788
2990
  // src/commands/path-too-long.ts
2789
2991
  import { existsSync as existsSync5 } from "fs";
2790
- import { mkdir as mkdir4, readFile as readFile12, rename as rename3, unlink } from "fs/promises";
2791
- import { dirname as dirname5, join as join16, posix, resolve as resolve4 } from "path";
2992
+ import { mkdir as mkdir5, readFile as readFile13, rename as rename3, unlink } from "fs/promises";
2993
+ import { dirname as dirname5, join as join17, posix, resolve as resolve4 } from "path";
2792
2994
  var MAX_PATH_LENGTH = 240;
2793
2995
  var WINDOWS_ABSOLUTE_PATH_LIMIT = 259;
2794
2996
  async function runPathTooLong(input) {
@@ -2821,10 +3023,10 @@ async function fixPathTooLong(input) {
2821
3023
  }
2822
3024
  try {
2823
3025
  if (target.mode === "dedupe") {
2824
- await unlink(join16(input.vault, violation.relPath));
3026
+ await unlink(join17(input.vault, violation.relPath));
2825
3027
  } else {
2826
- await mkdir4(dirname5(join16(input.vault, target.relPath)), { recursive: true });
2827
- await rename3(join16(input.vault, violation.relPath), join16(input.vault, target.relPath));
3028
+ await mkdir5(dirname5(join17(input.vault, target.relPath)), { recursive: true });
3029
+ await rename3(join17(input.vault, violation.relPath), join17(input.vault, target.relPath));
2828
3030
  }
2829
3031
  fixed.push({ from: violation.relPath, to: target.relPath });
2830
3032
  } catch {
@@ -2838,7 +3040,7 @@ async function fixPathTooLong(input) {
2838
3040
  for (const page of afterScan.data.allMarkdown) {
2839
3041
  if (!shouldRewriteReferences(page.relPath)) continue;
2840
3042
  try {
2841
- const original = await readFile12(page.absPath, "utf8");
3043
+ const original = await readFile13(page.absPath, "utf8");
2842
3044
  let updated = original;
2843
3045
  for (const fix of fixed) {
2844
3046
  updated = replacePathReferences(updated, fix.from, fix.to);
@@ -2901,9 +3103,9 @@ function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
2901
3103
  async function resolveFixTarget(vault, original, preferred, maxLength) {
2902
3104
  for (const candidate of candidateRelPaths(preferred, maxLength)) {
2903
3105
  if (candidate === original || candidate.length > maxLength) continue;
2904
- const candidatePath = join16(vault, candidate);
3106
+ const candidatePath = join17(vault, candidate);
2905
3107
  if (!existsSync5(candidatePath)) return { relPath: candidate, mode: "rename" };
2906
- if (await hasSameContent(join16(vault, original), candidatePath)) {
3108
+ if (await hasSameContent(join17(vault, original), candidatePath)) {
2907
3109
  return { relPath: candidate, mode: "dedupe" };
2908
3110
  }
2909
3111
  }
@@ -2927,7 +3129,7 @@ function candidateRelPaths(preferred, maxLength) {
2927
3129
  }
2928
3130
  async function hasSameContent(a, b) {
2929
3131
  try {
2930
- const [left, right] = await Promise.all([readFile12(a), readFile12(b)]);
3132
+ const [left, right] = await Promise.all([readFile13(a), readFile13(b)]);
2931
3133
  return left.equals(right);
2932
3134
  } catch {
2933
3135
  return false;
@@ -2974,7 +3176,7 @@ function buildCliSurface() {
2974
3176
  program.command("orphans").option("--wiki <name>");
2975
3177
  program.command("audit");
2976
3178
  program.command("install").option("--target <dir>").option("--dry-run").option("--skills-root <dir>").option("--symlink");
2977
- program.command("path").option("--vault <dir>").option("--target <dir>").option("--wiki <name>").option("--init-time").option("--explain");
3179
+ program.command("path").option("--vault <dir>").option("--target <dir>").option("--wiki <name>").option("--init-time").option("--explain").option("--plain");
2978
3180
  program.command("lang").option("--lang <code>").option("--explain");
2979
3181
  program.command("init").option("--target <dir>").requiredOption("--domain <text>").option("--taxonomy <csv>").option("--lang <code>").option("--force").option("--no-env").option("--profile <name>");
2980
3182
  program.command("links").option("--wiki <name>");
@@ -2986,7 +3188,9 @@ function buildCliSurface() {
2986
3188
  program.command("claim").option("--project <slug>").option("--slug <slug>").option("--wiki <name>");
2987
3189
  program.command("pagesize").option("--lines <n>").option("--wiki <name>");
2988
3190
  program.command("log-rotate").option("--threshold <n>").option("--apply").option("--wiki <name>");
2989
- program.command("log-append").requiredOption("--content <text>").option("--wiki <name>");
3191
+ program.command("log-append").requiredOption("--content <text>").option("--operation-id <id>").option("--write-event").option("--wiki <name>");
3192
+ program.command("work-complete").requiredOption("--work-item <path>").option("--operation-id <id>").option("--no-commit").option("--wiki <name>");
3193
+ program.command("work-validate").requiredOption("--work-item <path>").option("--require-complete").option("--wiki <name>");
2990
3194
  program.command("lint").option("--days <n>").option("--lines <n>").option("--log-threshold <n>").option("--fix").option("--only <bucket>").option("--summary").option("--examples <n>").option("--wiki <name>");
2991
3195
  program.command("config");
2992
3196
  program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
@@ -3388,10 +3592,10 @@ function summarizeLintOutput(output, examplesLimit = 3) {
3388
3592
  };
3389
3593
  }
3390
3594
  async function walkMarkdownFiles(absDir, vaultRoot) {
3391
- const entries = await readdir3(absDir, { withFileTypes: true });
3595
+ const entries = await readdir4(absDir, { withFileTypes: true });
3392
3596
  const pages = [];
3393
3597
  for (const entry of entries) {
3394
- const absPath = join17(absDir, entry.name);
3598
+ const absPath = join18(absDir, entry.name);
3395
3599
  if (entry.isDirectory()) {
3396
3600
  if (entry.name === ".git" || entry.name === "node_modules") continue;
3397
3601
  pages.push(...await walkMarkdownFiles(absPath, vaultRoot));
@@ -3402,12 +3606,12 @@ async function walkMarkdownFiles(absDir, vaultRoot) {
3402
3606
  return pages;
3403
3607
  }
3404
3608
  async function collectCliRefsPages(vault) {
3405
- if (!existsSync6(join17(vault, "SCHEMA.md"))) {
3609
+ if (!existsSync6(join18(vault, "SCHEMA.md"))) {
3406
3610
  return err("VAULT_PATH_INVALID", { root: vault, reason: "SCHEMA.md missing" });
3407
3611
  }
3408
3612
  const pages = [];
3409
3613
  for (const dir of CLI_REFS_TYPED_DIRS) {
3410
- const absDir = join17(vault, dir);
3614
+ const absDir = join18(vault, dir);
3411
3615
  if (!existsSync6(absDir)) continue;
3412
3616
  pages.push(...await walkMarkdownFiles(absDir, vault));
3413
3617
  }
@@ -3529,7 +3733,7 @@ async function applyFileSourceUrlFix(input, scan, fileSourceUrlFlags, fileSource
3529
3733
  for (const relPath of fileSourceUrlFrontmatterFlags) {
3530
3734
  try {
3531
3735
  const absPath = `${input.vault}/${relPath}`;
3532
- const raw = await readFile13(absPath, "utf8");
3736
+ const raw = await readFile14(absPath, "utf8");
3533
3737
  const parts = raw.split("---", 3);
3534
3738
  if (parts.length < 3) {
3535
3739
  unresolved.push(relPath);
@@ -3922,8 +4126,8 @@ async function runLint(input) {
3922
4126
  const readKnowledgeContent = (slug) => {
3923
4127
  const existing = knowledgeContentCache.get(slug);
3924
4128
  if (existing) return existing;
3925
- const knowledgePath = join17(lintVault, "projects", slug, "knowledge.md");
3926
- const pending = existsSync6(knowledgePath) ? readFile13(knowledgePath, "utf8").catch(() => null) : Promise.resolve(null);
4129
+ const knowledgePath = join18(lintVault, "projects", slug, "knowledge.md");
4130
+ const pending = existsSync6(knowledgePath) ? readFile14(knowledgePath, "utf8").catch(() => null) : Promise.resolve(null);
3927
4131
  knowledgeContentCache.set(slug, pending);
3928
4132
  return pending;
3929
4133
  };
@@ -4029,7 +4233,7 @@ async function runLint(input) {
4029
4233
  for (const relPath of legacyPages) {
4030
4234
  try {
4031
4235
  const absPath = `${input.vault}/${relPath}`;
4032
- const raw = await readFile13(absPath, "utf8");
4236
+ const raw = await readFile14(absPath, "utf8");
4033
4237
  const split = splitFrontmatter(raw);
4034
4238
  if (!split.ok) {
4035
4239
  unresolved.push(relPath);
@@ -4128,7 +4332,7 @@ ${newBody}`;
4128
4332
  for (const relPath of noOverview) {
4129
4333
  try {
4130
4334
  const absPath = `${input.vault}/${relPath}`;
4131
- const raw = await readFile13(absPath, "utf8");
4335
+ const raw = await readFile14(absPath, "utf8");
4132
4336
  const split = splitFrontmatter(raw);
4133
4337
  if (!split.ok) {
4134
4338
  unresolved.push(relPath);
@@ -4169,7 +4373,7 @@ ${trimmedBody}`;
4169
4373
  for (const relPath of missingTldrFlags) {
4170
4374
  try {
4171
4375
  const absPath = `${input.vault}/${relPath}`;
4172
- const raw = await readFile13(absPath, "utf8");
4376
+ const raw = await readFile14(absPath, "utf8");
4173
4377
  const split = splitFrontmatter(raw);
4174
4378
  if (!split.ok) {
4175
4379
  unresolved.push(relPath);
@@ -4219,7 +4423,7 @@ ${lines.join("\n")}`;
4219
4423
  for (const relPath of wikilinkCitationFlags) {
4220
4424
  try {
4221
4425
  const absPath = `${input.vault}/${relPath}`;
4222
- const raw = await readFile13(absPath, "utf8");
4426
+ const raw = await readFile14(absPath, "utf8");
4223
4427
  const split = splitFrontmatter(raw);
4224
4428
  if (!split.ok) {
4225
4429
  unresolved.push(relPath);
@@ -4560,14 +4764,14 @@ async function runSyncLintDelta(input) {
4560
4764
  }
4561
4765
 
4562
4766
  // src/commands/config.ts
4563
- import { readFile as readFile14 } from "fs/promises";
4767
+ import { readFile as readFile15 } from "fs/promises";
4564
4768
  import { existsSync as existsSync7 } from "fs";
4565
- import { join as join18 } from "path";
4769
+ import { join as join19 } from "path";
4566
4770
  function validateKey(key) {
4567
4771
  return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
4568
4772
  }
4569
4773
  function configPath(home) {
4570
- return join18(home, ".skillwiki", ".env");
4774
+ return join19(home, ".skillwiki", ".env");
4571
4775
  }
4572
4776
  async function runConfigGet(input) {
4573
4777
  if (!validateKey(input.key)) {
@@ -4585,7 +4789,7 @@ async function runConfigSet(input) {
4585
4789
  try {
4586
4790
  let originalContent;
4587
4791
  try {
4588
- originalContent = await readFile14(filePath, "utf8");
4792
+ originalContent = await readFile15(filePath, "utf8");
4589
4793
  } catch {
4590
4794
  }
4591
4795
  const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
@@ -4622,19 +4826,19 @@ async function runConfigPath(input) {
4622
4826
 
4623
4827
  // src/commands/doctor.ts
4624
4828
  import { existsSync as existsSync13, lstatSync, readlinkSync, readdirSync as readdirSync3, statSync as statSync2, readFileSync as readFileSync10 } from "fs";
4625
- import { join as join24, resolve as resolve5 } from "path";
4829
+ import { join as join25, resolve as resolve5 } from "path";
4626
4830
  import { execSync as execSync2 } from "child_process";
4627
4831
  import { platform as platform2 } from "os";
4628
4832
 
4629
4833
  // src/utils/plugin-registry.ts
4630
4834
  import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync5 } from "fs";
4631
- import { join as join19 } from "path";
4632
- var REGISTRY_PATH = join19(".claude", "plugins", "installed_plugins.json");
4633
- var CODEX_CONFIG_PATH = join19(".codex", "config.toml");
4835
+ import { join as join20 } from "path";
4836
+ var REGISTRY_PATH = join20(".claude", "plugins", "installed_plugins.json");
4837
+ var CODEX_CONFIG_PATH = join20(".codex", "config.toml");
4634
4838
  var PLUGIN_KEY = "skillwiki@llm-wiki";
4635
4839
  function readInstalledPlugins(home) {
4636
4840
  try {
4637
- const raw = readFileSync5(join19(home, REGISTRY_PATH), "utf8");
4841
+ const raw = readFileSync5(join20(home, REGISTRY_PATH), "utf8");
4638
4842
  return JSON.parse(raw);
4639
4843
  } catch {
4640
4844
  return null;
@@ -4670,7 +4874,7 @@ function findPluginInstallations(home, key = PLUGIN_KEY) {
4670
4874
  function findCodexPlugin(home, key, pluginName, marketplace) {
4671
4875
  const config = readCodexPluginConfig(home, key, marketplace);
4672
4876
  if (!config?.enabled) return null;
4673
- const cacheRoot = join19(home, ".codex", "plugins", "cache", marketplace, pluginName);
4877
+ const cacheRoot = join20(home, ".codex", "plugins", "cache", marketplace, pluginName);
4674
4878
  if (!existsSync8(cacheRoot)) return null;
4675
4879
  let versions;
4676
4880
  try {
@@ -4686,7 +4890,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
4686
4890
  key,
4687
4891
  pluginName,
4688
4892
  marketplace,
4689
- installPath: join19(cacheRoot, version),
4893
+ installPath: join20(cacheRoot, version),
4690
4894
  version,
4691
4895
  sourceType: config.sourceType,
4692
4896
  source: config.source
@@ -4703,7 +4907,7 @@ function parsePluginKey(key) {
4703
4907
  function readCodexPluginConfig(home, key, marketplace) {
4704
4908
  let raw;
4705
4909
  try {
4706
- raw = readFileSync5(join19(home, CODEX_CONFIG_PATH), "utf8");
4910
+ raw = readFileSync5(join20(home, CODEX_CONFIG_PATH), "utf8");
4707
4911
  } catch {
4708
4912
  return null;
4709
4913
  }
@@ -4746,7 +4950,7 @@ function parseTomlScalar(rawValue) {
4746
4950
 
4747
4951
  // src/utils/conflict-markers.ts
4748
4952
  import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
4749
- import { join as join20 } from "path";
4953
+ import { join as join21 } from "path";
4750
4954
  function scanConflictMarkerBlocksInText(relPath, text) {
4751
4955
  const findings = [];
4752
4956
  const lines = text.split(/\r?\n/);
@@ -4797,7 +5001,7 @@ function walkMarkdownFiles2(root, dir, rel, out) {
4797
5001
  for (const entry of entries) {
4798
5002
  if (entry.isDirectory()) {
4799
5003
  if (PRUNE_DIRS.has(entry.name)) continue;
4800
- walkMarkdownFiles2(root, join20(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
5004
+ walkMarkdownFiles2(root, join21(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
4801
5005
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
4802
5006
  out.push(rel ? `${rel}/${entry.name}` : entry.name);
4803
5007
  }
@@ -4811,7 +5015,7 @@ function scanVaultConflictMarkers(vaultRoot) {
4811
5015
  for (const rel of relPaths) {
4812
5016
  let text;
4813
5017
  try {
4814
- text = readFileSync6(join20(vaultRoot, rel), "utf8");
5018
+ text = readFileSync6(join21(vaultRoot, rel), "utf8");
4815
5019
  } catch {
4816
5020
  continue;
4817
5021
  }
@@ -4822,7 +5026,7 @@ function scanVaultConflictMarkers(vaultRoot) {
4822
5026
 
4823
5027
  // src/utils/remote-health.ts
4824
5028
  import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
4825
- import { join as join21 } from "path";
5029
+ import { join as join22 } from "path";
4826
5030
  import { execFileSync } from "child_process";
4827
5031
  var REMOTE_PROBE_TIMEOUT_MS = 3e3;
4828
5032
  var defaultExec = (file, args, cwd) => execFileSync(file, args, {
@@ -4833,7 +5037,7 @@ var defaultExec = (file, args, cwd) => execFileSync(file, args, {
4833
5037
  }).trim();
4834
5038
  function readWikiS3RemoteConfigured(home) {
4835
5039
  try {
4836
- const content = readFileSync7(join21(home, ".skillwiki", ".env"), "utf8");
5040
+ const content = readFileSync7(join22(home, ".skillwiki", ".env"), "utf8");
4837
5041
  for (const line of content.split(/\r?\n/)) {
4838
5042
  const trimmed = line.trim();
4839
5043
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -4858,7 +5062,7 @@ function resolveWikiS3Remote(input) {
4858
5062
  return readWikiS3RemoteConfigured(input.home);
4859
5063
  }
4860
5064
  function probeGithubReachability(vaultPath, exec = defaultExec) {
4861
- if (!existsSync10(join21(vaultPath, ".git"))) return "unknown";
5065
+ if (!existsSync10(join22(vaultPath, ".git"))) return "unknown";
4862
5066
  try {
4863
5067
  exec("git", ["remote", "get-url", "origin"], vaultPath);
4864
5068
  } catch {
@@ -4924,10 +5128,10 @@ function probeRemoteHealth(input) {
4924
5128
 
4925
5129
  // src/utils/satellite-run-health.ts
4926
5130
  import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
4927
- import { join as join22 } from "path";
5131
+ import { join as join23 } from "path";
4928
5132
  var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
4929
5133
  function satelliteLatestRunPath(vault) {
4930
- return join22(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
5134
+ return join23(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
4931
5135
  }
4932
5136
  function isFailedRunStatus(status) {
4933
5137
  return status === "fail" || status === "failure";
@@ -4980,8 +5184,8 @@ function evaluateSatelliteRunHealth(vault, now) {
4980
5184
  // src/utils/s3-mount-health.ts
4981
5185
  import { execSync } from "child_process";
4982
5186
  import { platform } from "os";
4983
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, readFileSync as readFile15 } from "fs";
4984
- import { join as join23 } from "path";
5187
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, readFileSync as readFile16 } from "fs";
5188
+ import { join as join24 } from "path";
4985
5189
  var OS = platform();
4986
5190
  function findRcloneMountPid() {
4987
5191
  try {
@@ -5139,7 +5343,7 @@ function detectFuseMount(vaultPath) {
5139
5343
  return null;
5140
5344
  }
5141
5345
  function writeTest(dir) {
5142
- const testFile = join23(dir, `.doctor-write-test-${process.pid}.tmp`);
5346
+ const testFile = join24(dir, `.doctor-write-test-${process.pid}.tmp`);
5143
5347
  const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
5144
5348
  const start = Date.now();
5145
5349
  try {
@@ -5150,7 +5354,7 @@ function writeTest(dir) {
5150
5354
  const writeMs = Date.now() - start;
5151
5355
  const readStart = Date.now();
5152
5356
  try {
5153
- const back = readFile15(testFile, "utf8");
5357
+ const back = readFile16(testFile, "utf8");
5154
5358
  const readMs = Date.now() - readStart;
5155
5359
  if (back !== payload) {
5156
5360
  try {
@@ -5239,12 +5443,12 @@ function detectCliChannels(argv, home) {
5239
5443
  }
5240
5444
  const plugin = findPlugin(home);
5241
5445
  if (plugin) {
5242
- const pluginBin = join24(plugin.installPath, "bin", "skillwiki");
5446
+ const pluginBin = join25(plugin.installPath, "bin", "skillwiki");
5243
5447
  if (existsSync13(pluginBin)) {
5244
5448
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
5245
5449
  }
5246
5450
  }
5247
- const installBin = join24(home, ".claude", "skills", "bin", "skillwiki");
5451
+ const installBin = join25(home, ".claude", "skills", "bin", "skillwiki");
5248
5452
  if (existsSync13(installBin)) {
5249
5453
  channels.push({ name: "install", path: installBin, isDevLink: false });
5250
5454
  }
@@ -5334,9 +5538,9 @@ function checkVaultStructure(resolvedPath) {
5334
5538
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
5335
5539
  }
5336
5540
  const missing = [];
5337
- if (!existsSync13(join24(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
5541
+ if (!existsSync13(join25(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
5338
5542
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
5339
- if (!existsSync13(join24(resolvedPath, dir))) missing.push(dir + "/");
5543
+ if (!existsSync13(join25(resolvedPath, dir))) missing.push(dir + "/");
5340
5544
  }
5341
5545
  if (missing.length === 0) {
5342
5546
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -5344,7 +5548,7 @@ function checkVaultStructure(resolvedPath) {
5344
5548
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
5345
5549
  }
5346
5550
  function checkSkillsInstalled(home, cwd) {
5347
- const srcDir = cwd ? join24(cwd, "packages", "skills") : void 0;
5551
+ const srcDir = cwd ? join25(cwd, "packages", "skills") : void 0;
5348
5552
  if (srcDir && existsSync13(srcDir)) {
5349
5553
  const found = findInstalledSkillMd(srcDir);
5350
5554
  if (found.length > 0) {
@@ -5358,7 +5562,7 @@ function checkSkillsInstalled(home, cwd) {
5358
5562
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
5359
5563
  }
5360
5564
  }
5361
- const skillsDir = join24(home, ".claude", "skills");
5565
+ const skillsDir = join25(home, ".claude", "skills");
5362
5566
  if (existsSync13(skillsDir)) {
5363
5567
  const found = findInstalledSkillMd(skillsDir);
5364
5568
  if (found.length > 0) {
@@ -5369,10 +5573,10 @@ function checkSkillsInstalled(home, cwd) {
5369
5573
  }
5370
5574
  function checkDuplicateSkills(home) {
5371
5575
  const plugin = findPlugin(home);
5372
- const skillsDir = join24(home, ".claude", "skills");
5576
+ const skillsDir = join25(home, ".claude", "skills");
5373
5577
  const agentSkillDirs = [
5374
- { label: "~/.codex/skills/", path: join24(home, ".codex", "skills") },
5375
- { label: "~/.agents/skills/", path: join24(home, ".agents", "skills") }
5578
+ { label: "~/.codex/skills/", path: join25(home, ".codex", "skills") },
5579
+ { label: "~/.agents/skills/", path: join25(home, ".agents", "skills") }
5376
5580
  ];
5377
5581
  if (!plugin) {
5378
5582
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -5475,7 +5679,7 @@ async function checkProfiles(home) {
5475
5679
  }
5476
5680
  async function checkProjectLocalOverride(cwd) {
5477
5681
  const dir = cwd ?? process.cwd();
5478
- const envPath = join24(dir, ".skillwiki", ".env");
5682
+ const envPath = join25(dir, ".skillwiki", ".env");
5479
5683
  if (existsSync13(envPath)) {
5480
5684
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
5481
5685
  }
@@ -5485,7 +5689,7 @@ function checkVaultGitRemote(resolvedPath) {
5485
5689
  if (resolvedPath === void 0) {
5486
5690
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
5487
5691
  }
5488
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5692
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5489
5693
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
5490
5694
  }
5491
5695
  try {
@@ -5508,9 +5712,9 @@ function checkObsidianTemplates(resolvedPath) {
5508
5712
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
5509
5713
  }
5510
5714
  const missing = [];
5511
- if (!existsSync13(join24(resolvedPath, "_Templates"))) missing.push("_Templates/");
5512
- if (!existsSync13(join24(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5513
- if (!existsSync13(join24(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5715
+ if (!existsSync13(join25(resolvedPath, "_Templates"))) missing.push("_Templates/");
5716
+ if (!existsSync13(join25(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
5717
+ if (!existsSync13(join25(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
5514
5718
  if (missing.length === 0) {
5515
5719
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
5516
5720
  }
@@ -5520,7 +5724,7 @@ function checkDotStoreClean(resolvedPath) {
5520
5724
  if (resolvedPath === void 0) {
5521
5725
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
5522
5726
  }
5523
- const rawDir = join24(resolvedPath, "raw");
5727
+ const rawDir = join25(resolvedPath, "raw");
5524
5728
  if (!existsSync13(rawDir)) {
5525
5729
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
5526
5730
  }
@@ -5536,7 +5740,7 @@ function checkDotStoreClean(resolvedPath) {
5536
5740
  if (entry.name === ".DS_Store") {
5537
5741
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
5538
5742
  } else if (entry.isDirectory()) {
5539
- walk(join24(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
5743
+ walk(join25(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
5540
5744
  }
5541
5745
  }
5542
5746
  })(rawDir, "");
@@ -5567,7 +5771,7 @@ function checkSyncLastPush(resolvedPath) {
5567
5771
  if (resolvedPath === void 0) {
5568
5772
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
5569
5773
  }
5570
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5774
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5571
5775
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
5572
5776
  }
5573
5777
  let timestamp;
@@ -5616,7 +5820,7 @@ function checkVaultGitDirty(resolvedPath) {
5616
5820
  if (resolvedPath === void 0) {
5617
5821
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
5618
5822
  }
5619
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5823
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5620
5824
  return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
5621
5825
  }
5622
5826
  try {
@@ -5685,7 +5889,7 @@ function remoteMainHash(resolvedPath) {
5685
5889
  }
5686
5890
  function checkStaleRemoteMain(resolvedPath) {
5687
5891
  if (resolvedPath === void 0) return void 0;
5688
- if (!existsSync13(join24(resolvedPath, ".git"))) return void 0;
5892
+ if (!existsSync13(join25(resolvedPath, ".git"))) return void 0;
5689
5893
  const localOrigin = gitRefHash(resolvedPath, "origin/main");
5690
5894
  if (!localOrigin) return void 0;
5691
5895
  const remoteMain = remoteMainHash(resolvedPath);
@@ -5701,7 +5905,7 @@ function checkVaultLocalGit(resolvedPath) {
5701
5905
  if (resolvedPath === void 0) {
5702
5906
  return check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved");
5703
5907
  }
5704
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5908
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5705
5909
  return check("warn", "vault_local_git", "Vault local git", "Not a git repository - sync features unavailable");
5706
5910
  }
5707
5911
  try {
@@ -5720,7 +5924,7 @@ function checkVaultGithubRemote(resolvedPath, exec) {
5720
5924
  if (resolvedPath === void 0) {
5721
5925
  return check("pass", "vault_github_remote", "Vault GitHub remote", "No vault path \u2014 check skipped");
5722
5926
  }
5723
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5927
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5724
5928
  return check("pass", "vault_github_remote", "Vault GitHub remote", "No git repo \u2014 check skipped");
5725
5929
  }
5726
5930
  const state = probeGithubReachability(resolvedPath, exec);
@@ -5764,7 +5968,7 @@ function checkVaultPromotionLag(resolvedPath) {
5764
5968
  if (resolvedPath === void 0) {
5765
5969
  return check("pass", "vault_promotion_lag", "Vault promotion lag", "No vault path \u2014 check skipped");
5766
5970
  }
5767
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5971
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5768
5972
  return check("pass", "vault_promotion_lag", "Vault promotion lag", "No git repo \u2014 check skipped");
5769
5973
  }
5770
5974
  try {
@@ -5791,7 +5995,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
5791
5995
  if (resolvedPath === void 0) {
5792
5996
  return check("pass", id, label, "No vault path \u2014 check skipped");
5793
5997
  }
5794
- if (!existsSync13(join24(resolvedPath, ".git"))) {
5998
+ if (!existsSync13(join25(resolvedPath, ".git"))) {
5795
5999
  return check("pass", id, label, "No git repo \u2014 check skipped");
5796
6000
  }
5797
6001
  if (!hasOriginMain(resolvedPath)) {
@@ -5908,11 +6112,11 @@ async function checkFleetIdentity(input) {
5908
6112
  }
5909
6113
  function pullLogPaths(home) {
5910
6114
  const paths = platform2() === "darwin" ? [
5911
- join24(home, "Library", "Logs", "wiki-pull.log"),
5912
- join24(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
6115
+ join25(home, "Library", "Logs", "wiki-pull.log"),
6116
+ join25(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
5913
6117
  ] : [
5914
- join24(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
5915
- join24(home, "Library", "Logs", "wiki-pull.log")
6118
+ join25(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
6119
+ join25(home, "Library", "Logs", "wiki-pull.log")
5916
6120
  ];
5917
6121
  return [...new Set(paths)];
5918
6122
  }
@@ -5952,7 +6156,7 @@ function checkS3MountPerf(resolvedPath) {
5952
6156
  return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
5953
6157
  }
5954
6158
  const mountPoint = fuse.mountPoint;
5955
- const conceptsDir = join24(resolvedPath, "concepts");
6159
+ const conceptsDir = join25(resolvedPath, "concepts");
5956
6160
  if (!existsSync13(conceptsDir)) {
5957
6161
  return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
5958
6162
  }
@@ -6135,7 +6339,7 @@ function checkWriteTest(resolvedPath) {
6135
6339
  if (!fuse) {
6136
6340
  return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
6137
6341
  }
6138
- const conceptsDir = join24(resolvedPath, "concepts");
6342
+ const conceptsDir = join25(resolvedPath, "concepts");
6139
6343
  if (!existsSync13(conceptsDir)) {
6140
6344
  return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
6141
6345
  }
@@ -6237,7 +6441,7 @@ function checkVaultSyncPullHelper(home, env) {
6237
6441
  );
6238
6442
  }
6239
6443
  function checkVaultSyncReviewRequiredJournals(vaultPath) {
6240
- if (!vaultPath || !existsSync13(join24(vaultPath, ".git"))) {
6444
+ if (!vaultPath || !existsSync13(join25(vaultPath, ".git"))) {
6241
6445
  return check("pass", "vault_sync_review_required_journals", "Review-required journals", "No git vault \u2014 check skipped");
6242
6446
  }
6243
6447
  try {
@@ -6258,7 +6462,7 @@ function checkVaultSyncReviewRequiredJournals(vaultPath) {
6258
6462
  }
6259
6463
  function readVaultSyncConfig(home) {
6260
6464
  try {
6261
- const content = readFileSync10(join24(home, ".skillwiki", ".env"), "utf8");
6465
+ const content = readFileSync10(join25(home, ".skillwiki", ".env"), "utf8");
6262
6466
  let installed = false;
6263
6467
  let role;
6264
6468
  let serviceScope;
@@ -6326,14 +6530,14 @@ function vaultSyncChecks(input) {
6326
6530
  ];
6327
6531
  }
6328
6532
  const isMac = os === "darwin";
6329
- const logDir = input.logDir ?? (isMac ? join24(home, "Library", "Logs") : join24(home, ".local", "state", "vault-sync", "log"));
6330
- const shareDir = input.shareDir ?? (isMac ? join24(home, "Library", "Application Support", "vault-sync", "bin") : join24(home, ".local", "share", "vault-sync", "bin"));
6331
- const filterPath = input.filterPath ?? join24(home, ".config", "rclone", "wiki-push-filters.txt");
6332
- const packagedSnapshotPath = join24(shareDir, "wiki-snapshot.sh");
6533
+ const logDir = input.logDir ?? (isMac ? join25(home, "Library", "Logs") : join25(home, ".local", "state", "vault-sync", "log"));
6534
+ const shareDir = input.shareDir ?? (isMac ? join25(home, "Library", "Application Support", "vault-sync", "bin") : join25(home, ".local", "share", "vault-sync", "bin"));
6535
+ const filterPath = input.filterPath ?? join25(home, ".config", "rclone", "wiki-push-filters.txt");
6536
+ const packagedSnapshotPath = join25(shareDir, "wiki-snapshot.sh");
6333
6537
  const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
6334
6538
  const snapshotPath = input.snapshotScriptPath ?? (existsSync13(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
6335
6539
  function snapshotLastStatusCheck() {
6336
- const snapshotLog = join24(logDir, "wiki-snapshot.log");
6540
+ const snapshotLog = join25(logDir, "wiki-snapshot.log");
6337
6541
  try {
6338
6542
  const logContent = readFileSync10(snapshotLog, "utf8");
6339
6543
  const lines = logContent.trim().split("\n").filter(Boolean);
@@ -6382,7 +6586,7 @@ function vaultSyncChecks(input) {
6382
6586
  if (input.vaultSyncRole === "snapshotter") {
6383
6587
  const c12 = existsSync13(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}`);
6384
6588
  const serviceScope = input.vaultSyncServiceScope ?? "user";
6385
- const userTimerPath = join24(home, ".config", "systemd", "user", "wiki-snapshot.timer");
6589
+ const userTimerPath = join25(home, ".config", "systemd", "user", "wiki-snapshot.timer");
6386
6590
  const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
6387
6591
  let c22;
6388
6592
  if (serviceScope === "user" && existsSync13(userTimerPath)) {
@@ -6454,7 +6658,7 @@ function vaultSyncChecks(input) {
6454
6658
  }
6455
6659
  return [c12, c22, c32, cFetch2, c42, c52];
6456
6660
  }
6457
- const pushScriptPath = join24(shareDir, "wiki-push.sh");
6661
+ const pushScriptPath = join25(shareDir, "wiki-push.sh");
6458
6662
  const c1 = existsSync13(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`);
6459
6663
  let c2;
6460
6664
  try {
@@ -6506,7 +6710,7 @@ function vaultSyncChecks(input) {
6506
6710
  "Scheduler check failed \u2014 run vault-sync-install"
6507
6711
  );
6508
6712
  }
6509
- const logFile = join24(logDir, "wiki-push.log");
6713
+ const logFile = join25(logDir, "wiki-push.log");
6510
6714
  let c3;
6511
6715
  try {
6512
6716
  const logContent = readFileSync10(logFile, "utf8");
@@ -6577,7 +6781,7 @@ function vaultSyncChecks(input) {
6577
6781
  `Log directory not found at ${logDir}`
6578
6782
  );
6579
6783
  }
6580
- const fetchLogFile = join24(logDir, "wiki-fetch.log");
6784
+ const fetchLogFile = join25(logDir, "wiki-fetch.log");
6581
6785
  let cFetch;
6582
6786
  try {
6583
6787
  const logContent = readFileSync10(fetchLogFile, "utf8");
@@ -6721,15 +6925,15 @@ function findSkillMd(dir) {
6721
6925
  }
6722
6926
  for (const entry of entries) {
6723
6927
  if (entry.isFile() && entry.name === "SKILL.md") {
6724
- results.push(join24(dir, entry.name));
6928
+ results.push(join25(dir, entry.name));
6725
6929
  } else if (entry.isDirectory()) {
6726
- results.push(...findSkillMd(join24(dir, entry.name)));
6930
+ results.push(...findSkillMd(join25(dir, entry.name)));
6727
6931
  }
6728
6932
  }
6729
6933
  return results;
6730
6934
  }
6731
6935
  function findInstalledSkillMd(dir) {
6732
- const directSkills = findSkillNames(dir).map((name) => join24(dir, name, "SKILL.md"));
6936
+ const directSkills = findSkillNames(dir).map((name) => join25(dir, name, "SKILL.md"));
6733
6937
  return directSkills.length > 0 ? directSkills : findSkillMd(dir);
6734
6938
  }
6735
6939
  function findSkillNames(dir) {
@@ -6741,7 +6945,7 @@ function findSkillNames(dir) {
6741
6945
  return results;
6742
6946
  }
6743
6947
  for (const entry of entries) {
6744
- if (entry.isDirectory() && existsSync13(join24(dir, entry.name, "SKILL.md"))) {
6948
+ if (entry.isDirectory() && existsSync13(join25(dir, entry.name, "SKILL.md"))) {
6745
6949
  results.push(entry.name);
6746
6950
  }
6747
6951
  }
@@ -6789,7 +6993,7 @@ async function vaultMetrics(resolvedPath) {
6789
6993
  }
6790
6994
  let logLines = 0;
6791
6995
  try {
6792
- logLines = readFileSync10(join24(scanRoot, "log.md"), "utf8").split("\n").length;
6996
+ logLines = readFileSync10(join25(scanRoot, "log.md"), "utf8").split("\n").length;
6793
6997
  } catch {
6794
6998
  }
6795
6999
  return [
@@ -6921,9 +7125,9 @@ function readCliPackageJson(baseUrl = import.meta.url) {
6921
7125
  }
6922
7126
 
6923
7127
  // src/commands/observe.ts
6924
- import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
7128
+ import { mkdir as mkdir6, writeFile as writeFile4 } from "fs/promises";
6925
7129
  import { existsSync as existsSync14, statSync as statSync3 } from "fs";
6926
- import { join as join25 } from "path";
7130
+ import { join as join26 } from "path";
6927
7131
  import { createHash as createHash6 } from "crypto";
6928
7132
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
6929
7133
  function slugify(text) {
@@ -6952,9 +7156,9 @@ async function runObserve(input) {
6952
7156
  result: err("VAULT_PATH_INVALID", { path: input.vault })
6953
7157
  };
6954
7158
  }
6955
- const transcriptsDir = join25(input.vault, "raw", "transcripts");
7159
+ const transcriptsDir = join26(input.vault, "raw", "transcripts");
6956
7160
  try {
6957
- await mkdir5(transcriptsDir, { recursive: true });
7161
+ await mkdir6(transcriptsDir, { recursive: true });
6958
7162
  } catch {
6959
7163
  return {
6960
7164
  exitCode: ExitCode.VAULT_PATH_INVALID,
@@ -6964,7 +7168,7 @@ async function runObserve(input) {
6964
7168
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6965
7169
  const slug = slugify(input.text);
6966
7170
  const fileName = `${today}-observation-${slug}.md`;
6967
- const filePath = join25(transcriptsDir, fileName);
7171
+ const filePath = join26(transcriptsDir, fileName);
6968
7172
  const body = `
6969
7173
  ${input.text.trim()}
6970
7174
  `;
@@ -7005,8 +7209,8 @@ ${input.text.trim()}
7005
7209
 
7006
7210
  // src/commands/memory.ts
7007
7211
  import { createHash as createHash7 } from "crypto";
7008
- import { mkdir as mkdir6, readFile as readFile16, readdir as readdir4, stat as stat5, writeFile as writeFile5 } from "fs/promises";
7009
- import { basename as basename2, extname, join as join26, relative as relative3, sep as sep3 } from "path";
7212
+ import { mkdir as mkdir7, readFile as readFile17, readdir as readdir5, stat as stat5, writeFile as writeFile5 } from "fs/promises";
7213
+ import { basename as basename2, extname, join as join27, relative as relative3, sep as sep3 } from "path";
7010
7214
 
7011
7215
  // src/utils/memory-authority.ts
7012
7216
  var TIER_RANK = {
@@ -7125,8 +7329,8 @@ async function runMemoryIndex(input) {
7125
7329
  }
7126
7330
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
7127
7331
  const relCachePath = memoryCacheRelPath(input.project);
7128
- const absCachePath = join26(input.vault, relCachePath);
7129
- await mkdir6(join26(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
7332
+ const absCachePath = join27(input.vault, relCachePath);
7333
+ await mkdir7(join27(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
7130
7334
  await writeFile5(absCachePath, `${JSON.stringify({
7131
7335
  generated_at: generatedAt,
7132
7336
  project: input.project,
@@ -7318,7 +7522,7 @@ async function buildMemoryIndexState(pages, project) {
7318
7522
  }
7319
7523
  async function checkMemoryIndex(vault, project, current) {
7320
7524
  const relCachePath = memoryCacheRelPath(project);
7321
- const cacheText = await readIfExists2(join26(vault, relCachePath));
7525
+ const cacheText = await readIfExists2(join27(vault, relCachePath));
7322
7526
  if (!cacheText) {
7323
7527
  return {
7324
7528
  ok: true,
@@ -7711,7 +7915,7 @@ function renderMemoryIndexStatusHint(status) {
7711
7915
  }
7712
7916
  async function readIfExists2(path) {
7713
7917
  try {
7714
- return await readFile16(path, "utf8");
7918
+ return await readFile17(path, "utf8");
7715
7919
  } catch {
7716
7920
  return "";
7717
7921
  }
@@ -7724,10 +7928,10 @@ async function collectImportFiles(source) {
7724
7928
  return files.sort((a, b) => a.localeCompare(b));
7725
7929
  }
7726
7930
  async function walkImportFiles(dir, out) {
7727
- const entries = await readdir4(dir, { withFileTypes: true });
7931
+ const entries = await readdir5(dir, { withFileTypes: true });
7728
7932
  for (const entry of entries) {
7729
7933
  if (entry.name === ".git" || entry.name === "node_modules") continue;
7730
- const path = join26(dir, entry.name);
7934
+ const path = join27(dir, entry.name);
7731
7935
  if (entry.isDirectory()) {
7732
7936
  await walkImportFiles(path, out);
7733
7937
  } else if (entry.isFile() && isImportCandidate(path)) {
@@ -7742,7 +7946,7 @@ function isImportCandidate(path) {
7742
7946
  async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
7743
7947
  const st = await stat5(file);
7744
7948
  const sourceKind = classifyImportSource(file);
7745
- const hash = createHash7("sha256").update(await readFile16(file)).digest("hex");
7949
+ const hash = createHash7("sha256").update(await readFile17(file)).digest("hex");
7746
7950
  const baseEntry = {
7747
7951
  source_path: file,
7748
7952
  source_kind: sourceKind,
@@ -7765,7 +7969,7 @@ async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
7765
7969
  reason: "policy_source_not_imported"
7766
7970
  };
7767
7971
  }
7768
- const text = await readFile16(file, "utf8");
7972
+ const text = await readFile17(file, "utf8");
7769
7973
  const extracted = extractImportText(text, sourceKind);
7770
7974
  if (!extracted) {
7771
7975
  return {
@@ -7794,8 +7998,8 @@ async function writeImportCapture(vault, entry, today) {
7794
7998
  const content = hiddenString(entry, "__content");
7795
7999
  const project = hiddenString(entry, "__project");
7796
8000
  const relPath = await availableImportPath(vault, entry.proposed_path);
7797
- const absPath = join26(vault, relPath);
7798
- await mkdir6(join26(vault, "raw", "transcripts"), { recursive: true });
8001
+ const absPath = join27(vault, relPath);
8002
+ await mkdir7(join27(vault, "raw", "transcripts"), { recursive: true });
7799
8003
  await writeFile5(absPath, renderImportCapture(entry, content, project, today), "utf8");
7800
8004
  const validation = await runValidate({ file: absPath });
7801
8005
  return {
@@ -7811,7 +8015,7 @@ async function availableImportPath(vault, proposed) {
7811
8015
  const stem = proposed.slice(0, -ext.length);
7812
8016
  let candidate = proposed;
7813
8017
  let i = 2;
7814
- while (await readIfExists2(join26(vault, candidate))) {
8018
+ while (await readIfExists2(join27(vault, candidate))) {
7815
8019
  candidate = `${stem}-${i}${ext}`;
7816
8020
  i++;
7817
8021
  }
@@ -7996,10 +8200,10 @@ function memoryCacheRelPath(project) {
7996
8200
  }
7997
8201
  async function readMemoryCache(vault, project) {
7998
8202
  if (project) {
7999
- const projectCache = await readIfExists2(join26(vault, memoryCacheRelPath(project)));
8203
+ const projectCache = await readIfExists2(join27(vault, memoryCacheRelPath(project)));
8000
8204
  if (projectCache) return projectCache;
8001
8205
  }
8002
- return readIfExists2(join26(vault, ".skillwiki", "memory-topics.json"));
8206
+ return readIfExists2(join27(vault, ".skillwiki", "memory-topics.json"));
8003
8207
  }
8004
8208
  function dedupePages(pages) {
8005
8209
  const seen = /* @__PURE__ */ new Set();
@@ -8090,8 +8294,8 @@ function slugify2(value) {
8090
8294
  }
8091
8295
 
8092
8296
  // src/commands/query.ts
8093
- import { readFile as readFile17, stat as stat6 } from "fs/promises";
8094
- import { join as join27 } from "path";
8297
+ import { readFile as readFile18, stat as stat6 } from "fs/promises";
8298
+ import { join as join28 } from "path";
8095
8299
  var W_KEYWORD = 2;
8096
8300
  var W_SOURCE_OVERLAP = 4;
8097
8301
  var W_WIKILINK = 3;
@@ -8212,7 +8416,7 @@ function computeKeywordScore(terms, title, tags, body) {
8212
8416
  return score;
8213
8417
  }
8214
8418
  async function loadOrBuildGraph(vault) {
8215
- const graphPath = join27(vault, ".skillwiki", "graph.json");
8419
+ const graphPath = join28(vault, ".skillwiki", "graph.json");
8216
8420
  let needsBuild = false;
8217
8421
  try {
8218
8422
  const fileStat = await stat6(graphPath);
@@ -8226,7 +8430,7 @@ async function loadOrBuildGraph(vault) {
8226
8430
  if (buildResult.exitCode !== 0) return null;
8227
8431
  }
8228
8432
  try {
8229
- const raw = await readFile17(graphPath, "utf8");
8433
+ const raw = await readFile18(graphPath, "utf8");
8230
8434
  return JSON.parse(raw);
8231
8435
  } catch {
8232
8436
  return null;
@@ -8241,7 +8445,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
8241
8445
  import { z } from "zod";
8242
8446
 
8243
8447
  // src/mcp/vault-resolve.ts
8244
- import { join as join28, resolve as resolve7 } from "path";
8448
+ import { join as join29, resolve as resolve7 } from "path";
8245
8449
 
8246
8450
  // src/mcp/allowlist.ts
8247
8451
  import { resolve as resolve6, sep as sep4 } from "path";
@@ -8303,7 +8507,7 @@ async function resolveMcpVault(input) {
8303
8507
  return ok({ vault: vaultPath, source });
8304
8508
  }
8305
8509
  function defaultGraphOut(vault) {
8306
- return join28(vault, ".skillwiki", "graph.json");
8510
+ return join29(vault, ".skillwiki", "graph.json");
8307
8511
  }
8308
8512
 
8309
8513
  // src/mcp/result-format.ts
@@ -8320,7 +8524,7 @@ function formatToolResult(payload) {
8320
8524
  // src/mcp/audit-log.ts
8321
8525
  import { appendFileSync, mkdirSync as mkdirSync5 } from "fs";
8322
8526
  import { homedir } from "os";
8323
- import { join as join29 } from "path";
8527
+ import { join as join30 } from "path";
8324
8528
  function auditEnabled() {
8325
8529
  const v = process.env.SKILLWIKI_MCP_AUDIT;
8326
8530
  if (v === "0" || v === "false") return false;
@@ -8332,7 +8536,7 @@ function auditSink() {
8332
8536
  function auditFilePath() {
8333
8537
  const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
8334
8538
  if (custom && custom.length > 0) return custom;
8335
- return join29(homedir(), ".skillwiki", "mcp-audit.jsonl");
8539
+ return join30(homedir(), ".skillwiki", "mcp-audit.jsonl");
8336
8540
  }
8337
8541
  function auditMcpToolCall(entry) {
8338
8542
  if (!auditEnabled()) return;
@@ -8342,7 +8546,7 @@ function auditMcpToolCall(entry) {
8342
8546
  return;
8343
8547
  }
8344
8548
  const path = auditFilePath();
8345
- mkdirSync5(join29(path, ".."), { recursive: true });
8549
+ mkdirSync5(join30(path, ".."), { recursive: true });
8346
8550
  appendFileSync(path, line, "utf8");
8347
8551
  }
8348
8552
  async function runMcpToolHandler(tool, input, fn) {
@@ -8562,8 +8766,8 @@ function registerMcpMutatingTools(server) {
8562
8766
  }
8563
8767
 
8564
8768
  // src/mcp/resources.ts
8565
- import { readFile as readFile19 } from "fs/promises";
8566
- import { join as join31 } from "path";
8769
+ import { readFile as readFile20 } from "fs/promises";
8770
+ import { join as join32 } from "path";
8567
8771
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
8568
8772
 
8569
8773
  // src/mcp/lint-bucket.ts
@@ -8691,8 +8895,8 @@ async function fetchQueryPreview(input) {
8691
8895
  }
8692
8896
 
8693
8897
  // src/mcp/graph-html.ts
8694
- import { readFile as readFile18 } from "fs/promises";
8695
- import { join as join30 } from "path";
8898
+ import { readFile as readFile19 } from "fs/promises";
8899
+ import { join as join31 } from "path";
8696
8900
  import { existsSync as existsSync15 } from "fs";
8697
8901
  var TYPE_COLORS = {
8698
8902
  entities: "#e74c3c",
@@ -8761,7 +8965,7 @@ ${nodeSvg}
8761
8965
  return { html, node_count: nodes.length, edge_count: edges.length, truncated };
8762
8966
  }
8763
8967
  async function fetchGraphHtmlReport(input) {
8764
- const graphPath = input.graphPath ?? join30(input.vault, ".skillwiki", "graph.json");
8968
+ const graphPath = input.graphPath ?? join31(input.vault, ".skillwiki", "graph.json");
8765
8969
  const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
8766
8970
  if (!existsSync15(graphPath)) {
8767
8971
  return {
@@ -8771,7 +8975,7 @@ async function fetchGraphHtmlReport(input) {
8771
8975
  }
8772
8976
  let raw;
8773
8977
  try {
8774
- raw = await readFile18(graphPath, "utf8");
8978
+ raw = await readFile19(graphPath, "utf8");
8775
8979
  } catch (e) {
8776
8980
  return {
8777
8981
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -8835,7 +9039,7 @@ async function fetchStaleSummary(input) {
8835
9039
 
8836
9040
  // src/mcp/resources.ts
8837
9041
  async function readVaultFile(vault, rel) {
8838
- return readFile19(join31(vault, rel), "utf8");
9042
+ return readFile20(join32(vault, rel), "utf8");
8839
9043
  }
8840
9044
  async function tailLines(text, lines) {
8841
9045
  const parts = text.split(/\r?\n/);
@@ -8921,9 +9125,9 @@ function registerMcpResources(server) {
8921
9125
  if (!v.ok) {
8922
9126
  return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
8923
9127
  }
8924
- const path = join31(v.data.vault, ".skillwiki", "graph.json");
9128
+ const path = join32(v.data.vault, ".skillwiki", "graph.json");
8925
9129
  try {
8926
- const raw = await readFile19(path, "utf8");
9130
+ const raw = await readFile20(path, "utf8");
8927
9131
  const graph = JSON.parse(raw);
8928
9132
  const adjacency = graph.adjacency ?? {};
8929
9133
  const nodes = Object.keys(adjacency);
@@ -9236,6 +9440,8 @@ export {
9236
9440
  readLastOp,
9237
9441
  appendLastOp,
9238
9442
  clearLastOp,
9443
+ writeLogEvent,
9444
+ readLogEvents,
9239
9445
  runLogAppend,
9240
9446
  renderIndexUpsert,
9241
9447
  upsertIndexEntry,
@@ -9266,6 +9472,7 @@ export {
9266
9472
  runStale,
9267
9473
  runPagesize,
9268
9474
  runLogRotate,
9475
+ scanConflictMarkerBlocksInText,
9269
9476
  runTopicMapCheck,
9270
9477
  runIndexLinkFormat,
9271
9478
  normalizeRemoteRoot,