skillwiki 0.10.111 → 0.10.112

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 (40) hide show
  1. package/dist/{chunk-TDBUJUYH.js → chunk-25OXNFDV.js} +42 -18
  2. package/dist/{chunk-6OQLBNKR.js → chunk-ABRYBO4T.js} +29 -4
  3. package/dist/{chunk-OJJBZDPR.js → chunk-APKDTEVV.js} +178 -210
  4. package/dist/{chunk-5IBLXGLY.js → chunk-CKQPUHM2.js} +2 -2
  5. package/dist/chunk-DOHWNPAZ.js +60 -0
  6. package/dist/{chunk-GQWCTJTU.js → chunk-EE7W3GLA.js} +1 -1
  7. package/dist/{chunk-KWEYA3OE.js → chunk-FCA3RRNF.js} +1 -1
  8. package/dist/{chunk-MRPJ7X7W.js → chunk-IR2DI4Y5.js} +3 -3
  9. package/dist/chunk-K3M6OSUW.js +77 -0
  10. package/dist/{chunk-I6IRJCUJ.js → chunk-KJG2CSYD.js} +2 -2
  11. package/dist/chunk-LOVLZNLX.js +111 -0
  12. package/dist/{chunk-HBJLFPQP.js → chunk-Q6EZF37H.js} +8 -8
  13. package/dist/{chunk-JQV5KNPT.js → chunk-RR6VDWSI.js} +3 -3
  14. package/dist/{chunk-PYPPG7RF.js → chunk-SKOLE6US.js} +1 -1
  15. package/dist/{chunk-X5GL2FCU.js → chunk-TKX6PSWZ.js} +1 -1
  16. package/dist/cli.js +77 -33
  17. package/dist/{index-projection-2K672CBO.js → index-projection-UNZG5UJZ.js} +5 -5
  18. package/dist/{journal-QU5IDXQF.js → journal-5IW3TZ5F.js} +3 -3
  19. package/dist/{managed-write-preflight-GRSHQYKC.js → managed-write-preflight-L7IRQ76C.js} +7 -7
  20. package/dist/mcp-auth-env-X5MI5IX6.js +25 -0
  21. package/dist/{migrate-PIB4QA54.js → migrate-RO4BJP5T.js} +6 -5
  22. package/dist/mirror-H5BTBAON.js +151 -0
  23. package/dist/runtime-VMFYW2K3.js +20 -0
  24. package/dist/skillwiki-mcp.js +14 -12
  25. package/dist/sources-AOYP6ROO.js +10 -0
  26. package/dist/{watcher-MHG46J3R.js → watcher-MGHQD3UI.js} +20 -110
  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/package.json +1 -1
  32. package/skills/skills/using-skillwiki/SKILL.md +1 -1
  33. package/skills/skills/wiki-init/SKILL.md +3 -0
  34. package/skills/using-skillwiki/SKILL.md +1 -1
  35. package/skills/wiki-init/SKILL.md +3 -0
  36. package/templates/launchd/com.karlchow.skillwiki-mirror-sse.plist +20 -0
  37. package/dist/chunk-I2J3IJB7.js +0 -54
  38. package/dist/runtime-GLMI5OCT.js +0 -15
  39. package/dist/sources-D2MGP246.js +0 -10
  40. package/dist/{chunk-UIEQIJXP.js → chunk-7POLZOFY.js} +42 -42
