sensemaking 0.15.4 → 0.17.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 (435) hide show
  1. package/README.md +33 -5
  2. package/dist/cjs/chunk/extract.d.cts +2 -0
  3. package/dist/cjs/chunk/extract.d.ts +2 -0
  4. package/dist/cjs/chunk/extract.js +139 -0
  5. package/dist/cjs/chunk/extract.js.map +1 -0
  6. package/dist/cjs/chunk/group.d.cts +4 -0
  7. package/dist/cjs/chunk/group.d.ts +4 -0
  8. package/dist/cjs/chunk/group.js +489 -0
  9. package/dist/cjs/chunk/group.js.map +1 -0
  10. package/dist/cjs/chunk/index.d.cts +8 -0
  11. package/dist/cjs/chunk/index.d.ts +8 -0
  12. package/dist/cjs/chunk/index.js +44 -0
  13. package/dist/cjs/chunk/index.js.map +1 -0
  14. package/dist/cjs/chunk/parse.d.cts +2 -0
  15. package/dist/cjs/chunk/parse.d.ts +2 -0
  16. package/dist/cjs/chunk/parse.js +71 -0
  17. package/dist/cjs/chunk/parse.js.map +1 -0
  18. package/dist/cjs/chunk/types.d.cts +19 -0
  19. package/dist/cjs/chunk/types.d.ts +19 -0
  20. package/dist/cjs/chunk/types.js +5 -0
  21. package/dist/cjs/chunk/types.js.map +1 -0
  22. package/dist/cjs/chunk/version.d.cts +1 -0
  23. package/dist/cjs/chunk/version.d.ts +1 -0
  24. package/dist/cjs/chunk/version.js +14 -0
  25. package/dist/cjs/chunk/version.js.map +1 -0
  26. package/dist/cjs/cli/download.js +12 -11
  27. package/dist/cjs/cli/download.js.map +1 -1
  28. package/dist/cjs/cli/index.d.cts +1 -1
  29. package/dist/cjs/cli/index.d.ts +1 -1
  30. package/dist/cjs/cli/index.js +1 -1
  31. package/dist/cjs/cli/index.js.map +1 -1
  32. package/dist/cjs/cli/init.js +31 -5
  33. package/dist/cjs/cli/init.js.map +1 -1
  34. package/dist/cjs/cli/map.js +1 -1
  35. package/dist/cjs/cli/map.js.map +1 -1
  36. package/dist/cjs/cli/named.js +1 -1
  37. package/dist/cjs/cli/named.js.map +1 -1
  38. package/dist/cjs/cli/path.js +2 -2
  39. package/dist/cjs/cli/path.js.map +1 -1
  40. package/dist/cjs/cli/peek.js +1 -1
  41. package/dist/cjs/cli/peek.js.map +1 -1
  42. package/dist/cjs/cli/related.js +1 -1
  43. package/dist/cjs/cli/related.js.map +1 -1
  44. package/dist/cjs/cli/search.js +1 -1
  45. package/dist/cjs/cli/search.js.map +1 -1
  46. package/dist/cjs/cli/shared.d.cts +1 -1
  47. package/dist/cjs/cli/shared.d.ts +1 -1
  48. package/dist/cjs/cli/shared.js +3 -3
  49. package/dist/cjs/cli/shared.js.map +1 -1
  50. package/dist/cjs/cli/status.js +344 -100
  51. package/dist/cjs/cli/status.js.map +1 -1
  52. package/dist/cjs/commands/map.d.cts +1 -1
  53. package/dist/cjs/commands/map.d.ts +1 -1
  54. package/dist/cjs/commands/map.js.map +1 -1
  55. package/dist/cjs/commands/peek.d.cts +1 -1
  56. package/dist/cjs/commands/peek.d.ts +1 -1
  57. package/dist/cjs/commands/peek.js +13 -6
  58. package/dist/cjs/commands/peek.js.map +1 -1
  59. package/dist/cjs/commands/related.js +17 -13
  60. package/dist/cjs/commands/related.js.map +1 -1
  61. package/dist/cjs/commands/scope.js +1 -1
  62. package/dist/cjs/commands/scope.js.map +1 -1
  63. package/dist/cjs/commands/search.d.cts +1 -1
  64. package/dist/cjs/commands/search.d.ts +1 -1
  65. package/dist/cjs/commands/search.js +28 -99
  66. package/dist/cjs/commands/search.js.map +1 -1
  67. package/dist/cjs/commands/signals.d.cts +18 -0
  68. package/dist/cjs/commands/signals.d.ts +18 -0
  69. package/dist/cjs/commands/signals.js +299 -0
  70. package/dist/cjs/commands/signals.js.map +1 -0
  71. package/dist/cjs/commands/status.d.cts +2 -2
  72. package/dist/cjs/commands/status.d.ts +2 -2
  73. package/dist/cjs/commands/status.js +1 -1
  74. package/dist/cjs/commands/status.js.map +1 -1
  75. package/dist/cjs/config/access.d.cts +8 -3
  76. package/dist/cjs/config/access.d.ts +8 -3
  77. package/dist/cjs/config/access.js +35 -17
  78. package/dist/cjs/config/access.js.map +1 -1
  79. package/dist/cjs/config/index.d.cts +5 -1
  80. package/dist/cjs/config/index.d.ts +5 -1
  81. package/dist/cjs/config/index.js +16 -2
  82. package/dist/cjs/config/index.js.map +1 -1
  83. package/dist/cjs/config/load.d.cts +6 -1
  84. package/dist/cjs/config/load.d.ts +6 -1
  85. package/dist/cjs/config/load.js +83 -8
  86. package/dist/cjs/config/load.js.map +1 -1
  87. package/dist/cjs/config/resolve.js +1 -1
  88. package/dist/cjs/config/resolve.js.map +1 -1
  89. package/dist/cjs/config/signals.d.cts +4 -0
  90. package/dist/cjs/config/signals.d.ts +4 -0
  91. package/dist/cjs/config/signals.js +30 -0
  92. package/dist/cjs/config/signals.js.map +1 -0
  93. package/dist/cjs/config/types.d.cts +7 -4
  94. package/dist/cjs/config/types.d.ts +7 -4
  95. package/dist/cjs/config/types.js +1 -1
  96. package/dist/cjs/config/types.js.map +1 -1
  97. package/dist/cjs/config/validate.d.cts +1 -0
  98. package/dist/cjs/config/validate.d.ts +1 -0
  99. package/dist/cjs/config/validate.js +127 -11
  100. package/dist/cjs/config/validate.js.map +1 -1
  101. package/dist/cjs/db/open.d.cts +1 -1
  102. package/dist/cjs/db/open.d.ts +1 -1
  103. package/dist/cjs/db/open.js +9 -4
  104. package/dist/cjs/db/open.js.map +1 -1
  105. package/dist/cjs/db/reconcile.d.cts +1 -0
  106. package/dist/cjs/db/reconcile.d.ts +1 -0
  107. package/dist/cjs/db/reconcile.js +23 -8
  108. package/dist/cjs/db/reconcile.js.map +1 -1
  109. package/dist/cjs/{sql-functions.js → db/sql-functions.js} +1 -1
  110. package/dist/cjs/db/sql-functions.js.map +1 -0
  111. package/dist/cjs/embed/cohere.d.cts +2 -0
  112. package/dist/cjs/embed/cohere.d.ts +2 -0
  113. package/dist/cjs/embed/cohere.js +238 -0
  114. package/dist/cjs/embed/cohere.js.map +1 -0
  115. package/dist/cjs/embed/http.d.cts +2 -0
  116. package/dist/cjs/embed/http.d.ts +2 -0
  117. package/dist/cjs/embed/http.js +258 -0
  118. package/dist/cjs/embed/http.js.map +1 -0
  119. package/dist/cjs/embed/identity.d.cts +13 -0
  120. package/dist/cjs/embed/identity.d.ts +13 -0
  121. package/dist/cjs/embed/identity.js +194 -0
  122. package/dist/cjs/embed/identity.js.map +1 -0
  123. package/dist/cjs/embed/langfit.d.cts +6 -0
  124. package/dist/cjs/embed/langfit.d.ts +6 -0
  125. package/dist/cjs/embed/langfit.js +286 -0
  126. package/dist/cjs/embed/langfit.js.map +1 -0
  127. package/dist/cjs/embed/languages.d.cts +3 -0
  128. package/dist/cjs/embed/languages.d.ts +3 -0
  129. package/dist/cjs/embed/languages.js +98 -0
  130. package/dist/cjs/embed/languages.js.map +1 -0
  131. package/dist/cjs/embed/openai.d.cts +2 -0
  132. package/dist/cjs/embed/openai.d.ts +2 -0
  133. package/dist/cjs/embed/openai.js +268 -0
  134. package/dist/cjs/embed/openai.js.map +1 -0
  135. package/dist/cjs/embed/query.d.cts +21 -0
  136. package/dist/cjs/embed/query.d.ts +21 -0
  137. package/dist/cjs/embed/query.js +610 -0
  138. package/dist/cjs/embed/query.js.map +1 -0
  139. package/dist/cjs/embed/registry.d.cts +3 -0
  140. package/dist/cjs/embed/registry.d.ts +3 -0
  141. package/dist/cjs/embed/registry.js +114 -0
  142. package/dist/cjs/embed/registry.js.map +1 -0
  143. package/dist/cjs/embed/static.d.cts +2 -0
  144. package/dist/cjs/embed/static.d.ts +2 -0
  145. package/dist/cjs/embed/static.js +330 -0
  146. package/dist/cjs/embed/static.js.map +1 -0
  147. package/dist/cjs/embed/store.d.cts +8 -0
  148. package/dist/cjs/embed/store.d.ts +8 -0
  149. package/dist/cjs/embed/store.js +498 -0
  150. package/dist/cjs/embed/store.js.map +1 -0
  151. package/dist/cjs/embed/types.d.cts +8 -0
  152. package/dist/cjs/embed/types.d.ts +8 -0
  153. package/dist/cjs/embed/types.js +7 -0
  154. package/dist/cjs/embed/types.js.map +1 -0
  155. package/dist/cjs/errors.d.cts +1 -1
  156. package/dist/cjs/errors.d.ts +1 -1
  157. package/dist/cjs/errors.js.map +1 -1
  158. package/dist/cjs/features/embed.d.cts +2 -28
  159. package/dist/cjs/features/embed.d.ts +2 -28
  160. package/dist/cjs/features/embed.js +39 -1027
  161. package/dist/cjs/features/embed.js.map +1 -1
  162. package/dist/cjs/{fences.js → features/fences.js} +1 -1
  163. package/dist/cjs/features/fences.js.map +1 -0
  164. package/dist/cjs/features/links.js +1 -1
  165. package/dist/cjs/features/links.js.map +1 -1
  166. package/dist/cjs/features/rank.js +1 -1
  167. package/dist/cjs/features/rank.js.map +1 -1
  168. package/dist/cjs/features/sections.js +4 -3
  169. package/dist/cjs/features/sections.js.map +1 -1
  170. package/dist/cjs/features/tags.js +1 -1
  171. package/dist/cjs/features/tags.js.map +1 -1
  172. package/dist/cjs/features/types.d.cts +4 -2
  173. package/dist/cjs/features/types.d.ts +4 -2
  174. package/dist/cjs/graph/graph.js.map +1 -0
  175. package/dist/cjs/graph/traverse.js.map +1 -0
  176. package/dist/cjs/index.d.cts +4 -4
  177. package/dist/cjs/index.d.ts +4 -4
  178. package/dist/cjs/index.js +4 -1
  179. package/dist/cjs/index.js.map +1 -1
  180. package/dist/cjs/output/column-hint.js.map +1 -0
  181. package/dist/cjs/{output.d.cts → output/output.d.cts} +2 -2
  182. package/dist/cjs/{output.d.ts → output/output.d.ts} +2 -2
  183. package/dist/cjs/{output.js → output/output.js} +9 -2
  184. package/dist/cjs/output/output.js.map +1 -0
  185. package/dist/cjs/output/progress.js.map +1 -0
  186. package/dist/cjs/{search-error.js → output/search-error.js} +1 -1
  187. package/dist/cjs/output/search-error.js.map +1 -0
  188. package/dist/cjs/scan/frontmatter.d.cts +13 -0
  189. package/dist/cjs/scan/frontmatter.d.ts +13 -0
  190. package/dist/cjs/scan/frontmatter.js +229 -0
  191. package/dist/cjs/scan/frontmatter.js.map +1 -0
  192. package/dist/cjs/scan/index.d.cts +25 -0
  193. package/dist/cjs/scan/index.d.ts +25 -0
  194. package/dist/cjs/scan/index.js +107 -0
  195. package/dist/cjs/scan/index.js.map +1 -0
  196. package/dist/cjs/scan/list.d.cts +12 -0
  197. package/dist/cjs/scan/list.d.ts +12 -0
  198. package/dist/cjs/scan/list.js +154 -0
  199. package/dist/cjs/scan/list.js.map +1 -0
  200. package/dist/cjs/text/segment.d.cts +3 -0
  201. package/dist/cjs/text/segment.d.ts +3 -0
  202. package/dist/cjs/text/segment.js +154 -0
  203. package/dist/cjs/text/segment.js.map +1 -0
  204. package/dist/cjs/text/strip.d.cts +3 -0
  205. package/dist/cjs/text/strip.d.ts +3 -0
  206. package/dist/cjs/text/strip.js +32 -0
  207. package/dist/cjs/text/strip.js.map +1 -0
  208. package/dist/esm/chunk/extract.d.ts +2 -0
  209. package/dist/esm/chunk/extract.js +114 -0
  210. package/dist/esm/chunk/extract.js.map +1 -0
  211. package/dist/esm/chunk/group.d.ts +4 -0
  212. package/dist/esm/chunk/group.js +253 -0
  213. package/dist/esm/chunk/group.js.map +1 -0
  214. package/dist/esm/chunk/index.d.ts +8 -0
  215. package/dist/esm/chunk/index.js +16 -0
  216. package/dist/esm/chunk/index.js.map +1 -0
  217. package/dist/esm/chunk/parse.d.ts +2 -0
  218. package/dist/esm/chunk/parse.js +63 -0
  219. package/dist/esm/chunk/parse.js.map +1 -0
  220. package/dist/esm/chunk/types.d.ts +19 -0
  221. package/dist/esm/chunk/types.js +2 -0
  222. package/dist/esm/chunk/types.js.map +1 -0
  223. package/dist/esm/chunk/version.d.ts +1 -0
  224. package/dist/esm/chunk/version.js +3 -0
  225. package/dist/esm/chunk/version.js.map +1 -0
  226. package/dist/esm/cli/download.js +9 -8
  227. package/dist/esm/cli/download.js.map +1 -1
  228. package/dist/esm/cli/index.d.ts +1 -1
  229. package/dist/esm/cli/index.js +1 -1
  230. package/dist/esm/cli/index.js.map +1 -1
  231. package/dist/esm/cli/init.js +32 -6
  232. package/dist/esm/cli/init.js.map +1 -1
  233. package/dist/esm/cli/map.js +1 -1
  234. package/dist/esm/cli/map.js.map +1 -1
  235. package/dist/esm/cli/named.js +1 -1
  236. package/dist/esm/cli/named.js.map +1 -1
  237. package/dist/esm/cli/path.js +2 -2
  238. package/dist/esm/cli/path.js.map +1 -1
  239. package/dist/esm/cli/peek.js +1 -1
  240. package/dist/esm/cli/peek.js.map +1 -1
  241. package/dist/esm/cli/related.js +1 -1
  242. package/dist/esm/cli/related.js.map +1 -1
  243. package/dist/esm/cli/search.js +1 -1
  244. package/dist/esm/cli/search.js.map +1 -1
  245. package/dist/esm/cli/shared.d.ts +1 -1
  246. package/dist/esm/cli/shared.js +3 -3
  247. package/dist/esm/cli/shared.js.map +1 -1
  248. package/dist/esm/cli/status.js +37 -9
  249. package/dist/esm/cli/status.js.map +1 -1
  250. package/dist/esm/commands/map.d.ts +1 -1
  251. package/dist/esm/commands/map.js.map +1 -1
  252. package/dist/esm/commands/peek.d.ts +1 -1
  253. package/dist/esm/commands/peek.js +8 -1
  254. package/dist/esm/commands/peek.js.map +1 -1
  255. package/dist/esm/commands/related.js +14 -10
  256. package/dist/esm/commands/related.js.map +1 -1
  257. package/dist/esm/commands/scope.js +1 -1
  258. package/dist/esm/commands/scope.js.map +1 -1
  259. package/dist/esm/commands/search.d.ts +1 -1
  260. package/dist/esm/commands/search.js +30 -81
  261. package/dist/esm/commands/search.js.map +1 -1
  262. package/dist/esm/commands/signals.d.ts +18 -0
  263. package/dist/esm/commands/signals.js +78 -0
  264. package/dist/esm/commands/signals.js.map +1 -0
  265. package/dist/esm/commands/status.d.ts +2 -2
  266. package/dist/esm/commands/status.js +2 -2
  267. package/dist/esm/commands/status.js.map +1 -1
  268. package/dist/esm/config/access.d.ts +8 -3
  269. package/dist/esm/config/access.js +34 -15
  270. package/dist/esm/config/access.js.map +1 -1
  271. package/dist/esm/config/index.d.ts +5 -1
  272. package/dist/esm/config/index.js +3 -1
  273. package/dist/esm/config/index.js.map +1 -1
  274. package/dist/esm/config/load.d.ts +6 -1
  275. package/dist/esm/config/load.js +67 -9
  276. package/dist/esm/config/load.js.map +1 -1
  277. package/dist/esm/config/resolve.js +2 -2
  278. package/dist/esm/config/resolve.js.map +1 -1
  279. package/dist/esm/config/signals.d.ts +4 -0
  280. package/dist/esm/config/signals.js +14 -0
  281. package/dist/esm/config/signals.js.map +1 -0
  282. package/dist/esm/config/types.d.ts +7 -4
  283. package/dist/esm/config/types.js +1 -1
  284. package/dist/esm/config/types.js.map +1 -1
  285. package/dist/esm/config/validate.d.ts +1 -0
  286. package/dist/esm/config/validate.js +65 -12
  287. package/dist/esm/config/validate.js.map +1 -1
  288. package/dist/esm/db/open.d.ts +1 -1
  289. package/dist/esm/db/open.js +11 -6
  290. package/dist/esm/db/open.js.map +1 -1
  291. package/dist/esm/db/reconcile.d.ts +1 -0
  292. package/dist/esm/db/reconcile.js +14 -4
  293. package/dist/esm/db/reconcile.js.map +1 -1
  294. package/dist/esm/{sql-functions.js → db/sql-functions.js} +1 -1
  295. package/dist/esm/db/sql-functions.js.map +1 -0
  296. package/dist/esm/embed/cohere.d.ts +2 -0
  297. package/dist/esm/embed/cohere.js +46 -0
  298. package/dist/esm/embed/cohere.js.map +1 -0
  299. package/dist/esm/embed/http.d.ts +2 -0
  300. package/dist/esm/embed/http.js +33 -0
  301. package/dist/esm/embed/http.js.map +1 -0
  302. package/dist/esm/embed/identity.d.ts +13 -0
  303. package/dist/esm/embed/identity.js +92 -0
  304. package/dist/esm/embed/identity.js.map +1 -0
  305. package/dist/esm/embed/langfit.d.ts +6 -0
  306. package/dist/esm/embed/langfit.js +44 -0
  307. package/dist/esm/embed/langfit.js.map +1 -0
  308. package/dist/esm/embed/languages.d.ts +3 -0
  309. package/dist/esm/embed/languages.js +80 -0
  310. package/dist/esm/embed/languages.js.map +1 -0
  311. package/dist/esm/embed/openai.d.ts +2 -0
  312. package/dist/esm/embed/openai.js +49 -0
  313. package/dist/esm/embed/openai.js.map +1 -0
  314. package/dist/esm/embed/query.d.ts +21 -0
  315. package/dist/esm/embed/query.js +181 -0
  316. package/dist/esm/embed/query.js.map +1 -0
  317. package/dist/esm/embed/registry.d.ts +3 -0
  318. package/dist/esm/embed/registry.js +45 -0
  319. package/dist/esm/embed/registry.js.map +1 -0
  320. package/dist/esm/embed/static.d.ts +2 -0
  321. package/dist/esm/embed/static.js +71 -0
  322. package/dist/esm/embed/static.js.map +1 -0
  323. package/dist/esm/embed/store.d.ts +8 -0
  324. package/dist/esm/embed/store.js +123 -0
  325. package/dist/esm/embed/store.js.map +1 -0
  326. package/dist/esm/embed/types.d.ts +8 -0
  327. package/dist/esm/embed/types.js +3 -0
  328. package/dist/esm/embed/types.js.map +1 -0
  329. package/dist/esm/errors.d.ts +1 -1
  330. package/dist/esm/errors.js.map +1 -1
  331. package/dist/esm/features/embed.d.ts +2 -28
  332. package/dist/esm/features/embed.js +33 -377
  333. package/dist/esm/features/embed.js.map +1 -1
  334. package/dist/esm/{fences.js → features/fences.js} +1 -1
  335. package/dist/esm/features/fences.js.map +1 -0
  336. package/dist/esm/features/links.js +1 -1
  337. package/dist/esm/features/links.js.map +1 -1
  338. package/dist/esm/features/rank.js +1 -1
  339. package/dist/esm/features/rank.js.map +1 -1
  340. package/dist/esm/features/sections.js +4 -3
  341. package/dist/esm/features/sections.js.map +1 -1
  342. package/dist/esm/features/tags.js +1 -1
  343. package/dist/esm/features/tags.js.map +1 -1
  344. package/dist/esm/features/types.d.ts +4 -2
  345. package/dist/esm/features/types.js.map +1 -1
  346. package/dist/esm/graph/graph.js.map +1 -0
  347. package/dist/esm/graph/traverse.js.map +1 -0
  348. package/dist/esm/index.d.ts +4 -4
  349. package/dist/esm/index.js +2 -2
  350. package/dist/esm/index.js.map +1 -1
  351. package/dist/esm/output/column-hint.js.map +1 -0
  352. package/dist/esm/{output.d.ts → output/output.d.ts} +2 -2
  353. package/dist/esm/{output.js → output/output.js} +6 -2
  354. package/dist/esm/output/output.js.map +1 -0
  355. package/dist/esm/output/progress.js.map +1 -0
  356. package/dist/esm/{search-error.js → output/search-error.js} +1 -1
  357. package/dist/esm/output/search-error.js.map +1 -0
  358. package/dist/esm/scan/frontmatter.d.ts +13 -0
  359. package/dist/esm/{scan.js → scan/frontmatter.js} +4 -120
  360. package/dist/esm/scan/frontmatter.js.map +1 -0
  361. package/dist/esm/scan/index.d.ts +25 -0
  362. package/dist/esm/scan/index.js +58 -0
  363. package/dist/esm/scan/index.js.map +1 -0
  364. package/dist/esm/scan/list.d.ts +12 -0
  365. package/dist/esm/scan/list.js +58 -0
  366. package/dist/esm/scan/list.js.map +1 -0
  367. package/dist/esm/text/segment.d.ts +3 -0
  368. package/dist/esm/text/segment.js +95 -0
  369. package/dist/esm/text/segment.js.map +1 -0
  370. package/dist/esm/text/strip.d.ts +3 -0
  371. package/dist/esm/text/strip.js +12 -0
  372. package/dist/esm/text/strip.js.map +1 -0
  373. package/package.json +23 -2
  374. package/schema.json +22 -8
  375. package/skills/sense/SKILL.md +21 -5
  376. package/skills/sense-setup/EXAMPLES.md +6 -6
  377. package/skills/sense-setup/SKILL.md +7 -5
  378. package/dist/cjs/column-hint.js.map +0 -1
  379. package/dist/cjs/fences.js.map +0 -1
  380. package/dist/cjs/graph.js.map +0 -1
  381. package/dist/cjs/output.js.map +0 -1
  382. package/dist/cjs/progress.js.map +0 -1
  383. package/dist/cjs/scan.d.cts +0 -35
  384. package/dist/cjs/scan.d.ts +0 -35
  385. package/dist/cjs/scan.js +0 -433
  386. package/dist/cjs/scan.js.map +0 -1
  387. package/dist/cjs/search-error.js.map +0 -1
  388. package/dist/cjs/segment.d.cts +0 -2
  389. package/dist/cjs/segment.d.ts +0 -2
  390. package/dist/cjs/segment.js +0 -98
  391. package/dist/cjs/segment.js.map +0 -1
  392. package/dist/cjs/sql-functions.js.map +0 -1
  393. package/dist/cjs/traverse.js.map +0 -1
  394. package/dist/esm/column-hint.js.map +0 -1
  395. package/dist/esm/fences.js.map +0 -1
  396. package/dist/esm/graph.js.map +0 -1
  397. package/dist/esm/output.js.map +0 -1
  398. package/dist/esm/progress.js.map +0 -1
  399. package/dist/esm/scan.d.ts +0 -35
  400. package/dist/esm/scan.js.map +0 -1
  401. package/dist/esm/search-error.js.map +0 -1
  402. package/dist/esm/segment.d.ts +0 -2
  403. package/dist/esm/segment.js +0 -65
  404. package/dist/esm/segment.js.map +0 -1
  405. package/dist/esm/sql-functions.js.map +0 -1
  406. package/dist/esm/traverse.js.map +0 -1
  407. /package/dist/cjs/{sql-functions.d.cts → db/sql-functions.d.cts} +0 -0
  408. /package/dist/cjs/{sql-functions.d.ts → db/sql-functions.d.ts} +0 -0
  409. /package/dist/cjs/{fences.d.cts → features/fences.d.cts} +0 -0
  410. /package/dist/cjs/{fences.d.ts → features/fences.d.ts} +0 -0
  411. /package/dist/cjs/{graph.d.cts → graph/graph.d.cts} +0 -0
  412. /package/dist/cjs/{graph.d.ts → graph/graph.d.ts} +0 -0
  413. /package/dist/cjs/{graph.js → graph/graph.js} +0 -0
  414. /package/dist/cjs/{traverse.d.cts → graph/traverse.d.cts} +0 -0
  415. /package/dist/cjs/{traverse.d.ts → graph/traverse.d.ts} +0 -0
  416. /package/dist/cjs/{traverse.js → graph/traverse.js} +0 -0
  417. /package/dist/cjs/{column-hint.d.cts → output/column-hint.d.cts} +0 -0
  418. /package/dist/cjs/{column-hint.d.ts → output/column-hint.d.ts} +0 -0
  419. /package/dist/cjs/{column-hint.js → output/column-hint.js} +0 -0
  420. /package/dist/cjs/{progress.d.cts → output/progress.d.cts} +0 -0
  421. /package/dist/cjs/{progress.d.ts → output/progress.d.ts} +0 -0
  422. /package/dist/cjs/{progress.js → output/progress.js} +0 -0
  423. /package/dist/cjs/{search-error.d.cts → output/search-error.d.cts} +0 -0
  424. /package/dist/cjs/{search-error.d.ts → output/search-error.d.ts} +0 -0
  425. /package/dist/esm/{sql-functions.d.ts → db/sql-functions.d.ts} +0 -0
  426. /package/dist/esm/{fences.d.ts → features/fences.d.ts} +0 -0
  427. /package/dist/esm/{graph.d.ts → graph/graph.d.ts} +0 -0
  428. /package/dist/esm/{graph.js → graph/graph.js} +0 -0
  429. /package/dist/esm/{traverse.d.ts → graph/traverse.d.ts} +0 -0
  430. /package/dist/esm/{traverse.js → graph/traverse.js} +0 -0
  431. /package/dist/esm/{column-hint.d.ts → output/column-hint.d.ts} +0 -0
  432. /package/dist/esm/{column-hint.js → output/column-hint.js} +0 -0
  433. /package/dist/esm/{progress.d.ts → output/progress.d.ts} +0 -0
  434. /package/dist/esm/{progress.js → output/progress.js} +0 -0
  435. /package/dist/esm/{search-error.d.ts → output/search-error.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/embed.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { Config, ResolvedConfig } from '../config/index.ts';\nimport { embedConfig } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { fenceTracker } from '../fences.ts';\nimport { progress } from '../progress.ts';\nimport { parseFile } from '../scan.ts';\nimport type { Feature } from './types.ts';\n\n// Heading-based chunks, int8 vectors with a per-vector scale, NULL vector = not yet embedded.\n// Reconcile writes dirty rows; embedding tops up on the next search, so staleness costs recall.\n\n// Storage lever fixed by the bake-off (BENCHMARKING.md): int8 at 256 dims is\n// quality-free vs f32-512 when fused. Queries stay f32 at the same dims.\nconst STORE_DIMS = 256;\n// Seed chunks that participate in a `related` scan; see similarNotes for the measurement.\nconst TARGET_CHUNK_CAP = 16;\nconst BATCH = 64;\n\nexport interface EmbedProvider {\n id: string; // model identity; participates in the cache key, change -> re-embed\n dims: number;\n embed(texts: string[]): Promise<Float32Array[]>;\n}\n\ninterface Chunk {\n startLine: number;\n endLine: number;\n text: string;\n}\n\n// Deterministic, so embed time can re-derive text from stored line ranges. Heading-delimited,\n// preamble kept, whole body when no headings; the title/summary prefix mirrors bm25 weighting.\n//\n// Chunks the body, not the raw file, with `offset` shifting line numbers back onto the raw\n// file so a range stays a direct Read range (sections is 1-indexed over raw too). Chunking raw\n// put the frontmatter block in its own leading chunk on every note with a heading, which made\n// `lines` point at YAML and made frontmatter-only notes near-identical to each other. It also\n// disagreed with FTS, which indexes the body alone.\nfunction chunksOf(body: string, search?: { title: string; summary: string }, offset = 0): Chunk[] {\n const lines = body.split('\\n');\n const starts: number[] = [];\n const fence = fenceTracker();\n for (let i = 0; i < lines.length; i++) {\n if (fence.feed(lines[i])) continue;\n if (!fence.inFence && /^#{1,6} +/.test(lines[i])) starts.push(i + 1);\n }\n const bounds = starts.length === 0 ? [1] : starts[0] > 1 ? [1, ...starts] : starts;\n const prefix = [search?.title, search?.summary].filter(Boolean).join('\\n');\n const chunks: Chunk[] = [];\n bounds.forEach((start, i) => {\n const end = i + 1 < bounds.length ? bounds[i + 1] - 1 : lines.length;\n const text = lines\n .slice(start - 1, end)\n .join('\\n')\n .trim();\n // A body with nothing in it yields no chunks at all, so a frontmatter-only note has no\n // vectors rather than a vector of its own YAML.\n if (text.length > 0) chunks.push({ startLine: start + offset, endLine: end + offset, text: prefix ? `${prefix}\\n${text}` : text });\n });\n return chunks;\n}\n\nexport const embed: Feature = {\n name: 'embed',\n schema(db) {\n db.exec(`CREATE TABLE IF NOT EXISTS embeddings (\"path\" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, scale REAL, vector BLOB, PRIMARY KEY (\"path\", chunk))`);\n },\n extract(raw, body, search) {\n // Lines the frontmatter occupies, so body line 1 maps back to its raw line number.\n return chunksOf(body, search, raw.split('\\n').length - body.split('\\n').length);\n },\n remove(db, path) {\n db.prepare('DELETE FROM embeddings WHERE \"path\" = ?').run(path);\n },\n // A tree with no embedding model never had extract() run for the doc (db.ts's per-file\n // filter skips it), so extracted is undefined here -- store nothing, i.e. no rows.\n store(db, path, extracted) {\n if (!extracted) return;\n const insert = db.prepare('INSERT INTO embeddings (\"path\", chunk, start_line, end_line, scale, vector) VALUES (?, ?, ?, ?, NULL, NULL)');\n (extracted as Chunk[]).forEach((c, idx) => insert.run(path, idx, c.startLine, c.endLine));\n },\n enabledForFile(_cfg, file) {\n return file.embed;\n },\n};\n\n// --- static type: Model2Vec safetensors + pure-JS tokenizer, model cached in ~/.cache ---\n// Encode convention from model2vec/model.py: no special tokens, drop unk ids, mean-pool,\n// L2-normalize.\n\nconst MODEL_FILES = ['model.safetensors', 'tokenizer.json'];\n// The pair, for messages that name what a local model directory must contain.\nexport const MODEL_FILENAMES = MODEL_FILES.join(' and ');\n\n// A Hugging Face repo id: one slash, HF's charset. Anything else is a path, used as one rather\n// than flattened into a cache key Windows would reject.\nconst HF_MODEL_ID = /^[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*$/;\n\n// Downloadable iff it names a Hugging Face repo; a local path is the caller's to populate.\nexport function isDownloadable(model: string): boolean {\n return HF_MODEL_ID.test(model);\n}\n\n// Machine-wide, not per-tree: `.sense/` is the index a rebuild throws away, while a 124 MB\n// model is shared by every tree on the machine. Honors XDG_CACHE_HOME where it is set;\n// elsewhere ~/.cache, which is also where Hugging Face's own libraries keep theirs.\nfunction cacheRoot(): string {\n const xdg = process.env.XDG_CACHE_HOME;\n return join(xdg && xdg.length > 0 ? xdg : join(homedir(), '.cache'), 'sensemaking', 'models');\n}\n\n// A model is either a local directory the caller pointed at, or the cache `sense download`\n// fills. Nothing here fetches: an absent model degrades search to its other signals rather\n// than pulling 124 MB out of a command that reads like a query.\nexport function modelDir(model: string): string {\n if (!HF_MODEL_ID.test(model)) return model;\n return join(cacheRoot(), model.replace(/\\//g, '--'));\n}\n\n// Both files, so an interrupted download reads as absent and the next one resumes it.\nexport function hasModelFiles(model: string): boolean {\n const dir = modelDir(model);\n return MODEL_FILES.every((file) => existsSync(join(dir, file)));\n}\n\n// api providers have nothing to download, so they are never \"missing\" here; an unreachable\n// endpoint surfaces as an EMBED_MODEL error at call time instead.\nexport function modelPresent(cfg: Config): boolean {\n const e = embedConfig(cfg);\n if (!e) return false;\n return e.type === 'api' || hasModelFiles(e.model);\n}\n\nfunction fetchToFile(url: string, dest: string): Promise<void> {\n return fetch(url).then(async (res) => {\n if (!res.ok) throw new SenseError('EMBED_MODEL', `model download failed: ${url} -> HTTP ${res.status}`);\n writeFileSync(`${dest}.part`, Buffer.from(await res.arrayBuffer()));\n renameSync(`${dest}.part`, dest);\n });\n}\n\n// The only code path that touches the network for weights. `sense download` calls it; no\n// query ever does. Idempotent: a file already on disk is left alone.\nexport async function downloadModel(model: string, onFile?: (file: string, dir: string) => void): Promise<string> {\n if (!isDownloadable(model)) {\n throw new SenseError('EMBED_MODEL', `embed.model \"${model}\" is a local path, not a Hugging Face model id, so there is nothing to download; put model.safetensors and tokenizer.json in that directory, or name a model id like \"minishlab/potion-retrieval-32M\"`);\n }\n const dir = modelDir(model);\n mkdirSync(dir, { recursive: true });\n for (const file of MODEL_FILES) {\n if (existsSync(join(dir, file))) continue;\n onFile?.(file, dir);\n await fetchToFile(`https://huggingface.co/${model}/resolve/main/${file}`, join(dir, file));\n }\n return dir;\n}\n\nasync function staticProvider(model: string): Promise<EmbedProvider> {\n const dir = modelDir(model);\n for (const file of MODEL_FILES) {\n if (!existsSync(join(dir, file))) {\n // A local path the caller controls gets told what is missing where; only a repo id can\n // be fixed by downloading.\n const fix = isDownloadable(model) ? 'run `sense download`' : `expected ${MODEL_FILENAMES} in that directory`;\n throw new SenseError('EMBED_MODEL_MISSING', `embed model ${model} is not available (looked in ${dir}); ${fix}`);\n }\n }\n\n const raw = readFileSync(join(dir, 'model.safetensors'));\n const headerLen = Number(raw.readBigUInt64LE(0));\n const header = JSON.parse(raw.subarray(8, 8 + headerLen).toString('utf8')) as Record<string, { dtype: string; shape: number[]; data_offsets: number[] }>;\n const entry = Object.entries(header).find(([k]) => k !== '__metadata__');\n if (!entry || entry[1].dtype !== 'F32') throw new SenseError('EMBED_MODEL', `${model}: expected an F32 safetensors matrix`);\n const spec = entry[1];\n const dims = spec.shape[1];\n const dataStart = raw.byteOffset + 8 + headerLen + spec.data_offsets[0];\n const matrix = dataStart % 4 === 0 ? new Float32Array(raw.buffer, dataStart, spec.shape[0] * dims) : new Float32Array(raw.buffer.slice(dataStart, dataStart + spec.shape[0] * dims * 4));\n\n const tokenizerJson = JSON.parse(readFileSync(join(dir, 'tokenizer.json'), 'utf8'));\n // Lazy import: the tokenizer loads only on the semantic path, never at CLI startup.\n const { Tokenizer } = await import('@huggingface/tokenizers');\n const tok = new Tokenizer(tokenizerJson, {});\n const unkId = tokenizerJson.model?.vocab?.[tokenizerJson.model?.unk_token] ?? -1;\n\n function one(text: string): Float32Array {\n // The tokenizer yields undefined (not the unk id) for tokens outside the vocab; an\n // undefined id would index the matrix at NaN and poison the whole mean-pool -- and the\n // int8 conversion then stores the NaN vector as all zeros, silently. Keep integers only.\n const ids = (tok.encode(text, { add_special_tokens: false }).ids as number[]).filter((id) => Number.isInteger(id) && id !== unkId);\n const v = new Float32Array(dims);\n if (ids.length === 0) return v;\n for (const id of ids) {\n const off = id * dims;\n for (let d = 0; d < dims; d++) v[d] += matrix[off + d];\n }\n let norm = 0;\n for (let d = 0; d < dims; d++) {\n v[d] /= ids.length;\n norm += v[d] * v[d];\n }\n norm = Math.sqrt(norm) + 1e-32;\n for (let d = 0; d < dims; d++) v[d] /= norm;\n return v;\n }\n\n return { id: `static:${model}`, dims, embed: async (texts) => texts.map(one) };\n}\n\n// --- api type: one POST against any OpenAI-compatible /embeddings endpoint ---\n\nasync function apiProvider(model: string, url: string | undefined, keyEnv: string | undefined): Promise<EmbedProvider> {\n if (!url) throw new SenseError('EMBED_MODEL', 'features.embed.type \"api\" requires a url');\n const base = url.replace(/\\/+$/, '');\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n const key = keyEnv ? process.env[keyEnv] : undefined;\n if (key) headers.authorization = `Bearer ${key}`;\n\n async function post(texts: string[]): Promise<Float32Array[]> {\n const res = await fetch(`${base}/embeddings`, { method: 'POST', headers, body: JSON.stringify({ model, input: texts }) });\n if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/embeddings -> HTTP ${res.status}`);\n const body = (await res.json()) as { data: Array<{ embedding: number[] }> };\n return body.data.map((d) => Float32Array.from(d.embedding));\n }\n\n const dims = (await post(['dimension probe']))[0].length;\n return { id: `api:${base}:${model}`, dims, embed: post };\n}\n\nconst providers = new Map<string, Promise<EmbedProvider>>();\n\nfunction getProvider(cfg: Config): Promise<EmbedProvider> {\n const e = embedConfig(cfg);\n if (!e) throw new SenseError('EMBED_DISABLED', 'this tree has no embedding model: add an \"embed\" block naming one to sense.config.json, then run `sense download`');\n const sig = `${e.type}:${e.model}:${e.url ?? ''}`;\n let p = providers.get(sig);\n if (!p) {\n p = e.type === 'api' ? apiProvider(e.model, e.url, e.key) : staticProvider(e.model);\n providers.set(sig, p);\n }\n return p;\n}\n\n// Slice + re-normalize (Matryoshka); optionally round through int8 storage.\nfunction toStore(full: Float32Array, dims: number, int8: boolean): { v: Float32Array; scale: number } {\n const v = new Float32Array(dims);\n let norm = 0;\n for (let d = 0; d < dims; d++) norm += full[d] * full[d];\n norm = Math.sqrt(norm) + 1e-32;\n for (let d = 0; d < dims; d++) v[d] = full[d] / norm;\n if (!int8) return { v, scale: 1 };\n let max = 0;\n for (let d = 0; d < dims; d++) max = Math.max(max, Math.abs(v[d]));\n return { v, scale: max / 127 || 1 };\n}\n\n// Embed rows whose vector is NULL, re-deriving chunk text from the files through the\n// same parse + chunker that stored the rows.\nexport async function embedPending(db: DatabaseSync, cfg: Config, baseDir: string): Promise<void> {\n const provider = await getProvider(cfg); // throws EMBED_DISABLED before touching the table\n const dirty = db.prepare('SELECT \"path\", chunk FROM embeddings WHERE vector IS NULL ORDER BY \"path\", chunk').all() as Array<{ path: string; chunk: number }>;\n if (dirty.length === 0) return;\n const storeDims = Math.min(STORE_DIMS, provider.dims);\n\n const byPath = new Map<string, number[]>();\n for (const row of dirty) {\n const list = byPath.get(row.path) ?? [];\n list.push(row.chunk);\n byPath.set(row.path, list);\n }\n\n const jobs: Array<{ path: string; chunk: number; text: string }> = [];\n for (const [path, chunkIdxs] of byPath) {\n let chunks: Chunk[];\n try {\n // presets/embed are irrelevant here -- re-deriving chunk text for a doc that already\n // has embeddings rows means the tree had an embedding model at reconcile time.\n chunks = parseFile({ relPath: path, absPath: join(baseDir, path), mtimeMs: 0, ctimeMs: 0, size: 0, presets: [], embed: true }, [embed]).doc.extracted.embed as Chunk[];\n } catch {\n continue; // vanished since reconcile; the next reconcile removes its rows\n }\n for (const idx of chunkIdxs) if (chunks[idx]) jobs.push({ path, chunk: idx, text: chunks[idx].text });\n }\n\n const update = db.prepare('UPDATE embeddings SET scale = ?, vector = ? WHERE \"path\" = ? AND chunk = ?');\n // The lazy build is the one long silence a first search hits (measured 23s at\n // 26k notes); progress makes it distinguishable from a hang.\n const report = progress('embedding chunks', jobs.length);\n for (let i = 0; i < jobs.length; i += BATCH) {\n const batch = jobs.slice(i, i + BATCH);\n const vectors = await provider.embed(batch.map((j) => j.text));\n db.exec('BEGIN');\n try {\n batch.forEach((job, j) => {\n const { v, scale } = toStore(vectors[j], storeDims, true);\n const q = new Int8Array(storeDims);\n for (let d = 0; d < storeDims; d++) q[d] = Math.round(v[d] / scale);\n update.run(scale, Buffer.from(q.buffer), job.path, job.chunk);\n });\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n report.tick(Math.min(i + BATCH, jobs.length));\n }\n report.finish();\n}\n\n// Dequantised int8 dot products land a little either side of a true cosine, so an identical\n// pair prints 1.001 and undermines a column whose whole job is being a bounded number.\nfunction asCosine(score: number): number {\n return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;\n}\n\n// Best chunk per file by cosine, its line range riding along; FTS5 operators are stripped as\n// lexical syntax. Similarity comes back because the fused score cannot express match quality,\n// and nearest-neighbour search always returns a neighbour however far away.\nexport async function semanticCandidates(db: DatabaseSync, cfg: Config, terms: string, fetch: number, allowed?: Set<string>): Promise<Array<{ path: string; lines: string; similarity: number }>> {\n const baseDir = (cfg as Partial<ResolvedConfig>).baseDir;\n if (!baseDir) throw new SenseError('EMBED_MODEL', 'semantic expansion needs a config with baseDir (use loadConfig/open)');\n await embedPending(db, cfg, baseDir);\n\n const provider = await getProvider(cfg);\n const storeDims = Math.min(STORE_DIMS, provider.dims);\n const text = (terms.match(/[\\p{L}\\p{N}]+/gu) ?? []).filter((t) => !['AND', 'OR', 'NOT', 'NEAR'].includes(t)).join(' ');\n const { v: qv } = toStore((await provider.embed([text]))[0], storeDims, false);\n\n const rows = db.prepare('SELECT \"path\", start_line, end_line, scale, vector FROM embeddings WHERE vector IS NOT NULL').all() as Array<{\n path: string;\n start_line: number;\n end_line: number;\n scale: number;\n vector: Uint8Array;\n }>;\n\n const best = new Map<string, { score: number; lines: string }>();\n for (const row of rows) {\n if (allowed && !allowed.has(row.path)) continue;\n const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));\n let dot = 0;\n for (let d = 0; d < q.length; d++) dot += q[d] * qv[d];\n const score = dot * row.scale;\n const existing = best.get(row.path);\n if (!existing || score > existing.score) best.set(row.path, { score, lines: `L${row.start_line}-${row.end_line}` });\n }\n return [...best.entries()]\n .sort((a, b) => b[1].score - a[1].score)\n .slice(0, fetch)\n .map(([path, b]) => ({ path, lines: b.lines, similarity: asCosine(b.score) }));\n}\n\n// Whether a note has any chunk with a vector. Distinguishes \"nothing is near this note\" from\n// \"this note has no text\", which look the same in an empty result.\nexport function hasEmbedding(db: DatabaseSync, path: string): boolean {\n const row = db.prepare('SELECT 1 AS ok FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL LIMIT 1').get(path) as { ok: number } | undefined;\n return row !== undefined;\n}\n\n// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear\n// scan of stored vectors. Reads only what is stored, so it stays sync.\nexport function similarNotes(db: DatabaseSync, _cfg: Config, path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Array<{ path: string; similarity: number }> {\n // Cost is target_chunks x stored_chunks, so a heading-dense seed multiplies a full-corpus\n // scan (12.7s at 201 chunks/note). Sample evenly, so late sections still get a vote.\n const targetRows = db.prepare('SELECT scale, vector FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL ORDER BY chunk').all(path) as Array<{ scale: number; vector: Uint8Array }>;\n if (targetRows.length === 0) return [];\n const step = Math.max(1, Math.ceil(targetRows.length / TARGET_CHUNK_CAP));\n const target = targetRows.filter((_, i) => i % step === 0).map((row) => ({ v: new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength), scale: row.scale }));\n\n const rows = db.prepare('SELECT \"path\", scale, vector FROM embeddings WHERE vector IS NOT NULL').all() as Array<{ path: string; scale: number; vector: Uint8Array }>;\n const best = new Map<string, number>();\n for (const row of rows) {\n if (row.path === path || opts.exclude.has(row.path) || (opts.allowed && !opts.allowed.has(row.path))) continue;\n const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n for (const t of target) {\n let dot = 0;\n const len = Math.min(t.v.length, other.length);\n for (let d = 0; d < len; d++) dot += t.v[d] * other[d];\n const score = dot * t.scale * row.scale;\n const existing = best.get(row.path);\n if (existing === undefined || score > existing) best.set(row.path, score);\n }\n }\n\n return [...best.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, opts.k)\n .map(([p, score]) => ({ path: p, similarity: asCosine(score) }));\n}\n"],"names":["MODEL_FILENAMES","downloadModel","embed","embedPending","hasEmbedding","hasModelFiles","isDownloadable","modelDir","modelPresent","semanticCandidates","similarNotes","STORE_DIMS","TARGET_CHUNK_CAP","BATCH","chunksOf","body","search","offset","lines","split","starts","fence","fenceTracker","i","length","feed","inFence","test","push","bounds","prefix","title","summary","filter","Boolean","join","chunks","forEach","start","end","text","slice","trim","startLine","endLine","name","schema","db","exec","extract","raw","remove","path","prepare","run","store","extracted","insert","c","idx","enabledForFile","_cfg","file","MODEL_FILES","HF_MODEL_ID","model","cacheRoot","xdg","process","env","XDG_CACHE_HOME","homedir","replace","dir","every","existsSync","cfg","e","embedConfig","type","fetchToFile","url","dest","fetch","then","res","ok","SenseError","status","Buffer","from","arrayBuffer","writeFileSync","renameSync","onFile","mkdirSync","recursive","staticProvider","tokenizerJson","fix","headerLen","header","entry","spec","dims","dataStart","matrix","Tokenizer","tok","unkId","one","ids","encode","add_special_tokens","id","Number","isInteger","v","Float32Array","off","d","norm","Math","sqrt","readFileSync","readBigUInt64LE","JSON","parse","subarray","toString","Object","entries","find","k","dtype","shape","byteOffset","data_offsets","buffer","vocab","unk_token","texts","map","apiProvider","keyEnv","base","headers","key","post","method","stringify","input","json","data","embedding","undefined","authorization","providers","Map","getProvider","sig","p","get","set","toStore","full","int8","scale","max","abs","baseDir","provider","dirty","storeDims","byPath","row","list","jobs","chunkIdxs","update","report","batch","vectors","j","job","q","Int8Array","round","chunk","err","tick","min","all","parseFile","relPath","absPath","mtimeMs","ctimeMs","size","presets","doc","progress","finish","asCosine","score","terms","allowed","qv","rows","best","dot","existing","match","t","includes","has","vector","byteLength","start_line","end_line","sort","a","b","similarity","opts","targetRows","step","ceil","target","_","exclude","other","len"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/embed.ts"],"sourcesContent":["import type { Block, Chunk } from '../chunk/index.ts';\nimport { CHUNK_VERSION, chunkFromBlocks, parse } from '../chunk/index.ts';\nimport { embedConfig } from '../config/index.ts';\nimport { modelIdentity } from '../embed/identity.ts';\nimport type { Feature } from './types.ts';\n\n// int8 vectors with a per-vector scale, NULL vector = not yet embedded. Reconcile writes dirty\n// rows; embedding tops up on the next search, so staleness costs recall. Providers, the vector\n// fill, and the search-time helpers live under src/embed/. Block/group structure (D1-D5) lives\n// in src/chunk; this keeps only the title/summary prefix and the empty-chunk drop (D6).\n\nexport type { Chunk };\n\n// Deterministic, so embed time can re-derive text from stored line ranges; the title/summary\n// prefix mirrors bm25 field weighting, and an empty chunk is dropped before the prefix is added.\n//\n// Chunks the body, not the raw file, with `offset` shifting line numbers back onto the raw\n// file so a range stays a direct Read range (sections is 1-indexed over raw too). Chunking raw\n// put the frontmatter block in its own leading chunk on every note with a heading, which made\n// `lines` point at YAML and made frontmatter-only notes near-identical to each other. It also\n// disagreed with FTS, which indexes the body alone.\nfunction chunksOf(blocks: Block[], body: string, search?: { title: string; summary: string }, offset = 0, chunkTokens?: number): Chunk[] {\n const prefix = [search?.title, search?.summary].filter(Boolean).join('\\n');\n // chunk()'s default text mode is 'raw' (D9), the shipped choice. A hardcoded option change\n // here must bump src/chunk/version.ts's CHUNK_VERSION -- the signature for every chunker\n // input not already carried in config.\n return chunkFromBlocks(blocks, body, chunkTokens !== undefined ? { targetTokens: chunkTokens } : undefined).map((c) => ({\n startLine: c.startLine + offset,\n endLine: c.endLine + offset,\n text: prefix ? `${prefix}\\n${c.text}` : c.text,\n }));\n}\n\nexport const embed: Feature = {\n name: 'embed',\n schema(db) {\n db.exec(`CREATE TABLE IF NOT EXISTS embeddings (\"path\" TEXT, chunk INTEGER, start_line INTEGER, end_line INTEGER, scale REAL, vector BLOB, PRIMARY KEY (\"path\", chunk))`);\n },\n extract(raw, body, search, _data, cfg, blocks) {\n // Lines the frontmatter occupies, so body line 1 maps back to its raw line number.\n // blocks comes from parseFile's shared parse; falls back to parsing body for a direct call.\n return chunksOf(blocks ?? parse(body), body, search, raw.split('\\n').length - body.split('\\n').length, cfg?.embed?.chunkTokens);\n },\n remove(db, path) {\n db.prepare('DELETE FROM embeddings WHERE \"path\" = ?').run(path);\n },\n // A tree with no embedding model never had extract() run for the doc (db.ts's per-file\n // filter skips it), so extracted is undefined here -- store nothing, i.e. no rows.\n store(db, path, extracted) {\n if (!extracted) return;\n const insert = db.prepare('INSERT INTO embeddings (\"path\", chunk, start_line, end_line, scale, vector) VALUES (?, ?, ?, ?, NULL, NULL)');\n (extracted as Chunk[]).forEach((c, idx) => insert.run(path, idx, c.startLine, c.endLine));\n },\n enabledForFile(_cfg, file) {\n return file.embed;\n },\n // Provider/model/chunk-version/identity, keyed so a rebuild notice can name it \"embed\n // settings\". Driven by embedConfig(cfg) directly (a model can be configured with no preset\n // yet using vectors), not by whether this feature is itself active.\n signature(cfg) {\n const e = embedConfig(cfg);\n if (!e) return 'embed:off';\n // Weight identity from identity.ts, no network: a static model's resolved sha or local\n // size+mtime, appended once known so changed weights re-embed.\n const identity = e.provider === 'static' ? modelIdentity(e.model) : undefined;\n // W3b: chunkTokens rides the version token, since it's the one owner lever the chunker\n // itself takes -- changing or clearing it must rebuild exactly like a chunker version bump.\n const chunkVersion = e.chunkTokens !== undefined ? `${CHUNK_VERSION}:${e.chunkTokens}` : CHUNK_VERSION;\n return `embed:${e.provider}:${e.model}:${chunkVersion}${identity !== undefined ? `@${identity}` : ''}`;\n },\n};\n"],"names":["embed","chunksOf","blocks","body","search","offset","chunkTokens","prefix","title","summary","filter","Boolean","join","chunkFromBlocks","undefined","targetTokens","map","c","startLine","endLine","text","name","schema","db","exec","extract","raw","_data","cfg","parse","split","length","remove","path","prepare","run","store","extracted","insert","forEach","idx","enabledForFile","_cfg","file","signature","e","embedConfig","identity","provider","modelIdentity","model","chunkVersion","CHUNK_VERSION"],"mappings":";;;;+BAiCaA;;;eAAAA;;;uBAhCyC;wBAC1B;0BACE;AAU9B,6FAA6F;AAC7F,iGAAiG;AACjG,EAAE;AACF,2FAA2F;AAC3F,+FAA+F;AAC/F,8FAA8F;AAC9F,8FAA8F;AAC9F,oDAAoD;AACpD,SAASC,SAASC,MAAe,EAAEC,IAAY,EAAEC,MAA2C;QAAEC,SAAAA,iEAAS,GAAGC;IACxG,IAAMC,SAAS;QAACH,mBAAAA,6BAAAA,OAAQI,KAAK;QAAEJ,mBAAAA,6BAAAA,OAAQK,OAAO;KAAC,CAACC,MAAM,CAACC,SAASC,IAAI,CAAC;IACrE,2FAA2F;IAC3F,yFAAyF;IACzF,uCAAuC;IACvC,OAAOC,IAAAA,wBAAe,EAACX,QAAQC,MAAMG,gBAAgBQ,YAAY;QAAEC,cAAcT;IAAY,IAAIQ,WAAWE,GAAG,CAAC,SAACC;eAAO;YACtHC,WAAWD,EAAEC,SAAS,GAAGb;YACzBc,SAASF,EAAEE,OAAO,GAAGd;YACrBe,MAAMb,SAAS,AAAC,GAAaU,OAAXV,QAAO,MAAW,OAAPU,EAAEG,IAAI,IAAKH,EAAEG,IAAI;QAChD;;AACF;AAEO,IAAMpB,QAAiB;IAC5BqB,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACPA,GAAGC,IAAI,CAAC;IACV;IACAC,SAAAA,SAAAA,QAAQC,GAAG,EAAEvB,IAAI,EAAEC,MAAM,EAAEuB,KAAK,EAAEC,GAAG,EAAE1B,MAAM;YAG4D0B;QAFvG,mFAAmF;QACnF,4FAA4F;QAC5F,OAAO3B,SAASC,mBAAAA,oBAAAA,SAAU2B,IAAAA,cAAK,EAAC1B,OAAOA,MAAMC,QAAQsB,IAAII,KAAK,CAAC,MAAMC,MAAM,GAAG5B,KAAK2B,KAAK,CAAC,MAAMC,MAAM,EAAEH,gBAAAA,2BAAAA,aAAAA,IAAK5B,KAAK,cAAV4B,iCAAAA,WAAYtB,WAAW;IAChI;IACA0B,QAAAA,SAAAA,OAAOT,EAAE,EAAEU,IAAI;QACbV,GAAGW,OAAO,CAAC,2CAA2CC,GAAG,CAACF;IAC5D;IACA,uFAAuF;IACvF,mFAAmF;IACnFG,OAAAA,SAAAA,MAAMb,EAAE,EAAEU,IAAI,EAAEI,SAAS;QACvB,IAAI,CAACA,WAAW;QAChB,IAAMC,SAASf,GAAGW,OAAO,CAAC;QACzBG,UAAsBE,OAAO,CAAC,SAACtB,GAAGuB;mBAAQF,OAAOH,GAAG,CAACF,MAAMO,KAAKvB,EAAEC,SAAS,EAAED,EAAEE,OAAO;;IACzF;IACAsB,gBAAAA,SAAAA,eAAeC,IAAI,EAAEC,IAAI;QACvB,OAAOA,KAAK3C,KAAK;IACnB;IACA,sFAAsF;IACtF,2FAA2F;IAC3F,oEAAoE;IACpE4C,WAAAA,SAAAA,UAAUhB,GAAG;QACX,IAAMiB,IAAIC,IAAAA,qBAAW,EAAClB;QACtB,IAAI,CAACiB,GAAG,OAAO;QACf,uFAAuF;QACvF,+DAA+D;QAC/D,IAAME,WAAWF,EAAEG,QAAQ,KAAK,WAAWC,IAAAA,yBAAa,EAACJ,EAAEK,KAAK,IAAIpC;QACpE,uFAAuF;QACvF,4FAA4F;QAC5F,IAAMqC,eAAeN,EAAEvC,WAAW,KAAKQ,YAAY,AAAC,GAAmB+B,OAAjBO,sBAAa,EAAC,KAAiB,OAAdP,EAAEvC,WAAW,IAAK8C,sBAAa;QACtG,OAAO,AAAC,SAAsBP,OAAdA,EAAEG,QAAQ,EAAC,KAAcG,OAAXN,EAAEK,KAAK,EAAC,KAAkBH,OAAfI,cAA4D,OAA7CJ,aAAajC,YAAY,AAAC,IAAY,OAATiC,YAAa;IACpG;AACF"}
@@ -1,6 +1,6 @@
1
1
  // Shared line-fence tracker for tags/sections/embed. Opener: >=3 backticks or tildes at column
2
2
  // 0 -- no indent allowance, a deliberate divergence from CommonMark's up-to-3-space rule (see
3
- // test/unit/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself
3
+ // test/unit/features/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself
4
4
  // contain a backtick (spec rule). A closer is a run of the SAME character, length >= the
5
5
  // opener's, holding nothing but trailing spaces after the run.
6
6
  "use strict";
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/fences.ts"],"sourcesContent":["// Shared line-fence tracker for tags/sections/embed. Opener: >=3 backticks or tildes at column\n// 0 -- no indent allowance, a deliberate divergence from CommonMark's up-to-3-space rule (see\n// test/unit/features/fences.test.ts DIVERGENCES table). A backtick opener's info string must not itself\n// contain a backtick (spec rule). A closer is a run of the SAME character, length >= the\n// opener's, holding nothing but trailing spaces after the run.\n\nexport interface FenceTracker {\n feed(line: string): boolean; // true iff this line is a fence delimiter (open or close)\n readonly inFence: boolean;\n}\n\nconst BACKTICK_OPEN = /^(`{3,})(.*)$/;\nconst TILDE_OPEN = /^(~{3,})(.*)$/;\n\nexport function fenceTracker(): FenceTracker {\n let inFence = false;\n let fenceChar = '';\n let fenceLen = 0;\n return {\n feed(line: string): boolean {\n if (!inFence) {\n const bt = BACKTICK_OPEN.exec(line);\n if (bt && !bt[2].includes('`')) {\n inFence = true;\n fenceChar = '`';\n fenceLen = bt[1].length;\n return true;\n }\n const td = TILDE_OPEN.exec(line);\n if (td) {\n inFence = true;\n fenceChar = '~';\n fenceLen = td[1].length;\n return true;\n }\n return false;\n }\n const run = fenceChar === '`' ? /^(`+)(.*)$/ : /^(~+)(.*)$/;\n const m = run.exec(line);\n if (m && m[1].length >= fenceLen && m[2].trim() === '') {\n inFence = false;\n return true;\n }\n return false;\n },\n get inFence() {\n return inFence;\n },\n };\n}\n\n// Masks <!-- ... --> spans that may cross line boundaries, one line at a time so callers can\n// interleave it with their own per-line state. In isolation an unclosed <!-- stays open forever\n// (mask() has no notion of a line boundary that should kill it); maskRegions() below is what\n// calls close() at the point an unclosed comment actually dies -- a blank line at paragraph\n// level, or its enclosing HTML block's end.\nexport interface CommentTracker {\n mask(line: string): string;\n close(): void; // force-ends an unclosed comment (paragraph blank line, or its HTML block ending)\n readonly inComment: boolean;\n}\n\nexport function commentTracker(): CommentTracker {\n let inComment = false;\n return {\n mask(line: string): string {\n let out = '';\n let i = 0;\n while (i < line.length) {\n if (inComment) {\n const end = line.indexOf('-->', i);\n if (end === -1) {\n out += ' '.repeat(line.length - i);\n i = line.length;\n } else {\n out += ' '.repeat(end + 3 - i);\n i = end + 3;\n inComment = false;\n }\n continue;\n }\n const start = line.indexOf('<!--', i);\n if (start === -1) {\n out += line.slice(i);\n i = line.length;\n } else {\n out += `${line.slice(i, start)} `;\n i = start + 4;\n inComment = true;\n }\n }\n return out;\n },\n close() {\n inComment = false;\n },\n get inComment() {\n return inComment;\n },\n };\n}\n\n// CommonMark's HTML-block type-6 list (fixed by the spec, not a drifting enumeration): a line\n// starting with an open or close tag of one of these, at column 0 (leading whitespace/`>`\n// blockquote markers tolerated), opens a block that swallows following lines -- tags, links,\n// and comments alike -- until a blank line closes it.\nconst HTML_BLOCK_TAGS = new Set([\n 'address',\n 'article',\n 'aside',\n 'base',\n 'basefont',\n 'blockquote',\n 'body',\n 'caption',\n 'center',\n 'col',\n 'colgroup',\n 'dd',\n 'details',\n 'dialog',\n 'dir',\n 'div',\n 'dl',\n 'dt',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'frame',\n 'frameset',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'head',\n 'header',\n 'hr',\n 'html',\n 'iframe',\n 'legend',\n 'li',\n 'link',\n 'main',\n 'menu',\n 'menuitem',\n 'nav',\n 'noframes',\n 'ol',\n 'optgroup',\n 'option',\n 'p',\n 'param',\n 'search',\n 'section',\n 'summary',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'title',\n 'tr',\n 'track',\n 'ul',\n]);\n// Type-1 blocks (script/pre/style/textarea): closes on the line holding the matching end tag,\n// not on a blank line, and that line is the last one swallowed.\nconst HTML_PRE_TAGS = new Set(['script', 'pre', 'style', 'textarea']);\nconst HTML_PRE_CLOSE_RE = /<\\/(?:script|pre|style|textarea)>/i; // any of the four closers ends the block, not only the tag that opened it\n// An opening or closing tag at column 0, tag name captured for the lookups above.\nconst HTML_BLOCK_OPEN_RE = /^<\\/?([a-zA-Z][a-zA-Z0-9]*)(?:[ \\t]|\\/?>|$)/;\nconst BLANK_LINE_RE = /^[ \\t>]*$/; // whitespace/blockquote-markers only -- a paragraph break, and what ends a type-6 block\n\n// Body with fenced-code, HTML-block, and <!-- --> comment regions replaced by spaces (newlines\n// preserved, so line numbers and offsets survive) plus inline code spans masked. The single\n// region masker for tags/links: fence and HTML-block delimiter+content lines are opaque, an\n// unclosed comment dies at its containing block's end (or the next blank line at paragraph\n// level -- never silently to end of file), and a closed comment masks exactly its span.\n// Bodies without a backtick/tilde/`<` fast-path out untouched.\nexport function maskRegions(body: string): string {\n if (!/[`~<]/.test(body)) return body;\n const fence = fenceTracker();\n const comment = commentTracker();\n let inHtmlBlock = false;\n let htmlBlockClose: RegExp | null = null; // set while inside a type-1 (script/pre/style/textarea) block\n return body\n .split('\\n')\n .map((line) => {\n if (inHtmlBlock) {\n if (htmlBlockClose) {\n if (htmlBlockClose.test(line)) {\n inHtmlBlock = false;\n htmlBlockClose = null;\n }\n return ' '.repeat(line.length);\n }\n if (BLANK_LINE_RE.test(line)) {\n // Block content never feeds the comment tracker, so an inner <!-- was only ever\n // masked text; nothing to close here.\n inHtmlBlock = false;\n return line;\n }\n return ' '.repeat(line.length);\n }\n if (!comment.inComment) {\n const isDelim = fence.feed(line);\n if (isDelim || fence.inFence) return ' '.repeat(line.length);\n }\n const uncommented = comment.inComment || line.includes('<!--') ? comment.mask(line) : line;\n if (comment.inComment && BLANK_LINE_RE.test(line)) comment.close(); // paragraph-level: dies at the next blank line\n // Indented or blockquoted HTML blocks still swallow their content in Obsidian, so the\n // opener test runs after stripping leading whitespace and > markers.\n const stripped = uncommented.replace(/^[ \\t>]*/, '');\n if (stripped[0] === '<') {\n const openMatch = HTML_BLOCK_OPEN_RE.exec(stripped);\n if (openMatch) {\n const tagName = openMatch[1].toLowerCase();\n const isClosingTag = stripped[1] === '/';\n if (!isClosingTag && HTML_PRE_TAGS.has(tagName)) {\n // The end condition can be met on the opening line itself: <script>x</script>\n // is a one-line block and the next line parses (Obsidian-verified).\n if (!HTML_PRE_CLOSE_RE.test(stripped.slice(openMatch[0].length))) {\n inHtmlBlock = true;\n htmlBlockClose = HTML_PRE_CLOSE_RE;\n }\n return ' '.repeat(line.length);\n }\n if (HTML_BLOCK_TAGS.has(tagName)) {\n inHtmlBlock = true;\n return ' '.repeat(line.length);\n }\n }\n }\n // Inline code spans hide their content too: `[[x]]` in prose is not a link.\n return uncommented.includes('`') ? maskCodeSpans(uncommented) : uncommented;\n })\n .join('\\n');\n}\n\n// A code span opens on a run of N backticks and closes at the next run of exactly N -- a\n// shorter or longer run in between is literal text, not a delimiter (CommonMark code spans).\n// Masked with spaces so column positions and tag-boundary whitespace are unaffected.\nexport function maskCodeSpans(line: string): string {\n let out = '';\n let i = 0;\n while (i < line.length) {\n if (line[i] !== '`') {\n out += line[i];\n i++;\n continue;\n }\n let j = i;\n while (line[j] === '`') j++;\n const n = j - i;\n let k = j;\n let closeStart = -1;\n let closeEnd = -1;\n while (k < line.length) {\n if (line[k] !== '`') {\n k++;\n continue;\n }\n let m = k;\n while (line[m] === '`') m++;\n if (m - k === n) {\n closeStart = k;\n closeEnd = m;\n break;\n }\n k = m;\n }\n if (closeStart >= 0) {\n out += ' '.repeat(closeEnd - i);\n i = closeEnd;\n } else {\n out += line.slice(i, j);\n i = j;\n }\n }\n return out;\n}\n"],"names":["commentTracker","fenceTracker","maskCodeSpans","maskRegions","BACKTICK_OPEN","TILDE_OPEN","inFence","fenceChar","fenceLen","feed","line","bt","exec","includes","length","td","run","m","trim","inComment","mask","out","i","end","indexOf","repeat","start","slice","close","HTML_BLOCK_TAGS","Set","HTML_PRE_TAGS","HTML_PRE_CLOSE_RE","HTML_BLOCK_OPEN_RE","BLANK_LINE_RE","body","test","fence","comment","inHtmlBlock","htmlBlockClose","split","map","isDelim","uncommented","stripped","replace","openMatch","tagName","toLowerCase","isClosingTag","has","join","j","n","k","closeStart","closeEnd"],"mappings":"AAAA,+FAA+F;AAC/F,8FAA8F;AAC9F,wGAAwG;AACxG,yFAAyF;AACzF,+DAA+D;;;;;;;;;;;;QA0D/CA;eAAAA;;QAhDAC;eAAAA;;QAyOAC;eAAAA;;QA/DAC;eAAAA;;;AA7KhB,IAAMC,gBAAgB;AACtB,IAAMC,aAAa;AAEZ,SAASJ;IACd,IAAIK,UAAU;IACd,IAAIC,YAAY;IAChB,IAAIC,WAAW;IACf,OAAO;QACLC,MAAAA,SAAAA,KAAKC,IAAY;YACf,IAAI,CAACJ,SAAS;gBACZ,IAAMK,KAAKP,cAAcQ,IAAI,CAACF;gBAC9B,IAAIC,MAAM,CAACA,EAAE,CAAC,EAAE,CAACE,QAAQ,CAAC,MAAM;oBAC9BP,UAAU;oBACVC,YAAY;oBACZC,WAAWG,EAAE,CAAC,EAAE,CAACG,MAAM;oBACvB,OAAO;gBACT;gBACA,IAAMC,KAAKV,WAAWO,IAAI,CAACF;gBAC3B,IAAIK,IAAI;oBACNT,UAAU;oBACVC,YAAY;oBACZC,WAAWO,EAAE,CAAC,EAAE,CAACD,MAAM;oBACvB,OAAO;gBACT;gBACA,OAAO;YACT;YACA,IAAME,MAAMT,cAAc,MAAM,eAAe;YAC/C,IAAMU,IAAID,IAAIJ,IAAI,CAACF;YACnB,IAAIO,KAAKA,CAAC,CAAC,EAAE,CAACH,MAAM,IAAIN,YAAYS,CAAC,CAAC,EAAE,CAACC,IAAI,OAAO,IAAI;gBACtDZ,UAAU;gBACV,OAAO;YACT;YACA,OAAO;QACT;QACA,IAAIA;YACF,OAAOA;QACT;IACF;AACF;AAaO,SAASN;IACd,IAAImB,YAAY;IAChB,OAAO;QACLC,MAAAA,SAAAA,KAAKV,IAAY;YACf,IAAIW,MAAM;YACV,IAAIC,IAAI;YACR,MAAOA,IAAIZ,KAAKI,MAAM,CAAE;gBACtB,IAAIK,WAAW;oBACb,IAAMI,MAAMb,KAAKc,OAAO,CAAC,OAAOF;oBAChC,IAAIC,QAAQ,CAAC,GAAG;wBACdF,OAAO,IAAII,MAAM,CAACf,KAAKI,MAAM,GAAGQ;wBAChCA,IAAIZ,KAAKI,MAAM;oBACjB,OAAO;wBACLO,OAAO,IAAII,MAAM,CAACF,MAAM,IAAID;wBAC5BA,IAAIC,MAAM;wBACVJ,YAAY;oBACd;oBACA;gBACF;gBACA,IAAMO,QAAQhB,KAAKc,OAAO,CAAC,QAAQF;gBACnC,IAAII,UAAU,CAAC,GAAG;oBAChBL,OAAOX,KAAKiB,KAAK,CAACL;oBAClBA,IAAIZ,KAAKI,MAAM;gBACjB,OAAO;oBACLO,OAAO,AAAC,GAAuB,OAArBX,KAAKiB,KAAK,CAACL,GAAGI,QAAO;oBAC/BJ,IAAII,QAAQ;oBACZP,YAAY;gBACd;YACF;YACA,OAAOE;QACT;QACAO,OAAAA,SAAAA;YACET,YAAY;QACd;QACA,IAAIA;YACF,OAAOA;QACT;IACF;AACF;AAEA,8FAA8F;AAC9F,0FAA0F;AAC1F,6FAA6F;AAC7F,sDAAsD;AACtD,IAAMU,kBAAkB,IAAIC,IAAI;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AACD,8FAA8F;AAC9F,gEAAgE;AAChE,IAAMC,gBAAgB,IAAID,IAAI;IAAC;IAAU;IAAO;IAAS;CAAW;AACpE,IAAME,oBAAoB,sCAAsC,0EAA0E;AAC1I,kFAAkF;AAClF,IAAMC,qBAAqB;AAC3B,IAAMC,gBAAgB,aAAa,wFAAwF;AAQpH,SAAS/B,YAAYgC,IAAY;IACtC,IAAI,CAAC,QAAQC,IAAI,CAACD,OAAO,OAAOA;IAChC,IAAME,QAAQpC;IACd,IAAMqC,UAAUtC;IAChB,IAAIuC,cAAc;IAClB,IAAIC,iBAAgC,MAAM,8DAA8D;IACxG,OAAOL,KACJM,KAAK,CAAC,MACNC,GAAG,CAAC,SAAChC;QACJ,IAAI6B,aAAa;YACf,IAAIC,gBAAgB;gBAClB,IAAIA,eAAeJ,IAAI,CAAC1B,OAAO;oBAC7B6B,cAAc;oBACdC,iBAAiB;gBACnB;gBACA,OAAO,IAAIf,MAAM,CAACf,KAAKI,MAAM;YAC/B;YACA,IAAIoB,cAAcE,IAAI,CAAC1B,OAAO;gBAC5B,gFAAgF;gBAChF,sCAAsC;gBACtC6B,cAAc;gBACd,OAAO7B;YACT;YACA,OAAO,IAAIe,MAAM,CAACf,KAAKI,MAAM;QAC/B;QACA,IAAI,CAACwB,QAAQnB,SAAS,EAAE;YACtB,IAAMwB,UAAUN,MAAM5B,IAAI,CAACC;YAC3B,IAAIiC,WAAWN,MAAM/B,OAAO,EAAE,OAAO,IAAImB,MAAM,CAACf,KAAKI,MAAM;QAC7D;QACA,IAAM8B,cAAcN,QAAQnB,SAAS,IAAIT,KAAKG,QAAQ,CAAC,UAAUyB,QAAQlB,IAAI,CAACV,QAAQA;QACtF,IAAI4B,QAAQnB,SAAS,IAAIe,cAAcE,IAAI,CAAC1B,OAAO4B,QAAQV,KAAK,IAAI,+CAA+C;QACnH,sFAAsF;QACtF,qEAAqE;QACrE,IAAMiB,WAAWD,YAAYE,OAAO,CAAC,YAAY;QACjD,IAAID,QAAQ,CAAC,EAAE,KAAK,KAAK;YACvB,IAAME,YAAYd,mBAAmBrB,IAAI,CAACiC;YAC1C,IAAIE,WAAW;gBACb,IAAMC,UAAUD,SAAS,CAAC,EAAE,CAACE,WAAW;gBACxC,IAAMC,eAAeL,QAAQ,CAAC,EAAE,KAAK;gBACrC,IAAI,CAACK,gBAAgBnB,cAAcoB,GAAG,CAACH,UAAU;oBAC/C,8EAA8E;oBAC9E,oEAAoE;oBACpE,IAAI,CAAChB,kBAAkBI,IAAI,CAACS,SAASlB,KAAK,CAACoB,SAAS,CAAC,EAAE,CAACjC,MAAM,IAAI;wBAChEyB,cAAc;wBACdC,iBAAiBR;oBACnB;oBACA,OAAO,IAAIP,MAAM,CAACf,KAAKI,MAAM;gBAC/B;gBACA,IAAIe,gBAAgBsB,GAAG,CAACH,UAAU;oBAChCT,cAAc;oBACd,OAAO,IAAId,MAAM,CAACf,KAAKI,MAAM;gBAC/B;YACF;QACF;QACA,4EAA4E;QAC5E,OAAO8B,YAAY/B,QAAQ,CAAC,OAAOX,cAAc0C,eAAeA;IAClE,GACCQ,IAAI,CAAC;AACV;AAKO,SAASlD,cAAcQ,IAAY;IACxC,IAAIW,MAAM;IACV,IAAIC,IAAI;IACR,MAAOA,IAAIZ,KAAKI,MAAM,CAAE;QACtB,IAAIJ,IAAI,CAACY,EAAE,KAAK,KAAK;YACnBD,OAAOX,IAAI,CAACY,EAAE;YACdA;YACA;QACF;QACA,IAAI+B,IAAI/B;QACR,MAAOZ,IAAI,CAAC2C,EAAE,KAAK,IAAKA;QACxB,IAAMC,IAAID,IAAI/B;QACd,IAAIiC,IAAIF;QACR,IAAIG,aAAa,CAAC;QAClB,IAAIC,WAAW,CAAC;QAChB,MAAOF,IAAI7C,KAAKI,MAAM,CAAE;YACtB,IAAIJ,IAAI,CAAC6C,EAAE,KAAK,KAAK;gBACnBA;gBACA;YACF;YACA,IAAItC,IAAIsC;YACR,MAAO7C,IAAI,CAACO,EAAE,KAAK,IAAKA;YACxB,IAAIA,IAAIsC,MAAMD,GAAG;gBACfE,aAAaD;gBACbE,WAAWxC;gBACX;YACF;YACAsC,IAAItC;QACN;QACA,IAAIuC,cAAc,GAAG;YACnBnC,OAAO,IAAII,MAAM,CAACgC,WAAWnC;YAC7BA,IAAImC;QACN,OAAO;YACLpC,OAAOX,KAAKiB,KAAK,CAACL,GAAG+B;YACrB/B,IAAI+B;QACN;IACF;IACA,OAAOhC;AACT"}
@@ -17,7 +17,7 @@ _export(exports, {
17
17
  }
18
18
  });
