skillwiki 0.10.112 → 0.10.114

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.
Files changed (48) hide show
  1. package/dist/{chunk-KJG2CSYD.js → chunk-3TKNAOJH.js} +2 -2
  2. package/dist/{chunk-LOVLZNLX.js → chunk-5D5HFQ76.js} +1 -1
  3. package/dist/{chunk-APKDTEVV.js → chunk-5RHR4FEK.js} +18 -14
  4. package/dist/{chunk-EE7W3GLA.js → chunk-6VUK2FTX.js} +1 -1
  5. package/dist/{chunk-RR6VDWSI.js → chunk-CBT35FAW.js} +3 -3
  6. package/dist/{chunk-Q6EZF37H.js → chunk-FQERKMD7.js} +456 -16
  7. package/dist/{chunk-SKOLE6US.js → chunk-IJKJGQKL.js} +1 -1
  8. package/dist/{chunk-IR2DI4Y5.js → chunk-IJWZDQDH.js} +3 -3
  9. package/dist/{chunk-TKX6PSWZ.js → chunk-JL2SCLXT.js} +1 -1
  10. package/dist/{chunk-CKQPUHM2.js → chunk-K3YZESND.js} +2 -2
  11. package/dist/{chunk-DOHWNPAZ.js → chunk-OGEAFXON.js} +1 -1
  12. package/dist/{chunk-ABRYBO4T.js → chunk-OWWWO2XS.js} +3 -3
  13. package/dist/{chunk-FCA3RRNF.js → chunk-SEY4N4CT.js} +1 -1
  14. package/dist/{chunk-7POLZOFY.js → chunk-VNSZCK5V.js} +5 -2
  15. package/dist/{chunk-25OXNFDV.js → chunk-XJ5VEIH7.js} +3 -3
  16. package/dist/cli.js +534 -114
  17. package/dist/{index-projection-UNZG5UJZ.js → index-projection-RQSOEDXM.js} +5 -5
  18. package/dist/{journal-5IW3TZ5F.js → journal-V5XHBEI5.js} +3 -3
  19. package/dist/{managed-write-preflight-L7IRQ76C.js → managed-write-preflight-2BMHZWVJ.js} +7 -7
  20. package/dist/{mcp-auth-env-X5MI5IX6.js → mcp-auth-env-QONKW4EO.js} +3 -3
  21. package/dist/{migrate-RO4BJP5T.js → migrate-SRRCXI2K.js} +6 -6
  22. package/dist/{runtime-VMFYW2K3.js → runtime-LQZIG3XN.js} +4 -4
  23. package/dist/skillwiki-mcp.js +14 -14
  24. package/dist/sources-E6KHDRDE.js +10 -0
  25. package/dist/{watcher-MGHQD3UI.js → watcher-VJ3TOU6J.js} +4 -4
  26. package/docs/clip-outbox.md +4 -0
  27. package/package.json +1 -1
  28. package/skills/.claude-plugin/plugin.json +1 -1
  29. package/skills/.codex-plugin/plugin.json +1 -1
  30. package/skills/.cursor-plugin/plugin.json +1 -1
  31. package/skills/agents/wiki-add-task.md +2 -0
  32. package/skills/agents/wiki-ingest.md +2 -0
  33. package/skills/package.json +1 -1
  34. package/skills/skills/skillwiki-mcp/SKILL.md +3 -1
  35. package/skills/skills/using-skillwiki/SKILL.md +7 -2
  36. package/skills/skills/using-skillwiki/activation.md +1 -0
  37. package/skills/skills/using-skillwiki/references/save-attachments.md +129 -0
  38. package/skills/skills/wiki-add-task/SKILL.md +4 -2
  39. package/skills/skills/wiki-ingest/SKILL.md +50 -1
  40. package/skills/skills/wiki-lint/SKILL.md +7 -0
  41. package/skills/skillwiki-mcp/SKILL.md +3 -1
  42. package/skills/using-skillwiki/SKILL.md +7 -2
  43. package/skills/using-skillwiki/activation.md +1 -0
  44. package/skills/using-skillwiki/references/save-attachments.md +129 -0
  45. package/skills/wiki-add-task/SKILL.md +4 -2
  46. package/skills/wiki-ingest/SKILL.md +50 -1
  47. package/skills/wiki-lint/SKILL.md +7 -0
  48. 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-APKDTEVV.js";
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-RR6VDWSI.js";
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-IR2DI4Y5.js";
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-Q6EZF37H.js";
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-CKQPUHM2.js";
194
+ } from "./chunk-K3YZESND.js";
194
195
  import {
195
196
  assertTargetInsideVault,
196
197
  prepareTypedPage
197
- } from "./chunk-KJG2CSYD.js";
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-TKX6PSWZ.js";
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-DOHWNPAZ.js";
219
+ } from "./chunk-OGEAFXON.js";
219
220
  import {
220
221
  CLIP_MIGRATION_HINT,
221
222
  refreshInstalledClipPlist
222
- } from "./chunk-25OXNFDV.js";
223
+ } from "./chunk-XJ5VEIH7.js";
223
224
  import {
224
225
  atomicWriteText
225
- } from "./chunk-EE7W3GLA.js";
226
+ } from "./chunk-6VUK2FTX.js";
226
227
  import {
227
228
  CLIP_NOT_CONFIGURED,
228
229
  loadClipOutboxConfig
229
- } from "./chunk-ABRYBO4T.js";
230
+ } from "./chunk-OWWWO2XS.js";
230
231
  import {
231
232
  runFetchGuard,
232
233
  runFetchGuardSync
233
- } from "./chunk-LOVLZNLX.js";
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-SKOLE6US.js";
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-7POLZOFY.js";
251
+ } from "./chunk-VNSZCK5V.js";
251
252
 
