@mulmoclaude/core 0.28.0 → 0.30.0

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 (53) hide show
  1. package/assets/helps/collection-skills.md +18 -4
  2. package/dist/collection/core/backlinks.d.ts +28 -2
  3. package/dist/collection/core/ontologyGraph.d.ts +6 -2
  4. package/dist/collection/index.cjs +2 -1
  5. package/dist/collection/index.cjs.map +1 -1
  6. package/dist/collection/index.js +2 -2
  7. package/dist/collection/index.js.map +1 -1
  8. package/dist/collection/registry/server/index.cjs +2 -2
  9. package/dist/collection/registry/server/index.js +2 -2
  10. package/dist/collection/server/backendAvailability.d.ts +5 -0
  11. package/dist/collection/server/index.cjs +4 -2
  12. package/dist/collection/server/index.d.ts +1 -0
  13. package/dist/collection/server/index.js +3 -3
  14. package/dist/collection/server/manageTool.d.ts +4 -0
  15. package/dist/collection/server/schemaDocs.d.ts +21 -0
  16. package/dist/collection/server/store.d.ts +37 -0
  17. package/dist/collection/server/watchFs.d.ts +17 -0
  18. package/dist/collection-watchers/index.cjs +114 -203
  19. package/dist/collection-watchers/index.cjs.map +1 -1
  20. package/dist/collection-watchers/index.d.ts +1 -1
  21. package/dist/collection-watchers/index.js +114 -202
  22. package/dist/collection-watchers/index.js.map +1 -1
  23. package/dist/collection-watchers/watcher.d.ts +8 -13
  24. package/dist/{discovery-BVdCgBFN.js → discovery-BbsJwVEq.js} +109 -8
  25. package/dist/discovery-BbsJwVEq.js.map +1 -0
  26. package/dist/{discovery-BKovdRpZ.cjs → discovery-Bklck7Ck.cjs} +119 -6
  27. package/dist/discovery-Bklck7Ck.cjs.map +1 -0
  28. package/dist/feeds/server/index.cjs +2 -2
  29. package/dist/feeds/server/index.js +2 -2
  30. package/dist/google/index.cjs +1 -1
  31. package/dist/google/index.js +1 -1
  32. package/dist/{promptSafety-cZIeiZtB.js → promptSafety-Bugq2kqL.js} +40 -5
  33. package/dist/promptSafety-Bugq2kqL.js.map +1 -0
  34. package/dist/{promptSafety-BFt2g_wn.cjs → promptSafety-DbE6eZmP.cjs} +45 -4
  35. package/dist/promptSafety-DbE6eZmP.cjs.map +1 -0
  36. package/dist/remote-view/index.cjs +8 -0
  37. package/dist/remote-view/index.cjs.map +1 -1
  38. package/dist/remote-view/index.d.ts +7 -0
  39. package/dist/remote-view/index.js +8 -1
  40. package/dist/remote-view/index.js.map +1 -1
  41. package/dist/{server-B8mvfZSF.js → server-8EZggEg7.js} +180 -15
  42. package/dist/server-8EZggEg7.js.map +1 -0
  43. package/dist/{server-rRQkNBvO.cjs → server-BOiz_HDi.cjs} +180 -15
  44. package/dist/server-BOiz_HDi.cjs.map +1 -0
  45. package/dist/whisper/client.cjs.map +1 -1
  46. package/dist/whisper/client.js.map +1 -1
  47. package/package.json +1 -1
  48. package/dist/discovery-BKovdRpZ.cjs.map +0 -1
  49. package/dist/discovery-BVdCgBFN.js.map +0 -1
  50. package/dist/promptSafety-BFt2g_wn.cjs.map +0 -1
  51. package/dist/promptSafety-cZIeiZtB.js.map +0 -1
  52. package/dist/server-B8mvfZSF.js.map +0 -1
  53. package/dist/server-rRQkNBvO.cjs.map +0 -1