19
19
  var _posix = /*#__PURE__*/ _interop_require_default(require("node:path/posix"));
20
- var _fencests = require("../fences.js");
20
+ var _fencests = require("./fences.js");
21
21
  function _array_like_to_array(arr, len) {
22
22
  if (len == null || len > arr.length) len = arr.length;
23
23
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport { maskRegions } from '../fences.ts';\nimport type { FileStat } from '../scan.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its baseKey\n// (indexed, for the incremental resolve below), dst the resolved path or NULL (a\n// queryable dead link), embed whether it's `![[x]]`/`![](x.md)` rather than `[[x]]`/`[](x.md)`\n// -- Obsidian's grain: a target both linked and embedded in the same note is two rows.\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds), internal markdown links, and\n// frontmatter values that are exactly a wikilink (\"[[X]]\"; mid-string and ![[...]] forms are\n// not links there -- Obsidian's rule, probe-verified).\n// One rule for [[inner]] text wherever it appears: alias stripped after |, a leading #\n// keeps the anchor as the target (a same-note self-link needs a heading name), otherwise\n// the anchor splits off. Null = not a link.\nfunction parseWikilinkInner(inner: string): string | null {\n const beforeAlias = inner.split('|')[0].trim();\n if (beforeAlias.startsWith('#')) return beforeAlias.length > 1 ? beforeAlias : null;\n const target = beforeAlias.split('#')[0].trim();\n return target || null;\n}\n\nfunction extract(_raw: string, rawBody: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): Array<{ target: string; embed: boolean }> {\n const body = /\\[\\[|\\]\\(/.test(rawBody) ? maskRegions(rawBody) : rawBody;\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in parseWikilinkInner.\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n // Any internal destination, not only .md-suffixed: Obsidian gives markdown links full\n // linkpath resolution, so \\](Zektor) resolves like [[Zektor]] and \\](#anchor) is a\n // self-edge. External URLs (a scheme anywhere survives the malformed double-paren case)\n // and titled links (dest stops at whitespace) are skipped.\n // A space in the destination is only valid ahead of a quoted title; a domain-shaped first\n // segment (www.example.com/...) is external even without a scheme.\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+(?:\"[^)]*\"|'[^)]*'))?\\)/g)) {\n const dest = m[2].trim();\n // External: a URI scheme prefix (mailto:, tel:, data:, https:...), protocol-relative\n // //host, a www. shorthand, or a scheme anywhere (the malformed double-paren case).\n if (!dest || dest.includes('://')) continue;\n if (/^([a-z][a-z0-9+.-]*:|\\/\\/|www\\.)/i.test(dest)) continue;\n const target = dest.startsWith('#') ? dest : dest.split('#')[0];\n if (target) add(target, m[1] === '!');\n }\n for (const value of Object.values(data ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (typeof item !== 'string') continue;\n const m = /^\\[\\[(.*)\\]\\]$/.exec(item.trim());\n if (!m) continue;\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, false);\n }\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking\n// note's directory, then basename match (shortest path wins on ties -- verified against a\n// cold-loaded Obsidian vault on 6+ real collision pairs; byBase's lists are pre-sorted that way).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n // [[#Heading]]: a same-note anchor, always the note itself -- never depends on other files.\n if (target.startsWith('#')) return src;\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n const candidates = byBase.get(baseKey(clean));\n if (!candidates) return null;\n // The linking note itself wins a basename collision (Obsidian resolves to self before the\n // shortest-path rule -- cold-load verified on the hub corpus).\n return candidates.includes(src) ? src : candidates[0];\n}\n\n// Shortest path wins a basename collision; equal lengths fall back to lexicographic, our own\n// deterministic tiebreak for a case Obsidian itself leaves registration-order-dependent.\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n const paths = files.map((f) => f.relPath).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0));\n for (const path of paths) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes. The UPDATE\n// keys on (src, target): a link/embed sibling pair shares one resolution, so either row's\n// mismatch rewrites both.\nfunction resolveRows(db: DatabaseSync, rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): boolean {\n const update = db.prepare('UPDATE links SET dst = ? WHERE src = ? AND target = ?');\n let changed = false;\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) {\n update.run(dst, row.src, row.target);\n changed = true;\n }\n }\n return changed;\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nfunction resolveAll(db: DatabaseSync, files: FileStat[]): boolean {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const rows = db.prepare('SELECT src, target, dst, embed FROM links').all() as unknown as LinkRow[];\n return resolveRows(db, rows, pathSet, byBase);\n}\n\n// Re-resolve only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst just vanished. All\n// three are indexed lookups, so this never reads the whole table.\nfunction resolveIncremental(db: DatabaseSync, delta: ReconcileDelta): boolean {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n // Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable\n // limit (SQLITE_MAX_VARIABLE_NUMBER, 32766).\n const collectIn = (column: string, keys: string[]) => {\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => '?').join(', ');\n collect(db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${placeholders})`).all(...chunk) as unknown as LinkRow[]);\n }\n };\n\n collectIn('src', delta.reparsed);\n collectIn('target_base', [...changedBasenames]);\n collectIn('dst', delta.vanished);\n\n return resolveRows(db, [...candidates.values()], pathSet, byBase);\n}\n\n// Resolved edges, for rank and for search's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n // One edge per row, as 0.12's grain always had it -- two written targets resolving to one\n // dst stay two edges (a weight the fusion evals were gated on). The only exclusion is an\n // embed whose exact (src, target) also exists as a link: that second row is new with the\n // embed grain and would double an edge that used to be one row. The NOT EXISTS probes the\n // primary key and fires only for embed rows; both branches still scan the table.\n // Self-edges (same-note anchors) are excluded here so PageRank mass is not self-recycled --\n // Obsidian's own graph view hides self-loops too. The rows themselves stay in the table for\n // backlinks/peek.\n const sql = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0 AND src != dst\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1 AND src != dst\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n return (db.prepare(sql).all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// remove() has already deleted the rows by the time afterReconcile runs, so it records here\n// whether they carried edges. Keyed on the delta object, so the state dies with the reconcile.\nconst removedWithLinks = new WeakMap<ReconcileDelta, Set<string>>();\n\nfunction recordRemoved(delta: ReconcileDelta, path: string): void {\n let set = removedWithLinks.get(delta);\n if (!set) {\n set = new Set();\n removedWithLinks.set(delta, set);\n }\n set.add(path);\n}\n\n// remove() runs per path; the vanished list is an array, so cache the Set per delta.\nconst vanishedSets = new WeakMap<ReconcileDelta, Set<string>>();\nfunction vanishedSet(delta: ReconcileDelta): Set<string> {\n let set = vanishedSets.get(delta);\n if (!set) {\n set = new Set(delta.vanished);\n vanishedSets.set(delta, set);\n }\n return set;\n}\n\nexport const links: Feature = {\n name: 'links',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() below instead of wiped here: deleting and\n // re-inserting resets dst to NULL, which made every touch-only reparse read as an edge\n // change and recompute PageRank -- measured as most of the remaining update cost.\n remove(db, path, delta) {\n if (!vanishedSet(delta).has(path)) return;\n const result = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n if (Number(result.changes) > 0) recordRemoved(delta, path);\n },\n store(db, path, extracted, delta) {\n const targets = extracted as Array<{ target: string; embed: boolean }>;\n // Stale rows (target/embed pairs the new parse no longer contains) are real edge removals.\n // \\0 can't appear in a target, so the composite key never collides.\n let stale: ReturnType<ReturnType<DatabaseSync['prepare']>['run']>;\n if (targets.length === 0) {\n stale = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n } else {\n const placeholders = targets.map(() => '?').join(', ');\n const keys = targets.map((t) => `${t.target}\\0${t.embed ? '1' : '0'}`);\n stale = db.prepare(`DELETE FROM links WHERE src = ? AND (target || char(0) || embed) NOT IN (${placeholders})`).run(path, ...keys);\n }\n if (Number(stale.changes) > 0) recordRemoved(delta, path);\n // Upsert preserves dst on surviving rows, so an unchanged link resolves to the same\n // value and reports no change; only genuinely new rows start at NULL.\n const insert = db.prepare('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base');\n // A same-note anchor's dst is always its own src, never another file's basename, so it\n // gets no target_base -- SQL's `IN` never matches NULL, keeping it out of\n // resolveIncremental's cross-file collectIn('target_base', ...) entirely.\n for (const { target, embed } of targets) insert.run(path, target, target.startsWith('#') ? null : baseKey(cleanTarget(target)), embed ? 1 : 0);\n },\n afterReconcile(db, delta) {\n // Any deleted rows that carried edges mean the edge set shrank -- whether the file\n // vanished or was reparsed down to fewer/no links. dstChanged alone misses the\n // reparsed-to-zero-links case (no surviving rows to re-resolve).\n const removeHadLinks = (removedWithLinks.get(delta)?.size ?? 0) > 0;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? resolveAll(db, delta.files) : resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["linkEdges","links","parseWikilinkInner","inner","beforeAlias","split","trim","startsWith","length","target","extract","_raw","rawBody","_search","data","body","test","maskRegions","seen","Set","results","add","embed","key","has","push","matchAll","m","index","dest","includes","Object","values","value","Array","isArray","item","exec","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","candidates","get","buildByBase","files","Map","paths","map","f","relPath","sort","a","b","list","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","collect","collectIn","column","keys","i","chunk","slice","placeholders","reparsed","sql","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","remove","result","Number","changes","store","extracted","targets","stale","t","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/links.ts"],"sourcesContent":["import posix from 'node:path/posix';\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { FileStat } from '../scan/index.ts';\nimport { maskRegions } from './fences.ts';\nimport type { Feature, ReconcileDelta } from './types.ts';\n\n// links(src, target, target_base, dst, embed): target as written, target_base its baseKey\n// (indexed, for the incremental resolve below), dst the resolved path or NULL (a\n// queryable dead link), embed whether it's `![[x]]`/`![](x.md)` rather than `[[x]]`/`[](x.md)`\n// -- Obsidian's grain: a target both linked and embedded in the same note is two rows.\n\n// Wikilinks ([[target]], [[target#anchor|alias]], embeds), internal markdown links, and\n// frontmatter values that are exactly a wikilink (\"[[X]]\"; mid-string and ![[...]] forms are\n// not links there -- Obsidian's rule, probe-verified).\n// One rule for [[inner]] text wherever it appears: alias stripped after |, a leading #\n// keeps the anchor as the target (a same-note self-link needs a heading name), otherwise\n// the anchor splits off. Null = not a link.\nfunction parseWikilinkInner(inner: string): string | null {\n const beforeAlias = inner.split('|')[0].trim();\n if (beforeAlias.startsWith('#')) return beforeAlias.length > 1 ? beforeAlias : null;\n const target = beforeAlias.split('#')[0].trim();\n return target || null;\n}\n\nfunction extract(_raw: string, rawBody: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): Array<{ target: string; embed: boolean }> {\n const body = /\\[\\[|\\]\\(/.test(rawBody) ? maskRegions(rawBody) : rawBody;\n const seen = new Set<string>();\n const results: Array<{ target: string; embed: boolean }> = [];\n const add = (target: string, embed: boolean) => {\n const key = `${target}\\0${embed ? '1' : '0'}`;\n if (seen.has(key)) return;\n seen.add(key);\n results.push({ target, embed });\n };\n // To the first ]], as Obsidian parses it, so a heading or alias holding a lone ] still\n // matches; anchor and alias split off in parseWikilinkInner.\n for (const m of body.matchAll(/\\[\\[(.*?)\\]\\]/g)) {\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, body[(m.index ?? 0) - 1] === '!');\n }\n // Any internal destination, not only .md-suffixed: Obsidian gives markdown links full\n // linkpath resolution, so \\](Zektor) resolves like [[Zektor]] and \\](#anchor) is a\n // self-edge. External URLs (a scheme anywhere survives the malformed double-paren case)\n // and titled links (dest stops at whitespace) are skipped.\n // A space in the destination is only valid ahead of a quoted title; a domain-shaped first\n // segment (www.example.com/...) is external even without a scheme.\n for (const m of body.matchAll(/(!)?\\[[^\\]]*\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+(?:\"[^)]*\"|'[^)]*'))?\\)/g)) {\n const dest = m[2].trim();\n // External: a URI scheme prefix (mailto:, tel:, data:, https:...), protocol-relative\n // //host, a www. shorthand, or a scheme anywhere (the malformed double-paren case).\n if (!dest || dest.includes('://')) continue;\n if (/^([a-z][a-z0-9+.-]*:|\\/\\/|www\\.)/i.test(dest)) continue;\n const target = dest.startsWith('#') ? dest : dest.split('#')[0];\n if (target) add(target, m[1] === '!');\n }\n for (const value of Object.values(data ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (typeof item !== 'string') continue;\n const m = /^\\[\\[(.*)\\]\\]$/.exec(item.trim());\n if (!m) continue;\n const target = parseWikilinkInner(m[1]);\n if (target) add(target, false);\n }\n }\n return results;\n}\n\nfunction cleanTarget(target: string): string {\n return target.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n}\n\nfunction baseKey(path: string): string {\n return posix.basename(path).replace(/\\.md$/i, '').toLowerCase();\n}\n\n// Obsidian-style: exact relative path (with/without .md), path relative to the linking\n// note's directory, then basename match (shortest path wins on ties -- verified against a\n// cold-loaded Obsidian vault on 6+ real collision pairs; byBase's lists are pre-sorted that way).\nfunction resolveTarget(src: string, target: string, pathSet: Set<string>, byBase: Map<string, string[]>): string | null {\n // [[#Heading]]: a same-note anchor, always the note itself -- never depends on other files.\n if (target.startsWith('#')) return src;\n const clean = cleanTarget(target);\n const fromSrc = posix.normalize(posix.join(posix.dirname(src), clean));\n for (const candidate of [clean, `${clean}.md`, fromSrc, `${fromSrc}.md`]) {\n if (pathSet.has(candidate)) return candidate;\n }\n const candidates = byBase.get(baseKey(clean));\n if (!candidates) return null;\n // The linking note itself wins a basename collision (Obsidian resolves to self before the\n // shortest-path rule -- cold-load verified on the hub corpus).\n return candidates.includes(src) ? src : candidates[0];\n}\n\n// Shortest path wins a basename collision; equal lengths fall back to lexicographic, our own\n// deterministic tiebreak for a case Obsidian itself leaves registration-order-dependent.\nfunction buildByBase(files: FileStat[]): Map<string, string[]> {\n const byBase = new Map<string, string[]>();\n const paths = files.map((f) => f.relPath).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0));\n for (const path of paths) {\n const key = baseKey(path);\n const list = byBase.get(key);\n if (list) list.push(path);\n else byBase.set(key, [path]);\n }\n return byBase;\n}\n\ninterface LinkRow {\n src: string;\n target: string;\n dst: string | null;\n embed: number;\n}\n\n// Applies resolveTarget to `rows`, writing only rows whose dst actually changes. The UPDATE\n// keys on (src, target): a link/embed sibling pair shares one resolution, so either row's\n// mismatch rewrites both.\nfunction resolveRows(db: DatabaseSync, rows: LinkRow[], pathSet: Set<string>, byBase: Map<string, string[]>): boolean {\n const update = db.prepare('UPDATE links SET dst = ? WHERE src = ? AND target = ?');\n let changed = false;\n for (const row of rows) {\n const dst = resolveTarget(row.src, row.target, pathSet, byBase);\n if (dst !== row.dst) {\n update.run(dst, row.src, row.target);\n changed = true;\n }\n }\n return changed;\n}\n\n// A new or deleted file can change any note's resolution, so re-resolve the whole table.\n// Fallback for cold builds and large deltas -- see afterReconcile's threshold.\nfunction resolveAll(db: DatabaseSync, files: FileStat[]): boolean {\n const pathSet = new Set(files.map((f) => f.relPath));\n const byBase = buildByBase(files);\n const rows = db.prepare('SELECT src, target, dst, embed FROM links').all() as unknown as LinkRow[];\n return resolveRows(db, rows, pathSet, byBase);\n}\n\n// Re-resolve only what this reconcile could have touched: re-stored sources, links whose\n// target basename matches an added/vanished file, and links whose dst just vanished. All\n// three are indexed lookups, so this never reads the whole table.\nfunction resolveIncremental(db: DatabaseSync, delta: ReconcileDelta): boolean {\n const pathSet = new Set(delta.files.map((f) => f.relPath));\n const byBase = buildByBase(delta.files);\n\n const changedBasenames = new Set<string>();\n for (const p of delta.added) changedBasenames.add(baseKey(p));\n for (const p of delta.vanished) changedBasenames.add(baseKey(p));\n\n // \\0 can't appear in a path or target, so the key never collides; a space can.\n const candidates = new Map<string, LinkRow>();\n const collect = (rows: LinkRow[]) => {\n for (const row of rows) candidates.set(`${row.src}\\0${row.target}\\0${row.embed}`, row);\n };\n // Chunked so a large-but-under-threshold delta never exceeds SQLite's bound-variable\n // limit (SQLITE_MAX_VARIABLE_NUMBER, 32766).\n const collectIn = (column: string, keys: string[]) => {\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => '?').join(', ');\n collect(db.prepare(`SELECT src, target, dst, embed FROM links WHERE ${column} IN (${placeholders})`).all(...chunk) as unknown as LinkRow[]);\n }\n };\n\n collectIn('src', delta.reparsed);\n collectIn('target_base', [...changedBasenames]);\n collectIn('dst', delta.vanished);\n\n return resolveRows(db, [...candidates.values()], pathSet, byBase);\n}\n\n// Resolved edges, for rank and for search's graph expansion.\nexport function linkEdges(db: DatabaseSync): [string, string][] {\n // One edge per row, as 0.12's grain always had it -- two written targets resolving to one\n // dst stay two edges (a weight the fusion evals were gated on). The only exclusion is an\n // embed whose exact (src, target) also exists as a link: that second row is new with the\n // embed grain and would double an edge that used to be one row. The NOT EXISTS probes the\n // primary key and fires only for embed rows; both branches still scan the table.\n // Self-edges (same-note anchors) are excluded here so PageRank mass is not self-recycled --\n // Obsidian's own graph view hides self-loops too. The rows themselves stay in the table for\n // backlinks/peek.\n const sql = `SELECT src, dst FROM links WHERE dst IS NOT NULL AND embed = 0 AND src != dst\n UNION ALL\n SELECT src, dst FROM links l WHERE dst IS NOT NULL AND embed = 1 AND src != dst\n AND NOT EXISTS (SELECT 1 FROM links l0 WHERE l0.src = l.src AND l0.target = l.target AND l0.embed = 0)`;\n return (db.prepare(sql).all() as Array<{ src: string; dst: string }>).map((r) => [r.src, r.dst]);\n}\n\n// remove() has already deleted the rows by the time afterReconcile runs, so it records here\n// whether they carried edges. Keyed on the delta object, so the state dies with the reconcile.\nconst removedWithLinks = new WeakMap<ReconcileDelta, Set<string>>();\n\nfunction recordRemoved(delta: ReconcileDelta, path: string): void {\n let set = removedWithLinks.get(delta);\n if (!set) {\n set = new Set();\n removedWithLinks.set(delta, set);\n }\n set.add(path);\n}\n\n// remove() runs per path; the vanished list is an array, so cache the Set per delta.\nconst vanishedSets = new WeakMap<ReconcileDelta, Set<string>>();\nfunction vanishedSet(delta: ReconcileDelta): Set<string> {\n let set = vanishedSets.get(delta);\n if (!set) {\n set = new Set(delta.vanished);\n vanishedSets.set(delta, set);\n }\n return set;\n}\n\nexport const links: Feature = {\n name: 'links',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS links (src TEXT, target TEXT, target_base TEXT, dst TEXT, embed INTEGER, PRIMARY KEY (src, target, embed))');\n db.exec('CREATE INDEX IF NOT EXISTS links_dst ON links(dst)');\n db.exec('CREATE INDEX IF NOT EXISTS links_target_base ON links(target_base)');\n },\n extract,\n // Reparsed docs are diffed in store() below instead of wiped here: deleting and\n // re-inserting resets dst to NULL, which made every touch-only reparse read as an edge\n // change and recompute PageRank -- measured as most of the remaining update cost.\n remove(db, path, delta) {\n if (!vanishedSet(delta).has(path)) return;\n const result = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n if (Number(result.changes) > 0) recordRemoved(delta, path);\n },\n store(db, path, extracted, delta) {\n const targets = extracted as Array<{ target: string; embed: boolean }>;\n // Stale rows (target/embed pairs the new parse no longer contains) are real edge removals.\n // \\0 can't appear in a target, so the composite key never collides.\n let stale: ReturnType<ReturnType<DatabaseSync['prepare']>['run']>;\n if (targets.length === 0) {\n stale = db.prepare('DELETE FROM links WHERE src = ?').run(path);\n } else {\n const placeholders = targets.map(() => '?').join(', ');\n const keys = targets.map((t) => `${t.target}\\0${t.embed ? '1' : '0'}`);\n stale = db.prepare(`DELETE FROM links WHERE src = ? AND (target || char(0) || embed) NOT IN (${placeholders})`).run(path, ...keys);\n }\n if (Number(stale.changes) > 0) recordRemoved(delta, path);\n // Upsert preserves dst on surviving rows, so an unchanged link resolves to the same\n // value and reports no change; only genuinely new rows start at NULL.\n const insert = db.prepare('INSERT INTO links (src, target, target_base, dst, embed) VALUES (?, ?, ?, NULL, ?) ON CONFLICT(src, target, embed) DO UPDATE SET target_base = excluded.target_base');\n // A same-note anchor's dst is always its own src, never another file's basename, so it\n // gets no target_base -- SQL's `IN` never matches NULL, keeping it out of\n // resolveIncremental's cross-file collectIn('target_base', ...) entirely.\n for (const { target, embed } of targets) insert.run(path, target, target.startsWith('#') ? null : baseKey(cleanTarget(target)), embed ? 1 : 0);\n },\n afterReconcile(db, delta) {\n // Any deleted rows that carried edges mean the edge set shrank -- whether the file\n // vanished or was reparsed down to fewer/no links. dstChanged alone misses the\n // reparsed-to-zero-links case (no surviving rows to re-resolve).\n const removeHadLinks = (removedWithLinks.get(delta)?.size ?? 0) > 0;\n\n const churn = delta.reparsed.length + delta.vanished.length;\n // Past this share of the tree, a full pass is the only one guaranteed to match\n // resolveTarget's ambiguity rules. A cold build clears the threshold on its own.\n const large = delta.files.length === 0 || churn > 0.2 * delta.files.length;\n\n const dstChanged = large ? resolveAll(db, delta.files) : resolveIncremental(db, delta);\n delta.linksChanged = dstChanged || removeHadLinks;\n },\n};\n"],"names":["linkEdges","links","parseWikilinkInner","inner","beforeAlias","split","trim","startsWith","length","target","extract","_raw","rawBody","_search","data","body","test","maskRegions","seen","Set","results","add","embed","key","has","push","matchAll","m","index","dest","includes","Object","values","value","Array","isArray","item","exec","cleanTarget","replace","baseKey","path","posix","basename","toLowerCase","resolveTarget","src","pathSet","byBase","clean","fromSrc","normalize","join","dirname","candidate","candidates","get","buildByBase","files","Map","paths","map","f","relPath","sort","a","b","list","set","resolveRows","db","rows","update","prepare","changed","row","dst","run","resolveAll","all","resolveIncremental","delta","changedBasenames","added","p","vanished","collect","collectIn","column","keys","i","chunk","slice","placeholders","reparsed","sql","r","removedWithLinks","WeakMap","recordRemoved","vanishedSets","vanishedSet","name","schema","remove","result","Number","changes","store","extracted","targets","stale","t","insert","afterReconcile","removeHadLinks","size","churn","large","dstChanged","linksChanged"],"mappings":"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"}
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "rank", {
8
8
  return rank;
9
9
  }
10
10
  });
11
- var _graphts = require("../graph.js");
11
+ var _graphts = require("../graph/graph.js");
12
12
  var _linksts = require("./links.js");
13
13
  function _array_like_to_array(arr, len) {
14
14
  if (len == null || len > arr.length) len = arr.length;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/rank.ts"],"sourcesContent":["import { pagerank } from '../graph.ts';\nimport { linkEdges } from './links.ts';\nimport type { Feature } from './types.ts';\n\n// frontmatter._rank: PageRank over resolved links -- a static \"how load-bearing is this\n// note\" prior. Depends on links (enforced in config.featureEnabled).\n\nexport const rank: Feature = {\n name: 'rank',\n schema(db) {\n const columns = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n if (!columns.some((c) => c.name === '_rank')) db.exec('ALTER TABLE frontmatter ADD COLUMN \"_rank\" REAL');\n },\n afterReconcile(db, delta) {\n // Only an explicit linksChanged:false skips. Rank also depends on the node set, so any\n // add or vanish recomputes -- otherwise a new linkless note keeps _rank NULL.\n if (delta.linksChanged === false && delta.added.length === 0 && delta.vanished.length === 0) return;\n const nodes = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const ranks = pagerank(nodes, linkEdges(db));\n const update = db.prepare('UPDATE frontmatter SET \"_rank\" = ? WHERE \"path\" = ?');\n for (const [path, value] of ranks) update.run(value, path);\n },\n};\n"],"names":["rank","name","schema","db","columns","prepare","all","some","c","exec","afterReconcile","delta","linksChanged","added","length","vanished","nodes","map","r","path","ranks","pagerank","linkEdges","update","value","run"],"mappings":";;;;+BAOaA;;;eAAAA;;;uBAPY;uBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMnB,IAAMA,OAAgB;IAC3BC,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACP,IAAMC,UAAUD,GAAGE,OAAO,CAAC,kCAAkCC,GAAG;QAChE,IAAI,CAACF,QAAQG,IAAI,CAAC,SAACC;mBAAMA,EAAEP,IAAI,KAAK;YAAUE,GAAGM,IAAI,CAAC;IACxD;IACAC,gBAAAA,SAAAA,eAAeP,EAAE,EAAEQ,KAAK;QACtB,uFAAuF;QACvF,8EAA8E;QAC9E,IAAIA,MAAMC,YAAY,KAAK,SAASD,MAAME,KAAK,CAACC,MAAM,KAAK,KAAKH,MAAMI,QAAQ,CAACD,MAAM,KAAK,GAAG;QAC7F,IAAME,QAAQ,AAACb,GAAGE,OAAO,CAAC,kCAAkCC,GAAG,GAA+BW,GAAG,CAAC,SAACC;mBAAMA,EAAEC,IAAI;;QAC/G,IAAMC,QAAQC,IAAAA,iBAAQ,EAACL,OAAOM,IAAAA,kBAAS,EAACnB;QACxC,IAAMoB,SAASpB,GAAGE,OAAO,CAAC;YACrB,kCAAA,2BAAA;;YAAL,QAAK,YAAuBe,0BAAvB,SAAA,6BAAA,QAAA,yBAAA;gBAAA,mCAAA,iBAAOD,uBAAMK;gBAAiBD,OAAOE,GAAG,CAACD,OAAOL;;;YAAhD;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/rank.ts"],"sourcesContent":["import { pagerank } from '../graph/graph.ts';\nimport { linkEdges } from './links.ts';\nimport type { Feature } from './types.ts';\n\n// frontmatter._rank: PageRank over resolved links -- a static \"how load-bearing is this\n// note\" prior. Depends on links (enforced in config.featureEnabled).\n\nexport const rank: Feature = {\n name: 'rank',\n schema(db) {\n const columns = db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>;\n if (!columns.some((c) => c.name === '_rank')) db.exec('ALTER TABLE frontmatter ADD COLUMN \"_rank\" REAL');\n },\n afterReconcile(db, delta) {\n // Only an explicit linksChanged:false skips. Rank also depends on the node set, so any\n // add or vanish recomputes -- otherwise a new linkless note keeps _rank NULL.\n if (delta.linksChanged === false && delta.added.length === 0 && delta.vanished.length === 0) return;\n const nodes = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const ranks = pagerank(nodes, linkEdges(db));\n const update = db.prepare('UPDATE frontmatter SET \"_rank\" = ? WHERE \"path\" = ?');\n for (const [path, value] of ranks) update.run(value, path);\n },\n};\n"],"names":["rank","name","schema","db","columns","prepare","all","some","c","exec","afterReconcile","delta","linksChanged","added","length","vanished","nodes","map","r","path","ranks","pagerank","linkEdges","update","value","run"],"mappings":";;;;+BAOaA;;;eAAAA;;;uBAPY;uBACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMnB,IAAMA,OAAgB;IAC3BC,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACP,IAAMC,UAAUD,GAAGE,OAAO,CAAC,kCAAkCC,GAAG;QAChE,IAAI,CAACF,QAAQG,IAAI,CAAC,SAACC;mBAAMA,EAAEP,IAAI,KAAK;YAAUE,GAAGM,IAAI,CAAC;IACxD;IACAC,gBAAAA,SAAAA,eAAeP,EAAE,EAAEQ,KAAK;QACtB,uFAAuF;QACvF,8EAA8E;QAC9E,IAAIA,MAAMC,YAAY,KAAK,SAASD,MAAME,KAAK,CAACC,MAAM,KAAK,KAAKH,MAAMI,QAAQ,CAACD,MAAM,KAAK,GAAG;QAC7F,IAAME,QAAQ,AAACb,GAAGE,OAAO,CAAC,kCAAkCC,GAAG,GAA+BW,GAAG,CAAC,SAACC;mBAAMA,EAAEC,IAAI;;QAC/G,IAAMC,QAAQC,IAAAA,iBAAQ,EAACL,OAAOM,IAAAA,kBAAS,EAACnB;QACxC,IAAMoB,SAASpB,GAAGE,OAAO,CAAC;YACrB,kCAAA,2BAAA;;YAAL,QAAK,YAAuBe,0BAAvB,SAAA,6BAAA,QAAA,yBAAA;gBAAA,mCAAA,iBAAOD,uBAAMK;gBAAiBD,OAAOE,GAAG,CAACD,OAAOL;;;YAAhD;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IACP;AACF"}
@@ -8,7 +8,8 @@ Object.defineProperty(exports, "sections", {
8
8
  return sections;
9
9
  }
10
10
  });