252
253
  // src/cli.ts
253
- import { join as join44 } from "path";
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 sep5 = false;
1605
+ let sep6 = false;
1605
1606
  for (const line of lines) {
1606
1607
  if (line.startsWith("<<<<<<< ")) {
1607
1608
  open2 = true;
1608
- sep5 = false;
1609
+ sep6 = false;
1609
1610
  continue;
1610
1611
  }
1611
1612
  if (line === "=======" && open2) {
1612
- sep5 = true;
1613
+ sep6 = true;
1613
1614
  continue;
1614
1615
  }
1615
- if (line.startsWith(">>>>>>> ") && open2 && sep5) return true;
1616
+ if (line.startsWith(">>>>>>> ") && open2 && sep6) return true;
1616
1617
  if (line.startsWith(">>>>>>> ")) {
1617
1618
  open2 = false;
1618
- sep5 = false;
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({ error: lint.result.data.summary.errors, warnings: lint.result.data.summary.warnings, info: lint.result.data.summary.info }),
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-5IW3TZ5F.js");
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-RO4BJP5T.js");
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-VMFYW2K3.js");
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-MGHQD3UI.js");
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-VMFYW2K3.js");
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-X5MI5IX6.js");
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((resolve9) => {
3676
- abort.signal.addEventListener("abort", () => resolve9(), { once: true });
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-UNZG5UJZ.js");
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: readFile23 } = await import("fs/promises");
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-UNZG5UJZ.js");
4302
- const before = await readFile23(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
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: mkdir13 } = await import("fs/promises");
6886
- await mkdir13(dirname7(prepared.targetPath), { recursive: true });
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((resolve9) => setTimeout(resolve9, 10));
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
8448
+ }
8449
+ var SAFE_HREF_RE = /^(https?:\/\/|mailto:|#)/i;
8450
+ function escapeAttr(text) {
8451
+ return escapeHtml(text).replace(/'/g, "&#39;");
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 mkdir11, readFile as readFile17 } from "fs/promises";
8435
- import { dirname as dirname10, join as join33, resolve as resolve6 } from "path";
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 dirname9, posix as posix2, relative as relative2, resolve as resolve5, sep as sep2 } from "path";
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 = resolve5(vaultReal, target);
8486
- const parent = dirname9(absolutePath);
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 = relative2(vaultReal, parentReal).split(sep2).join("/");
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 readFile17(input.draftPath, "utf8");
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 (resolve6(input.draftPath) === target.data.absolutePath || target.data.existingRealPath !== void 0 && draftRealPath === target.data.existingRealPath) {
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 = join33(vault, rendered.data.index_path);
9149
+ const knowledgePath = join34(vault, rendered.data.index_path);
8741
9150
  try {
8742
- await mkdir11(dirname10(knowledgePath), { recursive: true });
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 readFile17(knowledgePath, "utf8");
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 = join33(vault, projectIndex.data.index_path);
9180
+ const indexPath = join34(vault, projectIndex.data.index_path);
8772
9181
  try {
8773
- const existing = await readFile17(indexPath, "utf8");
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 join34, resolve as resolvePath } from "path";
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(join34(requested, ".git"))) {
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" ? join34(home, "Library", "Application Support", "vault-sync") : join34(home, ".local", "state", "vault-sync");
9079
- return join34(stateDir, "snapshot-maintenance-audit.jsonl");
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(join34(logPath, ".."), { recursive: true });
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(join34(gitDir, m))) return true;
9642
+ if (existsSync11(join35(gitDir, m))) return true;
9234
9643
  }
9235
- return existsSync11(join34(gitDir, "rebase-merge")) || existsSync11(join34(gitDir, "rebase-apply"));
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(join34(requested, ".git"))) {
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(join34(plan.snapshot_worktree, "index.md"), rendered.data.index);
9376
- writeFileSync6(join34(plan.snapshot_worktree, "log.md"), rendered.data.log);
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 join35, relative as relative3, dirname as dirname11 } from "path";
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 = join35(dir, entry.name);
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 relative3(base, full).replace(/\\/g, "/");
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 = join35(input.vault, relPath);
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 = join35(target, obj.Key);
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(dirname11(localPath), { recursive: true });
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 readFile18 } from "fs/promises";
9655
- import { join as join36 } from "path";
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 readFile18(join36(scanRoot, "SCHEMA.md"), "utf8");
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 join38, relative as relative5, resolve as resolve8 } from "path";
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 join37, resolve as resolve7 } from "path";
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(join37(home, ".skillwiki", ".env"));
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: resolve7(configured) };
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 readFile19 } from "fs/promises";
9780
- import { relative as relative4, sep as sep3 } from "path";
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 = relative4(liveVault, page.absPath).split(sep3).join("/");
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 readFile19(page.absPath);
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 resolve8(path);
10408
+ return resolve9(path);
10000
10409
  }