@@ -4,11 +4,11 @@ import {
4
4
  UNMANAGED_START,
5
5
  renderRootIndex,
6
6
  writeRootIndexProjection
7
- } from "./chunk-MRPJ7X7W.js";
8
- import "./chunk-I6IRJCUJ.js";
9
- import "./chunk-X5GL2FCU.js";
10
- import "./chunk-GQWCTJTU.js";
11
- import "./chunk-UIEQIJXP.js";
7
+ } from "./chunk-IR2DI4Y5.js";
8
+ import "./chunk-KJG2CSYD.js";
9
+ import "./chunk-TKX6PSWZ.js";
10
+ import "./chunk-EE7W3GLA.js";
11
+ import "./chunk-7POLZOFY.js";
12
12
  export {
13
13
  UNMANAGED_END,
14
14
  UNMANAGED_START,
@@ -3,9 +3,9 @@ import {
3
3
  readJournal,
4
4
  upsertJournal,
5
5
  writeJournal
6
- } from "./chunk-KWEYA3OE.js";
7
- import "./chunk-GQWCTJTU.js";
8
- import "./chunk-UIEQIJXP.js";
6
+ } from "./chunk-FCA3RRNF.js";
7
+ import "./chunk-EE7W3GLA.js";
8
+ import "./chunk-7POLZOFY.js";
9
9
  export {
10
10
  readJournal,
11
11
  upsertJournal,
@@ -6,13 +6,13 @@ import {
6
6
  runManagedWritePeerGate,
7
7
  runManagedWritePreflight,
8
8
  runManagedWriteTransaction
9
- } from "./chunk-HBJLFPQP.js";
10
- import "./chunk-5IBLXGLY.js";
11
- import "./chunk-I6IRJCUJ.js";
12
- import "./chunk-X5GL2FCU.js";
13
- import "./chunk-PYPPG7RF.js";
14
- import "./chunk-GQWCTJTU.js";
15
- import "./chunk-UIEQIJXP.js";
9
+ } from "./chunk-Q6EZF37H.js";
10
+ import "./chunk-CKQPUHM2.js";
11
+ import "./chunk-KJG2CSYD.js";
12
+ import "./chunk-TKX6PSWZ.js";
13
+ import "./chunk-EE7W3GLA.js";
14
+ import "./chunk-SKOLE6US.js";
15
+ import "./chunk-7POLZOFY.js";
16
16
  export {
17
17
  DEFAULT_MANAGED_WRITE_WAIT_MS,
18
18
  MANAGED_WRITE_POLL_INTERVAL_MS,
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MCP_HOST_ID_ENV,
4
+ MCP_TOKEN_ENV,
5
+ MCP_URL_ENV,
6
+ isMcpOnlyLeaf,
7
+ mcpAuthFromEnv,
8
+ mergeMcpAuthIntoEnv,
9
+ nonEmptyEnvValue,
10
+ redactMcpSecret,
11
+ resolveMcpAuthEnv
12
+ } from "./chunk-DOHWNPAZ.js";
13
+ import "./chunk-SKOLE6US.js";
14
+ import "./chunk-7POLZOFY.js";
15
+ export {
16
+ MCP_HOST_ID_ENV,
17
+ MCP_TOKEN_ENV,
18
+ MCP_URL_ENV,
19
+ isMcpOnlyLeaf,
20
+ mcpAuthFromEnv,
21
+ mergeMcpAuthIntoEnv,
22
+ nonEmptyEnvValue,
23
+ redactMcpSecret,
24
+ resolveMcpAuthEnv
25
+ };
@@ -6,11 +6,12 @@ import {
6
6
  refreshInstalledClipPlist,
7
7
  stableCliBinary,
8
8
  xml
9
- } from "./chunk-TDBUJUYH.js";
10
- import "./chunk-6OQLBNKR.js";
11
- import "./chunk-PYPPG7RF.js";
12
- import "./chunk-GQWCTJTU.js";
13
- import "./chunk-UIEQIJXP.js";
9
+ } from "./chunk-25OXNFDV.js";
10
+ import "./chunk-EE7W3GLA.js";
11
+ import "./chunk-ABRYBO4T.js";
12
+ import "./chunk-LOVLZNLX.js";
13
+ import "./chunk-SKOLE6US.js";
14
+ import "./chunk-7POLZOFY.js";
14
15
  export {
15
16
  CLIP_MIGRATION_HINT,
16
17
  migrateClipOutbox,
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ writeRegularExclusive
4
+ } from "./chunk-K3M6OSUW.js";
5
+ import {
6
+ parseDestination
7
+ } from "./chunk-ZRGZBAVS.js";
8
+ import "./chunk-QESNTTVY.js";
9
+
10
+ // src/clip-outbox/mirror.ts
11
+ import { mkdir, rmdir } from "fs/promises";
12
+ import { dirname, join } from "path";
13
+ import { GetObjectCommand } from "@aws-sdk/client-s3";
14
+ var ADMIT = /^raw\/(articles|assets)\/[A-Za-z0-9._/-]+$/;
15
+ function admitMirrorPath(path) {
16
+ return ADMIT.test(path) && !path.split("/").some((part) => part === ".." || part === "." || !part);
17
+ }
18
+ function orderMirrorPaths(paths) {
19
+ const unique = [...new Set(paths.filter(admitMirrorPath))];
20
+ return [...unique.filter((path) => path.startsWith("raw/assets/")), ...unique.filter((path) => path.startsWith("raw/articles/"))];
21
+ }
22
+ function eventsUrlFromMcp(mcpUrl) {
23
+ const url = new URL(mcpUrl);
24
+ url.pathname = "/events";
25
+ url.search = "";
26
+ url.hash = "";
27
+ return url.toString();
28
+ }
29
+ function wikiFetchPullLockDir(home) {
30
+ return join(home, "Library", "Caches", "vault-sync", "wiki-fetch", "pull.lock.d");
31
+ }
32
+ async function acquireWikiFetchPullLock(home) {
33
+ const lockDir = wikiFetchPullLockDir(home);
34
+ await mkdir(dirname(lockDir), { recursive: true });
35
+ try {
36
+ await mkdir(lockDir);
37
+ } catch (error) {
38
+ if (error.code === "EEXIST") return null;
39
+ throw error;
40
+ }
41
+ return async () => {
42
+ await rmdir(lockDir).catch(() => void 0);
43
+ };
44
+ }
45
+ function parseChangedSseBlock(block) {
46
+ let event = "message";
47
+ let data = "";
48
+ for (const line of block.split(/\r?\n/)) {
49
+ if (line.startsWith("event:")) event = line.slice(6).trim();
50
+ else if (line.startsWith("data:")) data += line.slice(5).trimStart();
51
+ }
52
+ if (event !== "changed" || !data) return null;
53
+ try {
54
+ const parsed = JSON.parse(data);
55
+ if (parsed.vault_id !== "central" || !Array.isArray(parsed.paths)) return null;
56
+ return { vault_id: "central", paths: parsed.paths.filter((path) => typeof path === "string") };
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ async function applyMirrorPaths(opts) {
62
+ const ordered = orderMirrorPaths(opts.paths);
63
+ const release = await opts.lock();
64
+ if (!release) return { copied: [], skipped: ordered };
65
+ try {
66
+ const copied = [];
67
+ const skipped = [];
68
+ for (const rel of ordered) {
69
+ const body = await opts.getObject(rel);
70
+ if (!body) {
71
+ skipped.push(rel);
72
+ continue;
73
+ }
74
+ const result = await opts.write(rel, body);
75
+ if (result === "conflict") skipped.push(rel);
76
+ else copied.push(rel);
77
+ }
78
+ return { copied, skipped };
79
+ } finally {
80
+ await release();
81
+ }
82
+ }
83
+ function createS3GetObject(opts) {
84
+ const prefix = opts.prefix ?? "";
85
+ const resolveKey = (rel) => [prefix, rel].filter(Boolean).join("/");
86
+ return async (key) => {
87
+ if (!admitMirrorPath(key)) return null;
88
+ try {
89
+ const response = await opts.client.send(new GetObjectCommand({ Bucket: opts.bucket, Key: resolveKey(key) }));
90
+ if (!response.Body) return null;
91
+ const bytes = Buffer.from(await response.Body.transformToByteArray());
92
+ return bytes.length > opts.maxBytes ? null : bytes;
93
+ } catch (error) {
94
+ const status = error.$metadata?.httpStatusCode;
95
+ if (status === 404 || error.name === "NoSuchKey") return null;
96
+ throw error;
97
+ }
98
+ };
99
+ }
100
+ async function startMirrorListener(input) {
101
+ const fetchImpl = input.fetchImpl ?? fetch;
102
+ const write = (rel, body) => writeRegularExclusive(input.config.mirrorRoot, rel, body);
103
+ const lock = input.lock ?? (() => acquireWikiFetchPullLock(input.home));
104
+ let delayMs = 1e3;
105
+ while (!input.signal.aborted) {
106
+ try {
107
+ const response = await fetchImpl(input.eventsUrl, {
108
+ headers: { Authorization: `Bearer ${input.mcpToken}`, Accept: "text/event-stream" },
109
+ signal: input.signal
110
+ });
111
+ if (!response.ok || !response.body) throw new Error("sse unavailable");
112
+ delayMs = 1e3;
113
+ let buffer = "";
114
+ const reader = response.body.getReader();
115
+ const decoder = new TextDecoder();
116
+ while (!input.signal.aborted) {
117
+ const { done, value } = await reader.read();
118
+ if (done) break;
119
+ buffer += decoder.decode(value, { stream: true }).replaceAll("\r\n", "\n");
120
+ let split;
121
+ while ((split = buffer.indexOf("\n\n")) >= 0) {
122
+ const block = buffer.slice(0, split);
123
+ buffer = buffer.slice(split + 2);
124
+ const changed = parseChangedSseBlock(block);
125
+ if (!changed) continue;
126
+ await applyMirrorPaths({ paths: changed.paths, getObject: input.getObject, write, lock });
127
+ }
128
+ }
129
+ } catch (error) {
130
+ if (input.signal.aborted) return;
131
+ if (error.name === "AbortError") return;
132
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
133
+ delayMs = Math.min(delayMs * 2, 3e4);
134
+ }
135
+ }
136
+ }
137
+ function parseDestinationForMirror(destination) {
138
+ return parseDestination(destination);
139
+ }
140
+ export {
141
+ acquireWikiFetchPullLock,
142
+ admitMirrorPath,
143
+ applyMirrorPaths,
144
+ createS3GetObject,
145
+ eventsUrlFromMcp,
146
+ orderMirrorPaths,
147
+ parseChangedSseBlock,
148
+ parseDestinationForMirror,
149
+ startMirrorListener,
150
+ wikiFetchPullLockDir
151
+ };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ assertClipS3Endpoint,
4
+ conditionalCreateProven,
5
+ evidencePath,
6
+ isRfc1918OrLoopback,
7
+ loadClipCredentials,
8
+ validateRuntime
9
+ } from "./chunk-ABRYBO4T.js";
10
+ import "./chunk-LOVLZNLX.js";
11
+ import "./chunk-SKOLE6US.js";
12
+ import "./chunk-7POLZOFY.js";
13
+ export {
14
+ assertClipS3Endpoint,
15
+ conditionalCreateProven,
16
+ evidencePath,
17
+ isRfc1918OrLoopback,
18
+ loadClipCredentials,
19
+ validateRuntime
20
+ };
@@ -1,19 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-OJJBZDPR.js";
5
- import "./chunk-JQV5KNPT.js";
4
+ } from "./chunk-APKDTEVV.js";
5
+ import "./chunk-RR6VDWSI.js";
6
6
  import "./chunk-7I2TPIV5.js";
