skillwiki 0.10.11 → 0.10.13

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
@@ -12,10 +12,12 @@ import {
12
12
  assessSourceIdentity,
13
13
  buildDegradedReasons,
14
14
  buildRemoteObjectPath,
15
+ canonicalEventJson,
15
16
  clearLastOp,
16
17
  configPath,
17
18
  evaluateDirtyVolumeGate,
18
19
  evaluateSatelliteRunHealth,
20
+ eventPathFor,
19
21
  extractCitationMarkers,
20
22
  extractTaxonomy,
21
23
  findPlugin,
@@ -78,8 +80,9 @@ import {
78
80
  scanConflictMarkerBlocksInText,
79
81
  taxonomyCommentForPage,
80
82
  upsertIndexEntry,
83
+ validateLogEvent,
81
84
  writeLogEvent
82
- } from "./chunk-P2FCRGKU.js";
85
+ } from "./chunk-X2CPXF4T.js";
83
86
  import {
84
87
  normalizeDistTag,
85
88
  readCache,
@@ -88,6 +91,8 @@ import {
88
91
  writeCache
89
92
  } from "./chunk-7I2TPIV5.js";
90
93
  import {
94
+ UNMANAGED_END,
95
+ UNMANAGED_START,
91
96
  assertTargetInsideVault,
92
97
  atomicWriteText,
93
98
  extractFrontmatter,
@@ -99,13 +104,13 @@ import {
99
104
  scanVault,
100
105
  splitFrontmatter,
101
106
  writeRootIndexProjection
102
- } from "./chunk-U34B2XQJ.js";
107
+ } from "./chunk-NMUYMNNB.js";
103
108
  import {
104
109
  acquireManagedWriteLock,
105
110
  releaseManagedWriteLock,
106
111
  runManagedWritePreflight,
107
112
  runManagedWriteTransaction
108
- } from "./chunk-3ZVZ2YEE.js";
113
+ } from "./chunk-IIUMTKKA.js";
109
114
  import {
110
115
  FLEET_REL_PATH,
111
116
  git,
@@ -127,7 +132,7 @@ import {
127
132
  snapshotterAliasForLocalHost,
128
133
  supersedeStaleReviewRequiredJournals,
129
134
  writeDotenv
130
- } from "./chunk-TYN2IHBY.js";
135
+ } from "./chunk-E3PMAHS3.js";
131
136
  import {
132
137
  ExitCode,
133
138
  MetaSchema,
@@ -138,7 +143,7 @@ import {
138
143
  } from "./chunk-C5OLZRRM.js";
139
144
 
140
145
  // src/cli.ts
141
- import { join as join35 } from "path";
146
+ import { join as join36 } from "path";
142
147
  import { Command } from "commander";
143
148
 
144
149
  // src/utils/output.ts
@@ -1683,10 +1688,265 @@ async function runProjectionsMaterialize(input, deps = defaultDeps) {
1683
1688
  };
1684
1689
  }
1685
1690
 
1691
+ // src/commands/projections-repair-legacy.ts
1692
+ import { readdir as readdir3, readFile as readFile4 } from "fs/promises";
1693
+ import { join as join13 } from "path";
1694
+ var OPERATION_ID_RE = /^[0-9a-f]{64}$/;
1695
+ var LEGACY_KEYS = ["created", "kind", "note", "operation_id", "target"];
1696
+ var EVENT_KEYS = [
1697
+ "actor",
1698
+ "host_id",
1699
+ "kind",
1700
+ "metadata",
1701
+ "note",
1702
+ "occurred_at",
1703
+ "operation_id",
1704
+ "schema",
1705
+ "target"
1706
+ ];
1707
+ var defaultDeps2 = {
1708
+ writeText: (path, text) => atomicWriteText(path, text)
1709
+ };
1710
+ function isPlainObject(value) {
1711
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1712
+ }
1713
+ function hasExactKeys(value, keys) {
1714
+ const actual = Object.keys(value).sort();
1715
+ return actual.length === keys.length && actual.every((key, index) => key === keys[index]);
1716
+ }
1717
+ function count(text, token) {
1718
+ return text.split(token).length - 1;
1719
+ }
1720
+ function planIndexRepair(current) {
1721
+ if (count(current, UNMANAGED_START) !== 1 || count(current, UNMANAGED_END) !== 1) {
1722
+ return err("SCHEME_REJECTED", {
1723
+ path: "index.md",
1724
+ message: "index.md must contain exactly one complete unmanaged marker pair"
1725
+ });
1726
+ }
1727
+ const start = current.indexOf(UNMANAGED_START);
1728
+ const end = current.indexOf(UNMANAGED_END);
1729
+ if (start < end) return ok({ current, repaired: current, needed: false });
1730
+ const between = current.slice(end + UNMANAGED_END.length, start);
1731
+ if (!/^\s*$/.test(between)) {
1732
+ return err("SCHEME_REJECTED", {
1733
+ path: "index.md",
1734
+ message: "reversed unmanaged markers must be adjacent except for whitespace"
1735
+ });
1736
+ }
1737
+ const repaired = current.slice(0, end) + UNMANAGED_START + between + UNMANAGED_END + current.slice(start + UNMANAGED_START.length);
1738
+ return ok({ current, repaired, needed: true });
1739
+ }
1740
+ function normalizeLegacyTimestamp(value) {
1741
+ if (typeof value !== "string") {
1742
+ return err("SCHEME_REJECTED", { message: "legacy event created must be a UTC timestamp" });
1743
+ }
1744
+ const match = value.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{3}))?Z$/);
1745
+ if (!match) {
1746
+ return err("SCHEME_REJECTED", { message: "legacy event created must be a UTC timestamp" });
1747
+ }
1748
+ const normalized = `${match[1]}.${match[2] ?? "000"}Z`;
1749
+ const date = new Date(normalized);
1750
+ if (Number.isNaN(date.getTime()) || date.toISOString() !== normalized) {
1751
+ return err("SCHEME_REJECTED", { message: "legacy event created is not a valid timestamp" });
1752
+ }
1753
+ return ok(normalized);
1754
+ }
1755
+ async function findEventPath(vault, operationId2) {
1756
+ const root = join13(vault, "meta", "log-events");
1757
+ const matches = [];
1758
+ let days;
1759
+ try {
1760
+ days = await readdir3(root, { withFileTypes: true });
1761
+ } catch (error) {
1762
+ return err("SCHEME_REJECTED", {
1763
+ message: "log event root is missing or unreadable",
1764
+ detail: String(error)
1765
+ });
1766
+ }
1767
+ for (const day2 of days) {
1768
+ if (!day2.isDirectory()) continue;
1769
+ const dayPath = join13(root, day2.name);
1770
+ const entries = await readdir3(dayPath, { withFileTypes: true });
1771
+ for (const entry of entries) {
1772
+ if (!entry.isFile() || entry.name !== `${operationId2}.json`) continue;
1773
+ matches.push({
1774
+ absolute: join13(dayPath, entry.name),
1775
+ relative: `meta/log-events/${day2.name}/${entry.name}`
1776
+ });
1777
+ }
1778
+ }
1779
+ if (matches.length !== 1) {
1780
+ return err("SCHEME_REJECTED", {
1781
+ message: "event operation ID must resolve to exactly one log event",
1782
+ operation_id: operationId2,
1783
+ matches: matches.map((match) => match.relative)
1784
+ });
1785
+ }
1786
+ return ok(matches[0]);
1787
+ }
1788
+ function planCanonicalEvent(parsed, operationId2, relativePath, current) {
1789
+ if (!hasExactKeys(parsed, EVENT_KEYS)) {
1790
+ return err("SCHEME_REJECTED", { path: relativePath, message: "canonical event has unexpected fields" });
1791
+ }
1792
+ const validated = validateLogEvent(parsed);
1793
+ if (!validated.ok) return validated;
1794
+ if (validated.data.operation_id !== operationId2 || eventPathFor(validated.data) !== relativePath) {
1795
+ return err("SCHEME_REJECTED", { path: relativePath, message: "path/identity mismatch" });
1796
+ }
1797
+ return ok({ current, repaired: current, needed: false });
1798
+ }
1799
+ function planLegacyEvent(parsed, operationId2, relativePath, current, hostId) {
1800
+ if (!hasExactKeys(parsed, LEGACY_KEYS)) {
1801
+ return err("SCHEME_REJECTED", {
1802
+ path: relativePath,
1803
+ message: "legacy event must contain exactly operation_id, kind, target, note, and created"
1804
+ });
1805
+ }
1806
+ if (parsed.operation_id !== operationId2) {
1807
+ return err("SCHEME_REJECTED", { path: relativePath, message: "path/identity mismatch" });
1808
+ }
1809
+ const { kind, target, note } = parsed;
1810
+ if (typeof kind !== "string" || typeof target !== "string" || typeof note !== "string") {
1811
+ return err("SCHEME_REJECTED", {
1812
+ path: relativePath,
1813
+ message: "legacy event kind, target, and note must be strings"
1814
+ });
1815
+ }
1816
+ const occurredAt = normalizeLegacyTimestamp(parsed.created);
1817
+ if (!occurredAt.ok) return occurredAt;
1818
+ const event = {
1819
+ schema: "skillwiki-log-event/v1",
1820
+ operation_id: operationId2,
1821
+ occurred_at: occurredAt.data,
1822
+ host_id: hostId,
1823
+ actor: "skillwiki-cli",
1824
+ kind,
1825
+ target,
1826
+ note,
1827
+ metadata: {}
1828
+ };
1829
+ const validated = validateLogEvent(event);
1830
+ if (!validated.ok) return validated;
1831
+ if (eventPathFor(validated.data) !== relativePath) {
1832
+ return err("SCHEME_REJECTED", { path: relativePath, message: "path/date mismatch" });
1833
+ }
1834
+ return ok({ current, repaired: canonicalEventJson(validated.data), needed: true });
1835
+ }
1836
+ async function planEventRepair(vault, operationId2, hostId) {
1837
+ const located = await findEventPath(vault, operationId2);
1838
+ if (!located.ok) return located;
1839
+ let current;
1840
+ try {
1841
+ current = await readFile4(located.data.absolute, "utf8");
1842
+ } catch (error) {
1843
+ return err("SCHEME_REJECTED", {
1844
+ path: located.data.relative,
1845
+ message: "event is unreadable",
1846
+ detail: String(error)
1847
+ });
1848
+ }
1849
+ let parsed;
1850
+ try {
1851
+ parsed = JSON.parse(current);
1852
+ } catch {
1853
+ return err("SCHEME_REJECTED", { path: located.data.relative, message: "invalid JSON" });
1854
+ }
1855
+ if (!isPlainObject(parsed)) {
1856
+ return err("SCHEME_REJECTED", { path: located.data.relative, message: "event must be an object" });
1857
+ }
1858
+ const plan = parsed.schema === "skillwiki-log-event/v1" ? planCanonicalEvent(parsed, operationId2, located.data.relative, current) : planLegacyEvent(parsed, operationId2, located.data.relative, current, hostId);
1859
+ if (!plan.ok) return plan;
1860
+ return ok({ ...plan.data, absolutePath: located.data.absolute, relativePath: located.data.relative });
1861
+ }
1862
+ async function runProjectionsRepairLegacy(input, deps = defaultDeps2) {
1863
+ if (!OPERATION_ID_RE.test(input.eventOperationId)) {
1864
+ return {
1865
+ exitCode: ExitCode.SCHEME_REJECTED,
1866
+ result: err("SCHEME_REJECTED", { message: "event operation ID must be 64 lowercase hex chars" })
1867
+ };
1868
+ }
1869
+ let indexText;
1870
+ try {
1871
+ indexText = await readFile4(join13(input.vault, "index.md"), "utf8");
1872
+ } catch (error) {
1873
+ return {
1874
+ exitCode: ExitCode.SCHEME_REJECTED,
1875
+ result: err("SCHEME_REJECTED", { path: "index.md", message: "index.md is unreadable", detail: String(error) })
1876
+ };
1877
+ }
1878
+ const indexPlan = planIndexRepair(indexText);
1879
+ if (!indexPlan.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: indexPlan };
1880
+ const eventPlan = await planEventRepair(
1881
+ input.vault,
1882
+ input.eventOperationId,
1883
+ input.hostId ?? "standalone"
1884
+ );
1885
+ if (!eventPlan.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: eventPlan };
1886
+ const baseOutput = {
1887
+ event_path: eventPlan.data.relativePath,
1888
+ index_repair_needed: indexPlan.data.needed,
1889
+ event_repair_needed: eventPlan.data.needed
1890
+ };
1891
+ if (!input.write) {
1892
+ return {
1893
+ exitCode: ExitCode.OK,
1894
+ result: ok({
1895
+ ...baseOutput,
1896
+ index_changed: false,
1897
+ event_changed: false,
1898
+ rolled_back: false,
1899
+ dry_run: true,
1900
+ humanHint: `dry run: index_repair_needed=${indexPlan.data.needed} event_repair_needed=${eventPlan.data.needed}`
1901
+ })
1902
+ };
1903
+ }
1904
+ let indexChanged = false;
1905
+ if (indexPlan.data.needed) {
1906
+ const written = await deps.writeText(join13(input.vault, "index.md"), indexPlan.data.repaired);
1907
+ if (!written.ok) return { exitCode: ExitCode.WRITE_FAILED, result: written };
1908
+ indexChanged = written.data.changed;
1909
+ }
1910
+ let eventChanged = false;
1911
+ if (eventPlan.data.needed) {
1912
+ const written = await deps.writeText(eventPlan.data.absolutePath, eventPlan.data.repaired);
1913
+ if (!written.ok) {
1914
+ let rolledBack = false;
1915
+ let rollbackError;
1916
+ if (indexChanged) {
1917
+ const rollback = await deps.writeText(join13(input.vault, "index.md"), indexPlan.data.current);
1918
+ rolledBack = rollback.ok;
1919
+ if (!rollback.ok) rollbackError = rollback;
1920
+ }
1921
+ return {
1922
+ exitCode: ExitCode.WRITE_FAILED,
1923
+ result: err("WRITE_FAILED", {
1924
+ message: "event repair failed",
1925
+ cause: written,
1926
+ rolled_back: rolledBack,
1927
+ rollback_error: rollbackError
1928
+ })
1929
+ };
1930
+ }
1931
+ eventChanged = written.data.changed;
1932
+ }
1933
+ return {
1934
+ exitCode: ExitCode.OK,
1935
+ result: ok({
1936
+ ...baseOutput,
1937
+ index_changed: indexChanged,
1938
+ event_changed: eventChanged,
1939
+ rolled_back: false,
1940
+ dry_run: false,
1941
+ humanHint: `repaired legacy projections index_changed=${indexChanged} event_changed=${eventChanged}`
1942
+ })
1943
+ };
1944
+ }
1945
+
1686
1946
  // src/commands/claim.ts