11
- var _fencests = require("../fences.js");
11
+ var _indexts = require("../chunk/index.js");
12
+ var _fencests = require("./fences.js");
12
13
  // Headings outside fenced code blocks.
13
14
  function extract(raw) {
14
15
  var lines = raw.split('\n');
@@ -28,8 +29,8 @@ function extract(raw) {
28
29
  }
29
30
  for(var s = 0; s < found.length; s++){
30
31
  if (s + 1 < found.length) found[s].endLine = found[s + 1].startLine - 1;
31
- var chars = lines.slice(found[s].startLine - 1, found[s].endLine).join('\n').length;
32
- found[s].tokens = Math.ceil(chars / 4);
32
+ var text = lines.slice(found[s].startLine - 1, found[s].endLine).join('\n');
33
+ found[s].tokens = Math.ceil((0, _indexts.estimateTokens)(text));
33
34
  }
34
35
  return found;
35
36
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/sections.ts"],"sourcesContent":["import { fenceTracker } from '../fences.ts';\nimport type { Feature } from './types.ts';\n\n// sections(path, idx, level, heading, start_line, end_line, tokens): the heading outline,\n// 1-indexed over the raw file so a row is a direct Read range; tokens is a chars/4 estimate.\n\nexport interface Section {\n level: number;\n heading: string;\n startLine: number;\n endLine: number;\n tokens: number;\n}\n\n// Headings outside fenced code blocks.\nfunction extract(raw: string): Section[] {\n const lines = raw.split('\\n');\n const found: Section[] = [];\n const fence = fenceTracker();\n for (let i = 0; i < lines.length; i++) {\n if (fence.feed(lines[i])) continue;\n if (fence.inFence) continue;\n const m = lines[i].match(/^(#{1,6}) +(.*)/);\n if (m) found.push({ level: m[1].length, heading: m[2].trim(), startLine: i + 1, endLine: lines.length, tokens: 0 });\n }\n for (let s = 0; s < found.length; s++) {\n if (s + 1 < found.length) found[s].endLine = found[s + 1].startLine - 1;\n const chars = lines.slice(found[s].startLine - 1, found[s].endLine).join('\\n').length;\n found[s].tokens = Math.ceil(chars / 4);\n }\n return found;\n}\n\nexport const sections: Feature = {\n name: 'sections',\n schema(db) {\n db.exec(`CREATE TABLE IF NOT EXISTS sections (\"path\" TEXT, idx INTEGER, level INTEGER, heading TEXT, start_line INTEGER, end_line INTEGER, tokens INTEGER, PRIMARY KEY (\"path\", idx))`);\n },\n extract,\n remove(db, path) {\n db.prepare('DELETE FROM sections WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const insert = db.prepare('INSERT INTO sections (\"path\", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?)');\n (extracted as Section[]).forEach((s, idx) => insert.run(path, idx, s.level, s.heading, s.startLine, s.endLine, s.tokens));\n },\n};\n"],"names":["sections","extract","raw","lines","split","found","fence","fenceTracker","i","length","feed","inFence","m","match","push","level","heading","trim","startLine","endLine","tokens","s","chars","slice","join","Math","ceil","name","schema","db","exec","remove","path","prepare","run","store","extracted","insert","forEach","idx"],"mappings":";;;;+BAiCaA;;;eAAAA;;;wBAjCgB;AAc7B,uCAAuC;AACvC,SAASC,QAAQC,GAAW;IAC1B,IAAMC,QAAQD,IAAIE,KAAK,CAAC;IACxB,IAAMC,QAAmB,EAAE;IAC3B,IAAMC,QAAQC,IAAAA,sBAAY;IAC1B,IAAK,IAAIC,IAAI,GAAGA,IAAIL,MAAMM,MAAM,EAAED,IAAK;QACrC,IAAIF,MAAMI,IAAI,CAACP,KAAK,CAACK,EAAE,GAAG;QAC1B,IAAIF,MAAMK,OAAO,EAAE;QACnB,IAAMC,IAAIT,KAAK,CAACK,EAAE,CAACK,KAAK,CAAC;QACzB,IAAID,GAAGP,MAAMS,IAAI,CAAC;YAAEC,OAAOH,CAAC,CAAC,EAAE,CAACH,MAAM;YAAEO,SAASJ,CAAC,CAAC,EAAE,CAACK,IAAI;YAAIC,WAAWV,IAAI;YAAGW,SAAShB,MAAMM,MAAM;YAAEW,QAAQ;QAAE;IACnH;IACA,IAAK,IAAIC,IAAI,GAAGA,IAAIhB,MAAMI,MAAM,EAAEY,IAAK;QACrC,IAAIA,IAAI,IAAIhB,MAAMI,MAAM,EAAEJ,KAAK,CAACgB,EAAE,CAACF,OAAO,GAAGd,KAAK,CAACgB,IAAI,EAAE,CAACH,SAAS,GAAG;QACtE,IAAMI,QAAQnB,MAAMoB,KAAK,CAAClB,KAAK,CAACgB,EAAE,CAACH,SAAS,GAAG,GAAGb,KAAK,CAACgB,EAAE,CAACF,OAAO,EAAEK,IAAI,CAAC,MAAMf,MAAM;QACrFJ,KAAK,CAACgB,EAAE,CAACD,MAAM,GAAGK,KAAKC,IAAI,CAACJ,QAAQ;IACtC;IACA,OAAOjB;AACT;AAEO,IAAML,WAAoB;IAC/B2B,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACPA,GAAGC,IAAI,CAAC;IACV;IACA7B,SAAAA;IACA8B,QAAAA,SAAAA,OAAOF,EAAE,EAAEG,IAAI;QACbH,GAAGI,OAAO,CAAC,yCAAyCC,GAAG,CAACF;IAC1D;IACAG,OAAAA,SAAAA,MAAMN,EAAE,EAAEG,IAAI,EAAEI,SAAS;QACvB,IAAMC,SAASR,GAAGI,OAAO,CAAC;QACzBG,UAAwBE,OAAO,CAAC,SAACjB,GAAGkB;mBAAQF,OAAOH,GAAG,CAACF,MAAMO,KAAKlB,EAAEN,KAAK,EAAEM,EAAEL,OAAO,EAAEK,EAAEH,SAAS,EAAEG,EAAEF,OAAO,EAAEE,EAAED,MAAM;;IACzH;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/sections.ts"],"sourcesContent":["import { estimateTokens } from '../chunk/index.ts';\nimport { fenceTracker } from './fences.ts';\nimport type { Feature } from './types.ts';\n\n// sections(path, idx, level, heading, start_line, end_line, tokens): the heading outline,\n// 1-indexed over the raw file so a row is a direct Read range; tokens is estimateTokens (D5),\n// CJK-aware rather than a flat chars/4.\n\nexport interface Section {\n level: number;\n heading: string;\n startLine: number;\n endLine: number;\n tokens: number;\n}\n\n// Headings outside fenced code blocks.\nfunction extract(raw: string): Section[] {\n const lines = raw.split('\\n');\n const found: Section[] = [];\n const fence = fenceTracker();\n for (let i = 0; i < lines.length; i++) {\n if (fence.feed(lines[i])) continue;\n if (fence.inFence) continue;\n const m = lines[i].match(/^(#{1,6}) +(.*)/);\n if (m) found.push({ level: m[1].length, heading: m[2].trim(), startLine: i + 1, endLine: lines.length, tokens: 0 });\n }\n for (let s = 0; s < found.length; s++) {\n if (s + 1 < found.length) found[s].endLine = found[s + 1].startLine - 1;\n const text = lines.slice(found[s].startLine - 1, found[s].endLine).join('\\n');\n found[s].tokens = Math.ceil(estimateTokens(text));\n }\n return found;\n}\n\nexport const sections: Feature = {\n name: 'sections',\n schema(db) {\n db.exec(`CREATE TABLE IF NOT EXISTS sections (\"path\" TEXT, idx INTEGER, level INTEGER, heading TEXT, start_line INTEGER, end_line INTEGER, tokens INTEGER, PRIMARY KEY (\"path\", idx))`);\n },\n extract,\n remove(db, path) {\n db.prepare('DELETE FROM sections WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const insert = db.prepare('INSERT INTO sections (\"path\", idx, level, heading, start_line, end_line, tokens) VALUES (?, ?, ?, ?, ?, ?, ?)');\n (extracted as Section[]).forEach((s, idx) => insert.run(path, idx, s.level, s.heading, s.startLine, s.endLine, s.tokens));\n },\n};\n"],"names":["sections","extract","raw","lines","split","found","fence","fenceTracker","i","length","feed","inFence","m","match","push","level","heading","trim","startLine","endLine","tokens","s","text","slice","join","Math","ceil","estimateTokens","name","schema","db","exec","remove","path","prepare","run","store","extracted","insert","forEach","idx"],"mappings":";;;;+BAmCaA;;;eAAAA;;;uBAnCkB;wBACF;AAe7B,uCAAuC;AACvC,SAASC,QAAQC,GAAW;IAC1B,IAAMC,QAAQD,IAAIE,KAAK,CAAC;IACxB,IAAMC,QAAmB,EAAE;IAC3B,IAAMC,QAAQC,IAAAA,sBAAY;IAC1B,IAAK,IAAIC,IAAI,GAAGA,IAAIL,MAAMM,MAAM,EAAED,IAAK;QACrC,IAAIF,MAAMI,IAAI,CAACP,KAAK,CAACK,EAAE,GAAG;QAC1B,IAAIF,MAAMK,OAAO,EAAE;QACnB,IAAMC,IAAIT,KAAK,CAACK,EAAE,CAACK,KAAK,CAAC;QACzB,IAAID,GAAGP,MAAMS,IAAI,CAAC;YAAEC,OAAOH,CAAC,CAAC,EAAE,CAACH,MAAM;YAAEO,SAASJ,CAAC,CAAC,EAAE,CAACK,IAAI;YAAIC,WAAWV,IAAI;YAAGW,SAAShB,MAAMM,MAAM;YAAEW,QAAQ;QAAE;IACnH;IACA,IAAK,IAAIC,IAAI,GAAGA,IAAIhB,MAAMI,MAAM,EAAEY,IAAK;QACrC,IAAIA,IAAI,IAAIhB,MAAMI,MAAM,EAAEJ,KAAK,CAACgB,EAAE,CAACF,OAAO,GAAGd,KAAK,CAACgB,IAAI,EAAE,CAACH,SAAS,GAAG;QACtE,IAAMI,OAAOnB,MAAMoB,KAAK,CAAClB,KAAK,CAACgB,EAAE,CAACH,SAAS,GAAG,GAAGb,KAAK,CAACgB,EAAE,CAACF,OAAO,EAAEK,IAAI,CAAC;QACxEnB,KAAK,CAACgB,EAAE,CAACD,MAAM,GAAGK,KAAKC,IAAI,CAACC,IAAAA,uBAAc,EAACL;IAC7C;IACA,OAAOjB;AACT;AAEO,IAAML,WAAoB;IAC/B4B,MAAM;IACNC,QAAAA,SAAAA,OAAOC,EAAE;QACPA,GAAGC,IAAI,CAAC;IACV;IACA9B,SAAAA;IACA+B,QAAAA,SAAAA,OAAOF,EAAE,EAAEG,IAAI;QACbH,GAAGI,OAAO,CAAC,yCAAyCC,GAAG,CAACF;IAC1D;IACAG,OAAAA,SAAAA,MAAMN,EAAE,EAAEG,IAAI,EAAEI,SAAS;QACvB,IAAMC,SAASR,GAAGI,OAAO,CAAC;QACzBG,UAAwBE,OAAO,CAAC,SAAClB,GAAGmB;mBAAQF,OAAOH,GAAG,CAACF,MAAMO,KAAKnB,EAAEN,KAAK,EAAEM,EAAEL,OAAO,EAAEK,EAAEH,SAAS,EAAEG,EAAEF,OAAO,EAAEE,EAAED,MAAM;;IACzH;AACF"}
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "tags", {
8
8
  return tags;
9
9
  }
10
10
  });