7
- import "./chunk-MRPJ7X7W.js";
8
- import "./chunk-HBJLFPQP.js";
9
- import "./chunk-5IBLXGLY.js";
10
- import "./chunk-I6IRJCUJ.js";
11
- import "./chunk-X5GL2FCU.js";
12
- import "./chunk-TDBUJUYH.js";
13
- import "./chunk-6OQLBNKR.js";
14
- import "./chunk-PYPPG7RF.js";
15
- import "./chunk-GQWCTJTU.js";
16
- import "./chunk-UIEQIJXP.js";
7
+ import "./chunk-IR2DI4Y5.js";
8
+ import "./chunk-Q6EZF37H.js";
9
+ import "./chunk-CKQPUHM2.js";
10
+ import "./chunk-KJG2CSYD.js";
11
+ import "./chunk-TKX6PSWZ.js";
12
+ import "./chunk-DOHWNPAZ.js";
13
+ import "./chunk-25OXNFDV.js";
14
+ import "./chunk-EE7W3GLA.js";
15
+ import "./chunk-ABRYBO4T.js";
16
+ import "./chunk-LOVLZNLX.js";
17
+ import "./chunk-SKOLE6US.js";
18
+ import "./chunk-7POLZOFY.js";
17
19
 
18
20
  // src/mcp-entry.ts
