sensemaking 0.6.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 (64) hide show
  1. package/dist/cjs/cli.js +3 -0
  2. package/dist/cjs/cli.js.map +1 -1
  3. package/dist/cjs/commands/check.d.cts +3 -0
  4. package/dist/cjs/commands/check.d.ts +3 -0
  5. package/dist/cjs/commands/check.js +130 -0
  6. package/dist/cjs/commands/check.js.map +1 -0
  7. package/dist/cjs/commands/index.js +5 -0
  8. package/dist/cjs/commands/index.js.map +1 -1
  9. package/dist/cjs/commands/status.js +67 -9
  10. package/dist/cjs/commands/status.js.map +1 -1
  11. package/dist/cjs/config.d.cts +6 -0
  12. package/dist/cjs/config.d.ts +6 -0
  13. package/dist/cjs/config.js +61 -1
  14. package/dist/cjs/config.js.map +1 -1
  15. package/dist/cjs/db.d.cts +1 -1
  16. package/dist/cjs/db.d.ts +1 -1
  17. package/dist/cjs/db.js +1 -1
  18. package/dist/cjs/db.js.map +1 -1
  19. package/dist/cjs/errors.d.cts +1 -1
  20. package/dist/cjs/errors.d.ts +1 -1
  21. package/dist/cjs/errors.js.map +1 -1
  22. package/dist/cjs/features/embed.d.cts +1 -0
  23. package/dist/cjs/features/embed.d.ts +1 -0
  24. package/dist/cjs/features/embed.js +6 -2
  25. package/dist/cjs/features/embed.js.map +1 -1
  26. package/dist/cjs/output.js +34 -8
  27. package/dist/cjs/output.js.map +1 -1
  28. package/dist/cjs/scan.d.cts +1 -1
  29. package/dist/cjs/scan.d.ts +1 -1
  30. package/dist/cjs/scan.js +8 -2
  31. package/dist/cjs/scan.js.map +1 -1
  32. package/dist/cjs/verbs.js +56 -12
  33. package/dist/cjs/verbs.js.map +1 -1
  34. package/dist/esm/cli.js +3 -0
  35. package/dist/esm/cli.js.map +1 -1
  36. package/dist/esm/commands/check.d.ts +3 -0
  37. package/dist/esm/commands/check.js +56 -0
  38. package/dist/esm/commands/check.js.map +1 -0
  39. package/dist/esm/commands/index.js +1 -0
  40. package/dist/esm/commands/index.js.map +1 -1
  41. package/dist/esm/commands/status.js +38 -7
  42. package/dist/esm/commands/status.js.map +1 -1
  43. package/dist/esm/config.d.ts +6 -0
  44. package/dist/esm/config.js +35 -1
  45. package/dist/esm/config.js.map +1 -1
  46. package/dist/esm/db.d.ts +1 -1
  47. package/dist/esm/db.js +1 -1
  48. package/dist/esm/db.js.map +1 -1
  49. package/dist/esm/errors.d.ts +1 -1
  50. package/dist/esm/errors.js.map +1 -1
  51. package/dist/esm/features/embed.d.ts +1 -0
  52. package/dist/esm/features/embed.js +11 -2
  53. package/dist/esm/features/embed.js.map +1 -1
  54. package/dist/esm/output.js +29 -7
  55. package/dist/esm/output.js.map +1 -1
  56. package/dist/esm/scan.d.ts +1 -1
  57. package/dist/esm/scan.js +8 -2
  58. package/dist/esm/scan.js.map +1 -1
  59. package/dist/esm/verbs.js +52 -12
  60. package/dist/esm/verbs.js.map +1 -1
  61. package/package.json +1 -1
  62. package/schema.json +19 -1
  63. package/skills/sense/SKILL.md +23 -2
  64. package/skills/sense-setup/SKILL.md +22 -4
package/dist/cjs/verbs.js CHANGED
@@ -220,9 +220,39 @@ function _ts_generator(thisArg, body) {
220
220
  // Each returns data; cli.ts renders. All of them degrade when a feature is off.
221
221
  var WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0)';
222
222
  var RRF_K = 60;