11
- var _fencests = require("../fences.js");
11
+ var _fencests = require("./fences.js");
12
12
  function _array_like_to_array(arr, len) {
13
13
  if (len == null || len > arr.length) len = arr.length;
14
14
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/tags.ts"],"sourcesContent":["import { maskRegions } from '../fences.ts';\nimport type { Feature } from './types.ts';\n\n// tags(path, tag): Obsidian's file.tags grain -- frontmatter list/string tags plus inline\n// #tags from the prose, deduplicated, source not distinguished. Nested tags store full\n// (book/scifi); `tag = 'book' OR tag LIKE 'book/%'` is how a caller matches the parent too.\n\n// Obsidian treats [[#Heading]] as a same-note link, not a tag.\nconst WIKILINK_RE = /\\[\\[.*?\\]\\]/g; // to the first ]], so a heading holding a lone ] still masks\n// Obsidian doesn't read tags inside HTML markup.\nconst HTML_TAG_RE = /<\\/?[a-zA-Z][^>]*>/g; // tag-shaped only: a comparison's `< 5` must not open a span\n// Anchors on start-of-line or a preceding whitespace/(/[ so `a#b` and URL fragments don't count.\nconst INLINE_TAG_RE = /(?:^|[\\s([])#([\\p{L}\\p{N}_/-]+)/gu;\n// A markdown link destination `](...)` -- `[text](#anchor)` is a same-page link, not a tag.\nconst LINK_DEST_RE = /\\]\\((?:[^()]|\\([^()]*\\))*\\)/g; // one paren-nesting level, as CommonMark destinations allow: (https://x/a_(b)#frag)\n\n// Strips a leading # (frontmatter entries may carry one) and a trailing /; rejects an\n// all-digit result -- a tag needs at least one non-digit character.\nfunction normalizeTag(raw: string): string | null {\n const stripped = raw.replace(/^#/, '').replace(/\\/+$/, '');\n if (!stripped || /^\\d+$/.test(stripped)) return null;\n return stripped;\n}\n\n// data.tags: a YAML list (Obsidian also accepts a bare string). Null members and non-string\n// members are skipped rather than throwing -- `tags:\\n -` parses to [null].\nfunction frontmatterTags(data?: Record<string, unknown>): string[] {\n const raw = data?.tags;\n const list = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];\n const found: string[] = [];\n for (const item of list) {\n if (typeof item !== 'string') continue;\n const tag = normalizeTag(item);\n if (tag) found.push(tag);\n }\n return found;\n}\n\n// #tag tokens outside fenced code blocks, inline code spans, wikilinks, HTML tags, HTML blocks,\n// <!-- --> comments, and link destinations. maskRegions() handles all of those region-level\n// concerns (and code spans, masked once there); this loop only does per-line tag extraction.\nfunction inlineTags(body: string): string[] {\n const found: string[] = [];\n for (const line of maskRegions(body).split('\\n')) {\n if (!line.includes('#')) continue; // most lines; skip the regex work\n let cleaned = line;\n if (cleaned.includes('[[')) cleaned = cleaned.replace(WIKILINK_RE, (m) => ' '.repeat(m.length));\n if (cleaned.includes('](')) cleaned = cleaned.replace(LINK_DEST_RE, (m) => ' '.repeat(m.length));\n if (cleaned.includes('<')) cleaned = cleaned.replace(HTML_TAG_RE, (m) => ' '.repeat(m.length));\n for (const m of cleaned.matchAll(INLINE_TAG_RE)) {\n const tag = normalizeTag(m[1]);\n if (tag) found.push(tag);\n }\n }\n return found;\n}\n\nfunction extract(_raw: string, body: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): string[] {\n return [...new Set([...frontmatterTags(data), ...inlineTags(body)])].sort();\n}\n\nexport const tags: Feature = {\n name: 'tags',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS tags (\"path\" TEXT, tag TEXT, PRIMARY KEY (\"path\", tag))');\n db.exec('CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag)');\n },\n extract,\n // Per-file rows with nothing else to resolve, so only a vanished file needs a delete here;\n // store() below handles a reparse's stale rows itself.\n remove(db, path, delta) {\n if (!delta.vanished.includes(path)) return;\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const found = extracted as string[];\n if (found.length === 0) {\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n } else {\n const placeholders = found.map(() => '?').join(', ');\n db.prepare(`DELETE FROM tags WHERE \"path\" = ? AND tag NOT IN (${placeholders})`).run(path, ...found);\n }\n const insert = db.prepare('INSERT OR IGNORE INTO tags (\"path\", tag) VALUES (?, ?)');\n for (const tag of found) insert.run(path, tag);\n },\n};\n"],"names":["tags","WIKILINK_RE","HTML_TAG_RE","INLINE_TAG_RE","LINK_DEST_RE","normalizeTag","raw","stripped","replace","test","frontmatterTags","data","list","Array","isArray","found","item","tag","push","inlineTags","body","maskRegions","split","line","includes","cleaned","m","repeat","length","matchAll","extract","_raw","_search","Set","sort","name","schema","db","exec","remove","path","delta","vanished","prepare","run","store","extracted","placeholders","map","join","insert"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/tags.ts"],"sourcesContent":["import { maskRegions } from './fences.ts';\nimport type { Feature } from './types.ts';\n\n// tags(path, tag): Obsidian's file.tags grain -- frontmatter list/string tags plus inline\n// #tags from the prose, deduplicated, source not distinguished. Nested tags store full\n// (book/scifi); `tag = 'book' OR tag LIKE 'book/%'` is how a caller matches the parent too.\n\n// Obsidian treats [[#Heading]] as a same-note link, not a tag.\nconst WIKILINK_RE = /\\[\\[.*?\\]\\]/g; // to the first ]], so a heading holding a lone ] still masks\n// Obsidian doesn't read tags inside HTML markup.\nconst HTML_TAG_RE = /<\\/?[a-zA-Z][^>]*>/g; // tag-shaped only: a comparison's `< 5` must not open a span\n// Anchors on start-of-line or a preceding whitespace/(/[ so `a#b` and URL fragments don't count.\nconst INLINE_TAG_RE = /(?:^|[\\s([])#([\\p{L}\\p{N}_/-]+)/gu;\n// A markdown link destination `](...)` -- `[text](#anchor)` is a same-page link, not a tag.\nconst LINK_DEST_RE = /\\]\\((?:[^()]|\\([^()]*\\))*\\)/g; // one paren-nesting level, as CommonMark destinations allow: (https://x/a_(b)#frag)\n\n// Strips a leading # (frontmatter entries may carry one) and a trailing /; rejects an\n// all-digit result -- a tag needs at least one non-digit character.\nfunction normalizeTag(raw: string): string | null {\n const stripped = raw.replace(/^#/, '').replace(/\\/+$/, '');\n if (!stripped || /^\\d+$/.test(stripped)) return null;\n return stripped;\n}\n\n// data.tags: a YAML list (Obsidian also accepts a bare string). Null members and non-string\n// members are skipped rather than throwing -- `tags:\\n -` parses to [null].\nfunction frontmatterTags(data?: Record<string, unknown>): string[] {\n const raw = data?.tags;\n const list = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];\n const found: string[] = [];\n for (const item of list) {\n if (typeof item !== 'string') continue;\n const tag = normalizeTag(item);\n if (tag) found.push(tag);\n }\n return found;\n}\n\n// #tag tokens outside fenced code blocks, inline code spans, wikilinks, HTML tags, HTML blocks,\n// <!-- --> comments, and link destinations. maskRegions() handles all of those region-level\n// concerns (and code spans, masked once there); this loop only does per-line tag extraction.\nfunction inlineTags(body: string): string[] {\n const found: string[] = [];\n for (const line of maskRegions(body).split('\\n')) {\n if (!line.includes('#')) continue; // most lines; skip the regex work\n let cleaned = line;\n if (cleaned.includes('[[')) cleaned = cleaned.replace(WIKILINK_RE, (m) => ' '.repeat(m.length));\n if (cleaned.includes('](')) cleaned = cleaned.replace(LINK_DEST_RE, (m) => ' '.repeat(m.length));\n if (cleaned.includes('<')) cleaned = cleaned.replace(HTML_TAG_RE, (m) => ' '.repeat(m.length));\n for (const m of cleaned.matchAll(INLINE_TAG_RE)) {\n const tag = normalizeTag(m[1]);\n if (tag) found.push(tag);\n }\n }\n return found;\n}\n\nfunction extract(_raw: string, body: string, _search?: { title: string; summary: string }, data?: Record<string, unknown>): string[] {\n return [...new Set([...frontmatterTags(data), ...inlineTags(body)])].sort();\n}\n\nexport const tags: Feature = {\n name: 'tags',\n schema(db) {\n db.exec('CREATE TABLE IF NOT EXISTS tags (\"path\" TEXT, tag TEXT, PRIMARY KEY (\"path\", tag))');\n db.exec('CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag)');\n },\n extract,\n // Per-file rows with nothing else to resolve, so only a vanished file needs a delete here;\n // store() below handles a reparse's stale rows itself.\n remove(db, path, delta) {\n if (!delta.vanished.includes(path)) return;\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n },\n store(db, path, extracted) {\n const found = extracted as string[];\n if (found.length === 0) {\n db.prepare('DELETE FROM tags WHERE \"path\" = ?').run(path);\n } else {\n const placeholders = found.map(() => '?').join(', ');\n db.prepare(`DELETE FROM tags WHERE \"path\" = ? AND tag NOT IN (${placeholders})`).run(path, ...found);\n }\n const insert = db.prepare('INSERT OR IGNORE INTO tags (\"path\", tag) VALUES (?, ?)');\n for (const tag of found) insert.run(path, tag);\n },\n};\n"],"names":["tags","WIKILINK_RE","HTML_TAG_RE","INLINE_TAG_RE","LINK_DEST_RE","normalizeTag","raw","stripped","replace","test","frontmatterTags","data","list","Array","isArray","found","item","tag","push","inlineTags","body","maskRegions","split","line","includes","cleaned","m","repeat","length","matchAll","extract","_raw","_search","Set","sort","name","schema","db","exec","remove","path","delta","vanished","prepare","run","store","extracted","placeholders","map","join","insert"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
+ import type { Block } from '../chunk/index.js';
2
3
  import type { Config, FeatureName } from '../config/index.js';
3
- import type { FileStat } from '../scan.js';
4
+ import type { FileStat } from '../scan/index.js';
4
5
  export interface ReconcileDelta {
5
6
  files: FileStat[];
6
7
  reparsed: string[];
@@ -14,9 +15,10 @@ export interface Feature {
14
15
  extract?(raw: string, body: string, search?: {
15
16
  title: string;
16
17
  summary: string;
17
- }, data?: Record<string, unknown>): unknown;
18
+ }, data?: Record<string, unknown>, cfg?: Config, blocks?: Block[]): unknown;
18
19
  remove?(db: DatabaseSync, path: string, delta: ReconcileDelta): void;
19
20
  store?(db: DatabaseSync, path: string, extracted: unknown, delta: ReconcileDelta): void;
20
21
  afterReconcile?(db: DatabaseSync, delta: ReconcileDelta): void;
21
22
  enabledForFile?(cfg: Config, file: FileStat): boolean;
23
+ signature?(cfg: Config): string | undefined;
22
24
  }