19
21
  runSkillwikiMcpStdio().catch((error) => {
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runSourcesPending
4
+ } from "./chunk-RR6VDWSI.js";
5
+ import "./chunk-CKQPUHM2.js";
6
+ import "./chunk-TKX6PSWZ.js";
7
+ import "./chunk-7POLZOFY.js";
8
+ export {
9
+ runSourcesPending
10
+ };
@@ -1,35 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- sha256Hex
4
- } from "./chunk-ZRGZBAVS.js";
3
+ readJournal,
4
+ upsertJournal,
5
+ writeJournal
6
+ } from "./chunk-FCA3RRNF.js";
5
7
  import {
6
8
  buildReadyHint
7
9
  } from "./chunk-OSL4J7GF.js";
8
10
  import {
9
- runFetchGuardSync
10
- } from "./chunk-I2J3IJB7.js";
11
- import "./chunk-QESNTTVY.js";
11
+ readRegular
12
+ } from "./chunk-K3M6OSUW.js";
12
13
  import {
13
- readJournal,
14
- upsertJournal,
15
- writeJournal
16
- } from "./chunk-KWEYA3OE.js";
17
- import "./chunk-GQWCTJTU.js";
14
+ sha256Hex
15
+ } from "./chunk-ZRGZBAVS.js";
16
+ import "./chunk-QESNTTVY.js";
17
+ import "./chunk-EE7W3GLA.js";
18
18
  import {
19
- ipv4InCidr,
20
- isBlockedHost
21
- } from "./chunk-UIEQIJXP.js";
19
+ assertPublicHttpsUrl
20
+ } from "./chunk-LOVLZNLX.js";
21
+ import "./chunk-7POLZOFY.js";
22
22
 
