skillwiki 0.10.11 → 0.10.12

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-YYWGP7DN.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,7 +104,7 @@ 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,
@@ -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, [
@@ -1990,11 +2250,11 @@ function writeWorkJournal(vault, opId, fields) {
1990
2250
  var PHASE_ORDER = ["validate", "evidence", "log", "projection", "commit", "done"];
1991
2251
  function resolveWorkDir(vault, workItem) {
1992
2252
  const rel = normalizeWorkItemRel(workItem);
1993
- const abs = join15(vault, rel);
2253
+ const abs = join16(vault, rel);
1994
2254
  if (!existsSync4(abs)) {
1995
2255
  return err("FILE_NOT_FOUND", { path: rel });
1996
2256
  }
1997
- if (!existsSync4(join15(abs, "spec.md"))) {
2257
+ if (!existsSync4(join16(abs, "spec.md"))) {
1998
2258
  return err("PREFLIGHT_FAILED", { reason: "missing-spec", path: rel });
1999
2259
  }
2000
2260
  return ok(abs);
@@ -2042,7 +2302,7 @@ async function setStatusCompleted(filePath) {
2042
2302
  return patchFrontmatterStatus(filePath);
2043
2303
  }
2044
2304
  async function writeEvidence(workDir, opId, phases) {
2045
- const path = join15(workDir, "evidence.md");
2305
+ const path = join16(workDir, "evidence.md");
2046
2306
  const body = [
2047
2307
  "---",
2048
2308
  "title: work-complete evidence",
@@ -2060,7 +2320,7 @@ async function writeEvidence(workDir, opId, phases) {
2060
2320
  return atomicWriteText(path, body);
2061
2321
  }
2062
2322
  async function markPlanComplete(workDir) {
2063
- const planPath = join15(workDir, "plan.md");
2323
+ const planPath = join16(workDir, "plan.md");
2064
2324
  if (!existsSync4(planPath)) return ok(null);
2065
2325
  return patchFrontmatterStatus(planPath, (body) => body.replace(/- \[ \]/g, "- [x]"));
2066
2326
  }
@@ -2147,7 +2407,7 @@ async function runWorkComplete(input) {
2147
2407
  advance("evidence");
2148
2408
  }
2149
2409
  if (phaseIndex(phase) <= phaseIndex("evidence")) {
2150
- const statusWrite = await setStatusCompleted(join15(workDir, "spec.md"));
2410
+ const statusWrite = await setStatusCompleted(join16(workDir, "spec.md"));
2151
2411
  if (!statusWrite.ok) return writeFailure("evidence-spec", statusWrite);
2152
2412
  const planWrite = await markPlanComplete(workDir);
2153
2413
  if (!planWrite.ok) return writeFailure("evidence-plan", planWrite);
@@ -2181,7 +2441,7 @@ async function runWorkComplete(input) {
2181
2441
  advance("projection");
2182
2442
  }
2183
2443
  if (phaseIndex(phase) <= phaseIndex("projection")) {
2184
- if (!existsSync4(join15(workDir, "evidence.md"))) {
2444
+ if (!existsSync4(join16(workDir, "evidence.md"))) {
2185
2445
  const evidenceWrite = await writeEvidence(workDir, opId, completedPhases);
2186
2446
  if (!evidenceWrite.ok) return writeFailure("projection-evidence", evidenceWrite);
2187
2447
  }
@@ -2206,7 +2466,7 @@ async function runWorkComplete(input) {
2206
2466
  }
2207
2467
  let committed = false;
2208
2468
  if (phaseIndex(phase) <= phaseIndex("commit")) {
2209
- if (!input.noCommit && existsSync4(join15(input.vault, ".git"))) {
2469
+ if (!input.noCommit && existsSync4(join16(input.vault, ".git"))) {
2210
2470
  try {
2211
2471
  appendLastOp(input.vault, {
2212
2472
  operation: "work-complete",
@@ -2267,7 +2527,7 @@ async function runWorkComplete(input) {
2267
2527
 
2268
2528
  // src/commands/health.ts
2269
2529
  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";
2530
+ import { dirname as dirname4, join as join17, resolve as resolve2 } from "path";
2271
2531
  import { platform } from "os";
2272
2532
  function statusFromCounts(counts) {
2273
2533
  if ((counts.error ?? 0) > 0) return "error";
@@ -2400,11 +2660,11 @@ function runVaultSyncHealth(home, syncMode) {
2400
2660
  };
2401
2661
  }
2402
2662
  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");
2663
+ const shareDir = isMac ? join17(home, "Library", "Application Support", "vault-sync", "bin") : join17(home, ".local", "share", "vault-sync", "bin");
2664
+ const logDir = isMac ? join17(home, "Library", "Logs") : join17(home, ".local", "state", "vault-sync", "log");
2665
+ const filterPath = join17(home, ".config", "rclone", "wiki-push-filters.txt");
2406
2666
  const checks = [];
2407
- const pushScript = join16(shareDir, "wiki-push.sh");
2667
+ const pushScript = join17(shareDir, "wiki-push.sh");
2408
2668
  if (syncMode === "optional" && !existsSync5(pushScript)) {
2409
2669
  return {
2410
2670
  status: "pass",
@@ -2420,20 +2680,20 @@ function runVaultSyncHealth(home, syncMode) {
2420
2680
  }
2421
2681
  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
2682
  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");
2683
+ const pushPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
2684
+ const fetchPlist = join17(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
2425
2685
  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
2686
  checks.push({ id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "macOS host \u2014 check skipped" });
2427
2687
  } 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");
2688
+ const pushTimer = join17(home, ".config", "systemd", "user", "wiki-push.timer");
2689
+ const fetchTimer = join17(home, ".config", "systemd", "user", "wiki-fetch.timer");
2690
+ const fuseTimer = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
2691
+ const fuseService = join17(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
2432
2692
  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
2693
  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
2694
  }
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/));
2695
+ checks.push(classifyLog(join17(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
2696
+ checks.push(classifyLog(join17(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
2437
2697
  if (!existsSync5(filterPath)) {
2438
2698
  checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "error", detail: `Filter missing: ${filterPath}` });
2439
2699
  } else {
@@ -2756,12 +3016,12 @@ async function runHealth(input) {
2756
3016
  }
2757
3017
 
2758
3018
  // 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";
3019
+ import { rename as rename2, mkdir as mkdir6, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
3020
+ import { join as join19, dirname as dirname5 } from "path";
2761
3021
 
2762
3022
  // 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";
3023
+ import { mkdir as mkdir5, writeFile as writeFile4, readdir as readdir4, readFile as readFile6 } from "fs/promises";
3024
+ import { join as join18 } from "path";
2765
3025
  var DELETE_INTENT_SCHEMA = "vault-delete-intent/v1";
2766
3026
  var DELETE_INTENT_DIR = "meta/delete-intents";
2767
3027
  function normalizeVaultRelPath(path) {
@@ -2792,10 +3052,10 @@ function buildDeleteIntent(input) {
2792
3052
  };
2793
3053
  }
2794
3054
  async function writeDeleteIntent(vault, intent) {
2795
- const dir = join17(vault, DELETE_INTENT_DIR);
3055
+ const dir = join18(vault, DELETE_INTENT_DIR);
2796
3056
  await mkdir5(dir, { recursive: true });
2797
3057
  const rel = `${DELETE_INTENT_DIR}/${pathToIntentFilename(intent.path)}`;
2798
- await writeFile4(join17(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
3058
+ await writeFile4(join18(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
2799
3059
  return rel;
2800
3060
  }
2801
3061
 
@@ -2833,7 +3093,7 @@ async function runArchive(input) {
2833
3093
  if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
2834
3094
  if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
2835
3095
  const slug = relPath.replace(/\.md$/, "").split("/").pop();
2836
- const archivePath = join18("_archive", relPath).replace(/\\/g, "/");
3096
+ const archivePath = join19("_archive", relPath).replace(/\\/g, "/");
2837
3097
  const remoteRoot = normalizeRemoteRoot(input.remote);
2838
3098
  const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
2839
3099
  let cascade;
@@ -2859,7 +3119,7 @@ async function runArchive(input) {
2859
3119
  const indexRefs = [];
2860
3120
  if (!isRaw) {
2861
3121
  try {
2862
- const idx = await readFile6(join18(input.vault, "index.md"), "utf8");
3122
+ const idx = await readFile7(join19(input.vault, "index.md"), "utf8");
2863
3123
  idx.split("\n").forEach((line, i) => {
2864
3124
  if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
2865
3125
  });
@@ -2886,8 +3146,8 @@ async function runArchive(input) {
2886
3146
  }
2887
3147
  if (input.cascade && input.apply && cascade) {
2888
3148
  for (const ref of cascade.source_array_refs) {
2889
- const absPath = join18(input.vault, ref.page);
2890
- const text = await readFile6(absPath, "utf8");
3149
+ const absPath = join19(input.vault, ref.page);
3150
+ const text = await readFile7(absPath, "utf8");
2891
3151
  const split = splitFrontmatter(text);
2892
3152
  if (!split.ok) continue;
2893
3153
  const before = split.data.rawFrontmatter;
@@ -2904,12 +3164,12 @@ ${fmRewritten}
2904
3164
  }
2905
3165
  }
2906
3166
  }
2907
- await mkdir6(dirname5(join18(input.vault, archivePath)), { recursive: true });
2908
- await rename2(join18(input.vault, relPath), join18(input.vault, archivePath));
3167
+ await mkdir6(dirname5(join19(input.vault, archivePath)), { recursive: true });
3168
+ await rename2(join19(input.vault, relPath), join19(input.vault, archivePath));
2909
3169
  let indexUpdated = false;
2910
3170
  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(() => "");
3171
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
3172
+ const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
2913
3173
  const fullTarget = relPath.replace(/\.md$/, "");
2914
3174
  const bare = fullTarget.split("/").pop() ?? fullTarget;
2915
3175
  const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
@@ -2960,7 +3220,7 @@ ${fmRewritten}
2960
3220
 
2961
3221
  // src/commands/remove.ts
2962
3222
  import { unlink as unlink2, access } from "fs/promises";
2963
- import { join as join19 } from "path";
3223
+ import { join as join20 } from "path";
2964
3224
  async function pathExists(abs) {
2965
3225
  try {
2966
3226
  await access(abs);
@@ -2986,7 +3246,7 @@ async function runRemove(input) {
2986
3246
  if (!relPath) {
2987
3247
  try {
2988
3248
  const candidate = normalizeVaultRelPath(input.page);
2989
- if (await pathExists(join19(input.vault, candidate))) {
3249
+ if (await pathExists(join20(input.vault, candidate))) {
2990
3250
  relPath = candidate;
2991
3251
  }
2992
3252
  } catch {
@@ -3015,12 +3275,12 @@ async function runRemove(input) {
3015
3275
  reason: input.reason
3016
3276
  });
3017
3277
  const tombstonePath = await writeDeleteIntent(input.vault, intent);
3018
- await unlink2(join19(input.vault, relPath));
3278
+ await unlink2(join20(input.vault, relPath));
3019
3279
  if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
3020
- const { readFile: readFile16 } = await import("fs/promises");
3280
+ const { readFile: readFile17 } = await import("fs/promises");
3021
3281
  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(() => "");
3282
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-MHXG7QO2.js");
3283
+ const before = await readFile17(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
3024
3284
  const fullTarget = relPath.replace(/\.md$/, "");
3025
3285
  const bare = fullTarget.split("/").pop() ?? fullTarget;
3026
3286
  const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
@@ -3368,7 +3628,7 @@ ${migratedBody}${newFooter}`;
3368
3628
 
3369
3629
  // src/commands/update.ts
3370
3630
  import { execSync } from "child_process";
3371
- import { join as join20 } from "path";
3631
+ import { join as join21 } from "path";
3372
3632
  function parseCoreSemver(version) {
3373
3633
  const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
3374
3634
  if (!m) return null;
@@ -3399,7 +3659,7 @@ function resolveGlobalSkillsRoot() {
3399
3659
  encoding: "utf8",
3400
3660
  timeout: 5e3
3401
3661
  }).trim();
3402
- return join20(globalRoot, "skillwiki", "skills");
3662
+ return join21(globalRoot, "skillwiki", "skills");
3403
3663
  } catch {
3404
3664
  return null;
3405
3665
  }
@@ -3427,7 +3687,7 @@ async function runUpdate(input) {
3427
3687
  const pkg2 = readCliPackageJson();
3428
3688
  const currentVersion = pkg2.version;
3429
3689
  const tag = normalizeDistTag(input.distTag);
3430
- const target = join20(input.home, ".claude", "skills");
3690
+ const target = join21(input.home, ".claude", "skills");
3431
3691
  let latest;
3432
3692
  try {
3433
3693
  latest = execSync(`npm view skillwiki@${tag} version`, {
@@ -3508,13 +3768,13 @@ async function runUpdate(input) {
3508
3768
  // src/commands/self-update.ts
3509
3769
  import { execSync as execSync2 } from "child_process";
3510
3770
  import { existsSync as existsSync6, readFileSync as readFileSync12 } from "fs";
3511
- import { join as join21 } from "path";
3771
+ import { join as join22 } from "path";
3512
3772
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
3513
3773
  async function runSelfUpdate(input) {
3514
3774
  const currentVersion = readCliPackageJson().version;
3515
3775
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
3516
3776
  const distTag = normalizeDistTag(input.distTag);
3517
- const localPkgPath = join21(sourceRoot, "packages", "cli", "package.json");
3777
+ const localPkgPath = join22(sourceRoot, "packages", "cli", "package.json");
3518
3778
  const hasLocalSource = existsSync6(localPkgPath);
3519
3779
  if (input.check) {
3520
3780
  let availableVersion = null;
@@ -3646,21 +3906,21 @@ async function runSelfUpdate(input) {
3646
3906
  }
3647
3907
 
3648
3908
  // 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";
3909
+ import { readdir as readdir5, stat as stat3, readFile as readFile9 } from "fs/promises";
3910
+ import { join as join23 } from "path";
3651
3911
  async function runTranscripts(input) {
3652
- const dir = join22(input.vault, "raw", "transcripts");
3912
+ const dir = join23(input.vault, "raw", "transcripts");
3653
3913
  let entries;
3654
3914
  try {
3655
- entries = await readdir4(dir, { withFileTypes: true });
3915
+ entries = await readdir5(dir, { withFileTypes: true });
3656
3916
  } catch {
3657
3917
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: { ok: false, error: "VAULT_PATH_INVALID", detail: `raw/transcripts/ not found: ${dir}` } };
3658
3918
  }
3659
3919
  const transcripts = [];
3660
3920
  for (const entry of entries) {
3661
3921
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
3662
- const filePath = join22(dir, entry.name);
3663
- const content = await readFile8(filePath, "utf8");
3922
+ const filePath = join23(dir, entry.name);
3923
+ const content = await readFile9(filePath, "utf8");
3664
3924
  const fm = extractFrontmatter(content);
3665
3925
  if (!fm.ok) continue;
3666
3926
  const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
@@ -3677,10 +3937,10 @@ async function runTranscripts(input) {
3677
3937
  }
3678
3938
 
3679
3939
  // 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";
3940
+ import { writeFile as writeFile7, mkdir as mkdir7, readdir as readdir6, unlink as unlink3 } from "fs/promises";
3941
+ import { join as join24 } from "path";
3682
3942
  import { existsSync as existsSync7 } from "fs";
3683
- import { readFile as readFile9 } from "fs/promises";
3943
+ import { readFile as readFile10 } from "fs/promises";
3684
3944
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
3685
3945
  var FIELD_RE = {
3686
3946
  improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
@@ -3778,17 +4038,17 @@ function extractRetroFields(date, cycleName, block) {
3778
4038
  };
3779
4039
  }
3780
4040
  async function runCompound(input) {
3781
- const logPath = join23(input.vault, "log.md");
4041
+ const logPath = join24(input.vault, "log.md");
3782
4042
  let logText;
3783
4043
  try {
3784
- logText = await readFile9(logPath, "utf8");
4044
+ logText = await readFile10(logPath, "utf8");
3785
4045
  } catch {
3786
4046
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
3787
4047
  }
3788
4048
  const entries = parseRetroEntries(logText);
3789
4049
  const promoted = [];
3790
4050
  const skipped = [];
3791
- const compoundDir = join23(input.vault, "projects", input.project, "compound");
4051
+ const compoundDir = join24(input.vault, "projects", input.project, "compound");
3792
4052
  for (const entry of entries) {
3793
4053
  const generalizeValue = entry.generalize.trim();
3794
4054
  if (!/^yes/i.test(generalizeValue)) {
@@ -3796,7 +4056,7 @@ async function runCompound(input) {
3796
4056
  continue;
3797
4057
  }
3798
4058
  const slug = slugify(entry.cycleName);
3799
- const compoundPath = join23(compoundDir, `${slug}.md`);
4059
+ const compoundPath = join24(compoundDir, `${slug}.md`);
3800
4060
  if (existsSync7(compoundPath)) {
3801
4061
  skipped.push(entry.date);
3802
4062
  continue;
@@ -3857,7 +4117,7 @@ async function runCompound(input) {
3857
4117
  };
3858
4118
  }
3859
4119
  async function runCompoundDelete(input) {
3860
- const projectDir = join23(input.vault, "projects", input.project);
4120
+ const projectDir = join24(input.vault, "projects", input.project);
3861
4121
  if (!existsSync7(projectDir)) {
3862
4122
  return {
3863
4123
  exitCode: ExitCode.PROJECT_NOT_FOUND,
@@ -3865,7 +4125,7 @@ async function runCompoundDelete(input) {
3865
4125
  };
3866
4126
  }
3867
4127
  const entryName = input.entry.replace(/\.md$/, "");
3868
- const compoundPath = join23(projectDir, "compound", `${entryName}.md`);
4128
+ const compoundPath = join24(projectDir, "compound", `${entryName}.md`);
3869
4129
  if (!existsSync7(compoundPath)) {
3870
4130
  return {
3871
4131
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -3899,7 +4159,7 @@ knowledge.md regenerated`
3899
4159
  };
3900
4160
  }
3901
4161
  async function runCompoundList(input) {
3902
- const compoundDir = join23(input.vault, "projects", input.project, "compound");
4162
+ const compoundDir = join24(input.vault, "projects", input.project, "compound");
3903
4163
  if (!existsSync7(compoundDir)) {
3904
4164
  return {
3905
4165
  exitCode: ExitCode.OK,
@@ -3914,7 +4174,7 @@ no compound directory found`
3914
4174
  }
3915
4175
  let dirents;
3916
4176
  try {
3917
- dirents = await readdir5(compoundDir, { withFileTypes: true });
4177
+ dirents = await readdir6(compoundDir, { withFileTypes: true });
3918
4178
  } catch {
3919
4179
  return {
3920
4180
  exitCode: ExitCode.OK,
@@ -3930,10 +4190,10 @@ could not read compound directory`
3930
4190
  const entries = [];
3931
4191
  for (const dirent of dirents) {
3932
4192
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
3933
- const filePath = join23(compoundDir, dirent.name);
4193
+ const filePath = join24(compoundDir, dirent.name);
3934
4194
  let text;
3935
4195
  try {
3936
- text = await readFile9(filePath, "utf8");
4196
+ text = await readFile10(filePath, "utf8");
3937
4197
  } catch {
3938
4198
  continue;
3939
4199
  }
@@ -3962,8 +4222,8 @@ no compound entries found`;
3962
4222
  }
3963
4223
 
3964
4224
  // 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";
4225
+ import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
4226
+ import { join as join25, relative, sep } from "path";
3967
4227
  var MAX_WORDS = 900;
3968
4228
  async function runSessionBrief(input) {
3969
4229
  const scan = await scanVault(input.vault);
@@ -4063,7 +4323,7 @@ async function resolveProject(input) {
4063
4323
  const envProject = input.env?.SKILLWIKI_PROJECT;
4064
4324
  if (envProject) return envProject;
4065
4325
  const cwd = input.cwd ?? process.cwd();
4066
- const projectDotenv = await readProjectSlug(join24(cwd, ".skillwiki", ".env"));
4326
+ const projectDotenv = await readProjectSlug(join25(cwd, ".skillwiki", ".env"));
4067
4327
  if (projectDotenv) return projectDotenv;
4068
4328
  const inferred = inferProjectFromPath(input.vault, cwd);
4069
4329
  if (inferred) return inferred;
@@ -4072,7 +4332,7 @@ async function resolveProject(input) {
4072
4332
  async function readProjectSlug(file) {
4073
4333
  let text;
4074
4334
  try {
4075
- text = await readFile10(file, "utf8");
4335
+ text = await readFile11(file, "utf8");
4076
4336
  } catch {
4077
4337
  return void 0;
4078
4338
  }
@@ -4149,7 +4409,7 @@ async function loadTrendDigests(typedPages) {
4149
4409
  return out;
4150
4410
  }
4151
4411
  async function loadSessionPins(vault, project) {
4152
- const text = await readIfExists(join24(vault, "meta", "session-pins.md"));
4412
+ const text = await readIfExists(join25(vault, "meta", "session-pins.md"));
4153
4413
  if (!text) return [];
4154
4414
  const fm = extractFrontmatter(text);
4155
4415
  if (!fm.ok) return [];
@@ -4266,7 +4526,7 @@ function satelliteHealthWarnings(warning) {
4266
4526
  return warning ? [warning] : [];
4267
4527
  }
4268
4528
  async function loadHealthWarnings(vault) {
4269
- const text = await readIfExists(join24(vault, ".skillwiki", "health.json"));
4529
+ const text = await readIfExists(join25(vault, ".skillwiki", "health.json"));
4270
4530
  if (!text) return [];
4271
4531
  try {
4272
4532
  const parsed = JSON.parse(text);
@@ -4279,7 +4539,7 @@ async function loadHealthWarnings(vault) {
4279
4539
  }
4280
4540
  }
4281
4541
  async function loadMemoryTopics(vault, project) {
4282
- const text = await readIfExists(join24(vault, ".skillwiki", "memory", project, "topics.json"));
4542
+ const text = await readIfExists(join25(vault, ".skillwiki", "memory", project, "topics.json"));
4283
4543
  if (!text) return [];
4284
4544
  try {
4285
4545
  const parsed = JSON.parse(text);
@@ -4304,11 +4564,11 @@ async function loadMemoryTopics(vault, project) {
4304
4564
  }
4305
4565
  }
4306
4566
  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 });
4567
+ const metaPath = join25(vault, "meta", "latest-session-brief.md");
4568
+ const cacheMdPath = join25(vault, ".skillwiki", "session-brief.md");
4569
+ const cacheJsonPath = join25(vault, ".skillwiki", "session-brief.json");
4570
+ await mkdir8(join25(vault, "meta"), { recursive: true });
4571
+ await mkdir8(join25(vault, ".skillwiki"), { recursive: true });
4312
4572
  const committed = renderCommittedBrief(input);
4313
4573
  const previousComparable = comparableBrief(await readIfExists(metaPath));
4314
4574
  const nextComparable = comparableBrief(committed);
@@ -4378,7 +4638,7 @@ function renderCommittedBrief(input) {
4378
4638
  ].filter((line) => line !== "").join("\n");
4379
4639
  }
4380
4640
  async function rebuildRootIndexProjection(vault) {
4381
- const before = await readIfExists(join24(vault, "index.md"));
4641
+ const before = await readIfExists(join25(vault, "index.md"));
4382
4642
  if (!before) return false;
4383
4643
  const projection = await renderRootIndex({ vault, currentText: before });
4384
4644
  if (!projection.ok) return false;
@@ -4388,7 +4648,7 @@ async function rebuildRootIndexProjection(vault) {
4388
4648
  }
4389
4649
  async function readIfExists(path) {
4390
4650
  try {
4391
- return await readFile10(path, "utf8");
4651
+ return await readFile11(path, "utf8");
4392
4652
  } catch {
4393
4653
  return "";
4394
4654
  }
@@ -4430,14 +4690,14 @@ function dateFromPath(path) {
4430
4690
  }
4431
4691
 
4432
4692
  // 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";
4693
+ import { readFile as readFile13, open, unlink as unlink4, mkdir as mkdir9 } from "fs/promises";
4694
+ import { join as join27 } from "path";
4435
4695
  import { createHash as createHash4 } from "crypto";
4436
4696
 
4437
4697
  // src/commands/page-publish.ts
4438
4698
  import { realpathSync } from "fs";
4439
- import { readFile as readFile11 } from "fs/promises";
4440
- import { join as join25, resolve as resolve3 } from "path";
4699
+ import { readFile as readFile12 } from "fs/promises";
4700
+ import { join as join26, resolve as resolve3 } from "path";
4441
4701
  var DEFAULT_DEPS = {
4442
4702
  afterStage: async () => void 0,
4443
4703
  preflight: (input) => runManagedWritePreflight(input)
@@ -4507,7 +4767,7 @@ function preparePagePublicationFromContent(input) {
4507
4767
  async function preparePagePublication(input) {
4508
4768
  let content;
4509
4769
  try {
4510
- content = await readFile11(input.draftPath, "utf8");
4770
+ content = await readFile12(input.draftPath, "utf8");
4511
4771
  } catch (error) {
4512
4772
  return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
4513
4773
  }
@@ -4578,10 +4838,10 @@ async function runLockedPrimaryStages(input, vault, deps) {
4578
4838
  ExitCode.VAULT_PATH_INVALID
4579
4839
  );
4580
4840
  }
4581
- const schemaPath = join25(vault, "SCHEMA.md");
4841
+ const schemaPath = join26(vault, "SCHEMA.md");
4582
4842
  let schemaText;
4583
4843
  try {
4584
- schemaText = await readFile11(schemaPath, "utf8");
4844
+ schemaText = await readFile12(schemaPath, "utf8");
4585
4845
  } catch (error) {
4586
4846
  return lockedFailure("schema", state, err("WRITE_FAILED", { message: String(error) }));
4587
4847
  }
@@ -4611,8 +4871,8 @@ async function runLockedPrimaryStages(input, vault, deps) {
4611
4871
  let visibleSchema;
4612
4872
  try {
4613
4873
  [visible, visibleSchema] = await Promise.all([
4614
- readFile11(input.targetPath, "utf8"),
4615
- readFile11(schemaPath, "utf8")
4874
+ readFile12(input.targetPath, "utf8"),
4875
+ readFile12(schemaPath, "utf8")
4616
4876
  ]);
4617
4877
  } catch (error) {
4618
4878
  return lockedFailure("verify", state, err("WRITE_FAILED", { message: String(error) }));
@@ -4697,17 +4957,17 @@ function renderPublicationLog(input, added) {
4697
4957
  }
4698
4958
  async function readPageChanged(targetPath, content) {
4699
4959
  try {
4700
- return ok(await readFile11(targetPath, "utf8") !== content);
4960
+ return ok(await readFile12(targetPath, "utf8") !== content);
4701
4961
  } catch (error) {
4702
4962
  if (error.code === "ENOENT") return ok(true);
4703
4963
  return err("WRITE_FAILED", { path: targetPath, message: String(error) });
4704
4964
  }
4705
4965
  }
4706
4966
  async function previewPreparedPagePublication(input, vault) {
4707
- const schemaPath = join25(vault, "SCHEMA.md");
4967
+ const schemaPath = join26(vault, "SCHEMA.md");
4708
4968
  let schemaText;
4709
4969
  try {
4710
- schemaText = await readFile11(schemaPath, "utf8");
4970
+ schemaText = await readFile12(schemaPath, "utf8");
4711
4971
  } catch (error) {
4712
4972
  const result = err("FILE_NOT_FOUND", { path: schemaPath, message: String(error) });
4713
4973
  return { exitCode: ExitCode.FILE_NOT_FOUND, result };
@@ -4719,10 +4979,10 @@ async function previewPreparedPagePublication(input, vault) {
4719
4979
  if (!reconciled.ok) return { exitCode: errorExitCode(reconciled.error), result: reconciled };
4720
4980
  const pageChanged = await readPageChanged(input.targetPath, input.page.content);
4721
4981
  if (!pageChanged.ok) return { exitCode: errorExitCode(pageChanged.error), result: pageChanged };
4722
- const indexPath = join25(vault, "index.md");
4982
+ const indexPath = join26(vault, "index.md");
4723
4983
  let indexText;
4724
4984
  try {
4725
- indexText = await readFile11(indexPath, "utf8");
4985
+ indexText = await readFile12(indexPath, "utf8");
4726
4986
  } catch (error) {
4727
4987
  const result = err("FILE_NOT_FOUND", { path: indexPath, message: String(error) });
4728
4988
  return { exitCode: ExitCode.FILE_NOT_FOUND, result };
@@ -4733,10 +4993,10 @@ async function previewPreparedPagePublication(input, vault) {
4733
4993
  type: input.page.type
4734
4994
  });
4735
4995
  if (!index.ok) return { exitCode: errorExitCode(index.error), result: index };
4736
- const logPath = join25(vault, "log.md");
4996
+ const logPath = join26(vault, "log.md");
4737
4997
  let logText;
4738
4998
  try {
4739
- logText = await readFile11(logPath, "utf8");
4999
+ logText = await readFile12(logPath, "utf8");
4740
5000
  } catch (error) {
4741
5001
  const result = err("FILE_NOT_FOUND", { path: logPath, message: String(error) });
4742
5002
  return { exitCode: ExitCode.FILE_NOT_FOUND, result };
@@ -4998,7 +5258,7 @@ function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance)
4998
5258
  }
4999
5259
  async function resolveRawCapture(input) {
5000
5260
  try {
5001
- const existing = await readFile12(input.path, "utf8");
5261
+ const existing = await readFile13(input.path, "utf8");
5002
5262
  const frontmatter = extractFrontmatter(existing);
5003
5263
  if (!frontmatter.ok) {
5004
5264
  return err("INGEST_VALIDATION_FAILED", {
@@ -5137,7 +5397,7 @@ async function runIngest(input) {
5137
5397
  sourceContent = fetchResult.data.body;
5138
5398
  } else {
5139
5399
  try {
5140
- sourceContent = await readFile12(input.source, "utf8");
5400
+ sourceContent = await readFile13(input.source, "utf8");
5141
5401
  } catch {
5142
5402
  return {
5143
5403
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -5162,7 +5422,7 @@ async function runIngest(input) {
5162
5422
  const rawRelPath = `raw/articles/${slug}.md`;
5163
5423
  const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
5164
5424
  const typedRelPath = `${typedDir}/${slug}.md`;
5165
- const rawAbsPath = join26(input.vault, rawRelPath);
5425
+ const rawAbsPath = join27(input.vault, rawRelPath);
5166
5426
  const identity = assessSourceIdentity({
5167
5427
  rawPath: rawRelPath,
5168
5428
  sourceUrl: sourceUrl ?? void 0,
@@ -5206,11 +5466,11 @@ async function runIngest(input) {
5206
5466
  );
5207
5467
  if (!input.dryRun) {
5208
5468
  try {
5209
- await mkdir9(join26(input.vault, typedDir), { recursive: true });
5469
+ await mkdir9(join27(input.vault, typedDir), { recursive: true });
5210
5470
  } catch (error) {
5211
5471
  return {
5212
5472
  exitCode: ExitCode.WRITE_FAILED,
5213
- result: err("WRITE_FAILED", { path: join26(input.vault, typedDir), message: String(error) })
5473
+ result: err("WRITE_FAILED", { path: join27(input.vault, typedDir), message: String(error) })
5214
5474
  };
5215
5475
  }
5216
5476
  }
@@ -5257,11 +5517,11 @@ async function runIngest(input) {
5257
5517
  };
5258
5518
  }
5259
5519
  try {
5260
- await mkdir9(join26(input.vault, "raw", "articles"), { recursive: true });
5520
+ await mkdir9(join27(input.vault, "raw", "articles"), { recursive: true });
5261
5521
  } catch (error) {
5262
5522
  return {
5263
5523
  exitCode: ExitCode.WRITE_FAILED,
5264
- result: err("WRITE_FAILED", { path: join26(input.vault, "raw", "articles"), message: String(error) })
5524
+ result: err("WRITE_FAILED", { path: join27(input.vault, "raw", "articles"), message: String(error) })
5265
5525
  };
5266
5526
  }
5267
5527
  const rawWrite = await writeResolvedRaw({
@@ -5489,8 +5749,8 @@ ${body}`;
5489
5749
  }
5490
5750
 
5491
5751
  // src/commands/tag-reconcile.ts
5492
- import { readFile as readFile13 } from "fs/promises";
5493
- import { join as join27, posix } from "path";
5752
+ import { readFile as readFile14 } from "fs/promises";
5753
+ import { join as join28, posix } from "path";
5494
5754
  var TYPED_TARGET_RE = /^(entities|concepts|comparisons|queries|meta)\/[a-z0-9][a-z0-9./_-]*\.md$/;
5495
5755
  function errorExitCode2(error) {
5496
5756
  switch (error) {
@@ -5533,7 +5793,7 @@ function asTagArray(frontmatter, path) {
5533
5793
  async function readTagsFromFile(path) {
5534
5794
  let text;
5535
5795
  try {
5536
- text = await readFile13(path, "utf8");
5796
+ text = await readFile14(path, "utf8");
5537
5797
  } catch (error) {
5538
5798
  if (error.code === "ENOENT") {
5539
5799
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path }) };
@@ -5554,7 +5814,7 @@ async function resolveRequestedTags(input, page) {
5554
5814
  result: err("INVALID_FRONTMATTER", { message: "explicit tags must be an array of strings" })
5555
5815
  };
5556
5816
  }
5557
- const source = input.from ?? (explicit.length === 0 ? join27(input.vault, page) : void 0);
5817
+ const source = input.from ?? (explicit.length === 0 ? join28(input.vault, page) : void 0);
5558
5818
  if (!source) return { exitCode: ExitCode.OK, result: ok({ tags: [...new Set(explicit)].sort() }) };
5559
5819
  const sourced = await readTagsFromFile(source);
5560
5820
  if (!sourced.result.ok) return { exitCode: sourced.exitCode, result: sourced.result };
@@ -5565,7 +5825,7 @@ async function resolveRequestedTags(input, page) {
5565
5825
  }
5566
5826
  async function readSchema(schemaPath) {
5567
5827
  try {
5568
- return { exitCode: ExitCode.OK, result: ok(await readFile13(schemaPath, "utf8")) };
5828
+ return { exitCode: ExitCode.OK, result: ok(await readFile14(schemaPath, "utf8")) };
5569
5829
  } catch (error) {
5570
5830
  if (error.code === "ENOENT") {
5571
5831
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: schemaPath }) };
@@ -5593,7 +5853,7 @@ function previewResult(page, tags, reconciled, dryRun, filesChanged) {
5593
5853
  };
5594
5854
  }
5595
5855
  async function reconcileTagsWhileLocked(input, page, tags, comment) {
5596
- const schemaPath = join27(input.vault, "SCHEMA.md");
5856
+ const schemaPath = join28(input.vault, "SCHEMA.md");
5597
5857
  const current = await readSchema(schemaPath);
5598
5858
  if (!current.result.ok) return { exitCode: current.exitCode, result: current.result };
5599
5859
  const next = reconcileTaxonomyDocument(current.result.data, { tags, comment });
@@ -5604,7 +5864,7 @@ async function reconcileTagsWhileLocked(input, page, tags, comment) {
5604
5864
  }
5605
5865
  let verifiedText;
5606
5866
  try {
5607
- verifiedText = await readFile13(schemaPath, "utf8");
5867
+ verifiedText = await readFile14(schemaPath, "utf8");
5608
5868
  } catch (error) {
5609
5869
  return {
5610
5870
  exitCode: ExitCode.WRITE_FAILED,
@@ -5630,7 +5890,7 @@ async function runTagReconcile(input) {
5630
5890
  const comment = taxonomyCommentForPage(page.data, date, input.reason);
5631
5891
  if (!comment.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: comment };
5632
5892
  if (!input.write) {
5633
- const schema = await readSchema(join27(input.vault, "SCHEMA.md"));
5893
+ const schema = await readSchema(join28(input.vault, "SCHEMA.md"));
5634
5894
  if (!schema.result.ok) return { exitCode: schema.exitCode, result: schema.result };
5635
5895
  const preview = reconcileTaxonomyDocument(schema.result.data, { tags, comment: comment.data });
5636
5896
  return previewResult(page.data, tags, preview, true, []);
@@ -5683,7 +5943,7 @@ async function runTagReconcile(input) {
5683
5943
 
5684
5944
  // src/commands/sync.ts
5685
5945
  import { existsSync as existsSync8 } from "fs";
5686
- import { join as join28 } from "path";
5946
+ import { join as join29 } from "path";
5687
5947
  import { execFileSync as execFileSync3 } from "child_process";
5688
5948
 
5689
5949
  // src/utils/vault-git-pathspec.ts
@@ -5737,7 +5997,7 @@ function refHasPath(vault, ref, path) {
5737
5997
  function runSyncStatus(input) {
5738
5998
  const vault = input.vault;
5739
5999
  const includeStashes = input.includeStashes ?? false;
5740
- if (!existsSync8(join28(vault, ".git"))) {
6000
+ if (!existsSync8(join29(vault, ".git"))) {
5741
6001
  return {
5742
6002
  exitCode: ExitCode.VAULT_PATH_INVALID,
5743
6003
  result: ok({
@@ -5844,7 +6104,7 @@ function runSyncStatus(input) {
5844
6104
  }
5845
6105
  async function runSyncPush(input) {
5846
6106
  const vault = input.vault;
5847
- if (!existsSync8(join28(vault, ".git"))) {
6107
+ if (!existsSync8(join29(vault, ".git"))) {
5848
6108
  return {
5849
6109
  exitCode: ExitCode.VAULT_PATH_INVALID,
5850
6110
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -6004,7 +6264,7 @@ function enableGitLongPathsOnWindows(vault) {
6004
6264
  }
6005
6265
  async function runSyncPull(input) {
6006
6266
  const vault = input.vault;
6007
- if (!existsSync8(join28(vault, ".git"))) {
6267
+ if (!existsSync8(join29(vault, ".git"))) {
6008
6268
  return {
6009
6269
  exitCode: ExitCode.VAULT_PATH_INVALID,
6010
6270
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -6232,7 +6492,7 @@ function runSyncJournalClearStale(input) {
6232
6492
 
6233
6493
  // src/commands/backup.ts
6234
6494
  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";
6495
+ import { join as join30, relative as relative2, dirname as dirname6 } from "path";
6236
6496
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
6237
6497
 
6238
6498
  // src/utils/s3-client.ts
@@ -6256,7 +6516,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
6256
6516
  function* walkMarkdown(dir, base) {
6257
6517
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
6258
6518
  if (SKIP_DIRS.has(entry.name)) continue;
6259
- const full = join29(dir, entry.name);
6519
+ const full = join30(dir, entry.name);
6260
6520
  if (entry.isDirectory()) {
6261
6521
  yield* walkMarkdown(full, base);
6262
6522
  } else if (entry.name.endsWith(".md")) {
@@ -6279,7 +6539,7 @@ async function runBackupSync(input) {
6279
6539
  let failed = 0;
6280
6540
  const files = [...walkMarkdown(input.vault, input.vault)];
6281
6541
  for (const relPath of files) {
6282
- const absPath = join29(input.vault, relPath);
6542
+ const absPath = join30(input.vault, relPath);
6283
6543
  const localStat = statSync2(absPath);
6284
6544
  let needsUpload = true;
6285
6545
  try {
@@ -6355,7 +6615,7 @@ async function runBackupRestore(input) {
6355
6615
  const objects = list.Contents ?? [];
6356
6616
  for (const obj of objects) {
6357
6617
  if (!obj.Key) continue;
6358
- const localPath = join29(target, obj.Key);
6618
+ const localPath = join30(target, obj.Key);
6359
6619
  try {
6360
6620
  const localStat = statSync2(localPath);
6361
6621
  if (obj.LastModified && localStat.mtime > obj.LastModified) {
@@ -6402,8 +6662,8 @@ async function runBackupRestore(input) {
6402
6662
 
6403
6663
  // src/commands/status.ts
6404
6664
  import { existsSync as existsSync9, statSync as statSync3 } from "fs";
6405
- import { readFile as readFile14 } from "fs/promises";
6406
- import { join as join30 } from "path";
6665
+ import { readFile as readFile15 } from "fs/promises";
6666
+ import { join as join31 } from "path";
6407
6667
  async function runStatus(input) {
6408
6668
  if (!existsSync9(input.vault)) {
6409
6669
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
@@ -6430,7 +6690,7 @@ async function runStatus(input) {
6430
6690
  const compound = scan.data.compound.length;
6431
6691
  let schemaVersion = "v1";
6432
6692
  try {
6433
- const schemaContent = await readFile14(join30(input.vault, "SCHEMA.md"), "utf8");
6693
+ const schemaContent = await readFile15(join31(input.vault, "SCHEMA.md"), "utf8");
6434
6694
  const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
6435
6695
  if (versionMatch) schemaVersion = versionMatch[1];
6436
6696
  } catch {
@@ -6491,7 +6751,7 @@ async function runStatus(input) {
6491
6751
 
6492
6752
  // src/commands/seed.ts
6493
6753
  import { mkdir as mkdir10, writeFile as writeFile9, stat as stat4 } from "fs/promises";
6494
- import { join as join31 } from "path";
6754
+ import { join as join32 } from "path";
6495
6755
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6496
6756
  var EXAMPLE_PAGES = {
6497
6757
  "entities/example-project.md": `---
@@ -6560,29 +6820,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
6560
6820
  `;
6561
6821
  async function runSeed(input) {
6562
6822
  try {
6563
- await stat4(join31(input.vault, "SCHEMA.md"));
6823
+ await stat4(join32(input.vault, "SCHEMA.md"));
6564
6824
  } catch {
6565
6825
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
6566
6826
  }
6567
6827
  const created = [];
6568
6828
  const skipped = [];
6569
6829
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
6570
- const absPath = join31(input.vault, relPath);
6830
+ const absPath = join32(input.vault, relPath);
6571
6831
  try {
6572
6832
  await stat4(absPath);
6573
6833
  skipped.push(relPath);
6574
6834
  } catch {
6575
- await mkdir10(join31(absPath, ".."), { recursive: true });
6835
+ await mkdir10(join32(absPath, ".."), { recursive: true });
6576
6836
  await writeFile9(absPath, content, "utf8");
6577
6837
  created.push(relPath);
6578
6838
  }
6579
6839
  }
6580
- const rawPath = join31(input.vault, "raw", "articles", "example-source.md");
6840
+ const rawPath = join32(input.vault, "raw", "articles", "example-source.md");
6581
6841
  try {
6582
6842
  await stat4(rawPath);
6583
6843
  skipped.push("raw/articles/example-source.md");
6584
6844
  } catch {
6585
- await mkdir10(join31(rawPath, ".."), { recursive: true });
6845
+ await mkdir10(join32(rawPath, ".."), { recursive: true });
6586
6846
  await writeFile9(rawPath, EXAMPLE_RAW, "utf8");
6587
6847
  created.push("raw/articles/example-source.md");
6588
6848
  }
@@ -6605,9 +6865,9 @@ async function runSeed(input) {
6605
6865
  }
6606
6866
 
6607
6867
  // src/commands/canvas.ts
6608
- import { readFile as readFile15, writeFile as writeFile10 } from "fs/promises";
6868
+ import { readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
6609
6869
  import { existsSync as existsSync10 } from "fs";
6610
- import { join as join32 } from "path";
6870
+ import { join as join33 } from "path";
6611
6871
  var NODE_WIDTH = 240;
6612
6872
  var NODE_HEIGHT = 60;
6613
6873
  var COLUMN_SPACING = 400;
@@ -6685,7 +6945,7 @@ function buildCanvasEdges(adjacency) {
6685
6945
  return edges;
6686
6946
  }
6687
6947
  async function runCanvasGenerate(input) {
6688
- const graphPath = input.graphPath ?? join32(input.vault, ".skillwiki", "graph.json");
6948
+ const graphPath = input.graphPath ?? join33(input.vault, ".skillwiki", "graph.json");
6689
6949
  if (!existsSync10(graphPath)) {
6690
6950
  return {
6691
6951
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -6697,7 +6957,7 @@ async function runCanvasGenerate(input) {
6697
6957
  }
6698
6958
  let raw;
6699
6959
  try {
6700
- raw = await readFile15(graphPath, "utf8");
6960
+ raw = await readFile16(graphPath, "utf8");
6701
6961
  } catch (e) {
6702
6962
  return {
6703
6963
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -6723,7 +6983,7 @@ async function runCanvasGenerate(input) {
6723
6983
  const nodes = buildCanvasNodes(paths);
6724
6984
  const edges = buildCanvasEdges(graph.adjacency);
6725
6985
  const canvas = { nodes, edges };
6726
- const outPath = join32(input.vault, "vault-graph.canvas");
6986
+ const outPath = join33(input.vault, "vault-graph.canvas");
6727
6987
  try {
6728
6988
  await writeFile10(outPath, JSON.stringify(canvas, null, 2));
6729
6989
  } catch (e) {
@@ -6748,12 +7008,12 @@ written: ${outPath}`
6748
7008
  import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
6749
7009
  import { execSync as nodeExecSync } from "child_process";
6750
7010
  import { hostname as nodeHostname, platform as nodePlatform } from "os";
6751
- import { join as join33 } from "path";
7011
+ import { join as join34 } from "path";
6752
7012
  var SSH_TIMEOUT_MS = 15e3;
6753
7013
  var TIMER_UNIT = "agent-memory-trends.timer";
6754
7014
  var SERVICE_UNIT = "agent-memory-trends.service";
6755
7015
  var SYSTEMD_SERVICE_FAILED_CLASS = "SYSTEMD_SERVICE_FAILED";
6756
- function defaultDeps2() {
7016
+ function defaultDeps3() {
6757
7017
  return {
6758
7018
  platform: () => nodePlatform(),
6759
7019
  execSync: nodeExecSync
@@ -6973,12 +7233,12 @@ function rowHealthy(reachable, timer, runUnhealthy, timerBad, platform2) {
6973
7233
  return true;
6974
7234
  }
6975
7235
  async function runFleetHealth(input) {
6976
- const deps = input.deps ?? defaultDeps2();
7236
+ const deps = input.deps ?? defaultDeps3();
6977
7237
  const env = input.env ?? process.env;
6978
7238
  const home = input.home ?? env.HOME ?? "";
6979
7239
  const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
6980
7240
  const vault = input.vault ?? env.WIKI_PATH;
6981
- const file = input.file ?? (vault ? join33(vault, FLEET_REL_PATH) : void 0);
7241
+ const file = input.file ?? (vault ? join34(vault, FLEET_REL_PATH) : void 0);
6982
7242
  if (!file) {
6983
7243
  return {
6984
7244
  exitCode: ExitCode.NO_VAULT_CONFIGURED,
@@ -7063,14 +7323,14 @@ async function runFleetHealth(input) {
7063
7323
 
7064
7324
  // src/utils/auto-commit.ts
7065
7325
  import { existsSync as existsSync12 } from "fs";
7066
- import { join as join34 } from "path";
7326
+ import { join as join35 } from "path";
7067
7327
  async function postCommit(vault, exitCode) {
7068
7328
  if (exitCode !== 0) return;
7069
7329
  const home = process.env.HOME ?? "";
7070
7330
  const dotenv = await parseDotenvFile(configPath(home));
7071
7331
  const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
7072
7332
  if (autoCommit === "false") return;
7073
- if (!existsSync12(join34(vault, ".git"))) return;
7333
+ if (!existsSync12(join35(vault, ".git"))) return;
7074
7334
  const lastOps = readLastOp(vault);
7075
7335
  if (lastOps.length === 0) return;
7076
7336
  const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
@@ -7249,7 +7509,7 @@ program.command("validate <file>").description("validate vault page frontmatter
7249
7509
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
7250
7510
  });
7251
7511
  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");
7512
+ const out = opts.out ?? join36(vault, ".skillwiki", "graph.json");
7253
7513
  return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
7254
7514
  });
7255
7515
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -7519,6 +7779,33 @@ projectionsCmd.command("materialize [vault]").description("preview or write root
7519
7779
  );
7520
7780
  } else emit(await runProjectionsMaterialize({ vault: v.vault, write: false }), v.vault);
7521
7781
  });
7782
+ 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) => {
7783
+ const v = await resolveVaultArg(vault, opts.wiki);
7784
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
7785
+ else if (opts.write) {
7786
+ return emitManagedVaultWrite(
7787
+ v.vault,
7788
+ "projections repair-legacy",
7789
+ (receipt) => runProjectionsRepairLegacy({
7790
+ vault: v.vault,
7791
+ eventOperationId: opts.eventOperationId,
7792
+ write: true,
7793
+ hostId: receipt.host_id
7794
+ }),
7795
+ { convergenceVault: opts.convergeVault, postCommit: false }
7796
+ );
7797
+ } else {
7798
+ return emit(
7799
+ await runProjectionsRepairLegacy({
7800
+ vault: v.vault,
7801
+ eventOperationId: opts.eventOperationId,
7802
+ write: false
7803
+ }),
7804
+ v.vault,
7805
+ { postCommit: false }
7806
+ );
7807
+ }
7808
+ });
7522
7809
  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
7810
  const v = await resolveVaultArg(vault, opts.wiki);
7524
7811
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });