sensemaking 0.18.4 → 0.19.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 (368) hide show
  1. package/README.md +22 -4
  2. package/dist/cjs/chunk/group.js +10 -23
  3. package/dist/cjs/chunk/group.js.map +1 -1
  4. package/dist/cjs/chunk/parse.js +2 -5
  5. package/dist/cjs/chunk/parse.js.map +1 -1
  6. package/dist/cjs/cli/index.js.map +1 -1
  7. package/dist/cjs/cli/shared.js +6 -10
  8. package/dist/cjs/cli/shared.js.map +1 -1
  9. package/dist/cjs/cli/status.js +44 -10
  10. package/dist/cjs/cli/status.js.map +1 -1
  11. package/dist/cjs/cli.js +2 -8
  12. package/dist/cjs/cli.js.map +1 -1
  13. package/dist/cjs/commands/map.js +1 -3
  14. package/dist/cjs/commands/map.js.map +1 -1
  15. package/dist/cjs/commands/peek.js.map +1 -1
  16. package/dist/cjs/commands/related.js +3 -5
  17. package/dist/cjs/commands/related.js.map +1 -1
  18. package/dist/cjs/commands/scope.js.map +1 -1
  19. package/dist/cjs/commands/search.js +6 -8
  20. package/dist/cjs/commands/search.js.map +1 -1
  21. package/dist/cjs/commands/signals.js.map +1 -1
  22. package/dist/cjs/config/access.js.map +1 -1
  23. package/dist/cjs/config/index.d.cts +1 -1
  24. package/dist/cjs/config/index.d.ts +1 -1
  25. package/dist/cjs/config/index.js +3 -0
  26. package/dist/cjs/config/index.js.map +1 -1
  27. package/dist/cjs/config/load.js +10 -24
  28. package/dist/cjs/config/load.js.map +1 -1
  29. package/dist/cjs/config/resolve.js.map +1 -1
  30. package/dist/cjs/config/signals.js +2 -3
  31. package/dist/cjs/config/signals.js.map +1 -1
  32. package/dist/cjs/config/types.d.cts +2 -1
  33. package/dist/cjs/config/types.d.ts +2 -1
  34. package/dist/cjs/config/types.js +8 -0
  35. package/dist/cjs/config/types.js.map +1 -1
  36. package/dist/cjs/config/validate.d.cts +1 -1
  37. package/dist/cjs/config/validate.d.ts +1 -1
  38. package/dist/cjs/config/validate.js +9 -10
  39. package/dist/cjs/config/validate.js.map +1 -1
  40. package/dist/cjs/embed/cohere.js +2 -2
  41. package/dist/cjs/embed/cohere.js.map +1 -1
  42. package/dist/cjs/embed/distribution.js +3 -3
  43. package/dist/cjs/embed/distribution.js.map +1 -1
  44. package/dist/cjs/embed/handoff.d.cts +3 -0
  45. package/dist/cjs/embed/handoff.d.ts +3 -0
  46. package/dist/cjs/embed/handoff.js +39 -0
  47. package/dist/cjs/embed/handoff.js.map +1 -0
  48. package/dist/cjs/embed/identity.d.cts +11 -10
  49. package/dist/cjs/embed/identity.d.ts +11 -10
  50. package/dist/cjs/embed/identity.js +30 -15
  51. package/dist/cjs/embed/identity.js.map +1 -1
  52. package/dist/cjs/embed/langfit.js +2 -3
  53. package/dist/cjs/embed/langfit.js.map +1 -1
  54. package/dist/cjs/embed/languages.js +2 -4
  55. package/dist/cjs/embed/languages.js.map +1 -1
  56. package/dist/cjs/embed/openai.js +2 -2
  57. package/dist/cjs/embed/openai.js.map +1 -1
  58. package/dist/cjs/embed/query.js +58 -20
  59. package/dist/cjs/embed/query.js.map +1 -1
  60. package/dist/cjs/embed/registry.js +1 -2
  61. package/dist/cjs/embed/registry.js.map +1 -1
  62. package/dist/cjs/embed/static.d.cts +1 -1
  63. package/dist/cjs/embed/static.d.ts +1 -1
  64. package/dist/cjs/embed/static.js +8 -10
  65. package/dist/cjs/embed/static.js.map +1 -1
  66. package/dist/cjs/embed/store.d.cts +2 -2
  67. package/dist/cjs/embed/store.d.ts +2 -2
  68. package/dist/cjs/embed/store.js +48 -34
  69. package/dist/cjs/embed/store.js.map +1 -1
  70. package/dist/cjs/embed/types.js +1 -3
  71. package/dist/cjs/embed/types.js.map +1 -1
  72. package/dist/cjs/errors.d.cts +1 -1
  73. package/dist/cjs/errors.d.ts +1 -1
  74. package/dist/cjs/errors.js.map +1 -1
  75. package/dist/cjs/features/embed.js +19 -17
  76. package/dist/cjs/features/embed.js.map +1 -1
  77. package/dist/cjs/features/fences.js +5 -10
  78. package/dist/cjs/features/fences.js.map +1 -1
  79. package/dist/cjs/features/links.js +20 -37
  80. package/dist/cjs/features/links.js.map +1 -1
  81. package/dist/cjs/features/sections.js +21 -19
  82. package/dist/cjs/features/sections.js.map +1 -1
  83. package/dist/cjs/features/tags.js +5 -8
  84. package/dist/cjs/features/tags.js.map +1 -1
  85. package/dist/cjs/graph/traverse.js +1 -2
  86. package/dist/cjs/graph/traverse.js.map +1 -1
  87. package/dist/cjs/index.d.cts +1 -1
  88. package/dist/cjs/index.d.ts +1 -1
  89. package/dist/cjs/index.js +3 -0
  90. package/dist/cjs/index.js.map +1 -1
  91. package/dist/cjs/output/column-hint.js +3 -6
  92. package/dist/cjs/output/column-hint.js.map +1 -1
  93. package/dist/cjs/output/output.js +7 -14
  94. package/dist/cjs/output/output.js.map +1 -1
  95. package/dist/cjs/output/search-error.js +2 -5
  96. package/dist/cjs/output/search-error.js.map +1 -1
  97. package/dist/cjs/scan/frontmatter.d.cts +1 -0
  98. package/dist/cjs/scan/frontmatter.d.ts +1 -0
  99. package/dist/cjs/scan/frontmatter.js +14 -18
  100. package/dist/cjs/scan/frontmatter.js.map +1 -1
  101. package/dist/cjs/scan/list.js +2 -3
  102. package/dist/cjs/scan/list.js.map +1 -1
  103. package/dist/cjs/scan/reparse.js +12 -26
  104. package/dist/cjs/scan/reparse.js.map +1 -1
  105. package/dist/cjs/scan/worker-error.js.map +1 -1
  106. package/dist/cjs/store/cache.d.cts +2 -0
  107. package/dist/cjs/store/cache.d.ts +2 -0
  108. package/dist/cjs/store/cache.js +20 -0
  109. package/dist/cjs/store/cache.js.map +1 -0
  110. package/dist/cjs/store/duckdb/batch.js +4 -16
  111. package/dist/cjs/store/duckdb/batch.js.map +1 -1
  112. package/dist/cjs/store/duckdb/connection.js +8 -20
  113. package/dist/cjs/store/duckdb/connection.js.map +1 -1
  114. package/dist/cjs/store/duckdb/fieldStats.js +2 -8
  115. package/dist/cjs/store/duckdb/fieldStats.js.map +1 -1
  116. package/dist/cjs/store/duckdb/lexical.js +14 -28
  117. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  118. package/dist/cjs/store/duckdb/native.js.map +1 -1
  119. package/dist/cjs/store/duckdb/open.js +12 -13
  120. package/dist/cjs/store/duckdb/open.js.map +1 -1
  121. package/dist/cjs/store/duckdb/reconcile.js +4 -11
  122. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  123. package/dist/cjs/store/duckdb/sql-functions.js +2 -4
  124. package/dist/cjs/store/duckdb/sql-functions.js.map +1 -1
  125. package/dist/cjs/store/duckdb/store.js +2 -3
  126. package/dist/cjs/store/duckdb/store.js.map +1 -1
  127. package/dist/cjs/store/duckdb/vectors.js +8 -18
  128. package/dist/cjs/store/duckdb/vectors.js.map +1 -1
  129. package/dist/cjs/store/index.d.cts +3 -2
  130. package/dist/cjs/store/index.d.ts +3 -2
  131. package/dist/cjs/store/index.js +7 -8
  132. package/dist/cjs/store/index.js.map +1 -1
  133. package/dist/cjs/store/native.js +6 -16
  134. package/dist/cjs/store/native.js.map +1 -1
  135. package/dist/cjs/store/sql-functions.js.map +1 -1
  136. package/dist/cjs/store/sqlite/connection.js +4 -8
  137. package/dist/cjs/store/sqlite/connection.js.map +1 -1
  138. package/dist/cjs/store/sqlite/fieldStats.js.map +1 -1
  139. package/dist/cjs/store/sqlite/lexical.js +3 -5
  140. package/dist/cjs/store/sqlite/lexical.js.map +1 -1
  141. package/dist/cjs/store/sqlite/open.d.cts +0 -1
  142. package/dist/cjs/store/sqlite/open.d.ts +0 -1
  143. package/dist/cjs/store/sqlite/open.js +22 -40
  144. package/dist/cjs/store/sqlite/open.js.map +1 -1
  145. package/dist/cjs/store/sqlite/reconcile.js +2 -3
  146. package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
  147. package/dist/cjs/store/sqlite/sql-functions.js +2 -4
  148. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
  149. package/dist/cjs/store/sqlite/store.js.map +1 -1
  150. package/dist/cjs/store/transaction.js +1 -3
  151. package/dist/cjs/store/transaction.js.map +1 -1
  152. package/dist/cjs/store/turso/connection.js +2 -6
  153. package/dist/cjs/store/turso/connection.js.map +1 -1
  154. package/dist/cjs/store/turso/fieldStats.js.map +1 -1
  155. package/dist/cjs/store/turso/lexical.d.cts +2 -0
  156. package/dist/cjs/store/turso/lexical.d.ts +2 -0
  157. package/dist/cjs/store/turso/lexical.js +347 -0
  158. package/dist/cjs/store/turso/lexical.js.map +1 -0
  159. package/dist/cjs/store/turso/native.js.map +1 -1
  160. package/dist/cjs/store/turso/open.d.cts +1 -1
  161. package/dist/cjs/store/turso/open.d.ts +1 -1
  162. package/dist/cjs/store/turso/open.js +81 -54
  163. package/dist/cjs/store/turso/open.js.map +1 -1
  164. package/dist/cjs/store/turso/reconcile.js +22 -22
  165. package/dist/cjs/store/turso/reconcile.js.map +1 -1
  166. package/dist/cjs/store/turso/store.js +18 -41
  167. package/dist/cjs/store/turso/store.js.map +1 -1
  168. package/dist/cjs/store/turso/vectors.d.cts +8 -0
  169. package/dist/cjs/store/turso/vectors.d.ts +8 -0
  170. package/dist/cjs/store/turso/vectors.js +435 -0
  171. package/dist/cjs/store/turso/vectors.js.map +1 -0
  172. package/dist/cjs/store/types.js +2 -3
  173. package/dist/cjs/store/types.js.map +1 -1
  174. package/dist/cjs/store/vectors.js.map +1 -1
  175. package/dist/cjs/text/segment.d.cts +1 -0
  176. package/dist/cjs/text/segment.d.ts +1 -0
  177. package/dist/cjs/text/segment.js +10 -14
  178. package/dist/cjs/text/segment.js.map +1 -1
  179. package/dist/cjs/watch.js +2 -5
  180. package/dist/cjs/watch.js.map +1 -1
  181. package/dist/cjs/workers/parse.js.map +1 -1
  182. package/dist/esm/chunk/group.js +10 -23
  183. package/dist/esm/chunk/group.js.map +1 -1
  184. package/dist/esm/chunk/parse.js +4 -8
  185. package/dist/esm/chunk/parse.js.map +1 -1
  186. package/dist/esm/cli/index.js +3 -5
  187. package/dist/esm/cli/index.js.map +1 -1
  188. package/dist/esm/cli/shared.js +12 -22
  189. package/dist/esm/cli/shared.js.map +1 -1
  190. package/dist/esm/cli/status.js +43 -9
  191. package/dist/esm/cli/status.js.map +1 -1
  192. package/dist/esm/cli.js +2 -8
  193. package/dist/esm/cli.js.map +1 -1
  194. package/dist/esm/commands/map.js +1 -3
  195. package/dist/esm/commands/map.js.map +1 -1
  196. package/dist/esm/commands/peek.js.map +1 -1
  197. package/dist/esm/commands/related.js +3 -5
  198. package/dist/esm/commands/related.js.map +1 -1
  199. package/dist/esm/commands/scope.js +3 -5
  200. package/dist/esm/commands/scope.js.map +1 -1
  201. package/dist/esm/commands/search.js +10 -16
  202. package/dist/esm/commands/search.js.map +1 -1
  203. package/dist/esm/commands/signals.js +3 -7
  204. package/dist/esm/commands/signals.js.map +1 -1
  205. package/dist/esm/config/access.js +8 -13
  206. package/dist/esm/config/access.js.map +1 -1
  207. package/dist/esm/config/index.d.ts +1 -1
  208. package/dist/esm/config/index.js +1 -1
  209. package/dist/esm/config/index.js.map +1 -1
  210. package/dist/esm/config/load.js +10 -24
  211. package/dist/esm/config/load.js.map +1 -1
  212. package/dist/esm/config/resolve.js +2 -3
  213. package/dist/esm/config/resolve.js.map +1 -1
  214. package/dist/esm/config/signals.js +3 -5
  215. package/dist/esm/config/signals.js.map +1 -1
  216. package/dist/esm/config/types.d.ts +2 -1
  217. package/dist/esm/config/types.js +8 -2
  218. package/dist/esm/config/types.js.map +1 -1
  219. package/dist/esm/config/validate.d.ts +1 -1
  220. package/dist/esm/config/validate.js +7 -10
  221. package/dist/esm/config/validate.js.map +1 -1
  222. package/dist/esm/embed/cohere.js +2 -2
  223. package/dist/esm/embed/cohere.js.map +1 -1
  224. package/dist/esm/embed/distribution.js +1 -1
  225. package/dist/esm/embed/distribution.js.map +1 -1
  226. package/dist/esm/embed/handoff.d.ts +3 -0
  227. package/dist/esm/embed/handoff.js +21 -0
  228. package/dist/esm/embed/handoff.js.map +1 -0
  229. package/dist/esm/embed/identity.d.ts +11 -10
  230. package/dist/esm/embed/identity.js +29 -26
  231. package/dist/esm/embed/identity.js.map +1 -1
  232. package/dist/esm/embed/langfit.js +2 -3
  233. package/dist/esm/embed/langfit.js.map +1 -1
  234. package/dist/esm/embed/languages.js +2 -4
  235. package/dist/esm/embed/languages.js.map +1 -1
  236. package/dist/esm/embed/openai.js +2 -2
  237. package/dist/esm/embed/openai.js.map +1 -1
  238. package/dist/esm/embed/query.js +21 -6
  239. package/dist/esm/embed/query.js.map +1 -1
  240. package/dist/esm/embed/registry.js +1 -2
  241. package/dist/esm/embed/registry.js.map +1 -1
  242. package/dist/esm/embed/static.d.ts +1 -1
  243. package/dist/esm/embed/static.js +8 -10
  244. package/dist/esm/embed/static.js.map +1 -1
  245. package/dist/esm/embed/store.d.ts +2 -2
  246. package/dist/esm/embed/store.js +28 -23
  247. package/dist/esm/embed/store.js.map +1 -1
  248. package/dist/esm/embed/types.js +1 -3
  249. package/dist/esm/embed/types.js.map +1 -1
  250. package/dist/esm/errors.d.ts +1 -1
  251. package/dist/esm/errors.js.map +1 -1
  252. package/dist/esm/features/embed.js +16 -16
  253. package/dist/esm/features/embed.js.map +1 -1
  254. package/dist/esm/features/fences.js +9 -19
  255. package/dist/esm/features/fences.js.map +1 -1
  256. package/dist/esm/features/links.js +24 -49
  257. package/dist/esm/features/links.js.map +1 -1
  258. package/dist/esm/features/sections.js +20 -20
  259. package/dist/esm/features/sections.js.map +1 -1
  260. package/dist/esm/features/tags.js +5 -8
  261. package/dist/esm/features/tags.js.map +1 -1
  262. package/dist/esm/features/types.js.map +1 -1
  263. package/dist/esm/graph/traverse.js +1 -2
  264. package/dist/esm/graph/traverse.js.map +1 -1
  265. package/dist/esm/index.d.ts +1 -1
  266. package/dist/esm/index.js +1 -1
  267. package/dist/esm/index.js.map +1 -1
  268. package/dist/esm/output/column-hint.js +3 -6
  269. package/dist/esm/output/column-hint.js.map +1 -1
  270. package/dist/esm/output/output.js +9 -20
  271. package/dist/esm/output/output.js.map +1 -1
  272. package/dist/esm/output/search-error.js +2 -5
  273. package/dist/esm/output/search-error.js.map +1 -1
  274. package/dist/esm/scan/frontmatter.d.ts +1 -0
  275. package/dist/esm/scan/frontmatter.js +13 -18
  276. package/dist/esm/scan/frontmatter.js.map +1 -1
  277. package/dist/esm/scan/list.js +2 -3
  278. package/dist/esm/scan/list.js.map +1 -1
  279. package/dist/esm/scan/reparse.js +16 -34
  280. package/dist/esm/scan/reparse.js.map +1 -1
  281. package/dist/esm/scan/worker-error.js.map +1 -1
  282. package/dist/esm/store/cache.d.ts +2 -0
  283. package/dist/esm/store/cache.js +11 -0
  284. package/dist/esm/store/cache.js.map +1 -0
  285. package/dist/esm/store/duckdb/batch.js +4 -16
  286. package/dist/esm/store/duckdb/batch.js.map +1 -1
  287. package/dist/esm/store/duckdb/connection.js +8 -20
  288. package/dist/esm/store/duckdb/connection.js.map +1 -1
  289. package/dist/esm/store/duckdb/fieldStats.js +4 -12
  290. package/dist/esm/store/duckdb/fieldStats.js.map +1 -1
  291. package/dist/esm/store/duckdb/lexical.js +19 -35
  292. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  293. package/dist/esm/store/duckdb/native.js +2 -7
  294. package/dist/esm/store/duckdb/native.js.map +1 -1
  295. package/dist/esm/store/duckdb/open.js +16 -23
  296. package/dist/esm/store/duckdb/open.js.map +1 -1
  297. package/dist/esm/store/duckdb/reconcile.js +4 -11
  298. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  299. package/dist/esm/store/duckdb/sql-functions.js +4 -9
  300. package/dist/esm/store/duckdb/sql-functions.js.map +1 -1
  301. package/dist/esm/store/duckdb/store.js +6 -14
  302. package/dist/esm/store/duckdb/store.js.map +1 -1
  303. package/dist/esm/store/duckdb/vectors.js +14 -31
  304. package/dist/esm/store/duckdb/vectors.js.map +1 -1
  305. package/dist/esm/store/index.d.ts +3 -2
  306. package/dist/esm/store/index.js +5 -6
  307. package/dist/esm/store/index.js.map +1 -1
  308. package/dist/esm/store/native.js +10 -25
  309. package/dist/esm/store/native.js.map +1 -1
  310. package/dist/esm/store/sql-functions.js +3 -5
  311. package/dist/esm/store/sql-functions.js.map +1 -1
  312. package/dist/esm/store/sqlite/connection.js +4 -8
  313. package/dist/esm/store/sqlite/connection.js.map +1 -1
  314. package/dist/esm/store/sqlite/fieldStats.js +2 -6
  315. package/dist/esm/store/sqlite/fieldStats.js.map +1 -1
  316. package/dist/esm/store/sqlite/lexical.js +5 -9
  317. package/dist/esm/store/sqlite/lexical.js.map +1 -1
  318. package/dist/esm/store/sqlite/open.d.ts +0 -1
  319. package/dist/esm/store/sqlite/open.js +21 -41
  320. package/dist/esm/store/sqlite/open.js.map +1 -1
  321. package/dist/esm/store/sqlite/reconcile.js +6 -13
  322. package/dist/esm/store/sqlite/reconcile.js.map +1 -1
  323. package/dist/esm/store/sqlite/sql-functions.js +2 -4
  324. package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
  325. package/dist/esm/store/sqlite/store.js +1 -2
  326. package/dist/esm/store/sqlite/store.js.map +1 -1
  327. package/dist/esm/store/transaction.js +2 -5
  328. package/dist/esm/store/transaction.js.map +1 -1
  329. package/dist/esm/store/turso/connection.js +2 -6
  330. package/dist/esm/store/turso/connection.js.map +1 -1
  331. package/dist/esm/store/turso/fieldStats.js +2 -5
  332. package/dist/esm/store/turso/fieldStats.js.map +1 -1
  333. package/dist/esm/store/turso/lexical.d.ts +2 -0
  334. package/dist/esm/store/turso/lexical.js +128 -0
  335. package/dist/esm/store/turso/lexical.js.map +1 -0
  336. package/dist/esm/store/turso/native.js +2 -7
  337. package/dist/esm/store/turso/native.js.map +1 -1
  338. package/dist/esm/store/turso/open.d.ts +1 -1
  339. package/dist/esm/store/turso/open.js +36 -29
  340. package/dist/esm/store/turso/open.js.map +1 -1
  341. package/dist/esm/store/turso/reconcile.js +22 -22
  342. package/dist/esm/store/turso/reconcile.js.map +1 -1
  343. package/dist/esm/store/turso/store.js +17 -28
  344. package/dist/esm/store/turso/store.js.map +1 -1
  345. package/dist/esm/store/turso/vectors.d.ts +8 -0
  346. package/dist/esm/store/turso/vectors.js +98 -0
  347. package/dist/esm/store/turso/vectors.js.map +1 -0
  348. package/dist/esm/store/types.js +2 -3
  349. package/dist/esm/store/types.js.map +1 -1
  350. package/dist/esm/store/vectors.js +2 -3
  351. package/dist/esm/store/vectors.js.map +1 -1
  352. package/dist/esm/text/segment.d.ts +1 -0
  353. package/dist/esm/text/segment.js +13 -21
  354. package/dist/esm/text/segment.js.map +1 -1
  355. package/dist/esm/watch.js +2 -5
  356. package/dist/esm/watch.js.map +1 -1
  357. package/dist/esm/workers/parse.js.map +1 -1
  358. package/package.json +3 -1
  359. package/schema.json +2 -2
  360. package/skills/sense/SKILL.md +2 -2
  361. package/skills/sense-setup/SKILL.md +3 -3
  362. package/dist/cjs/store/meta.d.cts +0 -3
  363. package/dist/cjs/store/meta.d.ts +0 -3
  364. package/dist/cjs/store/meta.js +0 -215
  365. package/dist/cjs/store/meta.js.map +0 -1
  366. package/dist/esm/store/meta.d.ts +0 -3
  367. package/dist/esm/store/meta.js +0 -17
  368. package/dist/esm/store/meta.js.map +0 -1