@@ -1,6 +1,7 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
+ import type { Block } from '../chunk/index.js';
2
3
  import type { Config, FeatureName } from '../config/index.js';
3
- import type { FileStat } from '../scan.js';
4
+ import type { FileStat } from '../scan/index.js';
4
5
  export interface ReconcileDelta {
5
6
  files: FileStat[];
6
7
  reparsed: string[];
@@ -14,9 +15,10 @@ export interface Feature {
14
15
  extract?(raw: string, body: string, search?: {
15
16
  title: string;
16
17
  summary: string;
17
- }, data?: Record<string, unknown>): unknown;
18
+ }, data?: Record<string, unknown>, cfg?: Config, blocks?: Block[]): unknown;
18
19
  remove?(db: DatabaseSync, path: string, delta: ReconcileDelta): void;
19
20
  store?(db: DatabaseSync, path: string, extracted: unknown, delta: ReconcileDelta): void;
20
21
  afterReconcile?(db: DatabaseSync, delta: ReconcileDelta): void;
21
22
  enabledForFile?(cfg: Config, file: FileStat): boolean;
23
+ signature?(cfg: Config): string | undefined;
22
24
  }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/graph/graph.ts"],"sourcesContent":["// Link-graph math: PageRank at reconcile, personalized PageRank at query time. Pure JS, no deps.\n\nexport type Edge = [src: string, dst: string];\n\n// Power iteration. `personal` biases both teleport and dangling mass toward seed nodes;\n// without it this is plain PageRank (uniform base). Returns scores summing to ~1.\nexport function pagerank(nodes: string[], edges: Edge[], opts: { damping?: number; iterations?: number; personal?: Map<string, number> } = {}): Map<string, number> {\n const damping = opts.damping ?? 0.85;\n const iterations = opts.iterations ?? 30;\n const n = nodes.length;\n if (n === 0) return new Map();\n\n const index = new Map(nodes.map((node, i) => [node, i]));\n const out: number[][] = nodes.map(() => []);\n for (const [src, dst] of edges) {\n const si = index.get(src);\n const di = index.get(dst);\n if (si !== undefined && di !== undefined) out[si].push(di);\n }\n\n let base: number[];\n if (opts.personal) {\n base = nodes.map(() => 0);\n let total = 0;\n for (const [node, weight] of opts.personal) {\n const i = index.get(node);\n if (i !== undefined && weight > 0) {\n base[i] = weight;\n total += weight;\n }\n }\n if (total === 0) return new Map(nodes.map((node) => [node, 0]));\n base = base.map((b) => b / total);\n } else {\n base = nodes.map(() => 1 / n);\n }\n\n let rank = [...base];\n for (let iter = 0; iter < iterations; iter++) {\n const next = base.map((b) => (1 - damping) * b);\n let dangling = 0;\n for (let i = 0; i < n; i++) {\n if (out[i].length === 0) {\n dangling += rank[i];\n continue;\n }\n const share = (damping * rank[i]) / out[i].length;\n for (const j of out[i]) next[j] += share;\n }\n for (let i = 0; i < n; i++) next[i] += damping * dangling * base[i];\n rank = next;\n }\n return new Map(nodes.map((node, i) => [node, rank[i]]));\n}\n\n// Connective expansion: walk the graph (undirected -- backlinks relate as much as links)\n// from a seed result set. Answers \"what is linked to the notes that matched\".\nexport function personalizedRank(nodes: string[], edges: Edge[], seeds: Map<string, number>): Map<string, number> {\n const undirected: Edge[] = [];\n for (const [src, dst] of edges) {\n undirected.push([src, dst], [dst, src]);\n }\n return pagerank(nodes, undirected, { damping: 0.7, iterations: 15, personal: seeds });\n}\n"],"names":["pagerank","personalizedRank","nodes","edges","opts","damping","iterations","n","length","Map","index","map","node","i","out","src","dst","si","get","di","undefined","push","base","personal","total","weight","b","rank","iter","next","dangling","share","j","seeds","undirected"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/graph/traverse.ts"],"sourcesContent":["// Frontier-by-frontier BFS through a temp visited table, one indexed anti-join per ring: a\n// local question touches a neighborhood, never the whole edge list.\nimport type { DatabaseSync } from 'node:sqlite';\n\ntype Direction = 'forward' | 'reverse' | 'both';\n\ninterface FindPathOptions {\n directed?: boolean;\n maxDepth?: number;\n allowed?: Set<string>;\n}\n\nfunction resetVisited(db: DatabaseSync): void {\n db.exec('DROP TABLE IF EXISTS temp.visited');\n db.exec('CREATE TEMP TABLE visited (path TEXT PRIMARY KEY, depth INTEGER NOT NULL, pred TEXT)');\n}\n\n// Materializes the caller-resolved scope Set into a temp table so it joins like any other\n// indexed column. `extra`, when given, is always in scope -- findPath's `to` endpoint.\nfunction setupAllowed(db: DatabaseSync, allowed: Set<string>, extra?: string): void {\n db.exec('DROP TABLE IF EXISTS temp.allowed_nodes');\n db.exec('CREATE TEMP TABLE allowed_nodes (path TEXT PRIMARY KEY)');\n db.prepare('INSERT INTO allowed_nodes SELECT DISTINCT value FROM json_each(?1)').run(JSON.stringify([...allowed]));\n if (extra !== undefined) db.prepare('INSERT OR IGNORE INTO allowed_nodes VALUES (?)').run(extra);\n}\n\n// Only resolved links (dst IS NOT NULL) are followed, in either direction.\nfunction candidateSelect(direction: Direction): string {\n const forward = 'SELECT l.dst AS path, l.src AS pred FROM links l JOIN visited v ON v.path = l.src AND v.depth = ?1 WHERE l.dst IS NOT NULL';\n const reverse = 'SELECT l.src AS path, l.dst AS pred FROM links l JOIN visited v ON v.path = l.dst AND v.depth = ?1 WHERE l.dst IS NOT NULL';\n if (direction === 'forward') return forward;\n if (direction === 'reverse') return reverse;\n return `${forward} UNION ALL ${reverse}`;\n}\n\n// One ring: candidates adjacent to the previous ring's frontier (?1), anti-joined against\n// everything already visited, optionally filtered to the allowed set, capped (?3) and\n// inserted at the new depth (?2). RETURNING hands back exactly what this ring newly reached.\nfunction ringSql(direction: Direction, hasAllowed: boolean): string {\n return `\nINSERT INTO visited (path, depth, pred)\nSELECT cand.path, ?2, MIN(cand.pred)\nFROM (${candidateSelect(direction)}) cand\nWHERE NOT EXISTS (SELECT 1 FROM visited v2 WHERE v2.path = cand.path)\n${hasAllowed ? 'AND EXISTS (SELECT 1 FROM allowed_nodes a WHERE a.path = cand.path)' : ''}\nGROUP BY cand.path\nORDER BY cand.path\nLIMIT ?3\nRETURNING path`;\n}\n\nfunction reconstructPath(db: DatabaseSync, to: string): string[] {\n const lookup = db.prepare('SELECT pred FROM visited WHERE path = ?');\n const path: string[] = [];\n let cur: string | null = to;\n while (cur !== null) {\n path.push(cur);\n const row = lookup.get(cur) as { pred: string | null } | undefined;\n cur = row ? row.pred : null;\n }\n return path.reverse();\n}\n\n// One BFS from `from`, tracking a predecessor per node. Undirected by default, matching\n// personalizedRank; null when `to` is unreached within `maxDepth`.\nexport function findPath(db: DatabaseSync, from: string, to: string, opts: FindPathOptions = {}): string[] | null {\n if (from === to) return [from];\n\n const direction: Direction = opts.directed ? 'forward' : 'both';\n const maxDepth = opts.maxDepth ?? Infinity;\n if (maxDepth <= 0) return null;\n\n resetVisited(db);\n db.prepare('INSERT INTO visited (path, depth, pred) VALUES (?, 0, NULL)').run(from);\n if (opts.allowed) setupAllowed(db, opts.allowed, to);\n\n const stmt = db.prepare(ringSql(direction, !!opts.allowed));\n for (let ring = 1; ring <= maxDepth; ring++) {\n const rows = stmt.all(ring - 1, ring, -1) as Array<{ path: string }>;\n if (rows.length === 0) break;\n if (rows.some((row) => row.path === to)) return reconstructPath(db, to);\n }\n return null;\n}\n"],"names":["findPath","resetVisited","db","exec","setupAllowed","allowed","extra","prepare","run","JSON","stringify","undefined","candidateSelect","direction","forward","reverse","ringSql","hasAllowed","reconstructPath","to","lookup","path","cur","push","row","get","pred","from","opts","directed","maxDepth","Infinity","stmt","ring","rows","all","length","some"],"mappings":"AAAA,2FAA2F;AAC3F,oEAAoE;;;;;+BAgEpDA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AArDhB,SAASC,aAAaC,EAAgB;IACpCA,GAAGC,IAAI,CAAC;IACRD,GAAGC,IAAI,CAAC;AACV;AAEA,0FAA0F;AAC1F,uFAAuF;AACvF,SAASC,aAAaF,EAAgB,EAAEG,OAAoB,EAAEC,KAAc;IAC1EJ,GAAGC,IAAI,CAAC;IACRD,GAAGC,IAAI,CAAC;IACRD,GAAGK,OAAO,CAAC,sEAAsEC,GAAG,CAACC,KAAKC,SAAS,CAAE,qBAAGL;IACxG,IAAIC,UAAUK,WAAWT,GAAGK,OAAO,CAAC,kDAAkDC,GAAG,CAACF;AAC5F;AAEA,2EAA2E;AAC3E,SAASM,gBAAgBC,SAAoB;IAC3C,IAAMC,UAAU;IAChB,IAAMC,UAAU;IAChB,IAAIF,cAAc,WAAW,OAAOC;IACpC,IAAID,cAAc,WAAW,OAAOE;IACpC,OAAO,AAAC,GAAuBA,OAArBD,SAAQ,eAAqB,OAARC;AACjC;AAEA,0FAA0F;AAC1F,sFAAsF;AACtF,6FAA6F;AAC7F,SAASC,QAAQH,SAAoB,EAAEI,UAAmB;IACxD,OAAO,AAAC,0FAKRA,OAFML,gBAAgBC,YAAW,mFAEuD,OAAxFI,aAAa,wEAAwE,IAAG;AAK1F;AAEA,SAASC,gBAAgBhB,EAAgB,EAAEiB,EAAU;IACnD,IAAMC,SAASlB,GAAGK,OAAO,CAAC;IAC1B,IAAMc,OAAiB,EAAE;IACzB,IAAIC,MAAqBH;IACzB,MAAOG,QAAQ,KAAM;QACnBD,KAAKE,IAAI,CAACD;QACV,IAAME,MAAMJ,OAAOK,GAAG,CAACH;QACvBA,MAAME,MAAMA,IAAIE,IAAI,GAAG;IACzB;IACA,OAAOL,KAAKN,OAAO;AACrB;AAIO,SAASf,SAASE,EAAgB,EAAEyB,IAAY,EAAER,EAAU;QAAES,OAAAA,iEAAwB,CAAC;QAI3EA;IAHjB,IAAID,SAASR,IAAI,OAAO;QAACQ;KAAK;IAE9B,IAAMd,YAAuBe,KAAKC,QAAQ,GAAG,YAAY;IACzD,IAAMC,YAAWF,iBAAAA,KAAKE,QAAQ,cAAbF,4BAAAA,iBAAiBG;IAClC,IAAID,YAAY,GAAG,OAAO;IAE1B7B,aAAaC;IACbA,GAAGK,OAAO,CAAC,+DAA+DC,GAAG,CAACmB;IAC9E,IAAIC,KAAKvB,OAAO,EAAED,aAAaF,IAAI0B,KAAKvB,OAAO,EAAEc;IAEjD,IAAMa,OAAO9B,GAAGK,OAAO,CAACS,QAAQH,WAAW,CAAC,CAACe,KAAKvB,OAAO;IACzD,IAAK,IAAI4B,OAAO,GAAGA,QAAQH,UAAUG,OAAQ;QAC3C,IAAMC,OAAOF,KAAKG,GAAG,CAACF,OAAO,GAAGA,MAAM,CAAC;QACvC,IAAIC,KAAKE,MAAM,KAAK,GAAG;QACvB,IAAIF,KAAKG,IAAI,CAAC,SAACb;mBAAQA,IAAIH,IAAI,KAAKF;YAAK,OAAOD,gBAAgBhB,IAAIiB;IACtE;IACA,OAAO;AACT"}
