skillwiki 0.10.16 → 0.10.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -83,7 +83,7 @@ import {
83
83
  upsertIndexEntry,
84
84
  validateLogEvent,
85
85
  writeLogEvent
86
- } from "./chunk-24MVNAFJ.js";
86
+ } from "./chunk-IAPD6YTC.js";
87
87
  import {
88
88
  normalizeDistTag,
89
89
  readCache,
@@ -105,13 +105,11 @@ import {
105
105
  scanVault,
106
106
  splitFrontmatter,
107
107
  writeRootIndexProjection
108
- } from "./chunk-NMUYMNNB.js";
108
+ } from "./chunk-IGCW3DR2.js";
109
109
  import {
110
- acquireManagedWriteLock,
111
- releaseManagedWriteLock,
112
110
  runManagedWritePreflight,
113
111
  runManagedWriteTransaction
114
- } from "./chunk-UJLSUJB6.js";
112
+ } from "./chunk-QJPCCW4N.js";
115
113
  import {
116
114
  FLEET_REL_PATH,
117
115
  canSupersedeJournal,
@@ -130,6 +128,7 @@ import {
130
128
  parseJournalEnv,
131
129
  profileKey,
132
130
  readJournal,
131
+ resolveConfiguredSnapshotWorktree,
133
132
  resolveFleetHostId,
134
133
  runFleetContext,
135
134
  runFleetValidate,
@@ -138,7 +137,7 @@ import {
138
137
  snapshotterAliasForLocalHost,
139
138
  supersedeStaleReviewRequiredJournals,
140
139
  writeDotenv
141
- } from "./chunk-OWPQJOFG.js";
140
+ } from "./chunk-GNS2ZV5P.js";
142
141
  import {
143
142
  ExitCode,
144
143
  MetaSchema,
@@ -146,10 +145,10 @@ import {
146
145
  err,
147
146
  isBlockedHost,
148
147
  ok
149
- } from "./chunk-C5OLZRRM.js";
148
+ } from "./chunk-Y6KRDGI2.js";
150
149
 
151
150
  // src/cli.ts
152
- import { join as join37 } from "path";
151
+ import { join as join36 } from "path";
153
152
  import { Command } from "commander";
154
153
 
155
154
  // src/utils/output.ts
@@ -833,12 +832,11 @@ async function runInit(input) {
833
832
  }
834
833
 
835
834
  // src/commands/index-rebuild.ts
836
- import { readFileSync as readFileSync3 } from "fs";
837
- import { join as join7 } from "path";
835
+ import { readFileSync as readFileSync2 } from "fs";
836
+ import { join as join6 } from "path";
838
837
 
839
838
  // src/utils/protected-vault-write-guard.ts
840
- import { readFileSync as readFileSync2 } from "fs";
841
- import { join as join6, resolve as resolvePath } from "path";
839
+ import { resolve as resolvePath } from "path";
842
840
  async function guardProtectedVaultWrite(input) {
843
841
  const env = input.env ?? process.env;
844
842
  const home = input.home ?? process.env.HOME ?? "";
@@ -860,14 +858,15 @@ async function guardProtectedVaultWrite(input) {
860
858
  if (!host || host.role !== "snapshotter" || host.protected !== true) {
861
859
  return { blocked: false };
862
860
  }
863
- const snapshotWorktree = resolveSnapshotWorktree(home);
864
861
  const targetVault = resolvePath(input.vault);
865
862
  const canonicalLiveVault = liveVaultPath ? resolvePath(liveVaultPath) : void 0;
866
- const canonicalSnapshotWorktree = snapshotWorktree ? resolvePath(snapshotWorktree) : void 0;
867
863
  if (canonicalLiveVault && targetVault === canonicalLiveVault) {
868
864
  return { blocked: false };
869
865
  }
870
- if (canonicalSnapshotWorktree && targetVault === canonicalSnapshotWorktree) {
866
+ const canonicalSnapshotWorktree = resolvePath(
867
+ resolveConfiguredSnapshotWorktree(home) ?? "/root/wiki-git"
868
+ );
869
+ if (targetVault === canonicalSnapshotWorktree) {
871
870
  return {
872
871
  blocked: true,
873
872
  exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED,
@@ -903,35 +902,6 @@ async function resolveLiveVaultPath(input) {
903
902
  });
904
903
  return resolved.ok ? resolved.data.path : void 0;
905
904
  }
906
- function resolveSnapshotWorktree(home) {
907
- const skillwikiEnv = join6(home, ".skillwiki", ".env");
908
- const explicitWorktree = readEnvKey(skillwikiEnv, ["vault_sync.snapshot_worktree"]);
909
- if (explicitWorktree) return explicitWorktree;
910
- const snapshotProfile = readEnvKey(skillwikiEnv, ["vault_sync.snapshot_profile"]);
911
- if (snapshotProfile) {
912
- const fromProfile = readEnvKey(snapshotProfile, ["WIKI_GIT_WORKTREE", "SNAPSHOT_WORKTREE", "GIT_DIR"]);
913
- if (fromProfile) return fromProfile;
914
- }
915
- return "/root/wiki-git";
916
- }
917
- function readEnvKey(path, keys) {
918
- try {
919
- const content = readFileSync2(path, "utf8");
920
- for (const line of content.split(/\r?\n/)) {
921
- const trimmed = line.trim();
922
- if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
923
- const eq = trimmed.indexOf("=");
924
- if (eq <= 0) continue;
925
- const key = trimmed.slice(0, eq).trim();
926
- if (!keys.includes(key)) continue;
927
- const value = trimmed.slice(eq + 1).trim();
928
- if (value.length > 0) return value;
929
- }
930
- } catch {
931
- return void 0;
932
- }
933
- return void 0;
934
- }
935
905
 
936
906
  // src/commands/index-rebuild.ts
937
907
  async function runIndexRebuild(input) {
@@ -942,7 +912,7 @@ async function runIndexRebuild(input) {
942
912
  }
943
913
  let current = "";
944
914
  try {
945
- current = readFileSync3(join7(input.vault, "index.md"), "utf8");
915
+ current = readFileSync2(join6(input.vault, "index.md"), "utf8");
946
916
  } catch {
947
917
  current = "";
948
918
  }
@@ -991,7 +961,7 @@ async function runIndexRebuild(input) {
991
961
  }
992
962
  let installed = "";
993
963
  try {
994
- installed = readFileSync3(join7(input.vault, "index.md"), "utf8");
964
+ installed = readFileSync2(join6(input.vault, "index.md"), "utf8");
995
965
  } catch (error) {
996
966
  return {
997
967
  exitCode: ExitCode.WRITE_FAILED,
@@ -1021,14 +991,14 @@ async function runIndexRebuild(input) {
1021
991
 
1022
992
  // src/commands/derived-conflicts.ts
1023
993
  import { execFileSync as execFileSync2 } from "child_process";
1024
- import { existsSync, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
1025
- import { join as join9 } from "path";
994
+ import { existsSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
995
+ import { join as join8 } from "path";
1026
996
 
1027
997
  // src/utils/log-merge.ts
1028
998
  import { execFileSync } from "child_process";
1029
999
  import { mkdtempSync, writeFileSync, rmSync } from "fs";
1030
1000
  import { tmpdir } from "os";
1031
- import { join as join8 } from "path";
1001
+ import { join as join7 } from "path";
1032
1002
  var ENTRY_RE = /^## \[/m;
1033
1003
  var OPERATION_MARKER_RE = /<!--\s*skillwiki-(?:page-publish|log-op):([a-f0-9]{64})\s*-->/i;
1034
1004
  function splitLog(text) {
@@ -1047,11 +1017,11 @@ function publishId(entry) {
1047
1017
  return OPERATION_MARKER_RE.exec(entry)?.[1]?.toLowerCase();
1048
1018
  }
1049
1019
  function unionUnstructured(base, ours, theirs) {
1050
- const dir = mkdtempSync(join8(tmpdir(), "log-union-"));
1020
+ const dir = mkdtempSync(join7(tmpdir(), "log-union-"));
1051
1021
  try {
1052
- const basePath = join8(dir, "base");
1053
- const oursPath = join8(dir, "ours");
1054
- const theirsPath = join8(dir, "theirs");
1022
+ const basePath = join7(dir, "base");
1023
+ const oursPath = join7(dir, "ours");
1024
+ const theirsPath = join7(dir, "theirs");
1055
1025
  writeFileSync(basePath, base.endsWith("\n") || base.length === 0 ? base : `${base}
1056
1026
  `);
1057
1027
  writeFileSync(oursPath, ours.endsWith("\n") || ours.length === 0 ? ours : `${ours}
@@ -1121,10 +1091,10 @@ function classifyDerivedPath(path) {
1121
1091
  function journalPath(vault, operationId2) {
1122
1092
  const gitPath = git(vault, ["rev-parse", "--git-path", `vault-sync/operations/${operationId2}.env`]);
1123
1093
  if (!gitPath) return null;
1124
- return gitPath.startsWith("/") ? gitPath : join9(vault, gitPath);
1094
+ return gitPath.startsWith("/") ? gitPath : join8(vault, gitPath);
1125
1095
  }
1126
1096
  function readJournalFields(path) {
1127
- const text = readFileSync5(path, "utf8");
1097
+ const text = readFileSync4(path, "utf8");
1128
1098
  const out = {};
1129
1099
  for (const line of text.split("\n")) {
1130
1100
  const i = line.indexOf("=");
@@ -1296,7 +1266,7 @@ async function runDerivedConflictResolution(input) {
1296
1266
  if (hasMarkers(path, resolved.data)) {
1297
1267
  throw new Error(`${path}: conflict markers remain after resolve`);
1298
1268
  }
1299
- writeFileSync2(join9(vault, path), resolved.data, "utf8");
1269
+ writeFileSync2(join8(vault, path), resolved.data, "utf8");
1300
1270
  execFileSync2("git", ["add", "--", path], { cwd: vault, stdio: ["pipe", "pipe", "pipe"] });
1301
1271
  staged.push(path);
1302
1272
  }
@@ -1341,8 +1311,8 @@ async function runDerivedConflictResolution(input) {
1341
1311
  }
1342
1312
 
1343
1313
  // src/commands/log-materialize.ts
1344
- import { readFileSync as readFileSync6 } from "fs";
1345
- import { join as join10 } from "path";
1314
+ import { readFileSync as readFileSync5 } from "fs";
1315
+ import { join as join9 } from "path";
1346
1316
 
1347
1317
  // src/utils/log-projection.ts
1348
1318
  function day(event) {
@@ -1448,7 +1418,7 @@ async function runLogMaterialize(input) {
1448
1418
  const text = renderLogProjection(events.data);
1449
1419
  let current = "";
1450
1420
  try {
1451
- current = readFileSync6(join10(input.vault, "log.md"), "utf8");
1421
+ current = readFileSync5(join9(input.vault, "log.md"), "utf8");
1452
1422
  } catch {
1453
1423
  current = "";
1454
1424
  }
@@ -1492,9 +1462,9 @@ async function runLogMaterialize(input) {
1492
1462
  })
1493
1463
  };
1494
1464
  }
1495
- const written = await atomicWriteText(join10(input.vault, "log.md"), text);
1465
+ const written = await atomicWriteText(join9(input.vault, "log.md"), text);
1496
1466
  if (!written.ok) return { exitCode: ExitCode.WRITE_FAILED, result: written };
1497
- const installed = readFileSync6(join10(input.vault, "log.md"), "utf8");
1467
+ const installed = readFileSync5(join9(input.vault, "log.md"), "utf8");
1498
1468
  if (installed !== text) {
1499
1469
  return {
1500
1470
  exitCode: ExitCode.WRITE_FAILED,
@@ -1513,8 +1483,8 @@ async function runLogMaterialize(input) {
1513
1483
  }
1514
1484
 
1515
1485
  // src/commands/log-migrate-legacy.ts
1516
- import { readFileSync as readFileSync7 } from "fs";
1517
- import { join as join11 } from "path";
1486
+ import { readFileSync as readFileSync6 } from "fs";
1487
+ import { join as join10 } from "path";
1518
1488
 
1519
1489
  // src/utils/operation-id.ts
1520
1490
  import { createHash as createHash2 } from "crypto";
@@ -1543,7 +1513,7 @@ function splitLegacyBlocks(text) {
1543
1513
  async function runLogMigrateLegacy(input) {
1544
1514
  let text = "";
1545
1515
  try {
1546
- text = readFileSync7(join11(input.vault, "log.md"), "utf8");
1516
+ text = readFileSync6(join10(input.vault, "log.md"), "utf8");
1547
1517
  } catch {
1548
1518
  return {
1549
1519
  exitCode: ExitCode.OK,
@@ -1599,8 +1569,8 @@ async function runLogMigrateLegacy(input) {
1599
1569
  }
1600
1570
 
1601
1571
  // src/commands/projections-materialize.ts
1602
- import { readFileSync as readFileSync8 } from "fs";
1603
- import { join as join12 } from "path";
1572
+ import { readFileSync as readFileSync7 } from "fs";
1573
+ import { join as join11 } from "path";
1604
1574
  var defaultDeps = {
1605
1575
  writeText: (path, text) => atomicWriteText(path, text)
1606
1576
  };
@@ -1616,11 +1586,11 @@ async function runProjectionsMaterialize(input, deps = defaultDeps) {
1616
1586
  let curIndex = "";
1617
1587
  let curLog = "";
1618
1588
  try {
1619
- curIndex = readFileSync8(join12(input.vault, "index.md"), "utf8");
1589
+ curIndex = readFileSync7(join11(input.vault, "index.md"), "utf8");
1620
1590
  } catch {
1621
1591
  }
1622
1592
  try {
1623
- curLog = readFileSync8(join12(input.vault, "log.md"), "utf8");
1593
+ curLog = readFileSync7(join11(input.vault, "log.md"), "utf8");
1624
1594
  } catch {
1625
1595
  }
1626
1596
  const indexDrift = curIndex !== indexProj.data.text;
@@ -1654,8 +1624,8 @@ async function runProjectionsMaterialize(input, deps = defaultDeps) {
1654
1624
  })
1655
1625
  };
1656
1626
  }
1657
- const indexPath = join12(input.vault, "index.md");
1658
- const logPath = join12(input.vault, "log.md");
1627
+ const indexPath = join11(input.vault, "index.md");
1628
+ const logPath = join11(input.vault, "log.md");
1659
1629
  const indexWrite = await deps.writeText(indexPath, indexProj.data.text);
1660
1630
  if (!indexWrite.ok) return { exitCode: ExitCode.WRITE_FAILED, result: indexWrite };
1661
1631
  const logWrite = await deps.writeText(logPath, logText);
@@ -1696,7 +1666,7 @@ async function runProjectionsMaterialize(input, deps = defaultDeps) {
1696
1666
 
1697
1667
  // src/commands/projections-repair-legacy.ts
1698
1668
  import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
1699
- import { join as join13 } from "path";
1669
+ import { join as join12 } from "path";
1700
1670
  var OPERATION_ID_RE = /^[0-9a-f]{64}$/;
1701
1671
  var LEGACY_KEYS = ["created", "kind", "note", "operation_id", "target"];
1702
1672
  var EVENT_KEYS = [
@@ -1759,7 +1729,7 @@ function normalizeLegacyTimestamp(value) {
1759
1729
  return ok(normalized);
1760
1730
  }
1761
1731
  async function findEventPath(vault, operationId2) {
1762
- const root = join13(vault, "meta", "log-events");
1732
+ const root = join12(vault, "meta", "log-events");
1763
1733
  const matches = [];
1764
1734
  let days;
1765
1735
  try {
@@ -1772,12 +1742,12 @@ async function findEventPath(vault, operationId2) {
1772
1742
  }
1773
1743
  for (const day2 of days) {
1774
1744
  if (!day2.isDirectory()) continue;
1775
- const dayPath = join13(root, day2.name);
1745
+ const dayPath = join12(root, day2.name);
1776
1746
  const entries = await readdir3(dayPath, { withFileTypes: true });
1777
1747
  for (const entry of entries) {
1778
1748
  if (!entry.isFile() || entry.name !== `${operationId2}.json`) continue;
1779
1749
  matches.push({
1780
- absolute: join13(dayPath, entry.name),
1750
+ absolute: join12(dayPath, entry.name),
1781
1751
  relative: `meta/log-events/${day2.name}/${entry.name}`
1782
1752
  });
1783
1753
  }
@@ -1874,7 +1844,7 @@ async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
1874
1844
  }
1875
1845
  let indexText;
1876
1846
  try {
1877
- indexText = await readFile4(join13(input.vault, "index.md"), "utf8");
1847
+ indexText = await readFile4(join12(input.vault, "index.md"), "utf8");
1878
1848
  } catch (error) {
1879
1849
  return {
1880
1850
  exitCode: ExitCode.SCHEME_REJECTED,
@@ -1909,7 +1879,7 @@ async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
1909
1879
  }
1910
1880
  let indexChanged = false;
1911
1881
  if (indexPlan.data.needed) {
1912
- const written = await deps.writeText(join13(input.vault, "index.md"), indexPlan.data.repaired);
1882
+ const written = await deps.writeText(join12(input.vault, "index.md"), indexPlan.data.repaired);
1913
1883
  if (!written.ok) return { exitCode: ExitCode.WRITE_FAILED, result: written };
1914
1884
  indexChanged = written.data.changed;
1915
1885
  }
@@ -1920,7 +1890,7 @@ async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
1920
1890
  let rolledBack = false;
1921
1891
  let rollbackError;
1922
1892
  if (indexChanged) {
1923
- const rollback = await deps.writeText(join13(input.vault, "index.md"), indexPlan.data.current);
1893
+ const rollback = await deps.writeText(join12(input.vault, "index.md"), indexPlan.data.current);
1924
1894
  rolledBack = rollback.ok;
1925
1895
  if (!rollback.ok) rollbackError = rollback;
1926
1896
  }
@@ -1952,7 +1922,7 @@ async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
1952
1922
  // src/commands/claim.ts
1953
1923
  import { mkdir as mkdir4, writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
1954
1924
  import { existsSync as existsSync2, statSync } from "fs";
1955
- import { join as join14 } from "path";
1925
+ import { join as join13 } from "path";
1956
1926
  function extractDate(filename) {
1957
1927
  const m = filename.match(/^(\d{4}-\d{2}-\d{2})/);
1958
1928
  return m?.[1] ?? "";
@@ -1967,7 +1937,7 @@ async function runClaim(input) {
1967
1937
  if (!existsSync2(input.vault) || !statSync(input.vault).isDirectory()) {
1968
1938
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { path: input.vault }) };
1969
1939
  }
1970
- const absTranscript = join14(input.vault, input.transcript);
1940
+ const absTranscript = join13(input.vault, input.transcript);
1971
1941
  if (!existsSync2(absTranscript)) {
1972
1942
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.transcript }) };
1973
1943
  }
@@ -1983,7 +1953,7 @@ async function runClaim(input) {
1983
1953
  result: err("SCHEME_REJECTED", { message: "No project specified. Use --project or set project in transcript frontmatter." })
1984
1954
  };
1985
1955
  }
1986
- const projectDir = join14(input.vault, "projects", projectSlug);
1956
+ const projectDir = join13(input.vault, "projects", projectSlug);
1987
1957
  if (!existsSync2(projectDir) || !statSync(projectDir).isDirectory()) {
1988
1958
  return {
1989
1959
  exitCode: ExitCode.PROJECT_NOT_FOUND,
@@ -2000,7 +1970,7 @@ async function runClaim(input) {
2000
1970
  }
2001
1971
  const workSlug = input.slug || extractSlugFromFilename(filename);
2002
1972
  const dirName = `${date}-${workSlug}`;
2003
- const workDir = join14(projectDir, "work", dirName);
1973
+ const workDir = join13(projectDir, "work", dirName);
2004
1974
  const relWorkDir = `projects/${projectSlug}/work/${dirName}`;
2005
1975
  const relSpecPath = `${relWorkDir}/spec.md`;
2006
1976
  if (existsSync2(workDir)) {
@@ -2029,7 +1999,7 @@ async function runClaim(input) {
2029
1999
  `Claimed from ${input.transcript}`,
2030
2000
  ""
2031
2001
  ];
2032
- await writeFile3(join14(workDir, "spec.md"), specLines.join("\n"), "utf8");
2002
+ await writeFile3(join13(workDir, "spec.md"), specLines.join("\n"), "utf8");
2033
2003
  appendLastOp(input.vault, {
2034
2004
  operation: "claim",
2035
2005
  summary: `claimed ${input.transcript} \u2192 ${relWorkDir}`,
@@ -2048,12 +2018,12 @@ async function runClaim(input) {
2048
2018
  }
2049
2019
 
2050
2020
  // src/commands/work-complete.ts
2051
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync10, renameSync, writeFileSync as writeFileSync3 } from "fs";
2052
- import { join as join16 } from "path";
2021
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync9, renameSync, writeFileSync as writeFileSync3 } from "fs";
2022
+ import { join as join15 } from "path";
2053
2023
 
2054
2024
  // src/commands/work-validate.ts
2055
- import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync9 } from "fs";
2056
- import { join as join15 } from "path";
2025
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync8 } from "fs";
2026
+ import { join as join14 } from "path";
2057
2027
  function normalizeWorkItemRel(workItem) {
2058
2028
  return workItem.replace(/\\/g, "/").replace(/^\.?\//, "");
2059
2029
  }
@@ -2110,7 +2080,7 @@ function validatePrMetadata(meta, findings) {
2110
2080
  }
2111
2081
  async function runWorkValidate(input) {
2112
2082
  const rel = normalizeWorkItemRel(input.workItem);
2113
- const workDir = join15(input.vault, rel);
2083
+ const workDir = join14(input.vault, rel);
2114
2084
  if (!existsSync3(workDir)) {
2115
2085
  return {
2116
2086
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -2129,10 +2099,10 @@ async function runWorkValidate(input) {
2129
2099
  findings.push({ code: "missing_spec", path: `${rel}/spec.md`, message: "spec.md is required" });
2130
2100
  }
2131
2101
  for (const file of files) {
2132
- const abs = join15(workDir, file);
2102
+ const abs = join14(workDir, file);
2133
2103
  let text;
2134
2104
  try {
2135
- text = readFileSync9(abs, "utf8");
2105
+ text = readFileSync8(abs, "utf8");
2136
2106
  } catch {
2137
2107
  findings.push({ code: "unreadable", path: `${rel}/${file}`, message: "cannot read file" });
2138
2108
  continue;
@@ -2226,20 +2196,20 @@ async function runWorkValidate(input) {
2226
2196
 
2227
2197
  // src/commands/work-complete.ts
2228
2198
  function workCompleteJournalPath(vault, opId) {
2229
- return join16(vault, ".skillwiki", "work-complete", `${opId}.env`);
2199
+ return join15(vault, ".skillwiki", "work-complete", `${opId}.env`);
2230
2200
  }
2231
2201
  function readWorkJournal(vault, opId) {
2232
2202
  const path = workCompleteJournalPath(vault, opId);
2233
2203
  if (!existsSync4(path)) return null;
2234
2204
  try {
2235
- return parseJournalEnv(readFileSync10(path, "utf8"));
2205
+ return parseJournalEnv(readFileSync9(path, "utf8"));
2236
2206
  } catch {
2237
2207
  return null;
2238
2208
  }
2239
2209
  }
2240
2210
  function writeWorkJournal(vault, opId, fields) {
2241
2211
  const path = workCompleteJournalPath(vault, opId);
2242
- const dir = join16(vault, ".skillwiki", "work-complete");
2212
+ const dir = join15(vault, ".skillwiki", "work-complete");
2243
2213
  mkdirSync(dir, { recursive: true });
2244
2214
  const tmp = `${path}.tmp.${process.pid}`;
2245
2215
  const body = serializeJournalEnv(fields, [
@@ -2259,11 +2229,11 @@ var DEFAULT_DEPS = {
2259
2229
  var PHASE_ORDER = ["validate", "evidence", "log", "projection", "commit", "done"];
2260
2230
  function resolveWorkDir(vault, workItem) {
2261
2231
  const rel = normalizeWorkItemRel(workItem);
2262
- const abs = join16(vault, rel);
2232
+ const abs = join15(vault, rel);
2263
2233
  if (!existsSync4(abs)) {
2264
2234
  return err("FILE_NOT_FOUND", { path: rel });
2265
2235
  }
2266
- if (!existsSync4(join16(abs, "spec.md"))) {
2236
+ if (!existsSync4(join15(abs, "spec.md"))) {
2267
2237
  return err("PREFLIGHT_FAILED", { reason: "missing-spec", path: rel });
2268
2238
  }
2269
2239
  return ok(abs);
@@ -2278,7 +2248,7 @@ function phaseIndex(phase) {
2278
2248
  async function patchFrontmatterStatus(filePath, extraBody) {
2279
2249
  let text;
2280
2250
  try {
2281
- text = readFileSync10(filePath, "utf8");
2251
+ text = readFileSync9(filePath, "utf8");
2282
2252
  } catch (error) {
2283
2253
  return err("WRITE_FAILED", { path: filePath, stage: "read", message: String(error) });
2284
2254
  }
@@ -2311,7 +2281,7 @@ async function setStatusCompleted(filePath) {
2311
2281
  return patchFrontmatterStatus(filePath);
2312
2282
  }
2313
2283
  async function writeEvidence(workDir, opId, phases, writeText) {
2314
- const path = join16(workDir, "evidence.md");
2284
+ const path = join15(workDir, "evidence.md");
2315
2285
  const body = [
2316
2286
  "---",
2317
2287
  "title: work-complete evidence",
@@ -2329,7 +2299,7 @@ async function writeEvidence(workDir, opId, phases, writeText) {
2329
2299
  return writeText(path, body);
2330
2300
  }
2331
2301
  async function markPlanComplete(workDir) {
2332
- const planPath = join16(workDir, "plan.md");
2302
+ const planPath = join15(workDir, "plan.md");
2333
2303
  if (!existsSync4(planPath)) return ok(null);
2334
2304
  return patchFrontmatterStatus(planPath, (body) => body.replace(/- \[ \]/g, "- [x]"));
2335
2305
  }
@@ -2416,7 +2386,7 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2416
2386
  advance("evidence");
2417
2387
  }
2418
2388
  if (phaseIndex(phase) <= phaseIndex("evidence")) {
2419
- const statusWrite = await setStatusCompleted(join16(workDir, "spec.md"));
2389
+ const statusWrite = await setStatusCompleted(join15(workDir, "spec.md"));
2420
2390
  if (!statusWrite.ok) return writeFailure("evidence-spec", statusWrite);
2421
2391
  const planWrite = await markPlanComplete(workDir);
2422
2392
  if (!planWrite.ok) return writeFailure("evidence-plan", planWrite);
@@ -2455,7 +2425,7 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2455
2425
  advance("projection");
2456
2426
  }
2457
2427
  if (phaseIndex(phase) <= phaseIndex("projection")) {
2458
- if (!existsSync4(join16(workDir, "evidence.md"))) {
2428
+ if (!existsSync4(join15(workDir, "evidence.md"))) {
2459
2429
  const evidenceWrite = await writeEvidence(
2460
2430
  workDir,
2461
2431
  opId,
@@ -2485,7 +2455,7 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2485
2455
  }
2486
2456
  let committed = false;
2487
2457
  if (phaseIndex(phase) <= phaseIndex("commit")) {
2488
- if (!input.noCommit && existsSync4(join16(input.vault, ".git"))) {
2458
+ if (!input.noCommit && existsSync4(join15(input.vault, ".git"))) {
2489
2459
  try {
2490
2460
  appendLastOp(input.vault, {
2491
2461
  operation: "work-complete",
@@ -2545,8 +2515,8 @@ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2545
2515
  }
2546
2516
 
2547
2517
  // src/commands/health.ts
2548
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
2549
- import { dirname as dirname4, join as join17, resolve as resolve2 } from "path";
2518
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
2519
+ import { dirname as dirname4, join as join16, resolve as resolve2 } from "path";
2550
2520
  import { platform } from "os";
2551
2521
  function statusFromCounts(counts) {
2552
2522
  if ((counts.error ?? 0) > 0) return "error";
@@ -2658,7 +2628,7 @@ function summarizeChecks(checks) {
2658
2628
  }
2659
2629
  function classifyLog(path, id, label, okPattern) {
2660
2630
  if (!existsSync5(path)) return { id, label, status: "warn", detail: `log file missing: ${path}` };
2661
- const lines = readFileSync11(path, "utf8").split(/\r?\n/).filter(Boolean);
2631
+ const lines = readFileSync10(path, "utf8").split(/\r?\n/).filter(Boolean);
2662
2632
  if (lines.length === 0) return { id, label, status: "warn", detail: `log file empty: ${path}` };
2663
2633
  const statusLine = [...lines].reverse().find(
2664
2634
  (line) => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z /.test(line) && (okPattern.test(line) || /\bFAIL\b|\bERROR\b/i.test(line))
@@ -2670,7 +2640,7 @@ function classifyLog(path, id, label, okPattern) {
2670
2640
  }
2671
2641
  function readVaultSyncRoleAndScope(home) {
2672
2642
  try {
2673
- const content = readFileSync11(join17(home, ".skillwiki", ".env"), "utf8");
2643
+ const content = readFileSync10(join16(home, ".skillwiki", ".env"), "utf8");
2674
2644
  let role;
2675
2645
  let serviceScope;
2676
2646
  let snapshotScript;
@@ -2702,11 +2672,11 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
2702
2672
  };
2703
2673
  }
2704
2674
  const isMac = platform() === "darwin";
2705
- const shareDir = isMac ? join17(home, "Library", "Application Support", "vault-sync", "bin") : join17(home, ".local", "share", "vault-sync", "bin");
2706
- const logDir = isMac ? join17(home, "Library", "Logs") : join17(home, ".local", "state", "vault-sync", "log");
2707
- const filterPath = join17(home, ".config", "rclone", "wiki-push-filters.txt");
2675
+ const shareDir = isMac ? join16(home, "Library", "Application Support", "vault-sync", "bin") : join16(home, ".local", "share", "vault-sync", "bin");
2676
+ const logDir = isMac ? join16(home, "Library", "Logs") : join16(home, ".local", "state", "vault-sync", "log");
2677
+ const filterPath = join16(home, ".config", "rclone", "wiki-push-filters.txt");
2708
2678
  const checks = [];
2709
- const pushScript = join17(shareDir, "wiki-push.sh");
2679
+ const pushScript = join16(shareDir, "wiki-push.sh");
2710
2680
  if (syncMode === "optional" && !existsSync5(pushScript)) {
2711
2681
  return {
2712
2682
  status: "pass",
@@ -2722,7 +2692,7 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
2722
2692
  }
2723
2693
  const vsConfig = readVaultSyncRoleAndScope(home);
2724
2694
  if (vsConfig.role === "snapshotter") {
2725
- const snapshotScript = vsConfig.snapshotScript ?? join17(shareDir, "wiki-snapshot.sh");
2695
+ const snapshotScript = vsConfig.snapshotScript ?? join16(shareDir, "wiki-snapshot.sh");
2726
2696
  const installed = existsSync5(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}` };
2727
2697
  const healthChecks = snapshotterHealthChecks(vsConfig.serviceScope ?? "user", logDir, env);
2728
2698
  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" };
@@ -2731,7 +2701,7 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
2731
2701
  if (!existsSync5(snapshotScript)) {
2732
2702
  guard = { id: "vault_sync_snapshot_guard", label: "Snapshot script guard", status: "error", detail: `Snapshot script not found at ${snapshotScript}` };
2733
2703
  } else {
2734
- const content = readFileSync11(snapshotScript, "utf8");
2704
+ const content = readFileSync10(snapshotScript, "utf8");
2735
2705
  guard = content.includes("--max-delete") ? { id: "vault_sync_snapshot_guard", label: "Snapshot script guard", status: "pass", detail: `--max-delete present in ${snapshotScript}` } : { id: "vault_sync_snapshot_guard", label: "Snapshot script guard", status: "error", detail: `${snapshotScript} is missing --max-delete guard` };
2736
2706
  }
2737
2707
  const allChecks = [installed, ...healthChecks, fetchNA, filterNA, guard];
@@ -2746,24 +2716,24 @@ function runVaultSyncHealth(home, syncMode, env = process.env) {
2746
2716
  }
2747
2717
  checks.push(existsSync5(pushScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found: ${pushScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Script missing: ${pushScript}` });
2748
2718
  if (isMac) {
2749
- const pushPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
2750
- const fetchPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
2719
+ const pushPlist = join16(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
2720
+ const fetchPlist = join16(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
2751
2721
  checks.push(existsSync5(pushPlist) && existsSync5(fetchPlist) ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: "launchd unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: "launchd unit files missing (read-only mode)" });
2752
2722
  checks.push({ id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "macOS host \u2014 check skipped" });
2753
2723
  } else {
2754
- const pushTimer = join17(home, ".config", "systemd", "user", "wiki-push.timer");
2755
- const fetchTimer = join17(home, ".config", "systemd", "user", "wiki-fetch.timer");
2756
- const fuseTimer = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
2757
- const fuseService = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
2724
+ const pushTimer = join16(home, ".config", "systemd", "user", "wiki-push.timer");
2725
+ const fetchTimer = join16(home, ".config", "systemd", "user", "wiki-fetch.timer");
2726
+ const fuseTimer = join16(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
2727
+ const fuseService = join16(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
2758
2728
  checks.push(existsSync5(pushTimer) && existsSync5(fetchTimer) ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: "systemd timer unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: "systemd timer unit files missing (read-only mode)" });
2759
2729
  checks.push(existsSync5(fuseTimer) && existsSync5(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)" });
2760
2730
  }
2761
- checks.push(classifyLog(join17(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
2762
- checks.push(classifyLog(join17(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
2731
+ checks.push(classifyLog(join16(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
2732
+ checks.push(classifyLog(join16(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
2763
2733
  if (!existsSync5(filterPath)) {
2764
2734
  checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "error", detail: `Filter missing: ${filterPath}` });
2765
2735
  } else {
2766
- const content = readFileSync11(filterPath, "utf8");
2736
+ const content = readFileSync10(filterPath, "utf8");
2767
2737
  const missing = [
2768
2738
  "remotely-save/data.json",
2769
2739
  ".skillwiki/sync.lock",
@@ -3083,11 +3053,11 @@ async function runHealth(input) {
3083
3053
 
3084
3054
  // src/commands/archive.ts
3085
3055
  import { rename as rename2, mkdir as mkdir6, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
3086
- import { join as join19, dirname as dirname5 } from "path";
3056
+ import { join as join18, dirname as dirname5 } from "path";
3087
3057
 
3088
3058
  // src/utils/delete-intent.ts
3089
3059
  import { mkdir as mkdir5, writeFile as writeFile4, readdir as readdir4, readFile as readFile6 } from "fs/promises";
3090
- import { join as join18 } from "path";
3060
+ import { join as join17 } from "path";
3091
3061
  var DELETE_INTENT_SCHEMA = "vault-delete-intent/v1";
3092
3062
  var DELETE_INTENT_DIR = "meta/delete-intents";
3093
3063
  function normalizeVaultRelPath(path) {
@@ -3118,10 +3088,10 @@ function buildDeleteIntent(input) {
3118
3088
  };
3119
3089
  }
3120
3090
  async function writeDeleteIntent(vault, intent) {
3121
- const dir = join18(vault, DELETE_INTENT_DIR);
3091
+ const dir = join17(vault, DELETE_INTENT_DIR);
3122
3092
  await mkdir5(dir, { recursive: true });
3123
3093
  const rel = `${DELETE_INTENT_DIR}/${pathToIntentFilename(intent.path)}`;
3124
- await writeFile4(join18(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
3094
+ await writeFile4(join17(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
3125
3095
  return rel;
3126
3096
  }
3127
3097
 
@@ -3159,7 +3129,7 @@ async function runArchive(input) {
3159
3129
  if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
3160
3130
  if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
3161
3131
  const slug = relPath.replace(/\.md$/, "").split("/").pop();
3162
- const archivePath = join19("_archive", relPath).replace(/\\/g, "/");
3132
+ const archivePath = join18("_archive", relPath).replace(/\\/g, "/");
3163
3133
  const remoteRoot = normalizeRemoteRoot(input.remote);
3164
3134
  const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
3165
3135
  let cascade;
@@ -3185,7 +3155,7 @@ async function runArchive(input) {
3185
3155
  const indexRefs = [];
3186
3156
  if (!isRaw) {
3187
3157
  try {
3188
- const idx = await readFile7(join19(input.vault, "index.md"), "utf8");
3158
+ const idx = await readFile7(join18(input.vault, "index.md"), "utf8");
3189
3159
  idx.split("\n").forEach((line, i) => {
3190
3160
  if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
3191
3161
  });
@@ -3212,7 +3182,7 @@ async function runArchive(input) {
3212
3182
  }
3213
3183
  if (input.cascade && input.apply && cascade) {
3214
3184
  for (const ref of cascade.source_array_refs) {
3215
- const absPath = join19(input.vault, ref.page);
3185
+ const absPath = join18(input.vault, ref.page);
3216
3186
  const text = await readFile7(absPath, "utf8");
3217
3187
  const split = splitFrontmatter(text);
3218
3188
  if (!split.ok) continue;
@@ -3230,12 +3200,12 @@ ${fmRewritten}
3230
3200
  }
3231
3201
  }
3232
3202
  }
3233
- await mkdir6(dirname5(join19(input.vault, archivePath)), { recursive: true });
3234
- await rename2(join19(input.vault, relPath), join19(input.vault, archivePath));
3203
+ await mkdir6(dirname5(join18(input.vault, archivePath)), { recursive: true });
3204
+ await rename2(join18(input.vault, relPath), join18(input.vault, archivePath));
3235
3205
  let indexUpdated = false;
3236
3206
  if (!isRaw) {
3237
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
3238
- const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
3207
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-DENEYZTK.js");
3208
+ const before = await readFile7(join18(input.vault, "index.md"), "utf8").catch(() => "");
3239
3209
  const fullTarget = relPath.replace(/\.md$/, "");
3240
3210
  const bare = fullTarget.split("/").pop() ?? fullTarget;
3241
3211
  const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
@@ -3286,7 +3256,7 @@ ${fmRewritten}
3286
3256
 
3287
3257
  // src/commands/remove.ts
3288
3258
  import { unlink as unlink2, access } from "fs/promises";
3289
- import { join as join20 } from "path";
3259
+ import { join as join19 } from "path";
3290
3260
  async function pathExists(abs) {
3291
3261
  try {
3292
3262
  await access(abs);
@@ -3312,7 +3282,7 @@ async function runRemove(input) {
3312
3282
  if (!relPath) {
3313
3283
  try {
3314
3284
  const candidate = normalizeVaultRelPath(input.page);
3315
- if (await pathExists(join20(input.vault, candidate))) {
3285
+ if (await pathExists(join19(input.vault, candidate))) {
3316
3286
  relPath = candidate;
3317
3287
  }
3318
3288
  } catch {
@@ -3341,11 +3311,11 @@ async function runRemove(input) {
3341
3311
  reason: input.reason
3342
3312
  });
3343
3313
  const tombstonePath = await writeDeleteIntent(input.vault, intent);
3344
- await unlink2(join20(input.vault, relPath));
3314
+ await unlink2(join19(input.vault, relPath));
3345
3315
  if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
3346
3316
  const { readFile: readFile17 } = await import("fs/promises");
3347
3317
  const { join: pathJoin } = await import("path");
3348
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
3318
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-DENEYZTK.js");
3349
3319
  const before = await readFile17(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
3350
3320
  const fullTarget = relPath.replace(/\.md$/, "");
3351
3321
  const bare = fullTarget.split("/").pop() ?? fullTarget;
@@ -3694,7 +3664,7 @@ ${migratedBody}${newFooter}`;
3694
3664
 
3695
3665
  // src/commands/update.ts
3696
3666
  import { execSync } from "child_process";
3697
- import { join as join21 } from "path";
3667
+ import { join as join20 } from "path";
3698
3668
  function parseCoreSemver(version) {
3699
3669
  const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
3700
3670
  if (!m) return null;
@@ -3725,7 +3695,7 @@ function resolveGlobalSkillsRoot() {
3725
3695
  encoding: "utf8",
3726
3696
  timeout: 5e3
3727
3697
  }).trim();
3728
- return join21(globalRoot, "skillwiki", "skills");
3698
+ return join20(globalRoot, "skillwiki", "skills");
3729
3699
  } catch {
3730
3700
  return null;
3731
3701
  }
@@ -3753,7 +3723,7 @@ async function runUpdate(input) {
3753
3723
  const pkg2 = readCliPackageJson();
3754
3724
  const currentVersion = pkg2.version;
3755
3725
  const tag = normalizeDistTag(input.distTag);
3756
- const target = join21(input.home, ".claude", "skills");
3726
+ const target = join20(input.home, ".claude", "skills");
3757
3727
  let latest;
3758
3728
  try {
3759
3729
  latest = execSync(`npm view skillwiki@${tag} version`, {
@@ -3833,14 +3803,14 @@ async function runUpdate(input) {
3833
3803
 
3834
3804
  // src/commands/self-update.ts
3835
3805
  import { execSync as execSync2 } from "child_process";
3836
- import { existsSync as existsSync6, readFileSync as readFileSync12 } from "fs";
3837
- import { join as join22 } from "path";
3806
+ import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
3807
+ import { join as join21 } from "path";
3838
3808
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
3839
3809
  async function runSelfUpdate(input) {
3840
3810
  const currentVersion = readCliPackageJson().version;
3841
3811
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
3842
3812
  const distTag = normalizeDistTag(input.distTag);
3843
- const localPkgPath = join22(sourceRoot, "packages", "cli", "package.json");
3813
+ const localPkgPath = join21(sourceRoot, "packages", "cli", "package.json");
3844
3814
  const hasLocalSource = existsSync6(localPkgPath);
3845
3815
  if (input.check) {
3846
3816
  let availableVersion = null;
@@ -3848,7 +3818,7 @@ async function runSelfUpdate(input) {
3848
3818
  if (hasLocalSource) {
3849
3819
  source = "local";
3850
3820
  try {
3851
- availableVersion = JSON.parse(readFileSync12(localPkgPath, "utf8")).version ?? null;
3821
+ availableVersion = JSON.parse(readFileSync11(localPkgPath, "utf8")).version ?? null;
3852
3822
  } catch {
3853
3823
  availableVersion = null;
3854
3824
  }
@@ -3906,7 +3876,7 @@ async function runSelfUpdate(input) {
3906
3876
  }
3907
3877
  const newVersion = (() => {
3908
3878
  try {
3909
- return JSON.parse(readFileSync12(localPkgPath, "utf8")).version ?? "unknown";
3879
+ return JSON.parse(readFileSync11(localPkgPath, "utf8")).version ?? "unknown";
3910
3880
  } catch {
3911
3881
  return "unknown";
3912
3882
  }
@@ -3973,9 +3943,9 @@ async function runSelfUpdate(input) {
3973
3943
 
3974
3944
  // src/commands/transcripts.ts
3975
3945
  import { readdir as readdir5, stat as stat3, readFile as readFile9 } from "fs/promises";
3976
- import { join as join23 } from "path";
3946
+ import { join as join22 } from "path";
3977
3947
  async function runTranscripts(input) {
3978
- const dir = join23(input.vault, "raw", "transcripts");
3948
+ const dir = join22(input.vault, "raw", "transcripts");
3979
3949
  let entries;
3980
3950
  try {
3981
3951
  entries = await readdir5(dir, { withFileTypes: true });
@@ -3985,7 +3955,7 @@ async function runTranscripts(input) {
3985
3955
  const transcripts = [];
3986
3956
  for (const entry of entries) {
3987
3957
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
3988
- const filePath = join23(dir, entry.name);
3958
+ const filePath = join22(dir, entry.name);
3989
3959
  const content = await readFile9(filePath, "utf8");
3990
3960
  const fm = extractFrontmatter(content);
3991
3961
  if (!fm.ok) continue;
@@ -4004,7 +3974,7 @@ async function runTranscripts(input) {
4004
3974
 
4005
3975
  // src/commands/compound.ts
4006
3976
  import { writeFile as writeFile7, mkdir as mkdir7, readdir as readdir6, unlink as unlink3 } from "fs/promises";
4007
- import { join as join24 } from "path";
3977
+ import { join as join23 } from "path";
4008
3978
  import { existsSync as existsSync7 } from "fs";
4009
3979
  import { readFile as readFile10 } from "fs/promises";
4010
3980
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
@@ -4104,7 +4074,7 @@ function extractRetroFields(date, cycleName, block) {
4104
4074
  };
4105
4075
  }
4106
4076
  async function runCompound(input) {
4107
- const logPath = join24(input.vault, "log.md");
4077
+ const logPath = join23(input.vault, "log.md");
4108
4078
  let logText;
4109
4079
  try {
4110
4080
  logText = await readFile10(logPath, "utf8");
@@ -4114,7 +4084,7 @@ async function runCompound(input) {
4114
4084
  const entries = parseRetroEntries(logText);
4115
4085
  const promoted = [];
4116
4086
  const skipped = [];
4117
- const compoundDir = join24(input.vault, "projects", input.project, "compound");
4087
+ const compoundDir = join23(input.vault, "projects", input.project, "compound");
4118
4088
  for (const entry of entries) {
4119
4089
  const generalizeValue = entry.generalize.trim();
4120
4090
  if (!/^yes/i.test(generalizeValue)) {
@@ -4122,7 +4092,7 @@ async function runCompound(input) {
4122
4092
  continue;
4123
4093
  }
4124
4094
  const slug = slugify(entry.cycleName);
4125
- const compoundPath = join24(compoundDir, `${slug}.md`);
4095
+ const compoundPath = join23(compoundDir, `${slug}.md`);
4126
4096
  if (existsSync7(compoundPath)) {
4127
4097
  skipped.push(entry.date);
4128
4098
  continue;
@@ -4183,7 +4153,7 @@ async function runCompound(input) {
4183
4153
  };
4184
4154
  }
4185
4155
  async function runCompoundDelete(input) {
4186
- const projectDir = join24(input.vault, "projects", input.project);
4156
+ const projectDir = join23(input.vault, "projects", input.project);
4187
4157
  if (!existsSync7(projectDir)) {
4188
4158
  return {
4189
4159
  exitCode: ExitCode.PROJECT_NOT_FOUND,
@@ -4191,7 +4161,7 @@ async function runCompoundDelete(input) {
4191
4161
  };
4192
4162
  }
4193
4163
  const entryName = input.entry.replace(/\.md$/, "");
4194
- const compoundPath = join24(projectDir, "compound", `${entryName}.md`);
4164
+ const compoundPath = join23(projectDir, "compound", `${entryName}.md`);
4195
4165
  if (!existsSync7(compoundPath)) {
4196
4166
  return {
4197
4167
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -4225,7 +4195,7 @@ knowledge.md regenerated`
4225
4195
  };
4226
4196
  }
4227
4197
  async function runCompoundList(input) {
4228
- const compoundDir = join24(input.vault, "projects", input.project, "compound");
4198
+ const compoundDir = join23(input.vault, "projects", input.project, "compound");
4229
4199
  if (!existsSync7(compoundDir)) {
4230
4200
  return {
4231
4201
  exitCode: ExitCode.OK,
@@ -4256,7 +4226,7 @@ could not read compound directory`
4256
4226
  const entries = [];
4257
4227
  for (const dirent of dirents) {
4258
4228
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
4259
- const filePath = join24(compoundDir, dirent.name);
4229
+ const filePath = join23(compoundDir, dirent.name);
4260
4230
  let text;
4261
4231
  try {
4262
4232
  text = await readFile10(filePath, "utf8");
@@ -4289,7 +4259,7 @@ no compound entries found`;
4289
4259
 
4290
4260
  // src/commands/session-brief.ts
4291
4261
  import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
4292
- import { join as join25, relative, sep } from "path";
4262
+ import { join as join24, relative, sep } from "path";
4293
4263
  var MAX_WORDS = 900;
4294
4264
  async function runSessionBrief(input) {
4295
4265
  const scan = await scanVault(input.vault);
@@ -4389,7 +4359,7 @@ async function resolveProject(input) {
4389
4359
  const envProject = input.env?.SKILLWIKI_PROJECT;
4390
4360
  if (envProject) return envProject;
4391
4361
  const cwd = input.cwd ?? process.cwd();
4392
- const projectDotenv = await readProjectSlug(join25(cwd, ".skillwiki", ".env"));
4362
+ const projectDotenv = await readProjectSlug(join24(cwd, ".skillwiki", ".env"));
4393
4363
  if (projectDotenv) return projectDotenv;
4394
4364
  const inferred = inferProjectFromPath(input.vault, cwd);
4395
4365
  if (inferred) return inferred;
@@ -4475,7 +4445,7 @@ async function loadTrendDigests(typedPages) {
4475
4445
  return out;
4476
4446
  }
4477
4447
  async function loadSessionPins(vault, project) {
4478
- const text = await readIfExists(join25(vault, "meta", "session-pins.md"));
4448
+ const text = await readIfExists(join24(vault, "meta", "session-pins.md"));
4479
4449
  if (!text) return [];
4480
4450
  const fm = extractFrontmatter(text);
4481
4451
  if (!fm.ok) return [];
@@ -4592,7 +4562,7 @@ function satelliteHealthWarnings(warning) {
4592
4562
  return warning ? [warning] : [];
4593
4563
  }
4594
4564
  async function loadHealthWarnings(vault) {
4595
- const text = await readIfExists(join25(vault, ".skillwiki", "health.json"));
4565
+ const text = await readIfExists(join24(vault, ".skillwiki", "health.json"));
4596
4566
  if (!text) return [];
4597
4567
  try {
4598
4568
  const parsed = JSON.parse(text);
@@ -4605,7 +4575,7 @@ async function loadHealthWarnings(vault) {
4605
4575
  }
4606
4576
  }
4607
4577
  async function loadMemoryTopics(vault, project) {
4608
- const text = await readIfExists(join25(vault, ".skillwiki", "memory", project, "topics.json"));
4578
+ const text = await readIfExists(join24(vault, ".skillwiki", "memory", project, "topics.json"));
4609
4579
  if (!text) return [];
4610
4580
  try {
4611
4581
  const parsed = JSON.parse(text);
@@ -4630,11 +4600,11 @@ async function loadMemoryTopics(vault, project) {
4630
4600
  }
4631
4601
  }
4632
4602
  async function writeBriefArtifacts(vault, input) {
4633
- const metaPath = join25(vault, "meta", "latest-session-brief.md");
4634
- const cacheMdPath = join25(vault, ".skillwiki", "session-brief.md");
4635
- const cacheJsonPath = join25(vault, ".skillwiki", "session-brief.json");
4636
- await mkdir8(join25(vault, "meta"), { recursive: true });
4637
- await mkdir8(join25(vault, ".skillwiki"), { recursive: true });
4603
+ const metaPath = join24(vault, "meta", "latest-session-brief.md");
4604
+ const cacheMdPath = join24(vault, ".skillwiki", "session-brief.md");
4605
+ const cacheJsonPath = join24(vault, ".skillwiki", "session-brief.json");
4606
+ await mkdir8(join24(vault, "meta"), { recursive: true });
4607
+ await mkdir8(join24(vault, ".skillwiki"), { recursive: true });
4638
4608
  const committed = renderCommittedBrief(input);
4639
4609
  const previousComparable = comparableBrief(await readIfExists(metaPath));
4640
4610
  const nextComparable = comparableBrief(committed);
@@ -4704,7 +4674,7 @@ function renderCommittedBrief(input) {
4704
4674
  ].filter((line) => line !== "").join("\n");
4705
4675
  }
4706
4676
  async function rebuildRootIndexProjection(vault) {
4707
- const before = await readIfExists(join25(vault, "index.md"));
4677
+ const before = await readIfExists(join24(vault, "index.md"));
4708
4678
  if (!before) return false;
4709
4679
  const projection = await renderRootIndex({ vault, currentText: before });
4710
4680
  if (!projection.ok) return false;
@@ -4757,17 +4727,20 @@ function dateFromPath(path) {
4757
4727
 
4758
4728
  // src/commands/ingest.ts
4759
4729
  import { readFile as readFile13, open, unlink as unlink4, mkdir as mkdir9 } from "fs/promises";
4760
- import { join as join27 } from "path";
4730
+ import { join as join26 } from "path";
4761
4731
  import { createHash as createHash4 } from "crypto";
4762
4732
 
4763
4733
  // src/commands/page-publish.ts
4764
4734
  import { realpathSync } from "fs";
4765
4735
  import { readFile as readFile12 } from "fs/promises";
4766
- import { join as join26, resolve as resolve3 } from "path";
4736
+ import { join as join25, resolve as resolve3 } from "path";
4767
4737
  var DEFAULT_DEPS2 = {
4768
4738
  afterStage: async () => void 0,
4769
4739
  preflight: (input) => runManagedWritePreflight(input)
4770
4740
  };
4741
+ function defaultPagePublishDeps(overrides = {}) {
4742
+ return { ...DEFAULT_DEPS2, ...overrides };
4743
+ }
4771
4744
  function errorExitCode(error) {
4772
4745
  switch (error) {
4773
4746
  case "FILE_NOT_FOUND":
@@ -4904,7 +4877,7 @@ async function runLockedPrimaryStages(input, vault, deps) {
4904
4877
  ExitCode.VAULT_PATH_INVALID
4905
4878
  );
4906
4879
  }
4907
- const schemaPath = join26(vault, "SCHEMA.md");
4880
+ const schemaPath = join25(vault, "SCHEMA.md");
4908
4881
  let schemaText;
4909
4882
  try {
4910
4883
  schemaText = await readFile12(schemaPath, "utf8");
@@ -5007,6 +4980,12 @@ function successReceipt(input, taxonomyAdded, pageChanged, indexUpdated, logAppe
5007
4980
  files_changed: filesChanged,
5008
4981
  base_oid: receipt?.base_oid ?? null,
5009
4982
  write_mode: receipt?.mode ?? null,
4983
+ ...receipt ? {
4984
+ mutation_vault: receipt.mutation_vault,
4985
+ git_vault: receipt.git_vault,
4986
+ convergence_source: receipt.convergence_source
4987
+ } : {},
4988
+ ...receipt?.convergence_vault ? { convergence_vault: receipt.convergence_vault } : {},
5010
4989
  ...receipt?.host_id ? { host_id: receipt.host_id } : {},
5011
4990
  humanHint: dryRun ? `dry run: would publish ${input.page.target} (${input.operationId.slice(0, 12)})` : `published ${input.page.target} (${input.operationId.slice(0, 12)})`
5012
4991
  })
@@ -5030,7 +5009,7 @@ async function readPageChanged(targetPath, content) {
5030
5009
  }
5031
5010
  }
5032
5011
  async function previewPreparedPagePublication(input, vault) {
5033
- const schemaPath = join26(vault, "SCHEMA.md");
5012
+ const schemaPath = join25(vault, "SCHEMA.md");
5034
5013
  let schemaText;
5035
5014
  try {
5036
5015
  schemaText = await readFile12(schemaPath, "utf8");
@@ -5045,7 +5024,7 @@ async function previewPreparedPagePublication(input, vault) {
5045
5024
  if (!reconciled.ok) return { exitCode: errorExitCode(reconciled.error), result: reconciled };
5046
5025
  const pageChanged = await readPageChanged(input.targetPath, input.page.content);
5047
5026
  if (!pageChanged.ok) return { exitCode: errorExitCode(pageChanged.error), result: pageChanged };
5048
- const indexPath = join26(vault, "index.md");
5027
+ const indexPath = join25(vault, "index.md");
5049
5028
  let indexText;
5050
5029
  try {
5051
5030
  indexText = await readFile12(indexPath, "utf8");
@@ -5059,7 +5038,7 @@ async function previewPreparedPagePublication(input, vault) {
5059
5038
  type: input.page.type
5060
5039
  });
5061
5040
  if (!index.ok) return { exitCode: errorExitCode(index.error), result: index };
5062
- const logPath = join26(vault, "log.md");
5041
+ const logPath = join25(vault, "log.md");
5063
5042
  let logText;
5064
5043
  try {
5065
5044
  logText = await readFile12(logPath, "utf8");
@@ -5084,160 +5063,164 @@ async function previewPreparedPagePublication(input, vault) {
5084
5063
  true
5085
5064
  );
5086
5065
  }
5087
- async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS2) {
5088
- const managed = acquireManagedWriteLock(vault, `page publish ${input.page.target}`);
5089
- if (!managed.ok) return { exitCode: errorExitCode(managed.error), result: managed };
5090
- try {
5091
- const preflight = await deps.preflight({
5092
- vault,
5093
- command: `page publish ${input.page.target}`,
5094
- lockToken: managed.data.ownerToken
5095
- });
5096
- if (!preflight.result.ok) {
5097
- return { exitCode: preflight.exitCode, result: preflight.result };
5098
- }
5099
- const writeReceipt = preflight.result.data;
5100
- const aggregateMode = resolveRootAggregateMode();
5101
- if (writeReceipt.base_oid) {
5102
- const head = git(vault, ["rev-parse", "HEAD"]);
5103
- if (head !== writeReceipt.base_oid) {
5104
- return {
5105
- exitCode: ExitCode.PREFLIGHT_FAILED,
5106
- result: err("PREFLIGHT_FAILED", {
5107
- reason: "base-oid-drift",
5108
- expected: writeReceipt.base_oid,
5109
- actual: head
5110
- })
5111
- };
5112
- }
5113
- }
5114
- let lock;
5115
- try {
5116
- lock = acquireOwnedSyncLock(vault, {
5117
- summary: `page publish ${input.page.target}`,
5118
- ttlMinutes: 1
5119
- });
5120
- } catch (error) {
5066
+ async function publishPreparedPageWithReceipt(input, vault, writeReceipt, deps = DEFAULT_DEPS2) {
5067
+ const canonicalVault = resolve3(vault);
5068
+ if (writeReceipt.mutation_vault !== canonicalVault) {
5069
+ return {
5070
+ exitCode: ExitCode.PREFLIGHT_FAILED,
5071
+ result: err("PREFLIGHT_FAILED", {
5072
+ reason: "mutation-vault-receipt-mismatch",
5073
+ expected: writeReceipt.mutation_vault,
5074
+ actual: canonicalVault
5075
+ })
5076
+ };
5077
+ }
5078
+ if (writeReceipt.base_oid) {
5079
+ const head = writeReceipt.git_vault ? git(writeReceipt.git_vault, ["rev-parse", "HEAD"]) : null;
5080
+ if (head !== writeReceipt.base_oid) {
5121
5081
  return {
5122
- exitCode: ExitCode.WRITE_FAILED,
5123
- result: err("WRITE_FAILED", { stage: "lock", message: String(error) })
5082
+ exitCode: ExitCode.PREFLIGHT_FAILED,
5083
+ result: err("PREFLIGHT_FAILED", {
5084
+ reason: "base-oid-drift",
5085
+ expected: writeReceipt.base_oid,
5086
+ actual: head,
5087
+ git_vault: writeReceipt.git_vault
5088
+ })
5124
5089
  };
5125
5090
  }
5126
- if (!lock.ok) return { exitCode: errorExitCode(lock.error), result: lock };
5127
- let primary;
5128
- let released;
5129
- try {
5130
- primary = await runLockedPrimaryStages(input, vault, deps);
5131
- } catch (error) {
5132
- primary = lockedFailure(
5133
- "schema",
5134
- emptyLockedState(),
5135
- err("WRITE_FAILED", { message: `unexpected primary-stage failure: ${String(error)}` })
5136
- );
5137
- } finally {
5138
- released = releaseOwnedSyncLock(lock.data);
5139
- }
5140
- const primaryState = primary?.ok ? primary.data : primary?.state;
5141
- if (released === void 0 || !released.ok || !released.data.released) {
5142
- return phaseFailure(
5143
- "unlock",
5144
- input,
5145
- primaryState?.published ?? false,
5146
- released && !released.ok ? released : err("WRITE_FAILED", { message: "lock release did not run" }),
5147
- {
5148
- primary_stage: primary && !primary.ok ? primary.stage : "complete",
5149
- primary_error: primary && !primary.ok ? primary.cause.error : void 0
5150
- }
5151
- );
5152
- }
5153
- const unlockHook = await observeStage(deps, "unlock");
5154
- if (unlockHook) return phaseFailure("unlock", input, primaryState?.published ?? false, unlockHook);
5155
- if (primary === void 0) {
5156
- return phaseFailure(
5157
- "schema",
5158
- input,
5159
- false,
5160
- err("WRITE_FAILED", { message: "locked publication produced no result" })
5161
- );
5162
- }
5163
- if (!primary.ok) {
5164
- return phaseFailure(
5165
- primary.stage,
5166
- input,
5167
- primary.state.published,
5168
- primary.cause,
5169
- void 0,
5170
- primary.exitCode
5171
- );
5172
- }
5173
- const state = primary.data;
5174
- const event = await writeLogEvent(vault, {
5175
- schema: "skillwiki-log-event/v1",
5176
- operation_id: input.operationId,
5177
- occurred_at: `${input.date}T00:00:00.000Z`,
5178
- host_id: writeReceipt.host_id ?? "standalone",
5179
- actor: "skillwiki-cli",
5180
- kind: "page-publish",
5181
- target: input.page.target,
5182
- note: input.logNote ?? `Published ${input.page.title}`,
5183
- metadata: {
5184
- page_type: input.page.type,
5185
- // Stable for replay: do not embed schema-delta taxonomy_added (varies after first write).
5186
- tags: [...input.page.tags].sort(),
5187
- base_oid: writeReceipt.base_oid
5091
+ }
5092
+ const aggregateMode = resolveRootAggregateMode();
5093
+ let lock;
5094
+ try {
5095
+ lock = acquireOwnedSyncLock(vault, {
5096
+ summary: `page publish ${input.page.target}`,
5097
+ ttlMinutes: 1
5098
+ });
5099
+ } catch (error) {
5100
+ return {
5101
+ exitCode: ExitCode.WRITE_FAILED,
5102
+ result: err("WRITE_FAILED", { stage: "lock", message: String(error) })
5103
+ };
5104
+ }
5105
+ if (!lock.ok) return { exitCode: errorExitCode(lock.error), result: lock };
5106
+ let primary;
5107
+ let released;
5108
+ try {
5109
+ primary = await runLockedPrimaryStages(input, vault, deps);
5110
+ } catch (error) {
5111
+ primary = lockedFailure(
5112
+ "schema",
5113
+ emptyLockedState(),
5114
+ err("WRITE_FAILED", { message: `unexpected primary-stage failure: ${String(error)}` })
5115
+ );
5116
+ } finally {
5117
+ released = releaseOwnedSyncLock(lock.data);
5118
+ }
5119
+ const primaryState = primary?.ok ? primary.data : primary?.state;
5120
+ if (released === void 0 || !released.ok || !released.data.released) {
5121
+ return phaseFailure(
5122
+ "unlock",
5123
+ input,
5124
+ primaryState?.published ?? false,
5125
+ released && !released.ok ? released : err("WRITE_FAILED", { message: "lock release did not run" }),
5126
+ {
5127
+ primary_stage: primary && !primary.ok ? primary.stage : "complete",
5128
+ primary_error: primary && !primary.ok ? primary.cause.error : void 0
5188
5129
  }
5130
+ );
5131
+ }
5132
+ const unlockHook = await observeStage(deps, "unlock");
5133
+ if (unlockHook) return phaseFailure("unlock", input, primaryState?.published ?? false, unlockHook);
5134
+ if (primary === void 0) {
5135
+ return phaseFailure(
5136
+ "schema",
5137
+ input,
5138
+ false,
5139
+ err("WRITE_FAILED", { message: "locked publication produced no result" })
5140
+ );
5141
+ }
5142
+ if (!primary.ok) {
5143
+ return phaseFailure(
5144
+ primary.stage,
5145
+ input,
5146
+ primary.state.published,
5147
+ primary.cause,
5148
+ void 0,
5149
+ primary.exitCode
5150
+ );
5151
+ }
5152
+ const state = primary.data;
5153
+ const event = await writeLogEvent(vault, {
5154
+ schema: "skillwiki-log-event/v1",
5155
+ operation_id: input.operationId,
5156
+ occurred_at: `${input.date}T00:00:00.000Z`,
5157
+ host_id: writeReceipt.host_id ?? "standalone",
5158
+ actor: "skillwiki-cli",
5159
+ kind: "page-publish",
5160
+ target: input.page.target,
5161
+ note: input.logNote ?? `Published ${input.page.title}`,
5162
+ metadata: {
5163
+ page_type: input.page.type,
5164
+ // Stable for replay: do not embed schema-delta taxonomy_added (varies after first write).
5165
+ tags: [...input.page.tags].sort(),
5166
+ base_oid: writeReceipt.base_oid
5167
+ }
5168
+ });
5169
+ if (!event.ok) {
5170
+ return phaseFailure(
5171
+ "event",
5172
+ input,
5173
+ true,
5174
+ event,
5175
+ void 0,
5176
+ event.error === "EVENT_IDENTITY_COLLISION" ? ExitCode.WRITE_FAILED : errorExitCode(event.error)
5177
+ );
5178
+ }
5179
+ if (event.data.created) state.changed.add(event.data.path);
5180
+ const eventHook = await observeStage(deps, "event");
5181
+ if (eventHook) return phaseFailure("event", input, true, eventHook);
5182
+ let logAppended = false;
5183
+ if (aggregateMode === "dual") {
5184
+ const log = await runLogAppend({
5185
+ vault,
5186
+ content: renderPublicationLog(input, state.taxonomyAdded),
5187
+ operationId: input.operationId,
5188
+ strictLock: true,
5189
+ recordLastOp: false
5189
5190
  });
5190
- if (!event.ok) {
5191
+ if (!log.result.ok) return phaseFailure("log", input, true, log.result);
5192
+ if (log.exitCode !== ExitCode.OK) {
5191
5193
  return phaseFailure(
5192
- "event",
5194
+ "log",
5193
5195
  input,
5194
5196
  true,
5195
- event,
5196
- void 0,
5197
- event.error === "EVENT_IDENTITY_COLLISION" ? ExitCode.WRITE_FAILED : errorExitCode(event.error)
5197
+ err("WRITE_FAILED", { message: "log append returned inconsistent success state" })
5198
5198
  );
5199
5199
  }
5200
- if (event.data.created) state.changed.add(event.data.path);
5201
- const eventHook = await observeStage(deps, "event");
5202
- if (eventHook) return phaseFailure("event", input, true, eventHook);
5203
- let logAppended = false;
5204
- if (aggregateMode === "dual" || writeReceipt.mode !== "immutable-record") {
5205
- if (aggregateMode === "dual") {
5206
- const log = await runLogAppend({
5207
- vault,
5208
- content: renderPublicationLog(input, state.taxonomyAdded),
5209
- operationId: input.operationId,
5210
- strictLock: true,
5211
- recordLastOp: false
5212
- });
5213
- if (!log.result.ok) return phaseFailure("log", input, true, log.result);
5214
- if (log.exitCode !== ExitCode.OK) {
5215
- return phaseFailure(
5216
- "log",
5217
- input,
5218
- true,
5219
- err("WRITE_FAILED", { message: "log append returned inconsistent success state" })
5220
- );
5221
- }
5222
- logAppended = log.result.data.appended;
5223
- if (logAppended) state.changed.add("log.md");
5224
- const logHook = await observeStage(deps, "log");
5225
- if (logHook) return phaseFailure("log", input, true, logHook);
5226
- }
5227
- }
5228
- return successReceipt(
5229
- input,
5230
- state.taxonomyAdded,
5231
- state.pageChanged,
5232
- state.indexUpdated,
5233
- logAppended,
5234
- [...state.changed],
5235
- false,
5236
- writeReceipt
5237
- );
5238
- } finally {
5239
- releaseManagedWriteLock(managed.data);
5200
+ logAppended = log.result.data.appended;
5201
+ if (logAppended) state.changed.add("log.md");
5202
+ const logHook = await observeStage(deps, "log");
5203
+ if (logHook) return phaseFailure("log", input, true, logHook);
5240
5204
  }
5205
+ return successReceipt(
5206
+ input,
5207
+ state.taxonomyAdded,
5208
+ state.pageChanged,
5209
+ state.indexUpdated,
5210
+ logAppended,
5211
+ [...state.changed],
5212
+ false,
5213
+ writeReceipt
5214
+ );
5215
+ }
5216
+ async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS2) {
5217
+ return runManagedWriteTransaction({
5218
+ vault,
5219
+ command: `page publish ${input.page.target}`,
5220
+ allowImmutableRecord: true,
5221
+ preflight: deps.preflight,
5222
+ mutate: async (receipt) => publishPreparedPageWithReceipt(input, vault, receipt, deps)
5223
+ });
5241
5224
  }
5242
5225
  async function runPagePublish(input, deps = DEFAULT_DEPS2) {
5243
5226
  const prepared = await preparePagePublication(input);
@@ -5394,7 +5377,7 @@ async function acquireRawCaptureLock(path) {
5394
5377
  }
5395
5378
  return err("WRITE_FAILED", { path: lockPath, phase: "raw-lock", message: "raw capture lock held" });
5396
5379
  }
5397
- async function runIngest(input) {
5380
+ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
5398
5381
  if (!input.source || input.source.trim().length === 0) {
5399
5382
  return {
5400
5383
  exitCode: ExitCode.SCHEME_REJECTED,
@@ -5488,7 +5471,7 @@ async function runIngest(input) {
5488
5471
  const rawRelPath = `raw/articles/${slug}.md`;
5489
5472
  const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
5490
5473
  const typedRelPath = `${typedDir}/${slug}.md`;
5491
- const rawAbsPath = join27(input.vault, rawRelPath);
5474
+ const rawAbsPath = join26(input.vault, rawRelPath);
5492
5475
  const identity = assessSourceIdentity({
5493
5476
  rawPath: rawRelPath,
5494
5477
  sourceUrl: sourceUrl ?? void 0,
@@ -5532,11 +5515,11 @@ async function runIngest(input) {
5532
5515
  );
5533
5516
  if (!input.dryRun) {
5534
5517
  try {
5535
- await mkdir9(join27(input.vault, typedDir), { recursive: true });
5518
+ await mkdir9(join26(input.vault, typedDir), { recursive: true });
5536
5519
  } catch (error) {
5537
5520
  return {
5538
5521
  exitCode: ExitCode.WRITE_FAILED,
5539
- result: err("WRITE_FAILED", { path: join27(input.vault, typedDir), message: String(error) })
5522
+ result: err("WRITE_FAILED", { path: join26(input.vault, typedDir), message: String(error) })
5540
5523
  };
5541
5524
  }
5542
5525
  }
@@ -5582,89 +5565,104 @@ async function runIngest(input) {
5582
5565
  })
5583
5566
  };
5584
5567
  }
5585
- try {
5586
- await mkdir9(join27(input.vault, "raw", "articles"), { recursive: true });
5587
- } catch (error) {
5588
- return {
5589
- exitCode: ExitCode.WRITE_FAILED,
5590
- result: err("WRITE_FAILED", { path: join27(input.vault, "raw", "articles"), message: String(error) })
5591
- };
5592
- }
5593
- const rawWrite = await writeResolvedRaw({
5594
- path: rawAbsPath,
5595
- sourceUrl,
5596
- sourceContent,
5597
- sha256,
5598
- today,
5599
- capture: resolvedRaw.data
5600
- });
5601
- if (!rawWrite.ok) {
5602
- return {
5603
- exitCode: rawWrite.error === "INGEST_VALIDATION_FAILED" ? ExitCode.INGEST_VALIDATION_FAILED : ExitCode.WRITE_FAILED,
5604
- result: rawWrite
5605
- };
5606
- }
5607
- if (rawWrite.data.capture.ingested !== publicationDate) {
5608
- publicationDate = rawWrite.data.capture.ingested;
5609
- typedContent = buildTypedContent(
5610
- input.title,
5611
- publicationDate,
5612
- input.type,
5613
- tags,
5614
- rawRelPath,
5615
- input.provenance
5616
- );
5617
- publication = preparePagePublicationFromContent({
5618
- vault: input.vault,
5619
- content: typedContent,
5620
- target: typedRelPath,
5621
- logNote: `ingested from ${rawRelPath}`,
5622
- now: /* @__PURE__ */ new Date(`${publicationDate}T00:00:00Z`)
5623
- });
5624
- if (!publication.ok) {
5568
+ let preparedPublication = publication.data;
5569
+ return runManagedWriteTransaction({
5570
+ vault: input.vault,
5571
+ command: `page publish ${preparedPublication.page.target}`,
5572
+ allowImmutableRecord: true,
5573
+ preflight: pagePublishDeps.preflight,
5574
+ mutate: async (receipt) => {
5575
+ try {
5576
+ await mkdir9(join26(input.vault, "raw", "articles"), { recursive: true });
5577
+ } catch (error) {
5578
+ return {
5579
+ exitCode: ExitCode.WRITE_FAILED,
5580
+ result: err("WRITE_FAILED", { path: join26(input.vault, "raw", "articles"), message: String(error) })
5581
+ };
5582
+ }
5583
+ const rawWrite = await writeResolvedRaw({
5584
+ path: rawAbsPath,
5585
+ sourceUrl,
5586
+ sourceContent,
5587
+ sha256,
5588
+ today,
5589
+ capture: resolvedRaw.data
5590
+ });
5591
+ if (!rawWrite.ok) {
5592
+ return {
5593
+ exitCode: rawWrite.error === "INGEST_VALIDATION_FAILED" ? ExitCode.INGEST_VALIDATION_FAILED : ExitCode.WRITE_FAILED,
5594
+ result: rawWrite
5595
+ };
5596
+ }
5597
+ if (rawWrite.data.capture.ingested !== publicationDate) {
5598
+ publicationDate = rawWrite.data.capture.ingested;
5599
+ typedContent = buildTypedContent(
5600
+ input.title,
5601
+ publicationDate,
5602
+ input.type,
5603
+ tags,
5604
+ rawRelPath,
5605
+ input.provenance
5606
+ );
5607
+ const refreshedPublication = preparePagePublicationFromContent({
5608
+ vault: input.vault,
5609
+ content: typedContent,
5610
+ target: typedRelPath,
5611
+ logNote: `ingested from ${rawRelPath}`,
5612
+ now: /* @__PURE__ */ new Date(`${publicationDate}T00:00:00Z`)
5613
+ });
5614
+ if (!refreshedPublication.ok) {
5615
+ return {
5616
+ exitCode: ExitCode.INGEST_VALIDATION_FAILED,
5617
+ result: refreshedPublication
5618
+ };
5619
+ }
5620
+ preparedPublication = refreshedPublication.data;
5621
+ }
5622
+ const published = await publishPreparedPageWithReceipt(
5623
+ preparedPublication,
5624
+ input.vault,
5625
+ receipt,
5626
+ pagePublishDeps
5627
+ );
5628
+ if (!published.result.ok) {
5629
+ return { exitCode: published.exitCode, result: published.result };
5630
+ }
5631
+ if (published.exitCode !== ExitCode.OK) {
5632
+ return {
5633
+ exitCode: published.exitCode,
5634
+ result: err("WRITE_FAILED", { message: "publisher returned inconsistent success state" })
5635
+ };
5636
+ }
5637
+ const changedFiles = [
5638
+ ...rawWrite.data.changed ? [rawRelPath] : [],
5639
+ ...published.result.data.files_changed
5640
+ ];
5641
+ if (changedFiles.length > 0) {
5642
+ appendLastOp(input.vault, {
5643
+ operation: "ingest",
5644
+ summary: `added ${slug}`,
5645
+ files: [...new Set(changedFiles)],
5646
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
5647
+ });
5648
+ }
5625
5649
  return {
5626
- exitCode: ExitCode.INGEST_VALIDATION_FAILED,
5627
- result: publication
5650
+ exitCode: ExitCode.OK,
5651
+ result: ok({
5652
+ raw_path: rawRelPath,
5653
+ typed_path: typedRelPath,
5654
+ sha256,
5655
+ dry_run: false,
5656
+ humanHint: [
5657
+ "created:",
5658
+ ` ${rawRelPath} (sha256: ${sha256.slice(0, 12)}...)`,
5659
+ ` ${typedRelPath}`
5660
+ ].join("\n"),
5661
+ publication: published.result.data
5662
+ })
5628
5663
  };
5629
5664
  }
5630
- }
5631
- const published = await publishPreparedPage(publication.data, input.vault);
5632
- if (!published.result.ok) {
5633
- return { exitCode: published.exitCode, result: published.result };
5634
- }
5635
- if (published.exitCode !== ExitCode.OK) {
5636
- return {
5637
- exitCode: published.exitCode,
5638
- result: err("WRITE_FAILED", { message: "publisher returned inconsistent success state" })
5639
- };
5640
- }
5641
- const changedFiles = [
5642
- ...rawWrite.data.changed ? [rawRelPath] : [],
5643
- ...published.result.data.files_changed
5644
- ];
5645
- if (changedFiles.length > 0) {
5646
- appendLastOp(input.vault, {
5647
- operation: "ingest",
5648
- summary: `added ${slug}`,
5649
- files: [...new Set(changedFiles)],
5650
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
5651
- });
5652
- }
5653
- return {
5654
- exitCode: ExitCode.OK,
5655
- result: ok({
5656
- raw_path: rawRelPath,
5657
- typed_path: typedRelPath,
5658
- sha256,
5659
- dry_run: false,
5660
- humanHint: [
5661
- "created:",
5662
- ` ${rawRelPath} (sha256: ${sha256.slice(0, 12)}...)`,
5663
- ` ${typedRelPath}`
5664
- ].join("\n"),
5665
- publication: published.result.data
5666
- })
5667
- };
5665
+ });
5668
5666
  }
5669
5667
 
5670
5668
  // src/commands/tag-sync.ts
@@ -5816,7 +5814,7 @@ ${body}`;
5816
5814
 
5817
5815
  // src/commands/tag-reconcile.ts
5818
5816
  import { readFile as readFile14 } from "fs/promises";
5819
- import { join as join28, posix } from "path";
5817
+ import { join as join27, posix } from "path";
5820
5818
  var TYPED_TARGET_RE = /^(entities|concepts|comparisons|queries|meta)\/[a-z0-9][a-z0-9./_-]*\.md$/;
5821
5819
  function errorExitCode2(error) {
5822
5820
  switch (error) {
@@ -5880,7 +5878,7 @@ async function resolveRequestedTags(input, page) {
5880
5878
  result: err("INVALID_FRONTMATTER", { message: "explicit tags must be an array of strings" })
5881
5879
  };
5882
5880
  }
5883
- const source = input.from ?? (explicit.length === 0 ? join28(input.vault, page) : void 0);
5881
+ const source = input.from ?? (explicit.length === 0 ? join27(input.vault, page) : void 0);
5884
5882
  if (!source) return { exitCode: ExitCode.OK, result: ok({ tags: [...new Set(explicit)].sort() }) };
5885
5883
  const sourced = await readTagsFromFile(source);
5886
5884
  if (!sourced.result.ok) return { exitCode: sourced.exitCode, result: sourced.result };
@@ -5919,7 +5917,7 @@ function previewResult(page, tags, reconciled, dryRun, filesChanged) {
5919
5917
  };
5920
5918
  }
5921
5919
  async function reconcileTagsWhileLocked(input, page, tags, comment) {
5922
- const schemaPath = join28(input.vault, "SCHEMA.md");
5920
+ const schemaPath = join27(input.vault, "SCHEMA.md");
5923
5921
  const current = await readSchema(schemaPath);
5924
5922
  if (!current.result.ok) return { exitCode: current.exitCode, result: current.result };
5925
5923
  const next = reconcileTaxonomyDocument(current.result.data, { tags, comment });
@@ -5956,7 +5954,7 @@ async function runTagReconcile(input) {
5956
5954
  const comment = taxonomyCommentForPage(page.data, date, input.reason);
5957
5955
  if (!comment.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: comment };
5958
5956
  if (!input.write) {
5959
- const schema = await readSchema(join28(input.vault, "SCHEMA.md"));
5957
+ const schema = await readSchema(join27(input.vault, "SCHEMA.md"));
5960
5958
  if (!schema.result.ok) return { exitCode: schema.exitCode, result: schema.result };
5961
5959
  const preview = reconcileTaxonomyDocument(schema.result.data, { tags, comment: comment.data });
5962
5960
  return previewResult(page.data, tags, preview, true, []);
@@ -6009,7 +6007,7 @@ async function runTagReconcile(input) {
6009
6007
 
6010
6008
  // src/commands/sync.ts
6011
6009
  import { existsSync as existsSync8 } from "fs";
6012
- import { join as join29 } from "path";
6010
+ import { join as join28 } from "path";
6013
6011
  import { execFileSync as execFileSync3 } from "child_process";
6014
6012
 
6015
6013
  // src/utils/vault-git-pathspec.ts
@@ -6063,7 +6061,7 @@ function refHasPath(vault, ref, path) {
6063
6061
  function runSyncStatus(input) {
6064
6062
  const vault = input.vault;
6065
6063
  const includeStashes = input.includeStashes ?? false;
6066
- if (!existsSync8(join29(vault, ".git"))) {
6064
+ if (!existsSync8(join28(vault, ".git"))) {
6067
6065
  return {
6068
6066
  exitCode: ExitCode.VAULT_PATH_INVALID,
6069
6067
  result: ok({
@@ -6170,7 +6168,7 @@ function runSyncStatus(input) {
6170
6168
  }
6171
6169
  async function runSyncPush(input) {
6172
6170
  const vault = input.vault;
6173
- if (!existsSync8(join29(vault, ".git"))) {
6171
+ if (!existsSync8(join28(vault, ".git"))) {
6174
6172
  return {
6175
6173
  exitCode: ExitCode.VAULT_PATH_INVALID,
6176
6174
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -6330,7 +6328,7 @@ function enableGitLongPathsOnWindows(vault) {
6330
6328
  }
6331
6329
  async function runSyncPull(input) {
6332
6330
  const vault = input.vault;
6333
- if (!existsSync8(join29(vault, ".git"))) {
6331
+ if (!existsSync8(join28(vault, ".git"))) {
6334
6332
  return {
6335
6333
  exitCode: ExitCode.VAULT_PATH_INVALID,
6336
6334
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -6557,42 +6555,13 @@ function runSyncJournalClearStale(input) {
6557
6555
  }
6558
6556
 
6559
6557
  // src/commands/snapshot-maintenance.ts
6560
- import { existsSync as existsSync9, readFileSync as readFileSync13, mkdirSync as mkdirSync3, appendFileSync } from "fs";
6558
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, appendFileSync } from "fs";
6561
6559
  import { createHash as createHash5 } from "crypto";
6562
6560
  import { execSync as execSync3, spawn } from "child_process";
6563
6561
  import { platform as platform2 } from "os";
6564
- import { join as join30, resolve as resolvePath2 } from "path";
6562
+ import { join as join29, resolve as resolvePath2 } from "path";
6565
6563
  var MAINTENANCE_SCHEMA_VERSION = 1;
6566
6564
  var MAINTENANCE_COMMAND = "snapshot-maintenance journal clear-stale";
6567
- function resolveConfiguredSnapshotWorktree(home) {
6568
- const skillwikiEnv = join30(home, ".skillwiki", ".env");
6569
- const explicit = readEnvKey2(skillwikiEnv, ["vault_sync.snapshot_worktree"]);
6570
- if (explicit) return resolvePath2(explicit);
6571
- const snapshotProfile = readEnvKey2(skillwikiEnv, ["vault_sync.snapshot_profile"]);
6572
- if (snapshotProfile) {
6573
- const fromProfile = readEnvKey2(snapshotProfile, ["WIKI_GIT_WORKTREE", "SNAPSHOT_WORKTREE", "GIT_DIR"]);
6574
- if (fromProfile) return resolvePath2(fromProfile);
6575
- }
6576
- return void 0;
6577
- }
6578
- function readEnvKey2(path, keys) {
6579
- try {
6580
- const content = readFileSync13(path, "utf8");
6581
- for (const line of content.split(/\r?\n/)) {
6582
- const trimmed = line.trim();
6583
- if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
6584
- const eq = trimmed.indexOf("=");
6585
- if (eq <= 0) continue;
6586
- const key = trimmed.slice(0, eq).trim();
6587
- if (!keys.includes(key)) continue;
6588
- const value = trimmed.slice(eq + 1).trim();
6589
- if (value.length > 0) return value;
6590
- }
6591
- } catch {
6592
- return void 0;
6593
- }
6594
- return void 0;
6595
- }
6596
6565
  function canonicalize(p) {
6597
6566
  return resolvePath2(p);
6598
6567
  }
@@ -6653,7 +6622,7 @@ async function runSnapshotMaintenanceDryRun(input) {
6653
6622
  audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE"));
6654
6623
  return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
6655
6624
  }
6656
- if (!existsSync9(requested) || !existsSync9(join30(requested, ".git"))) {
6625
+ if (!existsSync9(requested) || !existsSync9(join29(requested, ".git"))) {
6657
6626
  audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO"));
6658
6627
  return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
6659
6628
  }
@@ -6733,14 +6702,14 @@ async function resolveLiveVault(input) {
6733
6702
  return resolved.ok ? canonicalize(resolved.data.path) : void 0;
6734
6703
  }
6735
6704
  function snapshotMaintenanceAuditLogPath(home) {
6736
- const stateDir = platform2() === "darwin" ? join30(home, "Library", "Application Support", "vault-sync") : join30(home, ".local", "state", "vault-sync");
6737
- return join30(stateDir, "snapshot-maintenance-audit.jsonl");
6705
+ const stateDir = platform2() === "darwin" ? join29(home, "Library", "Application Support", "vault-sync") : join29(home, ".local", "state", "vault-sync");
6706
+ return join29(stateDir, "snapshot-maintenance-audit.jsonl");
6738
6707
  }
6739
6708
  function defaultAuditSink(home) {
6740
6709
  return (event) => {
6741
6710
  try {
6742
6711
  const logPath = snapshotMaintenanceAuditLogPath(home);
6743
- mkdirSync3(join30(logPath, ".."), { recursive: true });
6712
+ mkdirSync3(join29(logPath, ".."), { recursive: true });
6744
6713
  appendFileSync(logPath, JSON.stringify(event) + "\n", { encoding: "utf8" });
6745
6714
  } catch {
6746
6715
  }
@@ -6900,8 +6869,8 @@ function releaseSnapshotFlock(handle) {
6900
6869
  }
6901
6870
 
6902
6871
  // src/commands/backup.ts
6903
- import { statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6904
- import { join as join31, relative as relative2, dirname as dirname6 } from "path";
6872
+ import { statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6873
+ import { join as join30, relative as relative2, dirname as dirname6 } from "path";
6905
6874
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
6906
6875
 
6907
6876
  // src/utils/s3-client.ts
@@ -6925,7 +6894,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
6925
6894
  function* walkMarkdown(dir, base) {
6926
6895
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
6927
6896
  if (SKIP_DIRS.has(entry.name)) continue;
6928
- const full = join31(dir, entry.name);
6897
+ const full = join30(dir, entry.name);
6929
6898
  if (entry.isDirectory()) {
6930
6899
  yield* walkMarkdown(full, base);
6931
6900
  } else if (entry.name.endsWith(".md")) {
@@ -6948,7 +6917,7 @@ async function runBackupSync(input) {
6948
6917
  let failed = 0;
6949
6918
  const files = [...walkMarkdown(input.vault, input.vault)];
6950
6919
  for (const relPath of files) {
6951
- const absPath = join31(input.vault, relPath);
6920
+ const absPath = join30(input.vault, relPath);
6952
6921
  const localStat = statSync2(absPath);
6953
6922
  let needsUpload = true;
6954
6923
  try {
@@ -6967,7 +6936,7 @@ async function runBackupSync(input) {
6967
6936
  continue;
6968
6937
  }
6969
6938
  try {
6970
- const body = readFileSync14(absPath);
6939
+ const body = readFileSync13(absPath);
6971
6940
  await client.send(new PutObjectCommand({ Bucket: input.bucket, Key: relPath, Body: body }));
6972
6941
  uploaded++;
6973
6942
  } catch {
@@ -7024,7 +6993,7 @@ async function runBackupRestore(input) {
7024
6993
  const objects = list.Contents ?? [];
7025
6994
  for (const obj of objects) {
7026
6995
  if (!obj.Key) continue;
7027
- const localPath = join31(target, obj.Key);
6996
+ const localPath = join30(target, obj.Key);
7028
6997
  try {
7029
6998
  const localStat = statSync2(localPath);
7030
6999
  if (obj.LastModified && localStat.mtime > obj.LastModified) {
@@ -7072,7 +7041,7 @@ async function runBackupRestore(input) {
7072
7041
  // src/commands/status.ts
7073
7042
  import { existsSync as existsSync10, statSync as statSync3 } from "fs";
7074
7043
  import { readFile as readFile15 } from "fs/promises";
7075
- import { join as join32 } from "path";
7044
+ import { join as join31 } from "path";
7076
7045
  async function runStatus(input) {
7077
7046
  if (!existsSync10(input.vault)) {
7078
7047
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
@@ -7099,7 +7068,7 @@ async function runStatus(input) {
7099
7068
  const compound = scan.data.compound.length;
7100
7069
  let schemaVersion = "v1";
7101
7070
  try {
7102
- const schemaContent = await readFile15(join32(input.vault, "SCHEMA.md"), "utf8");
7071
+ const schemaContent = await readFile15(join31(input.vault, "SCHEMA.md"), "utf8");
7103
7072
  const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
7104
7073
  if (versionMatch) schemaVersion = versionMatch[1];
7105
7074
  } catch {
@@ -7160,7 +7129,7 @@ async function runStatus(input) {
7160
7129
 
7161
7130
  // src/commands/seed.ts
7162
7131
  import { mkdir as mkdir10, writeFile as writeFile9, stat as stat4 } from "fs/promises";
7163
- import { join as join33 } from "path";
7132
+ import { join as join32 } from "path";
7164
7133
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7165
7134
  var EXAMPLE_PAGES = {
7166
7135
  "entities/example-project.md": `---
@@ -7229,29 +7198,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
7229
7198
  `;
7230
7199
  async function runSeed(input) {
7231
7200
  try {
7232
- await stat4(join33(input.vault, "SCHEMA.md"));
7201
+ await stat4(join32(input.vault, "SCHEMA.md"));
7233
7202
  } catch {
7234
7203
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
7235
7204
  }
7236
7205
  const created = [];
7237
7206
  const skipped = [];
7238
7207
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
7239
- const absPath = join33(input.vault, relPath);
7208
+ const absPath = join32(input.vault, relPath);
7240
7209
  try {
7241
7210
  await stat4(absPath);
7242
7211
  skipped.push(relPath);
7243
7212
  } catch {
7244
- await mkdir10(join33(absPath, ".."), { recursive: true });
7213
+ await mkdir10(join32(absPath, ".."), { recursive: true });
7245
7214
  await writeFile9(absPath, content, "utf8");
7246
7215
  created.push(relPath);
7247
7216
  }
7248
7217
  }
7249
- const rawPath = join33(input.vault, "raw", "articles", "example-source.md");
7218
+ const rawPath = join32(input.vault, "raw", "articles", "example-source.md");
7250
7219
  try {
7251
7220
  await stat4(rawPath);
7252
7221
  skipped.push("raw/articles/example-source.md");
7253
7222
  } catch {
7254
- await mkdir10(join33(rawPath, ".."), { recursive: true });
7223
+ await mkdir10(join32(rawPath, ".."), { recursive: true });
7255
7224
  await writeFile9(rawPath, EXAMPLE_RAW, "utf8");
7256
7225
  created.push("raw/articles/example-source.md");
7257
7226
  }
@@ -7276,7 +7245,7 @@ async function runSeed(input) {
7276
7245
  // src/commands/canvas.ts
7277
7246
  import { readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
7278
7247
  import { existsSync as existsSync11 } from "fs";
7279
- import { join as join34 } from "path";
7248
+ import { join as join33 } from "path";
7280
7249
  var NODE_WIDTH = 240;
7281
7250
  var NODE_HEIGHT = 60;
7282
7251
  var COLUMN_SPACING = 400;
@@ -7354,7 +7323,7 @@ function buildCanvasEdges(adjacency) {
7354
7323
  return edges;
7355
7324
  }
7356
7325
  async function runCanvasGenerate(input) {
7357
- const graphPath = input.graphPath ?? join34(input.vault, ".skillwiki", "graph.json");
7326
+ const graphPath = input.graphPath ?? join33(input.vault, ".skillwiki", "graph.json");
7358
7327
  if (!existsSync11(graphPath)) {
7359
7328
  return {
7360
7329
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -7392,7 +7361,7 @@ async function runCanvasGenerate(input) {
7392
7361
  const nodes = buildCanvasNodes(paths);
7393
7362
  const edges = buildCanvasEdges(graph.adjacency);
7394
7363
  const canvas = { nodes, edges };
7395
- const outPath = join34(input.vault, "vault-graph.canvas");
7364
+ const outPath = join33(input.vault, "vault-graph.canvas");
7396
7365
  try {
7397
7366
  await writeFile10(outPath, JSON.stringify(canvas, null, 2));
7398
7367
  } catch (e) {
@@ -7414,10 +7383,10 @@ written: ${outPath}`
7414
7383
  }
7415
7384
 
7416
7385
  // src/commands/fleet-health.ts
7417
- import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
7386
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
7418
7387
  import { execSync as nodeExecSync } from "child_process";
7419
7388
  import { hostname as nodeHostname, platform as nodePlatform } from "os";
7420
- import { join as join35 } from "path";
7389
+ import { join as join34 } from "path";
7421
7390
  var SSH_TIMEOUT_MS = 15e3;
7422
7391
  var TIMER_UNIT = "agent-memory-trends.timer";
7423
7392
  var SERVICE_UNIT = "agent-memory-trends.service";
@@ -7516,7 +7485,7 @@ function probeLocal(vaultPath, deps) {
7516
7485
  let parsed = null;
7517
7486
  if (existsSync12(latestPath)) {
7518
7487
  try {
7519
- const wire = readSatelliteLatestRunFromText(readFileSync15(latestPath, "utf8"));
7488
+ const wire = readSatelliteLatestRunFromText(readFileSync14(latestPath, "utf8"));
7520
7489
  if (wire) {
7521
7490
  parsed = {
7522
7491
  status: wire.status,
@@ -7647,7 +7616,7 @@ async function runFleetHealth(input) {
7647
7616
  const home = input.home ?? env.HOME ?? "";
7648
7617
  const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
7649
7618
  const vault = input.vault ?? env.WIKI_PATH;
7650
- const file = input.file ?? (vault ? join35(vault, FLEET_REL_PATH) : void 0);
7619
+ const file = input.file ?? (vault ? join34(vault, FLEET_REL_PATH) : void 0);
7651
7620
  if (!file) {
7652
7621
  return {
7653
7622
  exitCode: ExitCode.NO_VAULT_CONFIGURED,
@@ -7732,14 +7701,14 @@ async function runFleetHealth(input) {
7732
7701
 
7733
7702
  // src/utils/auto-commit.ts
7734
7703
  import { existsSync as existsSync13 } from "fs";
7735
- import { join as join36 } from "path";
7704
+ import { join as join35 } from "path";
7736
7705
  async function postCommit(vault, exitCode) {
7737
7706
  if (exitCode !== 0) return;
7738
7707
  const home = process.env.HOME ?? "";
7739
7708
  const dotenv = await parseDotenvFile(configPath(home));
7740
7709
  const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
7741
7710
  if (autoCommit === "false") return;
7742
- if (!existsSync13(join36(vault, ".git"))) return;
7711
+ if (!existsSync13(join35(vault, ".git"))) return;
7743
7712
  const lastOps = readLastOp(vault);
7744
7713
  if (lastOps.length === 0) return;
7745
7714
  const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
@@ -7763,7 +7732,7 @@ async function postCommit(vault, exitCode) {
7763
7732
  }
7764
7733
 
7765
7734
  // src/commands/write-preflight.ts
7766
- import { existsSync as existsSync14, readFileSync as readFileSync16, statSync as statSync4 } from "fs";
7735
+ import { existsSync as existsSync14, readFileSync as readFileSync15, statSync as statSync4 } from "fs";
7767
7736
  async function runWritePreflightCommand(input) {
7768
7737
  if (!existsSync14(input.vault) || !statSync4(input.vault).isDirectory()) {
7769
7738
  return {
@@ -7779,7 +7748,7 @@ async function runWritePreflightCommand(input) {
7779
7748
  result: err("FILE_NOT_FOUND", { path: input.priorArtifactFile })
7780
7749
  };
7781
7750
  }
7782
- priorText = readFileSync16(input.priorArtifactFile, "utf8");
7751
+ priorText = readFileSync15(input.priorArtifactFile, "utf8");
7783
7752
  }
7784
7753
  const checkList = input.checks ? input.checks.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
7785
7754
  const result = runWritePreflight({
@@ -7893,12 +7862,17 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
7893
7862
  if (dirty) {
7894
7863
  return emit(dirty, void 0, { postCommit: false });
7895
7864
  }
7896
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-M2HL6DDY.js");
7865
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-LO4LZ6OQ.js");
7897
7866
  const run = await runManagedWriteTransaction2({
7898
7867
  vault,
7899
7868
  command,
7900
7869
  allowImmutableRecord: opts?.allowImmutableRecord === true,
7901
7870
  convergenceVault: opts?.convergenceVault,
7871
+ env: process.env,
7872
+ home: process.env.HOME ?? "",
7873
+ cwd: process.cwd(),
7874
+ osHostname: process.env.HOSTNAME,
7875
+ user: process.env.USER,
7902
7876
  mutate: async (receipt) => await mutate(receipt)
7903
7877
  });
7904
7878
  return emit(run, vault, opts);
@@ -7918,7 +7892,7 @@ program.command("validate <file>").description("validate vault page frontmatter
7918
7892
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
7919
7893
  });
7920
7894
  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) => {
7921
- const out = opts.out ?? join37(vault, ".skillwiki", "graph.json");
7895
+ const out = opts.out ?? join36(vault, ".skillwiki", "graph.json");
7922
7896
  return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
7923
7897
  });
7924
7898
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");