skillwiki 0.10.86 → 0.10.88

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.
@@ -40,7 +40,7 @@ import {
40
40
  snapshotterAliasForLocalHost,
41
41
  toUndirectedWeighted,
42
42
  writeDotenv
43
- } from "./chunk-BO7JIJIA.js";
43
+ } from "./chunk-6WE7E6VB.js";
44
44
  import {
45
45
  atomicWriteText,
46
46
  prepareTypedPage
@@ -57,11 +57,11 @@ import {
57
57
  planSourceCompileRelease,
58
58
  planSourceReview,
59
59
  runSourcesPending
60
- } from "./chunk-CGQQS7MO.js";
60
+ } from "./chunk-ECOEJABP.js";
61
61
  import {
62
62
  eventPathFor,
63
63
  writeLogEvent
64
- } from "./chunk-5BNCYHRN.js";
64
+ } from "./chunk-QFRZDTJY.js";
65
65
  import {
66
66
  CompoundSchema,
67
67
  ExitCode,
@@ -4496,6 +4496,7 @@ var HYGIENE_COMMANDS = /* @__PURE__ */ new Set([
4496
4496
  "lint",
4497
4497
  "lint --fix",
4498
4498
  "status",
4499
+ "copy-status",
4499
4500
  "path",
4500
4501
  "fleet context",
4501
4502
  "fleet validate",
@@ -6462,7 +6463,7 @@ async function runQuery(input) {
6462
6463
  }
6463
6464
  let pendingSources;
6464
6465
  if (input.includePending) {
6465
- const { runSourcesPending: runSourcesPending2 } = await import("./sources-TNX2VEMB.js");
6466
+ const { runSourcesPending: runSourcesPending2 } = await import("./sources-A5GFRY3F.js");
6466
6467
  const pending = await runSourcesPending2({
6467
6468
  vault: input.vault,
6468
6469
  match: input.text,
@@ -22,7 +22,7 @@ import {
22
22
  resolveExistingRegularFileInsideVault,
23
23
  stripFencedBlocks,
24
24
  writeLogEvent
25
- } from "./chunk-5BNCYHRN.js";
25
+ } from "./chunk-QFRZDTJY.js";
26
26
  import {
27
27
  ExitCode,
28
28
  FleetManifestSchema,
@@ -3333,6 +3333,7 @@ function buildCliSurface() {
3333
3333
  program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
3334
3334
  program.command("doctor").option("--check-snapshotter").option("--check-mcp");
3335
3335
  program.command("status").option("--wiki <name>");
3336
+ program.command("copy-status").option("--wiki <name>");
3336
3337
  program.command("archive").option("--wiki <name>").option("--cascade").option("--apply").option("--approve <token>").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>");
3337
3338
  program.command("remove").option("--wiki <name>").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>").option("--reason <text>");
3338
3339
  program.command("drift").option("--apply").option("--affected-pages").option("--new <date>").option("--wiki <name>");
@@ -3405,7 +3406,7 @@ function buildCliSurface() {
3405
3406
  const indexCmd = program.commands.find((c) => c.name() === "index");
3406
3407
  indexCmd.command("rebuild").option("--write").option("--wiki <name>");
3407
3408
  const projectionsCmd = program.commands.find((c) => c.name() === "projections");
3408
- projectionsCmd.command("materialize").option("--write").option("--converge-vault <dir>").option("--wiki <name>");
3409
+ projectionsCmd.command("materialize").option("--write").option("--converge-vault <dir>").option("--events-from <dir>").option("--wiki <name>");
3409
3410
  projectionsCmd.command("repair-legacy").requiredOption("--event-operation-id <id>").option("--write").option("--converge-vault <dir>").option("--wiki <name>");
3410
3411
  const syncCmd = program.commands.find((c) => c.name() === "sync");
3411
3412
  syncCmd.command("status").option("--wiki <name>").option("--include-stashes").option("--include-remote-health").option("--check-snapshotter");
@@ -5339,9 +5340,14 @@ async function runSyncLintDelta(input) {
5339
5340
 
5340
5341
  // src/utils/git.ts
5341
5342
  import { execFileSync as execFileSync2 } from "child_process";
5342
- function git(cwd, args) {
5343
+ function git(cwd, args, opts) {
5343
5344
  try {
5344
- return execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5345
+ return execFileSync2("git", args, {
5346
+ cwd,
5347
+ encoding: "utf8",
5348
+ stdio: ["pipe", "pipe", "pipe"],
5349
+ ...opts?.timeoutMs !== void 0 ? { timeout: opts.timeoutMs } : {}
5350
+ }).trim();
5345
5351
  } catch {
5346
5352
  return "";
5347
5353
  }
@@ -7413,6 +7419,7 @@ export {
7413
7419
  VAULT_GENERATED_COMMIT_PATHS,
7414
7420
  VAULT_COMMIT_PATHSPEC,
7415
7421
  stageVaultContentChanges,
7422
+ REMOTE_PROBE_TIMEOUT_MS,
7416
7423
  resolveWikiS3Remote,
7417
7424
  probeGithubReachability,
7418
7425
  probeS3Reachability,
@@ -8,7 +8,7 @@ import {
8
8
  readLogEvents,
9
9
  resolveExistingRegularFileInsideVault,
10
10
  writeLogEvent
11
- } from "./chunk-5BNCYHRN.js";
11
+ } from "./chunk-QFRZDTJY.js";
12
12
  import {
13
13
  ExitCode,
14
14
  RawSourceSchema,
@@ -203,8 +203,8 @@ async function writeLogEvent(vault, event) {
203
203
  return err("WRITE_FAILED", { path: rel, message: String(error) });
204
204
  }
205
205
  }
206
- async function readLogEvents(vault) {
207
- const root = join(vault, "meta", "log-events");
206
+ async function readLogEvents(vault, opts) {
207
+ const root = opts?.eventsRoot ?? join(vault, "meta", "log-events");
208
208
  let days;
209
209
  try {
210
210
  days = (await readdir(root, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name).sort();
package/dist/cli.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  snapshotterHealthChecks,
53
53
  upsertIndexEntry,
54
54
  vectorIndexStatus
55
- } from "./chunk-GZKT2XC5.js";
55
+ } from "./chunk-5QFHISNH.js";
56
56
  import {
57
57
  normalizeDistTag,
58
58
  readCache,
@@ -69,6 +69,7 @@ import {
69
69
  import {
70
70
  FLEET_REL_PATH,
71
71
  REDACTED_MALFORMED_REFERENCE,
72
+ REMOTE_PROBE_TIMEOUT_MS,
72
73
  VAULT_COMMIT_PATHSPEC,
73
74
  VAULT_GENERATED_COMMIT_PATHS,
74
75
  VAULT_HYGIENE_GITIGNORE_PATTERNS,
@@ -109,6 +110,7 @@ import {
109
110
  parseJournalEnv,
110
111
  planAndMaybePruneRemoteObjects,
111
112
  planRawStructuralMove,
113
+ probeS3Reachability,
112
114
  profileKey,
113
115
  readJournal,
114
116
  readLastOp,
@@ -119,6 +121,7 @@ import {
119
121
  resolveFleetHostId,
120
122
  resolveInitTimePath,
121
123
  resolveRuntimePath,
124
+ resolveWikiS3Remote,
122
125
  rewriteRawSourceReferences,
123
126
  runAudit,
124
127
  runDedup,
@@ -153,7 +156,7 @@ import {
153
156
  supersedeStaleReviewRequiredJournals,
154
157
  taxonomyCommentForPage,
155
158
  writeDotenv
156
- } from "./chunk-BO7JIJIA.js";
159
+ } from "./chunk-6WE7E6VB.js";
157
160
  import {
158
161
  assertTargetInsideVault,
159
162
  atomicWriteText,
@@ -166,7 +169,7 @@ import {
166
169
  inventorySources,
167
170
  planSourceDisposition,
168
171
  runSourcesPending
169
- } from "./chunk-CGQQS7MO.js";
172
+ } from "./chunk-ECOEJABP.js";
170
173
  import {
171
174
  authorizeRawOperation,
172
175
  buildSourceReferenceIndex,
@@ -183,7 +186,7 @@ import {
183
186
  resolveExistingRegularFileInsideVault,
184
187
  validateLogEvent,
185
188
  writeLogEvent
186
- } from "./chunk-5BNCYHRN.js";
189
+ } from "./chunk-QFRZDTJY.js";
187
190
  import {
188
191
  ExitCode,
189
192
  MetaSchema,
@@ -207,7 +210,7 @@ import {
207
210
  } from "./chunk-XJR3XXP5.js";
208
211
 
209
212
  // src/cli.ts
210
- import { join as join42 } from "path";
213
+ import { join as join43 } from "path";
211
214
  import { Command } from "commander";
212
215
 
213
216
  // src/utils/output.ts
@@ -2030,7 +2033,7 @@ async function runLogMigrateLegacy(input) {
2030
2033
  }
2031
2034
 
2032
2035
  // src/commands/projections-materialize.ts
2033
- import { readFileSync as readFileSync7 } from "fs";
2036
+ import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
2034
2037
  import { join as join12 } from "path";
2035
2038
  var defaultDeps = {
2036
2039
  writeText: (path, text) => atomicWriteText(path, text)
@@ -2039,9 +2042,18 @@ async function runProjectionsMaterialize(input, deps = defaultDeps) {
2039
2042
  const fleet = await loadFleetManifestAndHost({ vault: input.vault, hostId: input.hostId });
2040
2043
  const auth = resolveProjectionAuthority(fleet);
2041
2044
  if (!auth.ok) return { exitCode: ExitCode.PREFLIGHT_FAILED, result: auth };
2045
+ if (input.eventsFrom && !existsSync3(input.eventsFrom)) {
2046
+ return {
2047
+ exitCode: ExitCode.PREFLIGHT_FAILED,
2048
+ result: err("PREFLIGHT_FAILED", {
2049
+ reason: "events-from-missing",
2050
+ events_from: input.eventsFrom
2051
+ })
2052
+ };
2053
+ }
2042
2054
  const indexProj = await renderRootIndex({ vault: input.vault });
2043
2055
  if (!indexProj.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: indexProj };
2044
- const events = await readLogEvents(input.vault);
2056
+ const events = await readLogEvents(input.vault, { eventsRoot: input.eventsFrom });
2045
2057
  if (!events.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: events };
2046
2058
  const logText = renderLogProjection(events.data);
2047
2059
  let curIndex = "";
@@ -2382,7 +2394,7 @@ async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
2382
2394
 
2383
2395
  // src/commands/claim.ts
2384
2396
  import { mkdir as mkdir4, writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
2385
- import { existsSync as existsSync3, statSync } from "fs";
2397
+ import { existsSync as existsSync4, statSync } from "fs";
2386
2398
  import { join as join14 } from "path";
2387
2399
  function extractDate(filename) {
2388
2400
  const m = filename.match(/^(\d{4}-\d{2}-\d{2})/);
@@ -2392,11 +2404,11 @@ function extractSlugFromFilename(filename) {
2392
2404
  return filename.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/^(task|bug|idea|note|observation)-/, "").replace(/\.md$/, "");
2393
2405
  }
2394
2406
  async function runClaim(input) {
2395
- if (!existsSync3(input.vault) || !statSync(input.vault).isDirectory()) {
2407
+ if (!existsSync4(input.vault) || !statSync(input.vault).isDirectory()) {
2396
2408
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { path: input.vault }) };
2397
2409
  }
2398
2410
  const absTranscript = join14(input.vault, input.transcript);
2399
- if (!existsSync3(absTranscript)) {
2411
+ if (!existsSync4(absTranscript)) {
2400
2412
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.transcript }) };
2401
2413
  }
2402
2414
  const content = await readFile5(absTranscript, "utf8");
@@ -2421,7 +2433,7 @@ async function runClaim(input) {
2421
2433
  };
2422
2434
  }
2423
2435
  const projectDir = join14(input.vault, "projects", projectSlug);
2424
- if (!existsSync3(projectDir) || !statSync(projectDir).isDirectory()) {
2436
+ if (!existsSync4(projectDir) || !statSync(projectDir).isDirectory()) {
2425
2437
  return {
2426
2438
  exitCode: ExitCode.PROJECT_NOT_FOUND,
2427
2439
  result: err("PROJECT_NOT_FOUND", { project: projectSlug })
@@ -2440,7 +2452,7 @@ async function runClaim(input) {
2440
2452
  const workDir = join14(projectDir, "work", dirName);
2441
2453
  const relWorkDir = `projects/${projectSlug}/work/${dirName}`;
2442
2454
  const relSpecPath = `${relWorkDir}/spec.md`;
2443
- if (existsSync3(workDir)) {
2455
+ if (existsSync4(workDir)) {
2444
2456
  return {
2445
2457
  exitCode: ExitCode.OK,
2446
2458
  result: ok({
@@ -2485,11 +2497,11 @@ async function runClaim(input) {
2485
2497
  }
2486
2498
 
2487
2499
  // src/commands/work-complete.ts
2488
- import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync9, renameSync, writeFileSync as writeFileSync3 } from "fs";
2500
+ import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync9, renameSync, writeFileSync as writeFileSync3 } from "fs";
2489
2501
  import { join as join16 } from "path";
2490
2502
 
2491
2503
  // src/commands/work-validate.ts
2492
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
2504
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
2493
2505
  import { join as join15 } from "path";
2494
2506
  var GOAL_PLAN_MODE_FILES = /* @__PURE__ */ new Set(["spec.md", "plan.md", "build-report.md"]);
2495
2507
  function normalizeWorkItemRel(workItem) {
@@ -2565,7 +2577,7 @@ function validatePrMetadata(meta, findings) {
2565
2577
  async function runWorkValidate(input) {
2566
2578
  const rel = normalizeWorkItemRel(input.workItem);
2567
2579
  const workDir = join15(input.vault, rel);
2568
- if (!existsSync4(workDir)) {
2580
+ if (!existsSync5(workDir)) {
2569
2581
  return {
2570
2582
  exitCode: ExitCode.FILE_NOT_FOUND,
2571
2583
  result: err("FILE_NOT_FOUND", { path: rel })
@@ -2699,7 +2711,7 @@ function workCompleteJournalPath(vault, opId) {
2699
2711
  }
2700
2712
  function readWorkJournal(vault, opId) {
2701
2713
  const path = workCompleteJournalPath(vault, opId);
2702
- if (!existsSync5(path)) return null;
2714
+ if (!existsSync6(path)) return null;
2703
2715
  try {
2704
2716
  return parseJournalEnv(readFileSync9(path, "utf8"));
2705
2717
  } catch {
@@ -2729,10 +2741,10 @@ var PHASE_ORDER = ["validate", "evidence", "log", "projection", "commit", "done"
2729
2741
  function resolveWorkDir(vault, workItem) {
2730
2742
  const rel = normalizeWorkItemRel(workItem);
2731
2743
  const abs = join16(vault, rel);
2732
- if (!existsSync5(abs)) {
2744
+ if (!existsSync6(abs)) {
2733
2745
  return err("FILE_NOT_FOUND", { path: rel });
2734
2746
  }
2735
- if (!existsSync5(join16(abs, "spec.md"))) {
2747
+ if (!existsSync6(join16(abs, "spec.md"))) {
2736
2748
  return err("PREFLIGHT_FAILED", { reason: "missing-spec", path: rel });
2737
2749
  }
2738
2750
  return ok(abs);
@@ -2799,7 +2811,7 @@ async function writeEvidence(workDir, opId, phases, writeText) {
2799
2811
  }
2800
2812
  async function markPlanComplete(workDir) {
2801
2813
  const planPath = join16(workDir, "plan.md");
2802
- if (!existsSync5(planPath)) return ok(null);
2814
+ if (!existsSync6(planPath)) return ok(null);
2803
2815
  return patchFrontmatterStatus(planPath, (body) => body.replace(/- \[ \]/g, "- [x]"));
2804
2816
  }
2805
2817
  function writeFailure(stage, result) {
@@ -2924,7 +2936,7 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2924
2936
  advance("projection");
2925
2937
  }
2926
2938
  if (phaseIndex(phase) <= phaseIndex("projection")) {
2927
- if (!existsSync5(join16(workDir, "evidence.md"))) {
2939
+ if (!existsSync6(join16(workDir, "evidence.md"))) {
2928
2940
  const evidenceWrite = await writeEvidence(
2929
2941
  workDir,
2930
2942
  opId,
@@ -2954,7 +2966,7 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2954
2966
  }
2955
2967
  let committed = false;
2956
2968
  if (phaseIndex(phase) <= phaseIndex("commit")) {
2957
- if (!input.noCommit && existsSync5(join16(input.vault, ".git"))) {
2969
+ if (!input.noCommit && existsSync6(join16(input.vault, ".git"))) {
2958
2970
  try {
2959
2971
  appendLastOp(input.vault, {
2960
2972
  operation: "work-complete",
@@ -3014,7 +3026,7 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
3014
3026
  }
3015
3027
 
3016
3028
  // src/commands/health.ts
3017
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
3029
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
3018
3030
  import { dirname as dirname4, join as join17, resolve as resolve2 } from "path";
3019
3031
  import { platform } from "os";
3020
3032
  function statusFromCounts(counts) {
@@ -3126,7 +3138,7 @@ function summarizeChecks(checks) {
3126
3138
  };
3127
3139
  }
3128
3140
  function classifyLog(path, id, label, okPattern) {
3129
- if (!existsSync6(path)) return { id, label, status: "warn", detail: `log file missing: ${path}` };
3141
+ if (!existsSync7(path)) return { id, label, status: "warn", detail: `log file missing: ${path}` };
3130
3142
  const lines = readFileSync10(path, "utf8").split(/\r?\n/).filter(Boolean);
3131
3143
  if (lines.length === 0) return { id, label, status: "warn", detail: `log file empty: ${path}` };
3132
3144
  const journalFailRe = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z (FAIL|ERROR)\b/;
@@ -3156,7 +3168,7 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
3156
3168
  const vsConfig = readVaultSyncConfig(home);
3157
3169
  const fetchOnly = vsConfig.pushEnabled === false;
3158
3170
  const installedScript = fetchOnly ? join17(shareDir, "wiki-fetch-notify.sh") : join17(shareDir, "wiki-push.sh");
3159
- if (syncMode === "optional" && !existsSync6(installedScript)) {
3171
+ if (syncMode === "optional" && !existsSync7(installedScript)) {
3160
3172
  return {
3161
3173
  status: "pass",
3162
3174
  blocking: false,
@@ -3171,12 +3183,12 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
3171
3183
  }
3172
3184
  if (vsConfig.role === "snapshotter") {
3173
3185
  const snapshotScript = vsConfig.snapshotScript ?? join17(shareDir, "wiki-snapshot.sh");
3174
- const installed = existsSync6(snapshotScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found snapshot script: ${snapshotScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Snapshot script not found at ${snapshotScript}` };
3186
+ const installed = existsSync7(snapshotScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found snapshot script: ${snapshotScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Snapshot script not found at ${snapshotScript}` };
3175
3187
  const healthChecks = snapshotterHealthChecks(vsConfig.serviceScope ?? "user", logDir, env);
3176
3188
  const fetchNA = { id: "vault_sync_last_fetch_status", label: "Vault sync last fetch status", status: "pass", detail: "Snapshotter host \u2014 leaf wiki-fetch-notify log not applicable" };
3177
3189
  const filterNA = { id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "pass", detail: "Snapshotter host \u2014 leaf wiki-push filter not applicable" };
3178
3190
  let guard;
3179
- if (!existsSync6(snapshotScript)) {
3191
+ if (!existsSync7(snapshotScript)) {
3180
3192
  guard = { id: "vault_sync_snapshot_guard", label: "Snapshot script guard", status: "error", detail: `Snapshot script not found at ${snapshotScript}` };
3181
3193
  } else {
3182
3194
  const content = readFileSync10(snapshotScript, "utf8");
@@ -3192,11 +3204,11 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
3192
3204
  checks: allChecks.map((c) => ({ id: c.id, status: c.status, detail: c.detail }))
3193
3205
  };
3194
3206
  }
3195
- checks.push(existsSync6(installedScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found: ${installedScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Script missing: ${installedScript}` });
3207
+ checks.push(existsSync7(installedScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found: ${installedScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Script missing: ${installedScript}` });
3196
3208
  if (isMac) {
3197
3209
  const pushPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
3198
3210
  const fetchPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
3199
- const jobsOk = fetchOnly ? existsSync6(fetchPlist) : existsSync6(pushPlist) && existsSync6(fetchPlist);
3211
+ const jobsOk = fetchOnly ? existsSync7(fetchPlist) : existsSync7(pushPlist) && existsSync7(fetchPlist);
3200
3212
  checks.push(jobsOk ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: fetchOnly ? "launchd: wiki-fetch unit file present (read-only mode)" : "launchd unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: fetchOnly ? "launchd wiki-fetch unit file missing (read-only mode)" : "launchd unit files missing (read-only mode)" });
3201
3213
  checks.push({ id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "macOS host \u2014 check skipped" });
3202
3214
  } else {
@@ -3204,9 +3216,9 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
3204
3216
  const fetchTimer = join17(home, ".config", "systemd", "user", "wiki-fetch.timer");
3205
3217
  const fuseTimer = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
3206
3218
  const fuseService = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
3207
- const jobsOk = fetchOnly ? existsSync6(fetchTimer) : existsSync6(pushTimer) && existsSync6(fetchTimer);
3219
+ const jobsOk = fetchOnly ? existsSync7(fetchTimer) : existsSync7(pushTimer) && existsSync7(fetchTimer);
3208
3220
  checks.push(jobsOk ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: fetchOnly ? "systemd: wiki-fetch.timer unit file present (read-only mode)" : "systemd timer unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: fetchOnly ? "systemd wiki-fetch.timer unit file missing (read-only mode)" : "systemd timer unit files missing (read-only mode)" });
3209
- checks.push(existsSync6(fuseTimer) && existsSync6(fuseService) ? { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "wiki-fuse-refresh unit files present (read-only mode)" } : { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "warn", detail: "wiki-fuse-refresh unit files missing (read-only mode)" });
3221
+ checks.push(existsSync7(fuseTimer) && existsSync7(fuseService) ? { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "wiki-fuse-refresh unit files present (read-only mode)" } : { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "warn", detail: "wiki-fuse-refresh unit files missing (read-only mode)" });
3210
3222
  }
3211
3223
  if (fetchOnly) {
3212
3224
  checks.push({ id: "vault_sync_last_push_age", label: "Vault sync last push recency", status: "pass", detail: PUSH_NOT_IN_PROFILE });
@@ -3216,7 +3228,7 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
3216
3228
  checks.push(classifyLog(join17(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
3217
3229
  if (fetchOnly) {
3218
3230
  checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "pass", detail: PUSH_NOT_IN_PROFILE });
3219
- } else if (!existsSync6(filterPath)) {
3231
+ } else if (!existsSync7(filterPath)) {
3220
3232
  checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "error", detail: `Filter missing: ${filterPath}` });
3221
3233
  } else {
3222
3234
  const content = readFileSync10(filterPath, "utf8");
@@ -4405,7 +4417,7 @@ async function runUpdate(input) {
4405
4417
 
4406
4418
  // src/commands/self-update.ts
4407
4419
  import { execSync as execSync2 } from "child_process";
4408
- import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
4420
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
4409
4421
  import { join as join22 } from "path";
4410
4422
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
4411
4423
  async function runSelfUpdate(input) {
@@ -4413,7 +4425,7 @@ async function runSelfUpdate(input) {
4413
4425
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
4414
4426
  const distTag = normalizeDistTag(input.distTag);
4415
4427
  const localPkgPath = join22(sourceRoot, "packages", "cli", "package.json");
4416
- const hasLocalSource = existsSync7(localPkgPath);
4428
+ const hasLocalSource = existsSync8(localPkgPath);
4417
4429
  if (input.check) {
4418
4430
  let availableVersion = null;
4419
4431
  let source;
@@ -4714,7 +4726,7 @@ async function runClaimsAudit(input) {
4714
4726
  // src/commands/compound.ts
4715
4727
  import { writeFile as writeFile6, mkdir as mkdir7, readdir as readdir6, unlink as unlink3 } from "fs/promises";
4716
4728
  import { join as join25 } from "path";
4717
- import { existsSync as existsSync8 } from "fs";
4729
+ import { existsSync as existsSync9 } from "fs";
4718
4730
  import { readFile as readFile11 } from "fs/promises";
4719
4731
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
4720
4732
  var FIELD_RE = {
@@ -4832,7 +4844,7 @@ async function runCompound(input) {
4832
4844
  }
4833
4845
  const slug = slugify(entry.cycleName);
4834
4846
  const compoundPath = join25(compoundDir, `${slug}.md`);
4835
- if (existsSync8(compoundPath)) {
4847
+ if (existsSync9(compoundPath)) {
4836
4848
  skipped.push(entry.date);
4837
4849
  continue;
4838
4850
  }
@@ -4870,7 +4882,7 @@ async function runCompound(input) {
4870
4882
  ].join("\n");
4871
4883
  const content = frontmatter + "\n" + body;
4872
4884
  if (!input.dryRun) {
4873
- if (!existsSync8(compoundDir)) {
4885
+ if (!existsSync9(compoundDir)) {
4874
4886
  await mkdir7(compoundDir, { recursive: true });
4875
4887
  }
4876
4888
  await writeFile6(compoundPath, content, "utf8");
@@ -4893,7 +4905,7 @@ async function runCompound(input) {
4893
4905
  }
4894
4906
  async function runCompoundDelete(input) {
4895
4907
  const projectDir = join25(input.vault, "projects", input.project);
4896
- if (!existsSync8(projectDir)) {
4908
+ if (!existsSync9(projectDir)) {
4897
4909
  return {
4898
4910
  exitCode: ExitCode.PROJECT_NOT_FOUND,
4899
4911
  result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
@@ -4901,7 +4913,7 @@ async function runCompoundDelete(input) {
4901
4913
  }
4902
4914
  const entryName = input.entry.replace(/\.md$/, "");
4903
4915
  const compoundPath = join25(projectDir, "compound", `${entryName}.md`);
4904
- if (!existsSync8(compoundPath)) {
4916
+ if (!existsSync9(compoundPath)) {
4905
4917
  return {
4906
4918
  exitCode: ExitCode.FILE_NOT_FOUND,
4907
4919
  result: err("FILE_NOT_FOUND", { path: compoundPath })
@@ -4935,7 +4947,7 @@ knowledge.md regenerated`
4935
4947
  }
4936
4948
  async function runCompoundList(input) {
4937
4949
  const compoundDir = join25(input.vault, "projects", input.project, "compound");
4938
- if (!existsSync8(compoundDir)) {
4950
+ if (!existsSync9(compoundDir)) {
4939
4951
  return {
4940
4952
  exitCode: ExitCode.OK,
4941
4953
  result: ok({
@@ -5734,7 +5746,7 @@ import { dirname as dirname7, join as join29, resolve as resolve3 } from "path";
5734
5746
  // src/utils/publication-operation-journal.ts
5735
5747
  import {
5736
5748
  chmodSync,
5737
- existsSync as existsSync9,
5749
+ existsSync as existsSync10,
5738
5750
  mkdirSync as mkdirSync3,
5739
5751
  readFileSync as readFileSync12,
5740
5752
  realpathSync,
@@ -5886,7 +5898,7 @@ function buildIdentitySummary(input) {
5886
5898
  }
5887
5899
  function readPublicationJournal(vaultPath, operationId2, home = process.env.HOME || homedir()) {
5888
5900
  const { journalPath: journalPath2 } = resolveJournalPaths(vaultPath, operationId2, home);
5889
- if (!existsSync9(journalPath2)) return ok(null);
5901
+ if (!existsSync10(journalPath2)) return ok(null);
5890
5902
  let text;
5891
5903
  try {
5892
5904
  text = readFileSync12(journalPath2, "utf8");
@@ -5904,7 +5916,7 @@ function readPublicationJournal(vaultPath, operationId2, home = process.env.HOME
5904
5916
  function createPublicationJournal(input) {
5905
5917
  const home = input.home ?? process.env.HOME ?? homedir();
5906
5918
  const paths = resolveJournalPaths(input.vaultPath, input.operationId, home);
5907
- if (existsSync9(paths.journalPath)) {
5919
+ if (existsSync10(paths.journalPath)) {
5908
5920
  return err("WRITE_FAILED", { message: "journal already exists", path: paths.journalPath });
5909
5921
  }
5910
5922
  const record = {
@@ -5976,7 +5988,7 @@ function advancePublicationJournal(input) {
5976
5988
  }
5977
5989
  function deletePublicationJournal(vaultPath, operationId2, home = process.env.HOME || homedir()) {
5978
5990
  const { journalPath: journalPath2 } = resolveJournalPaths(vaultPath, operationId2, home);
5979
- if (!existsSync9(journalPath2)) return ok({ deleted: false });
5991
+ if (!existsSync10(journalPath2)) return ok({ deleted: false });
5980
5992
  try {
5981
5993
  unlinkSync(journalPath2);
5982
5994
  return ok({ deleted: true });
@@ -8483,7 +8495,7 @@ function runSyncJournalClearStale(input) {
8483
8495
  }
8484
8496
 
8485
8497
  // src/commands/snapshot-maintenance.ts
8486
- import { existsSync as existsSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, appendFileSync } from "fs";
8498
+ import { existsSync as existsSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, appendFileSync } from "fs";
8487
8499
  import { createHash as createHash5 } from "crypto";
8488
8500
  import { execSync as execSync3, spawn } from "child_process";
8489
8501
  import { platform as platform2 } from "os";
@@ -8552,7 +8564,7 @@ async function runSnapshotMaintenanceDryRun(input) {
8552
8564
  audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE"));
8553
8565
  return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
8554
8566
  }
8555
- if (!existsSync10(requested) || !existsSync10(join34(requested, ".git"))) {
8567
+ if (!existsSync11(requested) || !existsSync11(join34(requested, ".git"))) {
8556
8568
  audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO"));
8557
8569
  return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
8558
8570
  }
@@ -8787,9 +8799,9 @@ function nonMergeSequencerActive(repo) {
8787
8799
  const gitDir = git(repo, ["rev-parse", "--absolute-git-dir"]);
8788
8800
  if (!gitDir) return false;
8789
8801
  for (const m of ["CHERRY_PICK_HEAD", "REVERT_HEAD"]) {
8790
- if (existsSync10(join34(gitDir, m))) return true;
8802
+ if (existsSync11(join34(gitDir, m))) return true;
8791
8803
  }
8792
- return existsSync10(join34(gitDir, "rebase-merge")) || existsSync10(join34(gitDir, "rebase-apply"));
8804
+ return existsSync11(join34(gitDir, "rebase-merge")) || existsSync11(join34(gitDir, "rebase-apply"));
8793
8805
  }
8794
8806
  function extraUnmergedPaths(unmerged) {
8795
8807
  const allow = new Set(PROJECTION_CONFLICT_ALLOWLIST);
@@ -8828,7 +8840,7 @@ async function authorizeRepairContext(input) {
8828
8840
  audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE", void 0, REPAIR_COMMAND));
8829
8841
  return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
8830
8842
  }
8831
- if (!existsSync10(requested) || !existsSync10(join34(requested, ".git"))) {
8843
+ if (!existsSync11(requested) || !existsSync11(join34(requested, ".git"))) {
8832
8844
  audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO", void 0, REPAIR_COMMAND));
8833
8845
  return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
8834
8846
  }
@@ -9207,12 +9219,12 @@ async function runBackupRestore(input) {
9207
9219
  }
9208
9220
 
9209
9221
  // src/commands/status.ts
9210
- import { existsSync as existsSync11, statSync as statSync4 } from "fs";
9222
+ import { existsSync as existsSync12, statSync as statSync4 } from "fs";
9211
9223
  import { readFile as readFile18 } from "fs/promises";
9212
9224
  import { join as join36 } from "path";
9213
9225
  var FUSE_NO_MIRROR_NOTE = "FUSE vault with no read mirror - status scan may be slow";
9214
9226
  async function runStatus(input) {
9215
- if (!existsSync11(input.vault)) {
9227
+ if (!existsSync12(input.vault)) {
9216
9228
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
9217
9229
  }
9218
9230
  const { root: scanRoot, mirrored } = resolveReadOnlyVaultRoot(input.vault);
@@ -9314,9 +9326,166 @@ async function runStatus(input) {
9314
9326
  };
9315
9327
  }
9316
9328
 
9329
+ // src/commands/copy-status.ts
9330
+ import { existsSync as existsSync13 } from "fs";
9331
+ import { join as join37 } from "path";
9332
+
9333
+ // src/copy-status/copy-status.ts
9334
+ function liveRecord(p) {
9335
+ if (p.unknown || p.reachable === void 0) {
9336
+ return { state: "unknown", reachable: p.reachable, detail: p.detail ?? "S3/MCP unmeasured" };
9337
+ }
9338
+ if (p.reachable) {
9339
+ return { state: "ok", reachable: true, detail: p.detail ?? "S3 reachable" };
9340
+ }
9341
+ return { state: "unknown", reachable: false, detail: p.detail ?? "S3 unreachable" };
9342
+ }
9343
+ function githubRecord(p) {
9344
+ if (p.unknown || !p.oid) {
9345
+ return { state: "unknown", age_hours: p.ageHours, detail: p.detail ?? "GitHub HEAD unmeasured" };
9346
+ }
9347
+ const rec = { state: "ok", oid: p.oid, detail: p.detail ?? "ls-remote origin main" };
9348
+ if (p.ageHours !== void 0) rec.age_hours = p.ageHours;
9349
+ return rec;
9350
+ }
9351
+ function localRecord(p, githubOid) {
9352
+ if (p.blockedReason) {
9353
+ return {
9354
+ state: "blocked",
9355
+ oid: p.head,
9356
+ behind: p.behind,
9357
+ blocked_reason: p.blockedReason,
9358
+ detail: p.detail ?? p.blockedReason
9359
+ };
9360
+ }
9361
+ if (p.head && githubOid && p.head === githubOid && (p.behind === void 0 || p.behind === 0)) {
9362
+ return {
9363
+ state: "ok",
9364
+ oid: p.head,
9365
+ behind: p.behind,
9366
+ detail: p.detail ?? "HEAD matches GitHub"
9367
+ };
9368
+ }
9369
+ if (p.behind !== void 0 && p.behind > 0) {
9370
+ return {
9371
+ state: "stale",
9372
+ oid: p.head,
9373
+ behind: p.behind,
9374
+ detail: p.detail ?? `behind origin/main by ${p.behind}`
9375
+ };
9376
+ }
9377
+ if (p.head && githubOid && p.head !== githubOid) {
9378
+ return {
9379
+ state: "stale",
9380
+ oid: p.head,
9381
+ behind: p.behind,
9382
+ detail: p.detail ?? "HEAD differs from GitHub"
9383
+ };
9384
+ }
9385
+ if (p.head) {
9386
+ return {
9387
+ state: "ok",
9388
+ oid: p.head,
9389
+ behind: p.behind,
9390
+ detail: p.detail ?? "HEAD present"
9391
+ };
9392
+ }
9393
+ return { state: "unknown", detail: p.detail ?? "leaf clone unmeasured" };
9394
+ }
9395
+ function formatPlane(name, rec) {
9396
+ const parts = [`${name}: ${rec.state}`];
9397
+ if (rec.oid) parts.push(`oid=${rec.oid.slice(0, 12)}`);
9398
+ if (rec.behind !== void 0) parts.push(`behind=${rec.behind}`);
9399
+ if (rec.age_hours !== void 0) parts.push(`age_hours=${rec.age_hours}`);
9400
+ if (rec.blocked_reason) parts.push(rec.blocked_reason);
9401
+ if (rec.detail) parts.push(rec.detail);
9402
+ return parts.join(" ");
9403
+ }
9404
+ function composeCopyStatus(input) {
9405
+ const live = liveRecord(input.live);
9406
+ const github = githubRecord(input.github);
9407
+ const local_git = localRecord(input.local, input.github.oid);
9408
+ const humanHint = [
9409
+ formatPlane("live", live),
9410
+ formatPlane("github", github),
9411
+ formatPlane("local_git", local_git)
9412
+ ].join("\n");
9413
+ return { live, github, local_git, humanHint };
9414
+ }
9415
+ async function runCopyStatus(deps) {
9416
+ const [live, github, local] = await Promise.all([
9417
+ deps.probeLive(),
9418
+ deps.probeGithub(),
9419
+ deps.probeLocalGit()
9420
+ ]);
9421
+ return composeCopyStatus({ live, github, local });
9422
+ }
9423
+
9424
+ // src/commands/copy-status.ts
9425
+ function defaultCopyStatusDeps(input) {
9426
+ return {
9427
+ probeLive() {
9428
+ if (input.s3Ok === true) return { reachable: true, detail: "MCP S3 ok" };
9429
+ if (input.s3Ok === false) return { reachable: false, detail: "MCP S3 not ok" };
9430
+ const remote = resolveWikiS3Remote({ home: input.home });
9431
+ if (!remote) {
9432
+ return { unknown: true, detail: "leaf clone is not the live plane; S3 remote unconfigured" };
9433
+ }
9434
+ const reach = probeS3Reachability(remote);
9435
+ if (reach === "ok") return { reachable: true, detail: "S3 reachable" };
9436
+ if (reach === "unreachable") return { reachable: false, detail: "S3 unreachable" };
9437
+ return { unknown: true, detail: "S3 unmeasured" };
9438
+ },
9439
+ probeGithub() {
9440
+ if (!existsSync13(join37(input.vault, ".git"))) {
9441
+ return { unknown: true, detail: "vault is not a git repository" };
9442
+ }
9443
+ const ls = git(input.vault, ["ls-remote", "origin", "refs/heads/main"], {
9444
+ timeoutMs: REMOTE_PROBE_TIMEOUT_MS
9445
+ });
9446
+ const oid = ls.split(/\s+/)[0];
9447
+ if (!oid) return { unknown: true, detail: "git ls-remote origin main failed" };
9448
+ const originMain = git(input.vault, ["rev-parse", "--verify", "origin/main"]);
9449
+ let ageHours;
9450
+ if (originMain && originMain === oid) {
9451
+ const ct = git(input.vault, ["log", "-1", "--format=%ct", "origin/main"]);
9452
+ const sec = Number.parseInt(ct, 10);
9453
+ if (Number.isFinite(sec) && sec > 0) {
9454
+ ageHours = Math.max(0, Math.round((Date.now() / 1e3 - sec) / 3600));
9455
+ }
9456
+ }
9457
+ return { oid, ageHours, detail: "git ls-remote origin main" };
9458
+ },
9459
+ probeLocalGit() {
9460
+ if (!existsSync13(join37(input.vault, ".git"))) {
9461
+ return { unknown: true, detail: "vault is not a git repository" };
9462
+ }
9463
+ const head = git(input.vault, ["rev-parse", "HEAD"]);
9464
+ if (!head) return { unknown: true, detail: "HEAD unreadable" };
9465
+ const behindRaw = git(input.vault, ["rev-list", "--count", "HEAD..origin/main"]);
9466
+ const behind = behindRaw === "" ? void 0 : Number.parseInt(behindRaw, 10);
9467
+ const review = listReviewRequiredOps(input.vault)[0];
9468
+ const blockedReason = review ? `review-required:${review.opId}` : void 0;
9469
+ return {
9470
+ head,
9471
+ behind: Number.isFinite(behind) ? behind : void 0,
9472
+ blockedReason,
9473
+ detail: blockedReason
9474
+ };
9475
+ }
9476
+ };
9477
+ }
9478
+ async function runCopyStatusCommand(input) {
9479
+ if (!existsSync13(input.vault)) {
9480
+ return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
9481
+ }
9482
+ const data = await runCopyStatus(input.deps ?? defaultCopyStatusDeps(input));
9483
+ return { exitCode: ExitCode.OK, result: ok(data) };
9484
+ }
9485
+
9317
9486
  // src/commands/seed.ts
9318
9487
  import { mkdir as mkdir12, writeFile as writeFile8, stat as stat4 } from "fs/promises";
9319
- import { join as join37 } from "path";
9488
+ import { join as join38 } from "path";
9320
9489
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
9321
9490
  var EXAMPLE_PAGES = {
9322
9491
  "entities/example-project.md": `---
@@ -9385,29 +9554,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
9385
9554
  `;
9386
9555
  async function runSeed(input) {
9387
9556
  try {
9388
- await stat4(join37(input.vault, "SCHEMA.md"));
9557
+ await stat4(join38(input.vault, "SCHEMA.md"));
9389
9558
  } catch {
9390
9559
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
9391
9560
  }
9392
9561
  const created = [];
9393
9562
  const skipped = [];
9394
9563
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
9395
- const absPath = join37(input.vault, relPath);
9564
+ const absPath = join38(input.vault, relPath);
9396
9565
  try {
9397
9566
  await stat4(absPath);
9398
9567
  skipped.push(relPath);
9399
9568
  } catch {
9400
- await mkdir12(join37(absPath, ".."), { recursive: true });
9569
+ await mkdir12(join38(absPath, ".."), { recursive: true });
9401
9570
  await writeFile8(absPath, content, "utf8");
9402
9571
  created.push(relPath);
9403
9572
  }
9404
9573
  }
9405
- const rawPath = join37(input.vault, "raw", "articles", "example-source.md");
9574
+ const rawPath = join38(input.vault, "raw", "articles", "example-source.md");
9406
9575
  try {
9407
9576
  await stat4(rawPath);
9408
9577
  skipped.push("raw/articles/example-source.md");
9409
9578
  } catch {
9410
- await mkdir12(join37(rawPath, ".."), { recursive: true });
9579
+ await mkdir12(join38(rawPath, ".."), { recursive: true });
9411
9580
  await writeFile8(rawPath, EXAMPLE_RAW, "utf8");
9412
9581
  created.push("raw/articles/example-source.md");
9413
9582
  }
@@ -9431,8 +9600,8 @@ async function runSeed(input) {
9431
9600
 
9432
9601
  // src/commands/canvas.ts
9433
9602
  import { readFile as readFile19, writeFile as writeFile9 } from "fs/promises";
9434
- import { existsSync as existsSync12 } from "fs";
9435
- import { join as join38 } from "path";
9603
+ import { existsSync as existsSync14 } from "fs";
9604
+ import { join as join39 } from "path";
9436
9605
  var NODE_WIDTH = 240;
9437
9606
  var NODE_HEIGHT = 60;
9438
9607
  var COLUMN_SPACING = 400;
@@ -9510,8 +9679,8 @@ function buildCanvasEdges(adjacency) {
9510
9679
  return edges;
9511
9680
  }
9512
9681
  async function runCanvasGenerate(input) {
9513
- const graphPath = input.graphPath ?? join38(input.vault, ".skillwiki", "graph.json");
9514
- if (!existsSync12(graphPath)) {
9682
+ const graphPath = input.graphPath ?? join39(input.vault, ".skillwiki", "graph.json");
9683
+ if (!existsSync14(graphPath)) {
9515
9684
  return {
9516
9685
  exitCode: ExitCode.FILE_NOT_FOUND,
9517
9686
  result: err("FILE_NOT_FOUND", {
@@ -9548,7 +9717,7 @@ async function runCanvasGenerate(input) {
9548
9717
  const nodes = buildCanvasNodes(paths);
9549
9718
  const edges = buildCanvasEdges(graph.adjacency);
9550
9719
  const canvas = { nodes, edges };
9551
- const outPath = join38(input.vault, "vault-graph.canvas");
9720
+ const outPath = join39(input.vault, "vault-graph.canvas");
9552
9721
  try {
9553
9722
  await writeFile9(outPath, JSON.stringify(canvas, null, 2));
9554
9723
  } catch (e) {
@@ -9697,7 +9866,7 @@ import { readFile as readFile21, unlink as unlink5 } from "fs/promises";
9697
9866
 
9698
9867
  // src/utils/raw-asset-reference-index.ts
9699
9868
  import { readdir as readdir7, readFile as readFile20 } from "fs/promises";
9700
- import { join as join39, relative as relative4, sep as sep3 } from "path";
9869
+ import { join as join40, relative as relative4, sep as sep3 } from "path";
9701
9870
  var PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "pdf", "mp3", "wav", "m4a", "mp4", "webm"]);
9702
9871
  async function walkFiles(root, dir = root) {
9703
9872
  let entries;
@@ -9708,7 +9877,7 @@ async function walkFiles(root, dir = root) {
9708
9877
  }
9709
9878
  const out = [];
9710
9879
  for (const entry of entries) {
9711
- const path = join39(dir, entry.name);
9880
+ const path = join40(dir, entry.name);
9712
9881
  if (entry.isDirectory()) out.push(...await walkFiles(root, path));
9713
9882
  else if (entry.isFile()) out.push(relative4(root, path).split(sep3).join("/"));
9714
9883
  }
@@ -9724,7 +9893,7 @@ function addInbound(map, target, source) {
9724
9893
  map.set(target, refs);
9725
9894
  }
9726
9895
  async function buildRawAssetReferenceIndex(vault) {
9727
- const assetRoot = join39(vault, "raw", "assets");
9896
+ const assetRoot = join40(vault, "raw", "assets");
9728
9897
  const relativeAssets = await walkFiles(assetRoot);
9729
9898
  const assets = relativeAssets.map((path) => `raw/assets/${path}`).sort((a, b) => a.localeCompare(b));
9730
9899
  const assetSet = new Set(assets);
@@ -9921,10 +10090,10 @@ async function runSourceDisposal(input) {
9921
10090
  }
9922
10091
 
9923
10092
  // src/commands/fleet-health.ts
9924
- import { existsSync as existsSync13, readFileSync as readFileSync16 } from "fs";
10093
+ import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
9925
10094
  import { execSync as nodeExecSync } from "child_process";
9926
10095
  import { hostname as nodeHostname, platform as nodePlatform } from "os";
9927
- import { join as join40 } from "path";
10096
+ import { join as join41 } from "path";
9928
10097
  var SSH_TIMEOUT_MS = 15e3;
9929
10098
  var TIMER_UNIT = "agent-memory-trends.timer";
9930
10099
  var SERVICE_UNIT = "agent-memory-trends.service";
@@ -10021,7 +10190,7 @@ function applyServiceFailedOverlay(run, serviceFailed) {
10021
10190
  function probeLocal(vaultPath, deps) {
10022
10191
  const latestPath = satelliteLatestRunPath(vaultPath);
10023
10192
  let parsed = null;
10024
- if (existsSync13(latestPath)) {
10193
+ if (existsSync15(latestPath)) {
10025
10194
  try {
10026
10195
  const wire = readSatelliteLatestRunFromText(readFileSync16(latestPath, "utf8"));
10027
10196
  if (wire) {
@@ -10154,7 +10323,7 @@ async function runFleetHealth(input) {
10154
10323
  const home = input.home ?? env.HOME ?? "";
10155
10324
  const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
10156
10325
  const vault = input.vault ?? env.WIKI_PATH;
10157
- const file = input.file ?? (vault ? join40(vault, FLEET_REL_PATH) : void 0);
10326
+ const file = input.file ?? (vault ? join41(vault, FLEET_REL_PATH) : void 0);
10158
10327
  if (!file) {
10159
10328
  return {
10160
10329
  exitCode: ExitCode.NO_VAULT_CONFIGURED,
@@ -10238,8 +10407,8 @@ async function runFleetHealth(input) {
10238
10407
  }
10239
10408
 
10240
10409
  // src/utils/auto-commit.ts
10241
- import { existsSync as existsSync14 } from "fs";
10242
- import { isAbsolute, join as join41, normalize } from "path";
10410
+ import { existsSync as existsSync16 } from "fs";
10411
+ import { isAbsolute, join as join42, normalize } from "path";
10243
10412
  function isValidLastOpPath(file) {
10244
10413
  if (!file || typeof file !== "string") return false;
10245
10414
  const trimmed = file.trim();
@@ -10260,7 +10429,7 @@ async function postCommit(vault, exitCode) {
10260
10429
  const dotenv = await parseDotenvFile(configPath(home));
10261
10430
  const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
10262
10431
  if (autoCommit === "false") return;
10263
- if (!existsSync14(join41(vault, ".git"))) return;
10432
+ if (!existsSync16(join42(vault, ".git"))) return;
10264
10433
  const lastOps = readLastOp(vault);
10265
10434
  if (lastOps.length === 0) return;
10266
10435
  const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
@@ -10410,9 +10579,9 @@ async function runMcpAuthIssueHost(input) {
10410
10579
  }
10411
10580
 
10412
10581
  // src/commands/write-preflight.ts
10413
- import { existsSync as existsSync15, readFileSync as readFileSync18, statSync as statSync5 } from "fs";
10582
+ import { existsSync as existsSync17, readFileSync as readFileSync18, statSync as statSync5 } from "fs";
10414
10583
  async function runWritePreflightCommand(input) {
10415
- if (!existsSync15(input.vault) || !statSync5(input.vault).isDirectory()) {
10584
+ if (!existsSync17(input.vault) || !statSync5(input.vault).isDirectory()) {
10416
10585
  return {
10417
10586
  exitCode: ExitCode.VAULT_PATH_INVALID,
10418
10587
  result: err(GateError.VAULT_PATH_INVALID, { path: input.vault })
@@ -10420,7 +10589,7 @@ async function runWritePreflightCommand(input) {
10420
10589
  }
10421
10590
  let priorText = input.priorArtifactText;
10422
10591
  if (input.priorArtifactFile) {
10423
- if (!existsSync15(input.priorArtifactFile)) {
10592
+ if (!existsSync17(input.priorArtifactFile)) {
10424
10593
  return {
10425
10594
  exitCode: ExitCode.FILE_NOT_FOUND,
10426
10595
  result: err("FILE_NOT_FOUND", { path: input.priorArtifactFile })
@@ -10547,7 +10716,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
10547
10716
  if (dirty) {
10548
10717
  return emit(dirty, void 0, { postCommit: false });
10549
10718
  }
10550
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-BAIL5GMW.js");
10719
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-2Y5Q2WH4.js");
10551
10720
  const run = await runManagedWriteTransaction2({
10552
10721
  vault,
10553
10722
  command,
@@ -10577,7 +10746,7 @@ program.command("validate <file>").description("validate vault page frontmatter
10577
10746
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
10578
10747
  });
10579
10748
  program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
10580
- const out = opts.out ?? join42(vault, ".skillwiki", "graph.json");
10749
+ const out = opts.out ?? join43(vault, ".skillwiki", "graph.json");
10581
10750
  return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
10582
10751
  });
10583
10752
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -11060,17 +11229,17 @@ logCmd.command("migrate-legacy [vault]").description("backfill immutable events
11060
11229
  } else emit(await runLogMigrateLegacy({ vault: v.vault, write: false }), v.vault);
11061
11230
  });
11062
11231
  var projectionsCmd = program.command("projections").description("root index/log projection materialization");
11063
- projectionsCmd.command("materialize [vault]").description("preview or write root index.md and log.md as a pair").option("--write", "write projections", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
11232
+ projectionsCmd.command("materialize [vault]").description("preview or write root index.md and log.md as a pair").option("--write", "write projections", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--events-from <dir>", "log-events directory (day folders) instead of vault meta/log-events").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
11064
11233
  const v = await resolveVaultArg(vault, opts.wiki);
11065
11234
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
11066
11235
  else if (opts.write) {
11067
11236
  return emitManagedVaultWrite(
11068
11237
  v.vault,
11069
11238
  "projections materialize",
11070
- () => runProjectionsMaterialize({ vault: v.vault, write: true }),
11239
+ () => runProjectionsMaterialize({ vault: v.vault, write: true, eventsFrom: opts.eventsFrom }),
11071
11240
  { convergenceVault: opts.convergeVault }
11072
11241
  );
11073
- } else emit(await runProjectionsMaterialize({ vault: v.vault, write: false }), v.vault);
11242
+ } else emit(await runProjectionsMaterialize({ vault: v.vault, write: false, eventsFrom: opts.eventsFrom }), v.vault);
11074
11243
  });
11075
11244
  projectionsCmd.command("repair-legacy [vault]").description("repair one supported legacy root-index marker pair and log event").requiredOption("--event-operation-id <id>", "exact legacy event operation ID").option("--write", "write the bounded repairs", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
11076
11245
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -11199,6 +11368,14 @@ program.command("status [vault]").description("output vault diagnostics").option
11199
11368
  langEnvValue: process.env.WIKI_LANG
11200
11369
  }), v.vault);
11201
11370
  });
11371
+ program.command("copy-status [vault]").description("plane-tagged vault freshness: live S3 vs GitHub snapshot vs leaf clone").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
11372
+ const v = await resolveVaultArg(vault, opts.wiki);
11373
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
11374
+ else emit(await runCopyStatusCommand({
11375
+ vault: v.vault,
11376
+ home: process.env.HOME ?? ""
11377
+ }), v.vault, { postCommit: false });
11378
+ });
11202
11379
  program.command("archive <page> [vault]").description("archive a typed-knowledge or raw page").option("--wiki <name>", "wiki profile name").option("--cascade", "scan vault for references (wikilinks + sources arrays); preview by default", false).option("--apply", "with --cascade: mutate sources arrays and archive (without --apply, --cascade is preview-only)", false).option("--approve <token>", "approve an exact state-bound raw archive plan").option("--remote <remote>", "rclone remote root to prune the archived source path, for example seaweed-wiki:cloud/wiki").option("--remote-delete", "delete the archived source path from the remote after local archive", false).option("--max-remote-deletes <n>", "maximum remote object deletes allowed", "1").action(async (page, vault, opts) => {
11203
11380
  const v = await resolveVaultArg(vault, opts.wiki);
11204
11381
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
@@ -6,9 +6,9 @@ import {
6
6
  runManagedWritePeerGate,
7
7
  runManagedWritePreflight,
8
8
  runManagedWriteTransaction
9
- } from "./chunk-BO7JIJIA.js";
9
+ } from "./chunk-6WE7E6VB.js";
10
10
  import "./chunk-JHDN3FSF.js";
11
- import "./chunk-5BNCYHRN.js";
11
+ import "./chunk-QFRZDTJY.js";
12
12
  import "./chunk-XJR3XXP5.js";
13
13
  export {
14
14
  DEFAULT_MANAGED_WRITE_WAIT_MS,
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-GZKT2XC5.js";
4
+ } from "./chunk-5QFHISNH.js";
5
5
  import "./chunk-7I2TPIV5.js";
6
6
  import "./chunk-ZUHA2EXE.js";
7
- import "./chunk-BO7JIJIA.js";
7
+ import "./chunk-6WE7E6VB.js";
8
8
  import "./chunk-JHDN3FSF.js";
9
- import "./chunk-CGQQS7MO.js";
10
- import "./chunk-5BNCYHRN.js";
9
+ import "./chunk-ECOEJABP.js";
10
+ import "./chunk-QFRZDTJY.js";
11
11
  import "./chunk-XJR3XXP5.js";
12
12
 
13
13
  // src/mcp-entry.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSourcesPending
4
- } from "./chunk-CGQQS7MO.js";
5
- import "./chunk-5BNCYHRN.js";
4
+ } from "./chunk-ECOEJABP.js";
5
+ import "./chunk-QFRZDTJY.js";
6
6
  import "./chunk-XJR3XXP5.js";
7
7
  export {
8
8
  runSourcesPending
@@ -108,6 +108,7 @@ RCLONE_LOG="/tmp/rclone-${DATE}.log"
108
108
  SNAPSHOT_DIRECT_S3_NOT_GIT_COUNT=0
109
109
  SNAPSHOT_REMOTE_INVENTORY_READY=0
110
110
  PROJECTION_STATE_DIR=""
111
+ SNAPSHOT_S3_LOG_EVENTS=""
111
112
  PROJECTION_EXPECTED_INDEX_SHA256=""
112
113
  PROJECTION_EXPECTED_LOG_SHA256=""
113
114
  SNAPSHOT_BASE_OID=""
@@ -118,6 +119,9 @@ cleanup_projection_state() {
118
119
  if [ -n "$PROJECTION_STATE_DIR" ] && [ -d "$PROJECTION_STATE_DIR" ]; then
119
120
  rm -rf "$PROJECTION_STATE_DIR"
120
121
  fi
122
+ if [ -n "$SNAPSHOT_S3_LOG_EVENTS" ] && [ -d "$SNAPSHOT_S3_LOG_EVENTS" ]; then
123
+ rm -rf "$SNAPSHOT_S3_LOG_EVENTS"
124
+ fi
121
125
  }
122
126
  trap cleanup_projection_state EXIT
123
127
 
@@ -460,6 +464,25 @@ validate_projection_parity_config() {
460
464
  return 0
461
465
  }
462
466
 
467
+ # Direct-S3 log-event inventory shared by freeze and semantic preview so
468
+ # FUSE dir cache vs wiki-git find cannot disagree on the event set.
469
+ snapshot_capture_direct_s3_log_events() {
470
+ if [ -z "$CLOUD_REMOTE" ]; then
471
+ log "ERROR: CLOUD_REMOTE unset; cannot capture S3 log-events"
472
+ return 1
473
+ fi
474
+ SNAPSHOT_S3_LOG_EVENTS="$(mktemp -d)" || {
475
+ log "ERROR: could not create temp dir for S3 log-events"
476
+ return 1
477
+ }
478
+ if ! rclone copy "${CLOUD_REMOTE%/}/meta/log-events" "$SNAPSHOT_S3_LOG_EVENTS" 2>>"$LOG_FILE"; then
479
+ log "ERROR: rclone copy of S3 meta/log-events failed"
480
+ return 1
481
+ fi
482
+ log "OK captured S3 log-events at $SNAPSHOT_S3_LOG_EVENTS"
483
+ return 0
484
+ }
485
+
463
486
  snapshot_freeze_projection_expectations() {
464
487
  if [ "${WIKI_SNAPSHOT_TEST_SKIP_PROJECTION_PARITY:-0}" = "1" ] \
465
488
  && [ -n "${SNAPSHOT_TEST_ROOT:-}" ]; then
@@ -472,10 +495,12 @@ snapshot_freeze_projection_expectations() {
472
495
  return 1
473
496
  fi
474
497
 
475
- PROJECTION_STATE_DIR="$(mktemp -d)" || {
476
- log "ERROR: could not create private projection parity state"
477
- return 1
478
- }
498
+ if [ -z "$PROJECTION_STATE_DIR" ] || [ ! -d "$PROJECTION_STATE_DIR" ]; then
499
+ PROJECTION_STATE_DIR="$(mktemp -d)" || {
500
+ log "ERROR: could not create private projection parity state"
501
+ return 1
502
+ }
503
+ fi
479
504
  if ! cp "$WIKI_DIR/index.md" "$PROJECTION_STATE_DIR/expected-index.md" \
480
505
  || ! cp "$WIKI_DIR/log.md" "$PROJECTION_STATE_DIR/expected-log.md"; then
481
506
  log "ERROR: could not freeze live projection bytes"
@@ -523,6 +548,9 @@ snapshot_wait_for_direct_projection_parity() {
523
548
  # Test-only skip path above. Production always has frozen state.
524
549
  return 0
525
550
  fi
551
+ if [ ! -f "$PROJECTION_STATE_DIR/expected-index.md" ] || [ ! -f "$PROJECTION_STATE_DIR/expected-log.md" ]; then
552
+ return 0
553
+ fi
526
554
 
527
555
  local started deadline now attempts
528
556
  local remote_index="$PROJECTION_STATE_DIR/remote-index.md"
@@ -655,7 +683,11 @@ snapshot_verify_semantic_projection_preview() {
655
683
  return 0
656
684
  fi
657
685
  local preview="$PROJECTION_STATE_DIR/worktree-preview.json"
658
- if ! "$SKILLWIKI_BIN" projections materialize "$SNAPSHOT_WORKTREE" >"$preview" 2>>"$LOG_FILE"; then
686
+ local -a preview_args=("projections" "materialize" "$SNAPSHOT_WORKTREE")
687
+ if [ -n "${SNAPSHOT_S3_LOG_EVENTS:-}" ] && [ -d "$SNAPSHOT_S3_LOG_EVENTS" ]; then
688
+ preview_args+=(--events-from "$SNAPSHOT_S3_LOG_EVENTS")
689
+ fi
690
+ if ! "$SKILLWIKI_BIN" "${preview_args[@]}" >"$preview" 2>>"$LOG_FILE"; then
659
691
  log "ERROR: projection semantic preview failed"
660
692
  return 1
661
693
  fi
@@ -867,8 +899,13 @@ if command -v "$SKILLWIKI_BIN" >/dev/null 2>&1; then
867
899
  ;;
868
900
  esac
869
901
  proj_out="$(mktemp)"
902
+ if ! snapshot_capture_direct_s3_log_events; then
903
+ log "FAIL S3 log-events capture; snapshot promotion refused"
904
+ exit 1
905
+ fi
870
906
  if ! "$SKILLWIKI_BIN" projections materialize "$WIKI_DIR" --write \
871
- --converge-vault "$SNAPSHOT_WORKTREE" >"$proj_out" 2>&1; then
907
+ --converge-vault "$SNAPSHOT_WORKTREE" \
908
+ --events-from "$SNAPSHOT_S3_LOG_EVENTS" >"$proj_out" 2>&1; then
872
909
  cat "$proj_out" >>"$LOG_FILE" 2>/dev/null || true
873
910
  snapshot_inhibit_record_failure "$(snapshot_parse_operation_id "$proj_out")"
874
911
  rm -f "$proj_out"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.86",
3
+ "version": "0.10.88",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.86",
3
+ "version": "0.10.88",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by the deterministic `skillwiki` CLI.",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.86",
3
+ "version": "0.10.88",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 21 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.86",
3
+ "version": "0.10.88",
4
4
  "description": "Project-aware Karpathy-style knowledge base: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by HTTP MCP wiki_capture.",
5
5
  "author": {
6
6
  "name": "karlorz"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.10.86",
3
+ "version": "0.10.88",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",