1687
- import { mkdir as mkdir4, writeFile as writeFile3, readFile as readFile4 } from "fs/promises";
1947
+ import { mkdir as mkdir4, writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
1688
1948
  import { existsSync as existsSync2, statSync } from "fs";
1689
- import { join as join13 } from "path";
1949
+ import { join as join14 } from "path";
1690
1950
  function extractDate(filename) {
1691
1951
  const m = filename.match(/^(\d{4}-\d{2}-\d{2})/);
1692
1952
  return m?.[1] ?? "";
@@ -1701,11 +1961,11 @@ async function runClaim(input) {
1701
1961
  if (!existsSync2(input.vault) || !statSync(input.vault).isDirectory()) {
1702
1962
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { path: input.vault }) };
1703
1963
  }
1704
- const absTranscript = join13(input.vault, input.transcript);
1964
+ const absTranscript = join14(input.vault, input.transcript);
1705
1965
  if (!existsSync2(absTranscript)) {
1706
1966
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.transcript }) };
1707
1967
  }
1708
- const content = await readFile4(absTranscript, "utf8");
1968
+ const content = await readFile5(absTranscript, "utf8");
1709
1969
  const fm = extractFrontmatter(content);
1710
1970
  let projectSlug = input.project;
1711
1971
  if (!projectSlug && fm.ok && typeof fm.data.project === "string") {
@@ -1717,7 +1977,7 @@ async function runClaim(input) {
1717
1977
  result: err("SCHEME_REJECTED", { message: "No project specified. Use --project or set project in transcript frontmatter." })
1718
1978
  };
1719
1979
  }