@@ -640,6 +640,18 @@ ${dataJson}
640
640
  ${templateText}`;
641
641
  }
642
642
  //#endregion
643
+ //#region src/collection/server/backendAvailability.ts
644
+ /** Thrown by a store when its engine or session cannot serve the request. */
645
+ var BackendUnavailableError = class extends Error {
646
+ constructor(message) {
647
+ super(message);
648
+ this.name = "BackendUnavailableError";
649
+ }
650
+ };
651
+ function isBackendUnavailable(err) {
652
+ return err instanceof BackendUnavailableError;
653
+ }
654
+ //#endregion
643
655
  //#region src/collection/core/queryZ.ts
644
656
  /** Result-column aliases double as SQL identifiers and JSON keys — keep
645
657
  * them to a conservative identifier charset so neither side needs
@@ -1050,7 +1062,7 @@ async function duckDbInstance() {
1050
1062
  return await instancePromise;
1051
1063
  } catch (err) {
1052
1064
  instancePromise = null;
1053
- throw new Error(`DuckDB is unavailable on this host (@duckdb/node-api failed to load: ${String(err)}) — dataSource collections cannot be read`);
1065
+ throw new BackendUnavailableError(`DuckDB is unavailable on this host (@duckdb/node-api failed to load: ${String(err)}) — dataSource collections cannot be read`);
1054
1066
  }
1055
1067
  }
1056
1068
  async function queryCsv(sql, params) {
@@ -1135,6 +1147,89 @@ async function csvRunQuery(absPath, primaryKey, query, workspaceRoot) {
1135
1147
  return (await queryCsv(sql, [utf8Path, ...params])).map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) => [key, normalizeCsvValue(value)])));
1136
1148
  }
1137
1149
  //#endregion
1150
+ //#region src/collection/server/watchFs.ts
1151
+ /** An atomic file replace (editor save, `mv` over the target) surfaces as
1152
+ * 2-3 events. Collapse them so one user action reports one change. */
1153
+ var REPLACE_DEBOUNCE_MS = 300;
1154
+ /** The path to hand `watch()`, with Windows 8.3 short names resolved away.
1155
+ *
1156
+ * ReadDirectoryChangesW reports filenames against the LONG path, but a watch
1157
+ * opened on a short path (`C:\Users\RUNNER~1\…` — what `os.tmpdir()` returns
1158
+ * on GitHub's Windows runners) keeps the short form. libuv's
1159
+ * `assert(!_wcsnicmp(filename, dir, dirlen))` in `src/win/fs-event.c` then
1160
+ * aborts the PROCESS on the first event — a native assert, so neither
1161
+ * `watcher.on("error")` nor a try/catch can contain it.
1162
+ *
1163
+ * POSIX is deliberately left alone: `realpath` there also collapses symlinks
1164
+ * (`/var` → `/private/var` on macOS), which we neither need nor want to
1165
+ * change. A failure falls back to the original path — worst case we are no
1166
+ * worse off than before. */
1167
+ function watchablePath(dir) {
1168
+ if (process.platform !== "win32") return dir;
1169
+ try {
1170
+ return node_fs.realpathSync.native(dir);
1171
+ } catch {
1172
+ return dir;
1173
+ }
1174
+ }
1175
+ /** Watch `dir`, reporting each accepted filename. `accept` decides what is
1176
+ * noise; a null filename always passes (the platform didn't tell us which
1177
+ * file, so the caller must assume the worst). */
1178
+ async function watchDirectory(dir, accept, onHit) {
1179
+ try {
1180
+ await (0, node_fs_promises.mkdir)(dir, { recursive: true });
1181
+ const watcher = (0, node_fs.watch)(watchablePath(dir), { persistent: false }, (_eventType, rawFilename) => {
1182
+ const filename = rawFilename === null ? null : String(rawFilename);
1183
+ if (filename !== null && !accept(filename)) return;
1184
+ onHit(filename);
1185
+ });
1186
+ watcher.on("error", (err) => {
1187
+ log.warn("collections", "fs watch error", {
1188
+ dir,
1189
+ error: String(err)
1190
+ });
1191
+ });
1192
+ return { close: () => watcher.close() };
1193
+ } catch (err) {
1194
+ log.warn("collections", "fs watch start failed", {
1195
+ dir,
1196
+ error: String(err)
1197
+ });
1198
+ return null;
1199
+ }
1200
+ }
1201
+ /** Watch the single file `absPath` by watching its PARENT directory, so an
1202
+ * atomic replace can't strand the watch on a dead inode. `alsoAccept`
1203
+ * widens the filter beyond the exact basename (sqlite's `-wal`/`-journal`
1204
+ * sidecars). Reports are debounced: one replace, one call. */
1205
+ async function watchSingleFile(absPath, alsoAccept, onChange) {
1206
+ const dir = node_path.default.dirname(absPath);
1207
+ const base = node_path.default.basename(absPath);
1208
+ let timer = null;
1209
+ const fire = () => {
1210
+ if (timer) clearTimeout(timer);
1211
+ timer = setTimeout(() => {
1212
+ timer = null;
1213
+ onChange();
1214
+ }, REPLACE_DEBOUNCE_MS);
1215
+ timer.unref?.();
1216
+ };
1217
+ const handle = await watchDirectory(dir, (filename) => filename === base || alsoAccept(base, filename), fire);
1218
+ if (!handle) return null;
1219
+ return { close: () => {
1220
+ if (timer) clearTimeout(timer);
1221
+ timer = null;
1222
+ handle.close();
1223
+ } };
1224
+ }
1225
+ /** An `FsWatchHandle` as a bare unsubscribe — `null` straight through, so an
1226
+ * unarmed watch stays distinguishable from an armed one. Lives here rather
1227
+ * than beside the store contract so both `store.ts` and the backends it
1228
+ * registers can reach it without importing each other. */
1229
+ function closerFor(handle) {
1230
+ return handle === null ? null : () => handle.close();
1231
+ }
1232
+ //#endregion
1138
1233
  //#region src/collection/server/storePage.ts
1139
1234
  /** Project `fields` (+ the primary key, always) out of each record. Thin
1140
1235
  * server-typed alias over the shared isomorphic `projectRecordFields`
@@ -1162,7 +1257,7 @@ var sqliteModule = null;
1162
1257
  function loadSqlite() {
1163
1258
  sqliteModule ??= import("node:sqlite").then((mod) => mod, (err) => {
1164
1259
  sqliteModule = null;
1165
- throw new Error(`sqlite storage needs the node:sqlite module (Node.js >= 22.5) — this runtime cannot load it: ${String(err)}`);
1260
+ throw new BackendUnavailableError(`sqlite storage needs the node:sqlite module (Node.js >= 22.5) — this runtime cannot load it: ${String(err)}`);
1166
1261
  });
1167
1262
  return sqliteModule;
1168
1263
  }
@@ -1399,7 +1494,8 @@ function sqliteStoreFor(collection, opts) {
1399
1494
  delete: (itemId) => sqliteDelete(file, itemId, {
1400
1495
  workspaceRoot: root(),
1401
1496
  slug
1402
- })
1497
+ }),
1498
+ watch: async (onChange) => closerFor(await watchSingleFile(file, (base, name) => name.startsWith(base), () => onChange({ kind: "collection" })))
1403
1499
  };
1404
1500
  }
1405
1501
  //#endregion
@@ -1446,7 +1542,8 @@ function csvStoreFor(collection, opts) {
1446
1542
  list: () => listAll().then((result) => result.items),
1447
1543
  page: (pageOpts = {}) => listAll().then((result) => pageFromFullRead(result.items, pageOpts, key, result.truncated)),
1448
1544
  read: (itemId) => file === void 0 ? Promise.resolve(null) : csvRead(file, key, itemId, opts.workspaceRoot),
1449
- query: (query) => file === void 0 ? Promise.resolve([]) : csvRunQuery(file, key, query, opts.workspaceRoot)
1545
+ query: (query) => file === void 0 ? Promise.resolve([]) : csvRunQuery(file, key, query, opts.workspaceRoot),
1546
+ ...file === void 0 ? {} : { watch: async (onChange) => closerFor(await watchSingleFile(file, () => false, () => onChange({ kind: "collection" }))) }
1450
1547
  };
1451
1548
  }
1452
1549
  /** The classic file store over `<dataDir>/<itemId>.json` records. */
@@ -1469,7 +1566,11 @@ function fileStoreFor(collection, opts) {
1469
1566
  ...ioOpts,
1470
1567
  refuseOverwrite: writeOpts.refuseOverwrite
1471
1568
  }),
1472
- delete: (itemId) => deleteItem(collection.dataDir, itemId, ioOpts)
1569
+ delete: (itemId) => deleteItem(collection.dataDir, itemId, ioOpts),
1570
+ watch: async (onChange) => closerFor(await watchDirectory(collection.dataDir, (name) => name.endsWith(".json") && !name.startsWith("."), (filename) => onChange(filename === null ? { kind: "collection" } : {
1571
+ kind: "item",
1572
+ itemId: filename.slice(0, -5)
1573
+ })))
1473
1574
  };
1474
1575
  }
1475
1576
  var storeFactories = /* @__PURE__ */ new Map([
@@ -2521,6 +2622,12 @@ function toDetail(collection) {
2521
2622
  };
2522
2623
  }
2523
2624
  //#endregion
2625
+ Object.defineProperty(exports, "BackendUnavailableError", {
2626
+ enumerable: true,
2627
+ get: function() {
2628
+ return BackendUnavailableError;
2629
+ }
2630
+ });
2524
2631
  Object.defineProperty(exports, "CollectionQueryZ", {
2525
2632
  enumerable: true,
2526
2633
  get: function() {
@@ -2671,6 +2778,12 @@ Object.defineProperty(exports, "getWorkspaceRoot", {
2671
2778
  return getWorkspaceRoot;
2672
2779
  }
2673
2780
  });
2781
+ Object.defineProperty(exports, "isBackendUnavailable", {
2782
+ enumerable: true,
2783
+ get: function() {
2784
+ return isBackendUnavailable;
2785
+ }
2786
+ });
2674
2787
  Object.defineProperty(exports, "isContainedInRoot", {
2675
2788
  enumerable: true,
2676
2789
  get: function() {
@@ -2864,4 +2977,4 @@ Object.defineProperty(exports, "writeItem", {
2864
2977
  }
2865
2978
  });
2866
2979
 
2867
- //# sourceMappingURL=discovery-BKovdRpZ.cjs.map
2980
+ //# sourceMappingURL=discovery-Bklck7Ck.cjs.map