@@ -1,12 +1,12 @@
1
1
  export type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands/index.js';
2
2
  export { mapTree, peek, presetCoverage, search } from './commands/index.js';
3
- export type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides } from './config/index.js';
4
- export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.js';
3
+ export type { Config, EmbedConfig, FeatureName, InitOverrides, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides, SignalName, SignalWeights } from './config/index.js';
4
+ export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.js';
5
5
  export type { OpenResult } from './db/index.js';
6
6
  export { clearCache, open } from './db/index.js';
7
7
  export type { SenseErrorCode } from './errors.js';
8
8
  export { SenseError } from './errors.js';
9
- export type { Format, Row, RowFormat } from './output.js';
10
- export { printRows } from './output.js';
9
+ export type { Format, Row, RowFormat } from './output/output.js';
10
+ export { printRows } from './output/output.js';
11
11
  export type { WatchEvent, WatchOptions } from './watch.js';
12
12
  export { runWatch } from './watch.js';
@@ -1,12 +1,12 @@
1
1
  export type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands/index.js';
2
2
  export { mapTree, peek, presetCoverage, search } from './commands/index.js';
3
- export type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides } from './config/index.js';
4
- export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.js';
3
+ export type { Config, EmbedConfig, FeatureName, InitOverrides, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides, SignalName, SignalWeights } from './config/index.js';
4
+ export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.js';
5
5
  export type { OpenResult } from './db/index.js';