@@ -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 async schema(db) {\n await db.exec('CREATE TABLE IF NOT EXISTS tags (\"path\" TEXT, tag TEXT, PRIMARY KEY (\"path\", tag))');\n await db.exec('CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag)');\n },\n extract,\n // Blanket clear-then-reinsert (store() below always writes the full found list back), so\n // remove() covers both a vanished file and a reparsed one that already had rows -- no\n // per-file NOT IN diffing, which cannot be expressed as one statement shared across files.\n async remove(db, paths) {\n if (paths.length === 0) return;\n await db.runBatch(\n 'DELETE FROM tags WHERE \"path\" = ?',\n paths.map((p) => [p])\n );\n },\n async store(db, docs) {\n const rows: unknown[][] = [];\n for (const { path, extracted } of docs) for (const tag of extracted as string[]) rows.push([path, tag]);\n if (rows.length === 0) return;\n await db.runBatch('INSERT OR IGNORE INTO tags (\"path\", tag) VALUES (?, ?)', rows);\n },\n};\n"],"names":["maskRegions","WIKILINK_RE","HTML_TAG_RE","INLINE_TAG_RE","LINK_DEST_RE","normalizeTag","raw","stripped","replace","test","frontmatterTags","data","tags","list","Array","isArray","found","item","tag","push","inlineTags","body","line","split","includes","cleaned","m","repeat","length","matchAll","extract","_raw","_search","Set","sort","name","schema","db","exec","remove","paths","runBatch","map","p","store","docs","rows","path","extracted"],"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, deduplicated, source not distinguished. Nested tags store full (book/scifi); `tag = 'book' OR tag LIKE 'book/%'` 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 the regions maskRegions() masks (fences, code spans, wikilinks, HTML,\n// comments, link destinations); 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 async schema(db) {\n await db.exec('CREATE TABLE IF NOT EXISTS tags (\"path\" TEXT, tag TEXT, PRIMARY KEY (\"path\", tag))');\n await db.exec('CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag)');\n },\n extract,\n // Blanket clear-then-reinsert (store() always writes the full found list back), so remove()\n // covers vanished and reparsed files without per-file NOT IN diffing, which cannot be one statement shared across files.\n async remove(db, paths) {\n if (paths.length === 0) return;\n await db.runBatch(\n 'DELETE FROM tags WHERE \"path\" = ?',\n paths.map((p) => [p])\n );\n },\n async store(db, docs) {\n const rows: unknown[][] = [];\n for (const { path, extracted } of docs) for (const tag of extracted as string[]) rows.push([path, tag]);\n if (rows.length === 0) return;\n await db.runBatch('INSERT OR IGNORE INTO tags (\"path\", tag) VALUES (?, ?)', rows);\n },\n};\n"],"names":["maskRegions","WIKILINK_RE","HTML_TAG_RE","INLINE_TAG_RE","LINK_DEST_RE","normalizeTag","raw","stripped","replace","test","frontmatterTags","data","tags","list","Array","isArray","found","item","tag","push","inlineTags","body","line","split","includes","cleaned","m","repeat","length","matchAll","extract","_raw","_search","Set","sort","name","schema","db","exec","remove","paths","runBatch","map","p","store","docs","rows","path","extracted"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/types.ts"],"sourcesContent":["import type { Block } from '../chunk/index.ts';\nimport type { Config, FeatureName } from '../config/index.ts';\nimport type { FileStat } from '../scan/index.ts';\nimport type { Connection } from '../store/types.ts';\n\n// What changed this reconcile, so hooks act on the delta rather than re-reading the tree.\n// `added` is the brand-new subset of `reparsed`. Hooks communicate through this one object.\nexport interface ReconcileDelta {\n files: FileStat[];\n reparsed: string[];\n added: string[];\n vanished: string[];\n // Set by the links feature: the resolved edge set may differ from before this reconcile.\n linksChanged?: boolean;\n}\n\n// One doc's per-feature extracted value, as store()/remove() see it (reconcile.ts assembles\n// these from ParsedDoc.extracted before calling a feature, once for the whole batch).\nexport interface ExtractedDoc {\n path: string;\n extracted: unknown;\n}\n\n// A feature owns its schema, per-file extraction, batched storage, and any whole-tree pass.\n// Adding a feature = one module here + one entry in the registry (index.ts).\nexport interface Feature {\n name: FeatureName;\n schema(db: Connection): Promise<void>;\n // Pure, per file: raw is the full file, body the prose after frontmatter, search the\n // normalized title/summary strings, data the parsed frontmatter, cfg the resolved config\n // (mirrors enabledForFile's own cfg argument, for a feature whose extraction is config-tunable),\n // blocks the body already parsed once by parseFile, shared with a feature needing a parse tree.\n extract?(raw: string, body: string, search?: { title: string; summary: string }, data?: Record<string, unknown>, cfg?: Config, blocks?: Block[]): unknown;\n // Called once per reconcile (not per file) with every path whose rows need clearing: vanished\n // files, plus reparsed files that already had rows (a feature that diffs its own stale rows\n // in store() instead, e.g. links/tags, filters this list down to just delta.vanished itself).\n remove?(db: Connection, paths: string[], delta: ReconcileDelta): Promise<void>;\n // Called once per reconcile with every freshly parsed doc's extracted value, so a feature\n // writes its rows through one Connection.runBatch call instead of looping per file.\n store?(db: Connection, docs: ExtractedDoc[], delta: ReconcileDelta): Promise<void>;\n // After all rows are current, inside the reconcile transaction (resolution, rank).\n // Hooks run in registry order and communicate through fields on `delta`.\n afterReconcile?(db: Connection, delta: ReconcileDelta): Promise<void>;\n // Per-file opt-out (currently only embed, via FileStat.embed): absent means the feature\n // applies to every file it's otherwise on for. Checked per file before extract/store so a\n // doc in a tree with no embedding model gets no rows for it at all.\n enabledForFile?(cfg: Config, file: FileStat): boolean;\n // This feature's cache-identity segment for config.featureSignature, or undefined to\n // contribute nothing. Composed by access.ts in registry order (features/index.ts's FEATURES).\n signature?(cfg: Config): string | undefined;\n}\n"],"names":[],"mappings":"AAuBA,4FAA4F;AAC5F,6EAA6E;AAC7E,WAyBC"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/features/types.ts"],"sourcesContent":["import type { Block } from '../chunk/index.ts';\nimport type { Config, FeatureName } from '../config/index.ts';\nimport type { FileStat } from '../scan/index.ts';\nimport type { Connection } from '../store/types.ts';\n\n// What changed this reconcile, so hooks act on the delta rather than re-reading the tree.\n// `added` is the brand-new subset of `reparsed`. Hooks communicate through this one object.\nexport interface ReconcileDelta {\n files: FileStat[];\n reparsed: string[];\n added: string[];\n vanished: string[];\n // Set by the links feature: the resolved edge set may differ from before this reconcile.\n linksChanged?: boolean;\n}\n\n// One doc's per-feature extracted value, as store()/remove() see it (reconcile.ts assembles\n// these from ParsedDoc.extracted before calling a feature, once for the whole batch).\nexport interface ExtractedDoc {\n path: string;\n extracted: unknown;\n}\n\n// A feature owns its schema, per-file extraction, batched storage, and any whole-tree pass.\n// Adding a feature = one module here + one entry in the registry (index.ts).\nexport interface Feature {\n name: FeatureName;\n schema(db: Connection): Promise<void>;\n // Pure, per file: raw is the full file, body the prose after frontmatter, search the normalized\n // title/summary, data the frontmatter, cfg the resolved config, blocks the body's parse tree (parsed once by parseFile, shared).\n extract?(raw: string, body: string, search?: { title: string; summary: string }, data?: Record<string, unknown>, cfg?: Config, blocks?: Block[]): unknown;\n // Once per reconcile (not per file) with every path whose rows need clearing: vanished files,\n // plus reparsed ones that already had rows (a feature diffing its own stale rows in store() filters this to delta.vanished).\n remove?(db: Connection, paths: string[], delta: ReconcileDelta): Promise<void>;\n // Called once per reconcile with every freshly parsed doc's extracted value, so a feature\n // writes its rows through one Connection.runBatch call instead of looping per file.\n store?(db: Connection, docs: ExtractedDoc[], delta: ReconcileDelta): Promise<void>;\n // After all rows are current, inside the reconcile transaction (resolution, rank).\n // Hooks run in registry order and communicate through fields on `delta`.\n afterReconcile?(db: Connection, delta: ReconcileDelta): Promise<void>;\n // Per-file opt-out (currently only embed, via FileStat.embed): absent means the feature applies\n // to every file it's otherwise on for; checked before extract/store so such docs get no rows at all.\n enabledForFile?(cfg: Config, file: FileStat): boolean;\n // This feature's cache-identity segment for config.featureSignature, or undefined to\n // contribute nothing. Composed by access.ts in registry order (features/index.ts's FEATURES).\n signature?(cfg: Config): string | undefined;\n}\n"],"names":[],"mappings":"AAuBA,4FAA4F;AAC5F,6EAA6E;AAC7E,WAqBC"}
@@ -26,8 +26,7 @@ function candidateSelect(direction) {
26
26
  return `${forward} UNION ALL ${reverse}`;
27
27
  }
28
28
  // One ring: candidates adjacent to the previous ring's frontier (?1), anti-joined against