223
+ // FTS5 reads these as operators, so a bare term containing one is a syntax error or --
224
+ // worse -- a column filter: `end-to-end` parses as a filter on column `to`, and SQLite
225
+ // reports `no such column: to`, which is a true statement about the parse and a false
226
+ // one about the input. Both field reports on 0.6.0 misdiagnosed that message.
227
+ var FTS5_OPERATORS = /[-'"/.:^*()]/;
228
+ function searchError(err, terms, scope) {
229
+ var _terms_match;
230
+ var _exec;
231
+ var message = err.message;
232
+ if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;
233
+ var suspects = ((_terms_match = terms.match(/\S+/g)) !== null && _terms_match !== void 0 ? _terms_match : []).filter(function(t) {
234
+ return !t.startsWith('"') && FTS5_OPERATORS.test(t);
235
+ });
236
+ // Blame the terms only when the failing token actually came from one -- a typo'd column
237
+ // in --where (or the tree's default scope) raises "no such column" through this same
238
+ // statement, and naming a term for it would state a false fact about the input.
239
+ var col = (_exec = /no such column: (\S+)/.exec(message)) === null || _exec === void 0 ? void 0 : _exec[1];
240
+ var fromTerms = col === undefined ? suspects.length > 0 : suspects.some(function(t) {
241
+ return t.split(RegExp("[^\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "u")).includes(col);
242
+ });
243
+ if (fromTerms && suspects.length > 0) {
244
+ return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- the punctuation in ").concat(suspects.map(function(t) {
245
+ return "`".concat(t, "`");
246
+ }).join(', '), " is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"").concat(suspects[0], "\"'. Searchable columns are title, summary, text."));
247
+ }
248
+ if (col !== undefined && scope !== undefined) {
249
+ return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- the where condition (").concat(scope, ") references it; frontmatter columns are listed by sense query \"SELECT name FROM pragma_table_info('frontmatter')\"."));
250
+ }
251
+ return new _errorsts.SenseError('SEARCH_SYNTAX', "".concat(message, " -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense query (list them with pragma_table_info('frontmatter'))."));
252
+ }
223
253
  function find(_0, _1, _2) {
224
254
  return _async_to_generator(function(db, cfg, terms) {
225
- var opts, _opts_k, _hits_get, _chunkLines_get, k, fetch, whereJoin, whereCond, matchSql, matchRows, hits, candidates, edges, linked, nodes, seeds, ranked, chunkLines, vec, insert, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, path, c, where, linesCol;
255
+ var opts, _opts_k, _opts_where, _hits_get, _chunkLines_get, _chunkSimilarity_get, _cfg_defaults_find, _cfg_defaults, k, fetch, scope, whereJoin, whereCond, matchSql, matchRows, hits, candidates, edges, linked, nodes, seeds, ranked, chunkLines, chunkSimilarity, vec, insert, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, path, c, where, linesCol;
226
256
  var _arguments = arguments;
227
257
  return _ts_generator(this, function(_state) {
228
258
  switch(_state.label){
@@ -234,10 +264,17 @@ function find(_0, _1, _2) {
234
264
  // Invalid syntax propagates as an error, zero matches return zero -- no silent rewrites.
235
265
  // --where applies inside the candidate query (a post-filter over the top-N would drop
236
266
  // matches ranked past the pool) and again on the final select for link-derived rows.
237
- whereJoin = opts.where ? 'JOIN frontmatter f ON f."path" = content.path' : '';
238
- whereCond = opts.where ? "AND (".concat(opts.where, ")") : '';
267
+ // An explicit --where replaces the tree's declared default rather than ANDing with it,
268
+ // so a caller can always widen back to the whole tree.
269
+ scope = (_opts_where = opts.where) !== null && _opts_where !== void 0 ? _opts_where : (_cfg_defaults = cfg.defaults) === null || _cfg_defaults === void 0 ? void 0 : (_cfg_defaults_find = _cfg_defaults.find) === null || _cfg_defaults_find === void 0 ? void 0 : _cfg_defaults_find.where;
270
+ whereJoin = scope ? 'JOIN frontmatter f ON f."path" = content.path' : '';
271
+ whereCond = scope ? "AND (".concat(scope, ")") : '';
239
272
  matchSql = "SELECT content.path AS path, snippet(content, -1, '\xab', '\xbb', '…', 10) AS hit FROM content ".concat(whereJoin, " WHERE content MATCH ? ").concat(whereCond, " ORDER BY ").concat(WEIGHTED_BM25, " LIMIT ").concat(fetch);
240
- matchRows = db.prepare(matchSql).all(terms);
273
+ try {
274
+ matchRows = db.prepare(matchSql).all(terms);
275
+ } catch (err) {
276
+ throw searchError(err, terms, scope);
277
+ }
241
278
  hits = new Map(matchRows.map(function(r) {
242
279
  return [
243
280
  r.path,
@@ -290,6 +327,7 @@ function find(_0, _1, _2) {
290
327
  // Vector expansion, invoked only: a third RRF list at the swept flat-region constants
291
328
  // (weight 1, pool = fetch). Each row carries its best chunk's line range.
292
329
  chunkLines = new Map();
330
+ chunkSimilarity = new Map();
293
331
  if (!opts.semantic) return [
294
332
  3,
295
333
  2
@@ -301,8 +339,9 @@ function find(_0, _1, _2) {
301
339
  case 1:
302
340
  vec = _state.sent();
303
341
  vec.forEach(function(param, i) {
304
- var path = param.path, lines = param.lines;
342
+ var path = param.path, lines = param.lines, similarity = param.similarity;
305
343
  chunkLines.set(path, lines);
344
+ chunkSimilarity.set(path, similarity);
306
345
  var existing = candidates.get(path);
307
346
  if (existing) {
308
347
  existing.score += 1 / (RRF_K + i);
@@ -317,13 +356,13 @@ function find(_0, _1, _2) {
317
356
  _state.label = 2;
318
357
  case 2:
319
358
  db.exec('DROP TABLE IF EXISTS _find');
320
- db.exec('CREATE TEMP TABLE _find ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT)');
321
- insert = db.prepare('INSERT INTO _find ("path", score, via, hit, lines) VALUES (?, ?, ?, ?, ?)');
359
+ db.exec('CREATE TEMP TABLE _find ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');
360
+ insert = db.prepare('INSERT INTO _find ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)');
322
361
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
323
362
  try {
324
363
  for(_iterator = candidates[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
325
364
  _step_value = _sliced_to_array(_step.value, 2), path = _step_value[0], c = _step_value[1];
326
- insert.run(path, c.score, c.via, (_hits_get = hits.get(path)) !== null && _hits_get !== void 0 ? _hits_get : null, (_chunkLines_get = chunkLines.get(path)) !== null && _chunkLines_get !== void 0 ? _chunkLines_get : null);
365
+ insert.run(path, c.score, c.via, (_hits_get = hits.get(path)) !== null && _hits_get !== void 0 ? _hits_get : null, (_chunkLines_get = chunkLines.get(path)) !== null && _chunkLines_get !== void 0 ? _chunkLines_get : null, (_chunkSimilarity_get = chunkSimilarity.get(path)) !== null && _chunkSimilarity_get !== void 0 ? _chunkSimilarity_get : null);
327
366
  }
328
367
  } catch (err) {
329
368
  _didIteratorError = true;
@@ -339,8 +378,8 @@ function find(_0, _1, _2) {
339
378
  }
340
379
  }
341
380
  }
342
- where = opts.where ? "WHERE ".concat(opts.where) : '';
343
- linesCol = opts.semantic ? ', _find.lines' : '';
381
+ where = scope ? "WHERE ".concat(scope) : '';
382
+ linesCol = opts.semantic ? ', _find.lines, _find.similarity' : '';
344
383
  return [
345
384
  2,
346
385
  db.prepare('SELECT f."path" AS path, content.title, content.summary, _find.hit, _find.via, round(_find.score, 4) AS score'.concat(linesCol, '\n FROM _find JOIN frontmatter f ON f."path" = _find."path" JOIN content ON content.path = _find."path"\n ').concat(where, " ORDER BY _find.score DESC LIMIT ?")).all(k)
@@ -363,10 +402,15 @@ function mapTree(db, cfg) {
363
402
  return !INTERNAL_COLUMNS.has(name);
364
403
  });
365
404
  var allFields = columns.map(function(name) {
366
- var n = db.prepare('SELECT COUNT("'.concat(name.split('"').join('""'), '") AS n FROM frontmatter')).get().n;
405
+ var quoted = '"'.concat(name.split('"').join('""'), '"');
406
+ // Observed storage class, not a declared one: these columns are added dynamically and
407
+ // SQLite types per value, so a field can be text in most notes and numeric in a few.
408
+ // Listing every distinct type makes both the type and any drift visible.
409
+ var _db_prepare_get = db.prepare("SELECT COUNT(".concat(quoted, ") AS n, GROUP_CONCAT(DISTINCT typeof(").concat(quoted, ")) AS types FROM frontmatter WHERE ").concat(quoted, " IS NOT NULL")).get(), n = _db_prepare_get.n, types = _db_prepare_get.types;
367
410
  return {
368
411
  field: name,
369
- coverage: n
412
+ coverage: n,
413
+ type: types !== null && types !== void 0 ? types : ''
370
414
  };
371
415
  }).sort(function(a, b) {
372
416
  return b.coverage - a.coverage;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/verbs.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { Config, FeatureName } from './config.ts';\nimport { featureEnabled, featureStates } from './config.ts';\nimport { SenseError } from './errors.ts';\nimport { semanticCandidates } from './features/embed.ts';\nimport { linkEdges } from './features/index.ts';\nimport { personalizedRank } from './graph.ts';\nimport type { Row } from './output.ts';\n\n// The three layer verbs: mapTree (orient), find (locate), peek (structure).\n// Each returns data; cli.ts renders. All of them degrade when a feature is off.\n\nconst WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0)';\nconst RRF_K = 60;\n\nexport interface FindOptions {\n k?: number;\n where?: string; // SQL fragment against frontmatter alias `f`, e.g. \"f.status = 'active'\"\n semantic?: boolean; // invoke vector expansion (requires features.embed); rows gain via 'vector' and a lines column\n}\n\n// Layer 1: BM25 + link-graph expansion, fused by reciprocal rank. `via` says which\n// signal produced each row so the agent knows what evidence it is trusting.\n// Semantic expansion is per-query opt-in: without opts.semantic the result is\n// byte-for-byte independent of the embed feature.\nexport async function find(db: DatabaseSync, cfg: Config, terms: string, opts: FindOptions = {}): Promise<Row[]> {\n const k = opts.k ?? 10;\n const fetch = Math.max(k * 3, 30);\n\n // Terms pass verbatim to FTS5 MATCH: bare words AND-join, operators are the caller's.\n // Invalid syntax propagates as an error, zero matches return zero -- no silent rewrites.\n // --where applies inside the candidate query (a post-filter over the top-N would drop\n // matches ranked past the pool) and again on the final select for link-derived rows.\n const whereJoin = opts.where ? `JOIN frontmatter f ON f.\"path\" = content.path` : '';\n const whereCond = opts.where ? `AND (${opts.where})` : '';\n const matchSql = `SELECT content.path AS path, snippet(content, -1, '«', '»', '…', 10) AS hit FROM content ${whereJoin} WHERE content MATCH ? ${whereCond} ORDER BY ${WEIGHTED_BM25} LIMIT ${fetch}`;\n const matchRows = db.prepare(matchSql).all(terms) as Array<{ path: string; hit: string }>;\n\n const hits = new Map(matchRows.map((r) => [r.path, r.hit]));\n const candidates = new Map<string, { score: number; via: string }>();\n matchRows.forEach((r, i) => {\n candidates.set(r.path, { score: 1 / (RRF_K + i), via: 'match' });\n });\n\n const edges = featureEnabled(cfg, 'links') && matchRows.length > 0 ? linkEdges(db) : [];\n if (edges.length > 0) {\n // `linked` gates the label only, not the score: PPR restart mass gives every seed a\n // nonzero rank even without an incident edge, which is not link evidence — but dropping\n // that mass from the score list reweights fusion toward connectivity and measurably\n // wrecks ranking on link-dense corpora (FEVER hit@10 0.997 -> 0.907; fusion-tuning.md).\n const linked = new Set(edges.flat());\n const nodes = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const seeds = new Map(matchRows.map((r, i) => [r.path, 1 / (i + 1)]));\n const ranked = [...personalizedRank(nodes, edges, seeds)]\n .filter(([, score]) => score > 1e-9)\n .sort((a, b) => b[1] - a[1])\n .slice(0, fetch);\n ranked.forEach(([path], i) => {\n const existing = candidates.get(path);\n if (existing) {\n existing.score += 1 / (RRF_K + i);\n if (linked.has(path)) existing.via = 'match+link';\n } else {\n candidates.set(path, { score: 1 / (RRF_K + i), via: 'link' });\n }\n });\n }\n\n // Vector expansion, invoked only: a third RRF list at the swept flat-region constants\n // (weight 1, pool = fetch). Each row carries its best chunk's line range.\n const chunkLines = new Map<string, string>();\n if (opts.semantic) {\n const vec = await semanticCandidates(db, cfg, terms, fetch);\n vec.forEach(({ path, lines }, i) => {\n chunkLines.set(path, lines);\n const existing = candidates.get(path);\n if (existing) {\n existing.score += 1 / (RRF_K + i);\n existing.via = `${existing.via}+vector`;\n } else {\n candidates.set(path, { score: 1 / (RRF_K + i), via: 'vector' });\n }\n });\n }\n\n db.exec('DROP TABLE IF EXISTS _find');\n db.exec('CREATE TEMP TABLE _find (\"path\" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT)');\n const insert = db.prepare('INSERT INTO _find (\"path\", score, via, hit, lines) VALUES (?, ?, ?, ?, ?)');\n for (const [path, c] of candidates) insert.run(path, c.score, c.via, hits.get(path) ?? null, chunkLines.get(path) ?? null);\n\n const where = opts.where ? `WHERE ${opts.where}` : '';\n const linesCol = opts.semantic ? ', _find.lines' : '';\n return db\n .prepare(\n `SELECT f.\"path\" AS path, content.title, content.summary, _find.hit, _find.via, round(_find.score, 4) AS score${linesCol}\n FROM _find JOIN frontmatter f ON f.\"path\" = _find.\"path\" JOIN content ON content.path = _find.\"path\"\n ${where} ORDER BY _find.score DESC LIMIT ?`\n )\n .all(k) as Row[];\n}\n\nexport interface TreeMap {\n docs: { count: number; bytes: number };\n fields: Row[]; // top 20 by coverage; fieldsTotal carries the real count\n fieldsTotal: number;\n features: { on: FeatureName[]; off: FeatureName[] };\n hubs: Row[];\n recent: Row[];\n}\n\nconst INTERNAL_COLUMNS = new Set(['path', '_mtime', '_size', '_rank']);\n\n// Layer 0: what is this tree. Fixed-size output regardless of tree size.\nexport function mapTree(db: DatabaseSync, cfg: Config): TreeMap {\n const docs = db.prepare('SELECT COUNT(*) AS count, COALESCE(SUM(\"_size\"), 0) AS bytes FROM frontmatter').get() as { count: number; bytes: number };\n\n const columns = (db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>).map((r) => r.name).filter((name) => !INTERNAL_COLUMNS.has(name));\n const allFields = columns\n .map((name) => {\n const { n } = db.prepare(`SELECT COUNT(\"${name.split('\"').join('\"\"')}\") AS n FROM frontmatter`).get() as { n: number };\n return { field: name, coverage: n };\n })\n .sort((a, b) => (b.coverage as number) - (a.coverage as number)) as Row[];\n const fields = allFields.slice(0, 20);\n\n const hubs = featureEnabled(cfg, 'rank') ? (db.prepare(`SELECT f.\"path\" AS path, round(f.\"_rank\" * 100, 2) AS rank, content.title FROM frontmatter f JOIN content ON content.path = f.\"path\" WHERE f.\"_rank\" IS NOT NULL ORDER BY f.\"_rank\" DESC LIMIT 8`).all() as Row[]) : [];\n\n const recent = db.prepare(`SELECT \"path\", datetime(\"_mtime\" / 1000, 'unixepoch') AS modified FROM frontmatter ORDER BY \"_mtime\" DESC LIMIT 5`).all() as Row[];\n\n return { docs, fields, fieldsTotal: allFields.length, features: featureStates(cfg), hubs, recent };\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks, and peek's whole\n // point is bounded output. Query the links table directly for the full list.\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LINK_LIMIT = 20;\n\n// Layer 2: everything about one note except its prose -- frontmatter, outline with line\n// ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.\nexport function peek(db: DatabaseSync, cfg: Config, pathArg: string): Peek {\n const paths = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n let path = paths.find((p) => p === pathArg);\n if (!path) {\n const base = posix.basename(pathArg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) path = matches[0];\n else if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${pathArg}\" is ambiguous: ${matches.join(', ')}`);\n else throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${pathArg}\"`);\n }\n\n const row = db.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?').get(path) as Row;\n const frontmatter: Row = {};\n for (const [key, value] of Object.entries(row)) {\n if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;\n }\n\n const sections = featureEnabled(cfg, 'sections') ? (db.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx').all(path) as Row[]) : [];\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n if (featureEnabled(cfg, 'links')) {\n const out = db.prepare('SELECT target, dst FROM links WHERE src = ? ORDER BY target').all(path) as Array<{ target: string; dst: string | null }>;\n outbound = [...new Set(out.filter((l) => l.dst !== null).map((l) => l.dst as string))];\n unresolved = out.filter((l) => l.dst === null).map((l) => l.target);\n backlinksTotal = (db.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ?').get(path) as { n: number }).n;\n backlinks = (db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? ORDER BY src LIMIT ?').all(path, PEEK_LINK_LIMIT) as Array<{ src: string }>).map((r) => r.src);\n }\n\n return {\n path,\n tokens: Math.ceil(((row._size as number) ?? 0) / 4),\n frontmatter,\n sections,\n outbound: outbound.slice(0, PEEK_LINK_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LINK_LIMIT),\n outboundTotal: outbound.length,\n backlinksTotal,\n unresolvedTotal: unresolved.length,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n}\n"],"names":["find","mapTree","peek","WEIGHTED_BM25","RRF_K","db","cfg","terms","opts","hits","chunkLines","k","fetch","whereJoin","whereCond","matchSql","matchRows","candidates","edges","linked","nodes","seeds","ranked","vec","insert","path","c","where","linesCol","Math","max","prepare","all","Map","map","r","hit","forEach","i","set","score","via","featureEnabled","length","linkEdges","Set","flat","personalizedRank","filter","sort","a","b","slice","existing","get","has","semantic","semanticCandidates","lines","exec","run","INTERNAL_COLUMNS","docs","columns","name","allFields","n","split","join","field","coverage","fields","hubs","recent","fieldsTotal","features","featureStates","PEEK_LINK_LIMIT","pathArg","row","paths","p","base","posix","basename","replace","toLowerCase","matches","SenseError","frontmatter","Object","entries","key","value","sections","outbound","backlinks","unresolved","backlinksTotal","out","l","dst","target","src","tokens","ceil","_size","outboundTotal","unresolvedTotal","off"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/verbs.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { Config, FeatureName } from './config.ts';\nimport { featureEnabled, featureStates } from './config.ts';\nimport { SenseError } from './errors.ts';\nimport { semanticCandidates } from './features/embed.ts';\nimport { linkEdges } from './features/index.ts';\nimport { personalizedRank } from './graph.ts';\nimport type { Row } from './output.ts';\n\n// The three layer verbs: mapTree (orient), find (locate), peek (structure).\n// Each returns data; cli.ts renders. All of them degrade when a feature is off.\n\nconst WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0)';\nconst RRF_K = 60;\n\n// FTS5 reads these as operators, so a bare term containing one is a syntax error or --\n// worse -- a column filter: `end-to-end` parses as a filter on column `to`, and SQLite\n// reports `no such column: to`, which is a true statement about the parse and a false\n// one about the input. Both field reports on 0.6.0 misdiagnosed that message.\nconst FTS5_OPERATORS = /[-'\"/.:^*()]/;\n\nfunction searchError(err: Error, terms: string, scope?: string): Error {\n const message = err.message;\n if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;\n const suspects = (terms.match(/\\S+/g) ?? []).filter((t) => !t.startsWith('\"') && FTS5_OPERATORS.test(t));\n // Blame the terms only when the failing token actually came from one -- a typo'd column\n // in --where (or the tree's default scope) raises \"no such column\" through this same\n // statement, and naming a term for it would state a false fact about the input.\n const col = /no such column: (\\S+)/.exec(message)?.[1];\n const fromTerms = col === undefined ? suspects.length > 0 : suspects.some((t) => t.split(/[^\\p{L}\\p{N}]+/u).includes(col));\n if (fromTerms && suspects.length > 0) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the punctuation in ${suspects.map((t) => `\\`${t}\\``).join(', ')} is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"${suspects[0]}\"'. Searchable columns are title, summary, text.`);\n }\n if (col !== undefined && scope !== undefined) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the where condition (${scope}) references it; frontmatter columns are listed by sense query \"SELECT name FROM pragma_table_info('frontmatter')\".`);\n }\n return new SenseError('SEARCH_SYNTAX', `${message} -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense query (list them with pragma_table_info('frontmatter')).`);\n}\n\nexport interface FindOptions {\n k?: number;\n where?: string; // SQL fragment against frontmatter alias `f`, e.g. \"f.status = 'active'\"\n semantic?: boolean; // invoke vector expansion (requires features.embed); rows gain via 'vector' and a lines column\n}\n\n// Layer 1: BM25 + link-graph expansion, fused by reciprocal rank. `via` says which\n// signal produced each row so the agent knows what evidence it is trusting.\n// Semantic expansion is per-query opt-in: without opts.semantic the result is\n// byte-for-byte independent of the embed feature.\nexport async function find(db: DatabaseSync, cfg: Config, terms: string, opts: FindOptions = {}): Promise<Row[]> {\n const k = opts.k ?? 10;\n const fetch = Math.max(k * 3, 30);\n\n // Terms pass verbatim to FTS5 MATCH: bare words AND-join, operators are the caller's.\n // Invalid syntax propagates as an error, zero matches return zero -- no silent rewrites.\n // --where applies inside the candidate query (a post-filter over the top-N would drop\n // matches ranked past the pool) and again on the final select for link-derived rows.\n // An explicit --where replaces the tree's declared default rather than ANDing with it,\n // so a caller can always widen back to the whole tree.\n const scope = opts.where ?? cfg.defaults?.find?.where;\n const whereJoin = scope ? `JOIN frontmatter f ON f.\"path\" = content.path` : '';\n const whereCond = scope ? `AND (${scope})` : '';\n const matchSql = `SELECT content.path AS path, snippet(content, -1, '«', '»', '…', 10) AS hit FROM content ${whereJoin} WHERE content MATCH ? ${whereCond} ORDER BY ${WEIGHTED_BM25} LIMIT ${fetch}`;\n let matchRows: Array<{ path: string; hit: string }>;\n try {\n matchRows = db.prepare(matchSql).all(terms) as Array<{ path: string; hit: string }>;\n } catch (err) {\n throw searchError(err as Error, terms, scope);\n }\n\n const hits = new Map(matchRows.map((r) => [r.path, r.hit]));\n const candidates = new Map<string, { score: number; via: string }>();\n matchRows.forEach((r, i) => {\n candidates.set(r.path, { score: 1 / (RRF_K + i), via: 'match' });\n });\n\n const edges = featureEnabled(cfg, 'links') && matchRows.length > 0 ? linkEdges(db) : [];\n if (edges.length > 0) {\n // `linked` gates the label only, not the score: PPR restart mass gives every seed a\n // nonzero rank even without an incident edge, which is not link evidence — but dropping\n // that mass from the score list reweights fusion toward connectivity and measurably\n // wrecks ranking on link-dense corpora (FEVER hit@10 0.997 -> 0.907; fusion-tuning.md).\n const linked = new Set(edges.flat());\n const nodes = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const seeds = new Map(matchRows.map((r, i) => [r.path, 1 / (i + 1)]));\n const ranked = [...personalizedRank(nodes, edges, seeds)]\n .filter(([, score]) => score > 1e-9)\n .sort((a, b) => b[1] - a[1])\n .slice(0, fetch);\n ranked.forEach(([path], i) => {\n const existing = candidates.get(path);\n if (existing) {\n existing.score += 1 / (RRF_K + i);\n if (linked.has(path)) existing.via = 'match+link';\n } else {\n candidates.set(path, { score: 1 / (RRF_K + i), via: 'link' });\n }\n });\n }\n\n // Vector expansion, invoked only: a third RRF list at the swept flat-region constants\n // (weight 1, pool = fetch). Each row carries its best chunk's line range.\n const chunkLines = new Map<string, string>();\n const chunkSimilarity = new Map<string, number>();\n if (opts.semantic) {\n const vec = await semanticCandidates(db, cfg, terms, fetch);\n vec.forEach(({ path, lines, similarity }, i) => {\n chunkLines.set(path, lines);\n chunkSimilarity.set(path, similarity);\n const existing = candidates.get(path);\n if (existing) {\n existing.score += 1 / (RRF_K + i);\n existing.via = `${existing.via}+vector`;\n } else {\n candidates.set(path, { score: 1 / (RRF_K + i), via: 'vector' });\n }\n });\n }\n\n db.exec('DROP TABLE IF EXISTS _find');\n db.exec('CREATE TEMP TABLE _find (\"path\" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');\n const insert = db.prepare('INSERT INTO _find (\"path\", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)');\n for (const [path, c] of candidates) insert.run(path, c.score, c.via, hits.get(path) ?? null, chunkLines.get(path) ?? null, chunkSimilarity.get(path) ?? null);\n\n const where = scope ? `WHERE ${scope}` : '';\n const linesCol = opts.semantic ? ', _find.lines, _find.similarity' : '';\n return db\n .prepare(\n `SELECT f.\"path\" AS path, content.title, content.summary, _find.hit, _find.via, round(_find.score, 4) AS score${linesCol}\n FROM _find JOIN frontmatter f ON f.\"path\" = _find.\"path\" JOIN content ON content.path = _find.\"path\"\n ${where} ORDER BY _find.score DESC LIMIT ?`\n )\n .all(k) as Row[];\n}\n\nexport interface TreeMap {\n docs: { count: number; bytes: number };\n fields: Row[]; // top 20 by coverage; fieldsTotal carries the real count\n fieldsTotal: number;\n features: { on: FeatureName[]; off: FeatureName[] };\n hubs: Row[];\n recent: Row[];\n}\n\nconst INTERNAL_COLUMNS = new Set(['path', '_mtime', '_size', '_rank']);\n\n// Layer 0: what is this tree. Fixed-size output regardless of tree size.\nexport function mapTree(db: DatabaseSync, cfg: Config): TreeMap {\n const docs = db.prepare('SELECT COUNT(*) AS count, COALESCE(SUM(\"_size\"), 0) AS bytes FROM frontmatter').get() as { count: number; bytes: number };\n\n const columns = (db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>).map((r) => r.name).filter((name) => !INTERNAL_COLUMNS.has(name));\n const allFields = columns\n .map((name) => {\n const quoted = `\"${name.split('\"').join('\"\"')}\"`;\n // Observed storage class, not a declared one: these columns are added dynamically and\n // SQLite types per value, so a field can be text in most notes and numeric in a few.\n // Listing every distinct type makes both the type and any drift visible.\n const { n, types } = db.prepare(`SELECT COUNT(${quoted}) AS n, GROUP_CONCAT(DISTINCT typeof(${quoted})) AS types FROM frontmatter WHERE ${quoted} IS NOT NULL`).get() as { n: number; types: string | null };\n return { field: name, coverage: n, type: types ?? '' };\n })\n .sort((a, b) => (b.coverage as number) - (a.coverage as number)) as Row[];\n const fields = allFields.slice(0, 20);\n\n const hubs = featureEnabled(cfg, 'rank') ? (db.prepare(`SELECT f.\"path\" AS path, round(f.\"_rank\" * 100, 2) AS rank, content.title FROM frontmatter f JOIN content ON content.path = f.\"path\" WHERE f.\"_rank\" IS NOT NULL ORDER BY f.\"_rank\" DESC LIMIT 8`).all() as Row[]) : [];\n\n const recent = db.prepare(`SELECT \"path\", datetime(\"_mtime\" / 1000, 'unixepoch') AS modified FROM frontmatter ORDER BY \"_mtime\" DESC LIMIT 5`).all() as Row[];\n\n return { docs, fields, fieldsTotal: allFields.length, features: featureStates(cfg), hubs, recent };\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks, and peek's whole\n // point is bounded output. Query the links table directly for the full list.\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LINK_LIMIT = 20;\n\n// Layer 2: everything about one note except its prose -- frontmatter, outline with line\n// ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.\nexport function peek(db: DatabaseSync, cfg: Config, pathArg: string): Peek {\n const paths = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n let path = paths.find((p) => p === pathArg);\n if (!path) {\n const base = posix.basename(pathArg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) path = matches[0];\n else if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${pathArg}\" is ambiguous: ${matches.join(', ')}`);\n else throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${pathArg}\"`);\n }\n\n const row = db.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?').get(path) as Row;\n const frontmatter: Row = {};\n for (const [key, value] of Object.entries(row)) {\n if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;\n }\n\n const sections = featureEnabled(cfg, 'sections') ? (db.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx').all(path) as Row[]) : [];\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n if (featureEnabled(cfg, 'links')) {\n const out = db.prepare('SELECT target, dst FROM links WHERE src = ? ORDER BY target').all(path) as Array<{ target: string; dst: string | null }>;\n outbound = [...new Set(out.filter((l) => l.dst !== null).map((l) => l.dst as string))];\n unresolved = out.filter((l) => l.dst === null).map((l) => l.target);\n backlinksTotal = (db.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ?').get(path) as { n: number }).n;\n backlinks = (db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? ORDER BY src LIMIT ?').all(path, PEEK_LINK_LIMIT) as Array<{ src: string }>).map((r) => r.src);\n }\n\n return {\n path,\n tokens: Math.ceil(((row._size as number) ?? 0) / 4),\n frontmatter,\n sections,\n outbound: outbound.slice(0, PEEK_LINK_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LINK_LIMIT),\n outboundTotal: outbound.length,\n backlinksTotal,\n unresolvedTotal: unresolved.length,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n}\n"],"names":["find","mapTree","peek","WEIGHTED_BM25","RRF_K","FTS5_OPERATORS","searchError","err","terms","scope","message","test","suspects","match","filter","t","startsWith","col","exec","fromTerms","undefined","length","some","split","includes","SenseError","map","join","db","cfg","opts","hits","chunkLines","chunkSimilarity","k","fetch","whereJoin","whereCond","matchSql","matchRows","candidates","edges","linked","nodes","seeds","ranked","vec","insert","path","c","where","linesCol","Math","max","defaults","prepare","all","Map","r","hit","forEach","i","set","score","via","featureEnabled","linkEdges","Set","flat","personalizedRank","sort","a","b","slice","existing","get","has","semantic","semanticCandidates","lines","similarity","run","INTERNAL_COLUMNS","docs","columns","name","allFields","quoted","n","types","field","coverage","type","fields","hubs","recent","fieldsTotal","features","featureStates","PEEK_LINK_LIMIT","pathArg","row","paths","p","base","posix","basename","replace","toLowerCase","matches","frontmatter","Object","entries","key","value","sections","outbound","backlinks","unresolved","backlinksTotal","out","l","dst","target","src","tokens","ceil","_size","outboundTotal","unresolvedTotal","off"],"mappings":"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"}
package/dist/esm/cli.js CHANGED
@@ -91,6 +91,9 @@ export default async function cli(argv, name) {
91
91
  if (cfg.migratedFrom !== undefined) {
92
92
  console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);
93
93
  }
94
+ if (cfg.unknownKeys !== undefined) {
95
+ console.warn(`${name}: ${cfg.configPath} sets ${cfg.unknownKeys.join(', ')}, which this build does not read (no effect); see schema.json for the keys it does`);
96
+ }
94
97
  return cfg;
95
98
  },
96
99
  usageError (message) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { COMMANDS } from './commands/index.ts';\nimport type { Ctx } from './commands/types.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/commands/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n return (\n `usage: ${name} <name> [params...] [--format table|json] [--config path]\\n` +\n ` ${name} query \"<sql>\" [params...]\\n` +\n ` ${name} find \"<terms>\" [--where \"<sql>\"] [--k n] [--semantic]\\n` +\n ` ${name} map\\n` +\n ` ${name} peek <path>\\n` +\n ` ${name} --list\\n` +\n ` ${name} init\\n` +\n ` ${name} watch [--force]\\n` +\n ` ${name} status\\n` +\n ` ${name} rebuild\\n` +\n ` ${name} --version`\n );\n}\n\n// Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n let values: Record<string, string | boolean | undefined>;\n let positionals: string[];\n try {\n ({ values, positionals } = parseArgs({\n args: argv,\n options: {\n format: { type: 'string', default: 'table' },\n config: { type: 'string' },\n where: { type: 'string' },\n k: { type: 'string' },\n semantic: { type: 'boolean', default: false },\n list: { type: 'boolean', default: false },\n force: { type: 'boolean', default: false },\n version: { type: 'boolean', default: false, short: 'v' },\n help: { type: 'boolean', default: false, short: 'h' },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage(name));\n process.exit(2);\n }\n\n if (values.version) {\n console.log(`v${packageVersion()}`);\n return;\n }\n if (values.help) {\n console.log(usage(name));\n return;\n }\n\n const ctx: Ctx = {\n name,\n rest: positionals.slice(1),\n format: values.format === 'json' ? 'json' : 'table',\n values: values as Ctx['values'],\n resolveConfig() {\n const cfg = loadConfig(values.config as string | undefined);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\n }\n return cfg;\n },\n usageError(message) {\n console.error(message);\n process.exit(2);\n },\n };\n\n try {\n if (values.list) {\n for (const queryName of Object.keys(ctx.resolveConfig().queries).sort()) console.log(queryName);\n return;\n }\n\n const [first, ...params] = positionals;\n if (!first) {\n console.error(usage(name));\n process.exit(2);\n }\n\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else (await import('./commands/named.ts')).default(ctx, first, params);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n"],"names":["createRequire","parseArgs","COMMANDS","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","cli","argv","values","positionals","args","options","format","type","default","config","where","k","semantic","list","force","short","help","allowPositionals","err","console","error","message","process","exit","log","ctx","rest","slice","resolveConfig","cfg","migratedFrom","undefined","warn","configPath","usageError","queryName","Object","keys","queries","sort","first","params"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { parseArgs } from 'node:util';\nimport { COMMANDS } from './commands/index.ts';\nimport type { Ctx } from './commands/types.ts';\nimport { loadConfig, SUPPORTED_CONFIG_VERSION } from './config.ts';\n\n// Parsing and dispatch only. Commands live in src/commands/, one file each, lazy-loaded --\n// nothing tree- or dependency-heavy may be imported at the top of this file.\n\n// Works from dist/cjs and dist/esm alike: walk up past the dist type-marker package.json.\nfunction packageVersion(): string {\n const load = createRequire(import.meta.url);\n for (const rel of ['../package.json', '../../package.json', '../../../package.json']) {\n try {\n const pkg = load(rel) as { name?: string; version?: string };\n if (pkg.name === 'sensemaking' && pkg.version) return pkg.version;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction usage(name: string): string {\n return (\n `usage: ${name} <name> [params...] [--format table|json] [--config path]\\n` +\n ` ${name} query \"<sql>\" [params...]\\n` +\n ` ${name} find \"<terms>\" [--where \"<sql>\"] [--k n] [--semantic]\\n` +\n ` ${name} map\\n` +\n ` ${name} peek <path>\\n` +\n ` ${name} --list\\n` +\n ` ${name} init\\n` +\n ` ${name} watch [--force]\\n` +\n ` ${name} status\\n` +\n ` ${name} rebuild\\n` +\n ` ${name} --version`\n );\n}\n\n// Thrown errors -> exit 1 with the message verbatim; usage errors exit 2 directly.\nexport default async function cli(argv: string[], name: string): Promise<void> {\n let values: Record<string, string | boolean | undefined>;\n let positionals: string[];\n try {\n ({ values, positionals } = parseArgs({\n args: argv,\n options: {\n format: { type: 'string', default: 'table' },\n config: { type: 'string' },\n where: { type: 'string' },\n k: { type: 'string' },\n semantic: { type: 'boolean', default: false },\n list: { type: 'boolean', default: false },\n force: { type: 'boolean', default: false },\n version: { type: 'boolean', default: false, short: 'v' },\n help: { type: 'boolean', default: false, short: 'h' },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n console.error((err as Error).message);\n console.error(usage(name));\n process.exit(2);\n }\n\n if (values.version) {\n console.log(`v${packageVersion()}`);\n return;\n }\n if (values.help) {\n console.log(usage(name));\n return;\n }\n\n const ctx: Ctx = {\n name,\n rest: positionals.slice(1),\n format: values.format === 'json' ? 'json' : 'table',\n values: values as Ctx['values'],\n resolveConfig() {\n const cfg = loadConfig(values.config as string | undefined);\n if (cfg.migratedFrom !== undefined) {\n console.warn(`${name}: migrated ${cfg.configPath} from config version ${cfg.migratedFrom} to ${SUPPORTED_CONFIG_VERSION}`);\n }\n if (cfg.unknownKeys !== undefined) {\n console.warn(`${name}: ${cfg.configPath} sets ${cfg.unknownKeys.join(', ')}, which this build does not read (no effect); see schema.json for the keys it does`);\n }\n return cfg;\n },\n usageError(message) {\n console.error(message);\n process.exit(2);\n },\n };\n\n try {\n if (values.list) {\n for (const queryName of Object.keys(ctx.resolveConfig().queries).sort()) console.log(queryName);\n return;\n }\n\n const [first, ...params] = positionals;\n if (!first) {\n console.error(usage(name));\n process.exit(2);\n }\n\n const load = COMMANDS[first];\n if (load) await (await load()).default(ctx);\n else (await import('./commands/named.ts')).default(ctx, first, params);\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n}\n"],"names":["createRequire","parseArgs","COMMANDS","loadConfig","SUPPORTED_CONFIG_VERSION","packageVersion","load","url","rel","pkg","name","version","usage","cli","argv","values","positionals","args","options","format","type","default","config","where","k","semantic","list","force","short","help","allowPositionals","err","console","error","message","process","exit","log","ctx","rest","slice","resolveConfig","cfg","migratedFrom","undefined","warn","configPath","unknownKeys","join","usageError","queryName","Object","keys","queries","sort","first","params"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from './types.js';
2
+ declare const check: Command;
3
+ export default check;
@@ -0,0 +1,56 @@
1
+ import { open } from '../db.js';
2
+ import { printRows } from '../output.js';
3
+ import { printWarnings } from './shared.js';
4
+ // A saved query that returns zero rows looks identical to a true empty result, so a broken
5
+ // one can sit unnoticed -- one field report had `WHERE flag = 'true'` against a numeric
6
+ // column reading as "nothing tagged yet" for hours. Preparing each query catches syntax and
7
+ // unknown-column errors without executing it; queries that take no parameters are also run
8
+ // for a row count. Parameterised queries are validated but not counted: inventing arguments
9
+ // would report a count for a query nobody ran.
10
+ const check = (ctx)=>{
11
+ var _cfg_queries;
12
+ const cfg = ctx.resolveConfig();
13
+ const { db, warnings } = open(cfg);
14
+ printWarnings(warnings);
15
+ const rows = [];
16
+ let failed = 0;
17
+ for (const [name, sql] of Object.entries((_cfg_queries = cfg.queries) !== null && _cfg_queries !== void 0 ? _cfg_queries : {})){
18
+ var _sql_match;
19
+ const params = ((_sql_match = sql.match(/\?/g)) !== null && _sql_match !== void 0 ? _sql_match : []).length;
20
+ try {
21
+ const statement = db.prepare(sql);
22
+ if (params > 0) {
23
+ rows.push({
24
+ query: name,
25
+ params,
26
+ rows: '—',
27
+ status: 'ok (not run: needs parameters)'
28
+ });
29
+ continue;
30
+ }
31
+ const count = statement.all().length;
32
+ rows.push({
33
+ query: name,
34
+ params,
35
+ rows: count,
36
+ status: count === 0 ? 'ok, but returns 0 rows' : 'ok'
37
+ });
38
+ } catch (err) {
39
+ failed++;
40
+ rows.push({
41
+ query: name,
42
+ params,
43
+ rows: '—',
44
+ status: `FAILED: ${err.message}`
45
+ });
46
+ }
47
+ }
48
+ db.close();
49
+ if (rows.length === 0) {
50
+ console.log('no saved queries in config');
51
+ return;
52
+ }
53
+ printRows(rows, ctx.format);
54
+ if (failed > 0) process.exit(1);
55
+ };
56
+ export default check;
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/check.ts"],"sourcesContent":["import { open } from '../db.ts';\nimport type { Row } from '../output.ts';\nimport { printRows } from '../output.ts';\nimport { printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// A saved query that returns zero rows looks identical to a true empty result, so a broken\n// one can sit unnoticed -- one field report had `WHERE flag = 'true'` against a numeric\n// column reading as \"nothing tagged yet\" for hours. Preparing each query catches syntax and\n// unknown-column errors without executing it; queries that take no parameters are also run\n// for a row count. Parameterised queries are validated but not counted: inventing arguments\n// would report a count for a query nobody ran.\nconst check: Command = (ctx) => {\n const cfg = ctx.resolveConfig();\n const { db, warnings } = open(cfg);\n printWarnings(warnings);\n\n const rows: Row[] = [];\n let failed = 0;\n for (const [name, sql] of Object.entries(cfg.queries ?? {})) {\n const params = (sql.match(/\\?/g) ?? []).length;\n try {\n const statement = db.prepare(sql);\n if (params > 0) {\n rows.push({ query: name, params, rows: '—', status: 'ok (not run: needs parameters)' });\n continue;\n }\n const count = (statement.all() as unknown[]).length;\n rows.push({ query: name, params, rows: count, status: count === 0 ? 'ok, but returns 0 rows' : 'ok' });\n } catch (err) {\n failed++;\n rows.push({ query: name, params, rows: '—', status: `FAILED: ${(err as Error).message}` });\n }\n }\n\n db.close();\n if (rows.length === 0) {\n console.log('no saved queries in config');\n return;\n }\n printRows(rows, ctx.format);\n if (failed > 0) process.exit(1);\n};\nexport default check;\n"],"names":["open","printRows","printWarnings","check","ctx","cfg","resolveConfig","db","warnings","rows","failed","name","sql","Object","entries","queries","params","match","length","statement","prepare","push","query","status","count","all","err","message","close","console","log","format","process","exit"],"mappings":"AAAA,SAASA,IAAI,QAAQ,WAAW;AAEhC,SAASC,SAAS,QAAQ,eAAe;AACzC,SAASC,aAAa,QAAQ,cAAc;AAG5C,2FAA2F;AAC3F,wFAAwF;AACxF,4FAA4F;AAC5F,2FAA2F;AAC3F,4FAA4F;AAC5F,+CAA+C;AAC/C,MAAMC,QAAiB,CAACC;QAOmBC;IANzC,MAAMA,MAAMD,IAAIE,aAAa;IAC7B,MAAM,EAAEC,EAAE,EAAEC,QAAQ,EAAE,GAAGR,KAAKK;IAC9BH,cAAcM;IAEd,MAAMC,OAAc,EAAE;IACtB,IAAIC,SAAS;IACb,KAAK,MAAM,CAACC,MAAMC,IAAI,IAAIC,OAAOC,OAAO,EAACT,eAAAA,IAAIU,OAAO,cAAXV,0BAAAA,eAAe,CAAC,GAAI;YAC3CO;QAAhB,MAAMI,SAAS,EAACJ,aAAAA,IAAIK,KAAK,CAAC,oBAAVL,wBAAAA,aAAoB,EAAE,EAAEM,MAAM;QAC9C,IAAI;YACF,MAAMC,YAAYZ,GAAGa,OAAO,CAACR;YAC7B,IAAII,SAAS,GAAG;gBACdP,KAAKY,IAAI,CAAC;oBAAEC,OAAOX;oBAAMK;oBAAQP,MAAM;oBAAKc,QAAQ;gBAAiC;gBACrF;YACF;YACA,MAAMC,QAAQ,AAACL,UAAUM,GAAG,GAAiBP,MAAM;YACnDT,KAAKY,IAAI,CAAC;gBAAEC,OAAOX;gBAAMK;gBAAQP,MAAMe;gBAAOD,QAAQC,UAAU,IAAI,2BAA2B;YAAK;QACtG,EAAE,OAAOE,KAAK;YACZhB;YACAD,KAAKY,IAAI,CAAC;gBAAEC,OAAOX;gBAAMK;gBAAQP,MAAM;gBAAKc,QAAQ,CAAC,QAAQ,EAAE,AAACG,IAAcC,OAAO,EAAE;YAAC;QAC1F;IACF;IAEApB,GAAGqB,KAAK;IACR,IAAInB,KAAKS,MAAM,KAAK,GAAG;QACrBW,QAAQC,GAAG,CAAC;QACZ;IACF;IACA7B,UAAUQ,MAAML,IAAI2B,MAAM;IAC1B,IAAIrB,SAAS,GAAGsB,QAAQC,IAAI,CAAC;AAC/B;AACA,eAAe9B,MAAM"}
@@ -5,6 +5,7 @@ export const COMMANDS = {
5
5
  init: ()=>import('./init.js'),
6
6
  watch: ()=>import('./watch.js'),
7
7
  status: ()=>import('./status.js'),
8
+ check: ()=>import('./check.js'),
8
9
  rebuild: ()=>import('./rebuild.js'),
9
10
  query: ()=>import('./query.js'),
10
11
  find: ()=>import('./find.js'),
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/index.ts"],"sourcesContent":["import type { Command } from './types.ts';\n\n// Lazy registry: a command's imports load only when it runs, so a heavy dependency in one\n// command never taxes the others (or --version/--help). These names are reserved: a named\n// query in config can never shadow them.\nexport const COMMANDS: Record<string, () => Promise<{ default: Command }>> = {\n init: () => import('./init.ts'),\n watch: () => import('./watch.ts'),\n status: () => import('./status.ts'),\n rebuild: () => import('./rebuild.ts'),\n query: () => import('./query.ts'),\n find: () => import('./find.ts'),\n map: () => import('./map.ts'),\n peek: () => import('./peek.ts'),\n};\n\nexport type { Command, Ctx } from './types.ts';\n"],"names":["COMMANDS","init","watch","status","rebuild","query","find","map","peek"],"mappings":"AAEA,0FAA0F;AAC1F,0FAA0F;AAC1F,yCAAyC;AACzC,OAAO,MAAMA,WAAgE;IAC3EC,MAAM,IAAM,MAAM,CAAC;IACnBC,OAAO,IAAM,MAAM,CAAC;IACpBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,SAAS,IAAM,MAAM,CAAC;IACtBC,OAAO,IAAM,MAAM,CAAC;IACpBC,MAAM,IAAM,MAAM,CAAC;IACnBC,KAAK,IAAM,MAAM,CAAC;IAClBC,MAAM,IAAM,MAAM,CAAC;AACrB,EAAE"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/index.ts"],"sourcesContent":["import type { Command } from './types.ts';\n\n// Lazy registry: a command's imports load only when it runs, so a heavy dependency in one\n// command never taxes the others (or --version/--help). These names are reserved: a named\n// query in config can never shadow them.\nexport const COMMANDS: Record<string, () => Promise<{ default: Command }>> = {\n init: () => import('./init.ts'),\n watch: () => import('./watch.ts'),\n status: () => import('./status.ts'),\n check: () => import('./check.ts'),\n rebuild: () => import('./rebuild.ts'),\n query: () => import('./query.ts'),\n find: () => import('./find.ts'),\n map: () => import('./map.ts'),\n peek: () => import('./peek.ts'),\n};\n\nexport type { Command, Ctx } from './types.ts';\n"],"names":["COMMANDS","init","watch","status","check","rebuild","query","find","map","peek"],"mappings":"AAEA,0FAA0F;AAC1F,0FAA0F;AAC1F,yCAAyC;AACzC,OAAO,MAAMA,WAAgE;IAC3EC,MAAM,IAAM,MAAM,CAAC;IACnBC,OAAO,IAAM,MAAM,CAAC;IACpBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,OAAO,IAAM,MAAM,CAAC;IACpBC,SAAS,IAAM,MAAM,CAAC;IACtBC,OAAO,IAAM,MAAM,CAAC;IACpBC,MAAM,IAAM,MAAM,CAAC;IACnBC,KAAK,IAAM,MAAM,CAAC;IAClBC,MAAM,IAAM,MAAM,CAAC;AACrB,EAAE"}
@@ -1,20 +1,51 @@
1
1
  import { embedConfig, featureStates } from '../config.js';
2
2
  import { docCount, getMeta, open } from '../db.js';
3
3
  import { printWarnings } from './shared.js';
4
+ // Vectors are computed on the first semantic query, not at reconcile, so a tree can be
5
+ // embed-enabled with nothing embedded yet -- reported here because a fast `rebuild` on an
6
+ // embed tree otherwise reads as "the feature isn't on".
7
+ function vectorState(db) {
8
+ try {
9
+ const row = db.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings').get();
10
+ return row;
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
4
15
  const status = (ctx)=>{
16
+ var _ref;
17
+ var _cfg_defaults_find, _cfg_defaults;
5
18
  const cfg = ctx.resolveConfig();
6
19
  const { db, dbPath, warnings } = open(cfg);
7
20
  printWarnings(warnings);
8
- console.log(`db: ${dbPath}`);
9
- console.log(`docs: ${docCount(db)}`);
10
21
  const features = featureStates(cfg);
11
- const off = features.off.length > 0 ? ` · off: ${features.off.map((name)=>`${name} (features.${name})`).join(', ')}` : '';
12
- console.log(`features: ${features.on.join(', ')}${off}`);
13
22
  const e = embedConfig(cfg);
14
- if (e) console.log(`embed: ${e.type} ${e.model}`);
23
+ const vectors = e ? vectorState(db) : null;
15
24
  const heartbeat = getMeta(db, 'watch_heartbeat');
16
- if (heartbeat) console.log(`watcher: last heartbeat ${Math.round((Date.now() - Date.parse(heartbeat)) / 1000)}s ago`);
17
- else console.log('watcher: no watcher');
25
+ const result = {
26
+ db: dbPath,
27
+ docs: docCount(db),
28
+ features: features.on,
29
+ featuresOff: features.off,
30
+ embed: e ? {
31
+ type: e.type,
32
+ model: e.model,
33
+ ...vectors !== null && vectors !== void 0 ? vectors : {}
34
+ } : null,
35
+ findDefaultWhere: (_ref = (_cfg_defaults = cfg.defaults) === null || _cfg_defaults === void 0 ? void 0 : (_cfg_defaults_find = _cfg_defaults.find) === null || _cfg_defaults_find === void 0 ? void 0 : _cfg_defaults_find.where) !== null && _ref !== void 0 ? _ref : null,
36
+ watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null
37
+ };
38
+ if (ctx.format === 'json') {
39
+ console.log(JSON.stringify(result, null, 2));
40
+ } else {
41
+ console.log(`db: ${result.db}`);
42
+ console.log(`docs: ${result.docs}`);
43
+ const off = features.off.length > 0 ? ` · off: ${features.off.map((name)=>`${name} (features.${name})`).join(', ')}` : '';
44
+ console.log(`features: ${features.on.join(', ')}${off}`);
45
+ if (e) console.log(`embed: ${e.type} ${e.model}${vectors ? ` (vectors: ${vectors.embedded} embedded, ${vectors.pending} pending — embedded on first --semantic query)` : ''}`);
46
+ if (result.findDefaultWhere) console.log(`find default scope: ${result.findDefaultWhere} (--where replaces it)`);
47
+ console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: no watcher' : `watcher: last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);
48
+ }
18
49
  db.close();
19
50
  };
20
51
  export default status;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/status.ts"],"sourcesContent":["import { embedConfig, featureStates } from '../config.ts';\nimport { docCount, getMeta, open } from '../db.ts';\nimport { printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst status: Command = (ctx) => {\n const cfg = ctx.resolveConfig();\n const { db, dbPath, warnings } = open(cfg);\n printWarnings(warnings);\n console.log(`db: ${dbPath}`);\n console.log(`docs: ${docCount(db)}`);\n const features = featureStates(cfg);\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (features.${name})`).join(', ')}` : '';\n console.log(`features: ${features.on.join(', ')}${off}`);\n const e = embedConfig(cfg);\n if (e) console.log(`embed: ${e.type} ${e.model}`);\n const heartbeat = getMeta(db, 'watch_heartbeat');\n if (heartbeat) console.log(`watcher: last heartbeat ${Math.round((Date.now() - Date.parse(heartbeat)) / 1000)}s ago`);\n else console.log('watcher: no watcher');\n db.close();\n};\nexport default status;\n"],"names":["embedConfig","featureStates","docCount","getMeta","open","printWarnings","status","ctx","cfg","resolveConfig","db","dbPath","warnings","console","log","features","off","length","map","name","join","on","e","type","model","heartbeat","Math","round","Date","now","parse","close"],"mappings":"AAAA,SAASA,WAAW,EAAEC,aAAa,QAAQ,eAAe;AAC1D,SAASC,QAAQ,EAAEC,OAAO,EAAEC,IAAI,QAAQ,WAAW;AACnD,SAASC,aAAa,QAAQ,cAAc;AAG5C,MAAMC,SAAkB,CAACC;IACvB,MAAMC,MAAMD,IAAIE,aAAa;IAC7B,MAAM,EAAEC,EAAE,EAAEC,MAAM,EAAEC,QAAQ,EAAE,GAAGR,KAAKI;IACtCH,cAAcO;IACdC,QAAQC,GAAG,CAAC,CAAC,IAAI,EAAEH,QAAQ;IAC3BE,QAAQC,GAAG,CAAC,CAAC,MAAM,EAAEZ,SAASQ,KAAK;IACnC,MAAMK,WAAWd,cAAcO;IAC/B,MAAMQ,MAAMD,SAASC,GAAG,CAACC,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAEF,SAASC,GAAG,CAACE,GAAG,CAAC,CAACC,OAAS,GAAGA,KAAK,WAAW,EAAEA,KAAK,CAAC,CAAC,EAAEC,IAAI,CAAC,OAAO,GAAG;IACzHP,QAAQC,GAAG,CAAC,CAAC,UAAU,EAAEC,SAASM,EAAE,CAACD,IAAI,CAAC,QAAQJ,KAAK;IACvD,MAAMM,IAAItB,YAAYQ;IACtB,IAAIc,GAAGT,QAAQC,GAAG,CAAC,CAAC,OAAO,EAAEQ,EAAEC,IAAI,CAAC,CAAC,EAAED,EAAEE,KAAK,EAAE;IAChD,MAAMC,YAAYtB,QAAQO,IAAI;IAC9B,IAAIe,WAAWZ,QAAQC,GAAG,CAAC,CAAC,wBAAwB,EAAEY,KAAKC,KAAK,CAAC,AAACC,CAAAA,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAACL,UAAS,IAAK,MAAM,KAAK,CAAC;SAC/GZ,QAAQC,GAAG,CAAC;IACjBJ,GAAGqB,KAAK;AACV;AACA,eAAezB,OAAO"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/status.ts"],"sourcesContent":["import { embedConfig, featureStates } from '../config.ts';\nimport { docCount, getMeta, open } from '../db.ts';\nimport { printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can be\n// embed-enabled with nothing embedded yet -- reported here because a fast `rebuild` on an\n// embed tree otherwise reads as \"the feature isn't on\".\nfunction vectorState(db: ReturnType<typeof open>['db']): { embedded: number; pending: number } | null {\n try {\n const row = db.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings').get() as { embedded: number; pending: number };\n return row;\n } catch {\n return null;\n }\n}\n\nconst status: Command = (ctx) => {\n const cfg = ctx.resolveConfig();\n const { db, dbPath, warnings } = open(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const vectors = e ? vectorState(db) : null;\n const heartbeat = getMeta(db, 'watch_heartbeat');\n const result = {\n db: dbPath,\n docs: docCount(db),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { type: e.type, model: e.model, ...(vectors ?? {}) } : null,\n findDefaultWhere: cfg.defaults?.find?.where ?? null,\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n };\n\n if (ctx.format === 'json') {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(`db: ${result.db}`);\n console.log(`docs: ${result.docs}`);\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (features.${name})`).join(', ')}` : '';\n console.log(`features: ${features.on.join(', ')}${off}`);\n if (e) console.log(`embed: ${e.type} ${e.model}${vectors ? ` (vectors: ${vectors.embedded} embedded, ${vectors.pending} pending — embedded on first --semantic query)` : ''}`);\n if (result.findDefaultWhere) console.log(`find default scope: ${result.findDefaultWhere} (--where replaces it)`);\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: no watcher' : `watcher: last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n }\n db.close();\n};\nexport default status;\n"],"names":["embedConfig","featureStates","docCount","getMeta","open","printWarnings","vectorState","db","row","prepare","get","status","ctx","cfg","resolveConfig","dbPath","warnings","features","e","vectors","heartbeat","result","docs","on","featuresOff","off","embed","type","model","findDefaultWhere","defaults","find","where","watcherHeartbeatSecondsAgo","Math","round","Date","now","parse","format","console","log","JSON","stringify","length","map","name","join","embedded","pending","close"],"mappings":"AAAA,SAASA,WAAW,EAAEC,aAAa,QAAQ,eAAe;AAC1D,SAASC,QAAQ,EAAEC,OAAO,EAAEC,IAAI,QAAQ,WAAW;AACnD,SAASC,aAAa,QAAQ,cAAc;AAG5C,uFAAuF;AACvF,0FAA0F;AAC1F,wDAAwD;AACxD,SAASC,YAAYC,EAAiC;IACpD,IAAI;QACF,MAAMC,MAAMD,GAAGE,OAAO,CAAC,yFAAyFC,GAAG;QACnH,OAAOF;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,MAAMG,SAAkB,CAACC;;QAeHC,oBAAAA;IAdpB,MAAMA,MAAMD,IAAIE,aAAa;IAC7B,MAAM,EAAEP,EAAE,EAAEQ,MAAM,EAAEC,QAAQ,EAAE,GAAGZ,KAAKS;IACtCR,cAAcW;IAEd,MAAMC,WAAWhB,cAAcY;IAC/B,MAAMK,IAAIlB,YAAYa;IACtB,MAAMM,UAAUD,IAAIZ,YAAYC,MAAM;IACtC,MAAMa,YAAYjB,QAAQI,IAAI;IAC9B,MAAMc,SAAS;QACbd,IAAIQ;QACJO,MAAMpB,SAASK;QACfU,UAAUA,SAASM,EAAE;QACrBC,aAAaP,SAASQ,GAAG;QACzBC,OAAOR,IAAI;YAAES,MAAMT,EAAES,IAAI;YAAEC,OAAOV,EAAEU,KAAK;eAAMT,oBAAAA,qBAAAA,UAAW,CAAC,CAAhB;QAAmB,IAAI;QAClEU,gBAAgB,WAAEhB,gBAAAA,IAAIiB,QAAQ,cAAZjB,qCAAAA,qBAAAA,cAAckB,IAAI,cAAlBlB,yCAAAA,mBAAoBmB,KAAK,uCAAI;QAC/CC,4BAA4Bb,YAAYc,KAAKC,KAAK,CAAC,AAACC,CAAAA,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAAClB,UAAS,IAAK,QAAQ;IACpG;IAEA,IAAIR,IAAI2B,MAAM,KAAK,QAAQ;QACzBC,QAAQC,GAAG,CAACC,KAAKC,SAAS,CAACtB,QAAQ,MAAM;IAC3C,OAAO;QACLmB,QAAQC,GAAG,CAAC,CAAC,IAAI,EAAEpB,OAAOd,EAAE,EAAE;QAC9BiC,QAAQC,GAAG,CAAC,CAAC,MAAM,EAAEpB,OAAOC,IAAI,EAAE;QAClC,MAAMG,MAAMR,SAASQ,GAAG,CAACmB,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE3B,SAASQ,GAAG,CAACoB,GAAG,CAAC,CAACC,OAAS,GAAGA,KAAK,WAAW,EAAEA,KAAK,CAAC,CAAC,EAAEC,IAAI,CAAC,OAAO,GAAG;QACzHP,QAAQC,GAAG,CAAC,CAAC,UAAU,EAAExB,SAASM,EAAE,CAACwB,IAAI,CAAC,QAAQtB,KAAK;QACvD,IAAIP,GAAGsB,QAAQC,GAAG,CAAC,CAAC,OAAO,EAAEvB,EAAES,IAAI,CAAC,CAAC,EAAET,EAAEU,KAAK,GAAGT,UAAU,CAAC,WAAW,EAAEA,QAAQ6B,QAAQ,CAAC,WAAW,EAAE7B,QAAQ8B,OAAO,CAAC,8CAA8C,CAAC,GAAG,IAAI;QAC7K,IAAI5B,OAAOQ,gBAAgB,EAAEW,QAAQC,GAAG,CAAC,CAAC,oBAAoB,EAAEpB,OAAOQ,gBAAgB,CAAC,sBAAsB,CAAC;QAC/GW,QAAQC,GAAG,CAACpB,OAAOY,0BAA0B,KAAK,OAAO,wBAAwB,CAAC,wBAAwB,EAAEZ,OAAOY,0BAA0B,CAAC,KAAK,CAAC;IACtJ;IACA1B,GAAG2C,KAAK;AACV;AACA,eAAevC,OAAO"}
@@ -22,11 +22,17 @@ export interface Config {
22
22
  rank?: boolean;
23
23
  embed?: boolean | EmbedConfig;
24
24
  };
25
+ defaults?: {
26
+ find?: {
27
+ where?: string;
28
+ };
29
+ };
25
30
  queries: Record<string, string>;
26
31
  }
27
32
  export interface ResolvedConfig extends Config {
28
33
  baseDir: string;
29
34
  configPath: string | null;
35
+ unknownKeys?: string[];
30
36
  migratedFrom?: number;
31
37
  }
32
38
  export declare function featureEnabled(cfg: Config, name: FeatureName): boolean;
@@ -126,8 +126,36 @@ export function findConfigPath(startDir) {
126
126
  // Shape check for hand-edited files: a typo'd config fails with a named error, not a
127
127
  // TypeError from whatever code touched the missing field first. `queries` is optional
128
128
  // on disk (absent = none); `scan.include` has no usable default.
129
+ // Keys each block actually reads. A key outside these sets is accepted and ignored, which
130
+ // looks identical to working -- one field report set `scan.exclude`, saw no change in doc
131
+ // count and no warning, and reasonably concluded the filter had applied. Reported as a
132
+ // warning rather than an error so a config carrying an unknown key still runs.
133
+ const KNOWN_KEYS = new Set([
134
+ '$schema',
135
+ 'version',
136
+ 'scan',
137
+ 'features',
138
+ 'defaults',
139
+ 'queries'
140
+ ]);
141
+ const KNOWN_SCAN_KEYS = new Set([
142
+ 'include'
143
+ ]);
144
+ function unknownConfigKeys(cfg) {
145
+ const unknown = Object.keys(cfg).filter((k)=>!KNOWN_KEYS.has(k)).map((k)=>k);
146
+ const scan = cfg.scan;
147
+ if (scan && typeof scan === 'object' && !Array.isArray(scan)) {
148
+ unknown.push(...Object.keys(scan).filter((k)=>!KNOWN_SCAN_KEYS.has(k)).map((k)=>`scan.${k}`));
149
+ }
150
+ const features = cfg.features;
151
+ if (features && typeof features === 'object' && !Array.isArray(features)) {
152
+ unknown.push(...Object.keys(features).filter((k)=>!FEATURE_NAMES.includes(k)).map((k)=>`features.${k}`));
153
+ }
154
+ return unknown;
155
+ }
129
156
  function validateConfig(parsed, configPath) {
130
157
  var _cfg_features;
158
+ var _defaults_find;
131
159
  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
132
160
  throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);
133
161
  }
@@ -140,6 +168,10 @@ function validateConfig(parsed, configPath) {
140
168
  if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries) || !Object.values(cfg.queries).every((q)=>typeof q === 'string')) {
141
169
  throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> SQL string`);
142
170
  }
171
+ const defaults = cfg.defaults;
172
+ if (defaults !== undefined && (typeof 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')) {
173
+ throw new SenseError('CONFIG_INVALID', `${configPath}: defaults.find.where must be a SQL condition string`);
174
+ }
143
175
  if (cfg.features !== undefined && (typeof cfg.features !== 'object' || cfg.features === null || Array.isArray(cfg.features))) {
144
176
  throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);
145
177
  }
@@ -186,10 +218,12 @@ export function loadConfig(explicitPath) {
186
218
  migratedFrom = result.from;
187
219
  writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}\n`);
188
220
  }
221
+ const unknownKeys = unknownConfigKeys(cfg);
189
222
  return {
190
223
  ...cfg,
191
224
  baseDir: dirname(configPath),
192
225
  configPath,
193
- migratedFrom
226
+ migratedFrom,
227
+ unknownKeys: unknownKeys.length > 0 ? unknownKeys : undefined
194
228
  };
195
229
  }