1720
- const projectDir = join13(input.vault, "projects", projectSlug);
1980
+ const projectDir = join14(input.vault, "projects", projectSlug);
1721
1981
  if (!existsSync2(projectDir) || !statSync(projectDir).isDirectory()) {
1722
1982
  return {
1723
1983
  exitCode: ExitCode.PROJECT_NOT_FOUND,
@@ -1734,7 +1994,7 @@ async function runClaim(input) {
1734
1994
  }
1735
1995
  const workSlug = input.slug || extractSlugFromFilename(filename);
1736
1996
  const dirName = `${date}-${workSlug}`;
1737
- const workDir = join13(projectDir, "work", dirName);
1997
+ const workDir = join14(projectDir, "work", dirName);
1738
1998
  const relWorkDir = `projects/${projectSlug}/work/${dirName}`;
1739
1999
  const relSpecPath = `${relWorkDir}/spec.md`;
1740
2000
  if (existsSync2(workDir)) {
@@ -1763,7 +2023,7 @@ async function runClaim(input) {
1763
2023
  `Claimed from ${input.transcript}`,
1764
2024
  ""
1765
2025
  ];
1766
- await writeFile3(join13(workDir, "spec.md"), specLines.join("\n"), "utf8");
2026
+ await writeFile3(join14(workDir, "spec.md"), specLines.join("\n"), "utf8");
1767
2027
  appendLastOp(input.vault, {
1768
2028
  operation: "claim",
1769
2029
  summary: `claimed ${input.transcript} \u2192 ${relWorkDir}`,
@@ -1783,11 +2043,11 @@ async function runClaim(input) {
1783
2043
 
1784
2044
  // src/commands/work-complete.ts
1785
2045
  import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync10, renameSync, writeFileSync as writeFileSync3 } from "fs";
1786
- import { join as join15 } from "path";
2046
+ import { join as join16 } from "path";
1787
2047
 
1788
2048
  // src/commands/work-validate.ts
1789
2049
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync9 } from "fs";
1790
- import { join as join14 } from "path";
2050
+ import { join as join15 } from "path";
1791
2051
  function normalizeWorkItemRel(workItem) {
1792
2052
  return workItem.replace(/\\/g, "/").replace(/^\.?\//, "");
1793
2053
  }
@@ -1844,7 +2104,7 @@ function validatePrMetadata(meta, findings) {
1844
2104
  }
1845
2105
  async function runWorkValidate(input) {
1846
2106
  const rel = normalizeWorkItemRel(input.workItem);
1847
- const workDir = join14(input.vault, rel);
2107
+ const workDir = join15(input.vault, rel);
1848
2108
  if (!existsSync3(workDir)) {
1849
2109
  return {
1850
2110
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -1863,7 +2123,7 @@ async function runWorkValidate(input) {
1863
2123
  findings.push({ code: "missing_spec", path: `${rel}/spec.md`, message: "spec.md is required" });
1864
2124
  }
1865
2125
  for (const file of files) {
1866
- const abs = join14(workDir, file);
2126
+ const abs = join15(workDir, file);
1867
2127
  let text;
1868
2128
  try {
1869
2129
  text = readFileSync9(abs, "utf8");
@@ -1960,7 +2220,7 @@ async function runWorkValidate(input) {
1960
2220
 
1961
2221
  // src/commands/work-complete.ts
1962
2222
  function workCompleteJournalPath(vault, opId) {
1963
- return join15(vault, ".skillwiki", "work-complete", `${opId}.env`);
2223
+ return join16(vault, ".skillwiki", "work-complete", `${opId}.env`);
1964
2224
  }
1965
2225
  function readWorkJournal(vault, opId) {
1966
2226
  const path = workCompleteJournalPath(vault, opId);
@@ -1973,7 +2233,7 @@ function readWorkJournal(vault, opId) {
1973
2233
  }
1974
2234
  function writeWorkJournal(vault, opId, fields) {
1975
2235
  const path = workCompleteJournalPath(vault, opId);
1976
- const dir = join15(vault, ".skillwiki", "work-complete");
2236
+ const dir = join16(vault, ".skillwiki", "work-complete");
1977
2237
  mkdirSync(dir, { recursive: true });
1978
2238
  const tmp = `${path}.tmp.${process.pid}`;
1979
2239
  const body = serializeJournalEnv(fields, [
@@ -1987,14 +2247,17 @@ function writeWorkJournal(vault, opId, fields) {
1987
2247
  writeFileSync3(tmp, body, "utf8");
1988
2248
  renameSync(tmp, path);
1989
2249
  }
2250
+ var DEFAULT_DEPS = {
2251
+ writeEvidenceText: atomicWriteText
2252
+ };
1990
2253
  var PHASE_ORDER = ["validate", "evidence", "log", "projection", "commit", "done"];
1991
2254
  function resolveWorkDir(vault, workItem) {
1992
2255
  const rel = normalizeWorkItemRel(workItem);
1993
- const abs = join15(vault, rel);
2256
+ const abs = join16(vault, rel);
1994
2257
  if (!existsSync4(abs)) {
1995
2258
  return err("FILE_NOT_FOUND", { path: rel });
1996
2259
  }
1997
- if (!existsSync4(join15(abs, "spec.md"))) {
2260
+ if (!existsSync4(join16(abs, "spec.md"))) {
1998
2261
  return err("PREFLIGHT_FAILED", { reason: "missing-spec", path: rel });
1999
2262
  }
2000
2263
  return ok(abs);
@@ -2041,8 +2304,8 @@ ${body}`);
2041
2304
  async function setStatusCompleted(filePath) {
2042
2305
  return patchFrontmatterStatus(filePath);
2043
2306
  }
2044
- async function writeEvidence(workDir, opId, phases) {
2045
- const path = join15(workDir, "evidence.md");
2307
+ async function writeEvidence(workDir, opId, phases, writeText) {
2308
+ const path = join16(workDir, "evidence.md");
2046
2309
  const body = [
2047
2310
  "---",
2048
2311
  "title: work-complete evidence",
@@ -2057,10 +2320,10 @@ async function writeEvidence(workDir, opId, phases) {
2057
2320
  `- completed_at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
2058
2321
  ""
2059
2322
  ].join("\n");
2060
- return atomicWriteText(path, body);
2323
+ return writeText(path, body);
2061
2324
  }
2062
2325
  async function markPlanComplete(workDir) {
2063
- const planPath = join15(workDir, "plan.md");
2326
+ const planPath = join16(workDir, "plan.md");
2064
2327
  if (!existsSync4(planPath)) return ok(null);
2065
2328
  return patchFrontmatterStatus(planPath, (body) => body.replace(/- \[ \]/g, "- [x]"));
2066
2329
  }
@@ -2070,7 +2333,7 @@ function writeFailure(stage, result) {
2070
2333
  result: result.ok ? err("WRITE_FAILED", { stage, message: "unexpected ok" }) : result
2071
2334
  };
2072
2335
  }
2073
- async function runWorkComplete(input) {
2336
+ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2074
2337
  const workDirResult = resolveWorkDir(input.vault, input.workItem);
2075
2338
  if (!workDirResult.ok) {
2076
2339
  return {
@@ -2147,11 +2410,16 @@ async function runWorkComplete(input) {
2147
2410
  advance("evidence");
2148
2411
  }
2149
2412
  if (phaseIndex(phase) <= phaseIndex("evidence")) {
2150
- const statusWrite = await setStatusCompleted(join15(workDir, "spec.md"));
2413
+ const statusWrite = await setStatusCompleted(join16(workDir, "spec.md"));
2151
2414
  if (!statusWrite.ok) return writeFailure("evidence-spec", statusWrite);
2152
2415
  const planWrite = await markPlanComplete(workDir);
2153
2416
  if (!planWrite.ok) return writeFailure("evidence-plan", planWrite);
2154
- const evidenceWrite = await writeEvidence(workDir, opId, completedPhases);
2417
+ const evidenceWrite = await writeEvidence(
2418
+ workDir,
2419
+ opId,
2420
+ completedPhases,
2421
+ deps.writeEvidenceText
2422
+ );
2155
2423
  if (!evidenceWrite.ok) return writeFailure("evidence", evidenceWrite);
2156
2424
  if (input.failAfter === "evidence") {
2157
2425
  throw new Error("simulated failure after evidence");
@@ -2181,8 +2449,13 @@ async function runWorkComplete(input) {
2181
2449
  advance("projection");
2182
2450
  }
2183
2451
  if (phaseIndex(phase) <= phaseIndex("projection")) {
2184
- if (!existsSync4(join15(workDir, "evidence.md"))) {
2185
- const evidenceWrite = await writeEvidence(workDir, opId, completedPhases);
2452
+ if (!existsSync4(join16(workDir, "evidence.md"))) {
2453
+ const evidenceWrite = await writeEvidence(
2454
+ workDir,
2455
+ opId,
2456
+ completedPhases,
2457
+ deps.writeEvidenceText
2458
+ );
2186
2459
  if (!evidenceWrite.ok) return writeFailure("projection-evidence", evidenceWrite);
2187
2460
  }
2188
2461
  const finalCheck = await runWorkValidate({
@@ -2206,7 +2479,7 @@ async function runWorkComplete(input) {
2206
2479
  }
2207
2480
  let committed = false;
2208
2481
  if (phaseIndex(phase) <= phaseIndex("commit")) {
2209
- if (!input.noCommit && existsSync4(join15(input.vault, ".git"))) {
2482
+ if (!input.noCommit && existsSync4(join16(input.vault, ".git"))) {
2210
2483
  try {
2211
2484
  appendLastOp(input.vault, {
2212
2485
  operation: "work-complete",
@@ -2267,7 +2540,7 @@ async function runWorkComplete(input) {
2267
2540
 
2268
2541
  // src/commands/health.ts
2269
2542
  import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
2270
- import { dirname as dirname4, join as join16, resolve as resolve2 } from "path";
2543
+ import { dirname as dirname4, join as join17, resolve as resolve2 } from "path";
2271
2544
  import { platform } from "os";
2272
2545
  function statusFromCounts(counts) {
2273
2546
  if ((counts.error ?? 0) > 0) return "error";
@@ -2400,11 +2673,11 @@ function runVaultSyncHealth(home, syncMode) {
2400
2673
  };
2401
2674
  }
2402
2675
  const isMac = platform() === "darwin";
2403
- const shareDir = isMac ? join16(home, "Library", "Application Support", "vault-sync", "bin") : join16(home, ".local", "share", "vault-sync", "bin");
2404
- const logDir = isMac ? join16(home, "Library", "Logs") : join16(home, ".local", "state", "vault-sync", "log");
2405
- const filterPath = join16(home, ".config", "rclone", "wiki-push-filters.txt");
2676
+ const shareDir = isMac ? join17(home, "Library", "Application Support", "vault-sync", "bin") : join17(home, ".local", "share", "vault-sync", "bin");
2677
+ const logDir = isMac ? join17(home, "Library", "Logs") : join17(home, ".local", "state", "vault-sync", "log");
2678
+ const filterPath = join17(home, ".config", "rclone", "wiki-push-filters.txt");
2406
2679
  const checks = [];
2407
- const pushScript = join16(shareDir, "wiki-push.sh");
2680
+ const pushScript = join17(shareDir, "wiki-push.sh");
2408
2681
  if (syncMode === "optional" && !existsSync5(pushScript)) {
2409
2682
  return {
2410
2683
  status: "pass",
@@ -2420,20 +2693,20 @@ function runVaultSyncHealth(home, syncMode) {
2420
2693
  }
2421
2694
  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}` });
2422
2695
  if (isMac) {
2423
- const pushPlist = join16(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
2424
- const fetchPlist = join16(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
2696
+ const pushPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
2697
+ const fetchPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
2425
2698
  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)" });
2426
2699
  checks.push({ id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "macOS host \u2014 check skipped" });
2427
2700
  } else {
2428
- const pushTimer = join16(home, ".config", "systemd", "user", "wiki-push.timer");
2429
- const fetchTimer = join16(home, ".config", "systemd", "user", "wiki-fetch.timer");
2430
- const fuseTimer = join16(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
2431
- const fuseService = join16(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
2701
+ const pushTimer = join17(home, ".config", "systemd", "user", "wiki-push.timer");
2702
+ const fetchTimer = join17(home, ".config", "systemd", "user", "wiki-fetch.timer");
2703
+ const fuseTimer = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
2704
+ const fuseService = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
2432
2705
  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)" });
2433
2706
  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)" });
2434
2707
  }
2435
- checks.push(classifyLog(join16(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
2436
- checks.push(classifyLog(join16(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
2708
+ checks.push(classifyLog(join17(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
2709
+ checks.push(classifyLog(join17(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
2437
2710
  if (!existsSync5(filterPath)) {
2438
2711
  checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "error", detail: `Filter missing: ${filterPath}` });
2439
2712
  } else {
@@ -2756,12 +3029,12 @@ async function runHealth(input) {
2756
3029
  }
2757
3030
 
2758
3031
  // src/commands/archive.ts
2759
- import { rename as rename2, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
2760
- import { join as join18, dirname as dirname5 } from "path";
3032
+ import { rename as rename2, mkdir as mkdir6, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
3033
+ import { join as join19, dirname as dirname5 } from "path";
2761
3034
 
2762
3035
  // src/utils/delete-intent.ts
2763
- import { mkdir as mkdir5, writeFile as writeFile4, readdir as readdir3, readFile as readFile5 } from "fs/promises";
2764
- import { join as join17 } from "path";
3036
+ import { mkdir as mkdir5, writeFile as writeFile4, readdir as readdir4, readFile as readFile6 } from "fs/promises";
3037
+ import { join as join18 } from "path";
2765
3038
  var DELETE_INTENT_SCHEMA = "vault-delete-intent/v1";
2766
3039
  var DELETE_INTENT_DIR = "meta/delete-intents";
2767
3040
  function normalizeVaultRelPath(path) {
@@ -2792,10 +3065,10 @@ function buildDeleteIntent(input) {
2792
3065
  };
2793
3066
  }
2794
3067
  async function writeDeleteIntent(vault, intent) {
2795
- const dir = join17(vault, DELETE_INTENT_DIR);
3068
+ const dir = join18(vault, DELETE_INTENT_DIR);
2796
3069
  await mkdir5(dir, { recursive: true });
2797
3070
  const rel = `${DELETE_INTENT_DIR}/${pathToIntentFilename(intent.path)}`;
2798
- await writeFile4(join17(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
3071
+ await writeFile4(join18(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
2799
3072
  return rel;
2800
3073
  }
2801
3074
 
@@ -2833,7 +3106,7 @@ async function runArchive(input) {
2833
3106
  if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
2834
3107
  if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
2835
3108
  const slug = relPath.replace(/\.md$/, "").split("/").pop();
2836
- const archivePath = join18("_archive", relPath).replace(/\\/g, "/");
3109
+ const archivePath = join19("_archive", relPath).replace(/\\/g, "/");
2837
3110
  const remoteRoot = normalizeRemoteRoot(input.remote);
2838
3111
  const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
2839
3112
  let cascade;
@@ -2859,7 +3132,7 @@ async function runArchive(input) {
2859
3132
  const indexRefs = [];
2860
3133
  if (!isRaw) {
2861
3134
  try {
2862
- const idx = await readFile6(join18(input.vault, "index.md"), "utf8");
3135
+ const idx = await readFile7(join19(input.vault, "index.md"), "utf8");
2863
3136
  idx.split("\n").forEach((line, i) => {
2864
3137
  if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
2865
3138
  });
@@ -2886,8 +3159,8 @@ async function runArchive(input) {
2886
3159
  }
2887
3160
  if (input.cascade && input.apply && cascade) {
2888
3161
  for (const ref of cascade.source_array_refs) {
2889
- const absPath = join18(input.vault, ref.page);
2890
- const text = await readFile6(absPath, "utf8");
3162
+ const absPath = join19(input.vault, ref.page);
3163
+ const text = await readFile7(absPath, "utf8");
2891
3164
  const split = splitFrontmatter(text);
2892
3165
  if (!split.ok) continue;
2893
3166
  const before = split.data.rawFrontmatter;
@@ -2904,12 +3177,12 @@ ${fmRewritten}
2904
3177
  }
2905
3178
  }
2906
3179
  }
2907
- await mkdir6(dirname5(join18(input.vault, archivePath)), { recursive: true });
2908
- await rename2(join18(input.vault, relPath), join18(input.vault, archivePath));
3180
+ await mkdir6(dirname5(join19(input.vault, archivePath)), { recursive: true });
3181
+ await rename2(join19(input.vault, relPath), join19(input.vault, archivePath));
2909
3182
  let indexUpdated = false;
2910
3183
  if (!isRaw) {
2911
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-VNJ3TLEK.js");
2912
- const before = await readFile6(join18(input.vault, "index.md"), "utf8").catch(() => "");
3184
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
3185
+ const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
2913
3186
  const fullTarget = relPath.replace(/\.md$/, "");
2914
3187
  const bare = fullTarget.split("/").pop() ?? fullTarget;
2915
3188
  const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
@@ -2960,7 +3233,7 @@ ${fmRewritten}
2960
3233
 
2961
3234
  // src/commands/remove.ts
2962
3235
  import { unlink as unlink2, access } from "fs/promises";
2963
- import { join as join19 } from "path";
3236
+ import { join as join20 } from "path";
2964
3237
  async function pathExists(abs) {
2965
3238
  try {
2966
3239
  await access(abs);
@@ -2986,7 +3259,7 @@ async function runRemove(input) {
2986
3259
  if (!relPath) {
2987
3260
  try {
2988
3261
  const candidate = normalizeVaultRelPath(input.page);
2989
- if (await pathExists(join19(input.vault, candidate))) {
3262
+ if (await pathExists(join20(input.vault, candidate))) {
2990
3263
  relPath = candidate;
2991
3264
  }
2992
3265
  } catch {
@@ -3015,12 +3288,12 @@ async function runRemove(input) {
3015
3288
  reason: input.reason
3016
3289
  });
3017
3290
  const tombstonePath = await writeDeleteIntent(input.vault, intent);
3018
- await unlink2(join19(input.vault, relPath));
3291
+ await unlink2(join20(input.vault, relPath));
3019
3292
  if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
3020
- const { readFile: readFile16 } = await import("fs/promises");
3293
+ const { readFile: readFile17 } = await import("fs/promises");
3021
3294
  const { join: pathJoin } = await import("path");
3022
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-VNJ3TLEK.js");
3023
- const before = await readFile16(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
3295
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
3296
+ const before = await readFile17(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
3024
3297
  const fullTarget = relPath.replace(/\.md$/, "");
3025
3298
  const bare = fullTarget.split("/").pop() ?? fullTarget;
3026
3299
  const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
@@ -3368,7 +3641,7 @@ ${migratedBody}${newFooter}`;
3368
3641
 
3369
3642
  // src/commands/update.ts
3370
3643
  import { execSync } from "child_process";
3371
- import { join as join20 } from "path";
3644
+ import { join as join21 } from "path";
3372
3645
  function parseCoreSemver(version) {
3373
3646
  const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
3374
3647
  if (!m) return null;
@@ -3399,7 +3672,7 @@ function resolveGlobalSkillsRoot() {
3399
3672
  encoding: "utf8",
3400
3673
  timeout: 5e3
3401
3674
  }).trim();
3402
- return join20(globalRoot, "skillwiki", "skills");
3675
+ return join21(globalRoot, "skillwiki", "skills");
3403
3676
  } catch {
3404
3677
  return null;
3405
3678
  }
@@ -3427,7 +3700,7 @@ async function runUpdate(input) {
3427
3700
  const pkg2 = readCliPackageJson();
3428
3701
  const currentVersion = pkg2.version;
3429
3702
  const tag = normalizeDistTag(input.distTag);
3430
- const target = join20(input.home, ".claude", "skills");
3703
+ const target = join21(input.home, ".claude", "skills");
3431
3704
  let latest;
3432
3705
  try {
3433
3706
  latest = execSync(`npm view skillwiki@${tag} version`, {
@@ -3508,13 +3781,13 @@ async function runUpdate(input) {
3508
3781
  // src/commands/self-update.ts
3509
3782
  import { execSync as execSync2 } from "child_process";
3510
3783
  import { existsSync as existsSync6, readFileSync as readFileSync12 } from "fs";
3511
- import { join as join21 } from "path";
3784
+ import { join as join22 } from "path";
3512
3785
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
3513
3786
  async function runSelfUpdate(input) {
3514
3787
  const currentVersion = readCliPackageJson().version;
3515
3788
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
3516
3789
  const distTag = normalizeDistTag(input.distTag);
3517
- const localPkgPath = join21(sourceRoot, "packages", "cli", "package.json");
3790
+ const localPkgPath = join22(sourceRoot, "packages", "cli", "package.json");
3518
3791
  const hasLocalSource = existsSync6(localPkgPath);
3519
3792
  if (input.check) {
3520
3793
  let availableVersion = null;
@@ -3646,21 +3919,21 @@ async function runSelfUpdate(input) {
3646
3919
  }
3647
3920
 
3648
3921
  // src/commands/transcripts.ts
3649
- import { readdir as readdir4, stat as stat3, readFile as readFile8 } from "fs/promises";
3650
- import { join as join22 } from "path";
3922
+ import { readdir as readdir5, stat as stat3, readFile as readFile9 } from "fs/promises";
3923
+ import { join as join23 } from "path";
3651
3924
  async function runTranscripts(input) {
3652
- const dir = join22(input.vault, "raw", "transcripts");
3925
+ const dir = join23(input.vault, "raw", "transcripts");
3653
3926
  let entries;
3654
3927
  try {
3655
- entries = await readdir4(dir, { withFileTypes: true });
3928
+ entries = await readdir5(dir, { withFileTypes: true });
3656
3929
  } catch {
3657
3930
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: { ok: false, error: "VAULT_PATH_INVALID", detail: `raw/transcripts/ not found: ${dir}` } };
3658
3931
  }
3659
3932
  const transcripts = [];
3660
3933
  for (const entry of entries) {
3661
3934
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
3662
- const filePath = join22(dir, entry.name);
3663
- const content = await readFile8(filePath, "utf8");
3935
+ const filePath = join23(dir, entry.name);
3936
+ const content = await readFile9(filePath, "utf8");
3664
3937
  const fm = extractFrontmatter(content);
3665
3938
  if (!fm.ok) continue;
3666
3939
  const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
@@ -3677,10 +3950,10 @@ async function runTranscripts(input) {
3677
3950
  }
3678
3951
 
3679
3952
  // src/commands/compound.ts
3680
- import { writeFile as writeFile7, mkdir as mkdir7, readdir as readdir5, unlink as unlink3 } from "fs/promises";
3681
- import { join as join23 } from "path";
3953
+ import { writeFile as writeFile7, mkdir as mkdir7, readdir as readdir6, unlink as unlink3 } from "fs/promises";
3954
+ import { join as join24 } from "path";
3682
3955
  import { existsSync as existsSync7 } from "fs";
3683
- import { readFile as readFile9 } from "fs/promises";
3956
+ import { readFile as readFile10 } from "fs/promises";
3684
3957
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
3685
3958
  var FIELD_RE = {
3686
3959
  improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
@@ -3778,17 +4051,17 @@ function extractRetroFields(date, cycleName, block) {
3778
4051
  };
3779
4052
  }
3780
4053
  async function runCompound(input) {
3781
- const logPath = join23(input.vault, "log.md");
4054
+ const logPath = join24(input.vault, "log.md");
3782
4055
  let logText;
3783
4056
  try {
3784
- logText = await readFile9(logPath, "utf8");
4057
+ logText = await readFile10(logPath, "utf8");
3785
4058
  } catch {
3786
4059
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
3787
4060
  }
3788
4061
  const entries = parseRetroEntries(logText);
3789
4062
  const promoted = [];
3790
4063
  const skipped = [];
3791
- const compoundDir = join23(input.vault, "projects", input.project, "compound");
4064
+ const compoundDir = join24(input.vault, "projects", input.project, "compound");
3792
4065
  for (const entry of entries) {
3793
4066
  const generalizeValue = entry.generalize.trim();
3794
4067
  if (!/^yes/i.test(generalizeValue)) {
@@ -3796,7 +4069,7 @@ async function runCompound(input) {
3796
4069
  continue;
3797
4070
  }
3798
4071
  const slug = slugify(entry.cycleName);
3799
- const compoundPath = join23(compoundDir, `${slug}.md`);
4072
+ const compoundPath = join24(compoundDir, `${slug}.md`);
3800
4073
  if (existsSync7(compoundPath)) {
3801
4074
  skipped.push(entry.date);
3802
4075
  continue;
@@ -3857,7 +4130,7 @@ async function runCompound(input) {
3857
4130
  };
3858
4131
  }
3859
4132
  async function runCompoundDelete(input) {
3860
- const projectDir = join23(input.vault, "projects", input.project);
4133
+ const projectDir = join24(input.vault, "projects", input.project);
3861
4134
  if (!existsSync7(projectDir)) {
3862
4135
  return {
3863
4136
  exitCode: ExitCode.PROJECT_NOT_FOUND,
@@ -3865,7 +4138,7 @@ async function runCompoundDelete(input) {
3865
4138
  };
3866
4139
  }
3867
4140
  const entryName = input.entry.replace(/\.md$/, "");
3868
- const compoundPath = join23(projectDir, "compound", `${entryName}.md`);
4141
+ const compoundPath = join24(projectDir, "compound", `${entryName}.md`);
3869
4142
  if (!existsSync7(compoundPath)) {
3870
4143
  return {
3871
4144
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -3899,7 +4172,7 @@ knowledge.md regenerated`
3899
4172
  };
3900
4173
  }
3901
4174
  async function runCompoundList(input) {
3902
- const compoundDir = join23(input.vault, "projects", input.project, "compound");
4175
+ const compoundDir = join24(input.vault, "projects", input.project, "compound");
3903
4176
  if (!existsSync7(compoundDir)) {
3904
4177
  return {
3905
4178
  exitCode: ExitCode.OK,
@@ -3914,7 +4187,7 @@ no compound directory found`
3914
4187
  }
3915
4188
  let dirents;
3916
4189
  try {
3917
- dirents = await readdir5(compoundDir, { withFileTypes: true });
4190
+ dirents = await readdir6(compoundDir, { withFileTypes: true });
3918
4191
  } catch {
3919
4192
  return {
3920
4193
  exitCode: ExitCode.OK,
@@ -3930,10 +4203,10 @@ could not read compound directory`
3930
4203
  const entries = [];
3931
4204
  for (const dirent of dirents) {
3932
4205
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
3933
- const filePath = join23(compoundDir, dirent.name);
4206
+ const filePath = join24(compoundDir, dirent.name);
3934
4207
  let text;
3935
4208
  try {
3936
- text = await readFile9(filePath, "utf8");
4209
+ text = await readFile10(filePath, "utf8");
3937
4210
  } catch {
3938
4211
  continue;
3939
4212
  }
@@ -3962,8 +4235,8 @@ no compound entries found`;
3962
4235
  }
3963
4236
 
3964
4237
  // src/commands/session-brief.ts
3965
- import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
3966
- import { join as join24, relative, sep } from "path";
4238
+ import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
4239
+ import { join as join25, relative, sep } from "path";
3967
4240
  var MAX_WORDS = 900;
3968
4241
  async function runSessionBrief(input) {
3969
4242
  const scan = await scanVault(input.vault);
@@ -4063,7 +4336,7 @@ async function resolveProject(input) {
4063
4336
  const envProject = input.env?.SKILLWIKI_PROJECT;
4064
4337
  if (envProject) return envProject;
4065
4338
  const cwd = input.cwd ?? process.cwd();
4066
- const projectDotenv = await readProjectSlug(join24(cwd, ".skillwiki", ".env"));
4339
+ const projectDotenv = await readProjectSlug(join25(cwd, ".skillwiki", ".env"));
4067
4340
  if (projectDotenv) return projectDotenv;
4068
4341
  const inferred = inferProjectFromPath(input.vault, cwd);
4069
4342
  if (inferred) return inferred;
@@ -4072,7 +4345,7 @@ async function resolveProject(input) {
4072
4345
  async function readProjectSlug(file) {
4073
4346
  let text;
4074
4347
  try {
4075
- text = await readFile10(file, "utf8");
4348
+ text = await readFile11(file, "utf8");
4076
4349
  } catch {
4077
4350
  return void 0;
4078
4351
  }
@@ -4149,7 +4422,7 @@ async function loadTrendDigests(typedPages) {
4149
4422
  return out;
4150
4423
  }
4151
4424
  async function loadSessionPins(vault, project) {
4152
- const text = await readIfExists(join24(vault, "meta", "session-pins.md"));
4425
+ const text = await readIfExists(join25(vault, "meta", "session-pins.md"));
4153
4426
  if (!text) return [];
4154
4427
  const fm = extractFrontmatter(text);
4155
4428
  if (!fm.ok) return [];
@@ -4266,7 +4539,7 @@ function satelliteHealthWarnings(warning) {
4266
4539
  return warning ? [warning] : [];
4267
4540
  }
4268
4541
  async function loadHealthWarnings(vault) {
4269
- const text = await readIfExists(join24(vault, ".skillwiki", "health.json"));
4542
+ const text = await readIfExists(join25(vault, ".skillwiki", "health.json"));
4270
4543
  if (!text) return [];
4271
4544
  try {
4272
4545
  const parsed = JSON.parse(text);
@@ -4279,7 +4552,7 @@ async function loadHealthWarnings(vault) {
4279
4552
  }
4280
4553
  }
4281
4554
  async function loadMemoryTopics(vault, project) {
4282
- const text = await readIfExists(join24(vault, ".skillwiki", "memory", project, "topics.json"));
4555
+ const text = await readIfExists(join25(vault, ".skillwiki", "memory", project, "topics.json"));
4283
4556
  if (!text) return [];
4284
4557
  try {
4285
4558
  const parsed = JSON.parse(text);
@@ -4304,11 +4577,11 @@ async function loadMemoryTopics(vault, project) {
4304
4577
  }
4305
4578
  }
4306
4579
  async function writeBriefArtifacts(vault, input) {
4307
- const metaPath = join24(vault, "meta", "latest-session-brief.md");
4308
- const cacheMdPath = join24(vault, ".skillwiki", "session-brief.md");
4309
- const cacheJsonPath = join24(vault, ".skillwiki", "session-brief.json");
4310
- await mkdir8(join24(vault, "meta"), { recursive: true });
4311
- await mkdir8(join24(vault, ".skillwiki"), { recursive: true });
4580
+ const metaPath = join25(vault, "meta", "latest-session-brief.md");
4581
+ const cacheMdPath = join25(vault, ".skillwiki", "session-brief.md");
4582
+ const cacheJsonPath = join25(vault, ".skillwiki", "session-brief.json");
4583
+ await mkdir8(join25(vault, "meta"), { recursive: true });
4584
+ await mkdir8(join25(vault, ".skillwiki"), { recursive: true });
4312
4585
  const committed = renderCommittedBrief(input);
4313
4586
  const previousComparable = comparableBrief(await readIfExists(metaPath));
4314
4587
  const nextComparable = comparableBrief(committed);
@@ -4378,7 +4651,7 @@ function renderCommittedBrief(input) {
4378
4651
  ].filter((line) => line !== "").join("\n");
4379
4652
  }
4380
4653
  async function rebuildRootIndexProjection(vault) {
4381
- const before = await readIfExists(join24(vault, "index.md"));
4654
+ const before = await readIfExists(join25(vault, "index.md"));
4382
4655
  if (!before) return false;
4383
4656
  const projection = await renderRootIndex({ vault, currentText: before });
4384
4657
  if (!projection.ok) return false;
@@ -4388,7 +4661,7 @@ async function rebuildRootIndexProjection(vault) {
4388
4661
  }
4389
4662
  async function readIfExists(path) {
4390
4663
  try {
4391
- return await readFile10(path, "utf8");
4664
+ return await readFile11(path, "utf8");
4392
4665
  } catch {
4393
4666
  return "";
4394
4667
  }
@@ -4430,15 +4703,15 @@ function dateFromPath(path) {
4430
4703
  }
4431
4704
 
4432
4705
  // src/commands/ingest.ts
4433
- import { readFile as readFile12, open, unlink as unlink4, mkdir as mkdir9 } from "fs/promises";
4434
- import { join as join26 } from "path";
4706
+ import { readFile as readFile13, open, unlink as unlink4, mkdir as mkdir9 } from "fs/promises";
4707
+ import { join as join27 } from "path";
4435
4708
  import { createHash as createHash4 } from "crypto";
4436
4709
 
4437
4710
  // src/commands/page-publish.ts
4438
4711
  import { realpathSync } from "fs";
4439
- import { readFile as readFile11 } from "fs/promises";
4440
- import { join as join25, resolve as resolve3 } from "path";
4441
- var DEFAULT_DEPS = {
4712
+ import { readFile as readFile12 } from "fs/promises";
4713
+ import { join as join26, resolve as resolve3 } from "path";
4714
+ var DEFAULT_DEPS2 = {
4442
4715
  afterStage: async () => void 0,
4443
4716
  preflight: (input) => runManagedWritePreflight(input)
4444
4717
  };
@@ -4507,7 +4780,7 @@ function preparePagePublicationFromContent(input) {
4507
4780
  async function preparePagePublication(input) {
4508
4781
  let content;
4509
4782
  try {
4510
- content = await readFile11(input.draftPath, "utf8");
4783
+ content = await readFile12(input.draftPath, "utf8");
4511
4784
  } catch (error) {
4512
4785
  return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
4513
4786
  }
@@ -4578,10 +4851,10 @@ async function runLockedPrimaryStages(input, vault, deps) {
4578
4851
  ExitCode.VAULT_PATH_INVALID
4579
4852
  );
4580
4853
  }
4581
- const schemaPath = join25(vault, "SCHEMA.md");
4854
+ const schemaPath = join26(vault, "SCHEMA.md");
4582
4855
  let schemaText;
4583
4856
  try {
4584
- schemaText = await readFile11(schemaPath, "utf8");
4857
+ schemaText = await readFile12(schemaPath, "utf8");
4585
4858
  } catch (error) {
4586
4859
  return lockedFailure("schema", state, err("WRITE_FAILED", { message: String(error) }));
4587
4860
  }
@@ -4611,8 +4884,8 @@ async function runLockedPrimaryStages(input, vault, deps) {
4611
4884
  let visibleSchema;
4612
4885
  try {
4613
4886
  [visible, visibleSchema] = await Promise.all([
4614
- readFile11(input.targetPath, "utf8"),
4615
- readFile11(schemaPath, "utf8")
4887
+ readFile12(input.targetPath, "utf8"),
4888
+ readFile12(schemaPath, "utf8")
4616
4889
  ]);
4617
4890
  } catch (error) {
4618
4891
  return lockedFailure("verify", state, err("WRITE_FAILED", { message: String(error) }));
@@ -4697,17 +4970,17 @@ function renderPublicationLog(input, added) {
4697
4970
  }
4698
4971
  async function readPageChanged(targetPath, content) {
4699
4972
  try {
4700
- return ok(await readFile11(targetPath, "utf8") !== content);
4973
+ return ok(await readFile12(targetPath, "utf8") !== content);
4701
4974
  } catch (error) {
4702
4975
  if (error.code === "ENOENT") return ok(true);
4703
4976
  return err("WRITE_FAILED", { path: targetPath, message: String(error) });
4704
4977
  }
4705
4978
  }
4706
4979
  async function previewPreparedPagePublication(input, vault) {
4707
- const schemaPath = join25(vault, "SCHEMA.md");
4980
+ const schemaPath = join26(vault, "SCHEMA.md");
4708
4981
  let schemaText;
4709
4982
  try {
4710
- schemaText = await readFile11(schemaPath, "utf8");
4983
+ schemaText = await readFile12(schemaPath, "utf8");
4711
4984
  } catch (error) {
4712
4985
  const result = err("FILE_NOT_FOUND", { path: schemaPath, message: String(error) });
4713
4986
  return { exitCode: ExitCode.FILE_NOT_FOUND, result };
@@ -4719,10 +4992,10 @@ async function previewPreparedPagePublication(input, vault) {
4719
4992
  if (!reconciled.ok) return { exitCode: errorExitCode(reconciled.error), result: reconciled };
4720
4993
  const pageChanged = await readPageChanged(input.targetPath, input.page.content);
4721
4994
  if (!pageChanged.ok) return { exitCode: errorExitCode(pageChanged.error), result: pageChanged };
4722
- const indexPath = join25(vault, "index.md");
4995
+ const indexPath = join26(vault, "index.md");
4723
4996
  let indexText;
4724
4997
  try {
4725
- indexText = await readFile11(indexPath, "utf8");
4998
+ indexText = await readFile12(indexPath, "utf8");
4726
4999
  } catch (error) {
4727
5000
  const result = err("FILE_NOT_FOUND", { path: indexPath, message: String(error) });
4728
5001
  return { exitCode: ExitCode.FILE_NOT_FOUND, result };
@@ -4733,10 +5006,10 @@ async function previewPreparedPagePublication(input, vault) {
4733
5006
  type: input.page.type
4734
5007
  });
4735
5008
  if (!index.ok) return { exitCode: errorExitCode(index.error), result: index };
4736
- const logPath = join25(vault, "log.md");
5009
+ const logPath = join26(vault, "log.md");
4737
5010
  let logText;
4738
5011
  try {
4739
- logText = await readFile11(logPath, "utf8");
5012
+ logText = await readFile12(logPath, "utf8");
4740
5013
  } catch (error) {
4741
5014
  const result = err("FILE_NOT_FOUND", { path: logPath, message: String(error) });
4742
5015
  return { exitCode: ExitCode.FILE_NOT_FOUND, result };
@@ -4758,7 +5031,7 @@ async function previewPreparedPagePublication(input, vault) {
4758
5031
  true
4759
5032
  );
4760
5033
  }
4761
- async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS) {
5034
+ async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS2) {
4762
5035
  const managed = acquireManagedWriteLock(vault, `page publish ${input.page.target}`);
4763
5036
  if (!managed.ok) return { exitCode: errorExitCode(managed.error), result: managed };
4764
5037
  try {
@@ -4913,7 +5186,7 @@ async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS) {
4913
5186
  releaseManagedWriteLock(managed.data);
4914
5187
  }
4915
5188
  }
4916
- async function runPagePublish(input, deps = DEFAULT_DEPS) {
5189
+ async function runPagePublish(input, deps = DEFAULT_DEPS2) {
4917
5190
  const prepared = await preparePagePublication(input);
4918
5191
  if (!prepared.ok) return { exitCode: errorExitCode(prepared.error), result: prepared };
4919
5192
  if (!input.write) return previewPreparedPagePublication(prepared.data, input.vault);
@@ -4998,7 +5271,7 @@ function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance)
4998
5271
  }
4999
5272
  async function resolveRawCapture(input) {
5000
5273
  try {
5001
- const existing = await readFile12(input.path, "utf8");
5274
+ const existing = await readFile13(input.path, "utf8");
5002
5275
  const frontmatter = extractFrontmatter(existing);
5003
5276
  if (!frontmatter.ok) {
5004
5277
  return err("INGEST_VALIDATION_FAILED", {
@@ -5137,7 +5410,7 @@ async function runIngest(input) {
5137
5410
  sourceContent = fetchResult.data.body;
5138
5411
  } else {
5139
5412
  try {
5140
- sourceContent = await readFile12(input.source, "utf8");
5413
+ sourceContent = await readFile13(input.source, "utf8");
5141
5414
  } catch {
5142
5415
  return {
5143
5416
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -5162,7 +5435,7 @@ async function runIngest(input) {
5162
5435
  const rawRelPath = `raw/articles/${slug}.md`;
5163
5436
  const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
5164
5437
  const typedRelPath = `${typedDir}/${slug}.md`;
5165
- const rawAbsPath = join26(input.vault, rawRelPath);
5438
+ const rawAbsPath = join27(input.vault, rawRelPath);
5166
5439
  const identity = assessSourceIdentity({
5167
5440
  rawPath: rawRelPath,
5168
5441
  sourceUrl: sourceUrl ?? void 0,
@@ -5206,11 +5479,11 @@ async function runIngest(input) {
5206
5479
  );
5207
5480
  if (!input.dryRun) {
5208
5481
  try {
5209
- await mkdir9(join26(input.vault, typedDir), { recursive: true });
5482
+ await mkdir9(join27(input.vault, typedDir), { recursive: true });
5210
5483
  } catch (error) {
5211
5484
  return {
5212
5485
  exitCode: ExitCode.WRITE_FAILED,
5213
- result: err("WRITE_FAILED", { path: join26(input.vault, typedDir), message: String(error) })
5486
+ result: err("WRITE_FAILED", { path: join27(input.vault, typedDir), message: String(error) })
5214
5487
  };
5215
5488
  }
5216
5489
  }
@@ -5257,11 +5530,11 @@ async function runIngest(input) {
5257
5530
  };
5258
5531
  }
5259
5532
  try {
5260
- await mkdir9(join26(input.vault, "raw", "articles"), { recursive: true });
5533
+ await mkdir9(join27(input.vault, "raw", "articles"), { recursive: true });
5261
5534
  } catch (error) {
5262
5535
  return {
5263
5536
  exitCode: ExitCode.WRITE_FAILED,
5264
- result: err("WRITE_FAILED", { path: join26(input.vault, "raw", "articles"), message: String(error) })
5537
+ result: err("WRITE_FAILED", { path: join27(input.vault, "raw", "articles"), message: String(error) })
5265
5538
  };
5266
5539
  }
5267
5540
  const rawWrite = await writeResolvedRaw({
@@ -5489,8 +5762,8 @@ ${body}`;
5489
5762
  }
5490
5763
 
5491
5764
  // src/commands/tag-reconcile.ts
5492
- import { readFile as readFile13 } from "fs/promises";
5493
- import { join as join27, posix } from "path";
5765
+ import { readFile as readFile14 } from "fs/promises";
5766
+ import { join as join28, posix } from "path";
5494
5767
  var TYPED_TARGET_RE = /^(entities|concepts|comparisons|queries|meta)\/[a-z0-9][a-z0-9./_-]*\.md$/;
5495
5768
  function errorExitCode2(error) {
5496
5769
  switch (error) {
@@ -5533,7 +5806,7 @@ function asTagArray(frontmatter, path) {
5533
5806
  async function readTagsFromFile(path) {
5534
5807
  let text;
5535
5808
  try {
5536
- text = await readFile13(path, "utf8");
5809
+ text = await readFile14(path, "utf8");
5537
5810
  } catch (error) {
5538
5811
  if (error.code === "ENOENT") {
5539
5812
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path }) };
@@ -5554,7 +5827,7 @@ async function resolveRequestedTags(input, page) {
5554
5827
  result: err("INVALID_FRONTMATTER", { message: "explicit tags must be an array of strings" })
5555
5828
  };
5556
5829
  }
5557
- const source = input.from ?? (explicit.length === 0 ? join27(input.vault, page) : void 0);
5830
+ const source = input.from ?? (explicit.length === 0 ? join28(input.vault, page) : void 0);
5558
5831
  if (!source) return { exitCode: ExitCode.OK, result: ok({ tags: [...new Set(explicit)].sort() }) };
5559
5832
  const sourced = await readTagsFromFile(source);
5560
5833
  if (!sourced.result.ok) return { exitCode: sourced.exitCode, result: sourced.result };
@@ -5565,7 +5838,7 @@ async function resolveRequestedTags(input, page) {
5565
5838
  }
5566
5839
  async function readSchema(schemaPath) {
5567
5840
  try {
5568
- return { exitCode: ExitCode.OK, result: ok(await readFile13(schemaPath, "utf8")) };
5841
+ return { exitCode: ExitCode.OK, result: ok(await readFile14(schemaPath, "utf8")) };
5569
5842
  } catch (error) {
5570
5843
  if (error.code === "ENOENT") {
5571
5844
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: schemaPath }) };
@@ -5593,7 +5866,7 @@ function previewResult(page, tags, reconciled, dryRun, filesChanged) {
5593
5866
  };
5594
5867
  }
5595
5868
  async function reconcileTagsWhileLocked(input, page, tags, comment) {
5596
- const schemaPath = join27(input.vault, "SCHEMA.md");
5869
+ const schemaPath = join28(input.vault, "SCHEMA.md");
5597
5870
  const current = await readSchema(schemaPath);
5598
5871
  if (!current.result.ok) return { exitCode: current.exitCode, result: current.result };
5599
5872
  const next = reconcileTaxonomyDocument(current.result.data, { tags, comment });
@@ -5604,7 +5877,7 @@ async function reconcileTagsWhileLocked(input, page, tags, comment) {
5604
5877
  }
5605
5878
  let verifiedText;
5606
5879
  try {
5607
- verifiedText = await readFile13(schemaPath, "utf8");
5880
+ verifiedText = await readFile14(schemaPath, "utf8");
5608
5881
  } catch (error) {
5609
5882
  return {
5610
5883
  exitCode: ExitCode.WRITE_FAILED,
@@ -5630,7 +5903,7 @@ async function runTagReconcile(input) {
5630
5903
  const comment = taxonomyCommentForPage(page.data, date, input.reason);
5631
5904
  if (!comment.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: comment };
5632
5905
  if (!input.write) {
5633
- const schema = await readSchema(join27(input.vault, "SCHEMA.md"));
5906
+ const schema = await readSchema(join28(input.vault, "SCHEMA.md"));
5634
5907
  if (!schema.result.ok) return { exitCode: schema.exitCode, result: schema.result };
5635
5908
  const preview = reconcileTaxonomyDocument(schema.result.data, { tags, comment: comment.data });
5636
5909
  return previewResult(page.data, tags, preview, true, []);
@@ -5683,7 +5956,7 @@ async function runTagReconcile(input) {
5683
5956
 
5684
5957
  // src/commands/sync.ts
5685
5958
  import { existsSync as existsSync8 } from "fs";
5686
- import { join as join28 } from "path";
5959
+ import { join as join29 } from "path";
5687
5960
  import { execFileSync as execFileSync3 } from "child_process";
5688
5961
 
5689
5962
  // src/utils/vault-git-pathspec.ts
@@ -5737,7 +6010,7 @@ function refHasPath(vault, ref, path) {
5737
6010
  function runSyncStatus(input) {
5738
6011
  const vault = input.vault;
5739
6012
  const includeStashes = input.includeStashes ?? false;
5740
- if (!existsSync8(join28(vault, ".git"))) {
6013
+ if (!existsSync8(join29(vault, ".git"))) {
5741
6014
  return {
5742
6015
  exitCode: ExitCode.VAULT_PATH_INVALID,
5743
6016
  result: ok({
@@ -5844,7 +6117,7 @@ function runSyncStatus(input) {
5844
6117
  }
5845
6118
  async function runSyncPush(input) {
5846
6119
  const vault = input.vault;
5847
- if (!existsSync8(join28(vault, ".git"))) {
6120
+ if (!existsSync8(join29(vault, ".git"))) {
5848
6121
  return {
5849
6122
  exitCode: ExitCode.VAULT_PATH_INVALID,
5850
6123
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -6004,7 +6277,7 @@ function enableGitLongPathsOnWindows(vault) {
6004
6277
  }
6005
6278
  async function runSyncPull(input) {
6006
6279
  const vault = input.vault;
6007
- if (!existsSync8(join28(vault, ".git"))) {
6280
+ if (!existsSync8(join29(vault, ".git"))) {
6008
6281
  return {
6009
6282
  exitCode: ExitCode.VAULT_PATH_INVALID,
6010
6283
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -6232,7 +6505,7 @@ function runSyncJournalClearStale(input) {
6232
6505
 
6233
6506
  // src/commands/backup.ts
6234
6507
  import { statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
6235
- import { join as join29, relative as relative2, dirname as dirname6 } from "path";
6508
+ import { join as join30, relative as relative2, dirname as dirname6 } from "path";
6236
6509
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
6237
6510
 
6238
6511
  // src/utils/s3-client.ts
@@ -6256,7 +6529,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
6256
6529
  function* walkMarkdown(dir, base) {
6257
6530
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
6258
6531
  if (SKIP_DIRS.has(entry.name)) continue;
6259
- const full = join29(dir, entry.name);
6532
+ const full = join30(dir, entry.name);
6260
6533
  if (entry.isDirectory()) {
6261
6534
  yield* walkMarkdown(full, base);
6262
6535
  } else if (entry.name.endsWith(".md")) {
@@ -6279,7 +6552,7 @@ async function runBackupSync(input) {
6279
6552
  let failed = 0;
6280
6553
  const files = [...walkMarkdown(input.vault, input.vault)];
6281
6554
  for (const relPath of files) {
6282
- const absPath = join29(input.vault, relPath);
6555
+ const absPath = join30(input.vault, relPath);
6283
6556
  const localStat = statSync2(absPath);
6284
6557
  let needsUpload = true;
6285
6558
  try {
@@ -6355,7 +6628,7 @@ async function runBackupRestore(input) {
6355
6628
  const objects = list.Contents ?? [];
6356
6629
  for (const obj of objects) {
6357
6630
  if (!obj.Key) continue;
6358
- const localPath = join29(target, obj.Key);
6631
+ const localPath = join30(target, obj.Key);
6359
6632
  try {
6360
6633
  const localStat = statSync2(localPath);
6361
6634
  if (obj.LastModified && localStat.mtime > obj.LastModified) {
@@ -6402,8 +6675,8 @@ async function runBackupRestore(input) {
6402
6675
 
6403
6676
  // src/commands/status.ts
6404
6677
  import { existsSync as existsSync9, statSync as statSync3 } from "fs";
6405
- import { readFile as readFile14 } from "fs/promises";
6406
- import { join as join30 } from "path";
6678
+ import { readFile as readFile15 } from "fs/promises";
6679
+ import { join as join31 } from "path";
6407
6680
  async function runStatus(input) {
6408
6681
  if (!existsSync9(input.vault)) {
6409
6682
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
@@ -6430,7 +6703,7 @@ async function runStatus(input) {
6430
6703
  const compound = scan.data.compound.length;
6431
6704
  let schemaVersion = "v1";
6432
6705
  try {
6433
- const schemaContent = await readFile14(join30(input.vault, "SCHEMA.md"), "utf8");
6706
+ const schemaContent = await readFile15(join31(input.vault, "SCHEMA.md"), "utf8");
6434
6707
  const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
6435
6708
  if (versionMatch) schemaVersion = versionMatch[1];
6436
6709
  } catch {
@@ -6491,7 +6764,7 @@ async function runStatus(input) {
6491
6764
 
6492
6765
  // src/commands/seed.ts
6493
6766
  import { mkdir as mkdir10, writeFile as writeFile9, stat as stat4 } from "fs/promises";
6494
- import { join as join31 } from "path";
6767
+ import { join as join32 } from "path";
6495
6768
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6496
6769
  var EXAMPLE_PAGES = {
6497
6770
  "entities/example-project.md": `---
@@ -6560,29 +6833,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
6560
6833
  `;
6561
6834
  async function runSeed(input) {
6562
6835
  try {
6563
- await stat4(join31(input.vault, "SCHEMA.md"));
6836
+ await stat4(join32(input.vault, "SCHEMA.md"));
6564
6837
  } catch {
6565
6838
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
6566
6839
  }
6567
6840
  const created = [];
6568
6841
  const skipped = [];
6569
6842
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
6570
- const absPath = join31(input.vault, relPath);
6843
+ const absPath = join32(input.vault, relPath);
6571
6844
  try {
6572
6845
  await stat4(absPath);
6573
6846
  skipped.push(relPath);
6574
6847
  } catch {
6575
- await mkdir10(join31(absPath, ".."), { recursive: true });
6848
+ await mkdir10(join32(absPath, ".."), { recursive: true });
6576
6849
  await writeFile9(absPath, content, "utf8");
6577
6850
  created.push(relPath);
6578
6851
  }
6579
6852
  }
6580
- const rawPath = join31(input.vault, "raw", "articles", "example-source.md");
6853
+ const rawPath = join32(input.vault, "raw", "articles", "example-source.md");
6581
6854
  try {
6582
6855
  await stat4(rawPath);
6583
6856
  skipped.push("raw/articles/example-source.md");
6584
6857
  } catch {
6585
- await mkdir10(join31(rawPath, ".."), { recursive: true });
6858
+ await mkdir10(join32(rawPath, ".."), { recursive: true });
6586
6859
  await writeFile9(rawPath, EXAMPLE_RAW, "utf8");
6587
6860
  created.push("raw/articles/example-source.md");
6588
6861
  }
@@ -6605,9 +6878,9 @@ async function runSeed(input) {
6605
6878
  }
6606
6879
 
6607
6880
  // src/commands/canvas.ts
6608
- import { readFile as readFile15, writeFile as writeFile10 } from "fs/promises";
6881
+ import { readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
6609
6882
  import { existsSync as existsSync10 } from "fs";
6610
- import { join as join32 } from "path";
6883
+ import { join as join33 } from "path";
6611
6884
  var NODE_WIDTH = 240;
6612
6885
  var NODE_HEIGHT = 60;
6613
6886
  var COLUMN_SPACING = 400;
@@ -6685,7 +6958,7 @@ function buildCanvasEdges(adjacency) {
6685
6958
  return edges;
6686
6959
  }
6687
6960
  async function runCanvasGenerate(input) {
6688
- const graphPath = input.graphPath ?? join32(input.vault, ".skillwiki", "graph.json");
6961
+ const graphPath = input.graphPath ?? join33(input.vault, ".skillwiki", "graph.json");
6689
6962
  if (!existsSync10(graphPath)) {
6690
6963
  return {
6691
6964
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -6697,7 +6970,7 @@ async function runCanvasGenerate(input) {
6697
6970
  }
6698
6971
  let raw;
6699
6972
  try {
6700
- raw = await readFile15(graphPath, "utf8");
6973
+ raw = await readFile16(graphPath, "utf8");
6701
6974
  } catch (e) {
6702
6975
  return {
6703
6976
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -6723,7 +6996,7 @@ async function runCanvasGenerate(input) {
6723
6996
  const nodes = buildCanvasNodes(paths);
6724
6997
  const edges = buildCanvasEdges(graph.adjacency);
6725
6998
  const canvas = { nodes, edges };
6726
- const outPath = join32(input.vault, "vault-graph.canvas");
6999
+ const outPath = join33(input.vault, "vault-graph.canvas");
6727
7000
  try {
6728
7001
  await writeFile10(outPath, JSON.stringify(canvas, null, 2));
6729
7002
  } catch (e) {
@@ -6748,12 +7021,12 @@ written: ${outPath}`
6748
7021
  import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
6749
7022
  import { execSync as nodeExecSync } from "child_process";
6750
7023
  import { hostname as nodeHostname, platform as nodePlatform } from "os";
6751
- import { join as join33 } from "path";
7024
+ import { join as join34 } from "path";
6752
7025
  var SSH_TIMEOUT_MS = 15e3;
6753
7026
  var TIMER_UNIT = "agent-memory-trends.timer";
6754
7027
  var SERVICE_UNIT = "agent-memory-trends.service";
6755
7028
  var SYSTEMD_SERVICE_FAILED_CLASS = "SYSTEMD_SERVICE_FAILED";
6756
- function defaultDeps2() {
7029
+ function defaultDeps3() {
6757
7030
  return {
6758
7031
  platform: () => nodePlatform(),
6759
7032
  execSync: nodeExecSync
@@ -6973,12 +7246,12 @@ function rowHealthy(reachable, timer, runUnhealthy, timerBad, platform2) {
6973
7246
  return true;
6974
7247
  }
6975
7248
  async function runFleetHealth(input) {
6976
- const deps = input.deps ?? defaultDeps2();
7249
+ const deps = input.deps ?? defaultDeps3();
6977
7250
  const env = input.env ?? process.env;
6978
7251
  const home = input.home ?? env.HOME ?? "";
6979
7252
  const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
6980
7253
  const vault = input.vault ?? env.WIKI_PATH;
6981
- const file = input.file ?? (vault ? join33(vault, FLEET_REL_PATH) : void 0);
7254
+ const file = input.file ?? (vault ? join34(vault, FLEET_REL_PATH) : void 0);
6982
7255
  if (!file) {
6983
7256
  return {
6984
7257
  exitCode: ExitCode.NO_VAULT_CONFIGURED,
@@ -7063,14 +7336,14 @@ async function runFleetHealth(input) {
7063
7336
 
7064
7337
  // src/utils/auto-commit.ts
7065
7338
  import { existsSync as existsSync12 } from "fs";
7066
- import { join as join34 } from "path";
7339
+ import { join as join35 } from "path";
7067
7340
  async function postCommit(vault, exitCode) {
7068
7341
  if (exitCode !== 0) return;
7069
7342
  const home = process.env.HOME ?? "";
7070
7343
  const dotenv = await parseDotenvFile(configPath(home));
7071
7344
  const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
7072
7345
  if (autoCommit === "false") return;
7073
- if (!existsSync12(join34(vault, ".git"))) return;
7346
+ if (!existsSync12(join35(vault, ".git"))) return;
7074
7347
  const lastOps = readLastOp(vault);
7075
7348
  if (lastOps.length === 0) return;
7076
7349
  const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
@@ -7224,7 +7497,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
7224
7497
  if (dirty) {
7225
7498
  return emit(dirty, void 0, { postCommit: false });
7226
7499
  }
7227
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-7TWSEZJZ.js");
7500
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-4SWWFT75.js");
7228
7501
  const run = await runManagedWriteTransaction2({
7229
7502
  vault,
7230
7503
  command,
@@ -7249,7 +7522,7 @@ program.command("validate <file>").description("validate vault page frontmatter
7249
7522
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
7250
7523
  });
7251
7524
  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) => {
7252
- const out = opts.out ?? join35(vault, ".skillwiki", "graph.json");
7525
+ const out = opts.out ?? join36(vault, ".skillwiki", "graph.json");
7253
7526
  return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
7254
7527
  });
7255
7528
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -7519,6 +7792,33 @@ projectionsCmd.command("materialize [vault]").description("preview or write root
7519
7792
  );
7520
7793
  } else emit(await runProjectionsMaterialize({ vault: v.vault, write: false }), v.vault);
7521
7794
  });
7795
+ projectionsCmd.command("repair-legacy [vault]").description("repair one supported legacy root-index marker pair and log event").requiredOption("--event-operation-id <id>", "exact legacy event operation ID").option("--write", "write the bounded repairs", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
7796
+ const v = await resolveVaultArg(vault, opts.wiki);
7797
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
7798
+ else if (opts.write) {
7799
+ return emitManagedVaultWrite(
7800
+ v.vault,
7801
+ "projections repair-legacy",
7802
+ (receipt) => runProjectionsRepairLegacy({
7803
+ vault: v.vault,
7804
+ eventOperationId: opts.eventOperationId,
7805
+ write: true,
7806
+ hostId: receipt.host_id
7807
+ }),
7808
+ { convergenceVault: opts.convergeVault, postCommit: false }
7809
+ );
7810
+ } else {
7811
+ return emit(
7812
+ await runProjectionsRepairLegacy({
7813
+ vault: v.vault,
7814
+ eventOperationId: opts.eventOperationId,
7815
+ write: false
7816
+ }),
7817
+ v.vault,
7818
+ { postCommit: false }
7819
+ );
7820
+ }
7821
+ });
7522
7822
  program.command("lint [vault]").description("run all vault health checks").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).option("--fix", "auto-fix supported lint violations").option("--only <bucket>", "run only the specified lint bucket").option("--summary", "emit bounded bucket counts instead of full item arrays", false).option("--examples <n>", "example count per bucket in summary mode", (s) => parseInt(s, 10), 3).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
7523
7823
  const v = await resolveVaultArg(vault, opts.wiki);
7524
7824
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });