skillwiki 0.10.112 → 0.10.113
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-KJG2CSYD.js → chunk-3TKNAOJH.js} +2 -2
- package/dist/{chunk-LOVLZNLX.js → chunk-5D5HFQ76.js} +1 -1
- package/dist/{chunk-APKDTEVV.js → chunk-5RHR4FEK.js} +18 -14
- package/dist/{chunk-EE7W3GLA.js → chunk-6VUK2FTX.js} +1 -1
- package/dist/{chunk-RR6VDWSI.js → chunk-CBT35FAW.js} +3 -3
- package/dist/{chunk-Q6EZF37H.js → chunk-FQERKMD7.js} +456 -16
- package/dist/{chunk-SKOLE6US.js → chunk-IJKJGQKL.js} +1 -1
- package/dist/{chunk-IR2DI4Y5.js → chunk-IJWZDQDH.js} +3 -3
- package/dist/{chunk-TKX6PSWZ.js → chunk-JL2SCLXT.js} +1 -1
- package/dist/{chunk-CKQPUHM2.js → chunk-K3YZESND.js} +2 -2
- package/dist/{chunk-DOHWNPAZ.js → chunk-OGEAFXON.js} +1 -1
- package/dist/{chunk-ABRYBO4T.js → chunk-OWWWO2XS.js} +3 -3
- package/dist/{chunk-FCA3RRNF.js → chunk-SEY4N4CT.js} +1 -1
- package/dist/{chunk-7POLZOFY.js → chunk-VNSZCK5V.js} +5 -2
- package/dist/{chunk-25OXNFDV.js → chunk-XJ5VEIH7.js} +3 -3
- package/dist/cli.js +534 -114
- package/dist/{index-projection-UNZG5UJZ.js → index-projection-RQSOEDXM.js} +5 -5
- package/dist/{journal-5IW3TZ5F.js → journal-V5XHBEI5.js} +3 -3
- package/dist/{managed-write-preflight-L7IRQ76C.js → managed-write-preflight-2BMHZWVJ.js} +7 -7
- package/dist/{mcp-auth-env-X5MI5IX6.js → mcp-auth-env-QONKW4EO.js} +3 -3
- package/dist/{migrate-RO4BJP5T.js → migrate-SRRCXI2K.js} +6 -6
- package/dist/{runtime-VMFYW2K3.js → runtime-LQZIG3XN.js} +4 -4
- package/dist/skillwiki-mcp.js +14 -14
- package/dist/sources-E6KHDRDE.js +10 -0
- package/dist/{watcher-MGHQD3UI.js → watcher-VJ3TOU6J.js} +4 -4
- package/package.json +1 -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/wiki-ingest/SKILL.md +47 -0
- package/skills/skills/wiki-lint/SKILL.md +7 -0
- package/skills/wiki-ingest/SKILL.md +47 -0
- package/skills/wiki-lint/SKILL.md +7 -0
- package/dist/sources-AOYP6ROO.js +0 -10
package/dist/cli.js
CHANGED
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
upsertIndexEntry,
|
|
65
65
|
vectorIndexStatus,
|
|
66
66
|
withMcpClient
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-5RHR4FEK.js";
|
|
68
68
|
import {
|
|
69
69
|
applySourceDisposition,
|
|
70
70
|
decodeSourceActionApproval,
|
|
@@ -72,7 +72,7 @@ import {
|
|
|
72
72
|
inventorySources,
|
|
73
73
|
planSourceDisposition,
|
|
74
74
|
runSourcesPending
|
|
75
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-CBT35FAW.js";
|
|
76
76
|
import {
|
|
77
77
|
normalizeDistTag,
|
|
78
78
|
readCache,
|
|
@@ -85,7 +85,7 @@ import {
|
|
|
85
85
|
UNMANAGED_START,
|
|
86
86
|
renderRootIndex,
|
|
87
87
|
writeRootIndexProjection
|
|
88
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-IJWZDQDH.js";
|
|
89
89
|
import {
|
|
90
90
|
FLEET_REL_PATH,
|
|
91
91
|
REDACTED_MALFORMED_REFERENCE,
|
|
@@ -113,6 +113,7 @@ import {
|
|
|
113
113
|
gitStrict,
|
|
114
114
|
hasActiveGitSequencer,
|
|
115
115
|
hasUnmergedPaths,
|
|
116
|
+
isAdvisoryLintWarning,
|
|
116
117
|
isValidRemoteDeleteCap,
|
|
117
118
|
isWorktreeClean,
|
|
118
119
|
listJournalOpIds,
|
|
@@ -173,7 +174,7 @@ import {
|
|
|
173
174
|
stageVaultContentChanges,
|
|
174
175
|
supersedeStaleReviewRequiredJournals,
|
|
175
176
|
taxonomyCommentForPage
|
|
176
|
-
} from "./chunk-
|
|
177
|
+
} from "./chunk-FQERKMD7.js";
|
|
177
178
|
import {
|
|
178
179
|
authorizeRawOperation,
|
|
179
180
|
buildSourceReferenceIndex,
|
|
@@ -190,11 +191,11 @@ import {
|
|
|
190
191
|
resolveExistingRegularFileInsideVault,
|
|
191
192
|
validateLogEvent,
|
|
192
193
|
writeLogEvent
|
|
193
|
-
} from "./chunk-
|
|
194
|
+
} from "./chunk-K3YZESND.js";
|
|
194
195
|
import {
|
|
195
196
|
assertTargetInsideVault,
|
|
196
197
|
prepareTypedPage
|
|
197
|
-
} from "./chunk-
|
|
198
|
+
} from "./chunk-3TKNAOJH.js";
|
|
198
199
|
import {
|
|
199
200
|
extractFrontmatter,
|
|
200
201
|
mapWithConcurrency,
|
|
@@ -206,7 +207,7 @@ import {
|
|
|
206
207
|
scanVault,
|
|
207
208
|
splitFrontmatter,
|
|
208
209
|
vaultIoConcurrency
|
|
209
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-JL2SCLXT.js";
|
|
210
211
|
import {
|
|
211
212
|
MCP_HOST_ID_ENV,
|
|
212
213
|
MCP_TOKEN_ENV,
|
|
@@ -215,29 +216,29 @@ import {
|
|
|
215
216
|
mergeMcpAuthIntoEnv,
|
|
216
217
|
nonEmptyEnvValue,
|
|
217
218
|
redactMcpSecret
|
|
218
|
-
} from "./chunk-
|
|
219
|
+
} from "./chunk-OGEAFXON.js";
|
|
219
220
|
import {
|
|
220
221
|
CLIP_MIGRATION_HINT,
|
|
221
222
|
refreshInstalledClipPlist
|
|
222
|
-
} from "./chunk-
|
|
223
|
+
} from "./chunk-XJ5VEIH7.js";
|
|
223
224
|
import {
|
|
224
225
|
atomicWriteText
|
|
225
|
-
} from "./chunk-
|
|
226
|
+
} from "./chunk-6VUK2FTX.js";
|
|
226
227
|
import {
|
|
227
228
|
CLIP_NOT_CONFIGURED,
|
|
228
229
|
loadClipOutboxConfig
|
|
229
|
-
} from "./chunk-
|
|
230
|
+
} from "./chunk-OWWWO2XS.js";
|
|
230
231
|
import {
|
|
231
232
|
runFetchGuard,
|
|
232
233
|
runFetchGuardSync
|
|
233
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-5D5HFQ76.js";
|
|
234
235
|
import {
|
|
235
236
|
parseDotenvFile,
|
|
236
237
|
parseDotenvText,
|
|
237
238
|
profileKey,
|
|
238
239
|
resolveSkillwikiHome,
|
|
239
240
|
writeDotenv
|
|
240
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-IJKJGQKL.js";
|
|
241
242
|
import {
|
|
242
243
|
ExitCode,
|
|
243
244
|
MetaSchema,
|
|
@@ -247,10 +248,10 @@ import {
|
|
|
247
248
|
err,
|
|
248
249
|
ok,
|
|
249
250
|
resolveSessionKind
|
|
250
|
-
} from "./chunk-
|
|
251
|
+
} from "./chunk-VNSZCK5V.js";
|
|
251
252
|
|
|
252
253
|
// src/cli.ts
|
|
253
|
-
import { join as
|
|
254
|
+
import { join as join45 } from "path";
|
|
254
255
|
import { Command } from "commander";
|
|
255
256
|
|
|
256
257
|
// src/utils/output.ts
|
|
@@ -1601,21 +1602,21 @@ function rollbackPath(vault, path, stage2, stage3) {
|
|
|
1601
1602
|
function hasMarkers(path, text) {
|
|
1602
1603
|
const lines = text.split(/\r?\n/);
|
|
1603
1604
|
let open2 = false;
|
|
1604
|
-
let
|
|
1605
|
+
let sep6 = false;
|
|
1605
1606
|
for (const line of lines) {
|
|
1606
1607
|
if (line.startsWith("<<<<<<< ")) {
|
|
1607
1608
|
open2 = true;
|
|
1608
|
-
|
|
1609
|
+
sep6 = false;
|
|
1609
1610
|
continue;
|
|
1610
1611
|
}
|
|
1611
1612
|
if (line === "=======" && open2) {
|
|
1612
|
-
|
|
1613
|
+
sep6 = true;
|
|
1613
1614
|
continue;
|
|
1614
1615
|
}
|
|
1615
|
-
if (line.startsWith(">>>>>>> ") && open2 &&
|
|
1616
|
+
if (line.startsWith(">>>>>>> ") && open2 && sep6) return true;
|
|
1616
1617
|
if (line.startsWith(">>>>>>> ")) {
|
|
1617
1618
|
open2 = false;
|
|
1618
|
-
|
|
1619
|
+
sep6 = false;
|
|
1619
1620
|
}
|
|
1620
1621
|
}
|
|
1621
1622
|
return false;
|
|
@@ -3478,7 +3479,11 @@ async function runHealth(input) {
|
|
|
3478
3479
|
checks: doctor.result.data.checks.map((check) => ({ id: check.id, status: check.status, detail: check.detail }))
|
|
3479
3480
|
};
|
|
3480
3481
|
const lintComponent = {
|
|
3481
|
-
status: statusFromCounts({
|
|
3482
|
+
status: statusFromCounts({
|
|
3483
|
+
error: lint.result.data.summary.errors,
|
|
3484
|
+
warnings: lint.result.data.buckets.filter((bucket) => bucket.severity === "warning" && !isAdvisoryLintWarning(bucket.kind)).reduce((n, bucket) => n + bucket.count, 0),
|
|
3485
|
+
info: lint.result.data.summary.info
|
|
3486
|
+
}),
|
|
3482
3487
|
blocking: true,
|
|
3483
3488
|
summary: lint.result.data.summary,
|
|
3484
3489
|
buckets: lint.result.data.buckets,
|
|
@@ -3585,7 +3590,7 @@ async function runClipOutboxStatus(home) {
|
|
|
3585
3590
|
result: ok({ enabled: false, humanHint: CLIP_NOT_CONFIGURED })
|
|
3586
3591
|
};
|
|
3587
3592
|
}
|
|
3588
|
-
const { readJournal: readJournal2 } = await import("./journal-
|
|
3593
|
+
const { readJournal: readJournal2 } = await import("./journal-V5XHBEI5.js");
|
|
3589
3594
|
let queue;
|
|
3590
3595
|
try {
|
|
3591
3596
|
queue = (await readJournal2(resolved.config.stateRoot)).map(({ captureId, state, reason, attempts }) => ({ captureId, state, reason, attempts }));
|
|
@@ -3603,7 +3608,7 @@ async function runClipOutboxStatus(home) {
|
|
|
3603
3608
|
}
|
|
3604
3609
|
async function runClipOutboxInstall(input) {
|
|
3605
3610
|
try {
|
|
3606
|
-
const { migrateClipOutbox } = await import("./migrate-
|
|
3611
|
+
const { migrateClipOutbox } = await import("./migrate-SRRCXI2K.js");
|
|
3607
3612
|
return { exitCode: ExitCode.OK, result: ok(await migrateClipOutbox(input)) };
|
|
3608
3613
|
} catch (e) {
|
|
3609
3614
|
return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: e instanceof Error ? e.message : "clip migration failed" }) };
|
|
@@ -3616,10 +3621,10 @@ async function runClipOutboxWatch(home) {
|
|
|
3616
3621
|
let client;
|
|
3617
3622
|
try {
|
|
3618
3623
|
const { S3Client: S3Client3 } = await import("@aws-sdk/client-s3");
|
|
3619
|
-
const { loadClipCredentials, validateRuntime } = await import("./runtime-
|
|
3624
|
+
const { loadClipCredentials, validateRuntime } = await import("./runtime-LQZIG3XN.js");
|
|
3620
3625
|
const { createS3CreateOnlyPut, parseDestination } = await import("./upload-HTE62TOV.js");
|
|
3621
3626
|
const { postReadyHint } = await import("./ready-S7F53Q2D.js");
|
|
3622
|
-
const { startClipWorker } = await import("./watcher-
|
|
3627
|
+
const { startClipWorker } = await import("./watcher-VJ3TOU6J.js");
|
|
3623
3628
|
const cfg = resolved.config;
|
|
3624
3629
|
const credentials = await loadClipCredentials(home, cfg);
|
|
3625
3630
|
await validateRuntime(home, cfg, credentials.s3.endpoint);
|
|
@@ -3662,9 +3667,9 @@ async function runClipOutboxMirrorWatch(home) {
|
|
|
3662
3667
|
const abort = new AbortController();
|
|
3663
3668
|
try {
|
|
3664
3669
|
const { S3Client: S3Client3 } = await import("@aws-sdk/client-s3");
|
|
3665
|
-
const { loadClipCredentials, validateRuntime } = await import("./runtime-
|
|
3670
|
+
const { loadClipCredentials, validateRuntime } = await import("./runtime-LQZIG3XN.js");
|
|
3666
3671
|
const { createS3GetObject, eventsUrlFromMcp, parseDestinationForMirror, startMirrorListener } = await import("./mirror-H5BTBAON.js");
|
|
3667
|
-
const { resolveMcpAuthEnv } = await import("./mcp-auth-env-
|
|
3672
|
+
const { resolveMcpAuthEnv } = await import("./mcp-auth-env-QONKW4EO.js");
|
|
3668
3673
|
const cfg = resolved.config;
|
|
3669
3674
|
const credentials = await loadClipCredentials(home, cfg);
|
|
3670
3675
|
await validateRuntime(home, cfg, credentials.s3.endpoint);
|
|
@@ -3672,8 +3677,8 @@ async function runClipOutboxMirrorWatch(home) {
|
|
|
3672
3677
|
if (!auth.url || !auth.token) throw new Error("MCP bearer required for inbound SSE");
|
|
3673
3678
|
const destination = parseDestinationForMirror(cfg.destination);
|
|
3674
3679
|
client = new S3Client3({ endpoint: credentials.s3.endpoint, region: credentials.s3.region, credentials: credentials.s3, forcePathStyle: true, maxAttempts: 1 });
|
|
3675
|
-
const done = new Promise((
|
|
3676
|
-
abort.signal.addEventListener("abort", () =>
|
|
3680
|
+
const done = new Promise((resolve10) => {
|
|
3681
|
+
abort.signal.addEventListener("abort", () => resolve10(), { once: true });
|
|
3677
3682
|
});
|
|
3678
3683
|
process.once("SIGTERM", () => abort.abort());
|
|
3679
3684
|
process.once("SIGINT", () => abort.abort());
|
|
@@ -4175,7 +4180,7 @@ ${fmRewritten}
|
|
|
4175
4180
|
}
|
|
4176
4181
|
let indexUpdated = false;
|
|
4177
4182
|
if (!isRaw) {
|
|
4178
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
4183
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-RQSOEDXM.js");
|
|
4179
4184
|
const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
|
|
4180
4185
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
4181
4186
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
@@ -4296,10 +4301,10 @@ async function runRemove(input) {
|
|
|
4296
4301
|
const tombstonePath = await writeDeleteIntent(input.vault, intent);
|
|
4297
4302
|
await unlink2(join20(input.vault, relPath));
|
|
4298
4303
|
if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
|
|
4299
|
-
const { readFile:
|
|
4304
|
+
const { readFile: readFile24 } = await import("fs/promises");
|
|
4300
4305
|
const { join: pathJoin } = await import("path");
|
|
4301
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
4302
|
-
const before = await
|
|
4306
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-RQSOEDXM.js");
|
|
4307
|
+
const before = await readFile24(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
|
|
4303
4308
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
4304
4309
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
4305
4310
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -6882,8 +6887,8 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6882
6887
|
if (!strategy.usesJournal || !phaseAtLeast(currentPhase, "page")) {
|
|
6883
6888
|
if (strategy.publisherKind === "project-page") {
|
|
6884
6889
|
try {
|
|
6885
|
-
const { mkdir:
|
|
6886
|
-
await
|
|
6890
|
+
const { mkdir: mkdir14 } = await import("fs/promises");
|
|
6891
|
+
await mkdir14(dirname7(prepared.targetPath), { recursive: true });
|
|
6887
6892
|
} catch (error) {
|
|
6888
6893
|
return {
|
|
6889
6894
|
ok: false,
|
|
@@ -7690,7 +7695,7 @@ function buildRawContent(sourceUrl, ingested, sha2562, body) {
|
|
|
7690
7695
|
];
|
|
7691
7696
|
return lines.join("\n");
|
|
7692
7697
|
}
|
|
7693
|
-
function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance) {
|
|
7698
|
+
function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance, html) {
|
|
7694
7699
|
const aliases = [];
|
|
7695
7700
|
const sourcesYaml = ` - ${rawRelPath}`;
|
|
7696
7701
|
const tagsYaml = tags.length > 0 ? tags.map((t) => ` - ${t}`).join("\n") : " []";
|
|
@@ -7713,6 +7718,10 @@ function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance)
|
|
|
7713
7718
|
if (provenance) {
|
|
7714
7719
|
fmLines.push(`provenance: ${provenance}`);
|
|
7715
7720
|
}
|
|
7721
|
+
fmLines.push("output_standard: v2");
|
|
7722
|
+
if (html) {
|
|
7723
|
+
fmLines.push("html_render: true");
|
|
7724
|
+
}
|
|
7716
7725
|
fmLines.push("---");
|
|
7717
7726
|
fmLines.push("");
|
|
7718
7727
|
const body = [
|
|
@@ -7796,7 +7805,7 @@ async function acquireRawCaptureLock(path) {
|
|
|
7796
7805
|
if (error.code !== "EEXIST") {
|
|
7797
7806
|
return err("WRITE_FAILED", { path: lockPath, phase: "raw-lock", message: String(error) });
|
|
7798
7807
|
}
|
|
7799
|
-
await new Promise((
|
|
7808
|
+
await new Promise((resolve10) => setTimeout(resolve10, 10));
|
|
7800
7809
|
}
|
|
7801
7810
|
}
|
|
7802
7811
|
return err("WRITE_FAILED", { path: lockPath, phase: "raw-lock", message: "raw capture lock held" });
|
|
@@ -7935,7 +7944,8 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7935
7944
|
input.type,
|
|
7936
7945
|
tags,
|
|
7937
7946
|
rawRelPath,
|
|
7938
|
-
input.provenance
|
|
7947
|
+
input.provenance,
|
|
7948
|
+
input.html
|
|
7939
7949
|
);
|
|
7940
7950
|
if (!input.dryRun) {
|
|
7941
7951
|
try {
|
|
@@ -8026,7 +8036,8 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
8026
8036
|
input.type,
|
|
8027
8037
|
tags,
|
|
8028
8038
|
rawRelPath,
|
|
8029
|
-
input.provenance
|
|
8039
|
+
input.provenance,
|
|
8040
|
+
input.html
|
|
8030
8041
|
);
|
|
8031
8042
|
const refreshedPublication = preparePagePublicationFromContent({
|
|
8032
8043
|
vault: input.vault,
|
|
@@ -8429,14 +8440,412 @@ async function runTagReconcile(input) {
|
|
|
8429
8440
|
};
|
|
8430
8441
|
}
|
|
8431
8442
|
|
|
8443
|
+
// src/commands/page-render-html.ts
|
|
8444
|
+
import { lstat, mkdir as mkdir11, readFile as readFile17, realpath } from "fs/promises";
|
|
8445
|
+
import { basename, dirname as dirname9, join as join33, relative as relative2, resolve as resolve5, sep as sep2 } from "path";
|
|
8446
|
+
function escapeHtml(text) {
|
|
8447
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
8448
|
+
}
|
|
8449
|
+
var SAFE_HREF_RE = /^(https?:\/\/|mailto:|#)/i;
|
|
8450
|
+
function escapeAttr(text) {
|
|
8451
|
+
return escapeHtml(text).replace(/'/g, "'");
|
|
8452
|
+
}
|
|
8453
|
+
function slugifyHeading(text, used) {
|
|
8454
|
+
const base = text.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "section";
|
|
8455
|
+
const n = used.get(base) ?? 0;
|
|
8456
|
+
used.set(base, n + 1);
|
|
8457
|
+
return n === 0 ? base : `${base}-${n + 1}`;
|
|
8458
|
+
}
|
|
8459
|
+
function renderInlineMarkdown(raw) {
|
|
8460
|
+
const re = /(`[^`]+`)|(\[\[(?:[^\]]+)\]\])|(!\[[^\]]*\]\([^)]+\))|(\[[^\]]+\]\([^)]+\))|(\*\*[^*]+\*\*)|(\*[^*]+\*)/g;
|
|
8461
|
+
const parts = [];
|
|
8462
|
+
let last = 0;
|
|
8463
|
+
let match;
|
|
8464
|
+
while ((match = re.exec(raw)) !== null) {
|
|
8465
|
+
parts.push(escapeHtml(raw.slice(last, match.index)));
|
|
8466
|
+
const token = match[0];
|
|
8467
|
+
if (token.startsWith("`")) {
|
|
8468
|
+
parts.push(`<code>${escapeHtml(token.slice(1, -1))}</code>`);
|
|
8469
|
+
} else if (token.startsWith("[[")) {
|
|
8470
|
+
const inner = token.slice(2, -2);
|
|
8471
|
+
const pipe = inner.indexOf("|");
|
|
8472
|
+
const label = pipe === -1 ? inner : inner.slice(pipe + 1);
|
|
8473
|
+
parts.push(escapeHtml(label.trim()));
|
|
8474
|
+
} else if (token.startsWith("![")) {
|
|
8475
|
+
const alt = token.match(/^!\[([^\]]*)\]/)?.[1] ?? "";
|
|
8476
|
+
parts.push(escapeHtml(alt));
|
|
8477
|
+
} else if (token.startsWith("[")) {
|
|
8478
|
+
const linked = token.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
8479
|
+
if (linked && SAFE_HREF_RE.test(linked[2])) {
|
|
8480
|
+
parts.push(`<a href="${escapeAttr(linked[2])}">${escapeHtml(linked[1])}</a>`);
|
|
8481
|
+
} else if (linked) {
|
|
8482
|
+
parts.push(escapeHtml(linked[1]));
|
|
8483
|
+
} else {
|
|
8484
|
+
parts.push(escapeHtml(token));
|
|
8485
|
+
}
|
|
8486
|
+
} else if (token.startsWith("**")) {
|
|
8487
|
+
parts.push(`<strong>${escapeHtml(token.slice(2, -2))}</strong>`);
|
|
8488
|
+
} else {
|
|
8489
|
+
parts.push(`<em>${escapeHtml(token.slice(1, -1))}</em>`);
|
|
8490
|
+
}
|
|
8491
|
+
last = match.index + token.length;
|
|
8492
|
+
}
|
|
8493
|
+
parts.push(escapeHtml(raw.slice(last)));
|
|
8494
|
+
return parts.join("");
|
|
8495
|
+
}
|
|
8496
|
+
function parseTableRow(line) {
|
|
8497
|
+
const trimmed = line.trim();
|
|
8498
|
+
const inner = trimmed.startsWith("|") ? trimmed.slice(1) : trimmed;
|
|
8499
|
+
const withoutEnd = inner.endsWith("|") ? inner.slice(0, -1) : inner;
|
|
8500
|
+
return withoutEnd.split("|").map((cell) => cell.trim());
|
|
8501
|
+
}
|
|
8502
|
+
function isTableSeparator(line) {
|
|
8503
|
+
return /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(line);
|
|
8504
|
+
}
|
|
8505
|
+
function parseMarkdownBlocks(body) {
|
|
8506
|
+
const lines = body.split(/\r?\n/);
|
|
8507
|
+
const blocks = [];
|
|
8508
|
+
let i = 0;
|
|
8509
|
+
while (i < lines.length) {
|
|
8510
|
+
const line = lines[i];
|
|
8511
|
+
const fence = line.match(/^(\s*)(`{3,}|~{3,})(.*)$/);
|
|
8512
|
+
if (fence) {
|
|
8513
|
+
const marker = fence[2];
|
|
8514
|
+
const language = fence[3].trim();
|
|
8515
|
+
const content = [];
|
|
8516
|
+
i++;
|
|
8517
|
+
while (i < lines.length) {
|
|
8518
|
+
const close = lines[i].match(/^(\s*)(`{3,}|~{3,})\s*$/);
|
|
8519
|
+
if (close && close[2][0] === marker[0] && close[2].length >= marker.length) break;
|
|
8520
|
+
content.push(lines[i]);
|
|
8521
|
+
i++;
|
|
8522
|
+
}
|
|
8523
|
+
blocks.push({ kind: "code", language, text: content.join("\n") });
|
|
8524
|
+
i++;
|
|
8525
|
+
continue;
|
|
8526
|
+
}
|
|
8527
|
+
const heading = line.match(/^(#{1,6})\s+(\S.*)$/);
|
|
8528
|
+
if (heading) {
|
|
8529
|
+
blocks.push({ kind: "heading", level: heading[1].length, text: heading[2].trim() });
|
|
8530
|
+
i++;
|
|
8531
|
+
continue;
|
|
8532
|
+
}
|
|
8533
|
+
if (/^\s*\|.*\|\s*$/.test(line) && i + 1 < lines.length && isTableSeparator(lines[i + 1])) {
|
|
8534
|
+
const rows = [parseTableRow(line)];
|
|
8535
|
+
i += 2;
|
|
8536
|
+
while (i < lines.length && /^\s*\|.*\|\s*$/.test(lines[i]) && !isTableSeparator(lines[i])) {
|
|
8537
|
+
rows.push(parseTableRow(lines[i]));
|
|
8538
|
+
i++;
|
|
8539
|
+
}
|
|
8540
|
+
blocks.push({ kind: "table", rows });
|
|
8541
|
+
continue;
|
|
8542
|
+
}
|
|
8543
|
+
const ordered = line.match(/^\s*\d+\.\s+(.*)$/);
|
|
8544
|
+
const unordered = line.match(/^\s*[-*+]\s+(.*)$/);
|
|
8545
|
+
if (ordered || unordered) {
|
|
8546
|
+
const isOrdered = Boolean(ordered);
|
|
8547
|
+
const itemRe = isOrdered ? /^\s*\d+\.\s+(.*)$/ : /^\s*[-*+]\s+(.*)$/;
|
|
8548
|
+
const items = [];
|
|
8549
|
+
while (i < lines.length) {
|
|
8550
|
+
const item = lines[i].match(itemRe);
|
|
8551
|
+
if (!item) break;
|
|
8552
|
+
const chunks = [item[1]];
|
|
8553
|
+
i++;
|
|
8554
|
+
while (i < lines.length && /^\s+\S/.test(lines[i]) && !itemRe.test(lines[i]) && !/^(#{1,6})\s+/.test(lines[i])) {
|
|
8555
|
+
chunks.push(lines[i].trim());
|
|
8556
|
+
i++;
|
|
8557
|
+
}
|
|
8558
|
+
items.push(chunks.join(" "));
|
|
8559
|
+
}
|
|
8560
|
+
blocks.push({ kind: "list", ordered: isOrdered, items });
|
|
8561
|
+
continue;
|
|
8562
|
+
}
|
|
8563
|
+
if (!line.trim()) {
|
|
8564
|
+
i++;
|
|
8565
|
+
continue;
|
|
8566
|
+
}
|
|
8567
|
+
const para = [line.trim()];
|
|
8568
|
+
i++;
|
|
8569
|
+
while (i < lines.length) {
|
|
8570
|
+
const next = lines[i];
|
|
8571
|
+
if (!next.trim()) break;
|
|
8572
|
+
if (/^(#{1,6})\s+/.test(next) || /^(\s*)(`{3,}|~{3,})/.test(next) || /^\s*\|.*\|\s*$/.test(next) || /^\s*\d+\.\s+/.test(next) || /^\s*[-*+]\s+/.test(next)) {
|
|
8573
|
+
break;
|
|
8574
|
+
}
|
|
8575
|
+
para.push(next.trim());
|
|
8576
|
+
i++;
|
|
8577
|
+
}
|
|
8578
|
+
blocks.push({ kind: "paragraph", text: para.join(" ") });
|
|
8579
|
+
}
|
|
8580
|
+
return blocks;
|
|
8581
|
+
}
|
|
8582
|
+
function renderBlock(block) {
|
|
8583
|
+
switch (block.kind) {
|
|
8584
|
+
case "heading": {
|
|
8585
|
+
const tag = `h${Math.min(block.level, 6)}`;
|
|
8586
|
+
return `<${tag}>${renderInlineMarkdown(block.text)}</${tag}>`;
|
|
8587
|
+
}
|
|
8588
|
+
case "paragraph":
|
|
8589
|
+
return `<p>${renderInlineMarkdown(block.text)}</p>`;
|
|
8590
|
+
case "list": {
|
|
8591
|
+
const tag = block.ordered ? "ol" : "ul";
|
|
8592
|
+
const items = block.items.map((item) => `<li>${renderInlineMarkdown(item)}</li>`).join("");
|
|
8593
|
+
return `<${tag}>${items}</${tag}>`;
|
|
8594
|
+
}
|
|
8595
|
+
case "table": {
|
|
8596
|
+
if (block.rows.length === 0) return "";
|
|
8597
|
+
const [header, ...body] = block.rows;
|
|
8598
|
+
const thead = `<thead><tr>${header.map((cell) => `<th>${renderInlineMarkdown(cell)}</th>`).join("")}</tr></thead>`;
|
|
8599
|
+
const tbody = body.length > 0 ? `<tbody>${body.map((row) => `<tr>${row.map((cell) => `<td>${renderInlineMarkdown(cell)}</td>`).join("")}</tr>`).join("")}</tbody>` : "";
|
|
8600
|
+
return `<table>${thead}${tbody}</table>`;
|
|
8601
|
+
}
|
|
8602
|
+
case "code": {
|
|
8603
|
+
const cls = block.language.toLowerCase() === "mermaid" ? ' class="mermaid"' : "";
|
|
8604
|
+
return `<pre${cls}><code>${escapeHtml(block.text)}</code></pre>`;
|
|
8605
|
+
}
|
|
8606
|
+
}
|
|
8607
|
+
}
|
|
8608
|
+
function groupSections(blocks) {
|
|
8609
|
+
const used = /* @__PURE__ */ new Map();
|
|
8610
|
+
let title = "Page";
|
|
8611
|
+
const intro = [];
|
|
8612
|
+
const sections = [];
|
|
8613
|
+
let current = null;
|
|
8614
|
+
for (const block of blocks) {
|
|
8615
|
+
if (block.kind === "heading" && block.level === 1 && title === "Page") {
|
|
8616
|
+
title = block.text;
|
|
8617
|
+
continue;
|
|
8618
|
+
}
|
|
8619
|
+
if (block.kind === "heading" && block.level === 2) {
|
|
8620
|
+
current = { id: slugifyHeading(block.text, used), title: block.text, blocks: [] };
|
|
8621
|
+
sections.push(current);
|
|
8622
|
+
continue;
|
|
8623
|
+
}
|
|
8624
|
+
if (current) current.blocks.push(block);
|
|
8625
|
+
else intro.push(block);
|
|
8626
|
+
}
|
|
8627
|
+
return { title, intro, sections };
|
|
8628
|
+
}
|
|
8629
|
+
function renderPageHtml(title, body) {
|
|
8630
|
+
const parsed = parseMarkdownBlocks(body);
|
|
8631
|
+
const grouped = groupSections(parsed);
|
|
8632
|
+
const pageTitle = grouped.title !== "Page" ? grouped.title : title;
|
|
8633
|
+
const sections = grouped.sections.length > 0 ? grouped.sections : [{ id: "content", title: pageTitle, blocks: grouped.intro }];
|
|
8634
|
+
const introHtml = grouped.sections.length > 0 && grouped.intro.length > 0 ? grouped.intro.map(renderBlock).join("\n") : "";
|
|
8635
|
+
const toc = sections.map((section) => `<li><a href="#${escapeAttr(section.id)}">${escapeHtml(section.title)}</a></li>`).join("");
|
|
8636
|
+
const tabs = sections.map(
|
|
8637
|
+
(section, index) => `<button type="button" role="tab" aria-selected="${index === 0 ? "true" : "false"}" aria-controls="${escapeAttr(section.id)}" data-tab="${escapeAttr(section.id)}">${escapeHtml(section.title)}</button>`
|
|
8638
|
+
).join("");
|
|
8639
|
+
const panels = sections.map((section, index) => {
|
|
8640
|
+
const inner = section.blocks.map(renderBlock).join("\n");
|
|
8641
|
+
return `<section id="${escapeAttr(section.id)}" class="tab-panel" role="tabpanel"${index === 0 ? "" : " hidden"}><details open><summary>${escapeHtml(section.title)}</summary>
|
|
8642
|
+
${inner}
|
|
8643
|
+
</details></section>`;
|
|
8644
|
+
}).join("\n");
|
|
8645
|
+
return `<!DOCTYPE html>
|
|
8646
|
+
<html lang="en">
|
|
8647
|
+
<head>
|
|
8648
|
+
<meta charset="utf-8">
|
|
8649
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8650
|
+
<title>${escapeHtml(pageTitle)}</title>
|
|
8651
|
+
<style>
|
|
8652
|
+
:root { color-scheme: light dark; --bg: #f7f5f0; --fg: #1b1b1b; --accent: #355c7d; --line: #cfc8bc; }
|
|
8653
|
+
body { margin: 0 auto; max-width: 52rem; padding: 1.5rem; font: 16px/1.5 system-ui, sans-serif; background: var(--bg); color: var(--fg); }
|
|
8654
|
+
nav.toc { border: 1px solid var(--line); padding: 0.75rem 1rem; margin-bottom: 1rem; }
|
|
8655
|
+
nav.toc ol { margin: 0; padding-left: 1.25rem; }
|
|
8656
|
+
.tabs { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 1rem 0; }
|
|
8657
|
+
.tabs button { border: 1px solid var(--line); background: transparent; color: inherit; padding: 0.35rem 0.7rem; cursor: pointer; }
|
|
8658
|
+
.tabs button[aria-selected="true"] { background: var(--accent); color: #fff; }
|
|
8659
|
+
.tab-panel { border: 1px solid var(--line); padding: 0.75rem; }
|
|
8660
|
+
details { margin: 0; }
|
|
8661
|
+
summary { cursor: pointer; font-weight: 600; }
|
|
8662
|
+
pre { overflow: auto; padding: 0.75rem; background: #111; color: #f5f5f5; }
|
|
8663
|
+
pre.mermaid { white-space: pre-wrap; }
|
|
8664
|
+
table { border-collapse: collapse; width: 100%; }
|
|
8665
|
+
th, td { border: 1px solid var(--line); padding: 0.35rem 0.5rem; text-align: left; }
|
|
8666
|
+
a { color: var(--accent); }
|
|
8667
|
+
</style>
|
|
8668
|
+
</head>
|
|
8669
|
+
<body>
|
|
8670
|
+
<nav class="toc" id="toc"><strong>Contents</strong><ol>${toc}</ol></nav>
|
|
8671
|
+
${introHtml}
|
|
8672
|
+
<div class="tabs" role="tablist">${tabs}</div>
|
|
8673
|
+
${panels}
|
|
8674
|
+
<script>
|
|
8675
|
+
(function () {
|
|
8676
|
+
var tabs = document.querySelectorAll('[role="tab"]');
|
|
8677
|
+
var panels = document.querySelectorAll('.tab-panel');
|
|
8678
|
+
function activate(id) {
|
|
8679
|
+
if (!id) return;
|
|
8680
|
+
var found = false;
|
|
8681
|
+
panels.forEach(function (panel) { if (panel.id === id) found = true; });
|
|
8682
|
+
if (!found) return;
|
|
8683
|
+
tabs.forEach(function (tab) {
|
|
8684
|
+
tab.setAttribute("aria-selected", tab.getAttribute("data-tab") === id ? "true" : "false");
|
|
8685
|
+
});
|
|
8686
|
+
panels.forEach(function (panel) { panel.hidden = panel.id !== id; });
|
|
8687
|
+
}
|
|
8688
|
+
tabs.forEach(function (tab) {
|
|
8689
|
+
tab.addEventListener("click", function () {
|
|
8690
|
+
activate(tab.getAttribute("data-tab"));
|
|
8691
|
+
});
|
|
8692
|
+
});
|
|
8693
|
+
document.querySelectorAll("nav.toc a[href^='#']").forEach(function (link) {
|
|
8694
|
+
link.addEventListener("click", function () {
|
|
8695
|
+
activate((link.getAttribute("href") || "").replace(/^#/, ""));
|
|
8696
|
+
});
|
|
8697
|
+
});
|
|
8698
|
+
function fromHash() {
|
|
8699
|
+
activate((location.hash || "").replace(/^#/, ""));
|
|
8700
|
+
}
|
|
8701
|
+
window.addEventListener("hashchange", fromHash);
|
|
8702
|
+
fromHash();
|
|
8703
|
+
})();
|
|
8704
|
+
</script>
|
|
8705
|
+
</body>
|
|
8706
|
+
</html>
|
|
8707
|
+
`;
|
|
8708
|
+
}
|
|
8709
|
+
function isInsideCanonical(root, candidate) {
|
|
8710
|
+
const rel = relative2(root, candidate).split(sep2).join("/");
|
|
8711
|
+
return rel === "" || rel !== ".." && !rel.startsWith("../");
|
|
8712
|
+
}
|
|
8713
|
+
function outInsideVaultError(vault, out, message) {
|
|
8714
|
+
return err("HTML_RENDER_OUT_INSIDE_VAULT", { vault, out, message });
|
|
8715
|
+
}
|
|
8716
|
+
async function canonicalizeWithMissingSuffix(abs) {
|
|
8717
|
+
try {
|
|
8718
|
+
await lstat(abs);
|
|
8719
|
+
return await realpath(abs);
|
|
8720
|
+
} catch (error) {
|
|
8721
|
+
if (error.code !== "ENOENT") throw error;
|
|
8722
|
+
}
|
|
8723
|
+
const missing = [];
|
|
8724
|
+
let current = abs;
|
|
8725
|
+
while (true) {
|
|
8726
|
+
missing.unshift(basename(current));
|
|
8727
|
+
const parent = dirname9(current);
|
|
8728
|
+
try {
|
|
8729
|
+
await lstat(parent);
|
|
8730
|
+
return join33(await realpath(parent), ...missing);
|
|
8731
|
+
} catch (error) {
|
|
8732
|
+
if (error.code !== "ENOENT") throw error;
|
|
8733
|
+
if (parent === current) return join33(parent, ...missing);
|
|
8734
|
+
current = parent;
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
}
|
|
8738
|
+
async function resolveOutOutsideVault(vault, out) {
|
|
8739
|
+
let vaultReal;
|
|
8740
|
+
try {
|
|
8741
|
+
vaultReal = await realpath(vault);
|
|
8742
|
+
} catch (error) {
|
|
8743
|
+
return err("VAULT_PATH_INVALID", { message: "vault realpath failed", cause: String(error) });
|
|
8744
|
+
}
|
|
8745
|
+
const abs = resolve5(out);
|
|
8746
|
+
try {
|
|
8747
|
+
const info = await lstat(abs);
|
|
8748
|
+
if (info.isSymbolicLink()) {
|
|
8749
|
+
return outInsideVaultError(vault, out, "--out must be a regular path outside the vault");
|
|
8750
|
+
}
|
|
8751
|
+
} catch (error) {
|
|
8752
|
+
if (error.code !== "ENOENT") {
|
|
8753
|
+
return err("WRITE_FAILED", { path: abs, message: String(error) });
|
|
8754
|
+
}
|
|
8755
|
+
}
|
|
8756
|
+
let canonical;
|
|
8757
|
+
try {
|
|
8758
|
+
canonical = await canonicalizeWithMissingSuffix(abs);
|
|
8759
|
+
} catch (error) {
|
|
8760
|
+
return err("WRITE_FAILED", { path: abs, message: String(error) });
|
|
8761
|
+
}
|
|
8762
|
+
if (isInsideCanonical(vaultReal, canonical)) {
|
|
8763
|
+
return outInsideVaultError(vault, out, "--out must be outside the vault");
|
|
8764
|
+
}
|
|
8765
|
+
return ok(canonical);
|
|
8766
|
+
}
|
|
8767
|
+
async function runPageRenderHtml(input) {
|
|
8768
|
+
if (!input.out || input.out.trim().length === 0) {
|
|
8769
|
+
return {
|
|
8770
|
+
exitCode: ExitCode.USAGE,
|
|
8771
|
+
result: err("USAGE", { message: "--out is required" })
|
|
8772
|
+
};
|
|
8773
|
+
}
|
|
8774
|
+
const pagePath = await resolveExistingRegularFileInsideVault(input.vault, input.page);
|
|
8775
|
+
if (!pagePath.ok) {
|
|
8776
|
+
return {
|
|
8777
|
+
exitCode: pagePath.error === "FILE_NOT_FOUND" ? ExitCode.FILE_NOT_FOUND : ExitCode.VAULT_PATH_INVALID,
|
|
8778
|
+
result: pagePath
|
|
8779
|
+
};
|
|
8780
|
+
}
|
|
8781
|
+
const outPath = await resolveOutOutsideVault(input.vault, input.out);
|
|
8782
|
+
if (!outPath.ok) {
|
|
8783
|
+
const exitCode = outPath.error === "HTML_RENDER_OUT_INSIDE_VAULT" ? ExitCode.SCHEME_REJECTED : outPath.error === "WRITE_FAILED" ? ExitCode.WRITE_FAILED : ExitCode.VAULT_PATH_INVALID;
|
|
8784
|
+
return { exitCode, result: outPath };
|
|
8785
|
+
}
|
|
8786
|
+
let text;
|
|
8787
|
+
try {
|
|
8788
|
+
text = await readFile17(pagePath.data, "utf8");
|
|
8789
|
+
} catch {
|
|
8790
|
+
return {
|
|
8791
|
+
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
8792
|
+
result: err("FILE_NOT_FOUND", { path: input.page })
|
|
8793
|
+
};
|
|
8794
|
+
}
|
|
8795
|
+
const fm = extractFrontmatter(text);
|
|
8796
|
+
if (!fm.ok) {
|
|
8797
|
+
return {
|
|
8798
|
+
exitCode: ExitCode.INVALID_FRONTMATTER,
|
|
8799
|
+
result: fm
|
|
8800
|
+
};
|
|
8801
|
+
}
|
|
8802
|
+
if (fm.data.html_render !== true) {
|
|
8803
|
+
return {
|
|
8804
|
+
exitCode: ExitCode.HTML_RENDER_NOT_OPTED_IN,
|
|
8805
|
+
result: err("HTML_RENDER_NOT_OPTED_IN", {
|
|
8806
|
+
page: input.page,
|
|
8807
|
+
message: "page frontmatter must set html_render: true"
|
|
8808
|
+
})
|
|
8809
|
+
};
|
|
8810
|
+
}
|
|
8811
|
+
const split = splitFrontmatter(text);
|
|
8812
|
+
if (!split.ok) {
|
|
8813
|
+
return { exitCode: ExitCode.MISSING_CLOSING_DELIMITER, result: split };
|
|
8814
|
+
}
|
|
8815
|
+
const title = typeof fm.data.title === "string" ? fm.data.title : input.page;
|
|
8816
|
+
const html = renderPageHtml(title, split.data.body);
|
|
8817
|
+
const outAbs = outPath.data;
|
|
8818
|
+
try {
|
|
8819
|
+
await mkdir11(dirname9(outAbs), { recursive: true });
|
|
8820
|
+
} catch (error) {
|
|
8821
|
+
return {
|
|
8822
|
+
exitCode: ExitCode.WRITE_FAILED,
|
|
8823
|
+
result: err("WRITE_FAILED", { path: outAbs, message: String(error) })
|
|
8824
|
+
};
|
|
8825
|
+
}
|
|
8826
|
+
const written = await atomicWriteText(outAbs, html);
|
|
8827
|
+
if (!written.ok) {
|
|
8828
|
+
return { exitCode: ExitCode.WRITE_FAILED, result: written };
|
|
8829
|
+
}
|
|
8830
|
+
return {
|
|
8831
|
+
exitCode: ExitCode.OK,
|
|
8832
|
+
result: ok({
|
|
8833
|
+
page: input.page,
|
|
8834
|
+
out: outAbs,
|
|
8835
|
+
bytes: Buffer.byteLength(html, "utf8"),
|
|
8836
|
+
humanHint: `Wrote ${outAbs} (${Buffer.byteLength(html, "utf8")} bytes)`
|
|
8837
|
+
})
|
|
8838
|
+
};
|
|
8839
|
+
}
|
|
8840
|
+
|
|
8432
8841
|
// src/commands/project-page-publish.ts
|
|
8433
8842
|
import { realpathSync as realpathSync4 } from "fs";
|
|
8434
|
-
import { mkdir as
|
|
8435
|
-
import { dirname as
|
|
8843
|
+
import { mkdir as mkdir12, readFile as readFile18 } from "fs/promises";
|
|
8844
|
+
import { dirname as dirname11, join as join34, resolve as resolve7 } from "path";
|
|
8436
8845
|
|
|
8437
8846
|
// src/utils/architecture-page.ts
|
|
8438
8847
|
import { lstatSync, realpathSync as realpathSync3 } from "fs";
|
|
8439
|
-
import { dirname as
|
|
8848
|
+
import { dirname as dirname10, posix as posix2, relative as relative3, resolve as resolve6, sep as sep3 } from "path";
|
|
8440
8849
|
var PROJECT_SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
8441
8850
|
var ARCH_TARGET_RE = /^projects\/([a-z0-9][a-z0-9-]*)\/architecture\/([a-z0-9][a-z0-9._-]*\.md)$/;
|
|
8442
8851
|
function invalidFrontmatter(target, issues) {
|
|
@@ -8482,15 +8891,15 @@ function assertArchitectureTargetInsideVault(vault, target, project) {
|
|
|
8482
8891
|
} catch {
|
|
8483
8892
|
return err("VAULT_PATH_INVALID", { target, message: "vault realpath failed" });
|
|
8484
8893
|
}
|
|
8485
|
-
const absolutePath =
|
|
8486
|
-
const parent =
|
|
8894
|
+
const absolutePath = resolve6(vaultReal, target);
|
|
8895
|
+
const parent = dirname10(absolutePath);
|
|
8487
8896
|
let parentReal;
|
|
8488
8897
|
try {
|
|
8489
8898
|
parentReal = realpathSync3(parent);
|
|
8490
8899
|
} catch {
|
|
8491
8900
|
return err("VAULT_PATH_INVALID", { target, message: "target parent realpath failed" });
|
|
8492
8901
|
}
|
|
8493
|
-
const parentRelative =
|
|
8902
|
+
const parentRelative = relative3(vaultReal, parentReal).split(sep3).join("/");
|
|
8494
8903
|
if (parentRelative === ".." || parentRelative.startsWith("../")) {
|
|
8495
8904
|
return err("VAULT_PATH_INVALID", { target, message: "target parent escapes vault" });
|
|
8496
8905
|
}
|
|
@@ -8604,7 +9013,7 @@ function validateLogNote(logNote) {
|
|
|
8604
9013
|
async function prepareProjectPagePublication(input) {
|
|
8605
9014
|
let content;
|
|
8606
9015
|
try {
|
|
8607
|
-
content = await
|
|
9016
|
+
content = await readFile18(input.draftPath, "utf8");
|
|
8608
9017
|
} catch (error) {
|
|
8609
9018
|
return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
|
|
8610
9019
|
}
|
|
@@ -8619,7 +9028,7 @@ async function prepareProjectPagePublication(input) {
|
|
|
8619
9028
|
}
|
|
8620
9029
|
const target = assertArchitectureTargetInsideVault(input.vault, input.target, input.project);
|
|
8621
9030
|
if (!target.ok) return target;
|
|
8622
|
-
if (
|
|
9031
|
+
if (resolve7(input.draftPath) === target.data.absolutePath || target.data.existingRealPath !== void 0 && draftRealPath === target.data.existingRealPath) {
|
|
8623
9032
|
return err("VAULT_PATH_INVALID", { message: "draft must not alias the final target" });
|
|
8624
9033
|
}
|
|
8625
9034
|
const isNew = target.data.existingRealPath === void 0;
|
|
@@ -8737,9 +9146,9 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8737
9146
|
async updateIndicesLocked(prepared, vault, state, observe) {
|
|
8738
9147
|
const rendered = await renderProjectIndex(vault, prepared.page.project, { today: prepared.date });
|
|
8739
9148
|
if (!rendered.ok) return rendered;
|
|
8740
|
-
const knowledgePath =
|
|
9149
|
+
const knowledgePath = join34(vault, rendered.data.index_path);
|
|
8741
9150
|
try {
|
|
8742
|
-
await
|
|
9151
|
+
await mkdir12(dirname11(knowledgePath), { recursive: true });
|
|
8743
9152
|
} catch (error) {
|
|
8744
9153
|
return err("WRITE_FAILED", { message: String(error) });
|
|
8745
9154
|
}
|
|
@@ -8748,7 +9157,7 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8748
9157
|
state.projectIndexUpdated = knowledgeWrite.data.changed;
|
|
8749
9158
|
if (state.projectIndexUpdated) state.changed.add(rendered.data.index_path);
|
|
8750
9159
|
try {
|
|
8751
|
-
const visibleIndex = await
|
|
9160
|
+
const visibleIndex = await readFile18(knowledgePath, "utf8");
|
|
8752
9161
|
if (visibleIndex !== rendered.data.text) {
|
|
8753
9162
|
return err("WRITE_FAILED", { message: "project knowledge.md verification failed" });
|
|
8754
9163
|
}
|
|
@@ -8768,9 +9177,9 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8768
9177
|
}
|
|
8769
9178
|
let projectIndexChanged = true;
|
|
8770
9179
|
if (projectIndex.ok) {
|
|
8771
|
-
const indexPath =
|
|
9180
|
+
const indexPath = join34(vault, projectIndex.data.index_path);
|
|
8772
9181
|
try {
|
|
8773
|
-
const existing = await
|
|
9182
|
+
const existing = await readFile18(indexPath, "utf8");
|
|
8774
9183
|
projectIndexChanged = existing !== projectIndex.data.text || pageChanged;
|
|
8775
9184
|
} catch {
|
|
8776
9185
|
projectIndexChanged = true;
|
|
@@ -8930,7 +9339,7 @@ import { existsSync as existsSync11, writeFileSync as writeFileSync6, mkdirSync
|
|
|
8930
9339
|
import { createHash as createHash6 } from "crypto";
|
|
8931
9340
|
import { execSync as execSync3, spawn } from "child_process";
|
|
8932
9341
|
import { platform as platform2 } from "os";
|
|
8933
|
-
import { join as
|
|
9342
|
+
import { join as join35, resolve as resolvePath } from "path";
|
|
8934
9343
|
var MAINTENANCE_SCHEMA_VERSION = 1;
|
|
8935
9344
|
var MAINTENANCE_COMMAND = "snapshot-maintenance journal clear-stale";
|
|
8936
9345
|
var REPAIR_COMMAND = "snapshot-maintenance projection-conflict repair";
|
|
@@ -8995,7 +9404,7 @@ async function runSnapshotMaintenanceDryRun(input) {
|
|
|
8995
9404
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE"));
|
|
8996
9405
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
8997
9406
|
}
|
|
8998
|
-
if (!existsSync11(requested) || !existsSync11(
|
|
9407
|
+
if (!existsSync11(requested) || !existsSync11(join35(requested, ".git"))) {
|
|
8999
9408
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO"));
|
|
9000
9409
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
9001
9410
|
}
|
|
@@ -9075,14 +9484,14 @@ async function resolveLiveVault(input) {
|
|
|
9075
9484
|
return resolved.ok ? canonicalize(resolved.data.path) : void 0;
|
|
9076
9485
|
}
|
|
9077
9486
|
function snapshotMaintenanceAuditLogPath(home) {
|
|
9078
|
-
const stateDir = platform2() === "darwin" ?
|
|
9079
|
-
return
|
|
9487
|
+
const stateDir = platform2() === "darwin" ? join35(home, "Library", "Application Support", "vault-sync") : join35(home, ".local", "state", "vault-sync");
|
|
9488
|
+
return join35(stateDir, "snapshot-maintenance-audit.jsonl");
|
|
9080
9489
|
}
|
|
9081
9490
|
function defaultAuditSink(home) {
|
|
9082
9491
|
return (event) => {
|
|
9083
9492
|
try {
|
|
9084
9493
|
const logPath = snapshotMaintenanceAuditLogPath(home);
|
|
9085
|
-
mkdirSync4(
|
|
9494
|
+
mkdirSync4(join35(logPath, ".."), { recursive: true });
|
|
9086
9495
|
appendFileSync(logPath, JSON.stringify(event) + "\n", { encoding: "utf8" });
|
|
9087
9496
|
} catch {
|
|
9088
9497
|
}
|
|
@@ -9230,9 +9639,9 @@ function nonMergeSequencerActive(repo) {
|
|
|
9230
9639
|
const gitDir = git(repo, ["rev-parse", "--absolute-git-dir"]);
|
|
9231
9640
|
if (!gitDir) return false;
|
|
9232
9641
|
for (const m of ["CHERRY_PICK_HEAD", "REVERT_HEAD"]) {
|
|
9233
|
-
if (existsSync11(
|
|
9642
|
+
if (existsSync11(join35(gitDir, m))) return true;
|
|
9234
9643
|
}
|
|
9235
|
-
return existsSync11(
|
|
9644
|
+
return existsSync11(join35(gitDir, "rebase-merge")) || existsSync11(join35(gitDir, "rebase-apply"));
|
|
9236
9645
|
}
|
|
9237
9646
|
function extraUnmergedPaths(unmerged) {
|
|
9238
9647
|
const allow = new Set(PROJECTION_CONFLICT_ALLOWLIST);
|
|
@@ -9271,7 +9680,7 @@ async function authorizeRepairContext(input) {
|
|
|
9271
9680
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE", void 0, REPAIR_COMMAND));
|
|
9272
9681
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
9273
9682
|
}
|
|
9274
|
-
if (!existsSync11(requested) || !existsSync11(
|
|
9683
|
+
if (!existsSync11(requested) || !existsSync11(join35(requested, ".git"))) {
|
|
9275
9684
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO", void 0, REPAIR_COMMAND));
|
|
9276
9685
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
9277
9686
|
}
|
|
@@ -9372,8 +9781,8 @@ async function performProjectionRepair(input, plan, audit, now) {
|
|
|
9372
9781
|
return { exitCode: ExitCode.WRITE_FAILED, result: rendered };
|
|
9373
9782
|
}
|
|
9374
9783
|
try {
|
|
9375
|
-
writeFileSync6(
|
|
9376
|
-
writeFileSync6(
|
|
9784
|
+
writeFileSync6(join35(plan.snapshot_worktree, "index.md"), rendered.data.index);
|
|
9785
|
+
writeFileSync6(join35(plan.snapshot_worktree, "log.md"), rendered.data.log);
|
|
9377
9786
|
gitStrict(plan.snapshot_worktree, ["add", "--", "index.md", "log.md"]);
|
|
9378
9787
|
gitStrict(plan.snapshot_worktree, [
|
|
9379
9788
|
"commit",
|
|
@@ -9481,7 +9890,7 @@ function releaseSnapshotFlock(handle) {
|
|
|
9481
9890
|
|
|
9482
9891
|
// src/commands/backup.ts
|
|
9483
9892
|
import { statSync as statSync3, readdirSync as readdirSync2, readFileSync as readFileSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
9484
|
-
import { join as
|
|
9893
|
+
import { join as join36, relative as relative4, dirname as dirname12 } from "path";
|
|
9485
9894
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
9486
9895
|
|
|
9487
9896
|
// src/utils/s3-client.ts
|
|
@@ -9505,11 +9914,11 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
|
|
|
9505
9914
|
function* walkMarkdown(dir, base) {
|
|
9506
9915
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
9507
9916
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
9508
|
-
const full =
|
|
9917
|
+
const full = join36(dir, entry.name);
|
|
9509
9918
|
if (entry.isDirectory()) {
|
|
9510
9919
|
yield* walkMarkdown(full, base);
|
|
9511
9920
|
} else if (entry.name.endsWith(".md")) {
|
|
9512
|
-
yield
|
|
9921
|
+
yield relative4(base, full).replace(/\\/g, "/");
|
|
9513
9922
|
}
|
|
9514
9923
|
}
|
|
9515
9924
|
}
|
|
@@ -9528,7 +9937,7 @@ async function runBackupSync(input) {
|
|
|
9528
9937
|
let failed = 0;
|
|
9529
9938
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
9530
9939
|
for (const relPath of files) {
|
|
9531
|
-
const absPath =
|
|
9940
|
+
const absPath = join36(input.vault, relPath);
|
|
9532
9941
|
const localStat = statSync3(absPath);
|
|
9533
9942
|
let needsUpload = true;
|
|
9534
9943
|
try {
|
|
@@ -9604,7 +10013,7 @@ async function runBackupRestore(input) {
|
|
|
9604
10013
|
const objects = list.Contents ?? [];
|
|
9605
10014
|
for (const obj of objects) {
|
|
9606
10015
|
if (!obj.Key) continue;
|
|
9607
|
-
const localPath =
|
|
10016
|
+
const localPath = join36(target, obj.Key);
|
|
9608
10017
|
try {
|
|
9609
10018
|
const localStat = statSync3(localPath);
|
|
9610
10019
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -9617,7 +10026,7 @@ async function runBackupRestore(input) {
|
|
|
9617
10026
|
const resp = await client.send(new GetObjectCommand({ Bucket: input.bucket, Key: obj.Key }));
|
|
9618
10027
|
const body = await resp.Body?.transformToByteArray();
|
|
9619
10028
|
if (body) {
|
|
9620
|
-
mkdirSync5(
|
|
10029
|
+
mkdirSync5(dirname12(localPath), { recursive: true });
|
|
9621
10030
|
writeFileSync7(localPath, Buffer.from(body));
|
|
9622
10031
|
downloaded++;
|
|
9623
10032
|
}
|
|
@@ -9651,8 +10060,8 @@ async function runBackupRestore(input) {
|
|
|
9651
10060
|
|
|
9652
10061
|
// src/commands/status.ts
|
|
9653
10062
|
import { existsSync as existsSync12, statSync as statSync4 } from "fs";
|
|
9654
|
-
import { readFile as
|
|
9655
|
-
import { join as
|
|
10063
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
10064
|
+
import { join as join37 } from "path";
|
|
9656
10065
|
var FUSE_NO_MIRROR_NOTE = "FUSE vault with no read mirror - status scan may be slow";
|
|
9657
10066
|
async function runStatus(input) {
|
|
9658
10067
|
if (!existsSync12(input.vault)) {
|
|
@@ -9691,7 +10100,7 @@ async function runStatus(input) {
|
|
|
9691
10100
|
const compound = scan.data.compound.length;
|
|
9692
10101
|
let schemaVersion = "v1";
|
|
9693
10102
|
try {
|
|
9694
|
-
const schemaContent = await
|
|
10103
|
+
const schemaContent = await readFile19(join37(scanRoot, "SCHEMA.md"), "utf8");
|
|
9695
10104
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
9696
10105
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
9697
10106
|
} catch {
|
|
@@ -9759,25 +10168,25 @@ async function runStatus(input) {
|
|
|
9759
10168
|
|
|
9760
10169
|
// src/commands/copy-status.ts
|
|
9761
10170
|
import { existsSync as existsSync13, realpathSync as realpathSync5 } from "fs";
|
|
9762
|
-
import { isAbsolute as isAbsolute2, join as
|
|
10171
|
+
import { isAbsolute as isAbsolute2, join as join39, relative as relative6, resolve as resolve9 } from "path";
|
|
9763
10172
|
|
|
9764
10173
|
// src/utils/fetch-projection.ts
|
|
9765
|
-
import { isAbsolute, join as
|
|
10174
|
+
import { isAbsolute, join as join38, resolve as resolve8 } from "path";
|
|
9766
10175
|
function inspectConfiguredFetchProjection(home) {
|
|
9767
10176
|
if (!home) return { configured: false };
|
|
9768
|
-
const config = readSkillWikiConfig(
|
|
10177
|
+
const config = readSkillWikiConfig(join38(home, ".skillwiki", ".env"));
|
|
9769
10178
|
const configured = config["vault_sync.fetch_projection"];
|
|
9770
10179
|
if (!configured || configured === "none") return { configured: false };
|
|
9771
10180
|
if (!isAbsolute(configured)) {
|
|
9772
10181
|
return { configured: true, invalidDetail: "configured fetch projection path must be absolute" };
|
|
9773
10182
|
}
|
|
9774
|
-
return { configured: true, path:
|
|
10183
|
+
return { configured: true, path: resolve8(configured) };
|
|
9775
10184
|
}
|
|
9776
10185
|
|
|
9777
10186
|
// src/utils/live-drift.ts
|
|
9778
10187
|
import { createHash as createHash7 } from "crypto";
|
|
9779
|
-
import { readFile as
|
|
9780
|
-
import { relative as
|
|
10188
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
10189
|
+
import { relative as relative5, sep as sep4 } from "path";
|
|
9781
10190
|
|
|
9782
10191
|
// src/utils/git-promotion.ts
|
|
9783
10192
|
function normalizedRelativePath(path) {
|
|
@@ -9818,10 +10227,10 @@ async function measureAuthoritativeLiveDrift(liveVault, projection) {
|
|
|
9818
10227
|
let content = 0;
|
|
9819
10228
|
const livePromotable = /* @__PURE__ */ new Set();
|
|
9820
10229
|
for (const page of scan.data.allMarkdown) {
|
|
9821
|
-
const rel =
|
|
10230
|
+
const rel = relative5(liveVault, page.absPath).split(sep4).join("/");
|
|
9822
10231
|
if (!isGitPromotablePath(rel)) continue;
|
|
9823
10232
|
livePromotable.add(rel);
|
|
9824
|
-
const bytes = await
|
|
10233
|
+
const bytes = await readFile20(page.absPath);
|
|
9825
10234
|
if (projectionBlobs.get(rel) !== gitBlobOid(bytes)) content += 1;
|
|
9826
10235
|
}
|
|
9827
10236
|
for (const rel of projectionBlobs.keys()) {
|
|
@@ -9996,11 +10405,11 @@ function comparablePath(path) {
|
|
|
9996
10405
|
try {
|
|
9997
10406
|
return realpathSync5(path);
|
|
9998
10407
|
} catch {
|
|
9999
|
-
return
|
|
10408
|
+
return resolve9(path);
|
|
10000
10409
|
}
|
|
10001
10410
|
}
|
|
10002
10411
|
function pathsAreNested(left, right) {
|
|
10003
|
-
const rel =
|
|
10412
|
+
const rel = relative6(comparablePath(left), comparablePath(right));
|
|
10004
10413
|
return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
|
|
10005
10414
|
}
|
|
10006
10415
|
function defaultCopyStatusDeps(input) {
|
|
@@ -10016,7 +10425,7 @@ function defaultCopyStatusDeps(input) {
|
|
|
10016
10425
|
const projectionWarning = selection.configured ? `configured fetch projection ignored for Git status; using live vault${projectionProblem ? ` (${projectionProblem})` : ""}` : void 0;
|
|
10017
10426
|
const invalidProjectionDetail = projectionProblem ?? "configured fetch projection is not a git repository";
|
|
10018
10427
|
const gitRootProblem = () => {
|
|
10019
|
-
if (!existsSync13(
|
|
10428
|
+
if (!existsSync13(join39(gitVault, ".git"))) {
|
|
10020
10429
|
return "vault is not a git repository";
|
|
10021
10430
|
}
|
|
10022
10431
|
return void 0;
|
|
@@ -10096,7 +10505,7 @@ function defaultCopyStatusDeps(input) {
|
|
|
10096
10505
|
detail: projectionProblem ?? "live drift requires a configured fetch projection"
|
|
10097
10506
|
};
|
|
10098
10507
|
}
|
|
10099
|
-
if (!existsSync13(
|
|
10508
|
+
if (!existsSync13(join39(configuredProjection, ".git"))) {
|
|
10100
10509
|
return {
|
|
10101
10510
|
unknown: true,
|
|
10102
10511
|
reason_code: "fetch_projection_invalid",
|
|
@@ -10116,8 +10525,8 @@ async function runCopyStatusCommand(input) {
|
|
|
10116
10525
|
}
|
|
10117
10526
|
|
|
10118
10527
|
// src/commands/seed.ts
|
|
10119
|
-
import { mkdir as
|
|
10120
|
-
import { join as
|
|
10528
|
+
import { mkdir as mkdir13, writeFile as writeFile7, stat as stat5 } from "fs/promises";
|
|
10529
|
+
import { join as join40 } from "path";
|
|
10121
10530
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10122
10531
|
var EXAMPLE_PAGES = {
|
|
10123
10532
|
"entities/example-project.md": `---
|
|
@@ -10186,29 +10595,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
10186
10595
|
`;
|
|
10187
10596
|
async function runSeed(input) {
|
|
10188
10597
|
try {
|
|
10189
|
-
await stat5(
|
|
10598
|
+
await stat5(join40(input.vault, "SCHEMA.md"));
|
|
10190
10599
|
} catch {
|
|
10191
10600
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
10192
10601
|
}
|
|
10193
10602
|
const created = [];
|
|
10194
10603
|
const skipped = [];
|
|
10195
10604
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
10196
|
-
const absPath =
|
|
10605
|
+
const absPath = join40(input.vault, relPath);
|
|
10197
10606
|
try {
|
|
10198
10607
|
await stat5(absPath);
|
|
10199
10608
|
skipped.push(relPath);
|
|
10200
10609
|
} catch {
|
|
10201
|
-
await
|
|
10610
|
+
await mkdir13(join40(absPath, ".."), { recursive: true });
|
|
10202
10611
|
await writeFile7(absPath, content, "utf8");
|
|
10203
10612
|
created.push(relPath);
|
|
10204
10613
|
}
|
|
10205
10614
|
}
|
|
10206
|
-
const rawPath =
|
|
10615
|
+
const rawPath = join40(input.vault, "raw", "articles", "example-source.md");
|
|
10207
10616
|
try {
|
|
10208
10617
|
await stat5(rawPath);
|
|
10209
10618
|
skipped.push("raw/articles/example-source.md");
|
|
10210
10619
|
} catch {
|
|
10211
|
-
await
|
|
10620
|
+
await mkdir13(join40(rawPath, ".."), { recursive: true });
|
|
10212
10621
|
await writeFile7(rawPath, EXAMPLE_RAW, "utf8");
|
|
10213
10622
|
created.push("raw/articles/example-source.md");
|
|
10214
10623
|
}
|
|
@@ -10231,9 +10640,9 @@ async function runSeed(input) {
|
|
|
10231
10640
|
}
|
|
10232
10641
|
|
|
10233
10642
|
// src/commands/canvas.ts
|
|
10234
|
-
import { readFile as
|
|
10643
|
+
import { readFile as readFile21, writeFile as writeFile8 } from "fs/promises";
|
|
10235
10644
|
import { existsSync as existsSync14 } from "fs";
|
|
10236
|
-
import { join as
|
|
10645
|
+
import { join as join41 } from "path";
|
|
10237
10646
|
var NODE_WIDTH = 240;
|
|
10238
10647
|
var NODE_HEIGHT = 60;
|
|
10239
10648
|
var COLUMN_SPACING = 400;
|
|
@@ -10311,7 +10720,7 @@ function buildCanvasEdges(adjacency) {
|
|
|
10311
10720
|
return edges;
|
|
10312
10721
|
}
|
|
10313
10722
|
async function runCanvasGenerate(input) {
|
|
10314
|
-
const graphPath = input.graphPath ??
|
|
10723
|
+
const graphPath = input.graphPath ?? join41(input.vault, ".skillwiki", "graph.json");
|
|
10315
10724
|
if (!existsSync14(graphPath)) {
|
|
10316
10725
|
return {
|
|
10317
10726
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -10323,7 +10732,7 @@ async function runCanvasGenerate(input) {
|
|
|
10323
10732
|
}
|
|
10324
10733
|
let raw;
|
|
10325
10734
|
try {
|
|
10326
|
-
raw = await
|
|
10735
|
+
raw = await readFile21(graphPath, "utf8");
|
|
10327
10736
|
} catch (e) {
|
|
10328
10737
|
return {
|
|
10329
10738
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -10349,7 +10758,7 @@ async function runCanvasGenerate(input) {
|
|
|
10349
10758
|
const nodes = buildCanvasNodes(paths);
|
|
10350
10759
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
10351
10760
|
const canvas = { nodes, edges };
|
|
10352
|
-
const outPath =
|
|
10761
|
+
const outPath = join41(input.vault, "vault-graph.canvas");
|
|
10353
10762
|
try {
|
|
10354
10763
|
await writeFile8(outPath, JSON.stringify(canvas, null, 2));
|
|
10355
10764
|
} catch (e) {
|
|
@@ -10494,11 +10903,11 @@ async function runSourceDisposition(input) {
|
|
|
10494
10903
|
|
|
10495
10904
|
// src/utils/source-disposal.ts
|
|
10496
10905
|
import { createHash as createHash8 } from "crypto";
|
|
10497
|
-
import { readFile as
|
|
10906
|
+
import { readFile as readFile23, unlink as unlink5 } from "fs/promises";
|
|
10498
10907
|
|
|
10499
10908
|
// src/utils/raw-asset-reference-index.ts
|
|
10500
|
-
import { readdir as readdir6, readFile as
|
|
10501
|
-
import { join as
|
|
10909
|
+
import { readdir as readdir6, readFile as readFile22 } from "fs/promises";
|
|
10910
|
+
import { join as join42, relative as relative7, sep as sep5 } from "path";
|
|
10502
10911
|
var PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "pdf", "mp3", "wav", "m4a", "mp4", "webm"]);
|
|
10503
10912
|
async function walkFiles(root, dir = root) {
|
|
10504
10913
|
let entries;
|
|
@@ -10509,9 +10918,9 @@ async function walkFiles(root, dir = root) {
|
|
|
10509
10918
|
}
|
|
10510
10919
|
const out = [];
|
|
10511
10920
|
for (const entry of entries) {
|
|
10512
|
-
const path =
|
|
10921
|
+
const path = join42(dir, entry.name);
|
|
10513
10922
|
if (entry.isDirectory()) out.push(...await walkFiles(root, path));
|
|
10514
|
-
else if (entry.isFile()) out.push(
|
|
10923
|
+
else if (entry.isFile()) out.push(relative7(root, path).split(sep5).join("/"));
|
|
10515
10924
|
}
|
|
10516
10925
|
return out;
|
|
10517
10926
|
}
|
|
@@ -10525,7 +10934,7 @@ function addInbound(map, target, source) {
|
|
|
10525
10934
|
map.set(target, refs);
|
|
10526
10935
|
}
|
|
10527
10936
|
async function buildRawAssetReferenceIndex(vault) {
|
|
10528
|
-
const assetRoot =
|
|
10937
|
+
const assetRoot = join42(vault, "raw", "assets");
|
|
10529
10938
|
const relativeAssets = await walkFiles(assetRoot);
|
|
10530
10939
|
const assets = relativeAssets.map((path) => `raw/assets/${path}`).sort((a, b) => a.localeCompare(b));
|
|
10531
10940
|
const assetSet = new Set(assets);
|
|
@@ -10541,7 +10950,7 @@ async function buildRawAssetReferenceIndex(vault) {
|
|
|
10541
10950
|
for (const page of scan.data.allMarkdown) {
|
|
10542
10951
|
let text;
|
|
10543
10952
|
try {
|
|
10544
|
-
text = await
|
|
10953
|
+
text = await readFile22(page.absPath, "utf8");
|
|
10545
10954
|
} catch {
|
|
10546
10955
|
continue;
|
|
10547
10956
|
}
|
|
@@ -10601,7 +11010,7 @@ async function planSourceDisposal(input) {
|
|
|
10601
11010
|
if (sensitive.length > 0) return err("SENSITIVE_CONTENT_DETECTED", { findings: sensitive });
|
|
10602
11011
|
const target = await resolveExistingRegularFileInsideVault(input.vault, input.rawPath);
|
|
10603
11012
|
if (!target.ok) return err("RAW_DISPOSAL_TARGET_INVALID", { path: input.rawPath, cause: target });
|
|
10604
|
-
const bytes = await
|
|
11013
|
+
const bytes = await readFile23(target.data);
|
|
10605
11014
|
const size = bytes.byteLength;
|
|
10606
11015
|
let typedReferences = [];
|
|
10607
11016
|
let otherReferences = [];
|
|
@@ -10725,7 +11134,7 @@ async function runSourceDisposal(input) {
|
|
|
10725
11134
|
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
10726
11135
|
import { execSync as nodeExecSync } from "child_process";
|
|
10727
11136
|
import { hostname as nodeHostname, platform as nodePlatform } from "os";
|
|
10728
|
-
import { join as
|
|
11137
|
+
import { join as join43 } from "path";
|
|
10729
11138
|
var SSH_TIMEOUT_MS = 15e3;
|
|
10730
11139
|
var TIMER_UNIT = "agent-memory-trends.timer";
|
|
10731
11140
|
var SERVICE_UNIT = "agent-memory-trends.service";
|
|
@@ -10955,7 +11364,7 @@ async function runFleetHealth(input) {
|
|
|
10955
11364
|
const home = resolveSkillwikiHome(input.home, env);
|
|
10956
11365
|
const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
|
|
10957
11366
|
const vault = input.vault ?? env.WIKI_PATH;
|
|
10958
|
-
const file = input.file ?? (vault ?
|
|
11367
|
+
const file = input.file ?? (vault ? join43(vault, FLEET_REL_PATH) : void 0);
|
|
10959
11368
|
if (!file) {
|
|
10960
11369
|
return {
|
|
10961
11370
|
exitCode: ExitCode.NO_VAULT_CONFIGURED,
|
|
@@ -11040,7 +11449,7 @@ async function runFleetHealth(input) {
|
|
|
11040
11449
|
|
|
11041
11450
|
// src/utils/auto-commit.ts
|
|
11042
11451
|
import { existsSync as existsSync16 } from "fs";
|
|
11043
|
-
import { isAbsolute as isAbsolute3, join as
|
|
11452
|
+
import { isAbsolute as isAbsolute3, join as join44, normalize } from "path";
|
|
11044
11453
|
function isValidLastOpPath(file) {
|
|
11045
11454
|
if (!file || typeof file !== "string") return false;
|
|
11046
11455
|
const trimmed = file.trim();
|
|
@@ -11061,7 +11470,7 @@ async function postCommit(vault, exitCode) {
|
|
|
11061
11470
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
11062
11471
|
const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
|
|
11063
11472
|
if (autoCommit === "false") return;
|
|
11064
|
-
if (!existsSync16(
|
|
11473
|
+
if (!existsSync16(join44(vault, ".git"))) return;
|
|
11065
11474
|
const lastOps = readLastOp(vault);
|
|
11066
11475
|
if (lastOps.length === 0) return;
|
|
11067
11476
|
const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
|
|
@@ -11109,7 +11518,7 @@ function preflight(error) {
|
|
|
11109
11518
|
}
|
|
11110
11519
|
async function runMcpAuthIssueHost(input) {
|
|
11111
11520
|
const isTty = input.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
11112
|
-
const
|
|
11521
|
+
const readFile24 = input.readFile ?? readFileSync17;
|
|
11113
11522
|
const writeFile9 = input.writeFile ?? writeFileSync8;
|
|
11114
11523
|
const stderrWrite = input.stderrWrite ?? ((s) => {
|
|
11115
11524
|
process.stderr.write(s);
|
|
@@ -11124,7 +11533,7 @@ async function runMcpAuthIssueHost(input) {
|
|
|
11124
11533
|
if (write && !isTty) return preflight("NO_TTY");
|
|
11125
11534
|
let yamlText = "";
|
|
11126
11535
|
try {
|
|
11127
|
-
yamlText =
|
|
11536
|
+
yamlText = readFile24(mapPath, "utf8");
|
|
11128
11537
|
} catch (error) {
|
|
11129
11538
|
if (!isEnoent(error)) throw error;
|
|
11130
11539
|
}
|
|
@@ -11242,8 +11651,8 @@ async function emit(r, vault, opts) {
|
|
|
11242
11651
|
printJson(r.result);
|
|
11243
11652
|
}
|
|
11244
11653
|
if (vault && opts?.postCommit !== false) await postCommit(vault, r.exitCode);
|
|
11245
|
-
await new Promise((
|
|
11246
|
-
process.stdout.write("", (error) => error ? reject(error) :
|
|
11654
|
+
await new Promise((resolve10, reject) => {
|
|
11655
|
+
process.stdout.write("", (error) => error ? reject(error) : resolve10());
|
|
11247
11656
|
});
|
|
11248
11657
|
process.exit(r.exitCode);
|
|
11249
11658
|
}
|
|
@@ -11306,7 +11715,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
|
|
|
11306
11715
|
if (dirty) {
|
|
11307
11716
|
return emit(dirty, void 0, { postCommit: false });
|
|
11308
11717
|
}
|
|
11309
|
-
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-
|
|
11718
|
+
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-2BMHZWVJ.js");
|
|
11310
11719
|
const run = await runManagedWriteTransaction2({
|
|
11311
11720
|
vault,
|
|
11312
11721
|
command,
|
|
@@ -11336,7 +11745,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
11336
11745
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
11337
11746
|
});
|
|
11338
11747
|
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) => {
|
|
11339
|
-
const out = opts.out ??
|
|
11748
|
+
const out = opts.out ?? join45(vault, ".skillwiki", "graph.json");
|
|
11340
11749
|
return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
|
|
11341
11750
|
});
|
|
11342
11751
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
|
@@ -11680,6 +12089,15 @@ pageCmd.command("publish <draft> [vault]").description("preview or publish an un
|
|
|
11680
12089
|
{ postCommit: false }
|
|
11681
12090
|
);
|
|
11682
12091
|
});
|
|
12092
|
+
pageCmd.command("render-html <page> [vault]").description("render an opted-in typed page to a self-contained HTML file").requiredOption("--out <file>", "output HTML path (must be outside the vault)").option("--wiki <name>", "wiki profile name").action(async (page, vault, opts) => {
|
|
12093
|
+
const resolved = await resolveVaultArg(vault, opts.wiki);
|
|
12094
|
+
if (!resolved.ok) return emit({ exitCode: resolved.exitCode, result: resolved.payload });
|
|
12095
|
+
return emit(await runPageRenderHtml({
|
|
12096
|
+
vault: resolved.vault,
|
|
12097
|
+
page,
|
|
12098
|
+
out: opts.out
|
|
12099
|
+
}), resolved.vault, { postCommit: false });
|
|
12100
|
+
});
|
|
11683
12101
|
var projectPageCmd = program.command("project-page").description("publish project-local architecture pages");
|
|
11684
12102
|
projectPageCmd.command("publish <draft> [vault]").description("preview or publish a Layer-3 architecture page under projects/{slug}/architecture/").requiredOption("--project <slug>", "project slug").requiredOption("--target <path>", "vault-relative architecture target").option("--log-note <text>", "single-line publication log note").option("--write", "publish SCHEMA, page, project knowledge index, and log", false).option("--approve <token>", "required with --write; token from a prior dry-run").option("--wiki <name>", "wiki profile name").action(async (draft, vault, opts) => {
|
|
11685
12103
|
const resolved = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -12453,7 +12871,7 @@ memoryCmd.command("import [vault]").description("preview or apply local memory i
|
|
|
12453
12871
|
maxBytes: opts.maxBytes
|
|
12454
12872
|
}), v.vault, { postCommit: false });
|
|
12455
12873
|
});
|
|
12456
|
-
program.command("ingest <source>").description("ingest a source URL or local file into the vault").requiredOption("--vault <path>", "vault root directory").requiredOption("--type <type>", "typed-knowledge type (entity|concept|comparison|query)").requiredOption("--title <title>", "page title").option("--tags <csv>", "comma-separated tags").option("--provenance <provenance>", "provenance (research|project)").option("--dry-run", "preview without writing files", false).action(async (source, opts) => {
|
|
12874
|
+
program.command("ingest <source>").description("ingest a source URL or local file into the vault").requiredOption("--vault <path>", "vault root directory").requiredOption("--type <type>", "typed-knowledge type (entity|concept|comparison|query)").requiredOption("--title <title>", "page title").option("--tags <csv>", "comma-separated tags").option("--provenance <provenance>", "provenance (research|project)").option("--html", "stamp html_render: true on the typed stub", false).option("--dry-run", "preview without writing files", false).action(async (source, opts) => {
|
|
12457
12875
|
const tags = typeof opts.tags === "string" ? opts.tags.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
|
|
12458
12876
|
if (opts.dryRun) {
|
|
12459
12877
|
emit(await runIngest({
|
|
@@ -12463,6 +12881,7 @@ program.command("ingest <source>").description("ingest a source URL or local fil
|
|
|
12463
12881
|
title: opts.title,
|
|
12464
12882
|
tags,
|
|
12465
12883
|
provenance: opts.provenance,
|
|
12884
|
+
html: !!opts.html,
|
|
12466
12885
|
dryRun: true
|
|
12467
12886
|
}), opts.vault);
|
|
12468
12887
|
return;
|
|
@@ -12477,6 +12896,7 @@ program.command("ingest <source>").description("ingest a source URL or local fil
|
|
|
12477
12896
|
title: opts.title,
|
|
12478
12897
|
tags,
|
|
12479
12898
|
provenance: opts.provenance,
|
|
12899
|
+
html: !!opts.html,
|
|
12480
12900
|
dryRun: false
|
|
12481
12901
|
})
|
|
12482
12902
|
);
|