skillwiki 0.10.109 → 0.10.111

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