23
23
  // src/clip-outbox/watcher.ts
24
24
  import { createRequire } from "module";
25
25
  import { randomBytes as randomBytes2 } from "crypto";
26
26
  import { mkdir as mkdir2, readdir, rmdir, unlink, writeFile } from "fs/promises";
27
- import { basename, join as join2, resolve as resolve3 } from "path";
27
+ import { basename, join as join2, resolve as resolve2 } from "path";
28
28
 
29
29
  // src/clip-outbox/seal.ts
30
30
  import { randomBytes } from "crypto";
31
- import { mkdir, open as open2 } from "fs/promises";
32
- import { dirname, join, relative as relative3 } from "path";
31
+ import { mkdir, open } from "fs/promises";
32
+ import { dirname, join, relative as relative2 } from "path";
33
33
  import yaml from "js-yaml";
34
34
 
35
35
  // src/clip-outbox/localize.ts
@@ -108,60 +108,6 @@ function localAssetRelPath(target) {
108
108
  return void 0;
109
109
  }
110
110
 
111
- // src/clip-outbox/ssrf.ts
112
- import { isIP } from "net";
113
- import { lookup as dnsLookup } from "dns/promises";
114
- var extraBlockedV4 = [
115
- ["0.0.0.0", 8],
116
- ["100.64.0.0", 10],
117
- ["192.0.2.0", 24],
118
- ["198.18.0.0", 15],
119
- ["198.51.100.0", 24],
120
- ["203.0.113.0", 24],
121
- ["224.0.0.0", 4],
122
- ["240.0.0.0", 4]
123
- ];
124
- function isBlockedAddress(address) {
125
- if (isBlockedHost(address)) return true;
126
- const version = isIP(address);
127
- if (version === 4) {
128
- return extraBlockedV4.some(([base, prefix]) => ipv4InCidr(address, base, prefix));
129
- }
130
- if (version === 6) {
131
- const lower = address.toLowerCase();
132
- return lower === "::" || lower === "::1" || lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
133
- }
134
- return false;
135
- }
136
- async function assertPublicHttpsUrl(url, lookup = defaultLookup) {
137
- const guard = runFetchGuardSync({ url });
138
- if (!guard.result.ok) {
139
- return { ok: false, reason: guard.result.error };
140
- }
141
- let parsed;
142
- try {
143
- parsed = new URL(url);
144
- } catch {
145
- return { ok: false, reason: "MALFORMED_URL" };
146
- }
147
- if (isBlockedAddress(parsed.hostname)) {
148
- return { ok: false, reason: "HOST_BLOCKED" };
149
- }
150
- try {
151
- const addresses = await lookup(parsed.hostname);
152
- if (addresses.length === 0) return { ok: false, reason: "DNS_EMPTY" };
153
- if (addresses.some(isBlockedAddress)) return { ok: false, reason: "HOST_BLOCKED" };
154
- } catch {
155
- return { ok: false, reason: "DNS_FAILED" };
156
- }
157
- return { ok: true, href: parsed.href };
158
- }
159
- async function defaultLookup(hostname) {
160
- if (isIP(hostname)) return [hostname];
161
- const v4 = await dnsLookup(hostname, { all: true, verbatim: true });
162
- return v4.map((row) => row.address);
163
- }
164
-
165
111
  // src/clip-outbox/localize.ts
