sensemaking 0.5.0 → 0.7.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 (108) hide show
  1. package/README.md +13 -6
  2. package/dist/cjs/cli.js +8 -1
  3. package/dist/cjs/cli.js.map +1 -1
  4. package/dist/cjs/commands/check.d.cts +3 -0
  5. package/dist/cjs/commands/check.d.ts +3 -0
  6. package/dist/cjs/commands/check.js +130 -0
  7. package/dist/cjs/commands/check.js.map +1 -0
  8. package/dist/cjs/commands/find.js +173 -10
  9. package/dist/cjs/commands/find.js.map +1 -1
  10. package/dist/cjs/commands/index.js +5 -0
  11. package/dist/cjs/commands/index.js.map +1 -1
  12. package/dist/cjs/commands/map.js +1 -1
  13. package/dist/cjs/commands/map.js.map +1 -1
  14. package/dist/cjs/commands/peek.js +1 -1
  15. package/dist/cjs/commands/peek.js.map +1 -1
  16. package/dist/cjs/commands/shared.d.cts +1 -1
  17. package/dist/cjs/commands/shared.d.ts +1 -1
  18. package/dist/cjs/commands/shared.js +166 -8
  19. package/dist/cjs/commands/shared.js.map +1 -1
  20. package/dist/cjs/commands/status.js +72 -5
  21. package/dist/cjs/commands/status.js.map +1 -1
  22. package/dist/cjs/commands/types.d.cts +1 -0
  23. package/dist/cjs/commands/types.d.ts +1 -0
  24. package/dist/cjs/config.d.cts +32 -2
  25. package/dist/cjs/config.d.ts +32 -2
  26. package/dist/cjs/config.js +199 -11
  27. package/dist/cjs/config.js.map +1 -1
  28. package/dist/cjs/db.d.cts +1 -1
  29. package/dist/cjs/db.d.ts +1 -1
  30. package/dist/cjs/db.js +47 -17
  31. package/dist/cjs/db.js.map +1 -1
  32. package/dist/cjs/errors.d.cts +1 -1
  33. package/dist/cjs/errors.d.ts +1 -1
  34. package/dist/cjs/errors.js.map +1 -1
  35. package/dist/cjs/features/embed.d.cts +15 -0
  36. package/dist/cjs/features/embed.d.ts +15 -0
  37. package/dist/cjs/features/embed.js +844 -0
  38. package/dist/cjs/features/embed.js.map +1 -0
  39. package/dist/cjs/features/index.js +3 -1
  40. package/dist/cjs/features/index.js.map +1 -1
  41. package/dist/cjs/features/types.d.cts +4 -1
  42. package/dist/cjs/features/types.d.ts +4 -1
  43. package/dist/cjs/index.d.cts +1 -1
  44. package/dist/cjs/index.d.ts +1 -1
  45. package/dist/cjs/index.js.map +1 -1
  46. package/dist/cjs/output.d.cts +5 -0
  47. package/dist/cjs/output.d.ts +5 -0
  48. package/dist/cjs/output.js +48 -9
  49. package/dist/cjs/output.js.map +1 -1
  50. package/dist/cjs/scan.d.cts +1 -1
  51. package/dist/cjs/scan.d.ts +1 -1
  52. package/dist/cjs/scan.js +17 -10
  53. package/dist/cjs/scan.js.map +1 -1
  54. package/dist/cjs/verbs.d.cts +8 -2
  55. package/dist/cjs/verbs.d.ts +8 -2
  56. package/dist/cjs/verbs.js +308 -81
  57. package/dist/cjs/verbs.js.map +1 -1
  58. package/dist/esm/cli.js +8 -1
  59. package/dist/esm/cli.js.map +1 -1
  60. package/dist/esm/commands/check.d.ts +3 -0
  61. package/dist/esm/commands/check.js +56 -0
  62. package/dist/esm/commands/check.js.map +1 -0
  63. package/dist/esm/commands/find.js +5 -4
  64. package/dist/esm/commands/find.js.map +1 -1
  65. package/dist/esm/commands/index.js +1 -0
  66. package/dist/esm/commands/index.js.map +1 -1
  67. package/dist/esm/commands/map.js +1 -3
  68. package/dist/esm/commands/map.js.map +1 -1
  69. package/dist/esm/commands/peek.js +1 -1
  70. package/dist/esm/commands/peek.js.map +1 -1
  71. package/dist/esm/commands/shared.d.ts +1 -1
  72. package/dist/esm/commands/shared.js +2 -2
  73. package/dist/esm/commands/shared.js.map +1 -1
  74. package/dist/esm/commands/status.js +43 -5
  75. package/dist/esm/commands/status.js.map +1 -1
  76. package/dist/esm/commands/types.d.ts +1 -0
  77. package/dist/esm/commands/types.js.map +1 -1
  78. package/dist/esm/config.d.ts +32 -2
  79. package/dist/esm/config.js +98 -9
  80. package/dist/esm/config.js.map +1 -1
  81. package/dist/esm/db.d.ts +1 -1
  82. package/dist/esm/db.js +20 -11
  83. package/dist/esm/db.js.map +1 -1
  84. package/dist/esm/errors.d.ts +1 -1
  85. package/dist/esm/errors.js.map +1 -1
  86. package/dist/esm/features/embed.d.ts +15 -0
  87. package/dist/esm/features/embed.js +299 -0
  88. package/dist/esm/features/embed.js.map +1 -0
  89. package/dist/esm/features/index.js +3 -1
  90. package/dist/esm/features/index.js.map +1 -1
  91. package/dist/esm/features/types.d.ts +4 -1
  92. package/dist/esm/features/types.js.map +1 -1
  93. package/dist/esm/index.d.ts +1 -1
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/output.d.ts +5 -0
  96. package/dist/esm/output.js +40 -8
  97. package/dist/esm/output.js.map +1 -1
  98. package/dist/esm/scan.d.ts +1 -1
  99. package/dist/esm/scan.js +17 -10
  100. package/dist/esm/scan.js.map +1 -1
  101. package/dist/esm/verbs.d.ts +8 -2
  102. package/dist/esm/verbs.js +92 -18
  103. package/dist/esm/verbs.js.map +1 -1
  104. package/package.json +2 -1
  105. package/schema.json +35 -1
  106. package/skills/sense/EXAMPLES.md +16 -0
  107. package/skills/sense/SKILL.md +60 -20
  108. package/skills/sense-setup/SKILL.md +91 -0