10001
10410
  }
10002
10411
  function pathsAreNested(left, right) {
10003
- const rel = relative5(comparablePath(left), comparablePath(right));
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(join38(gitVault, ".git"))) {
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(join38(configuredProjection, ".git"))) {
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 mkdir12, writeFile as writeFile7, stat as stat5 } from "fs/promises";
10120
- import { join as join39 } from "path";
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(join39(input.vault, "SCHEMA.md"));
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 = join39(input.vault, relPath);
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 mkdir12(join39(absPath, ".."), { recursive: true });
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 = join39(input.vault, "raw", "articles", "example-source.md");
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 mkdir12(join39(rawPath, ".."), { recursive: true });
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 readFile20, writeFile as writeFile8 } from "fs/promises";
10643
+ import { readFile as readFile21, writeFile as writeFile8 } from "fs/promises";
10235
10644
  import { existsSync as existsSync14 } from "fs";
10236
- import { join as join40 } from "path";
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 ?? join40(input.vault, ".skillwiki", "graph.json");
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 readFile20(graphPath, "utf8");
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 = join40(input.vault, "vault-graph.canvas");
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 readFile22, unlink as unlink5 } from "fs/promises";
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 readFile21 } from "fs/promises";
10501
- import { join as join41, relative as relative6, sep as sep4 } from "path";
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 = join41(dir, entry.name);
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(relative6(root, path).split(sep4).join("/"));
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 = join41(vault, "raw", "assets");
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 readFile21(page.absPath, "utf8");
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 readFile22(target.data);
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 join42 } from "path";
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 ? join42(vault, FLEET_REL_PATH) : void 0);
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 join43, normalize } from "path";
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(join43(vault, ".git"))) return;
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 readFile23 = input.readFile ?? readFileSync17;
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 = readFile23(mapPath, "utf8");
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((resolve9, reject) => {
11246
- process.stdout.write("", (error) => error ? reject(error) : resolve9());
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-L7IRQ76C.js");
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 ?? join44(vault, ".skillwiki", "graph.json");
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
  );