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