166
112
  var LocalizeError = class extends Error {
167
113
  constructor(message, code = "LOCALIZE_HELD") {
@@ -279,42 +225,6 @@ async function downloadImage(href, config, fetchImpl, lookup) {
279
225
  throw new LocalizeError("too many redirects");
280
226
  }
281
227
 
282
- // src/clip-outbox/files.ts
283
- import { constants } from "fs";
284
- import { lstat, open, realpath } from "fs/promises";
285
- import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "path";
286
- async function readRegular(root, path, maxBytes) {
287
- const rel = relative2(root, path);
288
- if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || isAbsolute2(rel)) throw new Error("unsafe input path");
289
- let current = resolve2(root);
290
- for (const part of ["", ...rel.split(sep)]) {
291
- current = resolve2(current, part);
292
- const st = await lstat(current);
293
- if (st.isSymbolicLink()) throw new Error("symlink input refused");
294
- if (current !== path && !st.isDirectory()) throw new Error("invalid input parent");
295
- }
296
- const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
297
- try {
298
- const before = await handle.stat();
299
- if (!before.isFile() || before.size > maxBytes) throw new Error("input type or size refused");
300
- const bytes = Buffer.alloc(before.size + 1);
301
- let bytesRead = 0;
302
- while (bytesRead < bytes.length) {
303
- const chunk = await handle.read(bytes, bytesRead, bytes.length - bytesRead, bytesRead);
304
- if (chunk.bytesRead === 0) break;
305
- bytesRead += chunk.bytesRead;
306
- }
307
- const after = await handle.stat();
308
- if (await realpath(path) !== path) throw new Error("input path changed during read");
309
- const leaf = await lstat(path);
310
- if (leaf.ino !== before.ino || leaf.dev !== before.dev) throw new Error("input identity changed during read");
311
- if (bytesRead !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs) throw new Error("input changed during read");
312
- return bytes.subarray(0, bytesRead);
313
- } finally {
314
- await handle.close();
315
- }
316
- }
317
-
318
228
  // src/clip-outbox/seal.ts
319
229
  var UnstableCapture = class extends Error {
320
230
  };
@@ -325,7 +235,7 @@ function snapshotPath(config, entry, key) {
325
235
  }
326
236
  async function durableFile(root, path, bytes) {
327
237
  await mkdir(dirname(path), { recursive: true, mode: 448 });
328
- const f = await open2(path, "wx", 384);
238
+ const f = await open(path, "wx", 384);
329
239
  try {
330
240
  await f.writeFile(bytes);
331
241
  await f.sync();
@@ -334,7 +244,7 @@ async function durableFile(root, path, bytes) {
334
244
  }
335
245
  let dir = dirname(path);
336
246
  while (true) {
337
- const handle = await open2(dir, "r");
247
+ const handle = await open(dir, "r");
338
248
  try {
339
249
  await handle.sync();
340
250
  } finally {
@@ -383,7 +293,7 @@ async function sealCapture(config, entry, deps = {}) {
383
293
  return { ...entry, sourceSha256: sha256Hex(article), assetKeys: [...objects.keys()].filter((k) => k !== entry.articleKey), sha256: Object.fromEntries([...objects].map(([k, b]) => [k, sha256Hex(b)])), sizes: Object.fromEntries([...objects].map(([k, b]) => [k, b.length])), receipts: {}, state: "sealed", reason: void 0, attempts: 0, retryAt: void 0, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
384
294
  }
385
295
  function admittedArticle(root, path) {
386
- const rel = relative3(root, path).replaceAll("\\", "/");
296
+ const rel = relative2(root, path).replaceAll("\\", "/");
387
297
  if (!rel.startsWith("raw/articles/") || !rel.endsWith(".md")) return false;
388
298
  return rel.split("/").every((part) => part !== "." && part !== ".." && !part.startsWith(".") && !/[\x00-\x1f]/.test(part) && !part.endsWith("~"));
389
299
  }
@@ -580,7 +490,7 @@ async function startClipWorker(config, deps) {
580
490
  requestScan();
581
491
  return;
582
492
  }
583
- const absolute = resolve3(path);
493
+ const absolute = resolve2(path);
584
494
  if (admittedArticle(config.root, absolute)) schedule(absolute);
585
495
  else if (absolute.startsWith(join2(config.root, "raw/assets") + "/")) {
586
496
  for (const entry of entries) if (!entry.sourceSha256 && entry.source) schedule(entry.source);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.111",
3
+ "version": "0.10.112",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.111",
3
+ "version": "0.10.112",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 22 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp, skillwiki-connect) backed by the deterministic `skillwiki` CLI.",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.111",
3
+ "version": "0.10.112",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 22 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.111",
3
+ "version": "0.10.112",
4
4
  "description": "Project-aware Karpathy-style knowledge base: 22 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp, skillwiki-connect) backed by HTTP MCP wiki_capture.",
5
5
  "author": {
6
6
  "name": "karlorz"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.10.111",
3
+ "version": "0.10.112",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -183,7 +183,7 @@ If the vault has `.WIKI_GIT_FROZEN`:
183
183
  ## Skill Map
184
184
  | Skill | When to Invoke |
185
185
  |-------|----------------|
186
- | `wiki-init` | Bootstrap a vault and install `_Templates/web-clipper/llm-wiki-clippings.json` plus import guidance. File-level clips use a dedicated `~/Obsidian/clip-outbox` vault, not `~/wiki`; `skillwiki doctor` reports clip-channel rows; `wiki_capture` is not clip ingress. |
186
+ | `wiki-init` | Bootstrap a vault and install `_Templates/web-clipper/llm-wiki-clippings.json` plus import guidance. File-level clips use a dedicated `~/Obsidian/clip-outbox` vault, not `~/wiki`; `skillwiki doctor` reports clip-channel rows including the inbound `com.karlchow.skillwiki-mirror-sse` listener, which upgrades never auto-load; `wiki_capture` is not clip ingress. |
187
187
  | `wiki-ingest` | Convert URLs, files, or pasted text into typed-knowledge pages |
188
188
  | `wiki-query` | Search typed knowledge by default; work-queue / doctor / MCP diagnose questions use `wiki_context` or `--scope work` / `--scope all`; explicitly requested fresh/raw evidence uses the separate pending channel |
189
189
  | `wiki-lint` | Vault health and lint checks; use `health` for whole-system reports and `lint --summary` for bounded lint buckets |
@@ -41,6 +41,9 @@ None for the first run.
41
41
  worker localizes required v1 HTTPS images before freeze, writes
42
42
  `![[raw/assets/...]]` embeds, and uploads `raw/assets/<capture-id>/note.md`.
43
43
  `wiki_capture` is not clip ingress. Never use `.txt` as the only index.
44
+ The inbound production SSE listener is a separate attended
45
+ `skillwiki clip-outbox install-or-migrate --load-mirror` job; package
46
+ upgrade never auto-loads it and never watches `~/wiki` as clip input.
44
47
  8. **Suggest first sources.** Propose 3–5 initial sources (URLs, papers, articles) appropriate to the domain. Prompt the user to provide the first one to ingest, then hand off to wiki-ingest.
45
48
 
46
49
  ## Stop conditions
@@ -183,7 +183,7 @@ If the vault has `.WIKI_GIT_FROZEN`:
183
183
  ## Skill Map
184
184
  | Skill | When to Invoke |
185
185
  |-------|----------------|
186
- | `wiki-init` | Bootstrap a vault and install `_Templates/web-clipper/llm-wiki-clippings.json` plus import guidance. File-level clips use a dedicated `~/Obsidian/clip-outbox` vault, not `~/wiki`; `skillwiki doctor` reports clip-channel rows; `wiki_capture` is not clip ingress. |
186
+ | `wiki-init` | Bootstrap a vault and install `_Templates/web-clipper/llm-wiki-clippings.json` plus import guidance. File-level clips use a dedicated `~/Obsidian/clip-outbox` vault, not `~/wiki`; `skillwiki doctor` reports clip-channel rows including the inbound `com.karlchow.skillwiki-mirror-sse` listener, which upgrades never auto-load; `wiki_capture` is not clip ingress. |
187
187
  | `wiki-ingest` | Convert URLs, files, or pasted text into typed-knowledge pages |
188
188
  | `wiki-query` | Search typed knowledge by default; work-queue / doctor / MCP diagnose questions use `wiki_context` or `--scope work` / `--scope all`; explicitly requested fresh/raw evidence uses the separate pending channel |
189
189
  | `wiki-lint` | Vault health and lint checks; use `health` for whole-system reports and `lint --summary` for bounded lint buckets |
@@ -41,6 +41,9 @@ None for the first run.
41
41
  worker localizes required v1 HTTPS images before freeze, writes
42
42
  `![[raw/assets/...]]` embeds, and uploads `raw/assets/<capture-id>/note.md`.
43
43
  `wiki_capture` is not clip ingress. Never use `.txt` as the only index.
44
+ The inbound production SSE listener is a separate attended
45
+ `skillwiki clip-outbox install-or-migrate --load-mirror` job; package
46
+ upgrade never auto-loads it and never watches `~/wiki` as clip input.
44
47
  8. **Suggest first sources.** Propose 3–5 initial sources (URLs, papers, articles) appropriate to the domain. Prompt the user to provide the first one to ingest, then hand off to wiki-ingest.
45
48
 
46
49
  ## Stop conditions
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key><string>com.karlchow.skillwiki-mirror-sse</string>
6
+ <key>ProgramArguments</key>
7
+ <array>
8
+ <string>@NODE_BINARY@</string>
9
+ <string>@CLI_BINARY@</string>
10
+ <string>clip-outbox</string><string>mirror-watch</string>
11
+ </array>
12
+ <key>EnvironmentVariables</key>
13
+ <dict><key>HOME</key><string>@HOME@</string><key>SKILLWIKI_HOME</key><string>@HOME@</string></dict>
14
+ <key>KeepAlive</key><true/>
15
+ <key>RunAtLoad</key><true/>
16
+ <key>ThrottleInterval</key><integer>10</integer>
17
+ <key>StandardOutPath</key><string>@LOG_ROOT@/clip-mirror-sse.log</string>
18
+ <key>StandardErrorPath</key><string>@LOG_ROOT@/clip-mirror-sse-error.log</string>
19
+ </dict>
20
+ </plist>