@@ -12,8 +12,8 @@ _export(exports, {
12
12
  get CONFIG_FILENAME () {
13
13
  return CONFIG_FILENAME;
14
14
  },
15
- get FEATURE_NAMES () {
16
- return FEATURE_NAMES;
15
+ get DEFAULT_EMBED_MODEL () {
16
+ return DEFAULT_EMBED_MODEL;
17
17
  },
18
18
  get STATE_DIR () {
19
19
  return STATE_DIR;
@@ -21,12 +21,21 @@ _export(exports, {
21
21
  get SUPPORTED_CONFIG_VERSION () {
22
22
  return SUPPORTED_CONFIG_VERSION;
23
23
  },
24
+ get embedConfig () {
25
+ return embedConfig;
26
+ },
24
27
  get enabledFeatures () {
25
28
  return enabledFeatures;
26
29
  },
27
30
  get featureEnabled () {
28
31
  return featureEnabled;
29
32
  },
33
+ get featureSignature () {
34
+ return featureSignature;
35
+ },
36
+ get featureStates () {
37
+ return featureStates;
38
+ },
30
39
  get findConfigPath () {
31
40
  return findConfigPath;
32
41
  },
@@ -43,6 +52,17 @@ _export(exports, {
43
52
  var _nodefs = require("node:fs");
44
53
  var _nodepath = require("node:path");
45
54
  var _errorsts = require("./errors.js");
55
+ function _array_like_to_array(arr, len) {
56
+ if (len == null || len > arr.length) len = arr.length;
57
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
58
+ return arr2;
59
+ }
60
+ function _array_with_holes(arr) {
61
+ if (Array.isArray(arr)) return arr;
62
+ }
63
+ function _array_without_holes(arr) {
64
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
65
+ }
46
66
  function _define_property(obj, key, value) {
47
67
  if (key in obj) {
48
68
  Object.defineProperty(obj, key, {
@@ -56,6 +76,39 @@ function _define_property(obj, key, value) {
56
76
  }
57
77
  return obj;
58
78
  }
79
+ function _iterable_to_array(iter) {
80
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
81
+ }
82
+ function _iterable_to_array_limit(arr, i) {
83
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
84
+ if (_i == null) return;
85
+ var _arr = [];
86
+ var _n = true;
87
+ var _d = false;
88
+ var _s, _e;
89
+ try {
90
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
91
+ _arr.push(_s.value);
92
+ if (i && _arr.length === i) break;
93
+ }
94
+ } catch (err) {
95
+ _d = true;
96
+ _e = err;
97
+ } finally{
98
+ try {
99
+ if (!_n && _i["return"] != null) _i["return"]();
100
+ } finally{
101
+ if (_d) throw _e;
102
+ }
103
+ }
104
+ return _arr;
105
+ }
106
+ function _non_iterable_rest() {
107
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
108
+ }
109
+ function _non_iterable_spread() {
110
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
111
+ }
59
112
  function _object_spread(target) {
60
113
  for(var i = 1; i < arguments.length; i++){
61
114
  var source = arguments[i] != null ? arguments[i] : {};
@@ -95,21 +148,45 @@ function _object_spread_props(target, source) {
95
148
  }
96
149
  return target;
97
150
  }
151
+ function _sliced_to_array(arr, i) {
152
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
153
+ }
154
+ function _to_consumable_array(arr) {
155
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
156
+ }
98
157
  function _type_of(obj) {
99
158
  "@swc/helpers - typeof";
100
159
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
101
160
  }
161
+ function _unsupported_iterable_to_array(o, minLen) {
162
+ if (!o) return;
163
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
164
+ var n = Object.prototype.toString.call(o).slice(8, -1);
165
+ if (n === "Object" && o.constructor) n = o.constructor.name;
166
+ if (n === "Map" || n === "Set") return Array.from(n);
167
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
168
+ }
102
169
  var CONFIG_FILENAME = 'sense.config.json';
103
170
  var STATE_DIR = '.sense';
104
171
  var SUPPORTED_CONFIG_VERSION = 2;
172
+ // Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.
173
+ // links/sections/rank are opt-out (absent = on); embed is opt-in (absent = off) -- most trees don't need vectors.
105
174
  var FEATURE_NAMES = [
175
+ 'links',
176
+ 'sections',
177
+ 'rank',
178
+ 'embed'
179
+ ];
180
+ var OPT_OUT_NAMES = [
106
181
  'links',
107
182
  'sections',
108
183
  'rank'
109
184
  ];
185
+ var DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';
110
186
  function featureEnabled(cfg, name) {
111
- var _cfg_features;
112
- var enabled = ((_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features[name]) !== false;
187
+ var _cfg_features, _cfg_features1;
188
+ if (name === 'embed') return Boolean((_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features.embed);
189
+ var enabled = ((_cfg_features1 = cfg.features) === null || _cfg_features1 === void 0 ? void 0 : _cfg_features1[name]) !== false;
113
190
  if (name === 'rank') return enabled && featureEnabled(cfg, 'links');
114
191
  return enabled;
115
192
  }
@@ -118,13 +195,44 @@ function enabledFeatures(cfg) {
118
195
  return featureEnabled(cfg, name);
119
196
  });
120
197
  }
198
+ function featureStates(cfg) {
199
+ return {
200
+ on: FEATURE_NAMES.filter(function(name) {
201
+ return featureEnabled(cfg, name);
202
+ }),
203
+ off: FEATURE_NAMES.filter(function(name) {
204
+ return !featureEnabled(cfg, name);
205
+ })
206
+ };
207
+ }
208
+ function embedConfig(cfg) {
209
+ var _o_model, _o_type;
210
+ var _cfg_features;
211
+ var e = (_cfg_features = cfg.features) === null || _cfg_features === void 0 ? void 0 : _cfg_features.embed;
212
+ if (!e) return null;
213
+ var o = e === true ? {} : e;
214
+ return {
215
+ model: (_o_model = o.model) !== null && _o_model !== void 0 ? _o_model : DEFAULT_EMBED_MODEL,
216
+ type: (_o_type = o.type) !== null && _o_type !== void 0 ? _o_type : 'static',
217
+ url: o.url,
218
+ key: o.key
219
+ };
220
+ }
221
+ function featureSignature(cfg) {
222
+ return enabledFeatures(cfg).map(function(name) {
223
+ if (name !== 'embed') return name;
224
+ var e = embedConfig(cfg);
225
+ return "embed:".concat(e === null || e === void 0 ? void 0 : e.type, ":").concat(e === null || e === void 0 ? void 0 : e.model);
226
+ }).join(',');
227
+ }
121
228
  // Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.
122
229
  var MIGRATIONS = {
123
- // v1 -> v2: features block introduced, everything enabled (matches the old implicit behavior of `links` etc. not existing).
230
+ // v1 -> v2: features block introduced, opt-out features enabled (matches the old implicit
231
+ // behavior of `links` etc. not existing). embed stays absent -- opt-in.
124
232
  1: function(cfg) {
125
233
  return _object_spread_props(_object_spread({}, cfg), {
126
234
  version: 2,
127
- features: Object.fromEntries(FEATURE_NAMES.map(function(name) {
235
+ features: Object.fromEntries(OPT_OUT_NAMES.map(function(name) {
128
236
  return [
129
237
  name,
130
238
  true
@@ -156,7 +264,7 @@ function starterConfig() {
156
264
  '**/*.md'
157
265
  ]
158
266
  },
159
- features: Object.fromEntries(FEATURE_NAMES.map(function(name) {
267
+ features: Object.fromEntries(OPT_OUT_NAMES.map(function(name) {
160
268
  return [
161
269
  name,
162
270
  true
@@ -186,7 +294,50 @@ function findConfigPath(startDir) {
186
294
  // Shape check for hand-edited files: a typo'd config fails with a named error, not a
187
295
  // TypeError from whatever code touched the missing field first. `queries` is optional
188
296
  // on disk (absent = none); `scan.include` has no usable default.
297
+ // Keys each block actually reads. A key outside these sets is accepted and ignored, which
298
+ // looks identical to working -- one field report set `scan.exclude`, saw no change in doc
299
+ // count and no warning, and reasonably concluded the filter had applied. Reported as a
300
+ // warning rather than an error so a config carrying an unknown key still runs.
301
+ var KNOWN_KEYS = new Set([
302
+ '$schema',
303
+ 'version',
304
+ 'scan',
305
+ 'features',
306
+ 'defaults',
307
+ 'queries'
308
+ ]);
309
+ var KNOWN_SCAN_KEYS = new Set([
310
+ 'include'
311
+ ]);
312
+ function unknownConfigKeys(cfg) {
313
+ var unknown = Object.keys(cfg).filter(function(k) {
314
+ return !KNOWN_KEYS.has(k);
315
+ }).map(function(k) {
316
+ return k;
317
+ });
318
+ var scan = cfg.scan;
319
+ if (scan && (typeof scan === "undefined" ? "undefined" : _type_of(scan)) === 'object' && !Array.isArray(scan)) {
320
+ var _unknown;
321
+ (_unknown = unknown).push.apply(_unknown, _to_consumable_array(Object.keys(scan).filter(function(k) {
322
+ return !KNOWN_SCAN_KEYS.has(k);
323
+ }).map(function(k) {
324
+ return "scan.".concat(k);
325
+ })));
326
+ }
327
+ var features = cfg.features;
328
+ if (features && (typeof features === "undefined" ? "undefined" : _type_of(features)) === 'object' && !Array.isArray(features)) {
329
+ var _unknown1;
330
+ (_unknown1 = unknown).push.apply(_unknown1, _to_consumable_array(Object.keys(features).filter(function(k) {
331
+ return !FEATURE_NAMES.includes(k);
332
+ }).map(function(k) {
333
+ return "features.".concat(k);
334
+ })));
335
+ }
336
+ return unknown;
337
+ }
189
338
  function validateConfig(parsed, configPath) {
339
+ var _cfg_features;
340
+ var _defaults_find;
190
341
  if ((typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) !== 'object' || parsed === null || Array.isArray(parsed)) {
191
342
  throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": config must be a JSON object"));
192
343
  }
@@ -203,11 +354,46 @@ function validateConfig(parsed, configPath) {
203
354
  })) {
204
355
  throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": queries must be an object of name -> SQL string"));
205
356
  }
206
- if (cfg.features !== undefined && (_type_of(cfg.features) !== 'object' || cfg.features === null || Array.isArray(cfg.features) || !Object.values(cfg.features).every(function(v) {
207
- return typeof v === 'boolean';
208
- }))) {
357
+ var defaults = cfg.defaults;
358
+ if (defaults !== undefined && ((typeof defaults === "undefined" ? "undefined" : _type_of(defaults)) !== 'object' || defaults === null || Array.isArray(defaults) || defaults.find !== undefined && typeof ((_defaults_find = defaults.find) === null || _defaults_find === void 0 ? void 0 : _defaults_find.where) !== 'string')) {
359
+ throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": defaults.find.where must be a SQL condition string"));
360
+ }
361
+ if (cfg.features !== undefined && (_type_of(cfg.features) !== 'object' || cfg.features === null || Array.isArray(cfg.features))) {
209
362
  throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": features must be an object of name -> boolean"));
210
363
  }
364
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
365
+ try {
366
+ var _loop = function() {
367
+ var _step_value = _sliced_to_array(_step.value, 2), name = _step_value[0], value = _step_value[1];
368
+ if (typeof value === 'boolean') return "continue";
369
+ // embed alone takes an object form: { model?, type?: static|api, url?, key? }.
370
+ var embed = value;
371
+ var shapeOk = name === 'embed' && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object' && value !== null && !Array.isArray(value) && [
372
+ 'model',
373
+ 'url',
374
+ 'key'
375
+ ].every(function(f) {
376
+ return embed[f] === undefined || typeof embed[f] === 'string';
377
+ }) && (embed.type === undefined || embed.type === 'static' || embed.type === 'api');
378
+ if (!shapeOk) {
379
+ throw new _errorsts.SenseError('CONFIG_INVALID', "".concat(configPath, ": features.").concat(name, " must be a boolean").concat(name === 'embed' ? ' or { model?, type?: "static"|"api", url?, key? }' : ''));
380
+ }
381
+ };
382
+ for(var _iterator = Object.entries((_cfg_features = cfg.features) !== null && _cfg_features !== void 0 ? _cfg_features : {})[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
383
+ } catch (err) {
384
+ _didIteratorError = true;
385
+ _iteratorError = err;
386
+ } finally{
387
+ try {
388
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
389
+ _iterator.return();
390
+ }
391
+ } finally{
392
+ if (_didIteratorError) {
393
+ throw _iteratorError;
394
+ }
395
+ }
396
+ }
211
397
  return cfg;
212
398
  }
213
399
  function loadConfig(explicitPath) {
@@ -238,10 +424,12 @@ function loadConfig(explicitPath) {
238
424
  migratedFrom = result.from;
239
425
  (0, _nodefs.writeFileSync)(configPath, "".concat(JSON.stringify(cfg, null, 2), "\n"));
240
426
  }
427
+ var unknownKeys = unknownConfigKeys(cfg);
241
428
  return _object_spread_props(_object_spread({}, cfg), {
242
429
  baseDir: (0, _nodepath.dirname)(configPath),
243
430
  configPath: configPath,
244
- migratedFrom: migratedFrom
431
+ migratedFrom: migratedFrom,
432
+ unknownKeys: unknownKeys.length > 0 ? unknownKeys : undefined
245
433
  });
246
434
  }
247
435
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { SenseError } from './errors.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 2;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.\nexport const FEATURE_NAMES = ['links', 'sections', 'rank'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n scan: { include: string[] };\n features?: Partial<Record<FeatureName, boolean>>;\n queries: Record<string, string>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\n// Absent block or key means enabled -- features are opt-out. `rank` additionally requires `links`.\nexport function featureEnabled(cfg: Config, name: FeatureName): boolean {\n const enabled = cfg.features?.[name] !== false;\n if (name === 'rank') return enabled && featureEnabled(cfg, 'links');\n return enabled;\n}\n\nexport function enabledFeatures(cfg: Config): FeatureName[] {\n return FEATURE_NAMES.filter((name) => featureEnabled(cfg, name));\n}\n\n// Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.\nconst MIGRATIONS: Record<number, (cfg: Config) => Config> = {\n // v1 -> v2: features block introduced, everything enabled (matches the old implicit behavior of `links` etc. not existing).\n 1: (cfg) => ({ ...cfg, version: 2, features: Object.fromEntries(FEATURE_NAMES.map((name) => [name, true])) as Config['features'] }),\n};\n\nexport function migrateConfig(cfg: Config): { cfg: Config; from: number } {\n const from = cfg.version ?? 1;\n let current = cfg;\n for (let v = from; v < SUPPORTED_CONFIG_VERSION; v++) {\n const step = MIGRATIONS[v];\n if (!step) throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `no migration from config version ${v}`);\n current = step(current);\n }\n return { cfg: current, from };\n}\n\nfunction starterConfig(): Config {\n return {\n $schema: 'https://unpkg.com/sensemaking/schema.json',\n version: SUPPORTED_CONFIG_VERSION,\n scan: { include: ['**/*.md'] },\n features: Object.fromEntries(FEATURE_NAMES.map((name) => [name, true])) as Config['features'],\n queries: {},\n };\n}\n\n// Refuses to overwrite an existing config.\nexport function initConfig(dir: string): string {\n const configPath = join(dir, CONFIG_FILENAME);\n if (existsSync(configPath)) {\n throw new SenseError('CONFIG_EXISTS', `${CONFIG_FILENAME} already exists in ${dir}`);\n }\n writeFileSync(configPath, `${JSON.stringify(starterConfig(), null, 2)}\\n`);\n return configPath;\n}\n\nexport function findConfigPath(startDir: string): string | null {\n let dir = resolve(startDir);\n for (;;) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n// Shape check for hand-edited files: a typo'd config fails with a named error, not a\n// TypeError from whatever code touched the missing field first. `queries` is optional\n// on disk (absent = none); `scan.include` has no usable default.\nfunction validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n const scan = cfg.scan as { include?: unknown } | undefined;\n if (!scan || !Array.isArray(scan.include) || scan.include.length === 0 || !scan.include.every((g) => typeof g === 'string')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries) || !Object.values(cfg.queries).every((q) => typeof q === 'string')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> SQL string`);\n }\n if (cfg.features !== undefined && (typeof cfg.features !== 'object' || cfg.features === null || Array.isArray(cfg.features) || !Object.values(cfg.features).every((v) => typeof v === 'boolean'))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n return cfg as unknown as Config;\n}\n\nexport function loadConfig(explicitPath?: string): ResolvedConfig {\n let configPath: string;\n if (explicitPath) {\n configPath = resolve(process.cwd(), explicitPath);\n if (!existsSync(configPath)) throw new SenseError('CONFIG_NOT_FOUND', `config not found: ${configPath}`);\n } else {\n const found = findConfigPath(process.cwd());\n if (!found) {\n throw new SenseError('CONFIG_NOT_FOUND', `could not find ${CONFIG_FILENAME} in ${process.cwd()} or any parent directory`);\n }\n configPath = found;\n }\n\n const raw = readFileSync(configPath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n\n // Version gate before shape validation: a config written by a newer sense should fail\n // with \"requires a newer sense\", not with shape errors its own version may not have.\n const version = typeof parsed === 'object' && parsed !== null && typeof (parsed as { version?: unknown }).version === 'number' ? (parsed as { version: number }).version : 1;\n if (version > SUPPORTED_CONFIG_VERSION) {\n throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `config version ${version} requires a newer sense`);\n }\n\n let cfg = validateConfig(parsed, configPath);\n\n let migratedFrom: number | undefined;\n if (version < SUPPORTED_CONFIG_VERSION) {\n const result = migrateConfig(cfg);\n cfg = result.cfg;\n migratedFrom = result.from;\n writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\\n`);\n }\n\n return { ...cfg, baseDir: dirname(configPath), configPath, migratedFrom };\n}\n"],"names":["CONFIG_FILENAME","FEATURE_NAMES","STATE_DIR","SUPPORTED_CONFIG_VERSION","enabledFeatures","featureEnabled","findConfigPath","initConfig","loadConfig","migrateConfig","cfg","name","enabled","features","filter","MIGRATIONS","version","Object","fromEntries","map","from","current","v","step","SenseError","starterConfig","$schema","scan","include","queries","dir","configPath","join","existsSync","writeFileSync","JSON","stringify","startDir","resolve","candidate","parent","dirname","validateConfig","parsed","Array","isArray","length","every","g","undefined","values","q","explicitPath","process","cwd","found","raw","readFileSync","parse","migratedFrom","result","baseDir"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { SenseError } from './errors.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 2;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; verbs degrade when one is off.\n// links/sections/rank are opt-out (absent = on); embed is opt-in (absent = off) -- most trees don't need vectors.\nconst FEATURE_NAMES = ['links', 'sections', 'rank', 'embed'] as const;\nconst OPT_OUT_NAMES = ['links', 'sections', 'rank'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `true` = all defaults (static type, default model). A local `model` path skips the network.\nexport interface EmbedConfig {\n model?: string;\n type?: 'static' | 'api';\n url?: string; // api type: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // api type: name of the env var holding the bearer token, if any\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n scan: { include: string[] };\n features?: { links?: boolean; sections?: boolean; rank?: boolean; embed?: boolean | EmbedConfig };\n // Tree-declared default scope for `find`. A --where on the invocation replaces it, so a\n // caller can always reach the whole tree (`--where \"1=1\"`).\n defaults?: { find?: { where?: string } };\n queries: Record<string, string>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Keys the file declares that this build does not read; cli reports them.\n unknownKeys?: string[];\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\n// Opt-out features: absent block or key means enabled. `rank` additionally requires `links`.\n// `embed` is the opposite: enabled only when the config says so.\nexport function featureEnabled(cfg: Config, name: FeatureName): boolean {\n if (name === 'embed') return Boolean(cfg.features?.embed);\n const enabled = cfg.features?.[name] !== false;\n if (name === 'rank') return enabled && featureEnabled(cfg, 'links');\n return enabled;\n}\n\nexport function enabledFeatures(cfg: Config): FeatureName[] {\n return FEATURE_NAMES.filter((name) => featureEnabled(cfg, name));\n}\n\n// Every feature with its current state; verbs surface this so \"off\" and \"empty\" stay\n// distinguishable in output.\nexport function featureStates(cfg: Config): { on: FeatureName[]; off: FeatureName[] } {\n return {\n on: FEATURE_NAMES.filter((name) => featureEnabled(cfg, name)),\n off: FEATURE_NAMES.filter((name) => !featureEnabled(cfg, name)),\n };\n}\n\n// Resolved embed settings, or null when the feature is off.\nexport function embedConfig(cfg: Config): { model: string; type: 'static' | 'api'; url?: string; key?: string } | null {\n const e = cfg.features?.embed;\n if (!e) return null;\n const o = e === true ? {} : e;\n return { model: o.model ?? DEFAULT_EMBED_MODEL, type: o.type ?? 'static', url: o.url, key: o.key };\n}\n\n// Cache-key string: embed carries its type + model so a model change rebuilds like a toggle.\nexport function featureSignature(cfg: Config): string {\n return enabledFeatures(cfg)\n .map((name) => {\n if (name !== 'embed') return name;\n const e = embedConfig(cfg);\n return `embed:${e?.type}:${e?.model}`;\n })\n .join(',');\n}\n\n// Pure per-version steps; loadConfig chains them from the file's version up to SUPPORTED_CONFIG_VERSION.\nconst MIGRATIONS: Record<number, (cfg: Config) => Config> = {\n // v1 -> v2: features block introduced, opt-out features enabled (matches the old implicit\n // behavior of `links` etc. not existing). embed stays absent -- opt-in.\n 1: (cfg) => ({ ...cfg, version: 2, features: Object.fromEntries(OPT_OUT_NAMES.map((name) => [name, true])) as Config['features'] }),\n};\n\nexport function migrateConfig(cfg: Config): { cfg: Config; from: number } {\n const from = cfg.version ?? 1;\n let current = cfg;\n for (let v = from; v < SUPPORTED_CONFIG_VERSION; v++) {\n const step = MIGRATIONS[v];\n if (!step) throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `no migration from config version ${v}`);\n current = step(current);\n }\n return { cfg: current, from };\n}\n\nfunction starterConfig(): Config {\n return {\n $schema: 'https://unpkg.com/sensemaking/schema.json',\n version: SUPPORTED_CONFIG_VERSION,\n scan: { include: ['**/*.md'] },\n features: Object.fromEntries(OPT_OUT_NAMES.map((name) => [name, true])) as Config['features'],\n queries: {},\n };\n}\n\n// Refuses to overwrite an existing config.\nexport function initConfig(dir: string): string {\n const configPath = join(dir, CONFIG_FILENAME);\n if (existsSync(configPath)) {\n throw new SenseError('CONFIG_EXISTS', `${CONFIG_FILENAME} already exists in ${dir}`);\n }\n writeFileSync(configPath, `${JSON.stringify(starterConfig(), null, 2)}\\n`);\n return configPath;\n}\n\nexport function findConfigPath(startDir: string): string | null {\n let dir = resolve(startDir);\n for (;;) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n// Shape check for hand-edited files: a typo'd config fails with a named error, not a\n// TypeError from whatever code touched the missing field first. `queries` is optional\n// on disk (absent = none); `scan.include` has no usable default.\n// Keys each block actually reads. A key outside these sets is accepted and ignored, which\n// looks identical to working -- one field report set `scan.exclude`, saw no change in doc\n// count and no warning, and reasonably concluded the filter had applied. Reported as a\n// warning rather than an error so a config carrying an unknown key still runs.\nconst KNOWN_KEYS = new Set(['$schema', 'version', 'scan', 'features', 'defaults', 'queries']);\nconst KNOWN_SCAN_KEYS = new Set(['include']);\n\nfunction unknownConfigKeys(cfg: Record<string, unknown>): string[] {\n const unknown = Object.keys(cfg)\n .filter((k) => !KNOWN_KEYS.has(k))\n .map((k) => k);\n const scan = cfg.scan as Record<string, unknown> | undefined;\n if (scan && typeof scan === 'object' && !Array.isArray(scan)) {\n unknown.push(\n ...Object.keys(scan)\n .filter((k) => !KNOWN_SCAN_KEYS.has(k))\n .map((k) => `scan.${k}`)\n );\n }\n const features = cfg.features as Record<string, unknown> | undefined;\n if (features && typeof features === 'object' && !Array.isArray(features)) {\n unknown.push(\n ...Object.keys(features)\n .filter((k) => !(FEATURE_NAMES as readonly string[]).includes(k))\n .map((k) => `features.${k}`)\n );\n }\n return unknown;\n}\n\nfunction validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n const scan = cfg.scan as { include?: unknown } | undefined;\n if (!scan || !Array.isArray(scan.include) || scan.include.length === 0 || !scan.include.every((g) => typeof g === 'string')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries) || !Object.values(cfg.queries).every((q) => typeof q === 'string')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> SQL string`);\n }\n const defaults = cfg.defaults as { find?: { where?: unknown } } | undefined;\n if (defaults !== undefined && (typeof defaults !== 'object' || defaults === null || Array.isArray(defaults) || (defaults.find !== undefined && typeof defaults.find?.where !== 'string'))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: defaults.find.where must be a SQL condition string`);\n }\n if (cfg.features !== undefined && (typeof cfg.features !== 'object' || cfg.features === null || Array.isArray(cfg.features))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n for (const [name, value] of Object.entries(cfg.features ?? {})) {\n if (typeof value === 'boolean') continue;\n // embed alone takes an object form: { model?, type?: static|api, url?, key? }.\n const embed = value as Record<string, unknown>;\n const shapeOk = name === 'embed' && typeof value === 'object' && value !== null && !Array.isArray(value) && ['model', 'url', 'key'].every((f) => embed[f] === undefined || typeof embed[f] === 'string') && (embed.type === undefined || embed.type === 'static' || embed.type === 'api');\n if (!shapeOk) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean${name === 'embed' ? ' or { model?, type?: \"static\"|\"api\", url?, key? }' : ''}`);\n }\n }\n return cfg as unknown as Config;\n}\n\nexport function loadConfig(explicitPath?: string): ResolvedConfig {\n let configPath: string;\n if (explicitPath) {\n configPath = resolve(process.cwd(), explicitPath);\n if (!existsSync(configPath)) throw new SenseError('CONFIG_NOT_FOUND', `config not found: ${configPath}`);\n } else {\n const found = findConfigPath(process.cwd());\n if (!found) {\n throw new SenseError('CONFIG_NOT_FOUND', `could not find ${CONFIG_FILENAME} in ${process.cwd()} or any parent directory`);\n }\n configPath = found;\n }\n\n const raw = readFileSync(configPath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n\n // Version gate before shape validation: a config written by a newer sense should fail\n // with \"requires a newer sense\", not with shape errors its own version may not have.\n const version = typeof parsed === 'object' && parsed !== null && typeof (parsed as { version?: unknown }).version === 'number' ? (parsed as { version: number }).version : 1;\n if (version > SUPPORTED_CONFIG_VERSION) {\n throw new SenseError('CONFIG_VERSION_UNSUPPORTED', `config version ${version} requires a newer sense`);\n }\n\n let cfg = validateConfig(parsed, configPath);\n\n let migratedFrom: number | undefined;\n if (version < SUPPORTED_CONFIG_VERSION) {\n const result = migrateConfig(cfg);\n cfg = result.cfg;\n migratedFrom = result.from;\n writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\\n`);\n }\n\n const unknownKeys = unknownConfigKeys(cfg as unknown as Record<string, unknown>);\n return { ...cfg, baseDir: dirname(configPath), configPath, migratedFrom, unknownKeys: unknownKeys.length > 0 ? unknownKeys : undefined };\n}\n"],"names":["CONFIG_FILENAME","DEFAULT_EMBED_MODEL","STATE_DIR","SUPPORTED_CONFIG_VERSION","embedConfig","enabledFeatures","featureEnabled","featureSignature","featureStates","findConfigPath","initConfig","loadConfig","migrateConfig","FEATURE_NAMES","OPT_OUT_NAMES","cfg","name","Boolean","features","embed","enabled","filter","on","off","o","e","model","type","url","key","map","join","MIGRATIONS","version","Object","fromEntries","from","current","v","step","SenseError","starterConfig","$schema","scan","include","queries","dir","configPath","existsSync","writeFileSync","JSON","stringify","startDir","resolve","candidate","parent","dirname","KNOWN_KEYS","Set","KNOWN_SCAN_KEYS","unknownConfigKeys","unknown","keys","k","has","Array","isArray","push","includes","validateConfig","parsed","defaults","length","every","g","undefined","values","q","find","where","value","shapeOk","f","entries","explicitPath","process","cwd","found","raw","readFileSync","parse","migratedFrom","result","unknownKeys","baseDir"],"mappings":"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"}
package/dist/cjs/db.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  import type { Config, ResolvedConfig } from './config.js';
3
3
  export declare const DB_FILENAME = "cache.db";
4
- export declare const SCHEMA_VERSION = "3";
4
+ export declare const SCHEMA_VERSION = "4";
5
5
  export interface OpenResult {
6
6
  db: DatabaseSync;
7
7
  cfg: ResolvedConfig;
package/dist/cjs/db.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  import type { Config, ResolvedConfig } from './config.js';
3
3
  export declare const DB_FILENAME = "cache.db";
4
- export declare const SCHEMA_VERSION = "3";
4
+ export declare const SCHEMA_VERSION = "4";
5
5
  export interface OpenResult {
6
6
  db: DatabaseSync;
7
7
  cfg: ResolvedConfig;
package/dist/cjs/db.js CHANGED
@@ -66,7 +66,7 @@ function _unsupported_iterable_to_array(o, minLen) {
66
66
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
67
67
  }
68
68
  var DB_FILENAME = 'cache.db';
69
- var SCHEMA_VERSION = '3';
69
+ var SCHEMA_VERSION = '4'; // 4: frontmatter scalars store as INTEGER/REAL/TEXT by YAML type (was: all numbers REAL)
70
70
  function quoteIdent(name) {
71
71
  return '"'.concat(name.split('"').join('""'), '"');
72
72
  }
@@ -126,7 +126,7 @@ function ensureSchema(db, cfg) {
126
126
  }
127
127
  }
128
128
  if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);
129
- if (getMeta(db, 'features') === null) setMeta(db, 'features', (0, _configts.enabledFeatures)(cfg).join(','));
129
+ if (getMeta(db, 'features') === null) setMeta(db, 'features', (0, _configts.featureSignature)(cfg));
130
130
  }
131
131
  function getMeta(db, key) {
132
132
  var row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key);
@@ -301,6 +301,7 @@ function reconcile(db, cfg, baseDir) {
301
301
  var _loop = function() {
302
302
  var doc = _step5.value;
303
303
  var _insert;
304
+ var _feature_store;
304
305
  var values = allColumns.map(function(col) {
305
306
  var _doc_data_col;
306
307
  if (col === 'path') return doc.relPath;
@@ -309,27 +310,52 @@ function reconcile(db, cfg, baseDir) {
309
310
  return (_doc_data_col = doc.data[col]) !== null && _doc_data_col !== void 0 ? _doc_data_col : null;
310
311
  });
311
312
  (_insert = insert).run.apply(_insert, _to_consumable_array(values));
312
- delBody.run(doc.relPath);
313
+ // Delete-before-insert only for docs that have rows: an FTS5 DELETE by column
314
+ // cannot use an index and scans the whole table, so running it per doc on a
315
+ // cold build (empty table, nothing to delete) made the crawl quadratic --
316
+ // measured 4x time per note-count doubling at 13k/26k notes.
317
+ if (existing.has(doc.relPath)) {
318
+ var _feature_remove;
319
+ delBody.run(doc.relPath);
320
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
321
+ try {
322
+ for(var _iterator = features[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
323
+ var feature = _step.value;
324
+ (_feature_remove = feature.remove) === null || _feature_remove === void 0 ? void 0 : _feature_remove.call(feature, db, doc.relPath);
325
+ }
326
+ } catch (err) {
327
+ _didIteratorError = true;
328
+ _iteratorError = err;
329
+ } finally{
330
+ try {
331
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
332
+ _iterator.return();
333
+ }
334
+ } finally{
335
+ if (_didIteratorError) {
336
+ throw _iteratorError;
337
+ }
338
+ }
339
+ }
340
+ }
313
341
  insertBody.run(doc.search.title, doc.search.summary, doc.search.text, doc.relPath);
314
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
342
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
315
343
  try {
316
- for(var _iterator = features[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
317
- var feature = _step.value;
318
- var _feature_remove, _feature_store;
319
- (_feature_remove = feature.remove) === null || _feature_remove === void 0 ? void 0 : _feature_remove.call(feature, db, doc.relPath);
320
- (_feature_store = feature.store) === null || _feature_store === void 0 ? void 0 : _feature_store.call(feature, db, doc.relPath, doc.extracted[feature.name]);
344
+ for(var _iterator1 = features[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
345
+ var feature1 = _step1.value;
346
+ (_feature_store = feature1.store) === null || _feature_store === void 0 ? void 0 : _feature_store.call(feature1, db, doc.relPath, doc.extracted[feature1.name]);
321
347
  }
322
348
  } catch (err) {
323
- _didIteratorError = true;
324
- _iteratorError = err;
349
+ _didIteratorError1 = true;
350
+ _iteratorError1 = err;
325
351
  } finally{
326
352
  try {
327
- if (!_iteratorNormalCompletion && _iterator.return != null) {
328
- _iterator.return();
353
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
354
+ _iterator1.return();
329
355
  }
330
356
  } finally{
331
- if (_didIteratorError) {
332
- throw _iteratorError;
357
+ if (_didIteratorError1) {
358
+ throw _iteratorError1;
333
359
  }
334
360
  }
335
361
  }
@@ -388,14 +414,18 @@ function open(cfg) {
388
414
  var dbPath = (0, _nodepath.join)(stateDir, DB_FILENAME);
389
415
  var db = new _nodesqlite.DatabaseSync(dbPath);
390
416
  db.exec('PRAGMA journal_mode = WAL');
391
- db.exec('PRAGMA busy_timeout = 5000');
417
+ // Covers a concurrent watcher's bulk reconcile: the write transaction for 500 changed
418
+ // files measures ~5s at 26k notes, so 5s expired exactly at the boundary and queries
419
+ // racing the watcher got SQLITE_BUSY. 30s bounds the wait at ~3x the largest measured
420
+ // reconcile; a query that outwaits it still fails loudly.
421
+ db.exec('PRAGMA busy_timeout = 30000');
392
422
  registerFunctions(db);
393
423
  db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');
394
424
  // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an
395
425
  // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).
396
426
  var version = getMeta(db, 'schema_version');
397
427
  var features = getMeta(db, 'features');
398
- var wantFeatures = (0, _configts.enabledFeatures)(cfg).join(',');
428
+ var wantFeatures = (0, _configts.featureSignature)(cfg);
399
429
  if (version !== null && version !== SCHEMA_VERSION || features !== null && features !== wantFeatures) {
400
430
  db.close();
401
431
  (0, _nodefs.rmSync)(stateDir, {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/db.ts"],"sourcesContent":["import { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from './config.ts';\nimport { enabledFeatures, STATE_DIR } from './config.ts';\nimport { activeFeatures } from './features/index.ts';\nimport type { ParsedDoc } from './scan.ts';\nimport { listFiles, parseFile } from './scan.ts';\n\n// Rows -> SQLite: core schema, reconcile loop, has(). Parsing lives in scan.ts;\n// everything beyond frontmatter + content lives in src/features/.\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`.\nexport const SCHEMA_VERSION = '3';\n\nexport interface OpenResult {\n db: DatabaseSync;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.split('\"').join('\"\"')}\"`;\n}\n\n// has(field, value): JSON-array field -> membership, string field -> substring, NULL -> false.\nfunction registerFunctions(db: DatabaseSync): void {\n db.function('has', { deterministic: true, varargs: false }, (field: unknown, value: unknown): number => {\n if (field === null || field === undefined) return 0;\n\n const needle = String(value);\n\n if (typeof field === 'string') {\n if (field.startsWith('[')) {\n try {\n const parsed = JSON.parse(field);\n if (Array.isArray(parsed)) {\n return parsed.some((item) => String(item) === needle) ? 1 : 0;\n }\n } catch {}\n }\n return field.includes(needle) ? 1 : 0;\n }\n\n return String(field).includes(needle) ? 1 : 0;\n });\n}\n\nfunction getColumns(db: DatabaseSync): Set<string> {\n const rows = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nfunction ensureSchema(db: DatabaseSync, cfg: Config): void {\n db.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_size\" INTEGER)`);\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, tokenize = 'porter unicode61')`);\n for (const feature of activeFeatures(cfg)) feature.schema(db);\n if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);\n if (getMeta(db, 'features') === null) setMeta(db, 'features', enabledFeatures(cfg).join(','));\n}\n\nexport function getMeta(db: DatabaseSync, key: string): string | null {\n const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined;\n return row ? row.value : null;\n}\n\nexport function setMeta(db: DatabaseSync, key: string, value: string | null): void {\n if (value === null) {\n db.prepare('DELETE FROM meta WHERE key = ?').run(key);\n return;\n }\n db.prepare('INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, value);\n}\n\nexport function docCount(db: DatabaseSync): number {\n const row = db.prepare('SELECT COUNT(*) AS n FROM frontmatter').get() as { n: number };\n return row.n;\n}\n\nexport function reconcile(db: DatabaseSync, cfg: Config, baseDir: string): { parsed: number; warnings: string[] } {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingRows = db.prepare(`SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter`).all() as Array<{\n path: string;\n _mtime: number;\n _size: number;\n }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = getColumns(db);\n const newColumns: string[] = [];\n const parsedDocs: ParsedDoc[] = [];\n const warnings: string[] = [];\n\n for (const file of toReparse) {\n const { doc, warnings: fileWarnings } = parseFile(file, features);\n warnings.push(...fileWarnings);\n for (const key of Object.keys(doc.data)) {\n if (!seenColumns.has(key)) {\n seenColumns.add(key);\n newColumns.push(key);\n }\n }\n parsedDocs.push(doc);\n }\n\n const allColumns = [...seenColumns];\n const insertSql = `INSERT OR REPLACE INTO frontmatter (${allColumns.map(quoteIdent).join(', ')}) VALUES (${allColumns.map(() => '?').join(', ')})`;\n\n db.exec('BEGIN');\n try {\n for (const col of newColumns) db.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n // FTS5 has no upsert, so delete-before-insert into `content`.\n const delBody = db.prepare(`DELETE FROM content WHERE \"path\" = ?`);\n if (vanished.length > 0) {\n const del = db.prepare(`DELETE FROM frontmatter WHERE \"path\" = ?`);\n for (const path of vanished) {\n del.run(path);\n delBody.run(path);\n for (const feature of features) feature.remove?.(db, path);\n }\n }\n if (parsedDocs.length > 0) {\n const insert = db.prepare(insertSql);\n const insertBody = db.prepare(`INSERT INTO content (title, summary, text, \"path\") VALUES (?, ?, ?, ?)`);\n for (const doc of parsedDocs) {\n const values = allColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_size') return doc.size;\n return doc.data[col] ?? null;\n });\n insert.run(...values);\n delBody.run(doc.relPath);\n insertBody.run(doc.search.title, doc.search.summary, doc.search.text, doc.relPath);\n for (const feature of features) {\n feature.remove?.(db, doc.relPath);\n feature.store?.(db, doc.relPath, doc.extracted[feature.name]);\n }\n }\n }\n for (const feature of features) feature.afterReconcile?.(db, files);\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n\n return { parsed: parsedDocs.length, warnings };\n}\n\nexport function open(cfg: ResolvedConfig): OpenResult {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n db.exec('PRAGMA journal_mode = WAL');\n db.exec('PRAGMA busy_timeout = 5000');\n registerFunctions(db);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = getMeta(db, 'schema_version');\n const features = getMeta(db, 'features');\n const wantFeatures = enabledFeatures(cfg).join(',');\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n db.close();\n rmSync(stateDir, { recursive: true, force: true });\n return open(cfg);\n }\n\n ensureSchema(db, cfg);\n const { parsed, warnings } = reconcile(db, cfg, cfg.baseDir);\n\n return { db, cfg, dbPath, parsed, warnings };\n}\n\n// Manual reset for a doubted cache.\nexport function rebuild(cfg: ResolvedConfig): OpenResult {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n return open(cfg);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","docCount","getMeta","open","rebuild","reconcile","setMeta","quoteIdent","name","split","join","registerFunctions","db","function","deterministic","varargs","field","value","undefined","needle","String","startsWith","parsed","JSON","parse","Array","isArray","some","item","includes","getColumns","rows","prepare","all","Set","map","r","ensureSchema","cfg","exec","activeFeatures","feature","schema","enabledFeatures","key","row","get","run","n","baseDir","files","listFiles","currentSet","f","relPath","existingRows","existing","Map","path","vanished","filter","has","toReparse","_mtime","mtimeMs","_size","size","length","warnings","features","seenColumns","newColumns","parsedDocs","file","parseFile","doc","fileWarnings","push","Object","keys","data","add","allColumns","insertSql","col","delBody","del","remove","insert","insertBody","values","search","title","summary","text","store","extracted","afterReconcile","err","stateDir","STATE_DIR","mkdirSync","recursive","dbPath","DatabaseSync","version","wantFeatures","close","rmSync","force"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/db.ts"],"sourcesContent":["import { mkdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from './config.ts';\nimport { featureSignature, STATE_DIR } from './config.ts';\nimport { activeFeatures } from './features/index.ts';\nimport type { ParsedDoc } from './scan.ts';\nimport { listFiles, parseFile } from './scan.ts';\n\n// Rows -> SQLite: core schema, reconcile loop, has(). Parsing lives in scan.ts;\n// everything beyond frontmatter + content lives in src/features/.\n\nexport const DB_FILENAME = 'cache.db';\n// Cache shape version, independent of the config's own `version`.\nexport const SCHEMA_VERSION = '4'; // 4: frontmatter scalars store as INTEGER/REAL/TEXT by YAML type (was: all numbers REAL)\n\nexport interface OpenResult {\n db: DatabaseSync;\n cfg: ResolvedConfig;\n dbPath: string;\n parsed: number;\n warnings: string[];\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.split('\"').join('\"\"')}\"`;\n}\n\n// has(field, value): JSON-array field -> membership, string field -> substring, NULL -> false.\nfunction registerFunctions(db: DatabaseSync): void {\n db.function('has', { deterministic: true, varargs: false }, (field: unknown, value: unknown): number => {\n if (field === null || field === undefined) return 0;\n\n const needle = String(value);\n\n if (typeof field === 'string') {\n if (field.startsWith('[')) {\n try {\n const parsed = JSON.parse(field);\n if (Array.isArray(parsed)) {\n return parsed.some((item) => String(item) === needle) ? 1 : 0;\n }\n } catch {}\n }\n return field.includes(needle) ? 1 : 0;\n }\n\n return String(field).includes(needle) ? 1 : 0;\n });\n}\n\nfunction getColumns(db: DatabaseSync): Set<string> {\n const rows = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n}\n\n// Content is a separate table (not a column on frontmatter) so `SELECT * FROM frontmatter`\n// can't dump file text into context. Features add their own tables after the core ones.\nfunction ensureSchema(db: DatabaseSync, cfg: Config): void {\n db.exec(`CREATE TABLE IF NOT EXISTS frontmatter (\"path\" TEXT PRIMARY KEY, \"_mtime\" REAL, \"_size\" INTEGER)`);\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS content USING fts5(title, summary, text, path UNINDEXED, tokenize = 'porter unicode61')`);\n for (const feature of activeFeatures(cfg)) feature.schema(db);\n if (getMeta(db, 'schema_version') === null) setMeta(db, 'schema_version', SCHEMA_VERSION);\n if (getMeta(db, 'features') === null) setMeta(db, 'features', featureSignature(cfg));\n}\n\nexport function getMeta(db: DatabaseSync, key: string): string | null {\n const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined;\n return row ? row.value : null;\n}\n\nexport function setMeta(db: DatabaseSync, key: string, value: string | null): void {\n if (value === null) {\n db.prepare('DELETE FROM meta WHERE key = ?').run(key);\n return;\n }\n db.prepare('INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, value);\n}\n\nexport function docCount(db: DatabaseSync): number {\n const row = db.prepare('SELECT COUNT(*) AS n FROM frontmatter').get() as { n: number };\n return row.n;\n}\n\nexport function reconcile(db: DatabaseSync, cfg: Config, baseDir: string): { parsed: number; warnings: string[] } {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingRows = db.prepare(`SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter`).all() as Array<{\n path: string;\n _mtime: number;\n _size: number;\n }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = getColumns(db);\n const newColumns: string[] = [];\n const parsedDocs: ParsedDoc[] = [];\n const warnings: string[] = [];\n\n for (const file of toReparse) {\n const { doc, warnings: fileWarnings } = parseFile(file, features);\n warnings.push(...fileWarnings);\n for (const key of Object.keys(doc.data)) {\n if (!seenColumns.has(key)) {\n seenColumns.add(key);\n newColumns.push(key);\n }\n }\n parsedDocs.push(doc);\n }\n\n const allColumns = [...seenColumns];\n const insertSql = `INSERT OR REPLACE INTO frontmatter (${allColumns.map(quoteIdent).join(', ')}) VALUES (${allColumns.map(() => '?').join(', ')})`;\n\n db.exec('BEGIN');\n try {\n for (const col of newColumns) db.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n // FTS5 has no upsert, so delete-before-insert into `content`.\n const delBody = db.prepare(`DELETE FROM content WHERE \"path\" = ?`);\n if (vanished.length > 0) {\n const del = db.prepare(`DELETE FROM frontmatter WHERE \"path\" = ?`);\n for (const path of vanished) {\n del.run(path);\n delBody.run(path);\n for (const feature of features) feature.remove?.(db, path);\n }\n }\n if (parsedDocs.length > 0) {\n const insert = db.prepare(insertSql);\n const insertBody = db.prepare(`INSERT INTO content (title, summary, text, \"path\") VALUES (?, ?, ?, ?)`);\n for (const doc of parsedDocs) {\n const values = allColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_size') return doc.size;\n return doc.data[col] ?? null;\n });\n insert.run(...values);\n // Delete-before-insert only for docs that have rows: an FTS5 DELETE by column\n // cannot use an index and scans the whole table, so running it per doc on a\n // cold build (empty table, nothing to delete) made the crawl quadratic --\n // measured 4x time per note-count doubling at 13k/26k notes.\n if (existing.has(doc.relPath)) {\n delBody.run(doc.relPath);\n for (const feature of features) feature.remove?.(db, doc.relPath);\n }\n insertBody.run(doc.search.title, doc.search.summary, doc.search.text, doc.relPath);\n for (const feature of features) feature.store?.(db, doc.relPath, doc.extracted[feature.name]);\n }\n }\n for (const feature of features) feature.afterReconcile?.(db, files);\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n\n return { parsed: parsedDocs.length, warnings };\n}\n\nexport function open(cfg: ResolvedConfig): OpenResult {\n const stateDir = join(cfg.baseDir, STATE_DIR);\n mkdirSync(stateDir, { recursive: true });\n const dbPath = join(stateDir, DB_FILENAME);\n\n const db = new DatabaseSync(dbPath);\n db.exec('PRAGMA journal_mode = WAL');\n // Covers a concurrent watcher's bulk reconcile: the write transaction for 500 changed\n // files measures ~5s at 26k notes, so 5s expired exactly at the boundary and queries\n // racing the watcher got SQLITE_BUSY. 30s bounds the wait at ~3x the largest measured\n // reconcile; a query that outwaits it still fails loudly.\n db.exec('PRAGMA busy_timeout = 30000');\n registerFunctions(db);\n\n db.exec('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)');\n\n // Schema-version or feature-set mismatch: reconcile only reparses changed files, so an\n // old cache can't be patched incrementally -- rebuild instead (cheap: nothing expensive lives here).\n const version = getMeta(db, 'schema_version');\n const features = getMeta(db, 'features');\n const wantFeatures = featureSignature(cfg);\n if ((version !== null && version !== SCHEMA_VERSION) || (features !== null && features !== wantFeatures)) {\n db.close();\n rmSync(stateDir, { recursive: true, force: true });\n return open(cfg);\n }\n\n ensureSchema(db, cfg);\n const { parsed, warnings } = reconcile(db, cfg, cfg.baseDir);\n\n return { db, cfg, dbPath, parsed, warnings };\n}\n\n// Manual reset for a doubted cache.\nexport function rebuild(cfg: ResolvedConfig): OpenResult {\n rmSync(join(cfg.baseDir, STATE_DIR), { recursive: true, force: true });\n return open(cfg);\n}\n"],"names":["DB_FILENAME","SCHEMA_VERSION","docCount","getMeta","open","rebuild","reconcile","setMeta","quoteIdent","name","split","join","registerFunctions","db","function","deterministic","varargs","field","value","undefined","needle","String","startsWith","parsed","JSON","parse","Array","isArray","some","item","includes","getColumns","rows","prepare","all","Set","map","r","ensureSchema","cfg","exec","activeFeatures","feature","schema","featureSignature","key","row","get","run","n","baseDir","files","listFiles","currentSet","f","relPath","existingRows","existing","Map","path","vanished","filter","has","toReparse","_mtime","mtimeMs","_size","size","length","warnings","features","seenColumns","newColumns","parsedDocs","file","parseFile","doc","fileWarnings","push","Object","keys","data","add","allColumns","insertSql","col","delBody","del","remove","insert","insertBody","values","search","title","summary","text","store","extracted","afterReconcile","err","stateDir","STATE_DIR","mkdirSync","recursive","dbPath","DatabaseSync","version","wantFeatures","close","rmSync","force"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS';
1
+ export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'SEARCH_SYNTAX';
2
2
  export declare class SenseError extends Error {
3
3
  code: SenseErrorCode;
4
4
  constructor(code: SenseErrorCode, message: string);
@@ -1,4 +1,4 @@
1
- export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS';
1
+ export type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'SEARCH_SYNTAX';
2
2
  export declare class SenseError extends Error {
3
3
  code: SenseErrorCode;
4
4
  constructor(code: SenseErrorCode, message: string);
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/errors.ts"],"sourcesContent":["// Library code throws; only cli.ts prints and exits.\nexport type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS';\n\nexport class SenseError extends Error {\n code: SenseErrorCode;\n\n constructor(code: SenseErrorCode, message: string) {\n super(message);\n this.name = 'SenseError';\n this.code = code;\n }\n}\n"],"names":["SenseError","code","message","name","Error"],"mappings":"AAAA,qDAAqD;;;;;+BAGxCA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAN,IAAA,AAAMA,2BAAN;;cAAMA;aAAAA,WAGCC,IAAoB,EAAEC,OAAe;gCAHtCF;;gBAIT,kBAJSA;YAIHE;;QACN,MAAKC,IAAI,GAAG;QACZ,MAAKF,IAAI,GAAGA;;;WANHD;qBAAmBI"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/errors.ts"],"sourcesContent":["// Library code throws; only cli.ts prints and exits.\nexport type SenseErrorCode = 'CONFIG_NOT_FOUND' | 'CONFIG_EXISTS' | 'CONFIG_INVALID' | 'CONFIG_VERSION_UNSUPPORTED' | 'WATCH_ACTIVE' | 'NOTE_NOT_FOUND' | 'NOTE_AMBIGUOUS' | 'EMBED_DISABLED' | 'EMBED_MODEL' | 'SEARCH_SYNTAX';\n\nexport class SenseError extends Error {\n code: SenseErrorCode;\n\n constructor(code: SenseErrorCode, message: string) {\n super(message);\n this.name = 'SenseError';\n this.code = code;\n }\n}\n"],"names":["SenseError","code","message","name","Error"],"mappings":"AAAA,qDAAqD;;;;;+BAGxCA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAN,IAAA,AAAMA,2BAAN;;cAAMA;aAAAA,WAGCC,IAAoB,EAAEC,OAAe;gCAHtCF;;gBAIT,kBAJSA;YAIHE;;QACN,MAAKC,IAAI,GAAG;QACZ,MAAKF,IAAI,GAAGA;;;WANHD;qBAAmBI"}