29
- // everything already visited, optionally filtered to the allowed set, and inserted at the
30
- // new depth (?2). RETURNING hands back exactly what this ring newly reached.
29
+ // everything already visited, optionally filtered to the allowed set, inserted at the new depth (?2); RETURNING gives what this ring newly reached.
31
30
  function ringSql(direction, hasAllowed) {
32
31
  return `
33
32
  INSERT INTO visited (path, depth, pred)
@@ -1 +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 { Store } from '../store/types.ts';\n\ntype Direction = 'forward' | 'reverse' | 'both';\n\ninterface FindPathOptions {\n directed?: boolean;\n maxDepth?: number;\n allowed?: Set<string>;\n}\n\nasync function resetVisited(store: Store): Promise<void> {\n await store.exec('DROP TABLE IF EXISTS temp.visited');\n await store.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.\nasync function setupAllowed(store: Store, allowed: Set<string>, extra?: string): Promise<void> {\n const all = new Set(allowed);\n if (extra !== undefined) all.add(extra);\n await store.exec('DROP TABLE IF EXISTS temp.allowed_nodes');\n await store.exec('CREATE TEMP TABLE allowed_nodes (path TEXT PRIMARY KEY)');\n await store.runBatch(\n 'INSERT INTO allowed_nodes (path) VALUES (?)',\n [...all].map((p) => [p])\n );\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, and inserted at the\n// 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\nRETURNING path`;\n}\n\nasync function reconstructPath(store: Store, to: string): Promise<string[]> {\n const lookup = await store.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 = (await 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 async function findPath(store: Store, from: string, to: string, opts: FindPathOptions = {}): Promise<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 await resetVisited(store);\n const insertStart = await store.prepare('INSERT INTO visited (path, depth, pred) VALUES (?, 0, NULL)');\n await insertStart.run(from);\n if (opts.allowed) await setupAllowed(store, opts.allowed, to);\n\n const stmt = await store.prepare(ringSql(direction, !!opts.allowed));\n for (let ring = 1; ring <= maxDepth; ring++) {\n const rows = (await stmt.all(ring - 1, ring)) as Array<{ path: string }>;\n if (rows.length === 0) break;\n if (rows.some((row) => row.path === to)) return reconstructPath(store, to);\n }\n return null;\n}\n"],"names":["resetVisited","store","exec","setupAllowed","allowed","extra","all","Set","undefined","add","runBatch","map","p","candidateSelect","direction","forward","reverse","ringSql","hasAllowed","reconstructPath","to","lookup","prepare","path","cur","push","row","get","pred","findPath","from","opts","directed","maxDepth","Infinity","insertStart","run","stmt","ring","rows","length","some"],"mappings":"AAAA,2FAA2F;AAC3F,oEAAoE;AAWpE,eAAeA,aAAaC,KAAY;IACtC,MAAMA,MAAMC,IAAI,CAAC;IACjB,MAAMD,MAAMC,IAAI,CAAC;AACnB;AAEA,0FAA0F;AAC1F,uFAAuF;AACvF,eAAeC,aAAaF,KAAY,EAAEG,OAAoB,EAAEC,KAAc;IAC5E,MAAMC,MAAM,IAAIC,IAAIH;IACpB,IAAIC,UAAUG,WAAWF,IAAIG,GAAG,CAACJ;IACjC,MAAMJ,MAAMC,IAAI,CAAC;IACjB,MAAMD,MAAMC,IAAI,CAAC;IACjB,MAAMD,MAAMS,QAAQ,CAClB,+CACA;WAAIJ;KAAI,CAACK,GAAG,CAAC,CAACC,IAAM;YAACA;SAAE;AAE3B;AAEA,2EAA2E;AAC3E,SAASC,gBAAgBC,SAAoB;IAC3C,MAAMC,UAAU;IAChB,MAAMC,UAAU;IAChB,IAAIF,cAAc,WAAW,OAAOC;IACpC,IAAID,cAAc,WAAW,OAAOE;IACpC,OAAO,GAAGD,QAAQ,WAAW,EAAEC,SAAS;AAC1C;AAEA,0FAA0F;AAC1F,0FAA0F;AAC1F,6EAA6E;AAC7E,SAASC,QAAQH,SAAoB,EAAEI,UAAmB;IACxD,OAAO,CAAC;;;MAGJ,EAAEL,gBAAgBC,WAAW;;AAEnC,EAAEI,aAAa,wEAAwE,GAAG;;;cAG5E,CAAC;AACf;AAEA,eAAeC,gBAAgBlB,KAAY,EAAEmB,EAAU;IACrD,MAAMC,SAAS,MAAMpB,MAAMqB,OAAO,CAAC;IACnC,MAAMC,OAAiB,EAAE;IACzB,IAAIC,MAAqBJ;IACzB,MAAOI,QAAQ,KAAM;QACnBD,KAAKE,IAAI,CAACD;QACV,MAAME,MAAO,MAAML,OAAOM,GAAG,CAACH;QAC9BA,MAAME,MAAMA,IAAIE,IAAI,GAAG;IACzB;IACA,OAAOL,KAAKP,OAAO;AACrB;AAEA,wFAAwF;AACxF,mEAAmE;AACnE,OAAO,eAAea,SAAS5B,KAAY,EAAE6B,IAAY,EAAEV,EAAU,EAAEW,OAAwB,CAAC,CAAC;QAI9EA;IAHjB,IAAID,SAASV,IAAI,OAAO;QAACU;KAAK;IAE9B,MAAMhB,YAAuBiB,KAAKC,QAAQ,GAAG,YAAY;IACzD,MAAMC,YAAWF,iBAAAA,KAAKE,QAAQ,cAAbF,4BAAAA,iBAAiBG;IAClC,IAAID,YAAY,GAAG,OAAO;IAE1B,MAAMjC,aAAaC;IACnB,MAAMkC,cAAc,MAAMlC,MAAMqB,OAAO,CAAC;IACxC,MAAMa,YAAYC,GAAG,CAACN;IACtB,IAAIC,KAAK3B,OAAO,EAAE,MAAMD,aAAaF,OAAO8B,KAAK3B,OAAO,EAAEgB;IAE1D,MAAMiB,OAAO,MAAMpC,MAAMqB,OAAO,CAACL,QAAQH,WAAW,CAAC,CAACiB,KAAK3B,OAAO;IAClE,IAAK,IAAIkC,OAAO,GAAGA,QAAQL,UAAUK,OAAQ;QAC3C,MAAMC,OAAQ,MAAMF,KAAK/B,GAAG,CAACgC,OAAO,GAAGA;QACvC,IAAIC,KAAKC,MAAM,KAAK,GAAG;QACvB,IAAID,KAAKE,IAAI,CAAC,CAACf,MAAQA,IAAIH,IAAI,KAAKH,KAAK,OAAOD,gBAAgBlB,OAAOmB;IACzE;IACA,OAAO;AACT"}
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 { Store } from '../store/types.ts';\n\ntype Direction = 'forward' | 'reverse' | 'both';\n\ninterface FindPathOptions {\n directed?: boolean;\n maxDepth?: number;\n allowed?: Set<string>;\n}\n\nasync function resetVisited(store: Store): Promise<void> {\n await store.exec('DROP TABLE IF EXISTS temp.visited');\n await store.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.\nasync function setupAllowed(store: Store, allowed: Set<string>, extra?: string): Promise<void> {\n const all = new Set(allowed);\n if (extra !== undefined) all.add(extra);\n await store.exec('DROP TABLE IF EXISTS temp.allowed_nodes');\n await store.exec('CREATE TEMP TABLE allowed_nodes (path TEXT PRIMARY KEY)');\n await store.runBatch(\n 'INSERT INTO allowed_nodes (path) VALUES (?)',\n [...all].map((p) => [p])\n );\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, inserted at the new depth (?2); RETURNING gives 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\nRETURNING path`;\n}\n\nasync function reconstructPath(store: Store, to: string): Promise<string[]> {\n const lookup = await store.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 = (await 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 async function findPath(store: Store, from: string, to: string, opts: FindPathOptions = {}): Promise<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 await resetVisited(store);\n const insertStart = await store.prepare('INSERT INTO visited (path, depth, pred) VALUES (?, 0, NULL)');\n await insertStart.run(from);\n if (opts.allowed) await setupAllowed(store, opts.allowed, to);\n\n const stmt = await store.prepare(ringSql(direction, !!opts.allowed));\n for (let ring = 1; ring <= maxDepth; ring++) {\n const rows = (await stmt.all(ring - 1, ring)) as Array<{ path: string }>;\n if (rows.length === 0) break;\n if (rows.some((row) => row.path === to)) return reconstructPath(store, to);\n }\n return null;\n}\n"],"names":["resetVisited","store","exec","setupAllowed","allowed","extra","all","Set","undefined","add","runBatch","map","p","candidateSelect","direction","forward","reverse","ringSql","hasAllowed","reconstructPath","to","lookup","prepare","path","cur","push","row","get","pred","findPath","from","opts","directed","maxDepth","Infinity","insertStart","run","stmt","ring","rows","length","some"],"mappings":"AAAA,2FAA2F;AAC3F,oEAAoE;AAWpE,eAAeA,aAAaC,KAAY;IACtC,MAAMA,MAAMC,IAAI,CAAC;IACjB,MAAMD,MAAMC,IAAI,CAAC;AACnB;AAEA,0FAA0F;AAC1F,uFAAuF;AACvF,eAAeC,aAAaF,KAAY,EAAEG,OAAoB,EAAEC,KAAc;IAC5E,MAAMC,MAAM,IAAIC,IAAIH;IACpB,IAAIC,UAAUG,WAAWF,IAAIG,GAAG,CAACJ;IACjC,MAAMJ,MAAMC,IAAI,CAAC;IACjB,MAAMD,MAAMC,IAAI,CAAC;IACjB,MAAMD,MAAMS,QAAQ,CAClB,+CACA;WAAIJ;KAAI,CAACK,GAAG,CAAC,CAACC,IAAM;YAACA;SAAE;AAE3B;AAEA,2EAA2E;AAC3E,SAASC,gBAAgBC,SAAoB;IAC3C,MAAMC,UAAU;IAChB,MAAMC,UAAU;IAChB,IAAIF,cAAc,WAAW,OAAOC;IACpC,IAAID,cAAc,WAAW,OAAOE;IACpC,OAAO,GAAGD,QAAQ,WAAW,EAAEC,SAAS;AAC1C;AAEA,0FAA0F;AAC1F,oJAAoJ;AACpJ,SAASC,QAAQH,SAAoB,EAAEI,UAAmB;IACxD,OAAO,CAAC;;;MAGJ,EAAEL,gBAAgBC,WAAW;;AAEnC,EAAEI,aAAa,wEAAwE,GAAG;;;cAG5E,CAAC;AACf;AAEA,eAAeC,gBAAgBlB,KAAY,EAAEmB,EAAU;IACrD,MAAMC,SAAS,MAAMpB,MAAMqB,OAAO,CAAC;IACnC,MAAMC,OAAiB,EAAE;IACzB,IAAIC,MAAqBJ;IACzB,MAAOI,QAAQ,KAAM;QACnBD,KAAKE,IAAI,CAACD;QACV,MAAME,MAAO,MAAML,OAAOM,GAAG,CAACH;QAC9BA,MAAME,MAAMA,IAAIE,IAAI,GAAG;IACzB;IACA,OAAOL,KAAKP,OAAO;AACrB;AAEA,wFAAwF;AACxF,mEAAmE;AACnE,OAAO,eAAea,SAAS5B,KAAY,EAAE6B,IAAY,EAAEV,EAAU,EAAEW,OAAwB,CAAC,CAAC;QAI9EA;IAHjB,IAAID,SAASV,IAAI,OAAO;QAACU;KAAK;IAE9B,MAAMhB,YAAuBiB,KAAKC,QAAQ,GAAG,YAAY;IACzD,MAAMC,YAAWF,iBAAAA,KAAKE,QAAQ,cAAbF,4BAAAA,iBAAiBG;IAClC,IAAID,YAAY,GAAG,OAAO;IAE1B,MAAMjC,aAAaC;IACnB,MAAMkC,cAAc,MAAMlC,MAAMqB,OAAO,CAAC;IACxC,MAAMa,YAAYC,GAAG,CAACN;IACtB,IAAIC,KAAK3B,OAAO,EAAE,MAAMD,aAAaF,OAAO8B,KAAK3B,OAAO,EAAEgB;IAE1D,MAAMiB,OAAO,MAAMpC,MAAMqB,OAAO,CAACL,QAAQH,WAAW,CAAC,CAACiB,KAAK3B,OAAO;IAClE,IAAK,IAAIkC,OAAO,GAAGA,QAAQL,UAAUK,OAAQ;QAC3C,MAAMC,OAAQ,MAAMF,KAAK/B,GAAG,CAACgC,OAAO,GAAGA;QACvC,IAAIC,KAAKC,MAAM,KAAK,GAAG;QACvB,IAAID,KAAKE,IAAI,CAAC,CAACf,MAAQA,IAAIH,IAAI,KAAKH,KAAK,OAAOD,gBAAgBlB,OAAOmB;IACzE;IACA,OAAO;AACT"}
@@ -1,7 +1,7 @@
1
1
  export type { Peek, PresetCoverage, SearchOptions, TreeMap } from './commands/index.js';
2
2
  export { mapTree, peek, presetCoverage, search } from './commands/index.js';
3
3
  export type { Config, EmbedConfig, FeatureName, InitOverrides, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides, SignalName, SignalWeights, StoreName } from './config/index.js';
4
- export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.js';
4
+ export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, STORE_NAMES, SUPPORTED_CONFIG_VERSION } from './config/index.js';
5
5
  export type { SenseErrorCode } from './errors.js';
6
6
  export { SenseError } from './errors.js';
7
7
  export type { Format, Row, RowFormat } from './output/output.js';
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Public library API. Deliberately small: every export is a stability promise;
2
2
  // internals (feature registry, graph, scan, meta) stay module-private.
3
3
  export { mapTree, peek, presetCoverage, search } from './commands/index.js';
4
- export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/index.js';
4
+ export { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, STORE_NAMES, SUPPORTED_CONFIG_VERSION } from './config/index.js';
5
5
  export { SenseError } from './errors.js';
6
6
  export { printRows } from './output/output.js';
7
7
  export { clearCache, openStore as open } from './store/index.js';
@@ -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, InitOverrides, Preset, ResolvedConfig, SavedQuery, SavedSearch, SearchOverrides, SignalName, SignalWeights, StoreName } from './config/index.ts';\nexport { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, SUPPORTED_CONFIG_VERSION } from './config/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';\nexport type { OpenResult } from './store/index.ts';\nexport { clearCache, openStore as open } from './store/index.ts';\nexport type { Capability, DocumentStore, FieldStat, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './store/types.ts';\n\nexport type { WatchEvent, WatchOptions } from './watch.ts';\nexport { runWatch } from './watch.ts';\n"],"names":["mapTree","peek","presetCoverage","search","CONFIG_FILENAME","initConfig","loadConfig","migrateConfig","SIGNAL_NAMES","STATE_DIR","SUPPORTED_CONFIG_VERSION","SenseError","printRows","clearCache","openStore","open","runWatch"],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;AAGvE,SAASA,OAAO,EAAEC,IAAI,EAAEC,cAAc,EAAEC,MAAM,QAAQ,sBAAsB;AAE5E,SAASC,eAAe,EAAEC,UAAU,EAAEC,UAAU,EAAEC,aAAa,EAAEC,YAAY,EAAEC,SAAS,EAAEC,wBAAwB,QAAQ,oBAAoB;AAE9I,SAASC,UAAU,QAAQ,cAAc;AAEzC,SAASC,SAAS,QAAQ,qBAAqB;AAE/C,SAASC,UAAU,EAAEC,aAAaC,IAAI,QAAQ,mBAAmB;AAIjE,SAASC,QAAQ,QAAQ,aAAa"}
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, StoreName } from './config/index.ts';\nexport { CONFIG_FILENAME, initConfig, loadConfig, migrateConfig, SIGNAL_NAMES, STATE_DIR, STORE_NAMES, SUPPORTED_CONFIG_VERSION } from './config/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';\nexport type { OpenResult } from './store/index.ts';\nexport { clearCache, openStore as open } from './store/index.ts';\nexport type { Capability, DocumentStore, FieldStat, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './store/types.ts';\n\nexport type { WatchEvent, WatchOptions } from './watch.ts';\nexport { runWatch } from './watch.ts';\n"],"names":["mapTree","peek","presetCoverage","search","CONFIG_FILENAME","initConfig","loadConfig","migrateConfig","SIGNAL_NAMES","STATE_DIR","STORE_NAMES","SUPPORTED_CONFIG_VERSION","SenseError","printRows","clearCache","openStore","open","runWatch"],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;AAGvE,SAASA,OAAO,EAAEC,IAAI,EAAEC,cAAc,EAAEC,MAAM,QAAQ,sBAAsB;AAE5E,SAASC,eAAe,EAAEC,UAAU,EAAEC,UAAU,EAAEC,aAAa,EAAEC,YAAY,EAAEC,SAAS,EAAEC,WAAW,EAAEC,wBAAwB,QAAQ,oBAAoB;AAE3J,SAASC,UAAU,QAAQ,cAAc;AAEzC,SAASC,SAAS,QAAQ,qBAAqB;AAE/C,SAASC,UAAU,EAAEC,aAAaC,IAAI,QAAQ,mBAAmB;AAIjE,SAASC,QAAQ,QAAQ,aAAa"}
@@ -1,7 +1,5 @@
1
1
  // A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL
2
- // it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment
3
- // (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table
4
- // "..." not found`); this maps either back to the real column so the error names the fix.
2
+ // it parses as an expression; this maps SQLite's/DuckDB's fragment error back to the real column.
5
3
  function startsWithBoundary(name, prefix) {
6
4
  return name.length > prefix.length && name.startsWith(prefix) && /\W/.test(name[prefix.length]);
7
5
  }
@@ -12,9 +10,8 @@ export function columnHint(columns, err) {
12
10
  const match = /no such column: (\S+)|Referenced column "([^"]+)" not found in FROM clause|Referenced table "([^"]+)" not found/.exec(err.message);
13
11
  if (!match) return err;
14
12
  const token = (_ref = (_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : match[2]) !== null && _ref !== void 0 ? _ref : match[3];
15
- // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also
16
- // covers a column literally named `a.b` -- the full-token check below matches that case
17
- // directly, so stripping here never needs to special-case it.
13
+ // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, so a column
14
+ // literally named `a.b` still matches via the full-token check below.
18
15
  const aliasStripped = token.replace(/^[A-Za-z_]\w*\./, '');
19
16
  const forms = new Set([
20
17
  token,
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL\n// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment\n// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table\n// \"...\" not found`); this maps either back to the real column so the error names the fix.\n\nfunction startsWithBoundary(name: string, prefix: string): boolean {\n return name.length > prefix.length && name.startsWith(prefix) && /\\W/.test(name[prefix.length]);\n}\n\n// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this\n// stays a pure function rather than a store dependency.\nexport function columnHint(columns: string[], err: Error): Error {\n const match = /no such column: (\\S+)|Referenced column \"([^\"]+)\" not found in FROM clause|Referenced table \"([^\"]+)\" not found/.exec(err.message);\n if (!match) return err;\n const token = match[1] ?? match[2] ?? match[3];\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 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, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);\n}\n"],"names":["startsWithBoundary","name","prefix","length","startsWith","test","columnHint","columns","err","match","exec","message","token","aliasStripped","replace","forms","Set","candidates","form","add","size","quoted","map","split","join","Error"],"mappings":"AAAA,gGAAgG;AAChG,8FAA8F;AAC9F,+FAA+F;AAC/F,0FAA0F;AAE1F,SAASA,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;AAEA,6FAA6F;AAC7F,wDAAwD;AACxD,OAAO,SAASG,WAAWC,OAAiB,EAAEC,GAAU;QAGxCC,MAAAA;IAFd,MAAMA,QAAQ,kHAAkHC,IAAI,CAACF,IAAIG,OAAO;IAChJ,IAAI,CAACF,OAAO,OAAOD;IACnB,MAAMI,SAAQH,QAAAA,UAAAA,KAAK,CAAC,EAAE,cAARA,qBAAAA,UAAYA,KAAK,CAAC,EAAE,cAApBA,kBAAAA,OAAwBA,KAAK,CAAC,EAAE;IAC9C,uFAAuF;IACvF,wFAAwF;IACxF,8DAA8D;IAC9D,MAAMI,gBAAgBD,MAAME,OAAO,CAAC,mBAAmB;IACvD,MAAMC,QAAQ,IAAIC,IAAI;QAACJ;QAAOC;KAAc;IAC5C,MAAMI,aAAa,IAAID;IACvB,KAAK,MAAMf,QAAQM,QAAS;QAC1B,KAAK,MAAMW,QAAQH,MAAO;YACxB,IAAId,SAASiB,QAAQlB,mBAAmBC,MAAMiB,OAAOD,WAAWE,GAAG,CAAClB;QACtE;IACF;IACA,IAAIgB,WAAWG,IAAI,KAAK,GAAG,OAAOZ;IAClC,MAAMa,SAAS;WAAIJ;KAAW,CAACK,GAAG,CAAC,CAACrB,OAAS,CAAC,CAAC,EAAEA,KAAKsB,KAAK,CAAC,KAAKC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAEA,IAAI,CAAC;IACrF,OAAO,IAAIC,MAAM,GAAGjB,IAAIG,OAAO,CAAC,IAAI,EAAEU,OAAO,kIAAkI,CAAC;AAClL"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL\n// it parses as an expression; this maps SQLite's/DuckDB's fragment error back to the real column.\n\nfunction startsWithBoundary(name: string, prefix: string): boolean {\n return name.length > prefix.length && name.startsWith(prefix) && /\\W/.test(name[prefix.length]);\n}\n\n// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this\n// stays a pure function rather than a store dependency.\nexport function columnHint(columns: string[], err: Error): Error {\n const match = /no such column: (\\S+)|Referenced column \"([^\"]+)\" not found in FROM clause|Referenced table \"([^\"]+)\" not found/.exec(err.message);\n if (!match) return err;\n const token = match[1] ?? match[2] ?? match[3];\n // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, so a column\n // literally named `a.b` still matches via the full-token check below.\n const aliasStripped = token.replace(/^[A-Za-z_]\\w*\\./, '');\n const forms = new Set([token, aliasStripped]);\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, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);\n}\n"],"names":["startsWithBoundary","name","prefix","length","startsWith","test","columnHint","columns","err","match","exec","message","token","aliasStripped","replace","forms","Set","candidates","form","add","size","quoted","map","split","join","Error"],"mappings":"AAAA,gGAAgG;AAChG,kGAAkG;AAElG,SAASA,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;AAEA,6FAA6F;AAC7F,wDAAwD;AACxD,OAAO,SAASG,WAAWC,OAAiB,EAAEC,GAAU;QAGxCC,MAAAA;IAFd,MAAMA,QAAQ,kHAAkHC,IAAI,CAACF,IAAIG,OAAO;IAChJ,IAAI,CAACF,OAAO,OAAOD;IACnB,MAAMI,SAAQH,QAAAA,UAAAA,KAAK,CAAC,EAAE,cAARA,qBAAAA,UAAYA,KAAK,CAAC,EAAE,cAApBA,kBAAAA,OAAwBA,KAAK,CAAC,EAAE;IAC9C,wFAAwF;IACxF,sEAAsE;IACtE,MAAMI,gBAAgBD,MAAME,OAAO,CAAC,mBAAmB;IACvD,MAAMC,QAAQ,IAAIC,IAAI;QAACJ;QAAOC;KAAc;IAC5C,MAAMI,aAAa,IAAID;IACvB,KAAK,MAAMf,QAAQM,QAAS;QAC1B,KAAK,MAAMW,QAAQH,MAAO;YACxB,IAAId,SAASiB,QAAQlB,mBAAmBC,MAAMiB,OAAOD,WAAWE,GAAG,CAAClB;QACtE;IACF;IACA,IAAIgB,WAAWG,IAAI,KAAK,GAAG,OAAOZ;IAClC,MAAMa,SAAS;WAAIJ;KAAW,CAACK,GAAG,CAAC,CAACrB,OAAS,CAAC,CAAC,EAAEA,KAAKsB,KAAK,CAAC,KAAKC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAEA,IAAI,CAAC;IACrF,OAAO,IAAIC,MAAM,GAAGjB,IAAIG,OAAO,CAAC,IAAI,EAAEU,OAAO,kIAAkI,CAAC;AAClL"}
@@ -1,13 +1,12 @@
1
1
  // rows -> table (default) | json | csv
2
2
  // Warned about on stderr (keeps --format json stdout machine-readable), never truncated.
3
3
  const OVERSIZE_BYTES = 50000;
4
- // Flatten embedded newlines so they can't break table column alignment; JSON output is left alone.
4
+ // Flatten embedded newlines so they can't break table column alignment; JSON output keeps them as-is.
5
5
  function cell(value) {
6
6
  return String(value !== null && value !== void 0 ? value : '').replace(/\s*\n\s*/g, ' ');
7
7
  }
8
- // RFC 4180, and deliberately not cell(): csv is the format for redirecting a result to a
9
- // file, so it keeps every character, embedded newlines included. What it cannot carry is
10
- // NULL vs empty string, or a value's type -- that is what json is for.
8
+ // RFC 4180, deliberately not cell(): csv is the redirect-to-file format, so it keeps every
9
+ // character, newlines included; what it cannot carry (NULL vs empty, types) is what json is for.
11
10
  function csvField(value) {
12
11
  const text = value === null || value === undefined ? '' : String(value);
13
12
  return /["\r\n,]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
@@ -19,14 +18,8 @@ function warnOversize(bytes, rowCount) {
19
18
  if (bytes <= OVERSIZE_BYTES) return;
20
19
  console.warn(`warning: result is ${Math.round(bytes / 1000)} KB across ${rowCount} row(s); consider a LIMIT, fewer columns, snippet() instead of whole values, or --format csv redirected to a file`);
21
20
  }
22
- // Header names come from a row wherever there is one: a statement's column list keeps
23
- // duplicates that the row object has already collapsed, so `SELECT a AS x, b AS x` would print
24
- // b's value under both headers. `columns` is the fallback that labels a 0-row result, and with
25
- // no rows and no statement (a bounded command that found nothing) csv writes nothing at all --
26
- // a bare newline would read to a csv parser as one empty record.
27
- // JSON.stringify throws on BigInt, and stores return int64 as BigInt (DuckDB does). A value
28
- // that fits a safe integer stays a number; a larger one becomes its decimal string, since a
29
- // JSON number cannot carry it losslessly.
21
+ // JSON.stringify throws on BigInt, and stores return int64 as BigInt (DuckDB does): a value
22
+ // that fits a safe integer stays a number, a larger one becomes its decimal string.
30
23
  const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
31
24
  const bigintSafe = (_key, value)=>typeof value === 'bigint' ? value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value.toString() : value;
32
25
  export function stringifyJson(value, indent) {
@@ -34,8 +27,8 @@ export function stringifyJson(value, indent) {
34
27
  }
35
28
  export function printRows(rows, format, columns) {
36
29
  if (format === 'csv') {
37
- // Collapsed the same way a row object collapses them, so a statement naming a column
38
- // twice describes itself identically whether it matched anything or not.
30
+ // Names from a row wherever there is one: the statement's column list keeps duplicates the
31
+ // row collapsed. Nothing at all with no row and no columns -- a bare newline is an empty record.
39
32
  const names = rows.length > 0 ? Object.keys(rows[0]) : [
40
33
  ...new Set(columns !== null && columns !== void 0 ? columns : [])
41
34
  ];
@@ -53,12 +46,8 @@ export function printRows(rows, format, columns) {
53
46
  console.log(rendered);
54
47
  warnOversize(Buffer.byteLength(rendered), rows.length);
55
48
  }
56
- // Streaming counterpart for the one unbounded caller, `sense sql`: rows arrive from an async
57
- // iterator (raw.prepare().iterate(), one microtask for the whole statement, not per row), so a
58
- // large result is never held here as rows and again as a rendered string. Table buffers by
59
- // necessity (a column is as wide as its widest cell, which the last row can change); json and
60
- // csv write row by row. The writes are synchronous, so when stdout is a pipe Node buffers what
61
- // the reader has not taken yet -- the saving is this module's copies, not the operating system's.
49
+ // Streaming counterpart for the one unbounded caller, `sense sql`: the result is never held as
50
+ // rows and again as a rendered string. Table buffers (column width depends on the last row); json/csv write row by row.
62
51
  export async function printRowStream(rows, format, columns) {
63
52
  const iterator = rows[Symbol.asyncIterator]();
64
53
  // One step before any write. FTS5 parses a MATCH expression at step time rather than at
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/output.ts"],"sourcesContent":["// rows -> table (default) | json | csv\n\nexport type Row = Record<string, unknown>;\n\n// map/peek/status/path render structures rather than a row set, so csv has nothing to mean\n// there; the commands that emit rows take RowFormat.\nexport type Format = 'table' | 'json';\nexport type RowFormat = Format | 'csv';\n\n// Warned about on stderr (keeps --format json stdout machine-readable), never truncated.\nconst OVERSIZE_BYTES = 50_000;\n\n// Flatten embedded newlines so they can't break table column alignment; JSON output is left alone.\nfunction cell(value: unknown): string {\n return String(value ?? '').replace(/\\s*\\n\\s*/g, ' ');\n}\n\n// RFC 4180, and deliberately not cell(): csv is the format for redirecting a result to a\n// file, so it keeps every character, embedded newlines included. What it cannot carry is\n// NULL vs empty string, or a value's type -- that is what json is for.\nfunction csvField(value: unknown): string {\n const text = value === null || value === undefined ? '' : String(value);\n return /[\"\\r\\n,]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text;\n}\n\nfunction csvLine(values: unknown[]): string {\n return values.map(csvField).join(',');\n}\n\nfunction warnOversize(bytes: number, rowCount: number): void {\n if (bytes <= OVERSIZE_BYTES) return;\n console.warn(`warning: result is ${Math.round(bytes / 1000)} KB across ${rowCount} row(s); consider a LIMIT, fewer columns, snippet() instead of whole values, or --format csv redirected to a file`);\n}\n\n// Header names come from a row wherever there is one: a statement's column list keeps\n// duplicates that the row object has already collapsed, so `SELECT a AS x, b AS x` would print\n// b's value under both headers. `columns` is the fallback that labels a 0-row result, and with\n// no rows and no statement (a bounded command that found nothing) csv writes nothing at all --\n// a bare newline would read to a csv parser as one empty record.\n// JSON.stringify throws on BigInt, and stores return int64 as BigInt (DuckDB does). A value\n// that fits a safe integer stays a number; a larger one becomes its decimal string, since a\n// JSON number cannot carry it losslessly.\nconst MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);\nconst bigintSafe = (_key: string, value: unknown): unknown => (typeof value === 'bigint' ? (value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value.toString()) : value);\n\nexport function stringifyJson(value: unknown, indent?: number): string {\n return JSON.stringify(value, bigintSafe, indent);\n}\n\nexport function printRows(rows: Row[], format: RowFormat, columns?: string[]): void {\n if (format === 'csv') {\n // Collapsed the same way a row object collapses them, so a statement naming a column\n // twice describes itself identically whether it matched anything or not.\n const names = rows.length > 0 ? Object.keys(rows[0]) : [...new Set(columns ?? [])];\n if (names.length === 0) return;\n const rendered = [csvLine(names), ...rows.map((row) => csvLine(names.map((name) => row[name])))].join('\\n');\n console.log(rendered);\n // Bytes, not code units -- CJK is 3 bytes per character.\n warnOversize(Buffer.byteLength(rendered), rows.length);\n return;\n }\n const rendered = renderRows(rows, format);\n console.log(rendered);\n warnOversize(Buffer.byteLength(rendered), rows.length);\n}\n\n// Streaming counterpart for the one unbounded caller, `sense sql`: rows arrive from an async\n// iterator (raw.prepare().iterate(), one microtask for the whole statement, not per row), so a\n// large result is never held here as rows and again as a rendered string. Table buffers by\n// necessity (a column is as wide as its widest cell, which the last row can change); json and\n// csv write row by row. The writes are synchronous, so when stdout is a pipe Node buffers what\n// the reader has not taken yet -- the saving is this module's copies, not the operating system's.\nexport async function printRowStream(rows: AsyncIterable<Row>, format: RowFormat, columns: string[]): Promise<void> {\n const iterator = rows[Symbol.asyncIterator]();\n // One step before any write. FTS5 parses a MATCH expression at step time rather than at\n // prepare time, so a bad search string throws here, with stdout still clean for the error.\n const first = await iterator.next();\n const rest: AsyncIterable<Row> = { [Symbol.asyncIterator]: () => iterator };\n\n if (format === 'table') {\n const collected: Row[] = first.done ? [] : [first.value];\n for await (const row of rest) collected.push(row);\n printRows(collected, format, columns);\n return;\n }\n\n let bytes = 0;\n let rowCount = 0;\n const write = (text: string): void => {\n // Bytes, not code units -- CJK is 3 bytes per character.\n bytes += Buffer.byteLength(text);\n process.stdout.write(text);\n };\n\n if (format === 'json') {\n // Byte-identical to JSON.stringify(rows, null, 2) + newline: each row re-indented one\n // level, comma-joined, inside the array brackets written here.\n const indent = (row: Row) =>\n stringifyJson(row, 2)\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n if (first.done) {\n write('[]\\n');\n } else {\n write('[\\n');\n write(indent(first.value));\n rowCount = 1;\n for await (const row of rest) {\n write(',\\n');\n write(indent(row));\n rowCount++;\n }\n write('\\n]\\n');\n }\n } else if (first.done) {\n const names = [...new Set(columns)];\n if (names.length > 0) write(`${csvLine(names)}\\n`);\n } else {\n const names = Object.keys(first.value);\n write(`${csvLine(names)}\\n`);\n write(`${csvLine(names.map((name) => first.value[name]))}\\n`);\n rowCount = 1;\n for await (const row of rest) {\n write(`${csvLine(names.map((name) => row[name]))}\\n`);\n rowCount++;\n }\n }\n warnOversize(bytes, rowCount);\n}\n\n// Tables are for reading, and a row wider than the terminal wraps unreadably: widest columns\n// give up space first, down to a floor. --format json is never truncated.\nconst MIN_COLUMN = 12;\nconst TRUNCATED = '…';\n\nfunction fitWidths(widths: number[], limit: number): number[] {\n const gaps = (widths.length - 1) * 2;\n const fitted = [...widths];\n for (;;) {\n const total = fitted.reduce((a, b) => a + b, 0) + gaps;\n if (total <= limit) return fitted;\n const widest = fitted.indexOf(Math.max(...fitted));\n if (fitted[widest] <= MIN_COLUMN) return fitted;\n fitted[widest] = Math.max(MIN_COLUMN, fitted[widest] - Math.max(1, total - limit));\n }\n}\n\nfunction renderRows(rows: Row[], format: Format, width = process.stdout.columns): string {\n if (format === 'json') return stringifyJson(rows, 2);\n if (rows.length === 0) return '(0 rows)';\n\n const columns = Object.keys(rows[0]);\n const cells = rows.map((row) => columns.map((col) => cell(row[col])));\n const natural = columns.map((col, i) => Math.max(col.length, ...cells.map((row) => row[i].length)));\n const widths = width && width > MIN_COLUMN ? fitWidths(natural, width) : natural;\n\n const clip = (value: string, w: number) => (value.length <= w ? value.padEnd(w) : `${value.slice(0, w - 1)}${TRUNCATED}`);\n const formatRow = (values: string[]) =>\n values\n .map((value, i) => clip(value, widths[i]))\n .join(' ')\n .trimEnd();\n\n return [formatRow(columns), widths.map((w) => '-'.repeat(w)).join(' '), ...cells.map(formatRow)].join('\\n');\n}\n\n// Text renderers for the top-level commands; cli.ts prints what these return.\n\n// Names the config key that turns a feature back on. Only the three features-block toggles\n// reach here; `embed` has its own status line.\nexport function featureOffHint(name: string): string {\n return `features.${name}`;\n}\n\nexport function featuresLine(features: { on: string[]; off: string[] }): string {\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (${featureOffHint(name)})`).join(', ')}` : '';\n return `features: ${features.on.join(', ')}${off}`;\n}\n\n// name, or name:weight when the weight is not the default 1.\nfunction signalLabels(signals: Record<string, number>): string {\n return Object.entries(signals)\n .map(([name, weight]) => (weight === 1 ? name : `${name}:${weight}`))\n .join(', ');\n}\n\nexport function presetsLines(presets: Array<{ name: string; files: number; embedded: number; signals: Record<string, number> }>): string[] {\n // \"0 embedded\" is ambiguous between a scope that declined vectors and one that has not built\n // them yet, so a preset without the vectors signal says what it uses instead.\n return ['presets:', ...presets.map((p) => ` ${p.name}: ${p.files} file(s), ${p.embedded} embedded${p.signals.vectors !== undefined ? '' : ` (signals: ${signalLabels(p.signals)})`}`)];\n}\n\nexport function renderMap(result: {\n docs: { count: number; bytes: number };\n fields: Row[];\n fieldsTotal: number;\n features: { on: string[]; off: string[] };\n presets: Array<{ name: string; files: number; embedded: number; signals: Record<string, number> }>;\n hubs: Row[];\n recent: Row[];\n recentCaveat: string | null;\n}): string {\n const parts = [`docs: ${result.docs.count} (${Math.round(result.docs.bytes / 1024)} KB)`, featuresLine(result.features), '', ...presetsLines(result.presets), '', renderRows(result.fields, 'table')];\n if (result.fieldsTotal > result.fields.length) parts.push(`(+${result.fieldsTotal - result.fields.length} more fields)`);\n if (result.hubs.length > 0) parts.push('\\nhubs (by link rank):', renderRows(result.hubs, 'table'));\n else if (result.features.off.includes('rank')) parts.push(`\\nhubs: off (${featureOffHint('rank')})`);\n parts.push('\\nrecent:');\n if (result.recentCaveat) parts.push(result.recentCaveat);\n parts.push(renderRows(result.recent, 'table'));\n return parts.join('\\n');\n}\n\nexport function renderPeek(result: { path: string; tokens: number; frontmatter: Row; parseError?: string | null; sections: Row[]; outbound: string[]; backlinks: string[]; unresolved: string[]; sectionsTotal: number; outboundTotal: number; backlinksTotal: number; unresolvedTotal: number; off: string[] }): string {\n const lines = [`${result.path} (~${result.tokens} tokens)`];\n // Otherwise a refused parse is indistinguishable from a note that has no frontmatter, which\n // is the confusion the whole quarantine design exists to remove.\n if (result.parseError) lines.push(` frontmatter: did not parse, so none of it is indexed (${result.parseError})`);\n for (const [key, value] of Object.entries(result.frontmatter)) lines.push(` ${key}: ${value}`);\n if (result.sections.length > 0) {\n lines.push('', 'sections:');\n for (const s of result.sections) lines.push(` ${'#'.repeat(s.level as number)} ${s.heading} [L${s.start_line}-${s.end_line}, ~${s.tokens}t]`);\n if (result.sectionsTotal > result.sections.length) lines.push(` (+${result.sectionsTotal - result.sections.length} more sections -- sections table has all of them)`);\n } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');\n if (result.off.includes('links')) {\n lines.push('', 'links: off (features.links)');\n } else {\n const linkLine = (label: string, shown: string[], total: number) => {\n if (total > 0) lines.push(`${label} (${total}): ${shown.join(', ')}${total > shown.length ? `, +${total - shown.length} more` : ''}`);\n };\n if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');\n linkLine('links out', result.outbound, result.outboundTotal);\n linkLine('unresolved', result.unresolved, result.unresolvedTotal);\n linkLine('backlinks', result.backlinks, result.backlinksTotal);\n }\n return lines.join('\\n');\n}\n"],"names":["OVERSIZE_BYTES","cell","value","String","replace","csvField","text","undefined","test","csvLine","values","map","join","warnOversize","bytes","rowCount","console","warn","Math","round","MAX_SAFE","BigInt","Number","MAX_SAFE_INTEGER","bigintSafe","_key","toString","stringifyJson","indent","JSON","stringify","printRows","rows","format","columns","names","length","Object","keys","Set","rendered","row","name","log","Buffer","byteLength","renderRows","printRowStream","iterator","Symbol","asyncIterator","first","next","rest","collected","done","push","write","process","stdout","split","line","MIN_COLUMN","TRUNCATED","fitWidths","widths","limit","gaps","fitted","total","reduce","a","b","widest","indexOf","max","width","cells","col","natural","i","clip","w","padEnd","slice","formatRow","trimEnd","repeat","featureOffHint","featuresLine","features","off","on","signalLabels","signals","entries","weight","presetsLines","presets","p","files","embedded","vectors","renderMap","result","parts","docs","count","fields","fieldsTotal","hubs","includes","recentCaveat","recent","renderPeek","lines","path","tokens","parseError","key","frontmatter","sections","s","level","heading","start_line","end_line","sectionsTotal","linkLine","label","shown","outboundTotal","unresolvedTotal","backlinksTotal","outbound","unresolved","backlinks"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/output.ts"],"sourcesContent":["// rows -> table (default) | json | csv\n\nexport type Row = Record<string, unknown>;\n\n// map/peek/status/path render structures rather than a row set, so csv has nothing to mean\n// there; the commands that emit rows take RowFormat.\nexport type Format = 'table' | 'json';\nexport type RowFormat = Format | 'csv';\n\n// Warned about on stderr (keeps --format json stdout machine-readable), never truncated.\nconst OVERSIZE_BYTES = 50_000;\n\n// Flatten embedded newlines so they can't break table column alignment; JSON output keeps them as-is.\nfunction cell(value: unknown): string {\n return String(value ?? '').replace(/\\s*\\n\\s*/g, ' ');\n}\n\n// RFC 4180, deliberately not cell(): csv is the redirect-to-file format, so it keeps every\n// character, newlines included; what it cannot carry (NULL vs empty, types) is what json is for.\nfunction csvField(value: unknown): string {\n const text = value === null || value === undefined ? '' : String(value);\n return /[\"\\r\\n,]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text;\n}\n\nfunction csvLine(values: unknown[]): string {\n return values.map(csvField).join(',');\n}\n\nfunction warnOversize(bytes: number, rowCount: number): void {\n if (bytes <= OVERSIZE_BYTES) return;\n console.warn(`warning: result is ${Math.round(bytes / 1000)} KB across ${rowCount} row(s); consider a LIMIT, fewer columns, snippet() instead of whole values, or --format csv redirected to a file`);\n}\n\n// JSON.stringify throws on BigInt, and stores return int64 as BigInt (DuckDB does): a value\n// that fits a safe integer stays a number, a larger one becomes its decimal string.\nconst MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);\nconst bigintSafe = (_key: string, value: unknown): unknown => (typeof value === 'bigint' ? (value >= -MAX_SAFE && value <= MAX_SAFE ? Number(value) : value.toString()) : value);\n\nexport function stringifyJson(value: unknown, indent?: number): string {\n return JSON.stringify(value, bigintSafe, indent);\n}\n\nexport function printRows(rows: Row[], format: RowFormat, columns?: string[]): void {\n if (format === 'csv') {\n // Names from a row wherever there is one: the statement's column list keeps duplicates the\n // row collapsed. Nothing at all with no row and no columns -- a bare newline is an empty record.\n const names = rows.length > 0 ? Object.keys(rows[0]) : [...new Set(columns ?? [])];\n if (names.length === 0) return;\n const rendered = [csvLine(names), ...rows.map((row) => csvLine(names.map((name) => row[name])))].join('\\n');\n console.log(rendered);\n // Bytes, not code units -- CJK is 3 bytes per character.\n warnOversize(Buffer.byteLength(rendered), rows.length);\n return;\n }\n const rendered = renderRows(rows, format);\n console.log(rendered);\n warnOversize(Buffer.byteLength(rendered), rows.length);\n}\n\n// Streaming counterpart for the one unbounded caller, `sense sql`: the result is never held as\n// rows and again as a rendered string. Table buffers (column width depends on the last row); json/csv write row by row.\nexport async function printRowStream(rows: AsyncIterable<Row>, format: RowFormat, columns: string[]): Promise<void> {\n const iterator = rows[Symbol.asyncIterator]();\n // One step before any write. FTS5 parses a MATCH expression at step time rather than at\n // prepare time, so a bad search string throws here, with stdout still clean for the error.\n const first = await iterator.next();\n const rest: AsyncIterable<Row> = { [Symbol.asyncIterator]: () => iterator };\n\n if (format === 'table') {\n const collected: Row[] = first.done ? [] : [first.value];\n for await (const row of rest) collected.push(row);\n printRows(collected, format, columns);\n return;\n }\n\n let bytes = 0;\n let rowCount = 0;\n const write = (text: string): void => {\n // Bytes, not code units -- CJK is 3 bytes per character.\n bytes += Buffer.byteLength(text);\n process.stdout.write(text);\n };\n\n if (format === 'json') {\n // Byte-identical to JSON.stringify(rows, null, 2) + newline: each row re-indented one\n // level, comma-joined, inside the array brackets written here.\n const indent = (row: Row) =>\n stringifyJson(row, 2)\n .split('\\n')\n .map((line) => ` ${line}`)\n .join('\\n');\n if (first.done) {\n write('[]\\n');\n } else {\n write('[\\n');\n write(indent(first.value));\n rowCount = 1;\n for await (const row of rest) {\n write(',\\n');\n write(indent(row));\n rowCount++;\n }\n write('\\n]\\n');\n }\n } else if (first.done) {\n const names = [...new Set(columns)];\n if (names.length > 0) write(`${csvLine(names)}\\n`);\n } else {\n const names = Object.keys(first.value);\n write(`${csvLine(names)}\\n`);\n write(`${csvLine(names.map((name) => first.value[name]))}\\n`);\n rowCount = 1;\n for await (const row of rest) {\n write(`${csvLine(names.map((name) => row[name]))}\\n`);\n rowCount++;\n }\n }\n warnOversize(bytes, rowCount);\n}\n\n// Tables are for reading, and a row wider than the terminal wraps unreadably: widest columns\n// give up space first, down to a floor. --format json is never truncated.\nconst MIN_COLUMN = 12;\nconst TRUNCATED = '…';\n\nfunction fitWidths(widths: number[], limit: number): number[] {\n const gaps = (widths.length - 1) * 2;\n const fitted = [...widths];\n for (;;) {\n const total = fitted.reduce((a, b) => a + b, 0) + gaps;\n if (total <= limit) return fitted;\n const widest = fitted.indexOf(Math.max(...fitted));\n if (fitted[widest] <= MIN_COLUMN) return fitted;\n fitted[widest] = Math.max(MIN_COLUMN, fitted[widest] - Math.max(1, total - limit));\n }\n}\n\nfunction renderRows(rows: Row[], format: Format, width = process.stdout.columns): string {\n if (format === 'json') return stringifyJson(rows, 2);\n if (rows.length === 0) return '(0 rows)';\n\n const columns = Object.keys(rows[0]);\n const cells = rows.map((row) => columns.map((col) => cell(row[col])));\n const natural = columns.map((col, i) => Math.max(col.length, ...cells.map((row) => row[i].length)));\n const widths = width && width > MIN_COLUMN ? fitWidths(natural, width) : natural;\n\n const clip = (value: string, w: number) => (value.length <= w ? value.padEnd(w) : `${value.slice(0, w - 1)}${TRUNCATED}`);\n const formatRow = (values: string[]) =>\n values\n .map((value, i) => clip(value, widths[i]))\n .join(' ')\n .trimEnd();\n\n return [formatRow(columns), widths.map((w) => '-'.repeat(w)).join(' '), ...cells.map(formatRow)].join('\\n');\n}\n\n// Text renderers for the top-level commands; cli.ts prints what these return.\n\n// Names the config key that turns a feature back on. Only the three features-block toggles\n// reach here; `embed` has its own status line.\nexport function featureOffHint(name: string): string {\n return `features.${name}`;\n}\n\nexport function featuresLine(features: { on: string[]; off: string[] }): string {\n const off = features.off.length > 0 ? ` · off: ${features.off.map((name) => `${name} (${featureOffHint(name)})`).join(', ')}` : '';\n return `features: ${features.on.join(', ')}${off}`;\n}\n\n// name, or name:weight when the weight is not the default 1.\nfunction signalLabels(signals: Record<string, number>): string {\n return Object.entries(signals)\n .map(([name, weight]) => (weight === 1 ? name : `${name}:${weight}`))\n .join(', ');\n}\n\nexport function presetsLines(presets: Array<{ name: string; files: number; embedded: number; signals: Record<string, number> }>): string[] {\n // \"0 embedded\" is ambiguous between a scope that declined vectors and one that has not built\n // them yet, so a preset without the vectors signal says what it uses instead.\n return ['presets:', ...presets.map((p) => ` ${p.name}: ${p.files} file(s), ${p.embedded} embedded${p.signals.vectors !== undefined ? '' : ` (signals: ${signalLabels(p.signals)})`}`)];\n}\n\nexport function renderMap(result: {\n docs: { count: number; bytes: number };\n fields: Row[];\n fieldsTotal: number;\n features: { on: string[]; off: string[] };\n presets: Array<{ name: string; files: number; embedded: number; signals: Record<string, number> }>;\n hubs: Row[];\n recent: Row[];\n recentCaveat: string | null;\n}): string {\n const parts = [`docs: ${result.docs.count} (${Math.round(result.docs.bytes / 1024)} KB)`, featuresLine(result.features), '', ...presetsLines(result.presets), '', renderRows(result.fields, 'table')];\n if (result.fieldsTotal > result.fields.length) parts.push(`(+${result.fieldsTotal - result.fields.length} more fields)`);\n if (result.hubs.length > 0) parts.push('\\nhubs (by link rank):', renderRows(result.hubs, 'table'));\n else if (result.features.off.includes('rank')) parts.push(`\\nhubs: off (${featureOffHint('rank')})`);\n parts.push('\\nrecent:');\n if (result.recentCaveat) parts.push(result.recentCaveat);\n parts.push(renderRows(result.recent, 'table'));\n return parts.join('\\n');\n}\n\nexport function renderPeek(result: { path: string; tokens: number; frontmatter: Row; parseError?: string | null; sections: Row[]; outbound: string[]; backlinks: string[]; unresolved: string[]; sectionsTotal: number; outboundTotal: number; backlinksTotal: number; unresolvedTotal: number; off: string[] }): string {\n const lines = [`${result.path} (~${result.tokens} tokens)`];\n // Otherwise a refused parse is indistinguishable from a note that has no frontmatter, which\n // is the confusion the whole quarantine design exists to remove.\n if (result.parseError) lines.push(` frontmatter: did not parse, so none of it is indexed (${result.parseError})`);\n for (const [key, value] of Object.entries(result.frontmatter)) lines.push(` ${key}: ${value}`);\n if (result.sections.length > 0) {\n lines.push('', 'sections:');\n for (const s of result.sections) lines.push(` ${'#'.repeat(s.level as number)} ${s.heading} [L${s.start_line}-${s.end_line}, ~${s.tokens}t]`);\n if (result.sectionsTotal > result.sections.length) lines.push(` (+${result.sectionsTotal - result.sections.length} more sections -- sections table has all of them)`);\n } else if (result.off.includes('sections')) lines.push('', 'sections: off (features.sections)');\n if (result.off.includes('links')) {\n lines.push('', 'links: off (features.links)');\n } else {\n const linkLine = (label: string, shown: string[], total: number) => {\n if (total > 0) lines.push(`${label} (${total}): ${shown.join(', ')}${total > shown.length ? `, +${total - shown.length} more` : ''}`);\n };\n if (result.outboundTotal + result.unresolvedTotal + result.backlinksTotal > 0) lines.push('');\n linkLine('links out', result.outbound, result.outboundTotal);\n linkLine('unresolved', result.unresolved, result.unresolvedTotal);\n linkLine('backlinks', result.backlinks, result.backlinksTotal);\n }\n return lines.join('\\n');\n}\n"],"names":["OVERSIZE_BYTES","cell","value","String","replace","csvField","text","undefined","test","csvLine","values","map","join","warnOversize","bytes","rowCount","console","warn","Math","round","MAX_SAFE","BigInt","Number","MAX_SAFE_INTEGER","bigintSafe","_key","toString","stringifyJson","indent","JSON","stringify","printRows","rows","format","columns","names","length","Object","keys","Set","rendered","row","name","log","Buffer","byteLength","renderRows","printRowStream","iterator","Symbol","asyncIterator","first","next","rest","collected","done","push","write","process","stdout","split","line","MIN_COLUMN","TRUNCATED","fitWidths","widths","limit","gaps","fitted","total","reduce","a","b","widest","indexOf","max","width","cells","col","natural","i","clip","w","padEnd","slice","formatRow","trimEnd","repeat","featureOffHint","featuresLine","features","off","on","signalLabels","signals","entries","weight","presetsLines","presets","p","files","embedded","vectors","renderMap","result","parts","docs","count","fields","fieldsTotal","hubs","includes","recentCaveat","recent","renderPeek","lines","path","tokens","parseError","key","frontmatter","sections","s","level","heading","start_line","end_line","sectionsTotal","linkLine","label","shown","outboundTotal","unresolvedTotal","backlinksTotal","outbound","unresolved","backlinks"],"mappings":"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"}
@@ -1,8 +1,6 @@
1
1
  import { SenseError } from '../errors.js';
2
2
  // FTS5 reads punctuation as syntax, so `end-to-end` parses as a filter on column `to` and
3
- // errors `no such column: to` -- true about the parse, misleading about the input. Matched as
4
- // "not a bareword": an operator list is a list to keep current, and missing a character costs
5
- // the remedy, not the error.
3
+ // errors `no such column: to` -- true about the parse, misleading about the input.
6
4
  const FTS5_PUNCTUATION = /[^\p{L}\p{N}_\s]/u;
7
5
  export function searchError(err, terms, scope) {
8
6
  var _terms_match;
@@ -11,8 +9,7 @@ export function searchError(err, terms, scope) {
11
9
  if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;
12
10
  const suspects = ((_terms_match = terms.match(/\S+/g)) !== null && _terms_match !== void 0 ? _terms_match : []).filter((t)=>!t.startsWith('"') && FTS5_PUNCTUATION.test(t));
13
11
  // Blame the terms only when the failing token actually came from one -- a typo'd column
14
- // in --where (or the tree's default scope) raises "no such column" through this same
15
- // statement, and naming a term for it would state a false fact about the input.
12
+ // in --where can raise "no such column" through this same statement.
16
13
  const col = (_exec = /no such column: (\S+)/.exec(message)) === null || _exec === void 0 ? void 0 : _exec[1];
17
14
  const fromTerms = col === undefined ? suspects.length > 0 : suspects.some((t)=>t.split(/[^\p{L}\p{N}]+/u).includes(col));
18
15
  if (fromTerms && suspects.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/search-error.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\n\n// FTS5 reads punctuation as syntax, so `end-to-end` parses as a filter on column `to` and\n// errors `no such column: to` -- true about the parse, misleading about the input. Matched as\n// \"not a bareword\": an operator list is a list to keep current, and missing a character costs\n// the remedy, not the error.\nconst FTS5_PUNCTUATION = /[^\\p{L}\\p{N}_\\s]/u;\n\nexport function searchError(err: Error, terms: string, scope?: string): Error {\n const message = err.message;\n if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;\n const suspects = (terms.match(/\\S+/g) ?? []).filter((t) => !t.startsWith('\"') && FTS5_PUNCTUATION.test(t));\n // Blame the terms only when the failing token actually came from one -- a typo'd column\n // in --where (or the tree's default scope) raises \"no such column\" through this same\n // statement, and naming a term for it would state a false fact about the input.\n const col = /no such column: (\\S+)/.exec(message)?.[1];\n const fromTerms = col === undefined ? suspects.length > 0 : suspects.some((t) => t.split(/[^\\p{L}\\p{N}]+/u).includes(col));\n if (fromTerms && suspects.length > 0) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the punctuation in ${suspects.map((t) => `\\`${t}\\``).join(', ')} is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"${suspects[0]}\"'. Searchable columns are title, summary, text.`);\n }\n if (col !== undefined && scope !== undefined) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the where condition (${scope}) references it; frontmatter columns are listed by sense sql \"SELECT name FROM pragma_table_info('frontmatter')\".`);\n }\n return new SenseError('SEARCH_SYNTAX', `${message} -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense sql (list them with pragma_table_info('frontmatter')).`);\n}\n"],"names":["SenseError","FTS5_PUNCTUATION","searchError","err","terms","scope","message","test","suspects","match","filter","t","startsWith","col","exec","fromTerms","undefined","length","some","split","includes","map","join"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAE1C,0FAA0F;AAC1F,8FAA8F;AAC9F,8FAA8F;AAC9F,6BAA6B;AAC7B,MAAMC,mBAAmB;AAEzB,OAAO,SAASC,YAAYC,GAAU,EAAEC,KAAa,EAAEC,KAAc;QAGjDD;QAIN;IANZ,MAAME,UAAUH,IAAIG,OAAO;IAC3B,IAAI,CAAC,oDAAoDC,IAAI,CAACD,UAAU,OAAOH;IAC/E,MAAMK,WAAW,EAACJ,eAAAA,MAAMK,KAAK,CAAC,qBAAZL,0BAAAA,eAAuB,EAAE,EAAEM,MAAM,CAAC,CAACC,IAAM,CAACA,EAAEC,UAAU,CAAC,QAAQX,iBAAiBM,IAAI,CAACI;IACvG,wFAAwF;IACxF,qFAAqF;IACrF,gFAAgF;IAChF,MAAME,OAAM,QAAA,wBAAwBC,IAAI,CAACR,sBAA7B,4BAAA,KAAuC,CAAC,EAAE;IACtD,MAAMS,YAAYF,QAAQG,YAAYR,SAASS,MAAM,GAAG,IAAIT,SAASU,IAAI,CAAC,CAACP,IAAMA,EAAEQ,KAAK,CAAC,mBAAmBC,QAAQ,CAACP;IACrH,IAAIE,aAAaP,SAASS,MAAM,GAAG,GAAG;QACpC,OAAO,IAAIjB,WAAW,iBAAiB,GAAGM,QAAQ,uBAAuB,EAAEE,SAASa,GAAG,CAAC,CAACV,IAAM,CAAC,EAAE,EAAEA,EAAE,EAAE,CAAC,EAAEW,IAAI,CAAC,MAAM,gFAAgF,EAAEd,QAAQ,CAAC,EAAE,CAAC,gDAAgD,CAAC;IACvQ;IACA,IAAIK,QAAQG,aAAaX,UAAUW,WAAW;QAC5C,OAAO,IAAIhB,WAAW,iBAAiB,GAAGM,QAAQ,yBAAyB,EAAED,MAAM,iHAAiH,CAAC;IACvM;IACA,OAAO,IAAIL,WAAW,iBAAiB,GAAGM,QAAQ,4JAA4J,CAAC;AACjN"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/search-error.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\n\n// FTS5 reads punctuation as syntax, so `end-to-end` parses as a filter on column `to` and\n// errors `no such column: to` -- true about the parse, misleading about the input.\nconst FTS5_PUNCTUATION = /[^\\p{L}\\p{N}_\\s]/u;\n\nexport function searchError(err: Error, terms: string, scope?: string): Error {\n const message = err.message;\n if (!/no such column|fts5: syntax error|malformed MATCH/.test(message)) return err;\n const suspects = (terms.match(/\\S+/g) ?? []).filter((t) => !t.startsWith('\"') && FTS5_PUNCTUATION.test(t));\n // Blame the terms only when the failing token actually came from one -- a typo'd column\n // in --where can raise \"no such column\" through this same statement.\n const col = /no such column: (\\S+)/.exec(message)?.[1];\n const fromTerms = col === undefined ? suspects.length > 0 : suspects.some((t) => t.split(/[^\\p{L}\\p{N}]+/u).includes(col));\n if (fromTerms && suspects.length > 0) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the punctuation in ${suspects.map((t) => `\\`${t}\\``).join(', ')} is FTS5 syntax, not literal text; search for it literally by double-quoting: '\"${suspects[0]}\"'. Searchable columns are title, summary, text.`);\n }\n if (col !== undefined && scope !== undefined) {\n return new SenseError('SEARCH_SYNTAX', `${message} -- the where condition (${scope}) references it; frontmatter columns are listed by sense sql \"SELECT name FROM pragma_table_info('frontmatter')\".`);\n }\n return new SenseError('SEARCH_SYNTAX', `${message} -- searchable columns are title, summary, text; frontmatter fields are queried with --where or sense sql (list them with pragma_table_info('frontmatter')).`);\n}\n"],"names":["SenseError","FTS5_PUNCTUATION","searchError","err","terms","scope","message","test","suspects","match","filter","t","startsWith","col","exec","fromTerms","undefined","length","some","split","includes","map","join"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAE1C,0FAA0F;AAC1F,mFAAmF;AACnF,MAAMC,mBAAmB;AAEzB,OAAO,SAASC,YAAYC,GAAU,EAAEC,KAAa,EAAEC,KAAc;QAGjDD;QAGN;IALZ,MAAME,UAAUH,IAAIG,OAAO;IAC3B,IAAI,CAAC,oDAAoDC,IAAI,CAACD,UAAU,OAAOH;IAC/E,MAAMK,WAAW,EAACJ,eAAAA,MAAMK,KAAK,CAAC,qBAAZL,0BAAAA,eAAuB,EAAE,EAAEM,MAAM,CAAC,CAACC,IAAM,CAACA,EAAEC,UAAU,CAAC,QAAQX,iBAAiBM,IAAI,CAACI;IACvG,wFAAwF;IACxF,qEAAqE;IACrE,MAAME,OAAM,QAAA,wBAAwBC,IAAI,CAACR,sBAA7B,4BAAA,KAAuC,CAAC,EAAE;IACtD,MAAMS,YAAYF,QAAQG,YAAYR,SAASS,MAAM,GAAG,IAAIT,SAASU,IAAI,CAAC,CAACP,IAAMA,EAAEQ,KAAK,CAAC,mBAAmBC,QAAQ,CAACP;IACrH,IAAIE,aAAaP,SAASS,MAAM,GAAG,GAAG;QACpC,OAAO,IAAIjB,WAAW,iBAAiB,GAAGM,QAAQ,uBAAuB,EAAEE,SAASa,GAAG,CAAC,CAACV,IAAM,CAAC,EAAE,EAAEA,EAAE,EAAE,CAAC,EAAEW,IAAI,CAAC,MAAM,gFAAgF,EAAEd,QAAQ,CAAC,EAAE,CAAC,gDAAgD,CAAC;IACvQ;IACA,IAAIK,QAAQG,aAAaX,UAAUW,WAAW;QAC5C,OAAO,IAAIhB,WAAW,iBAAiB,GAAGM,QAAQ,yBAAyB,EAAED,MAAM,iHAAiH,CAAC;IACvM;IACA,OAAO,IAAIL,WAAW,iBAAiB,GAAGM,QAAQ,4JAA4J,CAAC;AACjN"}
@@ -3,6 +3,7 @@ export declare function normalizeText(value: unknown): string;
3
3
  export declare function looksLikeDatetime(value: string): boolean;
4
4
  export declare function normalizeDate(value: string): string;
5
5
  export declare function mapValue(value: unknown): string | number | bigint | null;
6
+ export declare function countLines(s: string): number;
6
7
  export declare function splitFrontmatter(raw: string): {
7
8
  fm: string | null;
8
9
  body: string;
@@ -12,12 +12,8 @@ export const RESERVED_COLUMNS = new Set([
12
12
  'links',
13
13
  'sections'
14
14
  ]);
15
- // YAML error codes whose recovery is unambiguous, so the parse is accepted rather than
16
- // quarantined. Only one qualifies: YAML 1.2 reserves `@` and `` ` `` at the start of a plain
17
- // scalar for future use, so they can never be valid and the text can only be what was typed
18
- // (`aliases: [@handle]` -> ["@handle"]). Every other code has a second reading -- an unquoted
19
- // `:` swallows the keys after it, an unquoted `[..](..)` drops the URL, a duplicate key picks
20
- // one value in silence -- so it writes values nobody wrote.
15
+ // Only BAD_SCALAR_START qualifies: YAML 1.2 reserves `@` and `` ` `` at the start of a plain scalar, so `aliases: [@handle]` can only mean ["@handle"].
16
+ // Every other code has a second reading (a swallowed key, a dropped URL, a silently overwritten duplicate) that would write values nobody wrote.
21
17
  const ACCEPTED_YAML_CODES = new Set([
22
18
  'BAD_SCALAR_START'
23
19
  ]);
@@ -54,6 +50,13 @@ export function mapValue(value) {
54
50
  if (typeof value === 'string') return normalizeDate(value);
55
51
  return JSON.stringify(value);
56
52
  }
53
+ // Exactly `s.split('\n').length` without allocating the array: callers that need only the count
54
+ // run it per file per feature on the parse hot path.
55
+ export function countLines(s) {
56
+ let n = 1;
57
+ for(let i = 0; i < s.length; i++)if (s.charCodeAt(i) === 10) n++;
58
+ return n;
59
+ }
57
60
  // The delimiter split is all this package used gray-matter for.
58
61
  export function splitFrontmatter(raw) {
59
62
  const open = raw.match(/^---\r?\n/);
@@ -72,11 +75,8 @@ export function splitFrontmatter(raw) {
72
75
  body: rest.slice(close.index + close[0].length)
73
76
  };
74
77
  }
75
- // A well-formed document can still hold a value nobody meant: `created: {{date}}` is valid
76
- // YAML for a flow map used as a mapping key, so it raises no error and stores
77
- // {"{ date }": null}. No error code can catch that, but yaml notices the stringified key, so
78
- // this reports it with the path instead (yaml's own warning has none, fires once per document,
79
- // and is what trains readers to discard stderr).
78
+ // A well-formed document can hold a value nobody meant: `created: {{date}}` is valid YAML for a flow map used as a mapping key, so it raises no error and stores {"{ date }": null}.
79
+ // No error code catches that; yaml's own warning lacks a path and fires once per document, so this reports it here instead, with the path.
80
80
  function warnStringifiedKeys(relPath, doc, warnings) {
81
81
  let found = false;
82
82
  // Nested, not top level: `created: {{date}}` puts the collection key one level down, inside
@@ -91,13 +91,8 @@ function warnStringifiedKeys(relPath, doc, warnings) {
91
91
  // One per file: a template repeats the same mistake on every field it stamps.
92
92
  if (found) warnings.push(`warning: ${relPath} frontmatter has a key that is itself a list or mapping, stored as text; this is usually an unrendered template placeholder like {{date}}`);
93
93
  }
94
- // Accept a clean parse, and one whose every error is unambiguous (ACCEPTED_YAML_CODES).
95
- // Anything else is quarantined: no frontmatter columns at all, and `_parse_error` carries the
96
- // reason. Recovering it would write values nobody wrote, which is worse than absence because
97
- // no query can see it. The file is still indexed -- content, links and sections never touch
98
- // frontmatter -- so a broken note stays searchable while it is being hunted for.
99
- // yaml's message continues onto a source excerpt, so the first line is the sentence -- minus
100
- // the colon that introduced the part being dropped.
94
+ // Anything past ACCEPTED_YAML_CODES is quarantined: no frontmatter columns, `_parse_error` carries the reason. Recovering it would write values nobody wrote, worse than absence since no query can see it; content, links and sections never touch frontmatter, so a broken note stays searchable.
95
+ // yaml's message continues onto a source excerpt, so the first line is the sentence, minus the colon that introduced the part being dropped.
101
96
  function firstLine(message) {
102
97
  return message.split('\n')[0].replace(/:\s*$/, '');
103
98
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/frontmatter.ts"],"sourcesContent":["import { isCollection, parseDocument, visit } from 'yaml';\n\n// Frontmatter keys that would collide with table columns. Exported so reconcile.ts's upsert can tell\n// a feature-owned column (`_rank`) from a parsed one and leave it alone on reparse.\nexport const RESERVED_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_rank', '_parse_error', 'content', 'links', 'sections']);\n\n// YAML error codes whose recovery is unambiguous, so the parse is accepted rather than\n// quarantined. Only one qualifies: YAML 1.2 reserves `@` and `` ` `` at the start of a plain\n// scalar for future use, so they can never be valid and the text can only be what was typed\n// (`aliases: [@handle]` -> [\"@handle\"]). Every other code has a second reading -- an unquoted\n// `:` swallows the keys after it, an unquoted `[..](..)` drops the URL, a duplicate key picks\n// one value in silence -- so it writes values nobody wrote.\nconst ACCEPTED_YAML_CODES = new Set(['BAD_SCALAR_START']);\n\nexport function normalizeText(value: unknown): string {\n if (value === null || value === undefined) return '';\n return String(value).replace(/\\s+/g, ' ').trim();\n}\n\n// SQLite's datetime() rejects a colonless offset (`-0800`) and a space separator, which ISO 8601\n// allows and producers emit. A rejected date is invisible, not excluded: every comparison is NULL.\nconst ISO_DATETIME = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}:\\d{2}(?::\\d{2})?(?:\\.\\d+)?)(Z|[+-]\\d{2}(?::?\\d{2})?)?$/;\n\n// A value opening `YYYY-MM-DDT` was meant to be a datetime; prose never is. Reported when it\n// cannot be normalized, so a typo surfaces on the next crawl instead of at some later audit.\nconst MEANT_AS_DATETIME = /^\\d{4}-\\d{2}-\\d{2}[T ]\\d/;\n\nexport function looksLikeDatetime(value: string): boolean {\n return MEANT_AS_DATETIME.test(value);\n}\n\n// Punctuation only, never a timezone conversion: the offset survives, so substr(d,1,10) is still\n// the local date. A shape that is not a real instant is left as written, and stays auditable.\nexport function normalizeDate(value: string): string {\n const m = ISO_DATETIME.exec(value);\n if (m === null) return value;\n const [, date, time, zone] = m;\n const digits = zone === undefined || zone === 'Z' ? '' : zone.replace(':', '');\n const offset = digits === '' ? (zone ?? '') : digits.length === 3 ? `${digits}:00` : `${digits.slice(0, 3)}:${digits.slice(3)}`;\n const normalized = `${date}T${time}${offset}`;\n return Number.isNaN(Date.parse(normalized)) ? value : normalized;\n}\n\n// Storage class follows the YAML scalar. Booleans store as 1/0, so `WHERE flag = 1` matches\n// and `WHERE flag = 'true'` cannot; `map` prints observed types so the mismatch is visible.\nexport function mapValue(value: unknown): string | number | bigint | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'boolean') return BigInt(value ? 1 : 0);\n if (typeof value === 'number') return Number.isSafeInteger(value) ? BigInt(value) : value;\n if (typeof value === 'string') return normalizeDate(value);\n return JSON.stringify(value);\n}\n\n// The delimiter split is all this package used gray-matter for.\nexport function splitFrontmatter(raw: string): { fm: string | null; body: string } {\n const open = raw.match(/^---\\r?\\n/);\n if (!open) return { fm: null, body: raw };\n const rest = raw.slice(open[0].length);\n const close = rest.match(/^---\\r?(\\n|$)/m);\n if (!close || close.index === undefined) return { fm: null, body: raw };\n return { fm: rest.slice(0, close.index), body: rest.slice(close.index + close[0].length) };\n}\n\n// A well-formed document can still hold a value nobody meant: `created: {{date}}` is valid\n// YAML for a flow map used as a mapping key, so it raises no error and stores\n// {\"{ date }\": null}. No error code can catch that, but yaml notices the stringified key, so\n// this reports it with the path instead (yaml's own warning has none, fires once per document,\n// and is what trains readers to discard stderr).\nfunction warnStringifiedKeys(relPath: string, doc: ReturnType<typeof parseDocument>, warnings: string[]): void {\n let found = false;\n // Nested, not top level: `created: {{date}}` puts the collection key one level down, inside\n // the flow map that `{{...}}` parses as.\n visit(doc, {\n Pair(_key, pair) {\n if (!isCollection(pair.key)) return undefined;\n found = true;\n return visit.BREAK;\n },\n });\n // One per file: a template repeats the same mistake on every field it stamps.\n if (found) warnings.push(`warning: ${relPath} frontmatter has a key that is itself a list or mapping, stored as text; this is usually an unrendered template placeholder like {{date}}`);\n}\n\n// Accept a clean parse, and one whose every error is unambiguous (ACCEPTED_YAML_CODES).\n// Anything else is quarantined: no frontmatter columns at all, and `_parse_error` carries the\n// reason. Recovering it would write values nobody wrote, which is worse than absence because\n// no query can see it. The file is still indexed -- content, links and sections never touch\n// frontmatter -- so a broken note stays searchable while it is being hunted for.\n// yaml's message continues onto a source excerpt, so the first line is the sentence -- minus\n// the colon that introduced the part being dropped.\nfunction firstLine(message: string): string {\n return message.split('\\n')[0].replace(/:\\s*$/, '');\n}\n\nexport function parseFrontmatter(relPath: string, fm: string, warnings: string[]): { data: Record<string, unknown>; parseError: string | null } {\n // logLevel silences yaml's own pathless warnings; warnStringifiedKeys re-reports the one\n // that carries information, with the file it came from.\n const doc = parseDocument(fm, { logLevel: 'silent' });\n const refused = doc.errors.filter((err) => !ACCEPTED_YAML_CODES.has(err.code));\n if (refused.length > 0) {\n const detail = refused.length > 1 ? ` (and ${refused.length - 1} more)` : '';\n const parseError = `${firstLine(refused[0].message)}${detail}`;\n warnings.push(`warning: ${relPath} frontmatter did not parse, so none of it is indexed: ${parseError}`);\n return { data: {}, parseError };\n }\n\n let data: unknown;\n try {\n data = doc.toJS();\n } catch (err) {\n // Reaches here with doc.errors empty: `title: **Bold**` parses, then opens an alias on\n // materialisation. An empty error list is not a successful parse.\n const parseError = firstLine((err as Error).message);\n warnings.push(`warning: ${relPath} frontmatter did not parse, so none of it is indexed: ${parseError}`);\n return { data: {}, parseError };\n }\n\n if (data === null || data === undefined) return { data: {}, parseError: null };\n if (typeof data !== 'object' || Array.isArray(data)) {\n const parseError = 'frontmatter is not a key-value mapping';\n warnings.push(`warning: ${relPath} ${parseError}; none of it is indexed`);\n return { data: {}, parseError };\n }\n warnStringifiedKeys(relPath, doc, warnings);\n return { data: data as Record<string, unknown>, parseError: null };\n}\n"],"names":["isCollection","parseDocument","visit","RESERVED_COLUMNS","Set","ACCEPTED_YAML_CODES","normalizeText","value","undefined","String","replace","trim","ISO_DATETIME","MEANT_AS_DATETIME","looksLikeDatetime","test","normalizeDate","m","exec","date","time","zone","digits","offset","length","slice","normalized","Number","isNaN","Date","parse","mapValue","BigInt","isSafeInteger","JSON","stringify","splitFrontmatter","raw","open","match","fm","body","rest","close","index","warnStringifiedKeys","relPath","doc","warnings","found","Pair","_key","pair","key","BREAK","push","firstLine","message","split","parseFrontmatter","logLevel","refused","errors","filter","err","has","code","detail","parseError","data","toJS","Array","isArray"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/frontmatter.ts"],"sourcesContent":["import { isCollection, parseDocument, visit } from 'yaml';\n\n// Frontmatter keys that would collide with table columns. Exported so reconcile.ts's upsert can tell\n// a feature-owned column (`_rank`) from a parsed one and leave it alone on reparse.\nexport const RESERVED_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_rank', '_parse_error', 'content', 'links', 'sections']);\n\n// Only BAD_SCALAR_START qualifies: YAML 1.2 reserves `@` and `` ` `` at the start of a plain scalar, so `aliases: [@handle]` can only mean [\"@handle\"].\n// Every other code has a second reading (a swallowed key, a dropped URL, a silently overwritten duplicate) that would write values nobody wrote.\nconst ACCEPTED_YAML_CODES = new Set(['BAD_SCALAR_START']);\n\nexport function normalizeText(value: unknown): string {\n if (value === null || value === undefined) return '';\n return String(value).replace(/\\s+/g, ' ').trim();\n}\n\n// SQLite's datetime() rejects a colonless offset (`-0800`) and a space separator, which ISO 8601\n// allows and producers emit. A rejected date is invisible, not excluded: every comparison is NULL.\nconst ISO_DATETIME = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}:\\d{2}(?::\\d{2})?(?:\\.\\d+)?)(Z|[+-]\\d{2}(?::?\\d{2})?)?$/;\n\n// A value opening `YYYY-MM-DDT` was meant to be a datetime; prose never is. Reported when it\n// cannot be normalized, so a typo surfaces on the next crawl instead of at some later audit.\nconst MEANT_AS_DATETIME = /^\\d{4}-\\d{2}-\\d{2}[T ]\\d/;\n\nexport function looksLikeDatetime(value: string): boolean {\n return MEANT_AS_DATETIME.test(value);\n}\n\n// Punctuation only, never a timezone conversion: the offset survives, so substr(d,1,10) is still\n// the local date. A shape that is not a real instant is left as written, and stays auditable.\nexport function normalizeDate(value: string): string {\n const m = ISO_DATETIME.exec(value);\n if (m === null) return value;\n const [, date, time, zone] = m;\n const digits = zone === undefined || zone === 'Z' ? '' : zone.replace(':', '');\n const offset = digits === '' ? (zone ?? '') : digits.length === 3 ? `${digits}:00` : `${digits.slice(0, 3)}:${digits.slice(3)}`;\n const normalized = `${date}T${time}${offset}`;\n return Number.isNaN(Date.parse(normalized)) ? value : normalized;\n}\n\n// Storage class follows the YAML scalar. Booleans store as 1/0, so `WHERE flag = 1` matches\n// and `WHERE flag = 'true'` cannot; `map` prints observed types so the mismatch is visible.\nexport function mapValue(value: unknown): string | number | bigint | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'boolean') return BigInt(value ? 1 : 0);\n if (typeof value === 'number') return Number.isSafeInteger(value) ? BigInt(value) : value;\n if (typeof value === 'string') return normalizeDate(value);\n return JSON.stringify(value);\n}\n\n// Exactly `s.split('\\n').length` without allocating the array: callers that need only the count\n// run it per file per feature on the parse hot path.\nexport function countLines(s: string): number {\n let n = 1;\n for (let i = 0; i < s.length; i++) if (s.charCodeAt(i) === 10) n++;\n return n;\n}\n\n// The delimiter split is all this package used gray-matter for.\nexport function splitFrontmatter(raw: string): { fm: string | null; body: string } {\n const open = raw.match(/^---\\r?\\n/);\n if (!open) return { fm: null, body: raw };\n const rest = raw.slice(open[0].length);\n const close = rest.match(/^---\\r?(\\n|$)/m);\n if (!close || close.index === undefined) return { fm: null, body: raw };\n return { fm: rest.slice(0, close.index), body: rest.slice(close.index + close[0].length) };\n}\n\n// A well-formed document can hold a value nobody meant: `created: {{date}}` is valid YAML for a flow map used as a mapping key, so it raises no error and stores {\"{ date }\": null}.\n// No error code catches that; yaml's own warning lacks a path and fires once per document, so this reports it here instead, with the path.\nfunction warnStringifiedKeys(relPath: string, doc: ReturnType<typeof parseDocument>, warnings: string[]): void {\n let found = false;\n // Nested, not top level: `created: {{date}}` puts the collection key one level down, inside\n // the flow map that `{{...}}` parses as.\n visit(doc, {\n Pair(_key, pair) {\n if (!isCollection(pair.key)) return undefined;\n found = true;\n return visit.BREAK;\n },\n });\n // One per file: a template repeats the same mistake on every field it stamps.\n if (found) warnings.push(`warning: ${relPath} frontmatter has a key that is itself a list or mapping, stored as text; this is usually an unrendered template placeholder like {{date}}`);\n}\n\n// Anything past ACCEPTED_YAML_CODES is quarantined: no frontmatter columns, `_parse_error` carries the reason. Recovering it would write values nobody wrote, worse than absence since no query can see it; content, links and sections never touch frontmatter, so a broken note stays searchable.\n// yaml's message continues onto a source excerpt, so the first line is the sentence, minus the colon that introduced the part being dropped.\nfunction firstLine(message: string): string {\n return message.split('\\n')[0].replace(/:\\s*$/, '');\n}\n\nexport function parseFrontmatter(relPath: string, fm: string, warnings: string[]): { data: Record<string, unknown>; parseError: string | null } {\n // logLevel silences yaml's own pathless warnings; warnStringifiedKeys re-reports the one\n // that carries information, with the file it came from.\n const doc = parseDocument(fm, { logLevel: 'silent' });\n const refused = doc.errors.filter((err) => !ACCEPTED_YAML_CODES.has(err.code));\n if (refused.length > 0) {\n const detail = refused.length > 1 ? ` (and ${refused.length - 1} more)` : '';\n const parseError = `${firstLine(refused[0].message)}${detail}`;\n warnings.push(`warning: ${relPath} frontmatter did not parse, so none of it is indexed: ${parseError}`);\n return { data: {}, parseError };\n }\n\n let data: unknown;\n try {\n data = doc.toJS();\n } catch (err) {\n // Reaches here with doc.errors empty: `title: **Bold**` parses, then opens an alias on\n // materialisation. An empty error list is not a successful parse.\n const parseError = firstLine((err as Error).message);\n warnings.push(`warning: ${relPath} frontmatter did not parse, so none of it is indexed: ${parseError}`);\n return { data: {}, parseError };\n }\n\n if (data === null || data === undefined) return { data: {}, parseError: null };\n if (typeof data !== 'object' || Array.isArray(data)) {\n const parseError = 'frontmatter is not a key-value mapping';\n warnings.push(`warning: ${relPath} ${parseError}; none of it is indexed`);\n return { data: {}, parseError };\n }\n warnStringifiedKeys(relPath, doc, warnings);\n return { data: data as Record<string, unknown>, parseError: null };\n}\n"],"names":["isCollection","parseDocument","visit","RESERVED_COLUMNS","Set","ACCEPTED_YAML_CODES","normalizeText","value","undefined","String","replace","trim","ISO_DATETIME","MEANT_AS_DATETIME","looksLikeDatetime","test","normalizeDate","m","exec","date","time","zone","digits","offset","length","slice","normalized","Number","isNaN","Date","parse","mapValue","BigInt","isSafeInteger","JSON","stringify","countLines","s","n","i","charCodeAt","splitFrontmatter","raw","open","match","fm","body","rest","close","index","warnStringifiedKeys","relPath","doc","warnings","found","Pair","_key","pair","key","BREAK","push","firstLine","message","split","parseFrontmatter","logLevel","refused","errors","filter","err","has","code","detail","parseError","data","toJS","Array","isArray"],"mappings":"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"}
@@ -6,9 +6,8 @@ import { embedEnabled, presetHasSignal, presetNames } from '../config/index.js';
6
6
  export function toPosixPath(relPath, separator = sep) {
7
7
  return separator === '\\' ? relPath.split(separator).join('/') : relPath;
8
8
  }
9
- // Every command pays listFiles before it answers (the freshness check stats each file), so
10
- // per-file work here is the hottest path in the package. Everything derivable from the config
11
- // alone is computed once, above the loop.
9
+ // Every command pays listFiles before it answers (the freshness check stats each file), so per-file work here is the hottest path in the package.
10
+ // Everything derivable from the config alone is computed once, above the loop.
12
11
  const NO_THROW = {
13
12
  throwIfNoEntry: false
14
13
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/list.ts"],"sourcesContent":["import { globSync, statSync } from 'node:fs';\nimport { join, sep } from 'node:path';\nimport type { Config } from '../config/index.ts';\nimport { embedEnabled, presetHasSignal, presetNames } from '../config/index.ts';\n\nexport interface FileStat {\n relPath: string;\n absPath: string;\n mtimeMs: number;\n ctimeMs: number; // filesystem birthtime; a clone or copy resets it, like _mtime\n size: number;\n presets: string[]; // every declared preset covering this file (>= 1; union, overlap allowed)\n embed: boolean; // true iff a model is named and some covering preset's signals include vectors\n}\n\n// Presets are views, not partitions: they overlap freely, and a file's covering set (not one\n// owner) drives indexing. Globs resolve relative to baseDir; unmatched files are not indexed.\nexport function toPosixPath(relPath: string, separator: string = sep): string {\n return separator === '\\\\' ? relPath.split(separator).join('/') : relPath;\n}\n\n// Every command pays listFiles before it answers (the freshness check stats each file), so\n// per-file work here is the hottest path in the package. Everything derivable from the config\n// alone is computed once, above the loop.\nconst NO_THROW = { throwIfNoEntry: false } as const;\n\nexport function listFiles(cfg: Config, baseDir: string): FileStat[] {\n const coverage = new Map<string, Set<string>>();\n const posixNeeded = sep === '\\\\';\n for (const name of presetNames(cfg)) {\n const preset = cfg.presets[name];\n for (const matched of globSync(preset.include, { cwd: baseDir, exclude: preset.exclude })) {\n const relPath = posixNeeded ? toPosixPath(matched) : matched;\n const set = coverage.get(relPath) ?? new Set<string>();\n set.add(name);\n coverage.set(relPath, set);\n }\n }\n\n // Which presets want vectors is a property of the config, not of any file.\n const embedding = embedEnabled(cfg);\n const vectorPresets = embedding ? new Set(presetNames(cfg).filter((name) => presetHasSignal(cfg, name, 'vectors'))) : null;\n\n const files: FileStat[] = [];\n for (const relPath of [...coverage.keys()].sort()) {\n const absPath = join(baseDir, relPath); // join re-applies the platform separator for fs calls\n // node:fs glob matches directories and dangling symlinks; fast-glob returned neither, so\n // one stat filters both back out (throwIfNoEntry keeps a dangling link from throwing).\n const st = statSync(absPath, NO_THROW);\n if (!st?.isFile()) continue;\n const presets = [...(coverage.get(relPath) as Set<string>)].sort();\n const embed = vectorPresets !== null && presets.some((name) => vectorPresets.has(name));\n files.push({ relPath, absPath, mtimeMs: st.mtimeMs, ctimeMs: st.birthtimeMs, size: st.size, presets, embed });\n }\n return files;\n}\n"],"names":["globSync","statSync","join","sep","embedEnabled","presetHasSignal","presetNames","toPosixPath","relPath","separator","split","NO_THROW","throwIfNoEntry","listFiles","cfg","baseDir","coverage","Map","posixNeeded","name","preset","presets","matched","include","cwd","exclude","set","get","Set","add","embedding","vectorPresets","filter","files","keys","sort","absPath","st","isFile","embed","some","has","push","mtimeMs","ctimeMs","birthtimeMs","size"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,QAAQ,QAAQ,UAAU;AAC7C,SAASC,IAAI,EAAEC,GAAG,QAAQ,YAAY;AAEtC,SAASC,YAAY,EAAEC,eAAe,EAAEC,WAAW,QAAQ,qBAAqB;AAYhF,6FAA6F;AAC7F,8FAA8F;AAC9F,OAAO,SAASC,YAAYC,OAAe,EAAEC,YAAoBN,GAAG;IAClE,OAAOM,cAAc,OAAOD,QAAQE,KAAK,CAACD,WAAWP,IAAI,CAAC,OAAOM;AACnE;AAEA,2FAA2F;AAC3F,8FAA8F;AAC9F,0CAA0C;AAC1C,MAAMG,WAAW;IAAEC,gBAAgB;AAAM;AAEzC,OAAO,SAASC,UAAUC,GAAW,EAAEC,OAAe;IACpD,MAAMC,WAAW,IAAIC;IACrB,MAAMC,cAAcf,QAAQ;IAC5B,KAAK,MAAMgB,QAAQb,YAAYQ,KAAM;QACnC,MAAMM,SAASN,IAAIO,OAAO,CAACF,KAAK;QAChC,KAAK,MAAMG,WAAWtB,SAASoB,OAAOG,OAAO,EAAE;YAAEC,KAAKT;YAASU,SAASL,OAAOK,OAAO;QAAC,GAAI;gBAE7ET;YADZ,MAAMR,UAAUU,cAAcX,YAAYe,WAAWA;YACrD,MAAMI,OAAMV,gBAAAA,SAASW,GAAG,CAACnB,sBAAbQ,2BAAAA,gBAAyB,IAAIY;YACzCF,IAAIG,GAAG,CAACV;YACRH,SAASU,GAAG,CAAClB,SAASkB;QACxB;IACF;IAEA,2EAA2E;IAC3E,MAAMI,YAAY1B,aAAaU;IAC/B,MAAMiB,gBAAgBD,YAAY,IAAIF,IAAItB,YAAYQ,KAAKkB,MAAM,CAAC,CAACb,OAASd,gBAAgBS,KAAKK,MAAM,eAAe;IAEtH,MAAMc,QAAoB,EAAE;IAC5B,KAAK,MAAMzB,WAAW;WAAIQ,SAASkB,IAAI;KAAG,CAACC,IAAI,GAAI;QACjD,MAAMC,UAAUlC,KAAKa,SAASP,UAAU,sDAAsD;QAC9F,yFAAyF;QACzF,uFAAuF;QACvF,MAAM6B,KAAKpC,SAASmC,SAASzB;QAC7B,IAAI,EAAC0B,eAAAA,yBAAAA,GAAIC,MAAM,KAAI;QACnB,MAAMjB,UAAU;eAAKL,SAASW,GAAG,CAACnB;SAAyB,CAAC2B,IAAI;QAChE,MAAMI,QAAQR,kBAAkB,QAAQV,QAAQmB,IAAI,CAAC,CAACrB,OAASY,cAAcU,GAAG,CAACtB;QACjFc,MAAMS,IAAI,CAAC;YAAElC;YAAS4B;YAASO,SAASN,GAAGM,OAAO;YAAEC,SAASP,GAAGQ,WAAW;YAAEC,MAAMT,GAAGS,IAAI;YAAEzB;YAASkB;QAAM;IAC7G;IACA,OAAON;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/scan/list.ts"],"sourcesContent":["import { globSync, statSync } from 'node:fs';\nimport { join, sep } from 'node:path';\nimport type { Config } from '../config/index.ts';\nimport { embedEnabled, presetHasSignal, presetNames } from '../config/index.ts';\n\nexport interface FileStat {\n relPath: string;\n absPath: string;\n mtimeMs: number;\n ctimeMs: number; // filesystem birthtime; a clone or copy resets it, like _mtime\n size: number;\n presets: string[]; // every declared preset covering this file (>= 1; union, overlap allowed)\n embed: boolean; // true iff a model is named and some covering preset's signals include vectors\n}\n\n// Presets are views, not partitions: they overlap freely, and a file's covering set (not one\n// owner) drives indexing. Globs resolve relative to baseDir; unmatched files are not indexed.\nexport function toPosixPath(relPath: string, separator: string = sep): string {\n return separator === '\\\\' ? relPath.split(separator).join('/') : relPath;\n}\n\n// Every command pays listFiles before it answers (the freshness check stats each file), so per-file work here is the hottest path in the package.\n// Everything derivable from the config alone is computed once, above the loop.\nconst NO_THROW = { throwIfNoEntry: false } as const;\n\nexport function listFiles(cfg: Config, baseDir: string): FileStat[] {\n const coverage = new Map<string, Set<string>>();\n const posixNeeded = sep === '\\\\';\n for (const name of presetNames(cfg)) {\n const preset = cfg.presets[name];\n for (const matched of globSync(preset.include, { cwd: baseDir, exclude: preset.exclude })) {\n const relPath = posixNeeded ? toPosixPath(matched) : matched;\n const set = coverage.get(relPath) ?? new Set<string>();\n set.add(name);\n coverage.set(relPath, set);\n }\n }\n\n // Which presets want vectors is a property of the config, not of any file.\n const embedding = embedEnabled(cfg);\n const vectorPresets = embedding ? new Set(presetNames(cfg).filter((name) => presetHasSignal(cfg, name, 'vectors'))) : null;\n\n const files: FileStat[] = [];\n for (const relPath of [...coverage.keys()].sort()) {\n const absPath = join(baseDir, relPath); // join re-applies the platform separator for fs calls\n // node:fs glob matches directories and dangling symlinks; fast-glob returned neither, so\n // one stat filters both back out (throwIfNoEntry keeps a dangling link from throwing).\n const st = statSync(absPath, NO_THROW);\n if (!st?.isFile()) continue;\n const presets = [...(coverage.get(relPath) as Set<string>)].sort();\n const embed = vectorPresets !== null && presets.some((name) => vectorPresets.has(name));\n files.push({ relPath, absPath, mtimeMs: st.mtimeMs, ctimeMs: st.birthtimeMs, size: st.size, presets, embed });\n }\n return files;\n}\n"],"names":["globSync","statSync","join","sep","embedEnabled","presetHasSignal","presetNames","toPosixPath","relPath","separator","split","NO_THROW","throwIfNoEntry","listFiles","cfg","baseDir","coverage","Map","posixNeeded","name","preset","presets","matched","include","cwd","exclude","set","get","Set","add","embedding","vectorPresets","filter","files","keys","sort","absPath","st","isFile","embed","some","has","push","mtimeMs","ctimeMs","birthtimeMs","size"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,QAAQ,QAAQ,UAAU;AAC7C,SAASC,IAAI,EAAEC,GAAG,QAAQ,YAAY;AAEtC,SAASC,YAAY,EAAEC,eAAe,EAAEC,WAAW,QAAQ,qBAAqB;AAYhF,6FAA6F;AAC7F,8FAA8F;AAC9F,OAAO,SAASC,YAAYC,OAAe,EAAEC,YAAoBN,GAAG;IAClE,OAAOM,cAAc,OAAOD,QAAQE,KAAK,CAACD,WAAWP,IAAI,CAAC,OAAOM;AACnE;AAEA,kJAAkJ;AAClJ,+EAA+E;AAC/E,MAAMG,WAAW;IAAEC,gBAAgB;AAAM;AAEzC,OAAO,SAASC,UAAUC,GAAW,EAAEC,OAAe;IACpD,MAAMC,WAAW,IAAIC;IACrB,MAAMC,cAAcf,QAAQ;IAC5B,KAAK,MAAMgB,QAAQb,YAAYQ,KAAM;QACnC,MAAMM,SAASN,IAAIO,OAAO,CAACF,KAAK;QAChC,KAAK,MAAMG,WAAWtB,SAASoB,OAAOG,OAAO,EAAE;YAAEC,KAAKT;YAASU,SAASL,OAAOK,OAAO;QAAC,GAAI;gBAE7ET;YADZ,MAAMR,UAAUU,cAAcX,YAAYe,WAAWA;YACrD,MAAMI,OAAMV,gBAAAA,SAASW,GAAG,CAACnB,sBAAbQ,2BAAAA,gBAAyB,IAAIY;YACzCF,IAAIG,GAAG,CAACV;YACRH,SAASU,GAAG,CAAClB,SAASkB;QACxB;IACF;IAEA,2EAA2E;IAC3E,MAAMI,YAAY1B,aAAaU;IAC/B,MAAMiB,gBAAgBD,YAAY,IAAIF,IAAItB,YAAYQ,KAAKkB,MAAM,CAAC,CAACb,OAASd,gBAAgBS,KAAKK,MAAM,eAAe;IAEtH,MAAMc,QAAoB,EAAE;IAC5B,KAAK,MAAMzB,WAAW;WAAIQ,SAASkB,IAAI;KAAG,CAACC,IAAI,GAAI;QACjD,MAAMC,UAAUlC,KAAKa,SAASP,UAAU,sDAAsD;QAC9F,yFAAyF;QACzF,uFAAuF;QACvF,MAAM6B,KAAKpC,SAASmC,SAASzB;QAC7B,IAAI,EAAC0B,eAAAA,yBAAAA,GAAIC,MAAM,KAAI;QACnB,MAAMjB,UAAU;eAAKL,SAASW,GAAG,CAACnB;SAAyB,CAAC2B,IAAI;QAChE,MAAMI,QAAQR,kBAAkB,QAAQV,QAAQmB,IAAI,CAAC,CAACrB,OAASY,cAAcU,GAAG,CAACtB;QACjFc,MAAMS,IAAI,CAAC;YAAElC;YAAS4B;YAASO,SAASN,GAAGM,OAAO;YAAEC,SAASP,GAAGQ,WAAW;YAAEC,MAAMT,GAAGS,IAAI;YAAEzB;YAASkB;QAAM;IAC7G;IACA,OAAON;AACT"}