6
6
  export { clearCache, open } from './db/index.js';
7
7
  export type { SenseErrorCode } from './errors.js';
8
8
  export { SenseError } from './errors.js';
9
- export type { Format, Row, RowFormat } from './output.js';
10
- export { printRows } from './output.js';
9
+ export type { Format, Row, RowFormat } from './output/output.js';
10
+ export { printRows } from './output/output.js';
11
11
  export type { WatchEvent, WatchOptions } from './watch.js';
12
12
  export { runWatch } from './watch.js';
package/dist/cjs/index.js CHANGED
@@ -14,6 +14,9 @@ _export(exports, {
14
14
  get CONFIG_FILENAME () {
15
15
  return _indexts1.CONFIG_FILENAME;
16
16
  },
17
+ get SIGNAL_NAMES () {
18
+ return _indexts1.SIGNAL_NAMES;
19
+ },
17
20
  get STATE_DIR () {
18
21
  return _indexts1.STATE_DIR;
19
22
  },
@@ -61,6 +64,6 @@ var _indexts = require("./commands/index.js");
61
64
  var _indexts1 = require("./config/index.js");
62
65
  var _indexts2 = require("./db/index.js");
63
66
  var _errorsts = require("./errors.js");
64
- var _outputts = require("./output.js");
67
+ var _outputts = require("./output/output.js");
65
68
  var _watchts = require("./watch.js");
66
69
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/index.ts"],"sourcesContent":["// Public library API. Deliberately small: every export is a stability promise;\n// internals (feature registry, graph, scan, meta) stay module-private.\n\nexport type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands/index.ts';\nexport { mapTree, peek, presetCoverage, search } from './commands/index.ts';\nexport type { Config, EmbedConfig, FeatureName, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides } from './config/index.ts';\nexport { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.ts';\nexport type { OpenResult } from './db/index.ts';\nexport { clearCache, open } from './db/index.ts';\nexport type { SenseErrorCode } from './errors.ts';\nexport { SenseError } from './errors.ts';\nexport type { Format, Row, RowFormat } from './output.ts';\nexport { printRows } from './output.ts';\n\nexport type { WatchEvent, WatchOptions } from './watch.ts';\nexport { runWatch } from './watch.ts';\n"],"names":["CONFIG_FILENAME","STATE_DIR","SUPPORTED_CONFIG_VERSION","SenseError","clearCache","initConfig","loadConfig","mapTree","migrateConfig","open","peek","presetCoverage","printRows","runWatch","search"],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;;;;;;;;;;;;QAK9DA;eAAAA,yBAAe;;QAAyCC;eAAAA,mBAAS;;QAAEC;eAAAA,kCAAwB;;QAI3FC;eAAAA,oBAAU;;QAFVC;eAAAA,oBAAU;;QAFOC;eAAAA,oBAAU;;QAAEC;eAAAA,oBAAU;;QAFvCC;eAAAA,gBAAO;;QAEkCC;eAAAA,uBAAa;;QAE1CC;eAAAA,cAAI;;QAJPC;eAAAA,aAAI;;QAAEC;eAAAA,uBAAc;;QAQ7BC;eAAAA,mBAAS;;QAGTC;eAAAA,iBAAQ;;QAXuBC;eAAAA,eAAM;;;uBAAQ;wBAEsD;wBAE3E;wBAEN;wBAED;uBAGD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/index.ts"],"sourcesContent":["// Public library API. Deliberately small: every export is a stability promise;\n// internals (feature registry, graph, scan, meta) stay module-private.\n\nexport type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands/index.ts';\nexport { mapTree, peek, presetCoverage, search } from './commands/index.ts';\nexport type { Config, EmbedConfig, FeatureName, InitOverrides, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides, SignalName, SignalWeights } from './config/index.ts';\nexport { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.ts';\nexport type { OpenResult } from './db/index.ts';\nexport { clearCache, open } from './db/index.ts';\nexport type { SenseErrorCode } from './errors.ts';\nexport { SenseError } from './errors.ts';\nexport type { Format, Row, RowFormat } from './output/output.ts';\nexport { printRows } from './output/output.ts';\n\nexport type { WatchEvent, WatchOptions } from './watch.ts';\nexport { runWatch } from './watch.ts';\n"],"names":["CONFIG_FILENAME","SIGNAL_NAMES","STATE_DIR","SUPPORTED_CONFIG_VERSION","SenseError","clearCache","initConfig","loadConfig","mapTree","migrateConfig","open","peek","presetCoverage","printRows","runWatch","search"],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;;;;;;;;;;;;QAK9DA;eAAAA,yBAAe;;QAAyCC;eAAAA,sBAAY;;QAAEC;eAAAA,mBAAS;;QAAEC;eAAAA,kCAAwB;;QAIzGC;eAAAA,oBAAU;;QAFVC;eAAAA,oBAAU;;QAFOC;eAAAA,oBAAU;;QAAEC;eAAAA,oBAAU;;QAFvCC;eAAAA,gBAAO;;QAEkCC;eAAAA,uBAAa;;QAE1CC;eAAAA,cAAI;;QAJPC;eAAAA,aAAI;;QAAEC;eAAAA,uBAAc;;QAQ7BC;eAAAA,mBAAS;;QAGTC;eAAAA,iBAAQ;;QAXuBC;eAAAA,eAAM;;;uBAAQ;wBAEoE;wBAEzF;wBAEN;wBAED;uBAGD"}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\n\n// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but naming it\n// unquoted in SQL parses as an expression: `plugin-id` is `plugin - id`, and SQLite's error\n// names a fragment (`plugin`) the user never wrote. This maps that fragment back to the\n// columns it could have come from, so the error names the actual fix instead.\n\nfunction startsWithBoundary(name: string, prefix: string): boolean {\n return name.length > prefix.length && name.startsWith(prefix) && /\\W/.test(name[prefix.length]);\n}\n\nexport function columnHint(db: DatabaseSync, err: Error): Error {\n const match = /no such column: (\\S+)/.exec(err.message);\n if (!match) return err;\n const token = match[1];\n // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also\n // covers a column literally named `a.b` -- the full-token check below matches that case\n // directly, so stripping here never needs to special-case it.\n const aliasStripped = token.replace(/^[A-Za-z_]\\w*\\./, '');\n const forms = new Set([token, aliasStripped]);\n const columns = (db.prepare('PRAGMA table_info(frontmatter)').all() as Array<{ name: string }>).map((c) => c.name);\n const candidates = new Set<string>();\n for (const name of columns) {\n for (const form of forms) {\n if (name === form || startsWithBoundary(name, form)) candidates.add(name);\n }\n }\n if (candidates.size === 0) return err;\n const quoted = [...candidates].map((name) => `\"${name.split('\"').join('\"\"')}\"`).join(', ');\n return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted, SQLite parses the punctuation as an operator instead of column syntax.`);\n}\n"],"names":["columnHint","startsWithBoundary","name","prefix","length","startsWith","test","db","err","match","exec","message","token","aliasStripped","replace","forms","Set","columns","prepare","all","map","c","candidates","form","add","size","quoted","split","join","Error"],"mappings":";;;;+BAWgBA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAThB,0FAA0F;AAC1F,4FAA4F;AAC5F,wFAAwF;AACxF,8EAA8E;AAE9E,SAASC,mBAAmBC,IAAY,EAAEC,MAAc;IACtD,OAAOD,KAAKE,MAAM,GAAGD,OAAOC,MAAM,IAAIF,KAAKG,UAAU,CAACF,WAAW,KAAKG,IAAI,CAACJ,IAAI,CAACC,OAAOC,MAAM,CAAC;AAChG;AAEO,SAASJ,WAAWO,EAAgB,EAAEC,GAAU;IACrD,IAAMC,QAAQ,wBAAwBC,IAAI,CAACF,IAAIG,OAAO;IACtD,IAAI,CAACF,OAAO,OAAOD;IACnB,IAAMI,QAAQH,KAAK,CAAC,EAAE;IACtB,uFAAuF;IACvF,wFAAwF;IACxF,8DAA8D;IAC9D,IAAMI,gBAAgBD,MAAME,OAAO,CAAC,mBAAmB;IACvD,IAAMC,QAAQ,IAAIC,IAAI;QAACJ;QAAOC;KAAc;IAC5C,IAAMI,UAAU,AAACV,GAAGW,OAAO,CAAC,kCAAkCC,GAAG,GAA+BC,GAAG,CAAC,SAACC;eAAMA,EAAEnB,IAAI;;IACjH,IAAMoB,aAAa,IAAIN;QAClB,kCAAA,2BAAA;;QAAL,QAAK,YAAcC,4BAAd,SAAA,6BAAA,QAAA,yBAAA,iCAAuB;YAAvB,IAAMf,OAAN;gBACE,mCAAA,4BAAA;;gBAAL,QAAK,aAAca,0BAAd,UAAA,8BAAA,SAAA,0BAAA,kCAAqB;oBAArB,IAAMQ,OAAN;oBACH,IAAIrB,SAASqB,QAAQtB,mBAAmBC,MAAMqB,OAAOD,WAAWE,GAAG,CAACtB;gBACtE;;gBAFK;gBAAA;;;yBAAA,8BAAA;wBAAA;;;wBAAA;8BAAA;;;;QAGP;;QAJK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAKL,IAAIoB,WAAWG,IAAI,KAAK,GAAG,OAAOjB;IAClC,IAAMkB,SAAS,AAAC,qBAAGJ,YAAYF,GAAG,CAAC,SAAClB;eAAS,AAAC,IAA8B,OAA3BA,KAAKyB,KAAK,CAAC,KAAKC,IAAI,CAAC,OAAM;OAAIA,IAAI,CAAC;IACrF,OAAO,IAAIC,MAAM,AAAC,GAAoBH,OAAlBlB,IAAIG,OAAO,EAAC,QAAa,OAAPe,QAAO;AAC/C"}