skillwiki 0.10.109 → 0.10.111
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-JCDAEZVD.js → chunk-5IBLXGLY.js} +5 -3
- package/dist/chunk-6OQLBNKR.js +250 -0
- package/dist/chunk-GQWCTJTU.js +55 -0
- package/dist/{chunk-FYAKMULW.js → chunk-HBJLFPQP.js} +91 -198
- package/dist/chunk-I2J3IJB7.js +54 -0
- package/dist/{chunk-HMHMCPCE.js → chunk-I6IRJCUJ.js} +12 -57
- package/dist/{chunk-QU2IXZRN.js → chunk-JQV5KNPT.js} +8 -6
- package/dist/chunk-KWEYA3OE.js +60 -0
- package/dist/{chunk-DTTKCCQ4.js → chunk-MRPJ7X7W.js} +5 -3
- package/dist/{chunk-LDEZQXH6.js → chunk-OJJBZDPR.js} +306 -116
- package/dist/chunk-OSL4J7GF.js +32 -0
- package/dist/chunk-PYPPG7RF.js +126 -0
- package/dist/chunk-QESNTTVY.js +50 -0
- package/dist/chunk-TDBUJUYH.js +109 -0
- package/dist/{chunk-GJC2CI3W.js → chunk-UIEQIJXP.js} +108 -336
- package/dist/chunk-X5GL2FCU.js +344 -0
- package/dist/chunk-ZRGZBAVS.js +98 -0
- package/dist/cli.js +352 -297
- package/dist/{index-projection-ESBJOBS7.js → index-projection-2K672CBO.js} +5 -3
- package/dist/journal-QU5IDXQF.js +13 -0
- package/dist/{managed-write-preflight-MAIGWFQJ.js → managed-write-preflight-GRSHQYKC.js} +7 -4
- package/dist/migrate-PIB4QA54.js +21 -0
- package/dist/ready-S7F53Q2D.js +9 -0
- package/dist/runtime-GLMI5OCT.js +15 -0
- package/dist/skillwiki-mcp.js +12 -7
- package/dist/sources-D2MGP246.js +10 -0
- package/dist/upload-HTE62TOV.js +14 -0
- package/dist/watcher-MHG46J3R.js +616 -0
- package/docs/clip-outbox.md +119 -0
- package/package.json +5 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/.cursor-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/skills/using-skillwiki/SKILL.md +1 -1
- package/skills/skills/wiki-init/SKILL.md +12 -11
- package/skills/using-skillwiki/SKILL.md +1 -1
- package/skills/wiki-init/SKILL.md +12 -11
- package/templates/launchd/com.karlchow.skillwiki-clip-outbox.plist +21 -0
- package/templates/web-clipper/llm-wiki-clippings.json +1 -1
- package/templates/web-clipper/readme.txt +13 -13
- package/dist/sources-QTQSZMST.js +0 -9
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runFetchGuard,
|
|
4
|
+
runFetchGuardSync
|
|
5
|
+
} from "./chunk-I2J3IJB7.js";
|
|
6
|
+
import {
|
|
7
|
+
buildDeleteIntent,
|
|
8
|
+
normalizeVaultRelPath,
|
|
9
|
+
pathToIntentFilename,
|
|
10
|
+
writeDeleteIntent
|
|
11
|
+
} from "./chunk-QESNTTVY.js";
|
|
2
12
|
import {
|
|
3
13
|
CAPTURE_HYGIENE_CONTRACT,
|
|
4
14
|
DEFAULT_CAPTURE_BUDGET,
|
|
@@ -65,7 +75,15 @@ import {
|
|
|
65
75
|
upsertIndexEntry,
|
|
66
76
|
vectorIndexStatus,
|
|
67
77
|
withMcpClient
|
|
68
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-OJJBZDPR.js";
|
|
79
|
+
import {
|
|
80
|
+
applySourceDisposition,
|
|
81
|
+
decodeSourceActionApproval,
|
|
82
|
+
encodeSourceActionApproval,
|
|
83
|
+
inventorySources,
|
|
84
|
+
planSourceDisposition,
|
|
85
|
+
runSourcesPending
|
|
86
|
+
} from "./chunk-JQV5KNPT.js";
|
|
69
87
|
import {
|
|
70
88
|
normalizeDistTag,
|
|
71
89
|
readCache,
|
|
@@ -78,7 +96,7 @@ import {
|
|
|
78
96
|
UNMANAGED_START,
|
|
79
97
|
renderRootIndex,
|
|
80
98
|
writeRootIndexProjection
|
|
81
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-MRPJ7X7W.js";
|
|
82
100
|
import {
|
|
83
101
|
FLEET_REL_PATH,
|
|
84
102
|
REDACTED_MALFORMED_REFERENCE,
|
|
@@ -118,13 +136,10 @@ import {
|
|
|
118
136
|
normalizeProjectSlug,
|
|
119
137
|
normalizeRemoteRoot,
|
|
120
138
|
parseActiveWorkPath,
|
|
121
|
-
parseDotenvFile,
|
|
122
|
-
parseDotenvText,
|
|
123
139
|
parseJournalEnv,
|
|
124
140
|
planAndMaybePruneRemoteObjects,
|
|
125
141
|
planRawStructuralMove,
|
|
126
142
|
probeS3Reachability,
|
|
127
|
-
profileKey,
|
|
128
143
|
readJournal,
|
|
129
144
|
readLastOp,
|
|
130
145
|
readSkillWikiConfig,
|
|
@@ -135,7 +150,6 @@ import {
|
|
|
135
150
|
resolveFleetHostId,
|
|
136
151
|
resolveInitTimePath,
|
|
137
152
|
resolveRuntimePath,
|
|
138
|
-
resolveSkillwikiHome,
|
|
139
153
|
resolveWikiS3Remote,
|
|
140
154
|
rewriteRawSourceReferences,
|
|
141
155
|
runAudit,
|
|
@@ -169,22 +183,8 @@ import {
|
|
|
169
183
|
snapshotterAliasForLocalHost,
|
|
170
184
|
stageVaultContentChanges,
|
|
171
185
|
supersedeStaleReviewRequiredJournals,
|
|
172
|
-
taxonomyCommentForPage
|
|
173
|
-
|
|
174
|
-
} from "./chunk-FYAKMULW.js";
|
|
175
|
-
import {
|
|
176
|
-
assertTargetInsideVault,
|
|
177
|
-
atomicWriteText,
|
|
178
|
-
prepareTypedPage
|
|
179
|
-
} from "./chunk-HMHMCPCE.js";
|
|
180
|
-
import {
|
|
181
|
-
applySourceDisposition,
|
|
182
|
-
decodeSourceActionApproval,
|
|
183
|
-
encodeSourceActionApproval,
|
|
184
|
-
inventorySources,
|
|
185
|
-
planSourceDisposition,
|
|
186
|
-
runSourcesPending
|
|
187
|
-
} from "./chunk-QU2IXZRN.js";
|
|
186
|
+
taxonomyCommentForPage
|
|
187
|
+
} from "./chunk-HBJLFPQP.js";
|
|
188
188
|
import {
|
|
189
189
|
authorizeRawOperation,
|
|
190
190
|
buildSourceReferenceIndex,
|
|
@@ -201,31 +201,54 @@ import {
|
|
|
201
201
|
resolveExistingRegularFileInsideVault,
|
|
202
202
|
validateLogEvent,
|
|
203
203
|
writeLogEvent
|
|
204
|
-
} from "./chunk-
|
|
204
|
+
} from "./chunk-5IBLXGLY.js";
|
|
205
|
+
import {
|
|
206
|
+
assertTargetInsideVault,
|
|
207
|
+
prepareTypedPage
|
|
208
|
+
} from "./chunk-I6IRJCUJ.js";
|
|
205
209
|
import {
|
|
206
|
-
ExitCode,
|
|
207
|
-
MetaSchema,
|
|
208
|
-
RawSourceSchema,
|
|
209
|
-
TypedKnowledgeSchema,
|
|
210
|
-
detectSchema,
|
|
211
|
-
err,
|
|
212
210
|
extractFrontmatter,
|
|
213
|
-
isBlockedHost,
|
|
214
211
|
mapWithConcurrency,
|
|
215
|
-
ok,
|
|
216
212
|
readPage,
|
|
217
213
|
readPageCached,
|
|
218
214
|
redactSensitiveContent,
|
|
219
215
|
resolveReadOnlyVaultRoot,
|
|
220
|
-
resolveSessionKind,
|
|
221
216
|
scanSensitiveContent,
|
|
222
217
|
scanVault,
|
|
223
218
|
splitFrontmatter,
|
|
224
219
|
vaultIoConcurrency
|
|
225
|
-
} from "./chunk-
|
|
220
|
+
} from "./chunk-X5GL2FCU.js";
|
|
221
|
+
import {
|
|
222
|
+
CLIP_MIGRATION_HINT,
|
|
223
|
+
refreshInstalledClipPlist
|
|
224
|
+
} from "./chunk-TDBUJUYH.js";
|
|
225
|
+
import {
|
|
226
|
+
CLIP_NOT_CONFIGURED,
|
|
227
|
+
loadClipOutboxConfig
|
|
228
|
+
} from "./chunk-6OQLBNKR.js";
|
|
229
|
+
import {
|
|
230
|
+
parseDotenvFile,
|
|
231
|
+
parseDotenvText,
|
|
232
|
+
profileKey,
|
|
233
|
+
resolveSkillwikiHome,
|
|
234
|
+
writeDotenv
|
|
235
|
+
} from "./chunk-PYPPG7RF.js";
|
|
236
|
+
import {
|
|
237
|
+
atomicWriteText
|
|
238
|
+
} from "./chunk-GQWCTJTU.js";
|
|
239
|
+
import {
|
|
240
|
+
ExitCode,
|
|
241
|
+
MetaSchema,
|
|
242
|
+
RawSourceSchema,
|
|
243
|
+
TypedKnowledgeSchema,
|
|
244
|
+
detectSchema,
|
|
245
|
+
err,
|
|
246
|
+
ok,
|
|
247
|
+
resolveSessionKind
|
|
248
|
+
} from "./chunk-UIEQIJXP.js";
|
|
226
249
|
|
|
227
250
|
// src/cli.ts
|
|
228
|
-
import { join as
|
|
251
|
+
import { join as join44 } from "path";
|
|
229
252
|
import { Command } from "commander";
|
|
230
253
|
|
|
231
254
|
// src/utils/output.ts
|
|
@@ -341,48 +364,6 @@ async function runHash(input) {
|
|
|
341
364
|
};
|
|
342
365
|
}
|
|
343
366
|
|
|
344
|
-
// src/commands/fetch-guard.ts
|
|
345
|
-
var REDACT_PARAMS = /* @__PURE__ */ new Set(["api_key", "token", "key", "auth", "password", "secret", "access_token"]);
|
|
346
|
-
var PATH_TOKEN_RE = /[A-Fa-f0-9]{32,}|[A-Za-z0-9_\-]{40,}/g;
|
|
347
|
-
function runFetchGuard(input) {
|
|
348
|
-
return Promise.resolve(runFetchGuardSync(input));
|
|
349
|
-
}
|
|
350
|
-
function runFetchGuardSync(input) {
|
|
351
|
-
let u;
|
|
352
|
-
try {
|
|
353
|
-
u = new URL(input.url);
|
|
354
|
-
} catch {
|
|
355
|
-
return { exitCode: ExitCode.MALFORMED_URL, result: err("MALFORMED_URL", { url: input.url }) };
|
|
356
|
-
}
|
|
357
|
-
const sanitized = sanitizeUrl(u);
|
|
358
|
-
if (u.protocol !== "https:") {
|
|
359
|
-
return {
|
|
360
|
-
exitCode: ExitCode.SCHEME_REJECTED,
|
|
361
|
-
result: err("SCHEME_REJECTED", { sanitized_url: sanitized, scheme: u.protocol })
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
if (isBlockedHost(u.hostname)) {
|
|
365
|
-
return {
|
|
366
|
-
exitCode: ExitCode.HOST_BLOCKED,
|
|
367
|
-
result: err("HOST_BLOCKED", { sanitized_url: sanitized, host: u.hostname })
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
return { exitCode: ExitCode.OK, result: ok({ allowed: true, sanitized_url: sanitized, humanHint: `ALLOWED: ${sanitized}` }) };
|
|
371
|
-
}
|
|
372
|
-
function sanitizeUrl(u) {
|
|
373
|
-
const clone = new URL(u.toString());
|
|
374
|
-
if (clone.username || clone.password) {
|
|
375
|
-
clone.username = "";
|
|
376
|
-
clone.password = "";
|
|
377
|
-
}
|
|
378
|
-
for (const k of Array.from(clone.searchParams.keys())) {
|
|
379
|
-
if (REDACT_PARAMS.has(k.toLowerCase())) clone.searchParams.set(k, "REDACTED");
|
|
380
|
-
}
|
|
381
|
-
let s = clone.toString();
|
|
382
|
-
s = s.replace(PATH_TOKEN_RE, "REDACTED");
|
|
383
|
-
return s;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
367
|
// src/commands/eval.ts
|
|
387
368
|
import { execFileSync } from "child_process";
|
|
388
369
|
import { existsSync, mkdtempSync, rmSync } from "fs";
|
|
@@ -1288,7 +1269,7 @@ gitignore: ${gi.written ? "merged" : "unchanged"} (write-gitignore only)`,
|
|
|
1288
1269
|
skipEnv ? "env: skipped" : `env: ${envWritten}`,
|
|
1289
1270
|
input.noGitignore ? "gitignore: skipped" : `gitignore: ${gitignoreWritten ? "written" : "unchanged"}`,
|
|
1290
1271
|
`web clipper template: ${WEB_CLIPPER_TEMPLATE_REL}`,
|
|
1291
|
-
"web clipper import:
|
|
1272
|
+
"web clipper import: create ~/Obsidian/clip-outbox (not ~/wiki), import the JSON in each browser profile, select that outbox vault, keep Add to Obsidian"
|
|
1292
1273
|
].join("\n");
|
|
1293
1274
|
if (created.length > 0) {
|
|
1294
1275
|
appendLastOp(target, {
|
|
@@ -3587,6 +3568,92 @@ async function runHealth(input) {
|
|
|
3587
3568
|
return { exitCode, result: ok(report) };
|
|
3588
3569
|
}
|
|
3589
3570
|
|
|
3571
|
+
// src/commands/clip-outbox.ts
|
|
3572
|
+
async function runClipOutboxStatus(home) {
|
|
3573
|
+
const resolved = await loadClipOutboxConfig(home);
|
|
3574
|
+
if (!resolved.ok) {
|
|
3575
|
+
return {
|
|
3576
|
+
exitCode: ExitCode.INVALID_CONFIG_VALUE,
|
|
3577
|
+
result: err("INVALID_CONFIG_VALUE", { message: resolved.message })
|
|
3578
|
+
};
|
|
3579
|
+
}
|
|
3580
|
+
if (!resolved.enabled) {
|
|
3581
|
+
return {
|
|
3582
|
+
exitCode: ExitCode.OK,
|
|
3583
|
+
result: ok({ enabled: false, humanHint: CLIP_NOT_CONFIGURED })
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
const { readJournal: readJournal2 } = await import("./journal-QU5IDXQF.js");
|
|
3587
|
+
let queue;
|
|
3588
|
+
try {
|
|
3589
|
+
queue = (await readJournal2(resolved.config.stateRoot)).map(({ captureId, state, reason, attempts }) => ({ captureId, state, reason, attempts }));
|
|
3590
|
+
} catch {
|
|
3591
|
+
return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: "clip journal unreadable; retain it for attended recovery" }) };
|
|
3592
|
+
}
|
|
3593
|
+
return {
|
|
3594
|
+
exitCode: ExitCode.OK,
|
|
3595
|
+
result: ok({
|
|
3596
|
+
enabled: true,
|
|
3597
|
+
queue,
|
|
3598
|
+
humanHint: [`clip-outbox enabled root=${resolved.config.root} mode=${resolved.config.mode}`, ...queue.map((e) => `${e.captureId}: ${e.state}${e.reason ? ` (${e.reason})` : ""}; attempts=${e.attempts ?? 0}`)].join("\n")
|
|
3599
|
+
})
|
|
3600
|
+
};
|
|
3601
|
+
}
|
|
3602
|
+
async function runClipOutboxInstall(input) {
|
|
3603
|
+
try {
|
|
3604
|
+
const { migrateClipOutbox } = await import("./migrate-PIB4QA54.js");
|
|
3605
|
+
return { exitCode: ExitCode.OK, result: ok(await migrateClipOutbox(input)) };
|
|
3606
|
+
} catch (e) {
|
|
3607
|
+
return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: e instanceof Error ? e.message : "clip migration failed" }) };
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
async function runClipOutboxWatch(home) {
|
|
3611
|
+
const resolved = await loadClipOutboxConfig(home);
|
|
3612
|
+
if (!resolved.ok || !resolved.enabled) return { exitCode: ExitCode.INVALID_CONFIG_VALUE, result: err("INVALID_CONFIG_VALUE", { message: resolved.ok ? CLIP_NOT_CONFIGURED : resolved.message }) };
|
|
3613
|
+
if (process.platform !== "darwin") return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: "clip-outbox watch requires macOS FSEvents" }) };
|
|
3614
|
+
let client;
|
|
3615
|
+
try {
|
|
3616
|
+
const { S3Client: S3Client3 } = await import("@aws-sdk/client-s3");
|
|
3617
|
+
const { loadClipCredentials, validateRuntime } = await import("./runtime-GLMI5OCT.js");
|
|
3618
|
+
const { createS3CreateOnlyPut, parseDestination } = await import("./upload-HTE62TOV.js");
|
|
3619
|
+
const { postReadyHint } = await import("./ready-S7F53Q2D.js");
|
|
3620
|
+
const { startClipWorker } = await import("./watcher-MHG46J3R.js");
|
|
3621
|
+
const cfg = resolved.config;
|
|
3622
|
+
const credentials = await loadClipCredentials(home, cfg);
|
|
3623
|
+
await validateRuntime(home, cfg, credentials.s3.endpoint);
|
|
3624
|
+
const destination = parseDestination(cfg.destination);
|
|
3625
|
+
client = new S3Client3({ endpoint: credentials.s3.endpoint, region: credentials.s3.region, credentials: credentials.s3, forcePathStyle: true, maxAttempts: 1 });
|
|
3626
|
+
let finish;
|
|
3627
|
+
const done = new Promise((r) => {
|
|
3628
|
+
finish = r;
|
|
3629
|
+
});
|
|
3630
|
+
let failed = false;
|
|
3631
|
+
const worker = await startClipWorker(cfg, {
|
|
3632
|
+
put: createS3CreateOnlyPut({ client, bucket: destination.bucket, prefix: destination.prefix }),
|
|
3633
|
+
ready: (hint) => postReadyHint({ url: cfg.readyUrl, token: credentials.readyToken, hint }),
|
|
3634
|
+
validate: () => validateRuntime(home, cfg, credentials.s3.endpoint),
|
|
3635
|
+
onError: () => {
|
|
3636
|
+
failed = true;
|
|
3637
|
+
finish();
|
|
3638
|
+
}
|
|
3639
|
+
});
|
|
3640
|
+
process.once("SIGTERM", finish);
|
|
3641
|
+
process.once("SIGINT", finish);
|
|
3642
|
+
try {
|
|
3643
|
+
await done;
|
|
3644
|
+
} finally {
|
|
3645
|
+
process.removeListener("SIGTERM", finish);
|
|
3646
|
+
process.removeListener("SIGINT", finish);
|
|
3647
|
+
await worker.stop();
|
|
3648
|
+
}
|
|
3649
|
+
return failed ? { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: "clip worker stopped; inspect configuration and retained journal" }) } : { exitCode: ExitCode.OK, result: ok({ humanHint: "Clip worker stopped; snapshots and journal retained." }) };
|
|
3650
|
+
} catch {
|
|
3651
|
+
return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: "clip worker startup failed; check configuration, protected credential references, conditional-create evidence and worker lock" }) };
|
|
3652
|
+
} finally {
|
|
3653
|
+
client?.destroy();
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3590
3657
|
// src/commands/connect.ts
|
|
3591
3658
|
import { chmod, mkdir as mkdir5, readFile as readFile6, stat as stat3 } from "fs/promises";
|
|
3592
3659
|
import { join as join18 } from "path";
|
|
@@ -3896,50 +3963,8 @@ async function runConnect(input) {
|
|
|
3896
3963
|
}
|
|
3897
3964
|
|
|
3898
3965
|
// src/commands/archive.ts
|
|
3899
|
-
import { rename as rename2, mkdir as
|
|
3900
|
-
import { join as
|
|
3901
|
-
|
|
3902
|
-
// src/utils/delete-intent.ts
|
|
3903
|
-
import { mkdir as mkdir6, writeFile as writeFile4, readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
3904
|
-
import { join as join19 } from "path";
|
|
3905
|
-
var DELETE_INTENT_SCHEMA = "vault-delete-intent/v1";
|
|
3906
|
-
var DELETE_INTENT_DIR = "meta/delete-intents";
|
|
3907
|
-
function normalizeVaultRelPath(path) {
|
|
3908
|
-
const p = path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
3909
|
-
if (!p || p.includes("..") || p.startsWith(".git/")) {
|
|
3910
|
-
throw new Error(`invalid vault-relative path: ${path}`);
|
|
3911
|
-
}
|
|
3912
|
-
return p;
|
|
3913
|
-
}
|
|
3914
|
-
function pathToIntentFilename(path) {
|
|
3915
|
-
const p = normalizeVaultRelPath(path);
|
|
3916
|
-
return `${p.replace(/\//g, "__")}.json`;
|
|
3917
|
-
}
|
|
3918
|
-
function intentHostId() {
|
|
3919
|
-
return process.env.SKILLWIKI_HOST_ID ?? process.env.AGENT_HOST_ID ?? "unknown";
|
|
3920
|
-
}
|
|
3921
|
-
function buildDeleteIntent(input) {
|
|
3922
|
-
return {
|
|
3923
|
-
schema: DELETE_INTENT_SCHEMA,
|
|
3924
|
-
path: normalizeVaultRelPath(input.path),
|
|
3925
|
-
action: input.action,
|
|
3926
|
-
created: input.created ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3927
|
-
host: input.host ?? intentHostId(),
|
|
3928
|
-
actor: input.actor,
|
|
3929
|
-
reason: input.reason,
|
|
3930
|
-
source: input.source,
|
|
3931
|
-
expires: input.expires ?? null
|
|
3932
|
-
};
|
|
3933
|
-
}
|
|
3934
|
-
async function writeDeleteIntent(vault, intent) {
|
|
3935
|
-
const dir = join19(vault, DELETE_INTENT_DIR);
|
|
3936
|
-
await mkdir6(dir, { recursive: true });
|
|
3937
|
-
const rel = `${DELETE_INTENT_DIR}/${pathToIntentFilename(intent.path)}`;
|
|
3938
|
-
await writeFile4(join19(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
|
|
3939
|
-
return rel;
|
|
3940
|
-
}
|
|
3941
|
-
|
|
3942
|
-
// src/commands/archive.ts
|
|
3966
|
+
import { rename as rename2, mkdir as mkdir6, readFile as readFile7 } from "fs/promises";
|
|
3967
|
+
import { join as join19, dirname as dirname5 } from "path";
|
|
3943
3968
|
function countWikilinks(body, slug) {
|
|
3944
3969
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3945
3970
|
const re = new RegExp(`\\[\\[${escaped}(?:[|#][^\\]]*)?\\]\\]`, "g");
|
|
@@ -3975,7 +4000,7 @@ async function runArchive(input) {
|
|
|
3975
4000
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
3976
4001
|
const rawArchive = isRaw ? lifecycleDestination(relPath, "archive") : void 0;
|
|
3977
4002
|
if (rawArchive && !rawArchive.ok) return { exitCode: ExitCode.USAGE, result: rawArchive };
|
|
3978
|
-
const archivePath = isRaw ? rawArchive.data :
|
|
4003
|
+
const archivePath = isRaw ? rawArchive.data : join19("_archive", relPath).replace(/\\/g, "/");
|
|
3979
4004
|
const remoteRoot = normalizeRemoteRoot(input.remote);
|
|
3980
4005
|
const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
|
|
3981
4006
|
let cascade;
|
|
@@ -4005,7 +4030,7 @@ async function runArchive(input) {
|
|
|
4005
4030
|
const indexRefs = [];
|
|
4006
4031
|
if (!isRaw) {
|
|
4007
4032
|
try {
|
|
4008
|
-
const idx = await
|
|
4033
|
+
const idx = await readFile7(join19(input.vault, "index.md"), "utf8");
|
|
4009
4034
|
idx.split("\n").forEach((line, i) => {
|
|
4010
4035
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
4011
4036
|
});
|
|
@@ -4063,8 +4088,8 @@ async function runArchive(input) {
|
|
|
4063
4088
|
const rewrittenCascadePages = [];
|
|
4064
4089
|
if ((input.cascade || isRaw) && input.apply && cascade) {
|
|
4065
4090
|
for (const ref of cascade.source_array_refs) {
|
|
4066
|
-
const absPath =
|
|
4067
|
-
const text = await
|
|
4091
|
+
const absPath = join19(input.vault, ref.page);
|
|
4092
|
+
const text = await readFile7(absPath, "utf8");
|
|
4068
4093
|
if (!arraysEqual(ref.sources_after, ref.sources_before)) {
|
|
4069
4094
|
let updated;
|
|
4070
4095
|
if (isRaw) {
|
|
@@ -4090,8 +4115,8 @@ ${fmRewritten}
|
|
|
4090
4115
|
if (isRaw) {
|
|
4091
4116
|
for (const ref of cascade.citation_refs) {
|
|
4092
4117
|
if (cascade.source_array_refs.some((sourceRef) => sourceRef.page === ref.page)) continue;
|
|
4093
|
-
const absPath =
|
|
4094
|
-
const original = await
|
|
4118
|
+
const absPath = join19(input.vault, ref.page);
|
|
4119
|
+
const original = await readFile7(absPath, "utf8");
|
|
4095
4120
|
const updated = rewriteRawSourceReferences(original, relPath, archivePath).text;
|
|
4096
4121
|
if (updated !== original) {
|
|
4097
4122
|
const write = await safeWritePage(absPath, updated);
|
|
@@ -4102,13 +4127,13 @@ ${fmRewritten}
|
|
|
4102
4127
|
}
|
|
4103
4128
|
}
|
|
4104
4129
|
if (!isRaw) {
|
|
4105
|
-
await
|
|
4106
|
-
await rename2(
|
|
4130
|
+
await mkdir6(dirname5(join19(input.vault, archivePath)), { recursive: true });
|
|
4131
|
+
await rename2(join19(input.vault, relPath), join19(input.vault, archivePath));
|
|
4107
4132
|
}
|
|
4108
4133
|
let indexUpdated = false;
|
|
4109
4134
|
if (!isRaw) {
|
|
4110
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
4111
|
-
const before = await
|
|
4135
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-2K672CBO.js");
|
|
4136
|
+
const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
|
|
4112
4137
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
4113
4138
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
4114
4139
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -4162,7 +4187,7 @@ ${fmRewritten}
|
|
|
4162
4187
|
|
|
4163
4188
|
// src/commands/remove.ts
|
|
4164
4189
|
import { unlink as unlink2, access } from "fs/promises";
|
|
4165
|
-
import { join as
|
|
4190
|
+
import { join as join20 } from "path";
|
|
4166
4191
|
async function pathExists(abs) {
|
|
4167
4192
|
try {
|
|
4168
4193
|
await access(abs);
|
|
@@ -4188,7 +4213,7 @@ async function runRemove(input) {
|
|
|
4188
4213
|
if (!relPath) {
|
|
4189
4214
|
try {
|
|
4190
4215
|
const candidate = normalizeVaultRelPath(input.page);
|
|
4191
|
-
if (await pathExists(
|
|
4216
|
+
if (await pathExists(join20(input.vault, candidate))) {
|
|
4192
4217
|
relPath = candidate;
|
|
4193
4218
|
}
|
|
4194
4219
|
} catch {
|
|
@@ -4226,12 +4251,12 @@ async function runRemove(input) {
|
|
|
4226
4251
|
reason: input.reason
|
|
4227
4252
|
});
|
|
4228
4253
|
const tombstonePath = await writeDeleteIntent(input.vault, intent);
|
|
4229
|
-
await unlink2(
|
|
4254
|
+
await unlink2(join20(input.vault, relPath));
|
|
4230
4255
|
if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
|
|
4231
|
-
const { readFile:
|
|
4256
|
+
const { readFile: readFile23 } = await import("fs/promises");
|
|
4232
4257
|
const { join: pathJoin } = await import("path");
|
|
4233
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
4234
|
-
const before = await
|
|
4258
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-2K672CBO.js");
|
|
4259
|
+
const before = await readFile23(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
|
|
4235
4260
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
4236
4261
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
4237
4262
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -4580,7 +4605,7 @@ ${migratedBody}${newFooter}`;
|
|
|
4580
4605
|
|
|
4581
4606
|
// src/commands/update.ts
|
|
4582
4607
|
import { execSync } from "child_process";
|
|
4583
|
-
import { join as
|
|
4608
|
+
import { join as join21 } from "path";
|
|
4584
4609
|
function parseCoreSemver(version) {
|
|
4585
4610
|
const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
4586
4611
|
if (!m) return null;
|
|
@@ -4624,6 +4649,8 @@ function migrationNotesForUpgrade(previousVersion, newVersion) {
|
|
|
4624
4649
|
"- frozen-leaf hosts write via HTTP MCP only; do not local-write raw/transcripts/ or vault git"
|
|
4625
4650
|
);
|
|
4626
4651
|
}
|
|
4652
|
+
const next = parseCoreSemver(newVersion);
|
|
4653
|
+
if (next && isAtLeast(next, 0, 10, 111)) notes.push(CLIP_MIGRATION_HINT);
|
|
4627
4654
|
return notes;
|
|
4628
4655
|
}
|
|
4629
4656
|
function resolveGlobalSkillsRoot() {
|
|
@@ -4632,7 +4659,7 @@ function resolveGlobalSkillsRoot() {
|
|
|
4632
4659
|
encoding: "utf8",
|
|
4633
4660
|
timeout: 5e3
|
|
4634
4661
|
}).trim();
|
|
4635
|
-
return
|
|
4662
|
+
return join21(globalRoot, "skillwiki", "skills");
|
|
4636
4663
|
} catch {
|
|
4637
4664
|
return null;
|
|
4638
4665
|
}
|
|
@@ -4660,7 +4687,7 @@ async function runUpdate(input) {
|
|
|
4660
4687
|
const pkg2 = readCliPackageJson();
|
|
4661
4688
|
const currentVersion = pkg2.version;
|
|
4662
4689
|
const tag = normalizeDistTag(input.distTag);
|
|
4663
|
-
const target =
|
|
4690
|
+
const target = join21(input.home, ".claude", "skills");
|
|
4664
4691
|
let latest;
|
|
4665
4692
|
try {
|
|
4666
4693
|
latest = execSync(`npm view skillwiki@${tag} version`, {
|
|
@@ -4693,7 +4720,8 @@ async function runUpdate(input) {
|
|
|
4693
4720
|
version_warnings: [],
|
|
4694
4721
|
skills_refreshed: false,
|
|
4695
4722
|
deferred_to_plugin: false,
|
|
4696
|
-
humanHint: `Already on npm@${tag}: v${currentVersion}
|
|
4723
|
+
humanHint: `Already on npm@${tag}: v${currentVersion}
|
|
4724
|
+
${CLIP_MIGRATION_HINT}`
|
|
4697
4725
|
})
|
|
4698
4726
|
};
|
|
4699
4727
|
}
|
|
@@ -4724,6 +4752,12 @@ async function runUpdate(input) {
|
|
|
4724
4752
|
for (const line of migrationNotesForUpgrade(currentVersion, latest)) {
|
|
4725
4753
|
hintLines.push(line);
|
|
4726
4754
|
}
|
|
4755
|
+
try {
|
|
4756
|
+
const clipHint = await refreshInstalledClipPlist(input.home);
|
|
4757
|
+
if (clipHint) hintLines.push(clipHint);
|
|
4758
|
+
} catch (e) {
|
|
4759
|
+
hintLines.push(`clip plist refresh skipped: ${e instanceof Error ? e.message : "migration failed"}`);
|
|
4760
|
+
}
|
|
4727
4761
|
return {
|
|
4728
4762
|
exitCode: ExitCode.OK,
|
|
4729
4763
|
result: ok({
|
|
@@ -4741,13 +4775,13 @@ async function runUpdate(input) {
|
|
|
4741
4775
|
// src/commands/self-update.ts
|
|
4742
4776
|
import { execSync as execSync2 } from "child_process";
|
|
4743
4777
|
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
|
|
4744
|
-
import { join as
|
|
4778
|
+
import { join as join22 } from "path";
|
|
4745
4779
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
4746
4780
|
async function runSelfUpdate(input) {
|
|
4747
4781
|
const currentVersion = readCliPackageJson().version;
|
|
4748
4782
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
4749
4783
|
const distTag = normalizeDistTag(input.distTag);
|
|
4750
|
-
const localPkgPath =
|
|
4784
|
+
const localPkgPath = join22(sourceRoot, "packages", "cli", "package.json");
|
|
4751
4785
|
const hasLocalSource = existsSync8(localPkgPath);
|
|
4752
4786
|
if (input.check) {
|
|
4753
4787
|
let availableVersion = null;
|
|
@@ -4879,21 +4913,21 @@ async function runSelfUpdate(input) {
|
|
|
4879
4913
|
}
|
|
4880
4914
|
|
|
4881
4915
|
// src/commands/transcripts.ts
|
|
4882
|
-
import { readdir as
|
|
4883
|
-
import { join as
|
|
4916
|
+
import { readdir as readdir4, stat as stat4, readFile as readFile9 } from "fs/promises";
|
|
4917
|
+
import { join as join23 } from "path";
|
|
4884
4918
|
async function runTranscripts(input) {
|
|
4885
|
-
const dir =
|
|
4919
|
+
const dir = join23(input.vault, "raw", "transcripts");
|
|
4886
4920
|
let entries;
|
|
4887
4921
|
try {
|
|
4888
|
-
entries = await
|
|
4922
|
+
entries = await readdir4(dir, { withFileTypes: true });
|
|
4889
4923
|
} catch {
|
|
4890
4924
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: { ok: false, error: "VAULT_PATH_INVALID", detail: `raw/transcripts/ not found: ${dir}` } };
|
|
4891
4925
|
}
|
|
4892
4926
|
const transcripts = [];
|
|
4893
4927
|
for (const entry of entries) {
|
|
4894
4928
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
4895
|
-
const filePath =
|
|
4896
|
-
const content = await
|
|
4929
|
+
const filePath = join23(dir, entry.name);
|
|
4930
|
+
const content = await readFile9(filePath, "utf8");
|
|
4897
4931
|
const fm = extractFrontmatter(content);
|
|
4898
4932
|
if (!fm.ok) continue;
|
|
4899
4933
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -4910,8 +4944,8 @@ async function runTranscripts(input) {
|
|
|
4910
4944
|
}
|
|
4911
4945
|
|
|
4912
4946
|
// src/commands/claims-audit.ts
|
|
4913
|
-
import { readFile as
|
|
4914
|
-
import { join as
|
|
4947
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
4948
|
+
import { join as join24 } from "path";
|
|
4915
4949
|
function safeProjectPresentation(raw) {
|
|
4916
4950
|
if (safeDiagnosticValue(raw) === REDACTED_MALFORMED_REFERENCE) {
|
|
4917
4951
|
return REDACTED_MALFORMED_REFERENCE;
|
|
@@ -4951,9 +4985,9 @@ async function runClaimsAudit(input) {
|
|
|
4951
4985
|
return !input.project || slugByDir.get(relDir) === input.project;
|
|
4952
4986
|
});
|
|
4953
4987
|
const claimSourceResults = await mapWithConcurrency(scopedSpecDirs, vaultIoConcurrency(), async (relDir) => {
|
|
4954
|
-
const specPath =
|
|
4988
|
+
const specPath = join24(input.vault, relDir, "spec.md");
|
|
4955
4989
|
try {
|
|
4956
|
-
const specText = await
|
|
4990
|
+
const specText = await readFile10(specPath, "utf8");
|
|
4957
4991
|
const fm = extractFrontmatter(specText);
|
|
4958
4992
|
if (!fm.ok) return null;
|
|
4959
4993
|
return {
|
|
@@ -5047,10 +5081,10 @@ async function runClaimsAudit(input) {
|
|
|
5047
5081
|
}
|
|
5048
5082
|
|
|
5049
5083
|
// src/commands/compound.ts
|
|
5050
|
-
import { writeFile as
|
|
5051
|
-
import { join as
|
|
5084
|
+
import { writeFile as writeFile5, mkdir as mkdir7, readdir as readdir5, unlink as unlink3 } from "fs/promises";
|
|
5085
|
+
import { join as join25 } from "path";
|
|
5052
5086
|
import { existsSync as existsSync9 } from "fs";
|
|
5053
|
-
import { readFile as
|
|
5087
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
5054
5088
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
5055
5089
|
var FIELD_RE = {
|
|
5056
5090
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -5148,17 +5182,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
5148
5182
|
};
|
|
5149
5183
|
}
|
|
5150
5184
|
async function runCompound(input) {
|
|
5151
|
-
const logPath =
|
|
5185
|
+
const logPath = join25(input.vault, "log.md");
|
|
5152
5186
|
let logText;
|
|
5153
5187
|
try {
|
|
5154
|
-
logText = await
|
|
5188
|
+
logText = await readFile11(logPath, "utf8");
|
|
5155
5189
|
} catch {
|
|
5156
5190
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
5157
5191
|
}
|
|
5158
5192
|
const entries = parseRetroEntries(logText);
|
|
5159
5193
|
const promoted = [];
|
|
5160
5194
|
const skipped = [];
|
|
5161
|
-
const compoundDir =
|
|
5195
|
+
const compoundDir = join25(input.vault, "projects", input.project, "compound");
|
|
5162
5196
|
for (const entry of entries) {
|
|
5163
5197
|
const generalizeValue = entry.generalize.trim();
|
|
5164
5198
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -5166,7 +5200,7 @@ async function runCompound(input) {
|
|
|
5166
5200
|
continue;
|
|
5167
5201
|
}
|
|
5168
5202
|
const slug = slugify(entry.cycleName);
|
|
5169
|
-
const compoundPath =
|
|
5203
|
+
const compoundPath = join25(compoundDir, `${slug}.md`);
|
|
5170
5204
|
if (existsSync9(compoundPath)) {
|
|
5171
5205
|
skipped.push(entry.date);
|
|
5172
5206
|
continue;
|
|
@@ -5206,9 +5240,9 @@ async function runCompound(input) {
|
|
|
5206
5240
|
const content = frontmatter + "\n" + body;
|
|
5207
5241
|
if (!input.dryRun) {
|
|
5208
5242
|
if (!existsSync9(compoundDir)) {
|
|
5209
|
-
await
|
|
5243
|
+
await mkdir7(compoundDir, { recursive: true });
|
|
5210
5244
|
}
|
|
5211
|
-
await
|
|
5245
|
+
await writeFile5(compoundPath, content, "utf8");
|
|
5212
5246
|
}
|
|
5213
5247
|
promoted.push(`${slug}.md`);
|
|
5214
5248
|
}
|
|
@@ -5227,7 +5261,7 @@ async function runCompound(input) {
|
|
|
5227
5261
|
};
|
|
5228
5262
|
}
|
|
5229
5263
|
async function runCompoundDelete(input) {
|
|
5230
|
-
const projectDir =
|
|
5264
|
+
const projectDir = join25(input.vault, "projects", input.project);
|
|
5231
5265
|
if (!existsSync9(projectDir)) {
|
|
5232
5266
|
return {
|
|
5233
5267
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
@@ -5235,7 +5269,7 @@ async function runCompoundDelete(input) {
|
|
|
5235
5269
|
};
|
|
5236
5270
|
}
|
|
5237
5271
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
5238
|
-
const compoundPath =
|
|
5272
|
+
const compoundPath = join25(projectDir, "compound", `${entryName}.md`);
|
|
5239
5273
|
if (!existsSync9(compoundPath)) {
|
|
5240
5274
|
return {
|
|
5241
5275
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -5269,7 +5303,7 @@ knowledge.md regenerated`
|
|
|
5269
5303
|
};
|
|
5270
5304
|
}
|
|
5271
5305
|
async function runCompoundList(input) {
|
|
5272
|
-
const compoundDir =
|
|
5306
|
+
const compoundDir = join25(input.vault, "projects", input.project, "compound");
|
|
5273
5307
|
if (!existsSync9(compoundDir)) {
|
|
5274
5308
|
return {
|
|
5275
5309
|
exitCode: ExitCode.OK,
|
|
@@ -5284,7 +5318,7 @@ no compound directory found`
|
|
|
5284
5318
|
}
|
|
5285
5319
|
let dirents;
|
|
5286
5320
|
try {
|
|
5287
|
-
dirents = await
|
|
5321
|
+
dirents = await readdir5(compoundDir, { withFileTypes: true });
|
|
5288
5322
|
} catch {
|
|
5289
5323
|
return {
|
|
5290
5324
|
exitCode: ExitCode.OK,
|
|
@@ -5300,10 +5334,10 @@ could not read compound directory`
|
|
|
5300
5334
|
const entries = [];
|
|
5301
5335
|
for (const dirent of dirents) {
|
|
5302
5336
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
5303
|
-
const filePath =
|
|
5337
|
+
const filePath = join25(compoundDir, dirent.name);
|
|
5304
5338
|
let text;
|
|
5305
5339
|
try {
|
|
5306
|
-
text = await
|
|
5340
|
+
text = await readFile11(filePath, "utf8");
|
|
5307
5341
|
} catch {
|
|
5308
5342
|
continue;
|
|
5309
5343
|
}
|
|
@@ -5332,8 +5366,8 @@ no compound entries found`;
|
|
|
5332
5366
|
}
|
|
5333
5367
|
|
|
5334
5368
|
// src/commands/session-brief.ts
|
|
5335
|
-
import { mkdir as
|
|
5336
|
-
import { join as
|
|
5369
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile6 } from "fs/promises";
|
|
5370
|
+
import { join as join26, relative, sep } from "path";
|
|
5337
5371
|
var MAX_WORDS = 900;
|
|
5338
5372
|
async function runSessionBrief(input) {
|
|
5339
5373
|
const { root: scanRoot } = resolveReadOnlyVaultRoot(input.vault);
|
|
@@ -5447,7 +5481,7 @@ async function resolveProject(input) {
|
|
|
5447
5481
|
const envProject = input.env?.SKILLWIKI_PROJECT;
|
|
5448
5482
|
if (envProject) return envProject;
|
|
5449
5483
|
const cwd = input.cwd ?? process.cwd();
|
|
5450
|
-
const projectDotenv = await readProjectSlug(
|
|
5484
|
+
const projectDotenv = await readProjectSlug(join26(cwd, ".skillwiki", ".env"));
|
|
5451
5485
|
if (projectDotenv) return projectDotenv;
|
|
5452
5486
|
const inferred = inferProjectFromPath(input.vault, cwd);
|
|
5453
5487
|
if (inferred) return inferred;
|
|
@@ -5456,7 +5490,7 @@ async function resolveProject(input) {
|
|
|
5456
5490
|
async function readProjectSlug(file) {
|
|
5457
5491
|
let text;
|
|
5458
5492
|
try {
|
|
5459
|
-
text = await
|
|
5493
|
+
text = await readFile12(file, "utf8");
|
|
5460
5494
|
} catch {
|
|
5461
5495
|
return void 0;
|
|
5462
5496
|
}
|
|
@@ -5547,7 +5581,7 @@ async function loadTrendPages(typedPages, pathPattern) {
|
|
|
5547
5581
|
return out;
|
|
5548
5582
|
}
|
|
5549
5583
|
async function loadSessionPins(vault, project) {
|
|
5550
|
-
const text = await readIfExists(
|
|
5584
|
+
const text = await readIfExists(join26(vault, "meta", "session-pins.md"));
|
|
5551
5585
|
if (!text) return [];
|
|
5552
5586
|
const fm = extractFrontmatter(text);
|
|
5553
5587
|
if (!fm.ok) return [];
|
|
@@ -5693,7 +5727,7 @@ function satelliteHealthWarning(health) {
|
|
|
5693
5727
|
return null;
|
|
5694
5728
|
}
|
|
5695
5729
|
async function loadHealthWarnings(vault) {
|
|
5696
|
-
const text = await readIfExists(
|
|
5730
|
+
const text = await readIfExists(join26(vault, ".skillwiki", "health.json"));
|
|
5697
5731
|
if (!text) return [];
|
|
5698
5732
|
try {
|
|
5699
5733
|
const parsed = JSON.parse(text);
|
|
@@ -5706,7 +5740,7 @@ async function loadHealthWarnings(vault) {
|
|
|
5706
5740
|
}
|
|
5707
5741
|
}
|
|
5708
5742
|
async function loadMemoryTopics(vault, project) {
|
|
5709
|
-
const text = await readIfExists(
|
|
5743
|
+
const text = await readIfExists(join26(vault, ".skillwiki", "memory", project, "topics.json"));
|
|
5710
5744
|
if (!text) return [];
|
|
5711
5745
|
try {
|
|
5712
5746
|
const parsed = JSON.parse(text);
|
|
@@ -5731,18 +5765,18 @@ async function loadMemoryTopics(vault, project) {
|
|
|
5731
5765
|
}
|
|
5732
5766
|
}
|
|
5733
5767
|
async function writeBriefArtifacts(vault, input) {
|
|
5734
|
-
const metaPath =
|
|
5735
|
-
const cacheMdPath =
|
|
5736
|
-
const cacheJsonPath =
|
|
5737
|
-
await
|
|
5738
|
-
await
|
|
5768
|
+
const metaPath = join26(vault, "meta", "latest-session-brief.md");
|
|
5769
|
+
const cacheMdPath = join26(vault, ".skillwiki", "session-brief.md");
|
|
5770
|
+
const cacheJsonPath = join26(vault, ".skillwiki", "session-brief.json");
|
|
5771
|
+
await mkdir8(join26(vault, "meta"), { recursive: true });
|
|
5772
|
+
await mkdir8(join26(vault, ".skillwiki"), { recursive: true });
|
|
5739
5773
|
const committed = renderCommittedBrief(input);
|
|
5740
5774
|
const previousComparable = comparableBrief(await readIfExists(metaPath));
|
|
5741
5775
|
const nextComparable = comparableBrief(committed);
|
|
5742
5776
|
const materialChange = previousComparable !== nextComparable;
|
|
5743
|
-
await
|
|
5744
|
-
await
|
|
5745
|
-
await
|
|
5777
|
+
await writeFile6(metaPath, committed, "utf8");
|
|
5778
|
+
await writeFile6(cacheMdPath, input.brief, "utf8");
|
|
5779
|
+
await writeFile6(cacheJsonPath, `${JSON.stringify({
|
|
5746
5780
|
project: input.project,
|
|
5747
5781
|
brief: input.brief,
|
|
5748
5782
|
word_count: input.wordCount,
|
|
@@ -5808,7 +5842,7 @@ function renderCommittedBrief(input) {
|
|
|
5808
5842
|
].filter((line) => line !== "").join("\n");
|
|
5809
5843
|
}
|
|
5810
5844
|
async function rebuildRootIndexProjection(vault) {
|
|
5811
|
-
const before = await readIfExists(
|
|
5845
|
+
const before = await readIfExists(join26(vault, "index.md"));
|
|
5812
5846
|
if (!before) return false;
|
|
5813
5847
|
const projection = await renderRootIndex({ vault, currentText: before });
|
|
5814
5848
|
if (!projection.ok) return false;
|
|
@@ -5818,7 +5852,7 @@ async function rebuildRootIndexProjection(vault) {
|
|
|
5818
5852
|
}
|
|
5819
5853
|
async function readIfExists(path) {
|
|
5820
5854
|
try {
|
|
5821
|
-
return await
|
|
5855
|
+
return await readFile12(path, "utf8");
|
|
5822
5856
|
} catch {
|
|
5823
5857
|
return "";
|
|
5824
5858
|
}
|
|
@@ -5860,14 +5894,14 @@ function dateFromPath(path) {
|
|
|
5860
5894
|
}
|
|
5861
5895
|
|
|
5862
5896
|
// src/commands/ingest.ts
|
|
5863
|
-
import { readFile as
|
|
5864
|
-
import { join as
|
|
5897
|
+
import { readFile as readFile15, open, unlink as unlink4, mkdir as mkdir10 } from "fs/promises";
|
|
5898
|
+
import { join as join31 } from "path";
|
|
5865
5899
|
import { createHash as createHash5 } from "crypto";
|
|
5866
5900
|
|
|
5867
5901
|
// src/commands/page-publish.ts
|
|
5868
5902
|
import { readFileSync as readFileSync13, realpathSync as realpathSync2 } from "fs";
|
|
5869
|
-
import { mkdir as
|
|
5870
|
-
import { dirname as dirname8, join as
|
|
5903
|
+
import { mkdir as mkdir9, readFile as readFile14 } from "fs/promises";
|
|
5904
|
+
import { dirname as dirname8, join as join30, resolve as resolve4 } from "path";
|
|
5871
5905
|
|
|
5872
5906
|
// src/utils/publication-approval.ts
|
|
5873
5907
|
import { createHash as createHash4 } from "crypto";
|
|
@@ -6095,8 +6129,8 @@ function redactApprovalTokens(text) {
|
|
|
6095
6129
|
}
|
|
6096
6130
|
|
|
6097
6131
|
// src/publish/pipeline.ts
|
|
6098
|
-
import { readFile as
|
|
6099
|
-
import { dirname as dirname7, join as
|
|
6132
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
6133
|
+
import { dirname as dirname7, join as join29, resolve as resolve3 } from "path";
|
|
6100
6134
|
|
|
6101
6135
|
// src/utils/publication-operation-journal.ts
|
|
6102
6136
|
import {
|
|
@@ -6109,7 +6143,7 @@ import {
|
|
|
6109
6143
|
unlinkSync,
|
|
6110
6144
|
writeFileSync as writeFileSync5
|
|
6111
6145
|
} from "fs";
|
|
6112
|
-
import { dirname as dirname6, join as
|
|
6146
|
+
import { dirname as dirname6, join as join27 } from "path";
|
|
6113
6147
|
var PUBLICATION_JOURNAL_SCHEMA = "skillwiki-publication-operation-v1";
|
|
6114
6148
|
var PHASE_ORDER2 = [
|
|
6115
6149
|
"locked",
|
|
@@ -6138,16 +6172,16 @@ function vaultIdentity(vaultPath) {
|
|
|
6138
6172
|
return sha256Hex(real).slice(0, 16);
|
|
6139
6173
|
}
|
|
6140
6174
|
function publicationJournalRoot(home = resolveSkillwikiHome()) {
|
|
6141
|
-
return
|
|
6175
|
+
return join27(resolveSkillwikiHome(home), ".skillwiki", "publication-operations");
|
|
6142
6176
|
}
|
|
6143
6177
|
function resolveJournalPaths(vaultPath, operationId2, home = resolveSkillwikiHome()) {
|
|
6144
6178
|
const identity = vaultIdentity(vaultPath);
|
|
6145
6179
|
const rootDir = publicationJournalRoot(home);
|
|
6146
|
-
const vaultDir =
|
|
6180
|
+
const vaultDir = join27(rootDir, identity);
|
|
6147
6181
|
return {
|
|
6148
6182
|
rootDir,
|
|
6149
6183
|
vaultDir,
|
|
6150
|
-
journalPath:
|
|
6184
|
+
journalPath: join27(vaultDir, `${operationId2}.json`)
|
|
6151
6185
|
};
|
|
6152
6186
|
}
|
|
6153
6187
|
function ensurePrivateDir(dir) {
|
|
@@ -6368,7 +6402,7 @@ function completePublicationJournal(input) {
|
|
|
6368
6402
|
}
|
|
6369
6403
|
|
|
6370
6404
|
// src/publish/hold-gates.ts
|
|
6371
|
-
import { join as
|
|
6405
|
+
import { join as join28 } from "path";
|
|
6372
6406
|
function evaluateSchemaGate(content, target, publisherKind) {
|
|
6373
6407
|
const frontmatter = extractFrontmatter(content);
|
|
6374
6408
|
if (!frontmatter.ok) return false;
|
|
@@ -6404,7 +6438,7 @@ function evaluateSchemaGate(content, target, publisherKind) {
|
|
|
6404
6438
|
return false;
|
|
6405
6439
|
}
|
|
6406
6440
|
function evaluateBrokenWikilinkGate(body, scan, ownTarget) {
|
|
6407
|
-
const draftPage = { absPath:
|
|
6441
|
+
const draftPage = { absPath: join28(scan.root, ownTarget), relPath: ownTarget };
|
|
6408
6442
|
const resolver = buildWikilinkResolver([...scan.allMarkdown, draftPage]);
|
|
6409
6443
|
const wikilinks = extractBodyWikilinks(body);
|
|
6410
6444
|
for (const link of wikilinks) {
|
|
@@ -6591,7 +6625,7 @@ function phaseAtLeast(current, needed) {
|
|
|
6591
6625
|
}
|
|
6592
6626
|
async function readPageChanged(targetPath, content) {
|
|
6593
6627
|
try {
|
|
6594
|
-
return ok(await
|
|
6628
|
+
return ok(await readFile13(targetPath, "utf8") !== content);
|
|
6595
6629
|
} catch (error) {
|
|
6596
6630
|
if (error.code === "ENOENT") return ok(true);
|
|
6597
6631
|
return err("WRITE_FAILED", { path: targetPath, message: String(error) });
|
|
@@ -6599,7 +6633,7 @@ async function readPageChanged(targetPath, content) {
|
|
|
6599
6633
|
}
|
|
6600
6634
|
async function readPriorTargetSha(targetPath) {
|
|
6601
6635
|
try {
|
|
6602
|
-
const text = await
|
|
6636
|
+
const text = await readFile13(targetPath, "utf8");
|
|
6603
6637
|
return ok(sha256Hex(text));
|
|
6604
6638
|
} catch (error) {
|
|
6605
6639
|
if (error.code === "ENOENT") return ok("absent");
|
|
@@ -6632,10 +6666,10 @@ function successReceipt(strategy, prepared, details) {
|
|
|
6632
6666
|
};
|
|
6633
6667
|
}
|
|
6634
6668
|
async function runPipelinePreview(strategy, prepared, vault) {
|
|
6635
|
-
const schemaPath =
|
|
6669
|
+
const schemaPath = join29(vault, "SCHEMA.md");
|
|
6636
6670
|
let schemaText;
|
|
6637
6671
|
try {
|
|
6638
|
-
schemaText = await
|
|
6672
|
+
schemaText = await readFile13(schemaPath, "utf8");
|
|
6639
6673
|
} catch (error) {
|
|
6640
6674
|
const result = err("FILE_NOT_FOUND", { path: schemaPath, message: String(error) });
|
|
6641
6675
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -6659,10 +6693,10 @@ async function runPipelinePreview(strategy, prepared, vault) {
|
|
|
6659
6693
|
if (!indices.ok) {
|
|
6660
6694
|
return { exitCode: indices.exitCode, result: indices.result };
|
|
6661
6695
|
}
|
|
6662
|
-
const logPath =
|
|
6696
|
+
const logPath = join29(vault, "log.md");
|
|
6663
6697
|
let logAppended;
|
|
6664
6698
|
try {
|
|
6665
|
-
const logText = await
|
|
6699
|
+
const logText = await readFile13(logPath, "utf8");
|
|
6666
6700
|
const marker = strategy.publisherKind === "page" ? `<!-- skillwiki-page-publish:${prepared.operationId} -->` : `<!-- skillwiki-project-page-publish:${prepared.operationId} -->`;
|
|
6667
6701
|
logAppended = !logText.includes(marker);
|
|
6668
6702
|
} catch (error) {
|
|
@@ -6753,10 +6787,10 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6753
6787
|
return { ok: false, exitCode: ExitCode.WRITE_FAILED, stage: "journal", state, cause: journalHook };
|
|
6754
6788
|
}
|
|
6755
6789
|
}
|
|
6756
|
-
const schemaPath =
|
|
6790
|
+
const schemaPath = join29(vault, "SCHEMA.md");
|
|
6757
6791
|
let schemaText;
|
|
6758
6792
|
try {
|
|
6759
|
-
schemaText = await
|
|
6793
|
+
schemaText = await readFile13(schemaPath, "utf8");
|
|
6760
6794
|
} catch (error) {
|
|
6761
6795
|
return {
|
|
6762
6796
|
ok: false,
|
|
@@ -6805,8 +6839,8 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6805
6839
|
if (!strategy.usesJournal || !phaseAtLeast(currentPhase, "page")) {
|
|
6806
6840
|
if (strategy.publisherKind === "project-page") {
|
|
6807
6841
|
try {
|
|
6808
|
-
const { mkdir:
|
|
6809
|
-
await
|
|
6842
|
+
const { mkdir: mkdir13 } = await import("fs/promises");
|
|
6843
|
+
await mkdir13(dirname7(prepared.targetPath), { recursive: true });
|
|
6810
6844
|
} catch (error) {
|
|
6811
6845
|
return {
|
|
6812
6846
|
ok: false,
|
|
@@ -6854,8 +6888,8 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6854
6888
|
let visibleSchema;
|
|
6855
6889
|
try {
|
|
6856
6890
|
[visible, visibleSchema] = await Promise.all([
|
|
6857
|
-
|
|
6858
|
-
|
|
6891
|
+
readFile13(prepared.targetPath, "utf8"),
|
|
6892
|
+
readFile13(schemaPath, "utf8")
|
|
6859
6893
|
]);
|
|
6860
6894
|
} catch (error) {
|
|
6861
6895
|
return {
|
|
@@ -7247,16 +7281,16 @@ async function refreshProjectIndexForTarget(vault, projectSlugs, today) {
|
|
|
7247
7281
|
for (const slug of projectSlugs) {
|
|
7248
7282
|
const rendered = await renderProjectIndex(vault, slug, { today });
|
|
7249
7283
|
if (!rendered.ok) return rendered;
|
|
7250
|
-
const indexPath =
|
|
7284
|
+
const indexPath = join30(vault, rendered.data.index_path);
|
|
7251
7285
|
try {
|
|
7252
|
-
await
|
|
7286
|
+
await mkdir9(dirname8(indexPath), { recursive: true });
|
|
7253
7287
|
} catch (error) {
|
|
7254
7288
|
return err("WRITE_FAILED", { path: indexPath, message: String(error) });
|
|
7255
7289
|
}
|
|
7256
7290
|
const written = await atomicWriteText(indexPath, rendered.data.text);
|
|
7257
7291
|
if (!written.ok) return written;
|
|
7258
7292
|
try {
|
|
7259
|
-
const visible = await
|
|
7293
|
+
const visible = await readFile14(indexPath, "utf8");
|
|
7260
7294
|
if (visible !== rendered.data.text) {
|
|
7261
7295
|
return err("WRITE_FAILED", { path: indexPath, message: "project index verification failed" });
|
|
7262
7296
|
}
|
|
@@ -7357,7 +7391,7 @@ function preparePagePublicationFromContent(input) {
|
|
|
7357
7391
|
async function preparePagePublication(input) {
|
|
7358
7392
|
let content;
|
|
7359
7393
|
try {
|
|
7360
|
-
content = await
|
|
7394
|
+
content = await readFile14(input.draftPath, "utf8");
|
|
7361
7395
|
} catch (error) {
|
|
7362
7396
|
return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
|
|
7363
7397
|
}
|
|
@@ -7445,10 +7479,10 @@ var PAGE_PUBLISH_STRATEGY = {
|
|
|
7445
7479
|
return void 0;
|
|
7446
7480
|
},
|
|
7447
7481
|
async previewIndices(prepared, vault, pageChanged) {
|
|
7448
|
-
const indexPath =
|
|
7482
|
+
const indexPath = join30(vault, "index.md");
|
|
7449
7483
|
let indexText;
|
|
7450
7484
|
try {
|
|
7451
|
-
indexText = await
|
|
7485
|
+
indexText = await readFile14(indexPath, "utf8");
|
|
7452
7486
|
} catch (error) {
|
|
7453
7487
|
const result = err("FILE_NOT_FOUND", { path: indexPath, message: String(error) });
|
|
7454
7488
|
return { ok: false, exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -7466,7 +7500,7 @@ var PAGE_PUBLISH_STRATEGY = {
|
|
|
7466
7500
|
if (!renderedProject.ok) {
|
|
7467
7501
|
return { ok: false, exitCode: errorExitCode(renderedProject.error), result: renderedProject };
|
|
7468
7502
|
}
|
|
7469
|
-
const projectIndexPath =
|
|
7503
|
+
const projectIndexPath = join30(vault, renderedProject.data.index_path);
|
|
7470
7504
|
const projectIndexChanged = await readPageChanged(projectIndexPath, renderedProject.data.text);
|
|
7471
7505
|
if (!projectIndexChanged.ok) {
|
|
7472
7506
|
return { ok: false, exitCode: errorExitCode(projectIndexChanged.error), result: projectIndexChanged };
|
|
@@ -7654,7 +7688,7 @@ function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance)
|
|
|
7654
7688
|
}
|
|
7655
7689
|
async function resolveRawCapture(input) {
|
|
7656
7690
|
try {
|
|
7657
|
-
const existing = await
|
|
7691
|
+
const existing = await readFile15(input.path, "utf8");
|
|
7658
7692
|
const frontmatter = extractFrontmatter(existing);
|
|
7659
7693
|
if (!frontmatter.ok) {
|
|
7660
7694
|
return err("INGEST_VALIDATION_FAILED", {
|
|
@@ -7793,7 +7827,7 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7793
7827
|
sourceContent = fetchResult.data.body;
|
|
7794
7828
|
} else {
|
|
7795
7829
|
try {
|
|
7796
|
-
sourceContent = await
|
|
7830
|
+
sourceContent = await readFile15(input.source, "utf8");
|
|
7797
7831
|
} catch {
|
|
7798
7832
|
return {
|
|
7799
7833
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -7818,7 +7852,7 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7818
7852
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
7819
7853
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
7820
7854
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
7821
|
-
const rawAbsPath =
|
|
7855
|
+
const rawAbsPath = join31(input.vault, rawRelPath);
|
|
7822
7856
|
const identity = assessSourceIdentity({
|
|
7823
7857
|
rawPath: rawRelPath,
|
|
7824
7858
|
sourceUrl: sourceUrl ?? void 0,
|
|
@@ -7862,11 +7896,11 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7862
7896
|
);
|
|
7863
7897
|
if (!input.dryRun) {
|
|
7864
7898
|
try {
|
|
7865
|
-
await
|
|
7899
|
+
await mkdir10(join31(input.vault, typedDir), { recursive: true });
|
|
7866
7900
|
} catch (error) {
|
|
7867
7901
|
return {
|
|
7868
7902
|
exitCode: ExitCode.WRITE_FAILED,
|
|
7869
|
-
result: err("WRITE_FAILED", { path:
|
|
7903
|
+
result: err("WRITE_FAILED", { path: join31(input.vault, typedDir), message: String(error) })
|
|
7870
7904
|
};
|
|
7871
7905
|
}
|
|
7872
7906
|
}
|
|
@@ -7920,11 +7954,11 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7920
7954
|
preflight: pagePublishDeps.preflight,
|
|
7921
7955
|
mutate: async (receipt) => {
|
|
7922
7956
|
try {
|
|
7923
|
-
await
|
|
7957
|
+
await mkdir10(join31(input.vault, "raw", "articles"), { recursive: true });
|
|
7924
7958
|
} catch (error) {
|
|
7925
7959
|
return {
|
|
7926
7960
|
exitCode: ExitCode.WRITE_FAILED,
|
|
7927
|
-
result: err("WRITE_FAILED", { path:
|
|
7961
|
+
result: err("WRITE_FAILED", { path: join31(input.vault, "raw", "articles"), message: String(error) })
|
|
7928
7962
|
};
|
|
7929
7963
|
}
|
|
7930
7964
|
const rawWrite = await writeResolvedRaw({
|
|
@@ -8160,8 +8194,8 @@ ${body}`;
|
|
|
8160
8194
|
}
|
|
8161
8195
|
|
|
8162
8196
|
// src/commands/tag-reconcile.ts
|
|
8163
|
-
import { readFile as
|
|
8164
|
-
import { join as
|
|
8197
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
8198
|
+
import { join as join32, posix } from "path";
|
|
8165
8199
|
var TYPED_TARGET_RE = /^(entities|concepts|comparisons|queries|meta)\/[a-z0-9][a-z0-9./_-]*\.md$/;
|
|
8166
8200
|
function errorExitCode2(error) {
|
|
8167
8201
|
switch (error) {
|
|
@@ -8204,7 +8238,7 @@ function asTagArray(frontmatter, path) {
|
|
|
8204
8238
|
async function readTagsFromFile(path) {
|
|
8205
8239
|
let text;
|
|
8206
8240
|
try {
|
|
8207
|
-
text = await
|
|
8241
|
+
text = await readFile16(path, "utf8");
|
|
8208
8242
|
} catch (error) {
|
|
8209
8243
|
if (error.code === "ENOENT") {
|
|
8210
8244
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path }) };
|
|
@@ -8225,7 +8259,7 @@ async function resolveRequestedTags(input, page) {
|
|
|
8225
8259
|
result: err("INVALID_FRONTMATTER", { message: "explicit tags must be an array of strings" })
|
|
8226
8260
|
};
|
|
8227
8261
|
}
|
|
8228
|
-
const source = input.from ?? (explicit.length === 0 ?
|
|
8262
|
+
const source = input.from ?? (explicit.length === 0 ? join32(input.vault, page) : void 0);
|
|
8229
8263
|
if (!source) return { exitCode: ExitCode.OK, result: ok({ tags: [...new Set(explicit)].sort() }) };
|
|
8230
8264
|
const sourced = await readTagsFromFile(source);
|
|
8231
8265
|
if (!sourced.result.ok) return { exitCode: sourced.exitCode, result: sourced.result };
|
|
@@ -8236,7 +8270,7 @@ async function resolveRequestedTags(input, page) {
|
|
|
8236
8270
|
}
|
|
8237
8271
|
async function readSchema(schemaPath) {
|
|
8238
8272
|
try {
|
|
8239
|
-
return { exitCode: ExitCode.OK, result: ok(await
|
|
8273
|
+
return { exitCode: ExitCode.OK, result: ok(await readFile16(schemaPath, "utf8")) };
|
|
8240
8274
|
} catch (error) {
|
|
8241
8275
|
if (error.code === "ENOENT") {
|
|
8242
8276
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: schemaPath }) };
|
|
@@ -8264,7 +8298,7 @@ function previewResult(page, tags, reconciled, dryRun, filesChanged) {
|
|
|
8264
8298
|
};
|
|
8265
8299
|
}
|
|
8266
8300
|
async function reconcileTagsWhileLocked(input, page, tags, comment) {
|
|
8267
|
-
const schemaPath =
|
|
8301
|
+
const schemaPath = join32(input.vault, "SCHEMA.md");
|
|
8268
8302
|
const current = await readSchema(schemaPath);
|
|
8269
8303
|
if (!current.result.ok) return { exitCode: current.exitCode, result: current.result };
|
|
8270
8304
|
const next = reconcileTaxonomyDocument(current.result.data, { tags, comment });
|
|
@@ -8275,7 +8309,7 @@ async function reconcileTagsWhileLocked(input, page, tags, comment) {
|
|
|
8275
8309
|
}
|
|
8276
8310
|
let verifiedText;
|
|
8277
8311
|
try {
|
|
8278
|
-
verifiedText = await
|
|
8312
|
+
verifiedText = await readFile16(schemaPath, "utf8");
|
|
8279
8313
|
} catch (error) {
|
|
8280
8314
|
return {
|
|
8281
8315
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -8301,7 +8335,7 @@ async function runTagReconcile(input) {
|
|
|
8301
8335
|
const comment = taxonomyCommentForPage(page.data, date, input.reason);
|
|
8302
8336
|
if (!comment.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: comment };
|
|
8303
8337
|
if (!input.write) {
|
|
8304
|
-
const schema = await readSchema(
|
|
8338
|
+
const schema = await readSchema(join32(input.vault, "SCHEMA.md"));
|
|
8305
8339
|
if (!schema.result.ok) return { exitCode: schema.exitCode, result: schema.result };
|
|
8306
8340
|
const preview = reconcileTaxonomyDocument(schema.result.data, { tags, comment: comment.data });
|
|
8307
8341
|
return previewResult(page.data, tags, preview, true, []);
|
|
@@ -8354,8 +8388,8 @@ async function runTagReconcile(input) {
|
|
|
8354
8388
|
|
|
8355
8389
|
// src/commands/project-page-publish.ts
|
|
8356
8390
|
import { realpathSync as realpathSync4 } from "fs";
|
|
8357
|
-
import { mkdir as
|
|
8358
|
-
import { dirname as dirname10, join as
|
|
8391
|
+
import { mkdir as mkdir11, readFile as readFile17 } from "fs/promises";
|
|
8392
|
+
import { dirname as dirname10, join as join33, resolve as resolve6 } from "path";
|
|
8359
8393
|
|
|
8360
8394
|
// src/utils/architecture-page.ts
|
|
8361
8395
|
import { lstatSync, realpathSync as realpathSync3 } from "fs";
|
|
@@ -8527,7 +8561,7 @@ function validateLogNote(logNote) {
|
|
|
8527
8561
|
async function prepareProjectPagePublication(input) {
|
|
8528
8562
|
let content;
|
|
8529
8563
|
try {
|
|
8530
|
-
content = await
|
|
8564
|
+
content = await readFile17(input.draftPath, "utf8");
|
|
8531
8565
|
} catch (error) {
|
|
8532
8566
|
return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
|
|
8533
8567
|
}
|
|
@@ -8660,9 +8694,9 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8660
8694
|
async updateIndicesLocked(prepared, vault, state, observe) {
|
|
8661
8695
|
const rendered = await renderProjectIndex(vault, prepared.page.project, { today: prepared.date });
|
|
8662
8696
|
if (!rendered.ok) return rendered;
|
|
8663
|
-
const knowledgePath =
|
|
8697
|
+
const knowledgePath = join33(vault, rendered.data.index_path);
|
|
8664
8698
|
try {
|
|
8665
|
-
await
|
|
8699
|
+
await mkdir11(dirname10(knowledgePath), { recursive: true });
|
|
8666
8700
|
} catch (error) {
|
|
8667
8701
|
return err("WRITE_FAILED", { message: String(error) });
|
|
8668
8702
|
}
|
|
@@ -8671,7 +8705,7 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8671
8705
|
state.projectIndexUpdated = knowledgeWrite.data.changed;
|
|
8672
8706
|
if (state.projectIndexUpdated) state.changed.add(rendered.data.index_path);
|
|
8673
8707
|
try {
|
|
8674
|
-
const visibleIndex = await
|
|
8708
|
+
const visibleIndex = await readFile17(knowledgePath, "utf8");
|
|
8675
8709
|
if (visibleIndex !== rendered.data.text) {
|
|
8676
8710
|
return err("WRITE_FAILED", { message: "project knowledge.md verification failed" });
|
|
8677
8711
|
}
|
|
@@ -8691,9 +8725,9 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8691
8725
|
}
|
|
8692
8726
|
let projectIndexChanged = true;
|
|
8693
8727
|
if (projectIndex.ok) {
|
|
8694
|
-
const indexPath =
|
|
8728
|
+
const indexPath = join33(vault, projectIndex.data.index_path);
|
|
8695
8729
|
try {
|
|
8696
|
-
const existing = await
|
|
8730
|
+
const existing = await readFile17(indexPath, "utf8");
|
|
8697
8731
|
projectIndexChanged = existing !== projectIndex.data.text || pageChanged;
|
|
8698
8732
|
} catch {
|
|
8699
8733
|
projectIndexChanged = true;
|
|
@@ -8853,7 +8887,7 @@ import { existsSync as existsSync11, writeFileSync as writeFileSync6, mkdirSync
|
|
|
8853
8887
|
import { createHash as createHash6 } from "crypto";
|
|
8854
8888
|
import { execSync as execSync3, spawn } from "child_process";
|
|
8855
8889
|
import { platform as platform2 } from "os";
|
|
8856
|
-
import { join as
|
|
8890
|
+
import { join as join34, resolve as resolvePath } from "path";
|
|
8857
8891
|
var MAINTENANCE_SCHEMA_VERSION = 1;
|
|
8858
8892
|
var MAINTENANCE_COMMAND = "snapshot-maintenance journal clear-stale";
|
|
8859
8893
|
var REPAIR_COMMAND = "snapshot-maintenance projection-conflict repair";
|
|
@@ -8918,7 +8952,7 @@ async function runSnapshotMaintenanceDryRun(input) {
|
|
|
8918
8952
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE"));
|
|
8919
8953
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
8920
8954
|
}
|
|
8921
|
-
if (!existsSync11(requested) || !existsSync11(
|
|
8955
|
+
if (!existsSync11(requested) || !existsSync11(join34(requested, ".git"))) {
|
|
8922
8956
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO"));
|
|
8923
8957
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
8924
8958
|
}
|
|
@@ -8998,14 +9032,14 @@ async function resolveLiveVault(input) {
|
|
|
8998
9032
|
return resolved.ok ? canonicalize(resolved.data.path) : void 0;
|
|
8999
9033
|
}
|
|
9000
9034
|
function snapshotMaintenanceAuditLogPath(home) {
|
|
9001
|
-
const stateDir = platform2() === "darwin" ?
|
|
9002
|
-
return
|
|
9035
|
+
const stateDir = platform2() === "darwin" ? join34(home, "Library", "Application Support", "vault-sync") : join34(home, ".local", "state", "vault-sync");
|
|
9036
|
+
return join34(stateDir, "snapshot-maintenance-audit.jsonl");
|
|
9003
9037
|
}
|
|
9004
9038
|
function defaultAuditSink(home) {
|
|
9005
9039
|
return (event) => {
|
|
9006
9040
|
try {
|
|
9007
9041
|
const logPath = snapshotMaintenanceAuditLogPath(home);
|
|
9008
|
-
mkdirSync4(
|
|
9042
|
+
mkdirSync4(join34(logPath, ".."), { recursive: true });
|
|
9009
9043
|
appendFileSync(logPath, JSON.stringify(event) + "\n", { encoding: "utf8" });
|
|
9010
9044
|
} catch {
|
|
9011
9045
|
}
|
|
@@ -9153,9 +9187,9 @@ function nonMergeSequencerActive(repo) {
|
|
|
9153
9187
|
const gitDir = git(repo, ["rev-parse", "--absolute-git-dir"]);
|
|
9154
9188
|
if (!gitDir) return false;
|
|
9155
9189
|
for (const m of ["CHERRY_PICK_HEAD", "REVERT_HEAD"]) {
|
|
9156
|
-
if (existsSync11(
|
|
9190
|
+
if (existsSync11(join34(gitDir, m))) return true;
|
|
9157
9191
|
}
|
|
9158
|
-
return existsSync11(
|
|
9192
|
+
return existsSync11(join34(gitDir, "rebase-merge")) || existsSync11(join34(gitDir, "rebase-apply"));
|
|
9159
9193
|
}
|
|
9160
9194
|
function extraUnmergedPaths(unmerged) {
|
|
9161
9195
|
const allow = new Set(PROJECTION_CONFLICT_ALLOWLIST);
|
|
@@ -9194,7 +9228,7 @@ async function authorizeRepairContext(input) {
|
|
|
9194
9228
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE", void 0, REPAIR_COMMAND));
|
|
9195
9229
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
9196
9230
|
}
|
|
9197
|
-
if (!existsSync11(requested) || !existsSync11(
|
|
9231
|
+
if (!existsSync11(requested) || !existsSync11(join34(requested, ".git"))) {
|
|
9198
9232
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO", void 0, REPAIR_COMMAND));
|
|
9199
9233
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
9200
9234
|
}
|
|
@@ -9295,8 +9329,8 @@ async function performProjectionRepair(input, plan, audit, now) {
|
|
|
9295
9329
|
return { exitCode: ExitCode.WRITE_FAILED, result: rendered };
|
|
9296
9330
|
}
|
|
9297
9331
|
try {
|
|
9298
|
-
writeFileSync6(
|
|
9299
|
-
writeFileSync6(
|
|
9332
|
+
writeFileSync6(join34(plan.snapshot_worktree, "index.md"), rendered.data.index);
|
|
9333
|
+
writeFileSync6(join34(plan.snapshot_worktree, "log.md"), rendered.data.log);
|
|
9300
9334
|
gitStrict(plan.snapshot_worktree, ["add", "--", "index.md", "log.md"]);
|
|
9301
9335
|
gitStrict(plan.snapshot_worktree, [
|
|
9302
9336
|
"commit",
|
|
@@ -9404,7 +9438,7 @@ function releaseSnapshotFlock(handle) {
|
|
|
9404
9438
|
|
|
9405
9439
|
// src/commands/backup.ts
|
|
9406
9440
|
import { statSync as statSync3, readdirSync as readdirSync2, readFileSync as readFileSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
9407
|
-
import { join as
|
|
9441
|
+
import { join as join35, relative as relative3, dirname as dirname11 } from "path";
|
|
9408
9442
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
9409
9443
|
|
|
9410
9444
|
// src/utils/s3-client.ts
|
|
@@ -9428,7 +9462,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
|
|
|
9428
9462
|
function* walkMarkdown(dir, base) {
|
|
9429
9463
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
9430
9464
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
9431
|
-
const full =
|
|
9465
|
+
const full = join35(dir, entry.name);
|
|
9432
9466
|
if (entry.isDirectory()) {
|
|
9433
9467
|
yield* walkMarkdown(full, base);
|
|
9434
9468
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -9451,7 +9485,7 @@ async function runBackupSync(input) {
|
|
|
9451
9485
|
let failed = 0;
|
|
9452
9486
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
9453
9487
|
for (const relPath of files) {
|
|
9454
|
-
const absPath =
|
|
9488
|
+
const absPath = join35(input.vault, relPath);
|
|
9455
9489
|
const localStat = statSync3(absPath);
|
|
9456
9490
|
let needsUpload = true;
|
|
9457
9491
|
try {
|
|
@@ -9527,7 +9561,7 @@ async function runBackupRestore(input) {
|
|
|
9527
9561
|
const objects = list.Contents ?? [];
|
|
9528
9562
|
for (const obj of objects) {
|
|
9529
9563
|
if (!obj.Key) continue;
|
|
9530
|
-
const localPath =
|
|
9564
|
+
const localPath = join35(target, obj.Key);
|
|
9531
9565
|
try {
|
|
9532
9566
|
const localStat = statSync3(localPath);
|
|
9533
9567
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -9574,8 +9608,8 @@ async function runBackupRestore(input) {
|
|
|
9574
9608
|
|
|
9575
9609
|
// src/commands/status.ts
|
|
9576
9610
|
import { existsSync as existsSync12, statSync as statSync4 } from "fs";
|
|
9577
|
-
import { readFile as
|
|
9578
|
-
import { join as
|
|
9611
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
9612
|
+
import { join as join36 } from "path";
|
|
9579
9613
|
var FUSE_NO_MIRROR_NOTE = "FUSE vault with no read mirror - status scan may be slow";
|
|
9580
9614
|
async function runStatus(input) {
|
|
9581
9615
|
if (!existsSync12(input.vault)) {
|
|
@@ -9614,7 +9648,7 @@ async function runStatus(input) {
|
|
|
9614
9648
|
const compound = scan.data.compound.length;
|
|
9615
9649
|
let schemaVersion = "v1";
|
|
9616
9650
|
try {
|
|
9617
|
-
const schemaContent = await
|
|
9651
|
+
const schemaContent = await readFile18(join36(scanRoot, "SCHEMA.md"), "utf8");
|
|
9618
9652
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
9619
9653
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
9620
9654
|
} catch {
|
|
@@ -9682,13 +9716,13 @@ async function runStatus(input) {
|
|
|
9682
9716
|
|
|
9683
9717
|
// src/commands/copy-status.ts
|
|
9684
9718
|
import { existsSync as existsSync13, realpathSync as realpathSync5 } from "fs";
|
|
9685
|
-
import { isAbsolute as isAbsolute2, join as
|
|
9719
|
+
import { isAbsolute as isAbsolute2, join as join38, relative as relative5, resolve as resolve8 } from "path";
|
|
9686
9720
|
|
|
9687
9721
|
// src/utils/fetch-projection.ts
|
|
9688
|
-
import { isAbsolute, join as
|
|
9722
|
+
import { isAbsolute, join as join37, resolve as resolve7 } from "path";
|
|
9689
9723
|
function inspectConfiguredFetchProjection(home) {
|
|
9690
9724
|
if (!home) return { configured: false };
|
|
9691
|
-
const config = readSkillWikiConfig(
|
|
9725
|
+
const config = readSkillWikiConfig(join37(home, ".skillwiki", ".env"));
|
|
9692
9726
|
const configured = config["vault_sync.fetch_projection"];
|
|
9693
9727
|
if (!configured || configured === "none") return { configured: false };
|
|
9694
9728
|
if (!isAbsolute(configured)) {
|
|
@@ -9699,7 +9733,7 @@ function inspectConfiguredFetchProjection(home) {
|
|
|
9699
9733
|
|
|
9700
9734
|
// src/utils/live-drift.ts
|
|
9701
9735
|
import { createHash as createHash7 } from "crypto";
|
|
9702
|
-
import { readFile as
|
|
9736
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
9703
9737
|
import { relative as relative4, sep as sep3 } from "path";
|
|
9704
9738
|
|
|
9705
9739
|
// src/utils/git-promotion.ts
|
|
@@ -9744,7 +9778,7 @@ async function measureAuthoritativeLiveDrift(liveVault, projection) {
|
|
|
9744
9778
|
const rel = relative4(liveVault, page.absPath).split(sep3).join("/");
|
|
9745
9779
|
if (!isGitPromotablePath(rel)) continue;
|
|
9746
9780
|
livePromotable.add(rel);
|
|
9747
|
-
const bytes = await
|
|
9781
|
+
const bytes = await readFile19(page.absPath);
|
|
9748
9782
|
if (projectionBlobs.get(rel) !== gitBlobOid(bytes)) content += 1;
|
|
9749
9783
|
}
|
|
9750
9784
|
for (const rel of projectionBlobs.keys()) {
|
|
@@ -9761,11 +9795,20 @@ async function measureAuthoritativeLiveDrift(liveVault, projection) {
|
|
|
9761
9795
|
|
|
9762
9796
|
// src/copy-status/copy-status.ts
|
|
9763
9797
|
function liveRecord(p) {
|
|
9798
|
+
if (p.writable === false) {
|
|
9799
|
+
return {
|
|
9800
|
+
state: "blocked",
|
|
9801
|
+
reachable: p.reachable,
|
|
9802
|
+
writable: false,
|
|
9803
|
+
blocked_reason: "s3_write_probe_failed",
|
|
9804
|
+
detail: p.detail ?? "S3 write probe failed"
|
|
9805
|
+
};
|
|
9806
|
+
}
|
|
9764
9807
|
if (p.unknown || p.reachable === void 0) {
|
|
9765
9808
|
return { state: "unknown", reachable: p.reachable, detail: p.detail ?? "S3/MCP unmeasured" };
|
|
9766
9809
|
}
|
|
9767
9810
|
if (p.reachable) {
|
|
9768
|
-
return { state: "ok", reachable: true, detail: p.detail ?? "S3 reachable" };
|
|
9811
|
+
return { state: "ok", reachable: true, ...p.writable === true ? { writable: true } : {}, detail: p.detail ?? "S3 reachable" };
|
|
9769
9812
|
}
|
|
9770
9813
|
return { state: "unknown", reachable: false, detail: p.detail ?? "S3 unreachable" };
|
|
9771
9814
|
}
|
|
@@ -9930,13 +9973,21 @@ function defaultCopyStatusDeps(input) {
|
|
|
9930
9973
|
const projectionWarning = selection.configured ? `configured fetch projection ignored for Git status; using live vault${projectionProblem ? ` (${projectionProblem})` : ""}` : void 0;
|
|
9931
9974
|
const invalidProjectionDetail = projectionProblem ?? "configured fetch projection is not a git repository";
|
|
9932
9975
|
const gitRootProblem = () => {
|
|
9933
|
-
if (!existsSync13(
|
|
9976
|
+
if (!existsSync13(join38(gitVault, ".git"))) {
|
|
9934
9977
|
return "vault is not a git repository";
|
|
9935
9978
|
}
|
|
9936
9979
|
return void 0;
|
|
9937
9980
|
};
|
|
9938
9981
|
return {
|
|
9939
9982
|
probeLive() {
|
|
9983
|
+
if (input.s3Writable === false) {
|
|
9984
|
+
return {
|
|
9985
|
+
reachable: input.s3Ok,
|
|
9986
|
+
writable: false,
|
|
9987
|
+
detail: `MCP S3 write probe failed${input.s3WritableError ? ` (${input.s3WritableError})` : ""}`
|
|
9988
|
+
};
|
|
9989
|
+
}
|
|
9990
|
+
if (input.s3Ok === true && input.s3Writable === true) return { reachable: true, writable: true, detail: "MCP S3 readable and writable" };
|
|
9940
9991
|
if (input.s3Ok === true) return { reachable: true, detail: "MCP S3 ok" };
|
|
9941
9992
|
if (input.s3Ok === false) return { reachable: false, detail: "MCP S3 not ok" };
|
|
9942
9993
|
const remote = resolveWikiS3Remote({ home: input.home });
|
|
@@ -10002,7 +10053,7 @@ function defaultCopyStatusDeps(input) {
|
|
|
10002
10053
|
detail: projectionProblem ?? "live drift requires a configured fetch projection"
|
|
10003
10054
|
};
|
|
10004
10055
|
}
|
|
10005
|
-
if (!existsSync13(
|
|
10056
|
+
if (!existsSync13(join38(configuredProjection, ".git"))) {
|
|
10006
10057
|
return {
|
|
10007
10058
|
unknown: true,
|
|
10008
10059
|
reason_code: "fetch_projection_invalid",
|
|
@@ -10022,8 +10073,8 @@ async function runCopyStatusCommand(input) {
|
|
|
10022
10073
|
}
|
|
10023
10074
|
|
|
10024
10075
|
// src/commands/seed.ts
|
|
10025
|
-
import { mkdir as
|
|
10026
|
-
import { join as
|
|
10076
|
+
import { mkdir as mkdir12, writeFile as writeFile7, stat as stat5 } from "fs/promises";
|
|
10077
|
+
import { join as join39 } from "path";
|
|
10027
10078
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10028
10079
|
var EXAMPLE_PAGES = {
|
|
10029
10080
|
"entities/example-project.md": `---
|
|
@@ -10092,30 +10143,30 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
10092
10143
|
`;
|
|
10093
10144
|
async function runSeed(input) {
|
|
10094
10145
|
try {
|
|
10095
|
-
await stat5(
|
|
10146
|
+
await stat5(join39(input.vault, "SCHEMA.md"));
|
|
10096
10147
|
} catch {
|
|
10097
10148
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
10098
10149
|
}
|
|
10099
10150
|
const created = [];
|
|
10100
10151
|
const skipped = [];
|
|
10101
10152
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
10102
|
-
const absPath =
|
|
10153
|
+
const absPath = join39(input.vault, relPath);
|
|
10103
10154
|
try {
|
|
10104
10155
|
await stat5(absPath);
|
|
10105
10156
|
skipped.push(relPath);
|
|
10106
10157
|
} catch {
|
|
10107
|
-
await
|
|
10108
|
-
await
|
|
10158
|
+
await mkdir12(join39(absPath, ".."), { recursive: true });
|
|
10159
|
+
await writeFile7(absPath, content, "utf8");
|
|
10109
10160
|
created.push(relPath);
|
|
10110
10161
|
}
|
|
10111
10162
|
}
|
|
10112
|
-
const rawPath =
|
|
10163
|
+
const rawPath = join39(input.vault, "raw", "articles", "example-source.md");
|
|
10113
10164
|
try {
|
|
10114
10165
|
await stat5(rawPath);
|
|
10115
10166
|
skipped.push("raw/articles/example-source.md");
|
|
10116
10167
|
} catch {
|
|
10117
|
-
await
|
|
10118
|
-
await
|
|
10168
|
+
await mkdir12(join39(rawPath, ".."), { recursive: true });
|
|
10169
|
+
await writeFile7(rawPath, EXAMPLE_RAW, "utf8");
|
|
10119
10170
|
created.push("raw/articles/example-source.md");
|
|
10120
10171
|
}
|
|
10121
10172
|
if (created.length > 0) {
|
|
@@ -10137,9 +10188,9 @@ async function runSeed(input) {
|
|
|
10137
10188
|
}
|
|
10138
10189
|
|
|
10139
10190
|
// src/commands/canvas.ts
|
|
10140
|
-
import { readFile as
|
|
10191
|
+
import { readFile as readFile20, writeFile as writeFile8 } from "fs/promises";
|
|
10141
10192
|
import { existsSync as existsSync14 } from "fs";
|
|
10142
|
-
import { join as
|
|
10193
|
+
import { join as join40 } from "path";
|
|
10143
10194
|
var NODE_WIDTH = 240;
|
|
10144
10195
|
var NODE_HEIGHT = 60;
|
|
10145
10196
|
var COLUMN_SPACING = 400;
|
|
@@ -10217,7 +10268,7 @@ function buildCanvasEdges(adjacency) {
|
|
|
10217
10268
|
return edges;
|
|
10218
10269
|
}
|
|
10219
10270
|
async function runCanvasGenerate(input) {
|
|
10220
|
-
const graphPath = input.graphPath ??
|
|
10271
|
+
const graphPath = input.graphPath ?? join40(input.vault, ".skillwiki", "graph.json");
|
|
10221
10272
|
if (!existsSync14(graphPath)) {
|
|
10222
10273
|
return {
|
|
10223
10274
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -10229,7 +10280,7 @@ async function runCanvasGenerate(input) {
|
|
|
10229
10280
|
}
|
|
10230
10281
|
let raw;
|
|
10231
10282
|
try {
|
|
10232
|
-
raw = await
|
|
10283
|
+
raw = await readFile20(graphPath, "utf8");
|
|
10233
10284
|
} catch (e) {
|
|
10234
10285
|
return {
|
|
10235
10286
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -10255,9 +10306,9 @@ async function runCanvasGenerate(input) {
|
|
|
10255
10306
|
const nodes = buildCanvasNodes(paths);
|
|
10256
10307
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
10257
10308
|
const canvas = { nodes, edges };
|
|
10258
|
-
const outPath =
|
|
10309
|
+
const outPath = join40(input.vault, "vault-graph.canvas");
|
|
10259
10310
|
try {
|
|
10260
|
-
await
|
|
10311
|
+
await writeFile8(outPath, JSON.stringify(canvas, null, 2));
|
|
10261
10312
|
} catch (e) {
|
|
10262
10313
|
return {
|
|
10263
10314
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -10400,22 +10451,22 @@ async function runSourceDisposition(input) {
|
|
|
10400
10451
|
|
|
10401
10452
|
// src/utils/source-disposal.ts
|
|
10402
10453
|
import { createHash as createHash8 } from "crypto";
|
|
10403
|
-
import { readFile as
|
|
10454
|
+
import { readFile as readFile22, unlink as unlink5 } from "fs/promises";
|
|
10404
10455
|
|
|
10405
10456
|
// src/utils/raw-asset-reference-index.ts
|
|
10406
|
-
import { readdir as
|
|
10407
|
-
import { join as
|
|
10457
|
+
import { readdir as readdir6, readFile as readFile21 } from "fs/promises";
|
|
10458
|
+
import { join as join41, relative as relative6, sep as sep4 } from "path";
|
|
10408
10459
|
var PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "pdf", "mp3", "wav", "m4a", "mp4", "webm"]);
|
|
10409
10460
|
async function walkFiles(root, dir = root) {
|
|
10410
10461
|
let entries;
|
|
10411
10462
|
try {
|
|
10412
|
-
entries = await
|
|
10463
|
+
entries = await readdir6(dir, { withFileTypes: true });
|
|
10413
10464
|
} catch {
|
|
10414
10465
|
return [];
|
|
10415
10466
|
}
|
|
10416
10467
|
const out = [];
|
|
10417
10468
|
for (const entry of entries) {
|
|
10418
|
-
const path =
|
|
10469
|
+
const path = join41(dir, entry.name);
|
|
10419
10470
|
if (entry.isDirectory()) out.push(...await walkFiles(root, path));
|
|
10420
10471
|
else if (entry.isFile()) out.push(relative6(root, path).split(sep4).join("/"));
|
|
10421
10472
|
}
|
|
@@ -10431,7 +10482,7 @@ function addInbound(map, target, source) {
|
|
|
10431
10482
|
map.set(target, refs);
|
|
10432
10483
|
}
|
|
10433
10484
|
async function buildRawAssetReferenceIndex(vault) {
|
|
10434
|
-
const assetRoot =
|
|
10485
|
+
const assetRoot = join41(vault, "raw", "assets");
|
|
10435
10486
|
const relativeAssets = await walkFiles(assetRoot);
|
|
10436
10487
|
const assets = relativeAssets.map((path) => `raw/assets/${path}`).sort((a, b) => a.localeCompare(b));
|
|
10437
10488
|
const assetSet = new Set(assets);
|
|
@@ -10447,7 +10498,7 @@ async function buildRawAssetReferenceIndex(vault) {
|
|
|
10447
10498
|
for (const page of scan.data.allMarkdown) {
|
|
10448
10499
|
let text;
|
|
10449
10500
|
try {
|
|
10450
|
-
text = await
|
|
10501
|
+
text = await readFile21(page.absPath, "utf8");
|
|
10451
10502
|
} catch {
|
|
10452
10503
|
continue;
|
|
10453
10504
|
}
|
|
@@ -10507,7 +10558,7 @@ async function planSourceDisposal(input) {
|
|
|
10507
10558
|
if (sensitive.length > 0) return err("SENSITIVE_CONTENT_DETECTED", { findings: sensitive });
|
|
10508
10559
|
const target = await resolveExistingRegularFileInsideVault(input.vault, input.rawPath);
|
|
10509
10560
|
if (!target.ok) return err("RAW_DISPOSAL_TARGET_INVALID", { path: input.rawPath, cause: target });
|
|
10510
|
-
const bytes = await
|
|
10561
|
+
const bytes = await readFile22(target.data);
|
|
10511
10562
|
const size = bytes.byteLength;
|
|
10512
10563
|
let typedReferences = [];
|
|
10513
10564
|
let otherReferences = [];
|
|
@@ -10631,7 +10682,7 @@ async function runSourceDisposal(input) {
|
|
|
10631
10682
|
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
10632
10683
|
import { execSync as nodeExecSync } from "child_process";
|
|
10633
10684
|
import { hostname as nodeHostname, platform as nodePlatform } from "os";
|
|
10634
|
-
import { join as
|
|
10685
|
+
import { join as join42 } from "path";
|
|
10635
10686
|
var SSH_TIMEOUT_MS = 15e3;
|
|
10636
10687
|
var TIMER_UNIT = "agent-memory-trends.timer";
|
|
10637
10688
|
var SERVICE_UNIT = "agent-memory-trends.service";
|
|
@@ -10861,7 +10912,7 @@ async function runFleetHealth(input) {
|
|
|
10861
10912
|
const home = resolveSkillwikiHome(input.home, env);
|
|
10862
10913
|
const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
|
|
10863
10914
|
const vault = input.vault ?? env.WIKI_PATH;
|
|
10864
|
-
const file = input.file ?? (vault ?
|
|
10915
|
+
const file = input.file ?? (vault ? join42(vault, FLEET_REL_PATH) : void 0);
|
|
10865
10916
|
if (!file) {
|
|
10866
10917
|
return {
|
|
10867
10918
|
exitCode: ExitCode.NO_VAULT_CONFIGURED,
|
|
@@ -10946,7 +10997,7 @@ async function runFleetHealth(input) {
|
|
|
10946
10997
|
|
|
10947
10998
|
// src/utils/auto-commit.ts
|
|
10948
10999
|
import { existsSync as existsSync16 } from "fs";
|
|
10949
|
-
import { isAbsolute as isAbsolute3, join as
|
|
11000
|
+
import { isAbsolute as isAbsolute3, join as join43, normalize } from "path";
|
|
10950
11001
|
function isValidLastOpPath(file) {
|
|
10951
11002
|
if (!file || typeof file !== "string") return false;
|
|
10952
11003
|
const trimmed = file.trim();
|
|
@@ -10967,7 +11018,7 @@ async function postCommit(vault, exitCode) {
|
|
|
10967
11018
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
10968
11019
|
const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
|
|
10969
11020
|
if (autoCommit === "false") return;
|
|
10970
|
-
if (!existsSync16(
|
|
11021
|
+
if (!existsSync16(join43(vault, ".git"))) return;
|
|
10971
11022
|
const lastOps = readLastOp(vault);
|
|
10972
11023
|
if (lastOps.length === 0) return;
|
|
10973
11024
|
const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
|
|
@@ -11015,8 +11066,8 @@ function preflight(error) {
|
|
|
11015
11066
|
}
|
|
11016
11067
|
async function runMcpAuthIssueHost(input) {
|
|
11017
11068
|
const isTty = input.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
11018
|
-
const
|
|
11019
|
-
const
|
|
11069
|
+
const readFile23 = input.readFile ?? readFileSync17;
|
|
11070
|
+
const writeFile9 = input.writeFile ?? writeFileSync8;
|
|
11020
11071
|
const stderrWrite = input.stderrWrite ?? ((s) => {
|
|
11021
11072
|
process.stderr.write(s);
|
|
11022
11073
|
});
|
|
@@ -11030,7 +11081,7 @@ async function runMcpAuthIssueHost(input) {
|
|
|
11030
11081
|
if (write && !isTty) return preflight("NO_TTY");
|
|
11031
11082
|
let yamlText = "";
|
|
11032
11083
|
try {
|
|
11033
|
-
yamlText =
|
|
11084
|
+
yamlText = readFile23(mapPath, "utf8");
|
|
11034
11085
|
} catch (error) {
|
|
11035
11086
|
if (!isEnoent(error)) throw error;
|
|
11036
11087
|
}
|
|
@@ -11052,7 +11103,7 @@ async function runMcpAuthIssueHost(input) {
|
|
|
11052
11103
|
const appended = appendHostHash(yamlText, hashHex, hostId, granted.vaults);
|
|
11053
11104
|
if ("error" in appended) return preflight(appended.error);
|
|
11054
11105
|
try {
|
|
11055
|
-
|
|
11106
|
+
writeFile9(mapPath, appended.yaml, { encoding: "utf8", mode: 416 });
|
|
11056
11107
|
} catch (error) {
|
|
11057
11108
|
if (isEnoent(error)) {
|
|
11058
11109
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND") };
|
|
@@ -11212,7 +11263,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
|
|
|
11212
11263
|
if (dirty) {
|
|
11213
11264
|
return emit(dirty, void 0, { postCommit: false });
|
|
11214
11265
|
}
|
|
11215
|
-
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-
|
|
11266
|
+
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-GRSHQYKC.js");
|
|
11216
11267
|
const run = await runManagedWriteTransaction2({
|
|
11217
11268
|
vault,
|
|
11218
11269
|
command,
|
|
@@ -11242,7 +11293,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
11242
11293
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
11243
11294
|
});
|
|
11244
11295
|
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) => {
|
|
11245
|
-
const out = opts.out ??
|
|
11296
|
+
const out = opts.out ?? join44(vault, ".skillwiki", "graph.json");
|
|
11246
11297
|
return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
|
|
11247
11298
|
});
|
|
11248
11299
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
|
@@ -11856,6 +11907,10 @@ program.command("doctor").description("diagnose skillwiki setup issues").option(
|
|
|
11856
11907
|
checkMcp: !!opts.checkMcp,
|
|
11857
11908
|
env: process.env
|
|
11858
11909
|
})));
|
|
11910
|
+
var clipOutboxCmd = program.command("clip-outbox").description("dedicated file-level web-clip outbox");
|
|
11911
|
+
clipOutboxCmd.command("status").description("show whether the clip-outbox channel is configured").action(async () => emit(await runClipOutboxStatus(resolveSkillwikiHome())));
|
|
11912
|
+
clipOutboxCmd.command("watch").description("run the enabled dedicated outbox worker using macOS FSEvents").action(async () => emit(await runClipOutboxWatch(resolveSkillwikiHome())));
|
|
11913
|
+
clipOutboxCmd.command("install-or-migrate").description("refresh the packaged launchd plist without enabling the channel").option("--load", "attended launchd bootstrap; requires existing enabled=true", false).option("--conditional-create-proven", "attest the deployed SeaweedFS If-None-Match:* race test passed", false).action(async (opts) => emit(await runClipOutboxInstall({ home: resolveSkillwikiHome(), load: opts.load, conditionalCreateProven: opts.conditionalCreateProven })));
|
|
11859
11914
|
program.command("connect").description(CONNECT_DESCRIPTION).option("--from-file <path>", "chat-attached env file (not Drive / \u4E91\u76D8)").option("--from-stdin", "read env file from stdin (no echo)").option("--dry-run", "validate without writing ~/.skillwiki/.env").option("--force", "overwrite a different existing token").action(async (opts) => emit(await runConnect({
|
|
11860
11915
|
home: resolveSkillwikiHome(),
|
|
11861
11916
|
fromFile: opts.fromFile,
|