skillwiki 0.10.26 → 0.10.28
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-EQPU2BPM.js → chunk-23CR4YPL.js} +6 -8
- package/dist/chunk-5XQWYC5K.js +5255 -0
- package/dist/chunk-NOXDRIHM.js +1194 -0
- package/dist/chunk-OJVIND5D.js +126 -0
- package/dist/{chunk-C2DKFJFA.js → chunk-RQARJ6HB.js} +286 -1
- package/dist/{chunk-QNTBNNEL.js → chunk-SGZPIGGX.js} +12 -14
- package/dist/{chunk-QBZYEEBD.js → chunk-UI4SOWI2.js} +7 -123
- package/dist/{chunk-VXMHHXVP.js → chunk-W7NPTDIU.js} +463 -4745
- package/dist/cli.js +107 -703
- package/dist/{index-projection-FRWTZB5Y.js → index-projection-64WW36N3.js} +3 -3
- package/dist/{managed-write-preflight-4LPVMP42.js → managed-write-preflight-2FU5F56H.js} +5 -3
- package/dist/skillwiki-mcp.js +6 -6
- package/dist/sources-7UC6PJEV.js +9 -0
- package/dist/vault-sync/scripts/lib/platform.sh +264 -0
- 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/chunk-6ZDKTNLA.js +0 -294
- package/dist/chunk-BASWDOQB.js +0 -455
- package/dist/chunk-PQG26AGJ.js +0 -941
- package/dist/sources-L2SQV63E.js +0 -10
package/dist/cli.js
CHANGED
|
@@ -6,82 +6,39 @@ import {
|
|
|
6
6
|
DEFAULT_NO_DECISION_STREAK,
|
|
7
7
|
GateError,
|
|
8
8
|
SATELLITE_STALE_MS,
|
|
9
|
-
acquireLock,
|
|
10
|
-
acquireOwnedSyncLock,
|
|
11
|
-
appendLastOp,
|
|
12
|
-
applyRawStructuralMove,
|
|
13
|
-
assessSourceIdentity,
|
|
14
|
-
buildDegradedReasons,
|
|
15
|
-
buildRemoteObjectPath,
|
|
16
|
-
clearLastOp,
|
|
17
9
|
configPath,
|
|
18
10
|
evaluateDirtyVolumeGate,
|
|
19
11
|
evaluateSatelliteRunHealth,
|
|
20
|
-
extractTaxonomy,
|
|
21
12
|
findPlugin,
|
|
22
|
-
fixPathTooLong,
|
|
23
|
-
getCliSessionId,
|
|
24
|
-
getSessionId,
|
|
25
13
|
isFailedRunStatus,
|
|
26
|
-
isValidRemoteDeleteCap,
|
|
27
14
|
memoryAuthorityTiersRank,
|
|
28
|
-
mergeTaxonomyConflict,
|
|
29
|
-
normalizeRemoteRoot,
|
|
30
|
-
planAndMaybePruneRemoteObjects,
|
|
31
|
-
planRawStructuralMove,
|
|
32
|
-
probeRemoteHealth,
|
|
33
15
|
readCliPackageJson,
|
|
34
|
-
readLastOp,
|
|
35
|
-
readLock,
|
|
36
16
|
readSatelliteLatestRunFromText,
|
|
37
|
-
reconcileTaxonomyDocument,
|
|
38
|
-
releaseLock,
|
|
39
|
-
releaseOwnedSyncLock,
|
|
40
17
|
renderIndexUpsert,
|
|
41
18
|
renderProjectIndex,
|
|
42
|
-
resolveInitTimePath,
|
|
43
|
-
resolveRuntimePath,
|
|
44
|
-
rewriteRawSourceReferences,
|
|
45
|
-
runAudit,
|
|
46
19
|
runConfigGet,
|
|
47
20
|
runConfigList,
|
|
48
21
|
runConfigPath,
|
|
49
22
|
runConfigSet,
|
|
50
|
-
runDedup,
|
|
51
23
|
runDoctor,
|
|
52
|
-
runFrontmatterFix,
|
|
53
24
|
runGraphBuild,
|
|
54
|
-
runIndexCheck,
|
|
55
|
-
runIndexLinkFormat,
|
|
56
|
-
runLinks,
|
|
57
|
-
runLint,
|
|
58
25
|
runLogAppend,
|
|
59
|
-
runLogRotate,
|
|
60
26
|
runMemoryImport,
|
|
61
27
|
runMemoryIndex,
|
|
62
28
|
runMemoryRecall,
|
|
63
29
|
runMemoryReview,
|
|
64
30
|
runMemoryTopics,
|
|
65
31
|
runObserve,
|
|
66
|
-
runOrphans,
|
|
67
|
-
runPagesize,
|
|
68
32
|
runProjectIndex,
|
|
69
33
|
runQuery,
|
|
70
34
|
runSkillwikiMcpStdio,
|
|
71
|
-
runStale,
|
|
72
|
-
runSyncLintDelta,
|
|
73
|
-
runTagAudit,
|
|
74
|
-
runTopicMapCheck,
|
|
75
35
|
runValidate,
|
|
76
36
|
runWritePreflight,
|
|
77
|
-
safeWritePage,
|
|
78
37
|
satelliteLatestRunPath,
|
|
79
38
|
scanConflictMarkerBlocksInText,
|
|
80
|
-
snapshotMaintainedPageState,
|
|
81
39
|
snapshotterHealthChecks,
|
|
82
|
-
taxonomyCommentForPage,
|
|
83
40
|
upsertIndexEntry
|
|
84
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-W7NPTDIU.js";
|
|
85
42
|
import {
|
|
86
43
|
normalizeDistTag,
|
|
87
44
|
readCache,
|
|
@@ -92,44 +49,89 @@ import {
|
|
|
92
49
|
import {
|
|
93
50
|
UNMANAGED_END,
|
|
94
51
|
UNMANAGED_START,
|
|
95
|
-
assertTargetInsideVault,
|
|
96
|
-
atomicWriteText,
|
|
97
|
-
prepareTypedPage,
|
|
98
52
|
renderRootIndex,
|
|
99
53
|
writeRootIndexProjection
|
|
100
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-OJVIND5D.js";
|
|
101
55
|
import {
|
|
56
|
+
VAULT_COMMIT_PATHSPEC,
|
|
102
57
|
runManagedWritePreflight,
|
|
103
|
-
runManagedWriteTransaction
|
|
104
|
-
|
|
58
|
+
runManagedWriteTransaction,
|
|
59
|
+
runSyncLock,
|
|
60
|
+
runSyncPeers,
|
|
61
|
+
runSyncPull,
|
|
62
|
+
runSyncPush,
|
|
63
|
+
runSyncStatus,
|
|
64
|
+
runSyncUnlock,
|
|
65
|
+
stageVaultContentChanges
|
|
66
|
+
} from "./chunk-NOXDRIHM.js";
|
|
105
67
|
import {
|
|
106
68
|
FLEET_REL_PATH,
|
|
69
|
+
acquireOwnedSyncLock,
|
|
70
|
+
appendLastOp,
|
|
71
|
+
applyRawStructuralMove,
|
|
72
|
+
assessSourceIdentity,
|
|
73
|
+
buildRemoteObjectPath,
|
|
107
74
|
canSupersedeJournal,
|
|
75
|
+
clearLastOp,
|
|
76
|
+
extractTaxonomy,
|
|
77
|
+
getCliSessionId,
|
|
108
78
|
git,
|
|
109
79
|
gitStrict,
|
|
110
80
|
hasActiveGitSequencer,
|
|
111
81
|
hasUnmergedPaths,
|
|
82
|
+
isValidRemoteDeleteCap,
|
|
112
83
|
isWorktreeClean,
|
|
113
84
|
listJournalOpIds,
|
|
114
85
|
listReviewRequiredOps,
|
|
115
86
|
loadFleetManifest,
|
|
116
87
|
loadFleetManifestAndHost,
|
|
117
88
|
markJournalSuperseded,
|
|
89
|
+
mergeTaxonomyConflict,
|
|
90
|
+
normalizeRemoteRoot,
|
|
118
91
|
parseDotenvFile,
|
|
119
92
|
parseDotenvText,
|
|
120
93
|
parseJournalEnv,
|
|
94
|
+
planAndMaybePruneRemoteObjects,
|
|
95
|
+
planRawStructuralMove,
|
|
121
96
|
profileKey,
|
|
122
97
|
readJournal,
|
|
98
|
+
readLastOp,
|
|
99
|
+
reconcileTaxonomyDocument,
|
|
100
|
+
releaseOwnedSyncLock,
|
|
123
101
|
resolveConfiguredSnapshotWorktree,
|
|
124
102
|
resolveFleetHostId,
|
|
103
|
+
resolveInitTimePath,
|
|
104
|
+
resolveRuntimePath,
|
|
105
|
+
rewriteRawSourceReferences,
|
|
106
|
+
runAudit,
|
|
107
|
+
runDedup,
|
|
125
108
|
runFleetContext,
|
|
126
109
|
runFleetValidate,
|
|
127
|
-
|
|
110
|
+
runFrontmatterFix,
|
|
111
|
+
runIndexCheck,
|
|
112
|
+
runIndexLinkFormat,
|
|
113
|
+
runLinks,
|
|
114
|
+
runLint,
|
|
115
|
+
runLogRotate,
|
|
116
|
+
runOrphans,
|
|
117
|
+
runPagesize,
|
|
118
|
+
runStale,
|
|
119
|
+
runSyncLintDelta,
|
|
120
|
+
runTagAudit,
|
|
121
|
+
runTopicMapCheck,
|
|
122
|
+
safeWritePage,
|
|
128
123
|
serializeJournalEnv,
|
|
124
|
+
snapshotMaintainedPageState,
|
|
129
125
|
snapshotterAliasForLocalHost,
|
|
130
126
|
supersedeStaleReviewRequiredJournals,
|
|
127
|
+
taxonomyCommentForPage,
|
|
131
128
|
writeDotenv
|
|
132
|
-
} from "./chunk-
|
|
129
|
+
} from "./chunk-5XQWYC5K.js";
|
|
130
|
+
import {
|
|
131
|
+
assertTargetInsideVault,
|
|
132
|
+
atomicWriteText,
|
|
133
|
+
prepareTypedPage
|
|
134
|
+
} from "./chunk-UI4SOWI2.js";
|
|
133
135
|
import {
|
|
134
136
|
applySourceDisposition,
|
|
135
137
|
decodeSourceActionApproval,
|
|
@@ -137,7 +139,7 @@ import {
|
|
|
137
139
|
inventorySources,
|
|
138
140
|
planSourceDisposition,
|
|
139
141
|
runSourcesPending
|
|
140
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-23CR4YPL.js";
|
|
141
143
|
import {
|
|
142
144
|
authorizeRawOperation,
|
|
143
145
|
buildSourceReferenceIndex,
|
|
@@ -153,15 +155,7 @@ import {
|
|
|
153
155
|
resolveExistingRegularFileInsideVault,
|
|
154
156
|
validateLogEvent,
|
|
155
157
|
writeLogEvent
|
|
156
|
-
} from "./chunk-
|
|
157
|
-
import {
|
|
158
|
-
extractFrontmatter,
|
|
159
|
-
readPage,
|
|
160
|
-
redactSensitiveContent,
|
|
161
|
-
scanSensitiveContent,
|
|
162
|
-
scanVault,
|
|
163
|
-
splitFrontmatter
|
|
164
|
-
} from "./chunk-6ZDKTNLA.js";
|
|
158
|
+
} from "./chunk-SGZPIGGX.js";
|
|
165
159
|
import {
|
|
166
160
|
ExitCode,
|
|
167
161
|
MetaSchema,
|
|
@@ -169,12 +163,18 @@ import {
|
|
|
169
163
|
TypedKnowledgeSchema,
|
|
170
164
|
detectSchema,
|
|
171
165
|
err,
|
|
166
|
+
extractFrontmatter,
|
|
172
167
|
isBlockedHost,
|
|
173
|
-
ok
|
|
174
|
-
|
|
168
|
+
ok,
|
|
169
|
+
readPage,
|
|
170
|
+
redactSensitiveContent,
|
|
171
|
+
scanSensitiveContent,
|
|
172
|
+
scanVault,
|
|
173
|
+
splitFrontmatter
|
|
174
|
+
} from "./chunk-RQARJ6HB.js";
|
|
175
175
|
|
|
176
176
|
// src/cli.ts
|
|
177
|
-
import { join as
|
|
177
|
+
import { join as join38 } from "path";
|
|
178
178
|
import { Command } from "commander";
|
|
179
179
|
|
|
180
180
|
// src/utils/output.ts
|
|
@@ -3348,7 +3348,7 @@ ${fmRewritten}
|
|
|
3348
3348
|
}
|
|
3349
3349
|
let indexUpdated = false;
|
|
3350
3350
|
if (!isRaw) {
|
|
3351
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
3351
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-64WW36N3.js");
|
|
3352
3352
|
const before = await readFile7(join18(input.vault, "index.md"), "utf8").catch(() => "");
|
|
3353
3353
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
3354
3354
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
@@ -3468,7 +3468,7 @@ async function runRemove(input) {
|
|
|
3468
3468
|
if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
|
|
3469
3469
|
const { readFile: readFile20 } = await import("fs/promises");
|
|
3470
3470
|
const { join: pathJoin } = await import("path");
|
|
3471
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
3471
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-64WW36N3.js");
|
|
3472
3472
|
const before = await readFile20(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
|
|
3473
3473
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
3474
3474
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
@@ -7793,602 +7793,6 @@ async function runProjectPagePublish(input, deps = DEFAULT_DEPS3) {
|
|
|
7793
7793
|
return publishPrepared(prepared.data, input.vault, deps, input.journalHome);
|
|
7794
7794
|
}
|
|
7795
7795
|
|
|
7796
|
-
// src/commands/sync.ts
|
|
7797
|
-
import { existsSync as existsSync9 } from "fs";
|
|
7798
|
-
import { join as join30 } from "path";
|
|
7799
|
-
import { execFileSync as execFileSync3 } from "child_process";
|
|
7800
|
-
|
|
7801
|
-
// src/utils/vault-git-pathspec.ts
|
|
7802
|
-
var VAULT_GENERATED_COMMIT_PATHS = [
|
|
7803
|
-
".skillwiki/last-op.json",
|
|
7804
|
-
".skillwiki/memory",
|
|
7805
|
-
".skillwiki/memory-topics.json"
|
|
7806
|
-
];
|
|
7807
|
-
var VAULT_GENERATED_COMMIT_EXCLUDES = [
|
|
7808
|
-
...VAULT_GENERATED_COMMIT_PATHS.map((path) => `:!${path}`)
|
|
7809
|
-
];
|
|
7810
|
-
var VAULT_COMMIT_PATHSPEC = [".", ...VAULT_GENERATED_COMMIT_EXCLUDES];
|
|
7811
|
-
function stageVaultContentChanges(vault) {
|
|
7812
|
-
gitStrict(vault, ["add", "-A", "--", "."]);
|
|
7813
|
-
for (const generatedPath of VAULT_GENERATED_COMMIT_PATHS) {
|
|
7814
|
-
try {
|
|
7815
|
-
gitStrict(vault, ["reset", "HEAD", "--", generatedPath]);
|
|
7816
|
-
} catch (_e) {
|
|
7817
|
-
}
|
|
7818
|
-
}
|
|
7819
|
-
}
|
|
7820
|
-
|
|
7821
|
-
// src/commands/sync.ts
|
|
7822
|
-
function parseDirtyPaths(porcelain) {
|
|
7823
|
-
if (!porcelain) return [];
|
|
7824
|
-
return porcelain.split("\n").map((line) => line.trimEnd()).filter((line) => line.length >= 4).map((line) => {
|
|
7825
|
-
const payload = line.length >= 3 && line[2] === " " ? line.slice(3) : line.length >= 2 && line[1] === " " ? line.slice(2) : line;
|
|
7826
|
-
const arrow = payload.lastIndexOf(" -> ");
|
|
7827
|
-
return arrow >= 0 ? payload.slice(arrow + 4) : payload;
|
|
7828
|
-
}).filter((path) => path.length > 0);
|
|
7829
|
-
}
|
|
7830
|
-
function splitNonEmptyLines(text) {
|
|
7831
|
-
if (!text) return [];
|
|
7832
|
-
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
7833
|
-
}
|
|
7834
|
-
function isTrackedNotePath(path) {
|
|
7835
|
-
if (!path.endsWith(".md")) return false;
|
|
7836
|
-
return !/^\.(skillwiki|claude|obsidian|antigravitycli|playwright-cli)\//.test(path);
|
|
7837
|
-
}
|
|
7838
|
-
function refHasPath(vault, ref, path) {
|
|
7839
|
-
try {
|
|
7840
|
-
execFileSync3("git", ["cat-file", "-e", `${ref}:${path}`], {
|
|
7841
|
-
cwd: vault,
|
|
7842
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
7843
|
-
});
|
|
7844
|
-
return true;
|
|
7845
|
-
} catch {
|
|
7846
|
-
return false;
|
|
7847
|
-
}
|
|
7848
|
-
}
|
|
7849
|
-
function runSyncStatus(input) {
|
|
7850
|
-
const vault = input.vault;
|
|
7851
|
-
const includeStashes = input.includeStashes ?? false;
|
|
7852
|
-
if (!existsSync9(join30(vault, ".git"))) {
|
|
7853
|
-
return {
|
|
7854
|
-
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7855
|
-
result: ok({
|
|
7856
|
-
is_git_repo: false,
|
|
7857
|
-
dirty: 0,
|
|
7858
|
-
ahead: 0,
|
|
7859
|
-
behind: 0,
|
|
7860
|
-
unpromoted_note_paths: 0,
|
|
7861
|
-
unpromoted_note_examples: [],
|
|
7862
|
-
last_commit: "never",
|
|
7863
|
-
status: "not_a_repo",
|
|
7864
|
-
humanHint: "not a git repository"
|
|
7865
|
-
})
|
|
7866
|
-
};
|
|
7867
|
-
}
|
|
7868
|
-
enableGitLongPathsOnWindows(vault);
|
|
7869
|
-
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
7870
|
-
const dirty = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0).length : 0;
|
|
7871
|
-
const dirtyPaths = parseDirtyPaths(porcelain);
|
|
7872
|
-
const untrackedPaths = splitNonEmptyLines(git(vault, ["ls-files", "--others", "--exclude-standard"]));
|
|
7873
|
-
const revOutput = git(vault, ["rev-list", "--left-right", "--count", "origin/HEAD...HEAD"]);
|
|
7874
|
-
let ahead = 0;
|
|
7875
|
-
let behind = 0;
|
|
7876
|
-
if (revOutput) {
|
|
7877
|
-
const parts = revOutput.split(/\s+/);
|
|
7878
|
-
behind = parseInt(parts[0], 10) || 0;
|
|
7879
|
-
ahead = parseInt(parts[1], 10) || 0;
|
|
7880
|
-
}
|
|
7881
|
-
const tsRaw = git(vault, ["log", "-1", "--format=%ct"]);
|
|
7882
|
-
let last_commit;
|
|
7883
|
-
if (tsRaw) {
|
|
7884
|
-
const ts = parseInt(tsRaw, 10);
|
|
7885
|
-
if (!isNaN(ts) && ts > 0) {
|
|
7886
|
-
last_commit = new Date(ts * 1e3).toISOString();
|
|
7887
|
-
} else {
|
|
7888
|
-
last_commit = "never";
|
|
7889
|
-
}
|
|
7890
|
-
} else {
|
|
7891
|
-
last_commit = "never";
|
|
7892
|
-
}
|
|
7893
|
-
const remoteRef = git(vault, ["rev-parse", "--verify", "origin/main"]) ? "origin/main" : git(vault, ["rev-parse", "--verify", "origin/HEAD"]) ? "origin/HEAD" : "HEAD";
|
|
7894
|
-
const unpromotedNoteAll = Array.from(/* @__PURE__ */ new Set([...dirtyPaths, ...untrackedPaths])).filter(isTrackedNotePath).filter((path) => !refHasPath(vault, remoteRef, path));
|
|
7895
|
-
const unpromotedNoteExamples = unpromotedNoteAll.slice(0, 5);
|
|
7896
|
-
const unpromotedNotePaths = unpromotedNoteAll.length;
|
|
7897
|
-
let status;
|
|
7898
|
-
if (dirty > 0) {
|
|
7899
|
-
status = "dirty";
|
|
7900
|
-
} else if (ahead > 0) {
|
|
7901
|
-
status = "ahead";
|
|
7902
|
-
} else if (behind > 0) {
|
|
7903
|
-
status = "behind";
|
|
7904
|
-
} else {
|
|
7905
|
-
status = "clean";
|
|
7906
|
-
}
|
|
7907
|
-
const hintLines = [
|
|
7908
|
-
`status: ${status}`,
|
|
7909
|
-
`dirty: ${dirty}`,
|
|
7910
|
-
`ahead: ${ahead}`,
|
|
7911
|
-
`behind: ${behind}`,
|
|
7912
|
-
`unpromoted_note_paths: ${unpromotedNotePaths}`,
|
|
7913
|
-
`last_commit: ${last_commit}`
|
|
7914
|
-
];
|
|
7915
|
-
const exitCode = status === "clean" ? ExitCode.OK : ExitCode.LINT_HAS_WARNINGS;
|
|
7916
|
-
let stashes;
|
|
7917
|
-
if (includeStashes) {
|
|
7918
|
-
stashes = enumerateStashes(vault);
|
|
7919
|
-
}
|
|
7920
|
-
const output = {
|
|
7921
|
-
is_git_repo: true,
|
|
7922
|
-
dirty,
|
|
7923
|
-
ahead,
|
|
7924
|
-
behind,
|
|
7925
|
-
unpromoted_note_paths: unpromotedNotePaths,
|
|
7926
|
-
unpromoted_note_examples: unpromotedNoteExamples,
|
|
7927
|
-
last_commit,
|
|
7928
|
-
status,
|
|
7929
|
-
humanHint: hintLines.join("\n")
|
|
7930
|
-
};
|
|
7931
|
-
if (stashes !== void 0) {
|
|
7932
|
-
output.stashes = stashes;
|
|
7933
|
-
}
|
|
7934
|
-
if (input.includeRemoteHealth) {
|
|
7935
|
-
const home = input.home ?? process.env.HOME ?? "";
|
|
7936
|
-
const remote_health = probeRemoteHealth({
|
|
7937
|
-
vaultPath: vault,
|
|
7938
|
-
home,
|
|
7939
|
-
s3Remote: input.s3Remote,
|
|
7940
|
-
snapshotterAlias: input.snapshotterAlias,
|
|
7941
|
-
checkSnapshotter: input.checkSnapshotter,
|
|
7942
|
-
exec: input.execProbe
|
|
7943
|
-
});
|
|
7944
|
-
output.remote_health = remote_health;
|
|
7945
|
-
const degraded = buildDegradedReasons(remote_health);
|
|
7946
|
-
if (degraded.length > 0) {
|
|
7947
|
-
output.degraded_reasons = degraded;
|
|
7948
|
-
hintLines.push(`degraded_reasons: ${degraded.join(", ")}`);
|
|
7949
|
-
output.humanHint = hintLines.join("\n");
|
|
7950
|
-
}
|
|
7951
|
-
}
|
|
7952
|
-
return {
|
|
7953
|
-
exitCode,
|
|
7954
|
-
result: ok(output)
|
|
7955
|
-
};
|
|
7956
|
-
}
|
|
7957
|
-
async function runSyncPush(input) {
|
|
7958
|
-
const vault = input.vault;
|
|
7959
|
-
if (!existsSync9(join30(vault, ".git"))) {
|
|
7960
|
-
return {
|
|
7961
|
-
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7962
|
-
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
7963
|
-
};
|
|
7964
|
-
}
|
|
7965
|
-
enableGitLongPathsOnWindows(vault);
|
|
7966
|
-
let pathFixes = 0;
|
|
7967
|
-
const pathFix = await fixPathTooLong({ vault });
|
|
7968
|
-
if (pathFix.result.ok && pathFix.result.data.fixed.length > 0) {
|
|
7969
|
-
pathFixes = pathFix.result.data.fixed.length;
|
|
7970
|
-
appendLastOp(vault, {
|
|
7971
|
-
operation: "lint-fix",
|
|
7972
|
-
summary: `fixed ${pathFixes} long path(s)`,
|
|
7973
|
-
files: pathFix.result.data.fixed.flatMap((f) => [f.from, f.to]),
|
|
7974
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7975
|
-
});
|
|
7976
|
-
}
|
|
7977
|
-
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
7978
|
-
const dirtyFiles = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0) : [];
|
|
7979
|
-
if (dirtyFiles.length === 0) {
|
|
7980
|
-
return {
|
|
7981
|
-
exitCode: ExitCode.OK,
|
|
7982
|
-
result: ok({
|
|
7983
|
-
files_committed: 0,
|
|
7984
|
-
commit_message: "",
|
|
7985
|
-
pushed: false,
|
|
7986
|
-
path_fixes: pathFixes,
|
|
7987
|
-
humanHint: "nothing to commit, working tree clean"
|
|
7988
|
-
})
|
|
7989
|
-
};
|
|
7990
|
-
}
|
|
7991
|
-
let delta = { full_errors: 0, base_errors: 0, new_errors: 0, resolved_errors: 0 };
|
|
7992
|
-
const preferredBase = git(vault, ["rev-parse", "--verify", "origin/main"]) ? "origin/main" : git(vault, ["rev-parse", "--verify", "origin/HEAD"]) ? "origin/HEAD" : "";
|
|
7993
|
-
if (preferredBase) {
|
|
7994
|
-
const deltaResult = await runSyncLintDelta({ vault, baseRef: preferredBase });
|
|
7995
|
-
if (!deltaResult.result.ok) {
|
|
7996
|
-
return {
|
|
7997
|
-
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
7998
|
-
result: err("LINT_DELTA_UNAVAILABLE", {
|
|
7999
|
-
message: "lint-delta evidence missing or failed \u2014 fail closed",
|
|
8000
|
-
detail: deltaResult.result
|
|
8001
|
-
})
|
|
8002
|
-
};
|
|
8003
|
-
}
|
|
8004
|
-
delta = deltaResult.result.data;
|
|
8005
|
-
if (delta.new_errors > 0) {
|
|
8006
|
-
return {
|
|
8007
|
-
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
8008
|
-
result: err("LINT_NEW_ERRORS_BLOCK_PUSH", {
|
|
8009
|
-
full_errors: delta.full_errors,
|
|
8010
|
-
base_errors: delta.base_errors,
|
|
8011
|
-
new_errors: delta.new_errors,
|
|
8012
|
-
resolved_errors: delta.resolved_errors,
|
|
8013
|
-
new_fingerprints: deltaResult.result.data.new_fingerprints
|
|
8014
|
-
})
|
|
8015
|
-
};
|
|
8016
|
-
}
|
|
8017
|
-
} else {
|
|
8018
|
-
const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
|
|
8019
|
-
if (lintResult.result.ok) {
|
|
8020
|
-
const fullErrors = lintResult.result.data.summary.errors;
|
|
8021
|
-
delta = { full_errors: fullErrors, base_errors: 0, new_errors: fullErrors, resolved_errors: 0 };
|
|
8022
|
-
if (fullErrors > 0) {
|
|
8023
|
-
const buckets = "by_severity" in lintResult.result.data ? lintResult.result.data.by_severity.error : [];
|
|
8024
|
-
return {
|
|
8025
|
-
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
8026
|
-
result: err("LINT_ERRORS_BLOCK_PUSH", {
|
|
8027
|
-
errors: fullErrors,
|
|
8028
|
-
buckets,
|
|
8029
|
-
message: "no origin base ref for delta; absolute lint errors block push"
|
|
8030
|
-
})
|
|
8031
|
-
};
|
|
8032
|
-
}
|
|
8033
|
-
} else {
|
|
8034
|
-
delta = { full_errors: 0, base_errors: 0, new_errors: 0, resolved_errors: 0 };
|
|
8035
|
-
}
|
|
8036
|
-
}
|
|
8037
|
-
try {
|
|
8038
|
-
stageVaultContentChanges(vault);
|
|
8039
|
-
} catch (e) {
|
|
8040
|
-
return {
|
|
8041
|
-
exitCode: ExitCode.SYNC_PUSH_FAILED,
|
|
8042
|
-
result: err("GIT_ADD_FAILED", { message: String(e) })
|
|
8043
|
-
};
|
|
8044
|
-
}
|
|
8045
|
-
const lastOps = readLastOp(vault);
|
|
8046
|
-
let commitMessage;
|
|
8047
|
-
if (lastOps.length > 0) {
|
|
8048
|
-
commitMessage = lastOps.map((op) => `${op.operation}: ${op.summary} (${op.files.length} files)`).join("; ");
|
|
8049
|
-
} else {
|
|
8050
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
8051
|
-
commitMessage = `sync: vault update ${timestamp}`;
|
|
8052
|
-
}
|
|
8053
|
-
try {
|
|
8054
|
-
gitStrict(vault, ["commit", "-m", commitMessage]);
|
|
8055
|
-
} catch (e) {
|
|
8056
|
-
return {
|
|
8057
|
-
exitCode: ExitCode.SYNC_PUSH_FAILED,
|
|
8058
|
-
result: err("GIT_COMMIT_FAILED", { message: String(e) })
|
|
8059
|
-
};
|
|
8060
|
-
}
|
|
8061
|
-
clearLastOp(vault);
|
|
8062
|
-
let pushed = false;
|
|
8063
|
-
try {
|
|
8064
|
-
gitStrict(vault, ["push", "origin", "HEAD"]);
|
|
8065
|
-
pushed = true;
|
|
8066
|
-
} catch (e) {
|
|
8067
|
-
return {
|
|
8068
|
-
exitCode: ExitCode.SYNC_PUSH_FAILED,
|
|
8069
|
-
result: ok({
|
|
8070
|
-
files_committed: dirtyFiles.length,
|
|
8071
|
-
commit_message: commitMessage,
|
|
8072
|
-
pushed: false,
|
|
8073
|
-
path_fixes: pathFixes,
|
|
8074
|
-
humanHint: `committed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""} but push failed: ${String(e)}`
|
|
8075
|
-
})
|
|
8076
|
-
};
|
|
8077
|
-
}
|
|
8078
|
-
const inheritedNote = delta.full_errors > 0 ? `; lint full=${delta.full_errors} base=${delta.base_errors} new=${delta.new_errors} resolved=${delta.resolved_errors} (inherited debt only)` : `; lint full=0 new=0`;
|
|
8079
|
-
return {
|
|
8080
|
-
exitCode: ExitCode.OK,
|
|
8081
|
-
result: ok({
|
|
8082
|
-
files_committed: dirtyFiles.length,
|
|
8083
|
-
commit_message: commitMessage,
|
|
8084
|
-
pushed,
|
|
8085
|
-
path_fixes: pathFixes,
|
|
8086
|
-
lint_full_errors: delta.full_errors,
|
|
8087
|
-
lint_base_errors: delta.base_errors,
|
|
8088
|
-
lint_new_errors: delta.new_errors,
|
|
8089
|
-
lint_resolved_errors: delta.resolved_errors,
|
|
8090
|
-
humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}${inheritedNote}`
|
|
8091
|
-
})
|
|
8092
|
-
};
|
|
8093
|
-
}
|
|
8094
|
-
function enumerateStashes(vault, nowMs = Date.now()) {
|
|
8095
|
-
const output = git(vault, ["log", "--format=%gd%x09%H%x09%s%x09%ct", "-g", "stash"]);
|
|
8096
|
-
if (!output) return [];
|
|
8097
|
-
const stashes = [];
|
|
8098
|
-
const lines = output.split("\n").filter((l) => l.trim().length > 0);
|
|
8099
|
-
for (const line of lines) {
|
|
8100
|
-
const parts = line.split(" ");
|
|
8101
|
-
if (parts.length < 4) continue;
|
|
8102
|
-
const ref = parts[0];
|
|
8103
|
-
const oid = parts[1];
|
|
8104
|
-
const message = parts[2];
|
|
8105
|
-
const ctStr = parts[3];
|
|
8106
|
-
const ct = parseInt(ctStr, 10);
|
|
8107
|
-
if (isNaN(ct)) continue;
|
|
8108
|
-
const age_minutes = Math.floor((nowMs - ct * 1e3) / (60 * 1e3));
|
|
8109
|
-
stashes.push({ ref, oid, message, age_minutes });
|
|
8110
|
-
}
|
|
8111
|
-
return stashes;
|
|
8112
|
-
}
|
|
8113
|
-
function enableGitLongPathsOnWindows(vault) {
|
|
8114
|
-
if (process.platform !== "win32") return;
|
|
8115
|
-
git(vault, ["config", "core.longpaths", "true"]);
|
|
8116
|
-
}
|
|
8117
|
-
async function runSyncPull(input) {
|
|
8118
|
-
const vault = input.vault;
|
|
8119
|
-
if (!existsSync9(join30(vault, ".git"))) {
|
|
8120
|
-
return {
|
|
8121
|
-
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8122
|
-
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
8123
|
-
};
|
|
8124
|
-
}
|
|
8125
|
-
enableGitLongPathsOnWindows(vault);
|
|
8126
|
-
const remoteUrl = git(vault, ["remote", "get-url", "origin"]);
|
|
8127
|
-
if (!remoteUrl) {
|
|
8128
|
-
return {
|
|
8129
|
-
exitCode: ExitCode.SYNC_PULL_FAILED,
|
|
8130
|
-
result: err("GIT_PULL_FAILED", { message: "no remote configured (origin)" })
|
|
8131
|
-
};
|
|
8132
|
-
}
|
|
8133
|
-
const helper = await runVaultSyncPullHelper({ vault, remote: "origin", branch: "main" });
|
|
8134
|
-
if (!helper.ok) {
|
|
8135
|
-
if (helper.error === "PREFLIGHT_FAILED") {
|
|
8136
|
-
return {
|
|
8137
|
-
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
8138
|
-
result: err("PREFLIGHT_FAILED", helper.detail)
|
|
8139
|
-
};
|
|
8140
|
-
}
|
|
8141
|
-
return {
|
|
8142
|
-
exitCode: ExitCode.SYNC_PULL_FAILED,
|
|
8143
|
-
result: err("GIT_PULL_FAILED", helper.detail)
|
|
8144
|
-
};
|
|
8145
|
-
}
|
|
8146
|
-
const fetched = true;
|
|
8147
|
-
const pulled = helper.data.changed;
|
|
8148
|
-
const conflicts = 0;
|
|
8149
|
-
const autoResolved = 0;
|
|
8150
|
-
let filesUpdated = 0;
|
|
8151
|
-
if (helper.data.changed) {
|
|
8152
|
-
const diffOutput = git(vault, ["diff", "--stat", `${helper.data.before_oid}..${helper.data.after_oid}`]);
|
|
8153
|
-
if (diffOutput) {
|
|
8154
|
-
const fileMatch = diffOutput.match(/(\d+) file[s]? changed/);
|
|
8155
|
-
if (fileMatch) filesUpdated = parseInt(fileMatch[1], 10);
|
|
8156
|
-
}
|
|
8157
|
-
}
|
|
8158
|
-
const pathFix = await fixPathTooLong({ vault });
|
|
8159
|
-
const pathFixCount = pathFix.result.ok ? pathFix.result.data.fixed.length : 0;
|
|
8160
|
-
let lintErrors = 0;
|
|
8161
|
-
let lintWarnings = 0;
|
|
8162
|
-
const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
|
|
8163
|
-
if (lintResult.result.ok) {
|
|
8164
|
-
lintErrors = lintResult.result.data.summary.errors;
|
|
8165
|
-
lintWarnings = lintResult.result.data.summary.warnings;
|
|
8166
|
-
}
|
|
8167
|
-
const hintParts = [];
|
|
8168
|
-
if (filesUpdated > 0) hintParts.push(`updated ${filesUpdated} file(s)`);
|
|
8169
|
-
else hintParts.push(pulled ? "pulled via vault-sync helper" : "already up to date");
|
|
8170
|
-
if (pathFixCount > 0) hintParts.push(`${pathFixCount} long path(s) fixed`);
|
|
8171
|
-
if (lintErrors > 0) hintParts.push(`${lintErrors} lint error(s)`);
|
|
8172
|
-
if (lintWarnings > 0) hintParts.push(`${lintWarnings} lint warning(s)`);
|
|
8173
|
-
const exitCode = lintErrors > 0 ? ExitCode.LINT_HAS_ERRORS : lintWarnings > 0 ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
|
|
8174
|
-
return {
|
|
8175
|
-
exitCode,
|
|
8176
|
-
result: ok({
|
|
8177
|
-
fetched,
|
|
8178
|
-
pulled,
|
|
8179
|
-
files_updated: filesUpdated,
|
|
8180
|
-
conflicts,
|
|
8181
|
-
auto_resolved: autoResolved,
|
|
8182
|
-
lint_errors: lintErrors,
|
|
8183
|
-
lint_warnings: lintWarnings,
|
|
8184
|
-
humanHint: hintParts.join(", ")
|
|
8185
|
-
})
|
|
8186
|
-
};
|
|
8187
|
-
}
|
|
8188
|
-
var STASH_RECENT_MINUTES = 120;
|
|
8189
|
-
var MANAGED_WRITER_PATTERNS = [
|
|
8190
|
-
["wiki-push", /(?:^|[\s/\\])wiki-push(?:\.[a-z0-9_-]+)?(?:\s|$)/i],
|
|
8191
|
-
["rclone", /(?:^|[\s/\\])rclone(?:\.[a-z0-9_-]+)?(?:\s|$)/i],
|
|
8192
|
-
["vault-sync", /(?:^|[\s/\\])vault-sync(?:\.[a-z0-9_-]+)?(?:\s|$)/i]
|
|
8193
|
-
];
|
|
8194
|
-
function classifyStash(ageMinutes, sessionId, currentSession) {
|
|
8195
|
-
if (sessionId && sessionId === currentSession) return "self_or_local_recovery_stash";
|
|
8196
|
-
if (sessionId && ageMinutes <= STASH_RECENT_MINUTES) return "recent_known_peer_stash";
|
|
8197
|
-
if (ageMinutes > STASH_RECENT_MINUTES) return "stale_stash_backlog";
|
|
8198
|
-
return "unknown_stash_ownership";
|
|
8199
|
-
}
|
|
8200
|
-
function classifyManagedWriterProcesses(snapshot, currentPid = process.pid) {
|
|
8201
|
-
const kinds = /* @__PURE__ */ new Set();
|
|
8202
|
-
let count2 = 0;
|
|
8203
|
-
for (const line of snapshot.split(/\r?\n/)) {
|
|
8204
|
-
const trimmed = line.trim();
|
|
8205
|
-
if (!trimmed) continue;
|
|
8206
|
-
let pid;
|
|
8207
|
-
let processName = "";
|
|
8208
|
-
const csvMatch = trimmed.match(/^"([^"]*)","(\d+)"(?:,|$)/);
|
|
8209
|
-
if (csvMatch) {
|
|
8210
|
-
processName = csvMatch[1];
|
|
8211
|
-
pid = Number(csvMatch[2]);
|
|
8212
|
-
} else {
|
|
8213
|
-
const pidMatch = trimmed.match(/^(\d+)\s+(.*)$/);
|
|
8214
|
-
if (!pidMatch) continue;
|
|
8215
|
-
pid = Number(pidMatch[1]);
|
|
8216
|
-
processName = pidMatch[2];
|
|
8217
|
-
}
|
|
8218
|
-
if (pid === currentPid) continue;
|
|
8219
|
-
const kind = MANAGED_WRITER_PATTERNS.find(([, pattern]) => pattern.test(processName))?.[0];
|
|
8220
|
-
if (!kind) continue;
|
|
8221
|
-
count2 += 1;
|
|
8222
|
-
kinds.add(kind);
|
|
8223
|
-
}
|
|
8224
|
-
return { count: count2, kinds: [...kinds].sort(), blocking: count2 > 0 };
|
|
8225
|
-
}
|
|
8226
|
-
function managedWriterSnapshot() {
|
|
8227
|
-
try {
|
|
8228
|
-
if (process.platform === "win32") {
|
|
8229
|
-
return execFileSync3("tasklist", ["/FO", "CSV", "/NH"], {
|
|
8230
|
-
encoding: "utf8",
|
|
8231
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
8232
|
-
});
|
|
8233
|
-
}
|
|
8234
|
-
return execFileSync3("ps", ["-axo", "pid=,command="], {
|
|
8235
|
-
encoding: "utf8",
|
|
8236
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
8237
|
-
});
|
|
8238
|
-
} catch {
|
|
8239
|
-
return "";
|
|
8240
|
-
}
|
|
8241
|
-
}
|
|
8242
|
-
function runSyncPeers(input) {
|
|
8243
|
-
const vault = input.vault;
|
|
8244
|
-
const currentSession = input.sessionId ?? getSessionId();
|
|
8245
|
-
const locks = [];
|
|
8246
|
-
const existingLock = readLock(vault);
|
|
8247
|
-
if (existingLock) {
|
|
8248
|
-
const self = existingLock.session_id === currentSession;
|
|
8249
|
-
locks.push({ ...existingLock, is_self: self });
|
|
8250
|
-
}
|
|
8251
|
-
const allStashes = enumerateStashes(vault, input.nowMs);
|
|
8252
|
-
const stashes = [];
|
|
8253
|
-
const wikiSyncAudit = [];
|
|
8254
|
-
const otherStashAudit = [];
|
|
8255
|
-
for (const stash of allStashes) {
|
|
8256
|
-
let actualMessage = stash.message;
|
|
8257
|
-
const prefixMatch = stash.message.match(/^On [^:]+:\s*(.*)/);
|
|
8258
|
-
if (prefixMatch) {
|
|
8259
|
-
actualMessage = prefixMatch[1];
|
|
8260
|
-
}
|
|
8261
|
-
const match = actualMessage.match(/^wiki-sync:([^:]+):([^:]+):(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z):(.*)$/);
|
|
8262
|
-
if (match) {
|
|
8263
|
-
const session_id = match[1];
|
|
8264
|
-
const cwd_hash = match[2];
|
|
8265
|
-
const timestamp = match[3];
|
|
8266
|
-
const summary = match[4];
|
|
8267
|
-
stashes.push({
|
|
8268
|
-
ref: stash.ref,
|
|
8269
|
-
oid: stash.oid,
|
|
8270
|
-
session_id,
|
|
8271
|
-
cwd_hash,
|
|
8272
|
-
timestamp,
|
|
8273
|
-
summary,
|
|
8274
|
-
age_minutes: stash.age_minutes
|
|
8275
|
-
});
|
|
8276
|
-
wikiSyncAudit.push({
|
|
8277
|
-
ref: stash.ref,
|
|
8278
|
-
oid: stash.oid,
|
|
8279
|
-
age_minutes: stash.age_minutes,
|
|
8280
|
-
classification: classifyStash(stash.age_minutes, session_id, currentSession),
|
|
8281
|
-
format: "wiki-sync",
|
|
8282
|
-
session_id
|
|
8283
|
-
});
|
|
8284
|
-
continue;
|
|
8285
|
-
}
|
|
8286
|
-
const vaultSyncMatch = actualMessage.match(/^vault-sync\s+op=([^\s]+)(?:\s+.*)?$/i);
|
|
8287
|
-
const operationId2 = vaultSyncMatch?.[1];
|
|
8288
|
-
const manualPeer = /^peer$/i.test(actualMessage.trim());
|
|
8289
|
-
const format = operationId2 ? "vault-sync" : manualPeer ? "manual" : "unknown";
|
|
8290
|
-
const classification = operationId2 ? stash.age_minutes <= STASH_RECENT_MINUTES ? "recent_known_peer_stash" : "stale_stash_backlog" : "unknown_stash_ownership";
|
|
8291
|
-
otherStashAudit.push({
|
|
8292
|
-
ref: stash.ref,
|
|
8293
|
-
oid: stash.oid,
|
|
8294
|
-
age_minutes: stash.age_minutes,
|
|
8295
|
-
classification,
|
|
8296
|
-
format,
|
|
8297
|
-
...operationId2 ? { operation_id: operationId2 } : {}
|
|
8298
|
-
});
|
|
8299
|
-
}
|
|
8300
|
-
const stashAudit = [...wikiSyncAudit, ...otherStashAudit];
|
|
8301
|
-
const managedWriters = classifyManagedWriterProcesses(
|
|
8302
|
-
input.processSnapshot ?? managedWriterSnapshot()
|
|
8303
|
-
);
|
|
8304
|
-
const hintParts = [];
|
|
8305
|
-
if (locks.length > 0) hintParts.push(`${locks.length} lock(s)`);
|
|
8306
|
-
if (stashes.length > 0) hintParts.push(`${stashes.length} wiki-sync stash(es)`);
|
|
8307
|
-
if (managedWriters.count > 0) hintParts.push(`${managedWriters.count} live writer overlap(s)`);
|
|
8308
|
-
if (stashAudit.length > 0) hintParts.push(`${stashAudit.length} stash audit item(s)`);
|
|
8309
|
-
const blocking = locks.some((lock) => !lock.is_self) || managedWriters.blocking || stashAudit.some((entry) => entry.classification === "recent_known_peer_stash");
|
|
8310
|
-
const humanHint = hintParts.length > 0 ? hintParts.join(", ") : "no peers detected";
|
|
8311
|
-
return {
|
|
8312
|
-
exitCode: ExitCode.OK,
|
|
8313
|
-
result: ok({
|
|
8314
|
-
locks,
|
|
8315
|
-
stashes,
|
|
8316
|
-
stash_audit: stashAudit,
|
|
8317
|
-
managed_writers: managedWriters,
|
|
8318
|
-
blocking,
|
|
8319
|
-
humanHint
|
|
8320
|
-
})
|
|
8321
|
-
};
|
|
8322
|
-
}
|
|
8323
|
-
function runSyncLock(input) {
|
|
8324
|
-
const vault = input.vault;
|
|
8325
|
-
if (!existsSync9(vault)) {
|
|
8326
|
-
return {
|
|
8327
|
-
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8328
|
-
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
8329
|
-
};
|
|
8330
|
-
}
|
|
8331
|
-
const result = acquireLock(vault, {
|
|
8332
|
-
sessionId: input.sessionId,
|
|
8333
|
-
summary: input.summary,
|
|
8334
|
-
ttlMinutes: input.ttlMinutes,
|
|
8335
|
-
force: input.force
|
|
8336
|
-
});
|
|
8337
|
-
if (result.ok) {
|
|
8338
|
-
return {
|
|
8339
|
-
exitCode: ExitCode.OK,
|
|
8340
|
-
result: ok({
|
|
8341
|
-
acquired: true,
|
|
8342
|
-
lock: result.lock,
|
|
8343
|
-
humanHint: `lock acquired for ${result.lock.summary} (expires ${result.lock.expires})`
|
|
8344
|
-
})
|
|
8345
|
-
};
|
|
8346
|
-
} else {
|
|
8347
|
-
return {
|
|
8348
|
-
exitCode: ExitCode.SYNC_LOCK_HELD,
|
|
8349
|
-
result: ok({
|
|
8350
|
-
acquired: false,
|
|
8351
|
-
lock: result.held,
|
|
8352
|
-
held_by: result.held,
|
|
8353
|
-
humanHint: `lock held by session ${result.held.session_id} (PID ${result.held.pid}) for ${result.held.summary}`
|
|
8354
|
-
})
|
|
8355
|
-
};
|
|
8356
|
-
}
|
|
8357
|
-
}
|
|
8358
|
-
function runSyncUnlock(input) {
|
|
8359
|
-
const vault = input.vault;
|
|
8360
|
-
if (!existsSync9(vault)) {
|
|
8361
|
-
return {
|
|
8362
|
-
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8363
|
-
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
8364
|
-
};
|
|
8365
|
-
}
|
|
8366
|
-
const result = releaseLock(vault, { sessionId: input.sessionId, force: input.force });
|
|
8367
|
-
let humanHint;
|
|
8368
|
-
if (result.released && result.prior) {
|
|
8369
|
-
humanHint = `lock force-released (was held by session ${result.prior.session_id}, PID ${result.prior.pid})`;
|
|
8370
|
-
} else if (result.released) {
|
|
8371
|
-
humanHint = "lock released";
|
|
8372
|
-
} else {
|
|
8373
|
-
humanHint = "lock not held by this session (no-op)";
|
|
8374
|
-
}
|
|
8375
|
-
const output = {
|
|
8376
|
-
released: result.released,
|
|
8377
|
-
humanHint
|
|
8378
|
-
};
|
|
8379
|
-
if (result.prior) {
|
|
8380
|
-
output.prior = {
|
|
8381
|
-
session_id: result.prior.session_id,
|
|
8382
|
-
pid: result.prior.pid,
|
|
8383
|
-
summary: result.prior.summary
|
|
8384
|
-
};
|
|
8385
|
-
}
|
|
8386
|
-
return {
|
|
8387
|
-
exitCode: ExitCode.OK,
|
|
8388
|
-
result: ok(output)
|
|
8389
|
-
};
|
|
8390
|
-
}
|
|
8391
|
-
|
|
8392
7796
|
// src/commands/sync-journal.ts
|
|
8393
7797
|
function runSyncJournalList(input) {
|
|
8394
7798
|
const byPhase = {};
|
|
@@ -8434,11 +7838,11 @@ function runSyncJournalClearStale(input) {
|
|
|
8434
7838
|
}
|
|
8435
7839
|
|
|
8436
7840
|
// src/commands/snapshot-maintenance.ts
|
|
8437
|
-
import { existsSync as
|
|
7841
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, appendFileSync } from "fs";
|
|
8438
7842
|
import { createHash as createHash5 } from "crypto";
|
|
8439
7843
|
import { execSync as execSync3, spawn } from "child_process";
|
|
8440
7844
|
import { platform as platform2 } from "os";
|
|
8441
|
-
import { join as
|
|
7845
|
+
import { join as join30, resolve as resolvePath2 } from "path";
|
|
8442
7846
|
var MAINTENANCE_SCHEMA_VERSION = 1;
|
|
8443
7847
|
var MAINTENANCE_COMMAND = "snapshot-maintenance journal clear-stale";
|
|
8444
7848
|
function canonicalize(p) {
|
|
@@ -8501,7 +7905,7 @@ async function runSnapshotMaintenanceDryRun(input) {
|
|
|
8501
7905
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE"));
|
|
8502
7906
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
8503
7907
|
}
|
|
8504
|
-
if (!
|
|
7908
|
+
if (!existsSync9(requested) || !existsSync9(join30(requested, ".git"))) {
|
|
8505
7909
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO"));
|
|
8506
7910
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
8507
7911
|
}
|
|
@@ -8581,14 +7985,14 @@ async function resolveLiveVault(input) {
|
|
|
8581
7985
|
return resolved.ok ? canonicalize(resolved.data.path) : void 0;
|
|
8582
7986
|
}
|
|
8583
7987
|
function snapshotMaintenanceAuditLogPath(home) {
|
|
8584
|
-
const stateDir = platform2() === "darwin" ?
|
|
8585
|
-
return
|
|
7988
|
+
const stateDir = platform2() === "darwin" ? join30(home, "Library", "Application Support", "vault-sync") : join30(home, ".local", "state", "vault-sync");
|
|
7989
|
+
return join30(stateDir, "snapshot-maintenance-audit.jsonl");
|
|
8586
7990
|
}
|
|
8587
7991
|
function defaultAuditSink(home) {
|
|
8588
7992
|
return (event) => {
|
|
8589
7993
|
try {
|
|
8590
7994
|
const logPath = snapshotMaintenanceAuditLogPath(home);
|
|
8591
|
-
mkdirSync4(
|
|
7995
|
+
mkdirSync4(join30(logPath, ".."), { recursive: true });
|
|
8592
7996
|
appendFileSync(logPath, JSON.stringify(event) + "\n", { encoding: "utf8" });
|
|
8593
7997
|
} catch {
|
|
8594
7998
|
}
|
|
@@ -8749,7 +8153,7 @@ function releaseSnapshotFlock(handle) {
|
|
|
8749
8153
|
|
|
8750
8154
|
// src/commands/backup.ts
|
|
8751
8155
|
import { statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
8752
|
-
import { join as
|
|
8156
|
+
import { join as join31, relative as relative3, dirname as dirname10 } from "path";
|
|
8753
8157
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
8754
8158
|
|
|
8755
8159
|
// src/utils/s3-client.ts
|
|
@@ -8773,7 +8177,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
|
|
|
8773
8177
|
function* walkMarkdown(dir, base) {
|
|
8774
8178
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
8775
8179
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
8776
|
-
const full =
|
|
8180
|
+
const full = join31(dir, entry.name);
|
|
8777
8181
|
if (entry.isDirectory()) {
|
|
8778
8182
|
yield* walkMarkdown(full, base);
|
|
8779
8183
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -8796,7 +8200,7 @@ async function runBackupSync(input) {
|
|
|
8796
8200
|
let failed = 0;
|
|
8797
8201
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
8798
8202
|
for (const relPath of files) {
|
|
8799
|
-
const absPath =
|
|
8203
|
+
const absPath = join31(input.vault, relPath);
|
|
8800
8204
|
const localStat = statSync2(absPath);
|
|
8801
8205
|
let needsUpload = true;
|
|
8802
8206
|
try {
|
|
@@ -8872,7 +8276,7 @@ async function runBackupRestore(input) {
|
|
|
8872
8276
|
const objects = list.Contents ?? [];
|
|
8873
8277
|
for (const obj of objects) {
|
|
8874
8278
|
if (!obj.Key) continue;
|
|
8875
|
-
const localPath =
|
|
8279
|
+
const localPath = join31(target, obj.Key);
|
|
8876
8280
|
try {
|
|
8877
8281
|
const localStat = statSync2(localPath);
|
|
8878
8282
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -8918,11 +8322,11 @@ async function runBackupRestore(input) {
|
|
|
8918
8322
|
}
|
|
8919
8323
|
|
|
8920
8324
|
// src/commands/status.ts
|
|
8921
|
-
import { existsSync as
|
|
8325
|
+
import { existsSync as existsSync10, statSync as statSync3 } from "fs";
|
|
8922
8326
|
import { readFile as readFile16 } from "fs/promises";
|
|
8923
|
-
import { join as
|
|
8327
|
+
import { join as join32 } from "path";
|
|
8924
8328
|
async function runStatus(input) {
|
|
8925
|
-
if (!
|
|
8329
|
+
if (!existsSync10(input.vault)) {
|
|
8926
8330
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
8927
8331
|
}
|
|
8928
8332
|
const scan = await scanVault(input.vault);
|
|
@@ -8947,7 +8351,7 @@ async function runStatus(input) {
|
|
|
8947
8351
|
const compound = scan.data.compound.length;
|
|
8948
8352
|
let schemaVersion = "v1";
|
|
8949
8353
|
try {
|
|
8950
|
-
const schemaContent = await readFile16(
|
|
8354
|
+
const schemaContent = await readFile16(join32(input.vault, "SCHEMA.md"), "utf8");
|
|
8951
8355
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
8952
8356
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
8953
8357
|
} catch {
|
|
@@ -9008,7 +8412,7 @@ async function runStatus(input) {
|
|
|
9008
8412
|
|
|
9009
8413
|
// src/commands/seed.ts
|
|
9010
8414
|
import { mkdir as mkdir12, writeFile as writeFile8, stat as stat4 } from "fs/promises";
|
|
9011
|
-
import { join as
|
|
8415
|
+
import { join as join33 } from "path";
|
|
9012
8416
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9013
8417
|
var EXAMPLE_PAGES = {
|
|
9014
8418
|
"entities/example-project.md": `---
|
|
@@ -9077,29 +8481,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
9077
8481
|
`;
|
|
9078
8482
|
async function runSeed(input) {
|
|
9079
8483
|
try {
|
|
9080
|
-
await stat4(
|
|
8484
|
+
await stat4(join33(input.vault, "SCHEMA.md"));
|
|
9081
8485
|
} catch {
|
|
9082
8486
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
9083
8487
|
}
|
|
9084
8488
|
const created = [];
|
|
9085
8489
|
const skipped = [];
|
|
9086
8490
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
9087
|
-
const absPath =
|
|
8491
|
+
const absPath = join33(input.vault, relPath);
|
|
9088
8492
|
try {
|
|
9089
8493
|
await stat4(absPath);
|
|
9090
8494
|
skipped.push(relPath);
|
|
9091
8495
|
} catch {
|
|
9092
|
-
await mkdir12(
|
|
8496
|
+
await mkdir12(join33(absPath, ".."), { recursive: true });
|
|
9093
8497
|
await writeFile8(absPath, content, "utf8");
|
|
9094
8498
|
created.push(relPath);
|
|
9095
8499
|
}
|
|
9096
8500
|
}
|
|
9097
|
-
const rawPath =
|
|
8501
|
+
const rawPath = join33(input.vault, "raw", "articles", "example-source.md");
|
|
9098
8502
|
try {
|
|
9099
8503
|
await stat4(rawPath);
|
|
9100
8504
|
skipped.push("raw/articles/example-source.md");
|
|
9101
8505
|
} catch {
|
|
9102
|
-
await mkdir12(
|
|
8506
|
+
await mkdir12(join33(rawPath, ".."), { recursive: true });
|
|
9103
8507
|
await writeFile8(rawPath, EXAMPLE_RAW, "utf8");
|
|
9104
8508
|
created.push("raw/articles/example-source.md");
|
|
9105
8509
|
}
|
|
@@ -9123,8 +8527,8 @@ async function runSeed(input) {
|
|
|
9123
8527
|
|
|
9124
8528
|
// src/commands/canvas.ts
|
|
9125
8529
|
import { readFile as readFile17, writeFile as writeFile9 } from "fs/promises";
|
|
9126
|
-
import { existsSync as
|
|
9127
|
-
import { join as
|
|
8530
|
+
import { existsSync as existsSync11 } from "fs";
|
|
8531
|
+
import { join as join34 } from "path";
|
|
9128
8532
|
var NODE_WIDTH = 240;
|
|
9129
8533
|
var NODE_HEIGHT = 60;
|
|
9130
8534
|
var COLUMN_SPACING = 400;
|
|
@@ -9202,8 +8606,8 @@ function buildCanvasEdges(adjacency) {
|
|
|
9202
8606
|
return edges;
|
|
9203
8607
|
}
|
|
9204
8608
|
async function runCanvasGenerate(input) {
|
|
9205
|
-
const graphPath = input.graphPath ??
|
|
9206
|
-
if (!
|
|
8609
|
+
const graphPath = input.graphPath ?? join34(input.vault, ".skillwiki", "graph.json");
|
|
8610
|
+
if (!existsSync11(graphPath)) {
|
|
9207
8611
|
return {
|
|
9208
8612
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
9209
8613
|
result: err("FILE_NOT_FOUND", {
|
|
@@ -9240,7 +8644,7 @@ async function runCanvasGenerate(input) {
|
|
|
9240
8644
|
const nodes = buildCanvasNodes(paths);
|
|
9241
8645
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
9242
8646
|
const canvas = { nodes, edges };
|
|
9243
|
-
const outPath =
|
|
8647
|
+
const outPath = join34(input.vault, "vault-graph.canvas");
|
|
9244
8648
|
try {
|
|
9245
8649
|
await writeFile9(outPath, JSON.stringify(canvas, null, 2));
|
|
9246
8650
|
} catch (e) {
|
|
@@ -9279,7 +8683,7 @@ import { readFile as readFile19, unlink as unlink5 } from "fs/promises";
|
|
|
9279
8683
|
|
|
9280
8684
|
// src/utils/raw-asset-reference-index.ts
|
|
9281
8685
|
import { readdir as readdir7, readFile as readFile18 } from "fs/promises";
|
|
9282
|
-
import { join as
|
|
8686
|
+
import { join as join35, relative as relative4, sep as sep3 } from "path";
|
|
9283
8687
|
var PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "pdf", "mp3", "wav", "m4a", "mp4", "webm"]);
|
|
9284
8688
|
async function walkFiles(root, dir = root) {
|
|
9285
8689
|
let entries;
|
|
@@ -9290,7 +8694,7 @@ async function walkFiles(root, dir = root) {
|
|
|
9290
8694
|
}
|
|
9291
8695
|
const out = [];
|
|
9292
8696
|
for (const entry of entries) {
|
|
9293
|
-
const path =
|
|
8697
|
+
const path = join35(dir, entry.name);
|
|
9294
8698
|
if (entry.isDirectory()) out.push(...await walkFiles(root, path));
|
|
9295
8699
|
else if (entry.isFile()) out.push(relative4(root, path).split(sep3).join("/"));
|
|
9296
8700
|
}
|
|
@@ -9306,7 +8710,7 @@ function addInbound(map, target, source) {
|
|
|
9306
8710
|
map.set(target, refs);
|
|
9307
8711
|
}
|
|
9308
8712
|
async function buildRawAssetReferenceIndex(vault) {
|
|
9309
|
-
const assetRoot =
|
|
8713
|
+
const assetRoot = join35(vault, "raw", "assets");
|
|
9310
8714
|
const relativeAssets = await walkFiles(assetRoot);
|
|
9311
8715
|
const assets = relativeAssets.map((path) => `raw/assets/${path}`).sort((a, b) => a.localeCompare(b));
|
|
9312
8716
|
const assetSet = new Set(assets);
|
|
@@ -9503,10 +8907,10 @@ async function runSourceDisposal(input) {
|
|
|
9503
8907
|
}
|
|
9504
8908
|
|
|
9505
8909
|
// src/commands/fleet-health.ts
|
|
9506
|
-
import { existsSync as
|
|
8910
|
+
import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
|
|
9507
8911
|
import { execSync as nodeExecSync } from "child_process";
|
|
9508
8912
|
import { hostname as nodeHostname, platform as nodePlatform } from "os";
|
|
9509
|
-
import { join as
|
|
8913
|
+
import { join as join36 } from "path";
|
|
9510
8914
|
var SSH_TIMEOUT_MS = 15e3;
|
|
9511
8915
|
var TIMER_UNIT = "agent-memory-trends.timer";
|
|
9512
8916
|
var SERVICE_UNIT = "agent-memory-trends.service";
|
|
@@ -9603,7 +9007,7 @@ function applyServiceFailedOverlay(run, serviceFailed) {
|
|
|
9603
9007
|
function probeLocal(vaultPath, deps) {
|
|
9604
9008
|
const latestPath = satelliteLatestRunPath(vaultPath);
|
|
9605
9009
|
let parsed = null;
|
|
9606
|
-
if (
|
|
9010
|
+
if (existsSync12(latestPath)) {
|
|
9607
9011
|
try {
|
|
9608
9012
|
const wire = readSatelliteLatestRunFromText(readFileSync16(latestPath, "utf8"));
|
|
9609
9013
|
if (wire) {
|
|
@@ -9736,7 +9140,7 @@ async function runFleetHealth(input) {
|
|
|
9736
9140
|
const home = input.home ?? env.HOME ?? "";
|
|
9737
9141
|
const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
|
|
9738
9142
|
const vault = input.vault ?? env.WIKI_PATH;
|
|
9739
|
-
const file = input.file ?? (vault ?
|
|
9143
|
+
const file = input.file ?? (vault ? join36(vault, FLEET_REL_PATH) : void 0);
|
|
9740
9144
|
if (!file) {
|
|
9741
9145
|
return {
|
|
9742
9146
|
exitCode: ExitCode.NO_VAULT_CONFIGURED,
|
|
@@ -9820,15 +9224,15 @@ async function runFleetHealth(input) {
|
|
|
9820
9224
|
}
|
|
9821
9225
|
|
|
9822
9226
|
// src/utils/auto-commit.ts
|
|
9823
|
-
import { existsSync as
|
|
9824
|
-
import { join as
|
|
9227
|
+
import { existsSync as existsSync13 } from "fs";
|
|
9228
|
+
import { join as join37 } from "path";
|
|
9825
9229
|
async function postCommit(vault, exitCode) {
|
|
9826
9230
|
if (exitCode !== 0) return;
|
|
9827
9231
|
const home = process.env.HOME ?? "";
|
|
9828
9232
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
9829
9233
|
const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
|
|
9830
9234
|
if (autoCommit === "false") return;
|
|
9831
|
-
if (!
|
|
9235
|
+
if (!existsSync13(join37(vault, ".git"))) return;
|
|
9832
9236
|
const lastOps = readLastOp(vault);
|
|
9833
9237
|
if (lastOps.length === 0) return;
|
|
9834
9238
|
const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
|
|
@@ -9852,9 +9256,9 @@ async function postCommit(vault, exitCode) {
|
|
|
9852
9256
|
}
|
|
9853
9257
|
|
|
9854
9258
|
// src/commands/write-preflight.ts
|
|
9855
|
-
import { existsSync as
|
|
9259
|
+
import { existsSync as existsSync14, readFileSync as readFileSync17, statSync as statSync4 } from "fs";
|
|
9856
9260
|
async function runWritePreflightCommand(input) {
|
|
9857
|
-
if (!
|
|
9261
|
+
if (!existsSync14(input.vault) || !statSync4(input.vault).isDirectory()) {
|
|
9858
9262
|
return {
|
|
9859
9263
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
9860
9264
|
result: err(GateError.VAULT_PATH_INVALID, { path: input.vault })
|
|
@@ -9862,7 +9266,7 @@ async function runWritePreflightCommand(input) {
|
|
|
9862
9266
|
}
|
|
9863
9267
|
let priorText = input.priorArtifactText;
|
|
9864
9268
|
if (input.priorArtifactFile) {
|
|
9865
|
-
if (!
|
|
9269
|
+
if (!existsSync14(input.priorArtifactFile)) {
|
|
9866
9270
|
return {
|
|
9867
9271
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
9868
9272
|
result: err("FILE_NOT_FOUND", { path: input.priorArtifactFile })
|
|
@@ -9982,7 +9386,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
|
|
|
9982
9386
|
if (dirty) {
|
|
9983
9387
|
return emit(dirty, void 0, { postCommit: false });
|
|
9984
9388
|
}
|
|
9985
|
-
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-
|
|
9389
|
+
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-2FU5F56H.js");
|
|
9986
9390
|
const run = await runManagedWriteTransaction2({
|
|
9987
9391
|
vault,
|
|
9988
9392
|
command,
|
|
@@ -10012,7 +9416,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
10012
9416
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
10013
9417
|
});
|
|
10014
9418
|
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) => {
|
|
10015
|
-
const out = opts.out ??
|
|
9419
|
+
const out = opts.out ?? join38(vault, ".skillwiki", "graph.json");
|
|
10016
9420
|
return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
|
|
10017
9421
|
});
|
|
10018
9422
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|