sensemaking 0.17.0 → 0.18.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 (429) hide show
  1. package/README.md +15 -13
  2. package/dist/cjs/cli/map.js +147 -6
  3. package/dist/cjs/cli/map.js.map +1 -1
  4. package/dist/cjs/cli/named.js +20 -12
  5. package/dist/cjs/cli/named.js.map +1 -1
  6. package/dist/cjs/cli/path.js +190 -29
  7. package/dist/cjs/cli/path.js.map +1 -1
  8. package/dist/cjs/cli/peek.js +148 -7
  9. package/dist/cjs/cli/peek.js.map +1 -1
  10. package/dist/cjs/cli/related.js +5 -5
  11. package/dist/cjs/cli/related.js.map +1 -1
  12. package/dist/cjs/cli/search.js +5 -5
  13. package/dist/cjs/cli/search.js.map +1 -1
  14. package/dist/cjs/cli/shared.d.cts +3 -3
  15. package/dist/cjs/cli/shared.d.ts +3 -3
  16. package/dist/cjs/cli/shared.js +156 -58
  17. package/dist/cjs/cli/shared.js.map +1 -1
  18. package/dist/cjs/cli/sql.js +151 -10
  19. package/dist/cjs/cli/sql.js.map +1 -1
  20. package/dist/cjs/cli/status.js +187 -62
  21. package/dist/cjs/cli/status.js.map +1 -1
  22. package/dist/cjs/commands/map.d.cts +2 -2
  23. package/dist/cjs/commands/map.d.ts +2 -2
  24. package/dist/cjs/commands/map.js +373 -67
  25. package/dist/cjs/commands/map.js.map +1 -1
  26. package/dist/cjs/commands/peek.d.cts +2 -2
  27. package/dist/cjs/commands/peek.d.ts +2 -2
  28. package/dist/cjs/commands/peek.js +321 -79
  29. package/dist/cjs/commands/peek.js.map +1 -1
  30. package/dist/cjs/commands/related.d.cts +2 -2
  31. package/dist/cjs/commands/related.d.ts +2 -2
  32. package/dist/cjs/commands/related.js +55 -10
  33. package/dist/cjs/commands/related.js.map +1 -1
  34. package/dist/cjs/commands/scope.d.cts +7 -7
  35. package/dist/cjs/commands/scope.d.ts +7 -7
  36. package/dist/cjs/commands/scope.js +382 -45
  37. package/dist/cjs/commands/scope.js.map +1 -1
  38. package/dist/cjs/commands/search.d.cts +2 -2
  39. package/dist/cjs/commands/search.d.ts +2 -2
  40. package/dist/cjs/commands/search.js +292 -99
  41. package/dist/cjs/commands/search.js.map +1 -1
  42. package/dist/cjs/commands/signals.d.cts +4 -10
  43. package/dist/cjs/commands/signals.d.ts +4 -10
  44. package/dist/cjs/commands/signals.js +91 -45
  45. package/dist/cjs/commands/signals.js.map +1 -1
  46. package/dist/cjs/commands/status.d.cts +2 -2
  47. package/dist/cjs/commands/status.d.ts +2 -2
  48. package/dist/cjs/commands/status.js +247 -10
  49. package/dist/cjs/commands/status.js.map +1 -1
  50. package/dist/cjs/config/access.d.cts +1 -0
  51. package/dist/cjs/config/access.d.ts +1 -0
  52. package/dist/cjs/config/access.js +7 -0
  53. package/dist/cjs/config/access.js.map +1 -1
  54. package/dist/cjs/config/index.d.cts +1 -1
  55. package/dist/cjs/config/index.d.ts +1 -1
  56. package/dist/cjs/config/index.js +3 -0
  57. package/dist/cjs/config/index.js.map +1 -1
  58. package/dist/cjs/config/types.d.cts +1 -0
  59. package/dist/cjs/config/types.d.ts +1 -0
  60. package/dist/cjs/config/types.js.map +1 -1
  61. package/dist/cjs/config/validate.js +10 -1
  62. package/dist/cjs/config/validate.js.map +1 -1
  63. package/dist/cjs/embed/distribution.d.cts +4 -0
  64. package/dist/cjs/embed/distribution.d.ts +4 -0
  65. package/dist/cjs/embed/distribution.js +186 -0
  66. package/dist/cjs/embed/distribution.js.map +1 -0
  67. package/dist/cjs/embed/langfit.d.cts +2 -5
  68. package/dist/cjs/embed/langfit.d.ts +2 -5
  69. package/dist/cjs/embed/langfit.js +73 -63
  70. package/dist/cjs/embed/langfit.js.map +1 -1
  71. package/dist/cjs/embed/query.d.cts +6 -13
  72. package/dist/cjs/embed/query.d.ts +6 -13
  73. package/dist/cjs/embed/query.js +56 -168
  74. package/dist/cjs/embed/query.js.map +1 -1
  75. package/dist/cjs/embed/static.js +13 -3
  76. package/dist/cjs/embed/static.js.map +1 -1
  77. package/dist/cjs/embed/types.d.cts +1 -0
  78. package/dist/cjs/embed/types.d.ts +1 -0
  79. package/dist/cjs/embed/types.js +11 -2
  80. package/dist/cjs/embed/types.js.map +1 -1
  81. package/dist/cjs/errors.d.cts +1 -1
  82. package/dist/cjs/errors.d.ts +1 -1
  83. package/dist/cjs/errors.js.map +1 -1
  84. package/dist/cjs/features/embed.js +218 -11
  85. package/dist/cjs/features/embed.js.map +1 -1
  86. package/dist/cjs/features/index.d.cts +1 -1
  87. package/dist/cjs/features/index.d.ts +1 -1
  88. package/dist/cjs/features/index.js +6 -0
  89. package/dist/cjs/features/index.js.map +1 -1
  90. package/dist/cjs/features/links.d.cts +7 -2
  91. package/dist/cjs/features/links.d.ts +7 -2
  92. package/dist/cjs/features/links.js +710 -198
  93. package/dist/cjs/features/links.js.map +1 -1
  94. package/dist/cjs/features/rank.js +231 -31
  95. package/dist/cjs/features/rank.js.map +1 -1
  96. package/dist/cjs/features/sections.js +218 -8
  97. package/dist/cjs/features/sections.js.map +1 -1
  98. package/dist/cjs/features/tags.js +236 -40
  99. package/dist/cjs/features/tags.js.map +1 -1
  100. package/dist/cjs/features/types.d.cts +9 -5
  101. package/dist/cjs/features/types.d.ts +9 -5
  102. package/dist/cjs/graph/traverse.d.cts +2 -2
  103. package/dist/cjs/graph/traverse.d.ts +2 -2
  104. package/dist/cjs/graph/traverse.js +321 -42
  105. package/dist/cjs/graph/traverse.js.map +1 -1
  106. package/dist/cjs/index.d.cts +3 -2
  107. package/dist/cjs/index.d.ts +3 -2
  108. package/dist/cjs/index.js +2 -2
  109. package/dist/cjs/index.js.map +1 -1
  110. package/dist/cjs/lib/guarded-tick.d.cts +1 -0
  111. package/dist/cjs/lib/guarded-tick.d.ts +1 -0
  112. package/dist/cjs/lib/guarded-tick.js +19 -0
  113. package/dist/cjs/lib/guarded-tick.js.map +1 -0
  114. package/dist/cjs/output/column-hint.d.cts +1 -2
  115. package/dist/cjs/output/column-hint.d.ts +1 -2
  116. package/dist/cjs/output/column-hint.js +1 -4
  117. package/dist/cjs/output/column-hint.js.map +1 -1
  118. package/dist/cjs/output/output.d.cts +2 -1
  119. package/dist/cjs/output/output.d.ts +2 -1
  120. package/dist/cjs/output/output.js +515 -97
  121. package/dist/cjs/output/output.js.map +1 -1
  122. package/dist/cjs/scan/frontmatter.js.map +1 -1
  123. package/dist/cjs/scan/index.js.map +1 -1
  124. package/dist/cjs/scan/reparse.d.cts +10 -0
  125. package/dist/cjs/scan/reparse.d.ts +10 -0
  126. package/dist/cjs/scan/reparse.js +104 -0
  127. package/dist/cjs/scan/reparse.js.map +1 -0
  128. package/dist/cjs/store/duckdb/batch.d.cts +16 -0
  129. package/dist/cjs/store/duckdb/batch.d.ts +16 -0
  130. package/dist/cjs/store/duckdb/batch.js +208 -0
  131. package/dist/cjs/store/duckdb/batch.js.map +1 -0
  132. package/dist/cjs/store/duckdb/connection.d.cts +5 -0
  133. package/dist/cjs/store/duckdb/connection.d.ts +5 -0
  134. package/dist/cjs/store/duckdb/connection.js +772 -0
  135. package/dist/cjs/store/duckdb/connection.js.map +1 -0
  136. package/dist/cjs/store/duckdb/lexical.d.cts +10 -0
  137. package/dist/cjs/store/duckdb/lexical.d.ts +10 -0
  138. package/dist/cjs/store/duckdb/lexical.js +502 -0
  139. package/dist/cjs/store/duckdb/lexical.js.map +1 -0
  140. package/dist/cjs/store/duckdb/native.d.cts +3 -0
  141. package/dist/cjs/store/duckdb/native.d.ts +3 -0
  142. package/dist/cjs/store/duckdb/native.js +360 -0
  143. package/dist/cjs/store/duckdb/native.js.map +1 -0
  144. package/dist/cjs/store/duckdb/open.d.cts +12 -0
  145. package/dist/cjs/store/duckdb/open.d.ts +12 -0
  146. package/dist/cjs/store/duckdb/open.js +501 -0
  147. package/dist/cjs/store/duckdb/open.js.map +1 -0
  148. package/dist/cjs/store/duckdb/reconcile.d.cts +6 -0
  149. package/dist/cjs/store/duckdb/reconcile.d.ts +6 -0
  150. package/dist/cjs/store/duckdb/reconcile.js +753 -0
  151. package/dist/cjs/store/duckdb/reconcile.js.map +1 -0
  152. package/dist/cjs/store/duckdb/sql-functions.d.cts +2 -0
  153. package/dist/cjs/store/duckdb/sql-functions.d.ts +2 -0
  154. package/dist/cjs/store/duckdb/sql-functions.js +203 -0
  155. package/dist/cjs/store/duckdb/sql-functions.js.map +1 -0
  156. package/dist/cjs/store/duckdb/store.d.cts +5 -0
  157. package/dist/cjs/store/duckdb/store.d.ts +5 -0
  158. package/dist/cjs/store/duckdb/store.js +504 -0
  159. package/dist/cjs/store/duckdb/store.js.map +1 -0
  160. package/dist/cjs/store/duckdb/vectors.d.cts +9 -0
  161. package/dist/cjs/store/duckdb/vectors.d.ts +9 -0
  162. package/dist/cjs/store/duckdb/vectors.js +502 -0
  163. package/dist/cjs/store/duckdb/vectors.js.map +1 -0
  164. package/dist/cjs/store/index.d.cts +7 -0
  165. package/dist/cjs/store/index.d.ts +7 -0
  166. package/dist/cjs/store/index.js +201 -0
  167. package/dist/cjs/store/index.js.map +1 -0
  168. package/dist/cjs/store/meta.d.cts +3 -0
  169. package/dist/cjs/store/meta.d.ts +3 -0
  170. package/dist/cjs/store/meta.js +215 -0
  171. package/dist/cjs/store/meta.js.map +1 -0
  172. package/dist/cjs/store/shared.d.cts +5 -0
  173. package/dist/cjs/store/shared.d.ts +5 -0
  174. package/dist/cjs/store/shared.js +252 -0
  175. package/dist/cjs/store/shared.js.map +1 -0
  176. package/dist/cjs/store/sql-functions.d.cts +2 -0
  177. package/dist/cjs/store/sql-functions.d.ts +2 -0
  178. package/dist/cjs/store/sql-functions.js +48 -0
  179. package/dist/cjs/store/sql-functions.js.map +1 -0
  180. package/dist/cjs/store/sqlite/connection.d.cts +3 -0
  181. package/dist/cjs/store/sqlite/connection.d.ts +3 -0
  182. package/dist/cjs/store/sqlite/connection.js +516 -0
  183. package/dist/cjs/store/sqlite/connection.js.map +1 -0
  184. package/dist/cjs/store/sqlite/lexical.d.cts +2 -0
  185. package/dist/cjs/store/sqlite/lexical.d.ts +2 -0
  186. package/dist/cjs/store/sqlite/lexical.js +177 -0
  187. package/dist/cjs/store/sqlite/lexical.js.map +1 -0
  188. package/dist/cjs/{db → store/sqlite}/open.d.cts +5 -5
  189. package/dist/cjs/{db → store/sqlite}/open.d.ts +5 -5
  190. package/dist/cjs/store/sqlite/open.js +604 -0
  191. package/dist/cjs/store/sqlite/open.js.map +1 -0
  192. package/dist/cjs/store/sqlite/reconcile.d.cts +10 -0
  193. package/dist/cjs/store/sqlite/reconcile.d.ts +10 -0
  194. package/dist/cjs/store/sqlite/reconcile.js +1020 -0
  195. package/dist/cjs/store/sqlite/reconcile.js.map +1 -0
  196. package/dist/cjs/store/sqlite/sql-functions.js +34 -0
  197. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -0
  198. package/dist/cjs/store/sqlite/store.d.cts +5 -0
  199. package/dist/cjs/store/sqlite/store.d.ts +5 -0
  200. package/dist/cjs/store/sqlite/store.js +588 -0
  201. package/dist/cjs/store/sqlite/store.js.map +1 -0
  202. package/dist/cjs/store/sqlite/vectors.d.cts +13 -0
  203. package/dist/cjs/store/sqlite/vectors.d.ts +13 -0
  204. package/dist/cjs/store/sqlite/vectors.js +470 -0
  205. package/dist/cjs/store/sqlite/vectors.js.map +1 -0
  206. package/dist/cjs/store/transaction.d.cts +5 -0
  207. package/dist/cjs/store/transaction.d.ts +5 -0
  208. package/dist/cjs/store/transaction.js +297 -0
  209. package/dist/cjs/store/transaction.js.map +1 -0
  210. package/dist/cjs/store/types.d.cts +91 -0
  211. package/dist/cjs/store/types.d.ts +91 -0
  212. package/dist/cjs/store/types.js +15 -0
  213. package/dist/cjs/store/types.js.map +1 -0
  214. package/dist/cjs/watch.d.cts +3 -0
  215. package/dist/cjs/watch.d.ts +3 -0
  216. package/dist/cjs/watch.js +215 -73
  217. package/dist/cjs/watch.js.map +1 -1
  218. package/dist/esm/cli/map.js +5 -5
  219. package/dist/esm/cli/map.js.map +1 -1
  220. package/dist/esm/cli/named.js +3 -3
  221. package/dist/esm/cli/named.js.map +1 -1
  222. package/dist/esm/cli/path.js +6 -5
  223. package/dist/esm/cli/path.js.map +1 -1
  224. package/dist/esm/cli/peek.js +5 -5
  225. package/dist/esm/cli/peek.js.map +1 -1
  226. package/dist/esm/cli/related.js +3 -3
  227. package/dist/esm/cli/related.js.map +1 -1
  228. package/dist/esm/cli/search.js +2 -2
  229. package/dist/esm/cli/search.js.map +1 -1
  230. package/dist/esm/cli/shared.d.ts +3 -3
  231. package/dist/esm/cli/shared.js +17 -17
  232. package/dist/esm/cli/shared.js.map +1 -1
  233. package/dist/esm/cli/sql.js +2 -2
  234. package/dist/esm/cli/sql.js.map +1 -1
  235. package/dist/esm/cli/status.js +25 -24
  236. package/dist/esm/cli/status.js.map +1 -1
  237. package/dist/esm/commands/map.d.ts +2 -2
  238. package/dist/esm/commands/map.js +51 -41
  239. package/dist/esm/commands/map.js.map +1 -1
  240. package/dist/esm/commands/peek.d.ts +2 -2
  241. package/dist/esm/commands/peek.js +58 -50
  242. package/dist/esm/commands/peek.js.map +1 -1
  243. package/dist/esm/commands/related.d.ts +2 -2
  244. package/dist/esm/commands/related.js +9 -9
  245. package/dist/esm/commands/related.js.map +1 -1
  246. package/dist/esm/commands/scope.d.ts +7 -7
  247. package/dist/esm/commands/scope.js +28 -17
  248. package/dist/esm/commands/scope.js.map +1 -1
  249. package/dist/esm/commands/search.d.ts +2 -2
  250. package/dist/esm/commands/search.js +42 -35
  251. package/dist/esm/commands/search.js.map +1 -1
  252. package/dist/esm/commands/signals.d.ts +4 -10
  253. package/dist/esm/commands/signals.js +17 -13
  254. package/dist/esm/commands/signals.js.map +1 -1
  255. package/dist/esm/commands/status.d.ts +2 -2
  256. package/dist/esm/commands/status.js +13 -7
  257. package/dist/esm/commands/status.js.map +1 -1
  258. package/dist/esm/config/access.d.ts +1 -0
  259. package/dist/esm/config/access.js +5 -0
  260. package/dist/esm/config/access.js.map +1 -1
  261. package/dist/esm/config/index.d.ts +1 -1
  262. package/dist/esm/config/index.js +1 -1
  263. package/dist/esm/config/index.js.map +1 -1
  264. package/dist/esm/config/types.d.ts +1 -0
  265. package/dist/esm/config/types.js.map +1 -1
  266. package/dist/esm/config/validate.js +10 -1
  267. package/dist/esm/config/validate.js.map +1 -1
  268. package/dist/esm/embed/distribution.d.ts +4 -0
  269. package/dist/esm/embed/distribution.js +11 -0
  270. package/dist/esm/embed/distribution.js.map +1 -0
  271. package/dist/esm/embed/langfit.d.ts +2 -5
  272. package/dist/esm/embed/langfit.js +13 -13
  273. package/dist/esm/embed/langfit.js.map +1 -1
  274. package/dist/esm/embed/query.d.ts +6 -13
  275. package/dist/esm/embed/query.js +23 -84
  276. package/dist/esm/embed/query.js.map +1 -1
  277. package/dist/esm/embed/static.js +6 -1
  278. package/dist/esm/embed/static.js.map +1 -1
  279. package/dist/esm/embed/types.d.ts +1 -0
  280. package/dist/esm/embed/types.js +5 -3
  281. package/dist/esm/embed/types.js.map +1 -1
  282. package/dist/esm/errors.d.ts +1 -1
  283. package/dist/esm/errors.js.map +1 -1
  284. package/dist/esm/features/embed.js +22 -10
  285. package/dist/esm/features/embed.js.map +1 -1
  286. package/dist/esm/features/index.d.ts +1 -1
  287. package/dist/esm/features/index.js +1 -1
  288. package/dist/esm/features/index.js.map +1 -1
  289. package/dist/esm/features/links.d.ts +7 -2
  290. package/dist/esm/features/links.js +124 -97
  291. package/dist/esm/features/links.js.map +1 -1
  292. package/dist/esm/features/rank.js +15 -8
  293. package/dist/esm/features/rank.js.map +1 -1
  294. package/dist/esm/features/sections.js +20 -7
  295. package/dist/esm/features/sections.js.map +1 -1
  296. package/dist/esm/features/tags.js +19 -18
  297. package/dist/esm/features/tags.js.map +1 -1
  298. package/dist/esm/features/types.d.ts +9 -5
  299. package/dist/esm/features/types.js +1 -1
  300. package/dist/esm/features/types.js.map +1 -1
  301. package/dist/esm/graph/traverse.d.ts +2 -2
  302. package/dist/esm/graph/traverse.js +26 -23
  303. package/dist/esm/graph/traverse.js.map +1 -1
  304. package/dist/esm/index.d.ts +3 -2
  305. package/dist/esm/index.js +1 -1
  306. package/dist/esm/index.js.map +1 -1
  307. package/dist/esm/lib/guarded-tick.d.ts +1 -0
  308. package/dist/esm/lib/guarded-tick.js +8 -0
  309. package/dist/esm/lib/guarded-tick.js.map +1 -0
  310. package/dist/esm/output/column-hint.d.ts +1 -2
  311. package/dist/esm/output/column-hint.js +3 -2
  312. package/dist/esm/output/column-hint.js.map +1 -1
  313. package/dist/esm/output/output.d.ts +2 -1
  314. package/dist/esm/output/output.js +27 -23
  315. package/dist/esm/output/output.js.map +1 -1
  316. package/dist/esm/scan/frontmatter.js +1 -1
  317. package/dist/esm/scan/frontmatter.js.map +1 -1
  318. package/dist/esm/scan/index.js +1 -1
  319. package/dist/esm/scan/index.js.map +1 -1
  320. package/dist/esm/scan/reparse.d.ts +10 -0
  321. package/dist/esm/scan/reparse.js +30 -0
  322. package/dist/esm/scan/reparse.js.map +1 -0
  323. package/dist/esm/store/duckdb/batch.d.ts +16 -0
  324. package/dist/esm/store/duckdb/batch.js +96 -0
  325. package/dist/esm/store/duckdb/batch.js.map +1 -0
  326. package/dist/esm/store/duckdb/connection.d.ts +5 -0
  327. package/dist/esm/store/duckdb/connection.js +97 -0
  328. package/dist/esm/store/duckdb/connection.js.map +1 -0
  329. package/dist/esm/store/duckdb/lexical.d.ts +10 -0
  330. package/dist/esm/store/duckdb/lexical.js +163 -0
  331. package/dist/esm/store/duckdb/lexical.js.map +1 -0
  332. package/dist/esm/store/duckdb/native.d.ts +3 -0
  333. package/dist/esm/store/duckdb/native.js +96 -0
  334. package/dist/esm/store/duckdb/native.js.map +1 -0
  335. package/dist/esm/store/duckdb/open.d.ts +12 -0
  336. package/dist/esm/store/duckdb/open.js +127 -0
  337. package/dist/esm/store/duckdb/open.js.map +1 -0
  338. package/dist/esm/store/duckdb/reconcile.d.ts +6 -0
  339. package/dist/esm/store/duckdb/reconcile.js +141 -0
  340. package/dist/esm/store/duckdb/reconcile.js.map +1 -0
  341. package/dist/esm/store/duckdb/sql-functions.d.ts +2 -0
  342. package/dist/esm/store/duckdb/sql-functions.js +56 -0
  343. package/dist/esm/store/duckdb/sql-functions.js.map +1 -0
  344. package/dist/esm/store/duckdb/store.d.ts +5 -0
  345. package/dist/esm/store/duckdb/store.js +104 -0
  346. package/dist/esm/store/duckdb/store.js.map +1 -0
  347. package/dist/esm/store/duckdb/vectors.d.ts +9 -0
  348. package/dist/esm/store/duckdb/vectors.js +171 -0
  349. package/dist/esm/store/duckdb/vectors.js.map +1 -0
  350. package/dist/esm/store/index.d.ts +7 -0
  351. package/dist/esm/store/index.js +35 -0
  352. package/dist/esm/store/index.js.map +1 -0
  353. package/dist/esm/store/meta.d.ts +3 -0
  354. package/dist/esm/store/meta.js +17 -0
  355. package/dist/esm/store/meta.js.map +1 -0
  356. package/dist/esm/store/shared.d.ts +5 -0
  357. package/dist/esm/store/shared.js +24 -0
  358. package/dist/esm/store/shared.js.map +1 -0
  359. package/dist/esm/store/sql-functions.d.ts +2 -0
  360. package/dist/esm/store/sql-functions.js +29 -0
  361. package/dist/esm/store/sql-functions.js.map +1 -0
  362. package/dist/esm/store/sqlite/connection.d.ts +3 -0
  363. package/dist/esm/store/sqlite/connection.js +50 -0
  364. package/dist/esm/store/sqlite/connection.js.map +1 -0
  365. package/dist/esm/store/sqlite/lexical.d.ts +2 -0
  366. package/dist/esm/store/sqlite/lexical.js +21 -0
  367. package/dist/esm/store/sqlite/lexical.js.map +1 -0
  368. package/dist/esm/{db → store/sqlite}/open.d.ts +5 -5
  369. package/dist/esm/store/sqlite/open.js +193 -0
  370. package/dist/esm/store/sqlite/open.js.map +1 -0
  371. package/dist/esm/store/sqlite/reconcile.d.ts +10 -0
  372. package/dist/esm/{db → store/sqlite}/reconcile.js +101 -99
  373. package/dist/esm/store/sqlite/reconcile.js.map +1 -0
  374. package/dist/esm/store/sqlite/sql-functions.js +23 -0
  375. package/dist/esm/store/sqlite/sql-functions.js.map +1 -0
  376. package/dist/esm/store/sqlite/store.d.ts +5 -0
  377. package/dist/esm/store/sqlite/store.js +82 -0
  378. package/dist/esm/store/sqlite/store.js.map +1 -0
  379. package/dist/esm/store/sqlite/vectors.d.ts +13 -0
  380. package/dist/esm/store/sqlite/vectors.js +90 -0
  381. package/dist/esm/store/sqlite/vectors.js.map +1 -0
  382. package/dist/esm/store/transaction.d.ts +5 -0
  383. package/dist/esm/store/transaction.js +57 -0
  384. package/dist/esm/store/transaction.js.map +1 -0
  385. package/dist/esm/store/types.d.ts +91 -0
  386. package/dist/esm/store/types.js +11 -0
  387. package/dist/esm/store/types.js.map +1 -0
  388. package/dist/esm/watch.d.ts +3 -0
  389. package/dist/esm/watch.js +39 -22
  390. package/dist/esm/watch.js.map +1 -1
  391. package/package.json +3 -1
  392. package/schema.json +5 -0
  393. package/skills/sense/EXAMPLES.md +3 -3
  394. package/skills/sense/SKILL.md +16 -14
  395. package/skills/sense-bases/EXAMPLES.md +2 -2
  396. package/skills/sense-bases/SKILL.md +6 -6
  397. package/skills/sense-setup/EXAMPLES.md +1 -1
  398. package/skills/sense-setup/SKILL.md +5 -4
  399. package/dist/cjs/db/index.d.cts +0 -4
  400. package/dist/cjs/db/index.d.ts +0 -4
  401. package/dist/cjs/db/index.js +0 -42
  402. package/dist/cjs/db/index.js.map +0 -1
  403. package/dist/cjs/db/open.js +0 -230
  404. package/dist/cjs/db/open.js.map +0 -1
  405. package/dist/cjs/db/reconcile.d.cts +0 -10
  406. package/dist/cjs/db/reconcile.d.ts +0 -10
  407. package/dist/cjs/db/reconcile.js +0 -579
  408. package/dist/cjs/db/reconcile.js.map +0 -1
  409. package/dist/cjs/db/shared.d.cts +0 -5
  410. package/dist/cjs/db/shared.d.ts +0 -5
  411. package/dist/cjs/db/shared.js +0 -45
  412. package/dist/cjs/db/shared.js.map +0 -1
  413. package/dist/cjs/db/sql-functions.js +0 -64
  414. package/dist/cjs/db/sql-functions.js.map +0 -1
  415. package/dist/esm/db/index.d.ts +0 -4
  416. package/dist/esm/db/index.js +0 -5
  417. package/dist/esm/db/index.js.map +0 -1
  418. package/dist/esm/db/open.js +0 -165
  419. package/dist/esm/db/open.js.map +0 -1
  420. package/dist/esm/db/reconcile.d.ts +0 -10
  421. package/dist/esm/db/reconcile.js.map +0 -1
  422. package/dist/esm/db/shared.d.ts +0 -5
  423. package/dist/esm/db/shared.js +0 -20
  424. package/dist/esm/db/shared.js.map +0 -1
  425. package/dist/esm/db/sql-functions.js +0 -47
  426. package/dist/esm/db/sql-functions.js.map +0 -1
  427. /package/dist/cjs/{db → store/sqlite}/sql-functions.d.cts +0 -0
  428. /package/dist/cjs/{db → store/sqlite}/sql-functions.d.ts +0 -0
  429. /package/dist/esm/{db → store/sqlite}/sql-functions.d.ts +0 -0
@@ -17,54 +17,62 @@ export function resolveNote(paths, arg) {
17
17
  const PEEK_LIST_LIMIT = 20;
18
18
  // peek: everything about one note except its prose -- frontmatter, outline with line
19
19
  // ranges + token estimates (so the follow-up Read is a range, not the file), links both ways.
20
- export function peek(db, cfg, pathArg, overrides = {}) {
21
- var _row__parse_error, _row__size;
22
- var _db_prepare_get;
23
- const paths = db.prepare('SELECT "path" FROM frontmatter').all().map((r)=>r.path);
24
- const path = resolveNote(paths, pathArg);
25
- const row = db.prepare('SELECT * FROM frontmatter WHERE "path" = ?').get(path);
26
- const parseError = (_row__parse_error = row._parse_error) !== null && _row__parse_error !== void 0 ? _row__parse_error : null;
27
- const frontmatter = {};
28
- for (const [key, value] of Object.entries(row)){
29
- if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;
30
- }
31
- const sectionsTotal = featureEnabled(cfg, 'sections') ? db.prepare('SELECT COUNT(*) AS n FROM sections WHERE "path" = ?').get(path).n : 0;
32
- const sections = featureEnabled(cfg, 'sections') ? db.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE "path" = ? ORDER BY idx LIMIT ?').all(path, PEEK_LIST_LIMIT) : [];
33
- let outbound = [];
34
- let backlinks = [];
35
- let unresolved = [];
36
- let backlinksTotal = 0;
37
- const _allowed = scopedPaths(db, cfg, overrides);
38
- if (featureEnabled(cfg, 'links')) {
39
- const out = db.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != src) ORDER BY target').all(path);
40
- outbound = [
41
- ...new Set(out.filter((l)=>l.dst !== null).map((l)=>l.dst))
42
- ];
43
- unresolved = out.filter((l)=>l.dst === null).map((l)=>l.target);
44
- backlinksTotal = db.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst').get(path).n;
45
- backlinks = db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?').all(path, PEEK_LIST_LIMIT).map((r)=>r.src);
46
- }
47
- // content.text is the stripped body already computed at reconcile time (no extra file read),
48
- // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.
49
- const body = (_db_prepare_get = db.prepare('SELECT text FROM content WHERE "path" = ?').get(path)) === null || _db_prepare_get === void 0 ? void 0 : _db_prepare_get.text;
50
- const byteTokens = Math.ceil(((_row__size = row._size) !== null && _row__size !== void 0 ? _row__size : 0) / 4);
51
- const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;
52
- return {
53
- path,
54
- tokens,
55
- frontmatter,
56
- parseError,
57
- sections,
58
- outbound: outbound.slice(0, PEEK_LIST_LIMIT),
59
- backlinks,
60
- unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),
61
- sectionsTotal,
62
- outboundTotal: outbound.length,
63
- backlinksTotal,
64
- unresolvedTotal: unresolved.length,
65
- off: [
66
- 'sections',
67
- 'links'
68
- ].filter((name)=>!featureEnabled(cfg, name))
69
- };
20
+ export async function peek(store, cfg, pathArg, overrides = {}) {
21
+ return store.transaction(async ()=>{
22
+ var _row__parse_error, _row__size;
23
+ var _this;
24
+ const pathsStmt = await store.prepare('SELECT "path" FROM frontmatter');
25
+ const paths = (await pathsStmt.all()).map((r)=>r.path);
26
+ const path = resolveNote(paths, pathArg);
27
+ const rowStmt = await store.prepare('SELECT * FROM frontmatter WHERE "path" = ?');
28
+ const row = await rowStmt.get(path);
29
+ const parseError = (_row__parse_error = row._parse_error) !== null && _row__parse_error !== void 0 ? _row__parse_error : null;
30
+ const frontmatter = {};
31
+ for (const [key, value] of Object.entries(row)){
32
+ if (!INTERNAL_COLUMNS.has(key) && value !== null) frontmatter[key] = value;
33
+ }
34
+ let sectionsTotal = 0;
35
+ let sections = [];
36
+ if (featureEnabled(cfg, 'sections')) {
37
+ sectionsTotal = (await (await store.prepare('SELECT COUNT(*) AS n FROM sections WHERE "path" = ?')).get(path)).n;
38
+ sections = await (await store.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE "path" = ? ORDER BY idx LIMIT ?')).all(path, PEEK_LIST_LIMIT);
39
+ }
40
+ let outbound = [];
41
+ let backlinks = [];
42
+ let unresolved = [];
43
+ let backlinksTotal = 0;
44
+ await scopedPaths(store, cfg, overrides); // validates --preset/--where/--include/--exclude even though peek's own output isn't scope-filtered
45
+ if (featureEnabled(cfg, 'links')) {
46
+ const out = await (await store.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != src) ORDER BY target')).all(path);
47
+ outbound = [
48
+ ...new Set(out.filter((l)=>l.dst !== null).map((l)=>l.dst))
49
+ ];
50
+ unresolved = out.filter((l)=>l.dst === null).map((l)=>l.target);
51
+ backlinksTotal = (await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst')).get(path)).n;
52
+ backlinks = (await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?')).all(path, PEEK_LIST_LIMIT)).map((r)=>r.src);
53
+ }
54
+ // content.text is the stripped body already computed at reconcile time (no extra file read),
55
+ // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.
56
+ const body = (_this = await (await store.prepare('SELECT text FROM content WHERE "path" = ?')).get(path)) === null || _this === void 0 ? void 0 : _this.text;
57
+ const byteTokens = Math.ceil(((_row__size = row._size) !== null && _row__size !== void 0 ? _row__size : 0) / 4);
58
+ const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;
59
+ return {
60
+ path,
61
+ tokens,
62
+ frontmatter,
63
+ parseError,
64
+ sections,
65
+ outbound: outbound.slice(0, PEEK_LIST_LIMIT),
66
+ backlinks,
67
+ unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),
68
+ sectionsTotal,
69
+ outboundTotal: outbound.length,
70
+ backlinksTotal,
71
+ unresolvedTotal: unresolved.length,
72
+ off: [
73
+ 'sections',
74
+ 'links'
75
+ ].filter((name)=>!featureEnabled(cfg, name))
76
+ };
77
+ });
70
78
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/peek.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { estimateTokens } from '../chunk/index.ts';\nimport type { FeatureName, ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { featureEnabled } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Row } from '../output/output.ts';\nimport { INTERNAL_COLUMNS, scopedPaths } from './scope.ts';\n\n// Note resolution shared by peek and path: an exact path, or a unique basename (case\n// insensitive, .md stripped).\nexport function resolveNote(paths: string[], arg: string): string {\n const exact = paths.find((p) => p === arg);\n if (exact) return exact;\n const base = posix.basename(arg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${arg}\" is ambiguous: ${matches.join(', ')}`);\n throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${arg}\"`);\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n // Set when the note's frontmatter was refused, so an empty frontmatter block reads as \"did\n // not parse\" rather than \"has none\". Same reason `_parse_error` sits in the row.\n parseError: string | null;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks (or a note thousands of\n // headings), and peek's whole point is bounded output. Query the sections/links tables\n // directly for the full list.\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n // Bounded k-hop expansion beyond the immediate ring already shown by outbound/backlinks\n // (depth starts at 2).\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LIST_LIMIT = 20;\n\n// peek: 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: ResolvedConfig, pathArg: string, overrides: SearchOverrides = {}): Peek {\n const paths = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const row = db.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?').get(path) as Row;\n const parseError = (row._parse_error as string | null) ?? null;\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 sectionsTotal = featureEnabled(cfg, 'sections') ? (db.prepare('SELECT COUNT(*) AS n FROM sections WHERE \"path\" = ?').get(path) as { n: number }).n : 0;\n const sections = featureEnabled(cfg, 'sections') ? (db.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx LIMIT ?').all(path, PEEK_LIST_LIMIT) as Row[]) : [];\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n const _allowed = scopedPaths(db, cfg, overrides);\n if (featureEnabled(cfg, 'links')) {\n const out = db.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != 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 = ? AND src != dst').get(path) as { n: number }).n;\n backlinks = (db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?').all(path, PEEK_LIST_LIMIT) as Array<{ src: string }>).map((r) => r.src);\n }\n\n // content.text is the stripped body already computed at reconcile time (no extra file read),\n // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.\n const body = (db.prepare('SELECT text FROM content WHERE \"path\" = ?').get(path) as { text: string } | undefined)?.text;\n const byteTokens = Math.ceil(((row._size as number) ?? 0) / 4);\n const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;\n\n return {\n path,\n tokens,\n frontmatter,\n parseError,\n sections,\n outbound: outbound.slice(0, PEEK_LIST_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),\n sectionsTotal,\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":["posix","estimateTokens","featureEnabled","SenseError","INTERNAL_COLUMNS","scopedPaths","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_LIST_LIMIT","peek","db","cfg","pathArg","overrides","row","prepare","all","map","r","path","get","parseError","_parse_error","frontmatter","key","value","Object","entries","has","sectionsTotal","n","sections","outbound","backlinks","unresolved","backlinksTotal","_allowed","out","Set","l","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","undefined","max","slice","outboundTotal","unresolvedTotal","off","name"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/peek.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport { estimateTokens } from '../chunk/index.ts';\nimport type { FeatureName, ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { featureEnabled } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Row } from '../output/output.ts';\nimport type { Store } from '../store/types.ts';\nimport { INTERNAL_COLUMNS, scopedPaths } from './scope.ts';\n\n// Note resolution shared by peek and path: an exact path, or a unique basename (case\n// insensitive, .md stripped).\nexport function resolveNote(paths: string[], arg: string): string {\n const exact = paths.find((p) => p === arg);\n if (exact) return exact;\n const base = posix.basename(arg).replace(/\\.md$/i, '').toLowerCase();\n const matches = paths.filter((p) => posix.basename(p).replace(/\\.md$/i, '').toLowerCase() === base);\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) throw new SenseError('NOTE_AMBIGUOUS', `\"${arg}\" is ambiguous: ${matches.join(', ')}`);\n throw new SenseError('NOTE_NOT_FOUND', `no note matches \"${arg}\"`);\n}\n\nexport interface Peek {\n path: string;\n tokens: number;\n frontmatter: Row;\n // Set when the note's frontmatter was refused, so an empty frontmatter block reads as \"did\n // not parse\" rather than \"has none\". Same reason `_parse_error` sits in the row.\n parseError: string | null;\n sections: Row[];\n outbound: string[];\n backlinks: string[];\n unresolved: string[];\n // Totals before truncation: a hub can have thousands of backlinks (or a note thousands of\n // headings), and peek's whole point is bounded output. Query the sections/links tables\n // directly for the full list.\n sectionsTotal: number;\n outboundTotal: number;\n backlinksTotal: number;\n unresolvedTotal: number;\n // Bounded k-hop expansion beyond the immediate ring already shown by outbound/backlinks\n // (depth starts at 2).\n off: FeatureName[]; // disabled features whose blocks are omitted (not empty)\n}\n\nconst PEEK_LIST_LIMIT = 20;\n\n// peek: 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 async function peek(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides = {}): Promise<Peek> {\n return store.transaction(async () => {\n const pathsStmt = await store.prepare('SELECT \"path\" FROM frontmatter');\n const paths = ((await pathsStmt.all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const rowStmt = await store.prepare('SELECT * FROM frontmatter WHERE \"path\" = ?');\n const row = (await rowStmt.get(path)) as Row;\n const parseError = (row._parse_error as string | null) ?? null;\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 let sectionsTotal = 0;\n let sections: Row[] = [];\n if (featureEnabled(cfg, 'sections')) {\n sectionsTotal = ((await (await store.prepare('SELECT COUNT(*) AS n FROM sections WHERE \"path\" = ?')).get(path)) as { n: number }).n;\n sections = (await (await store.prepare('SELECT level, heading, start_line, end_line, tokens FROM sections WHERE \"path\" = ? ORDER BY idx LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Row[];\n }\n\n let outbound: string[] = [];\n let backlinks: string[] = [];\n let unresolved: string[] = [];\n let backlinksTotal = 0;\n await scopedPaths(store, cfg, overrides); // validates --preset/--where/--include/--exclude even though peek's own output isn't scope-filtered\n if (featureEnabled(cfg, 'links')) {\n const out = (await (await store.prepare('SELECT target, dst FROM links WHERE src = ? AND (dst IS NULL OR dst != 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 = ((await (await store.prepare('SELECT COUNT(DISTINCT src) AS n FROM links WHERE dst = ? AND src != dst')).get(path)) as { n: number }).n;\n backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst ORDER BY src LIMIT ?')).all(path, PEEK_LIST_LIMIT)) as Array<{ src: string }>).map((r) => r.src);\n }\n\n // content.text is the stripped body already computed at reconcile time (no extra file read),\n // floored by the byte/4 estimate so stripped-away syntax and frontmatter can't vanish from the price.\n const body = ((await (await store.prepare('SELECT text FROM content WHERE \"path\" = ?')).get(path)) as { text: string } | undefined)?.text;\n const byteTokens = Math.ceil(((row._size as number) ?? 0) / 4);\n const tokens = body !== undefined ? Math.max(Math.ceil(estimateTokens(body)), byteTokens) : byteTokens;\n\n return {\n path,\n tokens,\n frontmatter,\n parseError,\n sections,\n outbound: outbound.slice(0, PEEK_LIST_LIMIT),\n backlinks,\n unresolved: unresolved.slice(0, PEEK_LIST_LIMIT),\n sectionsTotal,\n outboundTotal: outbound.length,\n backlinksTotal,\n unresolvedTotal: unresolved.length,\n off: (['sections', 'links'] as FeatureName[]).filter((name) => !featureEnabled(cfg, name)),\n };\n });\n}\n"],"names":["posix","estimateTokens","featureEnabled","SenseError","INTERNAL_COLUMNS","scopedPaths","resolveNote","paths","arg","exact","find","p","base","basename","replace","toLowerCase","matches","filter","length","join","PEEK_LIST_LIMIT","peek","store","cfg","pathArg","overrides","transaction","row","pathsStmt","prepare","all","map","r","path","rowStmt","get","parseError","_parse_error","frontmatter","key","value","Object","entries","has","sectionsTotal","sections","n","outbound","backlinks","unresolved","backlinksTotal","out","Set","l","dst","target","src","body","text","byteTokens","Math","ceil","_size","tokens","undefined","max","slice","outboundTotal","unresolvedTotal","off","name"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import type { DatabaseSync } from 'node:sqlite';
2
1
  import type { ResolvedConfig, SearchOverrides } from '../config/index.js';
3
- export declare function relatedNotes(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{
2
+ import type { Store } from '../store/types.js';
3
+ export declare function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{
4
4
  path: string;
5
5
  similarity: number;
6
6
  }>>;
@@ -6,11 +6,11 @@ import { resolveNote } from './peek.js';
6
6
  import { scopedPaths, scopeHasEmbeddings } from './scope.js';
7
7
  // Notes most similar by cosine, excluding self and everything already linked either way.
8
8
  // Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.
9
- export async function relatedNotes(db, cfg, pathArg, overrides, k) {
10
- const paths = db.prepare('SELECT "path" FROM frontmatter').all().map((r)=>r.path);
9
+ export async function relatedNotes(store, cfg, pathArg, overrides, k) {
10
+ const paths = (await (await store.prepare('SELECT "path" FROM frontmatter')).all()).map((r)=>r.path);
11
11
  const path = resolveNote(paths, pathArg);
12
- const outbound = db.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src').all(path).map((r)=>r.dst);
13
- const backlinks = db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst').all(path).map((r)=>r.src);
12
+ const outbound = (await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)).map((r)=>r.dst);
13
+ const backlinks = (await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)).map((r)=>r.src);
14
14
  const exclude = new Set([
15
15
  path,
16
16
  ...outbound,
@@ -35,15 +35,15 @@ export async function relatedNotes(db, cfg, pathArg, overrides, k) {
35
35
  if (localModelMissing(e)) {
36
36
  throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);
37
37
  }
38
- const allowed = scopedPaths(db, cfg, overrides);
38
+ const allowed = await scopedPaths(store, cfg, overrides);
39
39
  // Top up pending rows before the seed check, or a fresh index reports every note as
40
40
  // having no indexed text until some search has run.
41
- await embedPending(db, cfg, cfg.baseDir);
42
- if (!hasEmbedding(db, path)) {
41
+ await embedPending(store, cfg, cfg.baseDir);
42
+ if (!await hasEmbedding(store, path)) {
43
43
  throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);
44
44
  }
45
- if (!scopeHasEmbeddings(db, cfg, allowed)) return [];
46
- return similarNotes(db, cfg, path, {
45
+ if (!await scopeHasEmbeddings(store, cfg, allowed)) return [];
46
+ return similarNotes(store, cfg, path, {
47
47
  exclude,
48
48
  allowed,
49
49
  k
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { embedPending, hasEmbedding, similarNotes } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths, scopeHasEmbeddings } from './scope.ts';\n\n// Notes most similar by cosine, excluding self and everything already linked either way.\n// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.\nexport async function relatedNotes(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n const paths = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const outbound = (db.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src').all(path) as Array<{ dst: string }>).map((r) => r.dst);\n const backlinks = (db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst').all(path) as Array<{ src: string }>).map((r) => r.src);\n const exclude = new Set([path, ...outbound, ...backlinks]);\n\n // Vectors are the only signal `related` has, so every way of not having them is an error\n // naming the cause. An empty table then means one thing: nothing near in meaning that this\n // note does not already link to, which is a real answer.\n const effective = resolveSearch(cfg, overrides);\n if (!embedEnabled(cfg)) {\n throw new SenseError('EMBED_DISABLED', 'related ranks notes by meaning, and this tree has no embedding model; add an \"embed\" block naming one to sense.config.json (a Hugging Face id fetches automatically at first use; `sense download` prefetches it) -- search works without it, on words and links');\n }\n // search gates on the same signal (see wantsVectors above); reading it here too keeps a\n // preset's declared signals meaning one thing. Without this, an overlapping vectors-on\n // preset's vectors would answer for a scope that declined them.\n if (effective.signals.vectors === undefined) {\n throw new SenseError('PRESET_NOT_SEMANTIC', `preset \"${effective.presetName}\" has no \"vectors\" signal, so this scope has no vectors and related has no other signal; search it instead (words and links), or add \"vectors\" to that preset's signals`);\n }\n // A downloadable HF id proceeds -- embedPending's getProvider call fetches it lazily on\n // consent. Only a local path with missing files can never fetch itself.\n const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path \"${e.model}\" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);\n }\n const allowed = scopedPaths(db, cfg, overrides);\n // Top up pending rows before the seed check, or a fresh index reports every note as\n // having no indexed text until some search has run.\n await embedPending(db, cfg, cfg.baseDir);\n if (!hasEmbedding(db, path)) {\n throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);\n }\n if (!scopeHasEmbeddings(db, cfg, allowed)) return [];\n return similarNotes(db, cfg, path, { exclude, allowed, k });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","embedPending","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","resolveNote","scopedPaths","scopeHasEmbeddings","relatedNotes","db","cfg","pathArg","overrides","k","paths","prepare","all","map","r","path","outbound","dst","backlinks","src","exclude","Set","effective","signals","vectors","undefined","presetName","e","model","allowed","baseDir"],"mappings":"AAEA,SAASA,WAAW,EAAEC,YAAY,EAAEC,aAAa,QAAQ,qBAAqB;AAC9E,SAASC,YAAY,EAAEC,YAAY,EAAEC,YAAY,QAAQ,oBAAoB;AAC7E,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,oBAAoB;AACvE,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,EAAEC,kBAAkB,QAAQ,aAAa;AAE7D,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,eAAeC,aAAaC,EAAgB,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,SAA0B,EAAEC,CAAS;IAC9H,MAAMC,QAAQ,AAACL,GAAGM,OAAO,CAAC,kCAAkCC,GAAG,GAA+BC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IAC/G,MAAMA,OAAOd,YAAYS,OAAOH;IAEhC,MAAMS,WAAW,AAACX,GAAGM,OAAO,CAAC,mFAAmFC,GAAG,CAACG,MAAiCF,GAAG,CAAC,CAACC,IAAMA,EAAEG,GAAG;IACrK,MAAMC,YAAY,AAACb,GAAGM,OAAO,CAAC,+DAA+DC,GAAG,CAACG,MAAiCF,GAAG,CAAC,CAACC,IAAMA,EAAEK,GAAG;IAClJ,MAAMC,UAAU,IAAIC,IAAI;QAACN;WAASC;WAAaE;KAAU;IAEzD,yFAAyF;IACzF,2FAA2F;IAC3F,yDAAyD;IACzD,MAAMI,YAAY5B,cAAcY,KAAKE;IACrC,IAAI,CAACf,aAAaa,MAAM;QACtB,MAAM,IAAIN,WAAW,kBAAkB;IACzC;IACA,wFAAwF;IACxF,uFAAuF;IACvF,gEAAgE;IAChE,IAAIsB,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAIzB,WAAW,uBAAuB,CAAC,QAAQ,EAAEsB,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,wFAAwF;IACxF,wEAAwE;IACxE,MAAMC,IAAInC,YAAYc,MAAM,iDAAiD;IAC7E,IAAIR,kBAAkB6B,IAAI;QACxB,MAAM,IAAI3B,WAAW,uBAAuB,CAAC,2FAA2F,EAAE2B,EAAEC,KAAK,CAAC,aAAa,EAAE7B,gBAAgB,sGAAsG,CAAC;IAC1R;IACA,MAAM8B,UAAU3B,YAAYG,IAAIC,KAAKE;IACrC,oFAAoF;IACpF,oDAAoD;IACpD,MAAMb,aAAaU,IAAIC,KAAKA,IAAIwB,OAAO;IACvC,IAAI,CAAClC,aAAaS,IAAIU,OAAO;QAC3B,MAAM,IAAIf,WAAW,qBAAqB,GAAGe,KAAK,4GAA4G,CAAC;IACjK;IACA,IAAI,CAACZ,mBAAmBE,IAAIC,KAAKuB,UAAU,OAAO,EAAE;IACpD,OAAOhC,aAAaQ,IAAIC,KAAKS,MAAM;QAAEK;QAASS;QAASpB;IAAE;AAC3D"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { embedPending, hasEmbedding, similarNotes } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport type { Store } from '../store/types.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths, scopeHasEmbeddings } from './scope.ts';\n\n// Notes most similar by cosine, excluding self and everything already linked either way.\n// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.\nexport async function relatedNotes(store: Store, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n const paths = ((await (await store.prepare('SELECT \"path\" FROM frontmatter')).all()) as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const outbound = ((await (await store.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src')).all(path)) as Array<{ dst: string }>).map((r) => r.dst);\n const backlinks = ((await (await store.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst')).all(path)) as Array<{ src: string }>).map((r) => r.src);\n const exclude = new Set([path, ...outbound, ...backlinks]);\n\n // Vectors are the only signal `related` has, so every way of not having them is an error\n // naming the cause. An empty table then means one thing: nothing near in meaning that this\n // note does not already link to, which is a real answer.\n const effective = resolveSearch(cfg, overrides);\n if (!embedEnabled(cfg)) {\n throw new SenseError('EMBED_DISABLED', 'related ranks notes by meaning, and this tree has no embedding model; add an \"embed\" block naming one to sense.config.json (a Hugging Face id fetches automatically at first use; `sense download` prefetches it) -- search works without it, on words and links');\n }\n // search gates on the same signal (see wantsVectors above); reading it here too keeps a\n // preset's declared signals meaning one thing. Without this, an overlapping vectors-on\n // preset's vectors would answer for a scope that declined them.\n if (effective.signals.vectors === undefined) {\n throw new SenseError('PRESET_NOT_SEMANTIC', `preset \"${effective.presetName}\" has no \"vectors\" signal, so this scope has no vectors and related has no other signal; search it instead (words and links), or add \"vectors\" to that preset's signals`);\n }\n // A downloadable HF id proceeds -- embedPending's getProvider call fetches it lazily on\n // consent. Only a local path with missing files can never fetch itself.\n const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path \"${e.model}\" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);\n }\n const allowed = await scopedPaths(store, cfg, overrides);\n // Top up pending rows before the seed check, or a fresh index reports every note as\n // having no indexed text until some search has run.\n await embedPending(store, cfg, cfg.baseDir);\n if (!(await hasEmbedding(store, path))) {\n throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);\n }\n if (!(await scopeHasEmbeddings(store, cfg, allowed))) return [];\n return similarNotes(store, cfg, path, { exclude, allowed, k });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","embedPending","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","resolveNote","scopedPaths","scopeHasEmbeddings","relatedNotes","store","cfg","pathArg","overrides","k","paths","prepare","all","map","r","path","outbound","dst","backlinks","src","exclude","Set","effective","signals","vectors","undefined","presetName","e","model","allowed","baseDir"],"mappings":"AACA,SAASA,WAAW,EAAEC,YAAY,EAAEC,aAAa,QAAQ,qBAAqB;AAC9E,SAASC,YAAY,EAAEC,YAAY,EAAEC,YAAY,QAAQ,oBAAoB;AAC7E,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,oBAAoB;AACvE,SAASC,UAAU,QAAQ,eAAe;AAE1C,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,EAAEC,kBAAkB,QAAQ,aAAa;AAE7D,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,eAAeC,aAAaC,KAAY,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,SAA0B,EAAEC,CAAS;IAC1H,MAAMC,QAAQ,AAAE,CAAA,MAAM,AAAC,CAAA,MAAML,MAAMM,OAAO,CAAC,iCAAgC,EAAGC,GAAG,EAAC,EAA+BC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IAClI,MAAMA,OAAOd,YAAYS,OAAOH;IAEhC,MAAMS,WAAW,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMX,MAAMM,OAAO,CAAC,kFAAiF,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACC,IAAMA,EAAEG,GAAG;IACxL,MAAMC,YAAY,AAAE,CAAA,MAAM,AAAC,CAAA,MAAMb,MAAMM,OAAO,CAAC,8DAA6D,EAAGC,GAAG,CAACG,KAAI,EAA8BF,GAAG,CAAC,CAACC,IAAMA,EAAEK,GAAG;IACrK,MAAMC,UAAU,IAAIC,IAAI;QAACN;WAASC;WAAaE;KAAU;IAEzD,yFAAyF;IACzF,2FAA2F;IAC3F,yDAAyD;IACzD,MAAMI,YAAY5B,cAAcY,KAAKE;IACrC,IAAI,CAACf,aAAaa,MAAM;QACtB,MAAM,IAAIN,WAAW,kBAAkB;IACzC;IACA,wFAAwF;IACxF,uFAAuF;IACvF,gEAAgE;IAChE,IAAIsB,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAIzB,WAAW,uBAAuB,CAAC,QAAQ,EAAEsB,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,wFAAwF;IACxF,wEAAwE;IACxE,MAAMC,IAAInC,YAAYc,MAAM,iDAAiD;IAC7E,IAAIR,kBAAkB6B,IAAI;QACxB,MAAM,IAAI3B,WAAW,uBAAuB,CAAC,2FAA2F,EAAE2B,EAAEC,KAAK,CAAC,aAAa,EAAE7B,gBAAgB,sGAAsG,CAAC;IAC1R;IACA,MAAM8B,UAAU,MAAM3B,YAAYG,OAAOC,KAAKE;IAC9C,oFAAoF;IACpF,oDAAoD;IACpD,MAAMb,aAAaU,OAAOC,KAAKA,IAAIwB,OAAO;IAC1C,IAAI,CAAE,MAAMlC,aAAaS,OAAOU,OAAQ;QACtC,MAAM,IAAIf,WAAW,qBAAqB,GAAGe,KAAK,4GAA4G,CAAC;IACjK;IACA,IAAI,CAAE,MAAMZ,mBAAmBE,OAAOC,KAAKuB,UAAW,OAAO,EAAE;IAC/D,OAAOhC,aAAaQ,OAAOC,KAAKS,MAAM;QAAEK;QAASS;QAASpB;IAAE;AAC9D"}
@@ -1,10 +1,10 @@
1
- import type { DatabaseSync } from 'node:sqlite';
2
1
  import type { ResolvedConfig, SearchOverrides } from '../config/index.js';
2
+ import type { Store } from '../store/types.js';
3
3
  export declare const INTERNAL_COLUMNS: Set<string>;
4
4
  export declare function inScope(path: string, include: string[], exclude?: string[]): boolean;
5
- export declare function scopeHasEmbeddings(db: DatabaseSync, cfg: ResolvedConfig, scopedPaths: Set<string>): boolean;
6
- export declare function rawScope(db: DatabaseSync, cfg: ResolvedConfig, overrides: SearchOverrides, allPaths?: string[]): Set<string>;
7
- export declare function narrowByWhere(db: DatabaseSync, paths: Set<string>, where: string | undefined): Set<string>;
8
- export declare function scopedPaths(db: DatabaseSync, cfg: ResolvedConfig, overrides: SearchOverrides): Set<string>;
9
- export declare function materializeScope(db: DatabaseSync, table: string, paths: Set<string>): void;
10
- export declare function setupMapScope(db: DatabaseSync, paths: Set<string>): void;
5
+ export declare function scopeHasEmbeddings(store: Store, cfg: ResolvedConfig, scopedPaths: Set<string>): Promise<boolean>;
6
+ export declare function rawScope(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides, allPaths?: string[]): Promise<Set<string>>;
7
+ export declare function narrowByWhere(store: Store, paths: Set<string>, where: string | undefined): Promise<Set<string>>;
8
+ export declare function scopedPaths(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides): Promise<Set<string>>;
9
+ export declare function materializeScope(store: Store, table: string, paths: Set<string>): Promise<void>;
10
+ export declare function setupMapScope(store: Store, paths: Set<string>): Promise<void>;
@@ -17,29 +17,38 @@ export function inScope(path, include, exclude) {
17
17
  if (exclude === null || exclude === void 0 ? void 0 : exclude.some((g)=>matchesGlob(path, g))) return false;
18
18
  return true;
19
19
  }
20
- export function scopeHasEmbeddings(db, cfg, scopedPaths) {
20
+ export async function scopeHasEmbeddings(store, cfg, scopedPaths) {
21
21
  if (!anyPresetEmbeds(cfg)) return false; // the embeddings table doesn't exist at all in this case
22
- const rows = db.prepare('SELECT DISTINCT "path" FROM embeddings').all();
22
+ const stmt = await store.prepare('SELECT DISTINCT "path" FROM embeddings');
23
+ const rows = await stmt.all();
23
24
  return rows.some((r)=>scopedPaths.has(r.path));
24
25
  }
25
26
  // Scope only (no --where): preset_files for a named preset, JS glob matching for an ad hoc
26
27
  // include/exclude override. Shared by scopedPaths() and search(), which also needs the
27
28
  // pre-where set to size the candidate-pool filter.
28
- export function rawScope(db, cfg, overrides, allPaths) {
29
+ export async function rawScope(store, cfg, overrides, allPaths) {
29
30
  const effective = resolveSearch(cfg, overrides);
30
31
  const { include, exclude } = effective;
31
32
  const adHocScope = overrides.include !== undefined || overrides.exclude !== undefined || overrides.noExclude === true;
32
- if (!adHocScope) return new Set(db.prepare('SELECT "path" FROM preset_files WHERE preset = ?').all(effective.presetName).map((r)=>r.path));
33
- const paths = allPaths !== null && allPaths !== void 0 ? allPaths : db.prepare('SELECT "path" FROM frontmatter').all().map((r)=>r.path);
33
+ if (!adHocScope) {
34
+ const stmt = await store.prepare('SELECT "path" FROM preset_files WHERE preset = ?');
35
+ return new Set((await stmt.all(effective.presetName)).map((r)=>r.path));
36
+ }
37
+ const paths = allPaths !== null && allPaths !== void 0 ? allPaths : await allFrontmatterPaths(store);
34
38
  return new Set(paths.filter((p)=>inScope(p, include, exclude)));
35
39
  }
36
- export function narrowByWhere(db, paths, where) {
40
+ async function allFrontmatterPaths(store) {
41
+ const stmt = await store.prepare('SELECT "path" FROM frontmatter');
42
+ return (await stmt.all()).map((r)=>r.path);
43
+ }
44
+ export async function narrowByWhere(store, paths, where) {
37
45
  if (!where) return paths;
38
46
  let whereRows;
39
47
  try {
40
- whereRows = db.prepare(`SELECT "path" FROM frontmatter f WHERE (${where})`).all();
48
+ const stmt = await store.prepare(`SELECT "path" FROM frontmatter f WHERE (${where})`);
49
+ whereRows = await stmt.all();
41
50
  } catch (err) {
42
- throw columnHint(db, err);
51
+ throw columnHint(await store.docs.columns(), err);
43
52
  }
44
53
  const wherePaths = new Set(whereRows.map((r)=>r.path));
45
54
  return new Set([
@@ -48,19 +57,21 @@ export function narrowByWhere(db, paths, where) {
48
57
  }
49
58
  // The scope resolver for non-search commands (path, peek, map): same coverage rule search()
50
59
  // applies, then narrowed by the resolved `where`.
51
- export function scopedPaths(db, cfg, overrides) {
60
+ export async function scopedPaths(store, cfg, overrides) {
52
61
  const effective = resolveSearch(cfg, overrides);
53
- return narrowByWhere(db, rawScope(db, cfg, overrides), effective.where);
62
+ return narrowByWhere(store, await rawScope(store, cfg, overrides), effective.where);
54
63
  }
55
64
  // Materializes a path set into a named temp table (same shape as traverse.ts's allowed_nodes)
56
65
  // so a query can join/filter against it cheaply instead of binding a parameter per path.
57
- export function materializeScope(db, table, paths) {
58
- db.exec(`DROP TABLE IF EXISTS ${table}`);
59
- db.exec(`CREATE TEMP TABLE ${table} ("path" TEXT PRIMARY KEY)`);
60
- db.prepare(`INSERT INTO ${table} SELECT DISTINCT value FROM json_each(?1)`).run(JSON.stringify([
66
+ export async function materializeScope(store, table, paths) {
67
+ await store.exec(`DROP TABLE IF EXISTS ${table}`);
68
+ await store.exec(`CREATE TEMP TABLE ${table} ("path" TEXT PRIMARY KEY)`);
69
+ await store.runBatch(`INSERT INTO ${table} ("path") VALUES (?)`, [
61
70
  ...paths
62
- ]));
71
+ ].map((p)=>[
72
+ p
73
+ ]));
63
74
  }
64
- export function setupMapScope(db, paths) {
65
- materializeScope(db, '_map_scope', paths);
75
+ export async function setupMapScope(store, paths) {
76
+ await materializeScope(store, '_map_scope', paths);
66
77
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/scope.ts"],"sourcesContent":["import { matchesGlob } from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { anyPresetEmbeds, resolveSearch } from '../config/index.ts';\nimport { columnHint } from '../output/column-hint.ts';\n\nexport const INTERNAL_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_rank', '_parse_error']);\n\n// node:path's matchesGlob is experimental (stable behind an unstable-API flag) as of the\n// engines floor (Node >=22.20); scope filtering only ever needs single-pattern matching, so\n// it's used here in JS rather than running a directory walk in the query path.\nexport function inScope(path: string, include: string[], exclude?: string[]): boolean {\n if (!include.some((g) => matchesGlob(path, g))) return false;\n if (exclude?.some((g) => matchesGlob(path, g))) return false;\n return true;\n}\n\nexport function scopeHasEmbeddings(db: DatabaseSync, cfg: ResolvedConfig, scopedPaths: Set<string>): boolean {\n if (!anyPresetEmbeds(cfg)) return false; // the embeddings table doesn't exist at all in this case\n const rows = db.prepare('SELECT DISTINCT \"path\" FROM embeddings').all() as Array<{ path: string }>;\n return rows.some((r) => scopedPaths.has(r.path));\n}\n\n// Scope only (no --where): preset_files for a named preset, JS glob matching for an ad hoc\n// include/exclude override. Shared by scopedPaths() and search(), which also needs the\n// pre-where set to size the candidate-pool filter.\nexport function rawScope(db: DatabaseSync, cfg: ResolvedConfig, overrides: SearchOverrides, allPaths?: string[]): Set<string> {\n const effective = resolveSearch(cfg, overrides);\n const { include, exclude } = effective;\n const adHocScope = overrides.include !== undefined || overrides.exclude !== undefined || overrides.noExclude === true;\n if (!adHocScope) return new Set((db.prepare('SELECT \"path\" FROM preset_files WHERE preset = ?').all(effective.presetName) as Array<{ path: string }>).map((r) => r.path));\n const paths = allPaths ?? (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n return new Set(paths.filter((p) => inScope(p, include, exclude)));\n}\n\nexport function narrowByWhere(db: DatabaseSync, paths: Set<string>, where: string | undefined): Set<string> {\n if (!where) return paths;\n let whereRows: Array<{ path: string }>;\n try {\n whereRows = db.prepare(`SELECT \"path\" FROM frontmatter f WHERE (${where})`).all() as Array<{ path: string }>;\n } catch (err) {\n throw columnHint(db, err as Error);\n }\n const wherePaths = new Set(whereRows.map((r) => r.path));\n return new Set([...paths].filter((p) => wherePaths.has(p)));\n}\n\n// The scope resolver for non-search commands (path, peek, map): same coverage rule search()\n// applies, then narrowed by the resolved `where`.\nexport function scopedPaths(db: DatabaseSync, cfg: ResolvedConfig, overrides: SearchOverrides): Set<string> {\n const effective = resolveSearch(cfg, overrides);\n return narrowByWhere(db, rawScope(db, cfg, overrides), effective.where);\n}\n\n// Materializes a path set into a named temp table (same shape as traverse.ts's allowed_nodes)\n// so a query can join/filter against it cheaply instead of binding a parameter per path.\nexport function materializeScope(db: DatabaseSync, table: string, paths: Set<string>): void {\n db.exec(`DROP TABLE IF EXISTS ${table}`);\n db.exec(`CREATE TEMP TABLE ${table} (\"path\" TEXT PRIMARY KEY)`);\n db.prepare(`INSERT INTO ${table} SELECT DISTINCT value FROM json_each(?1)`).run(JSON.stringify([...paths]));\n}\n\nexport function setupMapScope(db: DatabaseSync, paths: Set<string>): void {\n materializeScope(db, '_map_scope', paths);\n}\n"],"names":["matchesGlob","anyPresetEmbeds","resolveSearch","columnHint","INTERNAL_COLUMNS","Set","inScope","path","include","exclude","some","g","scopeHasEmbeddings","db","cfg","scopedPaths","rows","prepare","all","r","has","rawScope","overrides","allPaths","effective","adHocScope","undefined","noExclude","presetName","map","paths","filter","p","narrowByWhere","where","whereRows","err","wherePaths","materializeScope","table","exec","run","JSON","stringify","setupMapScope"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/scope.ts"],"sourcesContent":["import { matchesGlob } from 'node:path';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { anyPresetEmbeds, resolveSearch } from '../config/index.ts';\nimport { columnHint } from '../output/column-hint.ts';\nimport type { Store } from '../store/types.ts';\n\nexport const INTERNAL_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_rank', '_parse_error']);\n\n// node:path's matchesGlob is experimental (stable behind an unstable-API flag) as of the\n// engines floor (Node >=22.20); scope filtering only ever needs single-pattern matching, so\n// it's used here in JS rather than running a directory walk in the query path.\nexport function inScope(path: string, include: string[], exclude?: string[]): boolean {\n if (!include.some((g) => matchesGlob(path, g))) return false;\n if (exclude?.some((g) => matchesGlob(path, g))) return false;\n return true;\n}\n\nexport async function scopeHasEmbeddings(store: Store, cfg: ResolvedConfig, scopedPaths: Set<string>): Promise<boolean> {\n if (!anyPresetEmbeds(cfg)) return false; // the embeddings table doesn't exist at all in this case\n const stmt = await store.prepare('SELECT DISTINCT \"path\" FROM embeddings');\n const rows = (await stmt.all()) as Array<{ path: string }>;\n return rows.some((r) => scopedPaths.has(r.path));\n}\n\n// Scope only (no --where): preset_files for a named preset, JS glob matching for an ad hoc\n// include/exclude override. Shared by scopedPaths() and search(), which also needs the\n// pre-where set to size the candidate-pool filter.\nexport async function rawScope(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides, allPaths?: string[]): Promise<Set<string>> {\n const effective = resolveSearch(cfg, overrides);\n const { include, exclude } = effective;\n const adHocScope = overrides.include !== undefined || overrides.exclude !== undefined || overrides.noExclude === true;\n if (!adHocScope) {\n const stmt = await store.prepare('SELECT \"path\" FROM preset_files WHERE preset = ?');\n return new Set(((await stmt.all(effective.presetName)) as Array<{ path: string }>).map((r) => r.path));\n }\n const paths = allPaths ?? (await allFrontmatterPaths(store));\n return new Set(paths.filter((p) => inScope(p, include, exclude)));\n}\n\nasync function allFrontmatterPaths(store: Store): Promise<string[]> {\n const stmt = await store.prepare('SELECT \"path\" FROM frontmatter');\n return ((await stmt.all()) as Array<{ path: string }>).map((r) => r.path);\n}\n\nexport async function narrowByWhere(store: Store, paths: Set<string>, where: string | undefined): Promise<Set<string>> {\n if (!where) return paths;\n let whereRows: Array<{ path: string }>;\n try {\n const stmt = await store.prepare(`SELECT \"path\" FROM frontmatter f WHERE (${where})`);\n whereRows = (await stmt.all()) as Array<{ path: string }>;\n } catch (err) {\n throw columnHint(await store.docs.columns(), err as Error);\n }\n const wherePaths = new Set(whereRows.map((r) => r.path));\n return new Set([...paths].filter((p) => wherePaths.has(p)));\n}\n\n// The scope resolver for non-search commands (path, peek, map): same coverage rule search()\n// applies, then narrowed by the resolved `where`.\nexport async function scopedPaths(store: Store, cfg: ResolvedConfig, overrides: SearchOverrides): Promise<Set<string>> {\n const effective = resolveSearch(cfg, overrides);\n return narrowByWhere(store, await rawScope(store, cfg, overrides), effective.where);\n}\n\n// Materializes a path set into a named temp table (same shape as traverse.ts's allowed_nodes)\n// so a query can join/filter against it cheaply instead of binding a parameter per path.\nexport async function materializeScope(store: Store, table: string, paths: Set<string>): Promise<void> {\n await store.exec(`DROP TABLE IF EXISTS ${table}`);\n await store.exec(`CREATE TEMP TABLE ${table} (\"path\" TEXT PRIMARY KEY)`);\n await store.runBatch(\n `INSERT INTO ${table} (\"path\") VALUES (?)`,\n [...paths].map((p) => [p])\n );\n}\n\nexport async function setupMapScope(store: Store, paths: Set<string>): Promise<void> {\n await materializeScope(store, '_map_scope', paths);\n}\n"],"names":["matchesGlob","anyPresetEmbeds","resolveSearch","columnHint","INTERNAL_COLUMNS","Set","inScope","path","include","exclude","some","g","scopeHasEmbeddings","store","cfg","scopedPaths","stmt","prepare","rows","all","r","has","rawScope","overrides","allPaths","effective","adHocScope","undefined","noExclude","presetName","map","paths","allFrontmatterPaths","filter","p","narrowByWhere","where","whereRows","err","docs","columns","wherePaths","materializeScope","table","exec","runBatch","setupMapScope"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import type { DatabaseSync } from 'node:sqlite';
2
1
  import type { ResolvedConfig } from '../config/index.js';
3
2
  import type { Row } from '../output/output.js';
3
+ import type { Store } from '../store/types.js';
4
4
  export interface SearchOptions {
5
5
  k?: number;
6
6
  where?: string;
@@ -9,4 +9,4 @@ export interface SearchOptions {
9
9
  exclude?: string[];
10
10
  noExclude?: boolean;
11
11
  }
12
- export declare function search(db: DatabaseSync, cfg: ResolvedConfig, terms: string, opts?: SearchOptions): Promise<Row[]>;
12
+ export declare function search(store: Store, cfg: ResolvedConfig, terms: string, opts?: SearchOptions): Promise<Row[]>;
@@ -1,10 +1,9 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { contentTokenize, embedConfig, featureEnabled, resolveSearch } from '../config/index.js';
3
+ import { embedConfig, featureEnabled, resolveSearch } from '../config/index.js';
4
4
  import { localModelMissing, MODEL_FILENAMES } from '../embed/store.js';
5
5
  import { SenseError } from '../errors.js';
6
6
  import { searchError } from '../output/search-error.js';
7
- import { segmentMatch } from '../text/segment.js';
8
7
  import { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.js';
9
8
  import { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.js';
10
9
  // snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc
@@ -97,25 +96,25 @@ function lineNumberAt(text, offset) {
97
96
  for(let i = 0; i < offset; i++)if (text.charCodeAt(i) === 10) line++;
98
97
  return line;
99
98
  }
100
- function lineRangeFor(db, path, line) {
101
- const row = db.prepare('SELECT start_line, end_line FROM sections WHERE "path" = ? AND start_line <= ? AND end_line >= ? ORDER BY start_line DESC LIMIT 1').get(path, line, line);
99
+ async function lineRangeFor(store, path, line) {
100
+ const stmt = await store.prepare('SELECT start_line, end_line FROM sections WHERE "path" = ? AND start_line <= ? AND end_line >= ? ORDER BY start_line DESC LIMIT 1');
101
+ const row = await stmt.get(path, line, line);
102
102
  return row ? `L${row.start_line}-${row.end_line}` : null;
103
103
  }
104
104
  // The declared or defaulted signals compose via RRF; `via` names which ones produced each row.
105
105
  // `opts` arrives already resolved (config.ts:resolveSearch).
106
- export async function search(db, cfg, terms, opts = {}) {
107
- var _hits_get, _chunkLines_get, _chunkSimilarity_get;
106
+ export async function search(store, cfg, terms, opts = {}) {
108
107
  const effective = resolveSearch(cfg, opts);
109
108
  const { k, signals } = effective;
110
- const allPaths = db.prepare('SELECT "path" FROM frontmatter').all().map((r)=>r.path);
111
- const scopePaths = rawScope(db, cfg, opts, allPaths);
109
+ const allPaths = (await (await store.prepare('SELECT "path" FROM frontmatter')).all()).map((r)=>r.path);
110
+ const scopePaths = await rawScope(store, cfg, opts, allPaths);
112
111
  const scopeActive = scopePaths.size < allPaths.length;
113
112
  // The set every candidate pool must be filtered to before truncation: scope narrowed by
114
113
  // --where, the same composition scopedPaths() gives the other commands. Runs the same where
115
114
  // fragment matchSql does, so a bad column gets the same attributed error either way.
116
115
  let allowedPaths;
117
116
  try {
118
- allowedPaths = narrowByWhere(db, scopePaths, effective.where);
117
+ allowedPaths = await narrowByWhere(store, scopePaths, effective.where);
119
118
  } catch (err) {
120
119
  throw searchError(err, terms, effective.where);
121
120
  }
@@ -129,17 +128,9 @@ export async function search(db, cfg, terms, opts = {}) {
129
128
  throw new SenseError('EMBED_MODEL_MISSING', `preset "${effective.presetName}" searches with vectors, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them, or drop "vectors" from that preset's signals to search without them`);
130
129
  }
131
130
  }
132
- const semanticEnabled = wantsVectors && scopeHasEmbeddings(db, cfg, allowedPaths);
133
- // Terms pass to MATCH as written, with one transform: a run of text whose language marks no
134
- // word boundaries becomes a quoted grapheme phrase (and a title:/summary:/text: qualifier
135
- // ahead of it retargets its _seg column), matching how it was indexed. Text that already
136
- // carries boundaries comes through untouched, so this is a no-op for the languages that
137
- // never needed it. Gated on the same predicate that decided whether the sidecars were
138
- // populated (contentTokenize(cfg) === undefined), so index and query can't disagree. Invalid
139
- // syntax still errors rather than being rewritten.
140
- // --where
141
- // applies inside the candidate query (a post-filter would drop matches ranked past the pool)
142
- // and again on the final select, for link-derived rows.
131
+ const semanticEnabled = wantsVectors && await scopeHasEmbeddings(store, cfg, allowedPaths);
132
+ // --where applies inside the candidate query (a post-filter would drop matches ranked past
133
+ // the pool) and again on the final select, for link-derived rows.
143
134
  const scope = effective.where;
144
135
  const whereJoin = scope ? `JOIN frontmatter f ON f."path" = content.path` : '';
145
136
  const whereCond = scope ? `AND (${scope})` : '';
@@ -147,16 +138,16 @@ export async function search(db, cfg, terms, opts = {}) {
147
138
  // after -- otherwise scoped notes ranking below the global top-`fetch` never reach the
148
139
  // filter. A join against a temp table, not a bound parameter list: real scopes run to
149
140
  // thousands of paths, past SQLITE_MAX_VARIABLE_NUMBER on older builds.
150
- if (scopeActive) materializeScope(db, '_search_scope', scopePaths);
141
+ if (scopeActive) await materializeScope(store, '_search_scope', scopePaths);
151
142
  const scopeCond = scopeActive ? `AND content.path IN (SELECT "path" FROM _search_scope)` : '';
152
- const query = contentTokenize(cfg) === undefined ? segmentMatch(terms) : terms;
153
143
  const candidates = new Map();
154
144
  let matchRows = [];
155
- // A query that rewrites to nothing (e.g. only unspaced-script punctuation) has no
156
- // searchable words: zero lexical rows, and the other signals still compose.
157
- if (signals.words !== undefined && query.trim() !== '') {
145
+ // A query that is only whitespace has no searchable words: zero lexical rows, and the other
146
+ // signals still compose. Any store-specific rewrite of `terms` (sqlite's grapheme phrase for
147
+ // unspaced scripts, duckdb's word/substring split) happens inside store.lexical.query itself.
148
+ if (signals.words !== undefined && terms.trim() !== '') {
158
149
  try {
159
- matchRows = wordsCandidates(db, candidates, query, whereJoin, whereCond, scopeCond, fetch, signals.words);
150
+ matchRows = await wordsCandidates(store, candidates, terms, whereJoin, whereCond, scopeCond, fetch, signals.words);
160
151
  } catch (err) {
161
152
  throw searchError(err, terms, scope);
162
153
  }
@@ -168,16 +159,31 @@ export async function search(db, cfg, terms, opts = {}) {
168
159
  // hit === null here means the bound suppressed snippet(), not "no match" -- distinguish
169
160
  // from via='link' rows (never in matchRows, so absent from this set) below.
170
161
  const oversized = new Set(matchRows.filter((r)=>r.hit === null).map((r)=>r.path));
171
- if (signals.links !== undefined) linksCandidates(db, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links);
162
+ if (signals.links !== undefined) await linksCandidates(store, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links);
172
163
  let chunkLines = new Map();
173
164
  let chunkSimilarity = new Map();
174
165
  if (semanticEnabled) {
175
- ({ chunkLines, chunkSimilarity } = await vectorsCandidates(db, cfg, candidates, terms, fetch, allowedPaths, signals.vectors));
166
+ ({ chunkLines, chunkSimilarity } = await vectorsCandidates(store, cfg, candidates, terms, fetch, allowedPaths, signals.vectors));
167
+ }
168
+ await store.exec('DROP TABLE IF EXISTS _search');
169
+ // DOUBLE, not REAL: sqlite's REAL is an 8-byte double but duckdb's is a 4-byte float, and the
170
+ // rounded score/similarity are printed at full precision in rows.
171
+ await store.exec('CREATE TEMP TABLE _search ("path" TEXT PRIMARY KEY, score DOUBLE, via TEXT, hit TEXT, lines TEXT, similarity DOUBLE)');
172
+ if (candidates.size > 0) {
173
+ await store.runBatch('INSERT INTO _search ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)', [
174
+ ...candidates
175
+ ].map(([path, c])=>{
176
+ var _hits_get, _chunkLines_get, _chunkSimilarity_get;
177
+ return [
178
+ path,
179
+ c.score,
180
+ c.via,
181
+ (_hits_get = hits.get(path)) !== null && _hits_get !== void 0 ? _hits_get : null,
182
+ (_chunkLines_get = chunkLines.get(path)) !== null && _chunkLines_get !== void 0 ? _chunkLines_get : null,
183
+ (_chunkSimilarity_get = chunkSimilarity.get(path)) !== null && _chunkSimilarity_get !== void 0 ? _chunkSimilarity_get : null
184
+ ];
185
+ }));
176
186
  }
177
- db.exec('DROP TABLE IF EXISTS _search');
178
- db.exec('CREATE TEMP TABLE _search ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');
179
- const insert = db.prepare('INSERT INTO _search ("path", score, via, hit, lines, similarity) VALUES (?, ?, ?, ?, ?, ?)');
180
- for (const [path, c] of candidates)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);
181
187
  // All three candidate paths (match, link, vector) already filtered to scope+where before
182
188
  // reaching _search; this reapplies --where anyway since the join to frontmatter is already
183
189
  // needed for the path column.
@@ -185,9 +191,10 @@ export async function search(db, cfg, terms, opts = {}) {
185
191
  // lines is always present now: semantic rows carry their chunk's range, oversized-doc
186
192
  // lexical rows gain one below, everything else stays null. similarity stays semantic-only.
187
193
  const similarityCol = semanticEnabled ? ', _search.similarity' : '';
188
- const rows = db.prepare(`SELECT f."path" AS path, content.title, content.summary, _search.hit, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}
194
+ const selectStmt = await store.prepare(`SELECT f."path" AS path, content.title, content.summary, _search.hit, _search.via, round(_search.score, 4) AS score, _search.lines${similarityCol}
189
195
  FROM _search JOIN frontmatter f ON f."path" = _search."path" JOIN content ON content.path = _search."path"
190
- ${where} ORDER BY _search.score DESC LIMIT ?`).all(k);
196
+ ${where} ORDER BY _search.score DESC LIMIT ?`);
197
+ const rows = await selectStmt.all(k);
191
198
  if (oversized.size > 0) {
192
199
  const bareTerms = extractBareTerms(terms);
193
200
  for (const row of rows){
@@ -200,7 +207,7 @@ export async function search(db, cfg, terms, opts = {}) {
200
207
  }
201
208
  const { excerpt, offset } = computeExcerpt(text, bareTerms);
202
209
  row.hit = excerpt;
203
- if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? lineRangeFor(db, row.path, lineNumberAt(text, offset)) : null;
210
+ if (row.lines == null) row.lines = featureEnabled(cfg, 'sections') ? await lineRangeFor(store, row.path, lineNumberAt(text, offset)) : null;
204
211
  }
205
212
  }
206
213
  return rows;