@soleri/core 9.0.4 → 9.3.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 (744) hide show
  1. package/data/flows/build.flow.yaml +8 -9
  2. package/data/flows/deliver.flow.yaml +9 -10
  3. package/data/flows/design.flow.yaml +3 -4
  4. package/data/flows/enhance.flow.yaml +5 -6
  5. package/data/flows/explore.flow.yaml +3 -4
  6. package/data/flows/fix.flow.yaml +5 -6
  7. package/data/flows/plan.flow.yaml +4 -5
  8. package/data/flows/review.flow.yaml +3 -4
  9. package/dist/brain/intelligence.d.ts +27 -0
  10. package/dist/brain/intelligence.d.ts.map +1 -1
  11. package/dist/brain/intelligence.js +160 -14
  12. package/dist/brain/intelligence.js.map +1 -1
  13. package/dist/brain/learning-radar.d.ts +4 -0
  14. package/dist/brain/learning-radar.d.ts.map +1 -1
  15. package/dist/brain/learning-radar.js +20 -1
  16. package/dist/brain/learning-radar.js.map +1 -1
  17. package/dist/brain/strength-scorer.d.ts +31 -0
  18. package/dist/brain/strength-scorer.d.ts.map +1 -0
  19. package/dist/brain/strength-scorer.js +264 -0
  20. package/dist/brain/strength-scorer.js.map +1 -0
  21. package/dist/chat/agent-loop.d.ts.map +1 -1
  22. package/dist/chat/agent-loop.js +2 -0
  23. package/dist/chat/agent-loop.js.map +1 -1
  24. package/dist/chat/notifications.d.ts.map +1 -1
  25. package/dist/chat/notifications.js +2 -0
  26. package/dist/chat/notifications.js.map +1 -1
  27. package/dist/claudemd/compose.js +1 -1
  28. package/dist/claudemd/compose.js.map +1 -1
  29. package/dist/control/intent-router.d.ts.map +1 -1
  30. package/dist/control/intent-router.js +12 -4
  31. package/dist/control/intent-router.js.map +1 -1
  32. package/dist/curator/contradiction-detector.d.ts +27 -0
  33. package/dist/curator/contradiction-detector.d.ts.map +1 -0
  34. package/dist/curator/contradiction-detector.js +62 -0
  35. package/dist/curator/contradiction-detector.js.map +1 -0
  36. package/dist/curator/curator.d.ts +3 -4
  37. package/dist/curator/curator.d.ts.map +1 -1
  38. package/dist/curator/curator.js +94 -453
  39. package/dist/curator/curator.js.map +1 -1
  40. package/dist/curator/duplicate-detector.d.ts +14 -0
  41. package/dist/curator/duplicate-detector.d.ts.map +1 -0
  42. package/dist/curator/duplicate-detector.js +77 -0
  43. package/dist/curator/duplicate-detector.js.map +1 -0
  44. package/dist/curator/health-audit.d.ts +15 -0
  45. package/dist/curator/health-audit.d.ts.map +1 -0
  46. package/dist/curator/health-audit.js +97 -0
  47. package/dist/curator/health-audit.js.map +1 -0
  48. package/dist/curator/metadata-enricher.d.ts +17 -0
  49. package/dist/curator/metadata-enricher.d.ts.map +1 -0
  50. package/dist/curator/metadata-enricher.js +60 -0
  51. package/dist/curator/metadata-enricher.js.map +1 -0
  52. package/dist/curator/schema.d.ts +7 -0
  53. package/dist/curator/schema.d.ts.map +1 -0
  54. package/dist/curator/schema.js +62 -0
  55. package/dist/curator/schema.js.map +1 -0
  56. package/dist/curator/tag-manager.d.ts +36 -0
  57. package/dist/curator/tag-manager.d.ts.map +1 -0
  58. package/dist/curator/tag-manager.js +78 -0
  59. package/dist/curator/tag-manager.js.map +1 -0
  60. package/dist/engine/bin/soleri-engine.js +24 -3
  61. package/dist/engine/bin/soleri-engine.js.map +1 -1
  62. package/dist/engine/core-ops.d.ts.map +1 -1
  63. package/dist/engine/core-ops.js +23 -8
  64. package/dist/engine/core-ops.js.map +1 -1
  65. package/dist/engine/module-manifest.d.ts.map +1 -1
  66. package/dist/engine/module-manifest.js +42 -2
  67. package/dist/engine/module-manifest.js.map +1 -1
  68. package/dist/engine/register-engine.d.ts.map +1 -1
  69. package/dist/engine/register-engine.js +50 -2
  70. package/dist/engine/register-engine.js.map +1 -1
  71. package/dist/errors/retry.d.ts.map +1 -1
  72. package/dist/errors/retry.js +2 -0
  73. package/dist/errors/retry.js.map +1 -1
  74. package/dist/facades/types.d.ts +1 -1
  75. package/dist/flows/chain-types.d.ts +18 -18
  76. package/dist/flows/gate-evaluator.d.ts.map +1 -1
  77. package/dist/flows/gate-evaluator.js +22 -0
  78. package/dist/flows/gate-evaluator.js.map +1 -1
  79. package/dist/flows/types.d.ts +157 -28
  80. package/dist/flows/types.d.ts.map +1 -1
  81. package/dist/flows/types.js +4 -0
  82. package/dist/flows/types.js.map +1 -1
  83. package/dist/index.d.ts +10 -2
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +9 -1
  86. package/dist/index.js.map +1 -1
  87. package/dist/intake/intake-pipeline.d.ts.map +1 -1
  88. package/dist/intake/intake-pipeline.js +1 -0
  89. package/dist/intake/intake-pipeline.js.map +1 -1
  90. package/dist/intake/text-ingester.d.ts.map +1 -1
  91. package/dist/intake/text-ingester.js +2 -0
  92. package/dist/intake/text-ingester.js.map +1 -1
  93. package/dist/llm/key-pool.d.ts +1 -1
  94. package/dist/llm/key-pool.d.ts.map +1 -1
  95. package/dist/llm/key-pool.js +3 -4
  96. package/dist/llm/key-pool.js.map +1 -1
  97. package/dist/llm/utils.d.ts.map +1 -1
  98. package/dist/llm/utils.js +2 -0
  99. package/dist/llm/utils.js.map +1 -1
  100. package/dist/migrations/migration-runner.test-helpers.d.ts +13 -0
  101. package/dist/migrations/migration-runner.test-helpers.d.ts.map +1 -0
  102. package/dist/migrations/migration-runner.test-helpers.js +47 -0
  103. package/dist/migrations/migration-runner.test-helpers.js.map +1 -0
  104. package/dist/operator/operator-profile.d.ts +44 -0
  105. package/dist/operator/operator-profile.d.ts.map +1 -0
  106. package/dist/operator/operator-profile.js +383 -0
  107. package/dist/operator/operator-profile.js.map +1 -0
  108. package/dist/operator/operator-signals.d.ts +45 -0
  109. package/dist/operator/operator-signals.d.ts.map +1 -0
  110. package/dist/operator/operator-signals.js +228 -0
  111. package/dist/operator/operator-signals.js.map +1 -0
  112. package/dist/operator/operator-types.d.ts +360 -0
  113. package/dist/operator/operator-types.d.ts.map +1 -0
  114. package/dist/operator/operator-types.js +24 -0
  115. package/dist/operator/operator-types.js.map +1 -0
  116. package/dist/packs/types.d.ts +27 -27
  117. package/dist/paths.d.ts +40 -0
  118. package/dist/paths.d.ts.map +1 -0
  119. package/dist/paths.js +98 -0
  120. package/dist/paths.js.map +1 -0
  121. package/dist/persistence/index.d.ts +1 -1
  122. package/dist/persistence/index.d.ts.map +1 -1
  123. package/dist/persistence/index.js +1 -1
  124. package/dist/persistence/index.js.map +1 -1
  125. package/dist/persistence/sqlite-provider.d.ts +2 -0
  126. package/dist/persistence/sqlite-provider.d.ts.map +1 -1
  127. package/dist/persistence/sqlite-provider.js +8 -5
  128. package/dist/persistence/sqlite-provider.js.map +1 -1
  129. package/dist/planning/evidence-collector.d.ts +13 -1
  130. package/dist/planning/evidence-collector.d.ts.map +1 -1
  131. package/dist/planning/evidence-collector.js +33 -0
  132. package/dist/planning/evidence-collector.js.map +1 -1
  133. package/dist/planning/gap-analysis.d.ts +5 -4
  134. package/dist/planning/gap-analysis.d.ts.map +1 -1
  135. package/dist/planning/gap-analysis.js +7 -341
  136. package/dist/planning/gap-analysis.js.map +1 -1
  137. package/dist/planning/gap-passes.d.ts +19 -0
  138. package/dist/planning/gap-passes.d.ts.map +1 -0
  139. package/dist/planning/gap-passes.js +174 -0
  140. package/dist/planning/gap-passes.js.map +1 -0
  141. package/dist/planning/gap-patterns.d.ts +29 -0
  142. package/dist/planning/gap-patterns.d.ts.map +1 -0
  143. package/dist/planning/gap-patterns.js +175 -0
  144. package/dist/planning/gap-patterns.js.map +1 -0
  145. package/dist/planning/gap-types.d.ts +1 -1
  146. package/dist/planning/gap-types.d.ts.map +1 -1
  147. package/dist/planning/gap-types.js +1 -0
  148. package/dist/planning/gap-types.js.map +1 -1
  149. package/dist/planning/github-projection.d.ts +122 -0
  150. package/dist/planning/github-projection.d.ts.map +1 -0
  151. package/dist/planning/github-projection.js +313 -0
  152. package/dist/planning/github-projection.js.map +1 -0
  153. package/dist/planning/impact-analyzer.d.ts +26 -0
  154. package/dist/planning/impact-analyzer.d.ts.map +1 -0
  155. package/dist/planning/impact-analyzer.js +201 -0
  156. package/dist/planning/impact-analyzer.js.map +1 -0
  157. package/dist/planning/plan-lifecycle.d.ts +136 -0
  158. package/dist/planning/plan-lifecycle.d.ts.map +1 -0
  159. package/dist/planning/plan-lifecycle.js +309 -0
  160. package/dist/planning/plan-lifecycle.js.map +1 -0
  161. package/dist/planning/planner-types.d.ts +202 -0
  162. package/dist/planning/planner-types.d.ts.map +1 -0
  163. package/dist/planning/planner-types.js +6 -0
  164. package/dist/planning/planner-types.js.map +1 -0
  165. package/dist/planning/planner.d.ts +31 -383
  166. package/dist/planning/planner.d.ts.map +1 -1
  167. package/dist/planning/planner.js +151 -832
  168. package/dist/planning/planner.js.map +1 -1
  169. package/dist/planning/rationalization-detector.d.ts +32 -0
  170. package/dist/planning/rationalization-detector.d.ts.map +1 -0
  171. package/dist/planning/rationalization-detector.js +89 -0
  172. package/dist/planning/rationalization-detector.js.map +1 -0
  173. package/dist/planning/reconciliation-engine.d.ts +47 -0
  174. package/dist/planning/reconciliation-engine.d.ts.map +1 -0
  175. package/dist/planning/reconciliation-engine.js +128 -0
  176. package/dist/planning/reconciliation-engine.js.map +1 -0
  177. package/dist/planning/task-verifier.d.ts +85 -0
  178. package/dist/planning/task-verifier.d.ts.map +1 -0
  179. package/dist/planning/task-verifier.js +235 -0
  180. package/dist/planning/task-verifier.js.map +1 -0
  181. package/dist/plugins/types.d.ts +4 -4
  182. package/dist/runtime/admin-ops.d.ts +2 -2
  183. package/dist/runtime/admin-ops.d.ts.map +1 -1
  184. package/dist/runtime/admin-ops.js +44 -17
  185. package/dist/runtime/admin-ops.js.map +1 -1
  186. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  187. package/dist/runtime/admin-setup-ops.js +22 -46
  188. package/dist/runtime/admin-setup-ops.js.map +1 -1
  189. package/dist/runtime/archive-ops.d.ts +10 -0
  190. package/dist/runtime/archive-ops.d.ts.map +1 -0
  191. package/dist/runtime/archive-ops.js +310 -0
  192. package/dist/runtime/archive-ops.js.map +1 -0
  193. package/dist/runtime/branching-ops.d.ts +12 -0
  194. package/dist/runtime/branching-ops.d.ts.map +1 -0
  195. package/dist/runtime/branching-ops.js +100 -0
  196. package/dist/runtime/branching-ops.js.map +1 -0
  197. package/dist/runtime/capture-ops.d.ts.map +1 -1
  198. package/dist/runtime/capture-ops.js +42 -7
  199. package/dist/runtime/capture-ops.js.map +1 -1
  200. package/dist/runtime/claude-md-helpers.js +1 -1
  201. package/dist/runtime/claude-md-helpers.js.map +1 -1
  202. package/dist/runtime/context-health.d.ts +31 -0
  203. package/dist/runtime/context-health.d.ts.map +1 -0
  204. package/dist/runtime/context-health.js +57 -0
  205. package/dist/runtime/context-health.js.map +1 -0
  206. package/dist/runtime/facades/archive-facade.d.ts +10 -0
  207. package/dist/runtime/facades/archive-facade.d.ts.map +1 -0
  208. package/dist/runtime/facades/archive-facade.js +11 -0
  209. package/dist/runtime/facades/archive-facade.js.map +1 -0
  210. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  211. package/dist/runtime/facades/brain-facade.js +2 -0
  212. package/dist/runtime/facades/brain-facade.js.map +1 -1
  213. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  214. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  215. package/dist/runtime/facades/branching-facade.js +8 -0
  216. package/dist/runtime/facades/branching-facade.js.map +1 -0
  217. package/dist/runtime/facades/chat-facade.d.ts +7 -0
  218. package/dist/runtime/facades/chat-facade.d.ts.map +1 -1
  219. package/dist/runtime/facades/chat-facade.js +15 -800
  220. package/dist/runtime/facades/chat-facade.js.map +1 -1
  221. package/dist/runtime/facades/chat-service-ops.d.ts +9 -0
  222. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -0
  223. package/dist/runtime/facades/chat-service-ops.js +332 -0
  224. package/dist/runtime/facades/chat-service-ops.js.map +1 -0
  225. package/dist/runtime/facades/chat-session-ops.d.ts +8 -0
  226. package/dist/runtime/facades/chat-session-ops.d.ts.map +1 -0
  227. package/dist/runtime/facades/chat-session-ops.js +136 -0
  228. package/dist/runtime/facades/chat-session-ops.js.map +1 -0
  229. package/dist/runtime/facades/chat-state.d.ts +31 -0
  230. package/dist/runtime/facades/chat-state.d.ts.map +1 -0
  231. package/dist/runtime/facades/chat-state.js +32 -0
  232. package/dist/runtime/facades/chat-state.js.map +1 -0
  233. package/dist/runtime/facades/chat-transport-ops.d.ts +9 -0
  234. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -0
  235. package/dist/runtime/facades/chat-transport-ops.js +337 -0
  236. package/dist/runtime/facades/chat-transport-ops.js.map +1 -0
  237. package/dist/runtime/facades/control-facade.d.ts.map +1 -1
  238. package/dist/runtime/facades/control-facade.js +4 -1
  239. package/dist/runtime/facades/control-facade.js.map +1 -1
  240. package/dist/runtime/facades/index.d.ts.map +1 -1
  241. package/dist/runtime/facades/index.js +48 -0
  242. package/dist/runtime/facades/index.js.map +1 -1
  243. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  244. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  245. package/dist/runtime/facades/intake-facade.js +11 -0
  246. package/dist/runtime/facades/intake-facade.js.map +1 -0
  247. package/dist/runtime/facades/links-facade.d.ts +9 -0
  248. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  249. package/dist/runtime/facades/links-facade.js +10 -0
  250. package/dist/runtime/facades/links-facade.js.map +1 -0
  251. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  252. package/dist/runtime/facades/memory-facade.js +75 -6
  253. package/dist/runtime/facades/memory-facade.js.map +1 -1
  254. package/dist/runtime/facades/operator-facade.d.ts +8 -0
  255. package/dist/runtime/facades/operator-facade.d.ts.map +1 -0
  256. package/dist/runtime/facades/operator-facade.js +220 -0
  257. package/dist/runtime/facades/operator-facade.js.map +1 -0
  258. package/dist/runtime/facades/orchestrate-facade.js +3 -3
  259. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  260. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  261. package/dist/runtime/facades/plan-facade.js +42 -6
  262. package/dist/runtime/facades/plan-facade.js.map +1 -1
  263. package/dist/runtime/facades/review-facade.d.ts +7 -0
  264. package/dist/runtime/facades/review-facade.d.ts.map +1 -0
  265. package/dist/runtime/facades/review-facade.js +8 -0
  266. package/dist/runtime/facades/review-facade.js.map +1 -0
  267. package/dist/runtime/facades/sync-facade.d.ts +7 -0
  268. package/dist/runtime/facades/sync-facade.d.ts.map +1 -0
  269. package/dist/runtime/facades/sync-facade.js +8 -0
  270. package/dist/runtime/facades/sync-facade.js.map +1 -0
  271. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  272. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  273. package/dist/runtime/facades/tier-facade.js +8 -0
  274. package/dist/runtime/facades/tier-facade.js.map +1 -0
  275. package/dist/runtime/facades/vault-facade.d.ts +12 -1
  276. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  277. package/dist/runtime/facades/vault-facade.js +55 -251
  278. package/dist/runtime/facades/vault-facade.js.map +1 -1
  279. package/dist/runtime/github-integration.d.ts +49 -0
  280. package/dist/runtime/github-integration.d.ts.map +1 -0
  281. package/dist/runtime/github-integration.js +120 -0
  282. package/dist/runtime/github-integration.js.map +1 -0
  283. package/dist/runtime/grading-ops.js +1 -1
  284. package/dist/runtime/grading-ops.js.map +1 -1
  285. package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
  286. package/dist/runtime/memory-extra-ops.js +6 -2
  287. package/dist/runtime/memory-extra-ops.js.map +1 -1
  288. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  289. package/dist/runtime/orchestrate-ops.js +386 -37
  290. package/dist/runtime/orchestrate-ops.js.map +1 -1
  291. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  292. package/dist/runtime/planning-extra-ops.js +69 -4
  293. package/dist/runtime/planning-extra-ops.js.map +1 -1
  294. package/dist/runtime/review-ops.d.ts +10 -0
  295. package/dist/runtime/review-ops.d.ts.map +1 -0
  296. package/dist/runtime/review-ops.js +97 -0
  297. package/dist/runtime/review-ops.js.map +1 -0
  298. package/dist/runtime/runtime.d.ts.map +1 -1
  299. package/dist/runtime/runtime.js +29 -12
  300. package/dist/runtime/runtime.js.map +1 -1
  301. package/dist/runtime/session-briefing.d.ts +3 -0
  302. package/dist/runtime/session-briefing.d.ts.map +1 -1
  303. package/dist/runtime/session-briefing.js +72 -1
  304. package/dist/runtime/session-briefing.js.map +1 -1
  305. package/dist/runtime/sync-ops.d.ts +12 -0
  306. package/dist/runtime/sync-ops.d.ts.map +1 -0
  307. package/dist/runtime/sync-ops.js +288 -0
  308. package/dist/runtime/sync-ops.js.map +1 -0
  309. package/dist/runtime/tier-ops.d.ts +13 -0
  310. package/dist/runtime/tier-ops.d.ts.map +1 -0
  311. package/dist/runtime/tier-ops.js +110 -0
  312. package/dist/runtime/tier-ops.js.map +1 -0
  313. package/dist/runtime/types.d.ts +10 -4
  314. package/dist/runtime/types.d.ts.map +1 -1
  315. package/dist/runtime/vault-extra-ops.d.ts +5 -4
  316. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  317. package/dist/runtime/vault-extra-ops.js +5 -300
  318. package/dist/runtime/vault-extra-ops.js.map +1 -1
  319. package/dist/runtime/vault-sharing-ops.d.ts +4 -4
  320. package/dist/runtime/vault-sharing-ops.d.ts.map +1 -1
  321. package/dist/runtime/vault-sharing-ops.js +5 -300
  322. package/dist/runtime/vault-sharing-ops.js.map +1 -1
  323. package/dist/skills/sync-skills.d.ts +27 -0
  324. package/dist/skills/sync-skills.d.ts.map +1 -0
  325. package/dist/skills/sync-skills.js +81 -0
  326. package/dist/skills/sync-skills.js.map +1 -0
  327. package/dist/update-check.d.ts +14 -0
  328. package/dist/update-check.d.ts.map +1 -0
  329. package/dist/update-check.js +96 -0
  330. package/dist/update-check.js.map +1 -0
  331. package/dist/vault/linking.d.ts +10 -12
  332. package/dist/vault/linking.d.ts.map +1 -1
  333. package/dist/vault/linking.js +140 -161
  334. package/dist/vault/linking.js.map +1 -1
  335. package/dist/vault/vault-entries.d.ts +69 -0
  336. package/dist/vault/vault-entries.d.ts.map +1 -0
  337. package/dist/vault/vault-entries.js +299 -0
  338. package/dist/vault/vault-entries.js.map +1 -0
  339. package/dist/vault/vault-interfaces.d.ts +153 -0
  340. package/dist/vault/vault-interfaces.d.ts.map +1 -0
  341. package/dist/vault/vault-interfaces.js +2 -0
  342. package/dist/vault/vault-interfaces.js.map +1 -0
  343. package/dist/vault/vault-maintenance.d.ts +40 -0
  344. package/dist/vault/vault-maintenance.d.ts.map +1 -0
  345. package/dist/vault/vault-maintenance.js +146 -0
  346. package/dist/vault/vault-maintenance.js.map +1 -0
  347. package/dist/vault/vault-markdown-sync.d.ts +22 -0
  348. package/dist/vault/vault-markdown-sync.d.ts.map +1 -0
  349. package/dist/vault/vault-markdown-sync.js +143 -0
  350. package/dist/vault/vault-markdown-sync.js.map +1 -0
  351. package/dist/vault/vault-memories.d.ts +61 -0
  352. package/dist/vault/vault-memories.d.ts.map +1 -0
  353. package/dist/vault/vault-memories.js +242 -0
  354. package/dist/vault/vault-memories.js.map +1 -0
  355. package/dist/vault/vault-schema.d.ts +9 -0
  356. package/dist/vault/vault-schema.d.ts.map +1 -0
  357. package/dist/vault/vault-schema.js +205 -0
  358. package/dist/vault/vault-schema.js.map +1 -0
  359. package/dist/vault/vault.d.ts +29 -81
  360. package/dist/vault/vault.d.ts.map +1 -1
  361. package/dist/vault/vault.js +82 -931
  362. package/dist/vault/vault.js.map +1 -1
  363. package/package.json +7 -7
  364. package/src/agency/agency-manager.test.ts +620 -0
  365. package/src/agency/default-rules.test.ts +236 -0
  366. package/src/{__tests__ → brain}/brain-intelligence.test.ts +37 -14
  367. package/src/{__tests__ → brain}/brain.test.ts +1 -1
  368. package/src/brain/intelligence.ts +196 -15
  369. package/src/brain/learning-radar.ts +22 -1
  370. package/src/{__tests__ → brain}/second-brain-features.test.ts +4 -4
  371. package/src/{__tests__ → brain}/session-lifecycle.test.ts +2 -2
  372. package/src/capabilities/chain-mapping.test.ts +66 -0
  373. package/src/capabilities/registry.test.ts +359 -0
  374. package/src/chat/agent-loop.test.ts +384 -0
  375. package/src/chat/agent-loop.ts +2 -0
  376. package/src/{__tests__ → chat}/chat-differentiators.test.ts +3 -3
  377. package/src/{__tests__ → chat}/chat-enhanced.test.ts +4 -4
  378. package/src/{__tests__ → chat}/chat-transport.test.ts +6 -6
  379. package/src/chat/mcp-bridge.test.ts +178 -0
  380. package/src/chat/notifications.ts +2 -0
  381. package/src/chat/output-compressor.test.ts +164 -0
  382. package/src/claudemd/compose.test.ts +178 -0
  383. package/src/claudemd/compose.ts +1 -1
  384. package/src/claudemd/inject.test.ts +201 -0
  385. package/src/context/context-engine.test.ts +506 -0
  386. package/src/control/identity-manager.test.ts +305 -0
  387. package/src/control/intent-router.test.ts +360 -0
  388. package/src/control/intent-router.ts +13 -4
  389. package/src/curator/classifier.test.ts +110 -0
  390. package/src/curator/contradiction-detector.test.ts +205 -0
  391. package/src/curator/contradiction-detector.ts +87 -0
  392. package/src/{__tests__ → curator}/curator-pipeline-e2e.test.ts +10 -10
  393. package/src/{__tests__ → curator}/curator.test.ts +77 -1
  394. package/src/curator/curator.ts +160 -600
  395. package/src/curator/duplicate-detector.test.ts +245 -0
  396. package/src/curator/duplicate-detector.ts +103 -0
  397. package/src/curator/health-audit.ts +126 -0
  398. package/src/curator/metadata-enricher.ts +84 -0
  399. package/src/curator/quality-gate.test.ts +175 -0
  400. package/src/curator/schema.ts +65 -0
  401. package/src/curator/tag-manager.test.ts +173 -0
  402. package/src/curator/tag-manager.ts +109 -0
  403. package/src/domain-packs/inject-rules.test.ts +117 -0
  404. package/src/domain-packs/knowledge-installer.test.ts +163 -0
  405. package/src/domain-packs/loader.test.ts +86 -0
  406. package/src/domain-packs/pack-runtime.test.ts +140 -0
  407. package/src/domain-packs/skills-installer.test.ts +135 -0
  408. package/src/domain-packs/token-resolver.test.ts +148 -0
  409. package/src/domain-packs/types.test.ts +144 -0
  410. package/src/enforcement/adapters/claude-code.test.ts +216 -0
  411. package/src/enforcement/registry.test.ts +258 -0
  412. package/src/engine/bin/soleri-engine.ts +30 -4
  413. package/src/engine/core-ops.test.ts +254 -0
  414. package/src/engine/core-ops.ts +25 -8
  415. package/src/engine/module-manifest.test.ts +125 -0
  416. package/src/engine/module-manifest.ts +42 -2
  417. package/src/engine/register-engine.test.ts +235 -0
  418. package/src/engine/register-engine.ts +50 -3
  419. package/src/errors/classify.test.ts +203 -0
  420. package/src/errors/retry.test.ts +153 -0
  421. package/src/errors/retry.ts +2 -0
  422. package/src/errors/types.test.ts +108 -0
  423. package/src/events/event-bus.test.ts +149 -0
  424. package/src/extensions/middleware.test.ts +234 -0
  425. package/src/facades/facade-factory.test.ts +470 -0
  426. package/src/flows/chain-runner.test.ts +273 -0
  427. package/src/flows/context-router.test.ts +52 -0
  428. package/src/flows/dispatch-registry.test.ts +128 -0
  429. package/src/flows/epilogue.test.ts +113 -0
  430. package/src/flows/executor.test.ts +263 -0
  431. package/src/flows/gate-evaluator.test.ts +200 -0
  432. package/src/flows/gate-evaluator.ts +23 -0
  433. package/src/flows/types.ts +4 -0
  434. package/src/governance/governance.test.ts +842 -0
  435. package/src/{__tests__ → health}/health-registry.test.ts +75 -55
  436. package/src/health/vault-integrity.test.ts +110 -0
  437. package/src/index.ts +92 -0
  438. package/src/intake/content-classifier.test.ts +279 -0
  439. package/src/intake/dedup-gate.test.ts +147 -0
  440. package/src/intake/intake-pipeline.test.ts +508 -0
  441. package/src/intake/intake-pipeline.ts +1 -0
  442. package/src/intake/text-ingester.test.ts +200 -0
  443. package/src/intake/text-ingester.ts +2 -0
  444. package/src/llm/key-pool.test.ts +234 -0
  445. package/src/llm/key-pool.ts +3 -4
  446. package/src/llm/llm-client.test.ts +342 -0
  447. package/src/llm/oauth-discovery.test.ts +180 -0
  448. package/src/llm/utils.test.ts +371 -0
  449. package/src/llm/utils.ts +2 -0
  450. package/src/{__tests__ → logging}/logger.test.ts +44 -62
  451. package/src/loop/loop-manager.test.ts +515 -0
  452. package/src/migrations/migration-runner.edge-cases.test.ts +314 -0
  453. package/src/migrations/migration-runner.test-helpers.ts +64 -0
  454. package/src/migrations/migration-runner.test.ts +385 -0
  455. package/src/operator/auto-signal-pipeline.test.ts +207 -0
  456. package/src/operator/operator-profile-extended.test.ts +330 -0
  457. package/src/operator/operator-profile.test.ts +332 -0
  458. package/src/operator/operator-profile.ts +485 -0
  459. package/src/operator/operator-signals-extended.test.ts +257 -0
  460. package/src/operator/operator-signals.test.ts +277 -0
  461. package/src/operator/operator-signals.ts +262 -0
  462. package/src/operator/operator-types.ts +444 -0
  463. package/src/operator/prompts/hook-precompact-operator-dispatch.md +98 -0
  464. package/src/operator/prompts/subagent-soft-signal-extractor.md +130 -0
  465. package/src/operator/prompts/subagent-synthesis-cognition.md +190 -0
  466. package/src/operator/prompts/subagent-synthesis-communication.md +146 -0
  467. package/src/operator/prompts/subagent-synthesis-technical.md +170 -0
  468. package/src/operator/prompts/subagent-synthesis-trust.md +149 -0
  469. package/src/{__tests__ → packs}/pack-lockfile.test.ts +3 -3
  470. package/src/{__tests__ → packs}/pack-system.test.ts +2 -2
  471. package/src/paths.ts +115 -0
  472. package/src/persistence/index.ts +1 -1
  473. package/src/persistence/sqlite-provider.test.ts +540 -0
  474. package/src/persistence/sqlite-provider.ts +8 -5
  475. package/src/persona/defaults.test.ts +55 -0
  476. package/src/persona/loader.test.ts +67 -0
  477. package/src/persona/prompt-generator.test.ts +127 -0
  478. package/src/planning/evidence-collector.test.ts +515 -0
  479. package/src/planning/evidence-collector.ts +47 -0
  480. package/src/planning/gap-analysis-alternatives.test.ts +199 -0
  481. package/src/planning/gap-analysis.ts +21 -636
  482. package/src/planning/gap-passes.test.ts +554 -0
  483. package/src/planning/gap-passes.ts +367 -0
  484. package/src/planning/gap-patterns.test.ts +394 -0
  485. package/src/planning/gap-patterns.ts +317 -0
  486. package/src/planning/gap-types.ts +4 -1
  487. package/src/planning/github-projection.test.ts +182 -0
  488. package/src/planning/github-projection.ts +446 -0
  489. package/src/planning/impact-analyzer.test.ts +167 -0
  490. package/src/planning/impact-analyzer.ts +251 -0
  491. package/src/planning/plan-lifecycle.test.ts +379 -0
  492. package/src/planning/plan-lifecycle.ts +377 -0
  493. package/src/planning/planner-types.ts +215 -0
  494. package/src/{__tests__ → planning}/planner.test.ts +179 -15
  495. package/src/planning/planner.ts +221 -1112
  496. package/src/planning/rationalization-detector.test.ts +156 -0
  497. package/src/planning/rationalization-detector.ts +136 -0
  498. package/src/planning/reconciliation-engine.test.ts +158 -0
  499. package/src/planning/reconciliation-engine.ts +161 -0
  500. package/src/planning/task-verifier.test.ts +267 -0
  501. package/src/planning/task-verifier.ts +309 -0
  502. package/src/planning/verification-protocol.test.ts +201 -0
  503. package/src/playbooks/generic/generic-playbooks.test.ts +438 -0
  504. package/src/playbooks/index.test.ts +77 -0
  505. package/src/playbooks/playbook-executor.test.ts +253 -0
  506. package/src/playbooks/playbook-registry.test.ts +232 -0
  507. package/src/playbooks/playbook-seeder.test.ts +153 -0
  508. package/src/plugins/plugin-loader.test.ts +217 -0
  509. package/src/plugins/plugin-registry.test.ts +284 -0
  510. package/src/project/project-registry.test.ts +439 -0
  511. package/src/prompts/parser.test.ts +100 -0
  512. package/src/prompts/template-manager.test.ts +112 -0
  513. package/src/{__tests__ → queue}/async-infrastructure.test.ts +3 -3
  514. package/src/queue/job-queue.test.ts +327 -0
  515. package/src/queue/pipeline-runner.test.ts +209 -0
  516. package/src/runtime/admin-extra-ops.test.ts +513 -0
  517. package/src/runtime/admin-ops.test.ts +255 -0
  518. package/src/runtime/admin-ops.ts +45 -17
  519. package/src/runtime/admin-setup-ops.test.ts +327 -0
  520. package/src/runtime/admin-setup-ops.ts +26 -42
  521. package/src/runtime/archive-ops.test.ts +272 -0
  522. package/src/runtime/archive-ops.ts +347 -0
  523. package/src/runtime/branching-ops.test.ts +144 -0
  524. package/src/runtime/branching-ops.ts +107 -0
  525. package/src/runtime/capture-ops.test.ts +419 -0
  526. package/src/runtime/capture-ops.ts +50 -8
  527. package/src/runtime/chain-ops.test.ts +159 -0
  528. package/src/runtime/claude-md-helpers.test.ts +189 -0
  529. package/src/runtime/claude-md-helpers.ts +1 -1
  530. package/src/runtime/context-health.test.ts +76 -0
  531. package/src/runtime/context-health.ts +83 -0
  532. package/src/runtime/curator-extra-ops.test.ts +204 -0
  533. package/src/runtime/deprecation.test.ts +98 -0
  534. package/src/runtime/domain-ops.test.ts +278 -0
  535. package/src/runtime/facades/admin-facade.test.ts +330 -0
  536. package/src/runtime/facades/agency-facade.test.ts +278 -0
  537. package/src/runtime/facades/archive-facade.test.ts +308 -0
  538. package/src/runtime/facades/archive-facade.ts +14 -0
  539. package/src/runtime/facades/brain-facade.test.ts +818 -0
  540. package/src/runtime/facades/brain-facade.ts +2 -0
  541. package/src/runtime/facades/branching-facade.test.ts +43 -0
  542. package/src/runtime/facades/branching-facade.ts +11 -0
  543. package/src/runtime/facades/chat-facade.test.ts +219 -0
  544. package/src/runtime/facades/chat-facade.ts +15 -906
  545. package/src/runtime/facades/chat-service-ops.test.ts +381 -0
  546. package/src/runtime/facades/chat-service-ops.ts +376 -0
  547. package/src/runtime/facades/chat-session-ops.test.ts +212 -0
  548. package/src/runtime/facades/chat-session-ops.ts +146 -0
  549. package/src/runtime/facades/chat-state.ts +60 -0
  550. package/src/runtime/facades/chat-transport-ops.test.ts +336 -0
  551. package/src/runtime/facades/chat-transport-ops.ts +379 -0
  552. package/src/runtime/facades/context-facade.test.ts +123 -0
  553. package/src/runtime/facades/control-facade.test.ts +436 -0
  554. package/src/runtime/facades/control-facade.ts +6 -1
  555. package/src/runtime/facades/curator-facade.test.ts +303 -0
  556. package/src/runtime/facades/index.ts +48 -0
  557. package/src/runtime/facades/intake-facade.test.ts +215 -0
  558. package/src/runtime/facades/intake-facade.ts +14 -0
  559. package/src/runtime/facades/links-facade.test.ts +203 -0
  560. package/src/runtime/facades/links-facade.ts +13 -0
  561. package/src/runtime/facades/loop-facade.test.ts +262 -0
  562. package/src/runtime/facades/memory-facade.test.ts +283 -0
  563. package/src/runtime/facades/memory-facade.ts +78 -6
  564. package/src/runtime/facades/operator-facade.test.ts +221 -0
  565. package/src/runtime/facades/operator-facade.ts +244 -0
  566. package/src/runtime/facades/orchestrate-facade.test.ts +191 -0
  567. package/src/runtime/facades/orchestrate-facade.ts +3 -3
  568. package/src/runtime/facades/plan-facade.test.ts +283 -0
  569. package/src/runtime/facades/plan-facade.ts +47 -6
  570. package/src/runtime/facades/review-facade.test.ts +82 -0
  571. package/src/runtime/facades/review-facade.ts +11 -0
  572. package/src/runtime/facades/sync-facade.test.ts +113 -0
  573. package/src/runtime/facades/sync-facade.ts +11 -0
  574. package/src/runtime/facades/tier-facade.test.ts +47 -0
  575. package/src/runtime/facades/tier-facade.ts +11 -0
  576. package/src/runtime/facades/vault-facade.test.ts +563 -0
  577. package/src/runtime/facades/vault-facade.ts +66 -265
  578. package/src/runtime/feature-flags.test.ts +140 -0
  579. package/src/runtime/github-integration.test.ts +89 -0
  580. package/src/runtime/github-integration.ts +162 -0
  581. package/src/runtime/grading-ops.test.ts +172 -0
  582. package/src/runtime/grading-ops.ts +1 -1
  583. package/src/runtime/intake-ops.test.ts +261 -0
  584. package/src/runtime/loop-ops.test.ts +248 -0
  585. package/src/runtime/memory-cross-project-ops.test.ts +188 -0
  586. package/src/runtime/memory-extra-ops.test.ts +453 -0
  587. package/src/runtime/memory-extra-ops.ts +6 -2
  588. package/src/runtime/orchestrate-ops.test.ts +302 -0
  589. package/src/runtime/orchestrate-ops.ts +461 -45
  590. package/src/runtime/pack-ops.test.ts +175 -0
  591. package/src/runtime/planning-extra-ops.test.ts +593 -0
  592. package/src/runtime/planning-extra-ops.ts +74 -4
  593. package/src/{__tests__ → runtime}/playbook-ops-execution.test.ts +3 -3
  594. package/src/runtime/playbook-ops.test.ts +285 -0
  595. package/src/runtime/plugin-ops.test.ts +259 -0
  596. package/src/runtime/project-ops.test.ts +255 -0
  597. package/src/runtime/review-ops.test.ts +142 -0
  598. package/src/runtime/review-ops.ts +99 -0
  599. package/src/runtime/runtime.test.ts +363 -0
  600. package/src/runtime/runtime.ts +41 -12
  601. package/src/runtime/session-briefing.test.ts +431 -0
  602. package/src/runtime/session-briefing.ts +86 -1
  603. package/src/runtime/sync-ops.test.ts +212 -0
  604. package/src/runtime/sync-ops.ts +325 -0
  605. package/src/runtime/telemetry-ops.test.ts +157 -0
  606. package/src/runtime/tier-ops.test.ts +159 -0
  607. package/src/runtime/tier-ops.ts +119 -0
  608. package/src/runtime/types.ts +10 -4
  609. package/src/runtime/vault-extra-ops.test.ts +270 -0
  610. package/src/runtime/vault-extra-ops.ts +5 -332
  611. package/src/runtime/vault-linking-ops.test.ts +237 -0
  612. package/src/runtime/vault-sharing-ops.test.ts +127 -0
  613. package/src/runtime/vault-sharing-ops.ts +5 -329
  614. package/src/skills/sync-skills.ts +98 -0
  615. package/src/streams/normalize.test.ts +95 -0
  616. package/src/streams/replayable-stream.test.ts +166 -0
  617. package/src/telemetry/telemetry.test.ts +143 -0
  618. package/src/transport/http-server.test.ts +394 -0
  619. package/src/transport/lsp-server.test.ts +458 -0
  620. package/src/transport/rate-limiter.test.ts +126 -0
  621. package/src/transport/session-manager.test.ts +133 -0
  622. package/src/transport/token-auth.test.ts +136 -0
  623. package/src/transport/ws-server.test.ts +297 -0
  624. package/src/update-check.ts +111 -0
  625. package/src/vault/__tests__/vault-characterization.test.ts +579 -0
  626. package/src/vault/content-hash.test.ts +78 -0
  627. package/src/vault/git-vault-sync.test.ts +234 -0
  628. package/src/vault/knowledge-review.test.ts +269 -0
  629. package/src/vault/linking.test.ts +391 -0
  630. package/src/vault/linking.ts +188 -181
  631. package/src/vault/obsidian-sync.test.ts +345 -0
  632. package/src/vault/playbook.test.ts +152 -0
  633. package/src/vault/scope-detector.test.ts +185 -0
  634. package/src/vault/vault-branching.test.ts +252 -0
  635. package/src/{__tests__ → vault}/vault-connect.test.ts +1 -1
  636. package/src/vault/vault-entries.ts +426 -0
  637. package/src/vault/vault-maintenance.ts +200 -0
  638. package/src/vault/vault-manager.test.ts +206 -0
  639. package/src/vault/vault-markdown-sync.test.ts +203 -0
  640. package/src/vault/vault-markdown-sync.ts +163 -0
  641. package/src/vault/vault-memories.ts +339 -0
  642. package/src/{__tests__ → vault}/vault-scaling.test.ts +1 -1
  643. package/src/vault/vault-schema.ts +238 -0
  644. package/src/{__tests__ → vault}/vault-sharing.test.ts +4 -4
  645. package/src/{__tests__ → vault}/vault.test.ts +2 -2
  646. package/src/vault/vault.ts +87 -1123
  647. package/dist/cognee/client.d.ts +0 -43
  648. package/dist/cognee/client.d.ts.map +0 -1
  649. package/dist/cognee/client.js +0 -375
  650. package/dist/cognee/client.js.map +0 -1
  651. package/dist/cognee/sync-manager.d.ts +0 -153
  652. package/dist/cognee/sync-manager.d.ts.map +0 -1
  653. package/dist/cognee/sync-manager.js +0 -390
  654. package/dist/cognee/sync-manager.js.map +0 -1
  655. package/dist/cognee/types.d.ts +0 -62
  656. package/dist/cognee/types.d.ts.map +0 -1
  657. package/dist/cognee/types.js +0 -3
  658. package/dist/cognee/types.js.map +0 -1
  659. package/dist/governance/index.d.ts +0 -3
  660. package/dist/governance/index.d.ts.map +0 -1
  661. package/dist/governance/index.js +0 -2
  662. package/dist/governance/index.js.map +0 -1
  663. package/dist/health/doctor-checks.d.ts +0 -15
  664. package/dist/health/doctor-checks.d.ts.map +0 -1
  665. package/dist/health/doctor-checks.js +0 -98
  666. package/dist/health/doctor-checks.js.map +0 -1
  667. package/dist/persistence/postgres-provider.d.ts +0 -81
  668. package/dist/persistence/postgres-provider.d.ts.map +0 -1
  669. package/dist/persistence/postgres-provider.js +0 -256
  670. package/dist/persistence/postgres-provider.js.map +0 -1
  671. package/dist/runtime/cognee-sync-ops.d.ts +0 -12
  672. package/dist/runtime/cognee-sync-ops.d.ts.map +0 -1
  673. package/dist/runtime/cognee-sync-ops.js +0 -93
  674. package/dist/runtime/cognee-sync-ops.js.map +0 -1
  675. package/dist/runtime/core-ops.d.ts +0 -23
  676. package/dist/runtime/core-ops.d.ts.map +0 -1
  677. package/dist/runtime/core-ops.js +0 -1296
  678. package/dist/runtime/core-ops.js.map +0 -1
  679. package/dist/runtime/facades/cognee-facade.d.ts +0 -8
  680. package/dist/runtime/facades/cognee-facade.d.ts.map +0 -1
  681. package/dist/runtime/facades/cognee-facade.js +0 -156
  682. package/dist/runtime/facades/cognee-facade.js.map +0 -1
  683. package/src/__tests__/admin-extra-ops.test.ts +0 -484
  684. package/src/__tests__/admin-ops.test.ts +0 -268
  685. package/src/__tests__/admin-setup-ops.test.ts +0 -355
  686. package/src/__tests__/agency-manager.test.ts +0 -374
  687. package/src/__tests__/agent-loop.test.ts +0 -256
  688. package/src/__tests__/capture-ops.test.ts +0 -784
  689. package/src/__tests__/claudemd.test.ts +0 -282
  690. package/src/__tests__/content-hash.test.ts +0 -60
  691. package/src/__tests__/context-engine.test.ts +0 -251
  692. package/src/__tests__/core-ops.test.ts +0 -550
  693. package/src/__tests__/curator-extra-ops.test.ts +0 -383
  694. package/src/__tests__/deprecation.test.ts +0 -78
  695. package/src/__tests__/domain-ops.test.ts +0 -226
  696. package/src/__tests__/domain-packs.test.ts +0 -421
  697. package/src/__tests__/enforcement.test.ts +0 -153
  698. package/src/__tests__/errors.test.ts +0 -388
  699. package/src/__tests__/extensions.test.ts +0 -233
  700. package/src/__tests__/facade-factory.test.ts +0 -271
  701. package/src/__tests__/feature-flags.test.ts +0 -137
  702. package/src/__tests__/flows.test.ts +0 -604
  703. package/src/__tests__/git-vault-sync.test.ts +0 -230
  704. package/src/__tests__/governance.test.ts +0 -522
  705. package/src/__tests__/grading-ops.test.ts +0 -361
  706. package/src/__tests__/identity-manager.test.ts +0 -243
  707. package/src/__tests__/intake-pipeline.test.ts +0 -162
  708. package/src/__tests__/intent-router.test.ts +0 -222
  709. package/src/__tests__/knowledge-review.test.ts +0 -104
  710. package/src/__tests__/llm-client.test.ts +0 -69
  711. package/src/__tests__/llm.test.ts +0 -556
  712. package/src/__tests__/loader.test.ts +0 -176
  713. package/src/__tests__/loop-ops.test.ts +0 -469
  714. package/src/__tests__/lsp-transport.test.ts +0 -442
  715. package/src/__tests__/memory-cross-project-ops.test.ts +0 -248
  716. package/src/__tests__/memory-extra-ops.test.ts +0 -352
  717. package/src/__tests__/migration-runner.test.ts +0 -170
  718. package/src/__tests__/module-manifest-drift.test.ts +0 -59
  719. package/src/__tests__/normalize.test.ts +0 -85
  720. package/src/__tests__/obsidian-sync.test.ts +0 -354
  721. package/src/__tests__/orchestrate-ops.test.ts +0 -289
  722. package/src/__tests__/pack-ops.test.ts +0 -146
  723. package/src/__tests__/persistence.test.ts +0 -291
  724. package/src/__tests__/planning-extra-ops.test.ts +0 -706
  725. package/src/__tests__/playbook-executor.test.ts +0 -249
  726. package/src/__tests__/playbook-registry.test.ts +0 -326
  727. package/src/__tests__/playbook-seeder.test.ts +0 -163
  728. package/src/__tests__/playbook.test.ts +0 -389
  729. package/src/__tests__/plugin-ops.test.ts +0 -411
  730. package/src/__tests__/plugin-system.test.ts +0 -509
  731. package/src/__tests__/project-ops.test.ts +0 -381
  732. package/src/__tests__/replayable-stream.test.ts +0 -177
  733. package/src/__tests__/runtime.test.ts +0 -95
  734. package/src/__tests__/scope-detector.test.ts +0 -121
  735. package/src/__tests__/template-manager.test.ts +0 -222
  736. package/src/__tests__/token-resolver.test.ts +0 -79
  737. package/src/__tests__/transport.test.ts +0 -758
  738. package/src/__tests__/vault-branching.test.ts +0 -274
  739. package/src/__tests__/vault-extra-ops.test.ts +0 -482
  740. package/src/__tests__/vault-integrity.test.ts +0 -71
  741. package/src/__tests__/vault-manager.test.ts +0 -238
  742. package/src/__tests__/ws-transport.test.ts +0 -479
  743. package/src/engine/index.ts +0 -21
  744. package/src/persona/index.ts +0 -9
@@ -1,8 +1,12 @@
1
1
  import type { PersistenceProvider } from '../persistence/types.js';
2
2
  import { SQLitePersistenceProvider } from '../persistence/sqlite-provider.js';
3
3
  import type { IntelligenceEntry } from '../intelligence/types.js';
4
- import { computeContentHash } from './content-hash.js';
5
4
  import type { LinkManager } from './linking.js';
5
+ import { initializeSchema, checkFormatVersion, VAULT_FORMAT_VERSION } from './vault-schema.js';
6
+ import * as entries from './vault-entries.js';
7
+ import * as memories from './vault-memories.js';
8
+ import * as maintenance from './vault-maintenance.js';
9
+ import type { AutoLinkConfig, EntryUpdateFields } from './vault-entries.js';
6
10
 
7
11
  export interface SearchResult {
8
12
  entry: IntelligenceEntry;
@@ -54,18 +58,13 @@ export class Vault {
54
58
  private sqliteProvider: SQLitePersistenceProvider | null;
55
59
  private linkManager: LinkManager | null = null;
56
60
  private autoLinkEnabled = true;
57
- /** Minimum number of FTS5 suggestions to auto-link. Top N are linked. */
58
61
  private autoLinkMaxLinks = 3;
59
62
 
60
- /**
61
- * Create a Vault with a PersistenceProvider or a SQLite path (backward compat).
62
- */
63
63
  constructor(providerOrPath: PersistenceProvider | string = ':memory:') {
64
64
  if (typeof providerOrPath === 'string') {
65
65
  const sqlite = new SQLitePersistenceProvider(providerOrPath);
66
66
  this.provider = sqlite;
67
67
  this.sqliteProvider = sqlite;
68
- // SQLite-specific pragmas
69
68
  this.provider.run('PRAGMA journal_mode = WAL');
70
69
  this.provider.run('PRAGMA foreign_keys = ON');
71
70
  this.provider.run('PRAGMA synchronous = NORMAL');
@@ -74,33 +73,18 @@ export class Vault {
74
73
  this.sqliteProvider =
75
74
  providerOrPath instanceof SQLitePersistenceProvider ? providerOrPath : null;
76
75
  }
77
- this.initialize();
78
- this.checkFormatVersion();
76
+ initializeSchema(this.provider);
77
+ checkFormatVersion(this.provider);
79
78
  }
80
79
 
81
- /**
82
- * Vault format version — tracks schema changes for migration safety.
83
- * Increment when schema changes in a way that requires migration.
84
- *
85
- * History:
86
- * 1 — Initial schema (v8.0.0): entries, memories, brain_feedback, FTS5
87
- */
88
- static readonly FORMAT_VERSION = 1;
80
+ static readonly FORMAT_VERSION = VAULT_FORMAT_VERSION;
89
81
 
90
- private checkFormatVersion(): void {
91
- const row = this.provider.get<{ user_version: number }>('PRAGMA user_version');
92
- const current = row?.user_version ?? 0;
93
-
94
- if (current === 0) {
95
- // Fresh database — stamp with current version
96
- this.provider.run(`PRAGMA user_version = ${Vault.FORMAT_VERSION}`);
97
- } else if (current > Vault.FORMAT_VERSION) {
98
- throw new Error(
99
- `Vault format version ${current} is newer than engine supports (${Vault.FORMAT_VERSION}). ` +
100
- `Upgrade @soleri/core to a compatible version.`,
101
- );
102
- }
103
- // current < FORMAT_VERSION → future: run migration scripts here
82
+ private getAutoLinkConfig(): AutoLinkConfig {
83
+ return {
84
+ linkManager: this.linkManager,
85
+ enabled: this.autoLinkEnabled,
86
+ maxLinks: this.autoLinkMaxLinks,
87
+ };
104
88
  }
105
89
 
106
90
  setLinkManager(mgr: LinkManager, opts?: { enabled?: boolean; maxLinks?: number }): void {
@@ -109,360 +93,27 @@ export class Vault {
109
93
  if (opts?.maxLinks !== undefined) this.autoLinkMaxLinks = opts.maxLinks;
110
94
  }
111
95
 
112
- /**
113
- * Auto-link a newly added entry using FTS5 suggestions.
114
- * Called after seed() for each entry. Creates links for top N suggestions.
115
- */
116
- private autoLink(entryId: string): void {
117
- if (!this.linkManager || !this.autoLinkEnabled) return;
118
- try {
119
- const suggestions = this.linkManager.suggestLinks(entryId, this.autoLinkMaxLinks);
120
- for (const s of suggestions) {
121
- this.linkManager.addLink(entryId, s.entryId, s.suggestedType, `auto: ${s.reason}`);
122
- }
123
- } catch {
124
- // Auto-linking is best-effort — never block ingestion
125
- }
96
+ isAutoLinkEnabled(): boolean {
97
+ return this.autoLinkEnabled && this.linkManager !== null;
126
98
  }
127
99
 
128
- /** Backward-compatible factory. */
129
100
  static createWithSQLite(dbPath: string = ':memory:'): Vault {
130
101
  return new Vault(dbPath);
131
102
  }
132
103
 
133
- private initialize(): void {
134
- this.provider.execSql(`
135
- CREATE TABLE IF NOT EXISTS entries (
136
- id TEXT PRIMARY KEY,
137
- type TEXT NOT NULL CHECK(type IN ('pattern', 'anti-pattern', 'rule', 'playbook')),
138
- domain TEXT NOT NULL,
139
- title TEXT NOT NULL,
140
- severity TEXT NOT NULL CHECK(severity IN ('critical', 'warning', 'suggestion')),
141
- description TEXT NOT NULL,
142
- context TEXT, example TEXT, counter_example TEXT, why TEXT,
143
- tags TEXT NOT NULL DEFAULT '[]',
144
- applies_to TEXT DEFAULT '[]',
145
- created_at INTEGER NOT NULL DEFAULT (unixepoch()),
146
- updated_at INTEGER NOT NULL DEFAULT (unixepoch())
147
- );
148
- CREATE VIRTUAL TABLE IF NOT EXISTS entries_fts USING fts5(
149
- id, title, description, context, tags,
150
- content='entries', content_rowid='rowid', tokenize='porter unicode61'
151
- );
152
- CREATE TRIGGER IF NOT EXISTS entries_ai AFTER INSERT ON entries BEGIN
153
- INSERT INTO entries_fts(rowid,id,title,description,context,tags) VALUES(new.rowid,new.id,new.title,new.description,new.context,new.tags);
154
- END;
155
- CREATE TRIGGER IF NOT EXISTS entries_ad AFTER DELETE ON entries BEGIN
156
- INSERT INTO entries_fts(entries_fts,rowid,id,title,description,context,tags) VALUES('delete',old.rowid,old.id,old.title,old.description,old.context,old.tags);
157
- END;
158
- CREATE TRIGGER IF NOT EXISTS entries_au AFTER UPDATE ON entries BEGIN
159
- INSERT INTO entries_fts(entries_fts,rowid,id,title,description,context,tags) VALUES('delete',old.rowid,old.id,old.title,old.description,old.context,old.tags);
160
- INSERT INTO entries_fts(rowid,id,title,description,context,tags) VALUES(new.rowid,new.id,new.title,new.description,new.context,new.tags);
161
- END;
162
- CREATE TABLE IF NOT EXISTS entries_archive (
163
- id TEXT PRIMARY KEY,
164
- type TEXT NOT NULL,
165
- domain TEXT NOT NULL,
166
- title TEXT NOT NULL,
167
- severity TEXT NOT NULL,
168
- description TEXT NOT NULL,
169
- context TEXT, example TEXT, counter_example TEXT, why TEXT,
170
- tags TEXT NOT NULL DEFAULT '[]',
171
- applies_to TEXT DEFAULT '[]',
172
- created_at INTEGER NOT NULL,
173
- updated_at INTEGER NOT NULL,
174
- valid_from INTEGER,
175
- valid_until INTEGER,
176
- archived_at INTEGER NOT NULL DEFAULT (unixepoch()),
177
- archive_reason TEXT
178
- );
179
- CREATE TABLE IF NOT EXISTS projects (
180
- path TEXT PRIMARY KEY,
181
- name TEXT NOT NULL,
182
- registered_at INTEGER NOT NULL DEFAULT (unixepoch()),
183
- last_seen_at INTEGER NOT NULL DEFAULT (unixepoch()),
184
- session_count INTEGER NOT NULL DEFAULT 1
185
- );
186
- CREATE TABLE IF NOT EXISTS memories (
187
- id TEXT PRIMARY KEY,
188
- project_path TEXT NOT NULL,
189
- type TEXT NOT NULL CHECK(type IN ('session', 'lesson', 'preference')),
190
- context TEXT NOT NULL,
191
- summary TEXT NOT NULL,
192
- topics TEXT NOT NULL DEFAULT '[]',
193
- files_modified TEXT NOT NULL DEFAULT '[]',
194
- tools_used TEXT NOT NULL DEFAULT '[]',
195
- created_at INTEGER NOT NULL DEFAULT (unixepoch()),
196
- archived_at INTEGER
197
- );
198
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
199
- id, context, summary, topics,
200
- content='memories', content_rowid='rowid', tokenize='porter unicode61'
201
- );`);
202
-
203
- // Add new columns if they don't exist (backward-compatible migration)
204
- const memCols = this.provider
205
- .all<{ name: string }>(`PRAGMA table_info(memories)`)
206
- .map((r) => r.name);
207
- if (!memCols.includes('intent')) {
208
- this.provider.execSql(`
209
- ALTER TABLE memories ADD COLUMN intent TEXT;
210
- ALTER TABLE memories ADD COLUMN decisions TEXT NOT NULL DEFAULT '[]';
211
- ALTER TABLE memories ADD COLUMN current_state TEXT;
212
- ALTER TABLE memories ADD COLUMN next_steps TEXT NOT NULL DEFAULT '[]';
213
- ALTER TABLE memories ADD COLUMN vault_entries_referenced TEXT NOT NULL DEFAULT '[]';
214
- `);
215
- }
216
-
217
- this.provider.execSql(`
218
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
219
- INSERT INTO memories_fts(rowid,id,context,summary,topics) VALUES(new.rowid,new.id,new.context,new.summary,new.topics);
220
- END;
221
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
222
- INSERT INTO memories_fts(memories_fts,rowid,id,context,summary,topics) VALUES('delete',old.rowid,old.id,old.context,old.summary,old.topics);
223
- END;
224
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
225
- INSERT INTO memories_fts(memories_fts,rowid,id,context,summary,topics) VALUES('delete',old.rowid,old.id,old.context,old.summary,old.topics);
226
- INSERT INTO memories_fts(rowid,id,context,summary,topics) VALUES(new.rowid,new.id,new.context,new.summary,new.topics);
227
- END;
228
- CREATE TABLE IF NOT EXISTS brain_vocabulary (
229
- term TEXT PRIMARY KEY,
230
- idf REAL NOT NULL,
231
- doc_count INTEGER NOT NULL DEFAULT 1,
232
- updated_at INTEGER NOT NULL DEFAULT (unixepoch())
233
- );
234
- CREATE TABLE IF NOT EXISTS brain_feedback (
235
- id INTEGER PRIMARY KEY AUTOINCREMENT,
236
- query TEXT NOT NULL,
237
- entry_id TEXT NOT NULL,
238
- action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed', 'modified', 'failed')),
239
- source TEXT NOT NULL DEFAULT 'search',
240
- confidence REAL NOT NULL DEFAULT 0.6,
241
- duration INTEGER,
242
- context TEXT NOT NULL DEFAULT '{}',
243
- reason TEXT,
244
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
245
- );
246
- CREATE INDEX IF NOT EXISTS idx_brain_feedback_query ON brain_feedback(query);
247
- CREATE INDEX IF NOT EXISTS idx_entries_domain ON entries(domain);
248
- CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(type);
249
- CREATE INDEX IF NOT EXISTS idx_entries_severity ON entries(severity);
250
- CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project_path);
251
- CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
252
- `);
253
- this.migrateBrainSchema();
254
- this.migrateTemporalSchema();
255
- this.migrateOriginColumn();
256
- this.migrateContentHash();
257
- this.migrateTierColumn();
258
- }
259
-
260
- private migrateTierColumn(): void {
261
- try {
262
- this.provider.run("ALTER TABLE entries ADD COLUMN tier TEXT DEFAULT 'agent'");
263
- } catch {
264
- // Column already exists
265
- }
266
- this.provider.execSql(
267
- 'CREATE INDEX IF NOT EXISTS idx_entries_tier ON entries(tier) WHERE tier IS NOT NULL',
268
- );
269
- }
270
-
271
- private migrateTemporalSchema(): void {
272
- try {
273
- this.provider.run('ALTER TABLE entries ADD COLUMN valid_from INTEGER');
274
- } catch {
275
- // Column already exists
276
- }
277
- try {
278
- this.provider.run('ALTER TABLE entries ADD COLUMN valid_until INTEGER');
279
- } catch {
280
- // Column already exists
281
- }
282
- }
104
+ // ── Entry operations (vault-entries.ts) ───────────────────────────────
283
105
 
284
- private migrateOriginColumn(): void {
285
- try {
286
- this.provider.run(
287
- "ALTER TABLE entries ADD COLUMN origin TEXT NOT NULL DEFAULT 'user' CHECK(origin IN ('agent', 'pack', 'user'))",
288
- );
289
- } catch {
290
- // Column already exists
291
- }
292
- this.provider.execSql('CREATE INDEX IF NOT EXISTS idx_entries_origin ON entries(origin)');
106
+ seed(entryList: IntelligenceEntry[]): number {
107
+ return entries.seed(this.provider, entryList, this.getAutoLinkConfig());
293
108
  }
294
-
295
- private migrateContentHash(): void {
296
- try {
297
- this.provider.run('ALTER TABLE entries ADD COLUMN content_hash TEXT');
298
- } catch {
299
- // Column already exists
300
- }
301
- this.provider.execSql(
302
- 'CREATE INDEX IF NOT EXISTS idx_entries_content_hash ON entries(content_hash) WHERE content_hash IS NOT NULL',
303
- );
304
- // Backfill existing entries that lack a hash
305
- const unhashed = this.provider.all<{
306
- id: string;
307
- type: string;
308
- domain: string;
309
- title: string;
310
- description: string;
311
- tags: string;
312
- example: string | null;
313
- counter_example: string | null;
314
- }>(
315
- 'SELECT id, type, domain, title, description, tags, example, counter_example FROM entries WHERE content_hash IS NULL',
316
- );
317
- if (unhashed.length > 0) {
318
- this.provider.transaction(() => {
319
- for (const row of unhashed) {
320
- const hash = computeContentHash({
321
- type: row.type,
322
- domain: row.domain,
323
- title: row.title,
324
- description: row.description,
325
- tags: JSON.parse(row.tags),
326
- example: row.example ?? undefined,
327
- counterExample: row.counter_example ?? undefined,
328
- });
329
- this.provider.run('UPDATE entries SET content_hash = @hash WHERE id = @id', {
330
- hash,
331
- id: row.id,
332
- });
333
- }
334
- });
335
- }
109
+ installPack(entryList: IntelligenceEntry[]): { installed: number; skipped: number } {
110
+ return entries.installPack(this.provider, entryList, this.getAutoLinkConfig());
336
111
  }
337
-
338
- private migrateBrainSchema(): void {
339
- const columns = this.provider.all<{ name: string }>('PRAGMA table_info(brain_feedback)');
340
- const hasSource = columns.some((c) => c.name === 'source');
341
-
342
- if (!hasSource && columns.length > 0) {
343
- this.provider.transaction(() => {
344
- this.provider.run(`
345
- CREATE TABLE brain_feedback_new (
346
- id INTEGER PRIMARY KEY AUTOINCREMENT,
347
- query TEXT NOT NULL,
348
- entry_id TEXT NOT NULL,
349
- action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed', 'modified', 'failed')),
350
- source TEXT NOT NULL DEFAULT 'search',
351
- confidence REAL NOT NULL DEFAULT 0.6,
352
- duration INTEGER,
353
- context TEXT NOT NULL DEFAULT '{}',
354
- reason TEXT,
355
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
356
- )
357
- `);
358
- this.provider.run(`
359
- INSERT INTO brain_feedback_new (id, query, entry_id, action, created_at)
360
- SELECT id, query, entry_id, action, created_at FROM brain_feedback
361
- `);
362
- this.provider.run('DROP TABLE brain_feedback');
363
- this.provider.run('ALTER TABLE brain_feedback_new RENAME TO brain_feedback');
364
- this.provider.run(
365
- 'CREATE INDEX IF NOT EXISTS idx_brain_feedback_query ON brain_feedback(query)',
366
- );
367
- });
368
- }
369
-
370
- try {
371
- const sessionCols = this.provider.all<{ name: string }>('PRAGMA table_info(brain_sessions)');
372
- if (sessionCols.length > 0 && !sessionCols.some((c) => c.name === 'extracted_at')) {
373
- this.provider.run('ALTER TABLE brain_sessions ADD COLUMN extracted_at TEXT');
374
- }
375
- } catch {
376
- // brain_sessions table doesn't exist yet — BrainIntelligence will create it
377
- }
378
- }
379
-
380
- seed(entries: IntelligenceEntry[]): number {
381
- const sql = `
382
- INSERT INTO entries (id,type,domain,title,severity,description,context,example,counter_example,why,tags,applies_to,valid_from,valid_until,content_hash,tier,origin)
383
- VALUES (@id,@type,@domain,@title,@severity,@description,@context,@example,@counterExample,@why,@tags,@appliesTo,@validFrom,@validUntil,@contentHash,@tier,@origin)
384
- ON CONFLICT(id) DO UPDATE SET type=excluded.type,domain=excluded.domain,title=excluded.title,severity=excluded.severity,
385
- description=excluded.description,context=excluded.context,example=excluded.example,counter_example=excluded.counter_example,
386
- why=excluded.why,tags=excluded.tags,applies_to=excluded.applies_to,valid_from=excluded.valid_from,valid_until=excluded.valid_until,
387
- content_hash=excluded.content_hash,tier=excluded.tier,origin=excluded.origin,updated_at=unixepoch()
388
- `;
389
- return this.provider.transaction(() => {
390
- let count = 0;
391
- for (const entry of entries) {
392
- this.provider.run(sql, {
393
- id: entry.id,
394
- type: entry.type,
395
- domain: entry.domain,
396
- title: entry.title,
397
- severity: entry.severity,
398
- description: entry.description,
399
- context: entry.context ?? null,
400
- example: entry.example ?? null,
401
- counterExample: entry.counterExample ?? null,
402
- why: entry.why ?? null,
403
- tags: JSON.stringify(entry.tags),
404
- appliesTo: JSON.stringify(entry.appliesTo ?? []),
405
- validFrom: entry.validFrom ?? null,
406
- validUntil: entry.validUntil ?? null,
407
- contentHash: computeContentHash(entry),
408
- tier: entry.tier ?? 'agent',
409
- origin: entry.origin ?? 'agent',
410
- });
411
- count++;
412
- }
413
- // Auto-link after all entries are inserted (so they can link to each other).
414
- // Skip for large batches (>100) — use relink_vault for bulk imports.
415
- if (entries.length <= 100) {
416
- for (const entry of entries) {
417
- this.autoLink(entry.id);
418
- }
419
- }
420
- return count;
421
- });
422
- }
423
-
424
- /**
425
- * Install a knowledge pack — seeds entries with origin:'pack' and content-hash dedup.
426
- * Packs are installable domain knowledge (UX laws, design tokens, clean code rules).
427
- * Unlike seed(), this forces origin:'pack' regardless of what the entry says.
428
- */
429
- installPack(entries: IntelligenceEntry[]): { installed: number; skipped: number } {
430
- let installed = 0;
431
- let skipped = 0;
432
- // Tag all entries with origin:'pack' and seed — seed() handles its own transaction
433
- const tagged = entries.map((e) => ({ ...e, origin: 'pack' as const }));
434
- const results = this.seedDedup(tagged);
435
- for (const r of results) {
436
- if (r.action === 'inserted') installed++;
437
- else skipped++;
438
- }
439
- return { installed, skipped };
440
- }
441
-
442
- /**
443
- * Seed entries with content-hash dedup. Returns per-entry results.
444
- * Unlike seed(), skips entries whose content already exists in the vault.
445
- */
446
112
  seedDedup(
447
- entries: IntelligenceEntry[],
113
+ entryList: IntelligenceEntry[],
448
114
  ): Array<{ id: string; action: 'inserted' | 'duplicate'; existingId?: string }> {
449
- return this.provider.transaction(() => {
450
- const results: Array<{ id: string; action: 'inserted' | 'duplicate'; existingId?: string }> =
451
- [];
452
- for (const entry of entries) {
453
- const hash = computeContentHash(entry);
454
- const existing = this.findByContentHash(hash);
455
- if (existing && existing !== entry.id) {
456
- results.push({ id: entry.id, action: 'duplicate', existingId: existing });
457
- } else {
458
- this.seed([entry]);
459
- results.push({ id: entry.id, action: 'inserted' });
460
- }
461
- }
462
- return results;
463
- });
115
+ return entries.seedDedup(this.provider, entryList, this.getAutoLinkConfig());
464
116
  }
465
-
466
117
  search(
467
118
  query: string,
468
119
  options?: {
@@ -474,66 +125,11 @@ export class Vault {
474
125
  includeExpired?: boolean;
475
126
  },
476
127
  ): SearchResult[] {
477
- const limit = options?.limit ?? 10;
478
- const filters: string[] = [];
479
- const fp: Record<string, unknown> = {};
480
- if (options?.domain) {
481
- filters.push('e.domain = @domain');
482
- fp.domain = options.domain;
483
- }
484
- if (options?.type) {
485
- filters.push('e.type = @type');
486
- fp.type = options.type;
487
- }
488
- if (options?.severity) {
489
- filters.push('e.severity = @severity');
490
- fp.severity = options.severity;
491
- }
492
- if (options?.origin) {
493
- filters.push('e.origin = @origin');
494
- fp.origin = options.origin;
495
- }
496
- if (!options?.includeExpired) {
497
- const now = Math.floor(Date.now() / 1000);
498
- filters.push('(e.valid_until IS NULL OR e.valid_until > @now)');
499
- filters.push('(e.valid_from IS NULL OR e.valid_from <= @now)');
500
- fp.now = now;
501
- }
502
- const wc = filters.length > 0 ? `AND ${filters.join(' AND ')}` : '';
503
-
504
- // Build FTS5 query: use OR between terms for broader matching,
505
- // with title column boosted 3x for relevance ranking.
506
- // FTS5 BM25 with default AND degrades with more entries because
507
- // fewer documents match ALL terms simultaneously.
508
- const ftsQuery = buildFtsQuery(query);
509
-
510
- try {
511
- const rows = this.provider.all<Record<string, unknown>>(
512
- `SELECT e.*, bm25(entries_fts, 5.0, 10.0, 3.0, 1.0, 2.0) as score FROM entries_fts fts JOIN entries e ON e.rowid = fts.rowid WHERE entries_fts MATCH @query ${wc} ORDER BY score ASC LIMIT @limit`,
513
- { query: ftsQuery, limit, ...fp },
514
- );
515
- return rows.map(rowToSearchResult);
516
- } catch {
517
- // Fallback: try original query if FTS5 syntax fails
518
- try {
519
- const rows = this.provider.all<Record<string, unknown>>(
520
- `SELECT e.*, -rank as score FROM entries_fts fts JOIN entries e ON e.rowid = fts.rowid WHERE entries_fts MATCH @query ${wc} ORDER BY score DESC LIMIT @limit`,
521
- { query, limit, ...fp },
522
- );
523
- return rows.map(rowToSearchResult);
524
- } catch {
525
- return [];
526
- }
527
- }
128
+ return entries.search(this.provider, query, options);
528
129
  }
529
-
530
130
  get(id: string): IntelligenceEntry | null {
531
- const row = this.provider.get<Record<string, unknown>>('SELECT * FROM entries WHERE id = ?', [
532
- id,
533
- ]);
534
- return row ? rowToEntry(row) : null;
131
+ return entries.get(this.provider, id);
535
132
  }
536
-
537
133
  list(options?: {
538
134
  domain?: string;
539
135
  type?: string;
@@ -544,789 +140,157 @@ export class Vault {
544
140
  offset?: number;
545
141
  includeExpired?: boolean;
546
142
  }): IntelligenceEntry[] {
547
- const filters: string[] = [];
548
- const params: Record<string, unknown> = {};
549
- if (options?.domain) {
550
- filters.push('domain = @domain');
551
- params.domain = options.domain;
552
- }
553
- if (options?.type) {
554
- filters.push('type = @type');
555
- params.type = options.type;
556
- }
557
- if (options?.severity) {
558
- filters.push('severity = @severity');
559
- params.severity = options.severity;
560
- }
561
- if (options?.origin) {
562
- filters.push('origin = @origin');
563
- params.origin = options.origin;
564
- }
565
- if (options?.tags?.length) {
566
- const c = options.tags.map((t, i) => {
567
- params[`tag${i}`] = `%"${t}"%`;
568
- return `tags LIKE @tag${i}`;
569
- });
570
- filters.push(`(${c.join(' OR ')})`);
571
- }
572
- if (!options?.includeExpired) {
573
- const now = Math.floor(Date.now() / 1000);
574
- filters.push('(valid_until IS NULL OR valid_until > @now)');
575
- filters.push('(valid_from IS NULL OR valid_from <= @now)');
576
- params.now = now;
577
- }
578
- const wc = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
579
- const rows = this.provider.all<Record<string, unknown>>(
580
- `SELECT * FROM entries ${wc} ORDER BY severity, domain, title LIMIT @limit OFFSET @offset`,
581
- { ...params, limit: options?.limit ?? 50, offset: options?.offset ?? 0 },
582
- );
583
- return rows.map(rowToEntry);
143
+ return entries.list(this.provider, options);
584
144
  }
585
-
586
145
  stats(): VaultStats {
587
- const total = this.provider.get<{ count: number }>(
588
- 'SELECT COUNT(*) as count FROM entries',
589
- )!.count;
590
- return {
591
- totalEntries: total,
592
- byType: gc(this.provider, 'type'),
593
- byDomain: gc(this.provider, 'domain'),
594
- bySeverity: gc(this.provider, 'severity'),
595
- };
146
+ return entries.stats(this.provider);
596
147
  }
597
-
598
148
  add(entry: IntelligenceEntry): void {
599
- this.seed([entry]);
149
+ entries.add(this.provider, entry, this.getAutoLinkConfig());
600
150
  }
601
151
  remove(id: string): boolean {
602
- const deleted = this.provider.run('DELETE FROM entries WHERE id = ?', [id]).changes > 0;
603
- return deleted;
152
+ return entries.remove(this.provider, id);
604
153
  }
605
-
606
- update(
607
- id: string,
608
- fields: Partial<
609
- Pick<
610
- IntelligenceEntry,
611
- | 'title'
612
- | 'description'
613
- | 'context'
614
- | 'example'
615
- | 'counterExample'
616
- | 'why'
617
- | 'tags'
618
- | 'appliesTo'
619
- | 'severity'
620
- | 'type'
621
- | 'domain'
622
- | 'validFrom'
623
- | 'validUntil'
624
- >
625
- >,
626
- ): IntelligenceEntry | null {
627
- const existing = this.get(id);
628
- if (!existing) return null;
629
- const merged: IntelligenceEntry = { ...existing, ...fields };
630
- this.seed([merged]);
631
- return this.get(id);
154
+ update(id: string, fields: EntryUpdateFields): IntelligenceEntry | null {
155
+ return entries.update(this.provider, id, fields, this.getAutoLinkConfig());
632
156
  }
633
-
634
157
  setTemporal(id: string, validFrom?: number, validUntil?: number): boolean {
635
- const sets: string[] = [];
636
- const params: Record<string, unknown> = { id };
637
- if (validFrom !== undefined) {
638
- sets.push('valid_from = @validFrom');
639
- params.validFrom = validFrom;
640
- }
641
- if (validUntil !== undefined) {
642
- sets.push('valid_until = @validUntil');
643
- params.validUntil = validUntil;
644
- }
645
- if (sets.length === 0) return false;
646
- sets.push('updated_at = unixepoch()');
647
- return (
648
- this.provider.run(`UPDATE entries SET ${sets.join(', ')} WHERE id = @id`, params).changes > 0
649
- );
158
+ return entries.setTemporal(this.provider, id, validFrom, validUntil);
650
159
  }
651
-
652
160
  findExpiring(withinDays: number): IntelligenceEntry[] {
653
- const now = Math.floor(Date.now() / 1000);
654
- const cutoff = now + withinDays * 86400;
655
- const rows = this.provider.all<Record<string, unknown>>(
656
- 'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until > @now AND valid_until <= @cutoff ORDER BY valid_until ASC',
657
- { now, cutoff },
658
- );
659
- return rows.map(rowToEntry);
161
+ return entries.findExpiring(this.provider, withinDays);
660
162
  }
661
-
662
163
  findExpired(limit: number = 50): IntelligenceEntry[] {
663
- const now = Math.floor(Date.now() / 1000);
664
- const rows = this.provider.all<Record<string, unknown>>(
665
- 'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until <= @now ORDER BY valid_until DESC LIMIT @limit',
666
- { now, limit },
667
- );
668
- return rows.map(rowToEntry);
164
+ return entries.findExpired(this.provider, limit);
669
165
  }
670
-
671
166
  bulkRemove(ids: string[]): number {
672
- return this.provider.transaction(() => {
673
- let count = 0;
674
- for (const id of ids) {
675
- count += this.provider.run('DELETE FROM entries WHERE id = ?', [id]).changes;
676
- }
677
- return count;
678
- });
167
+ return entries.bulkRemove(this.provider, ids);
679
168
  }
680
-
681
169
  getTags(): Array<{ tag: string; count: number }> {
682
- const rows = this.provider.all<{ tags: string }>('SELECT tags FROM entries');
683
- const counts = new Map<string, number>();
684
- for (const row of rows) {
685
- const tags: string[] = JSON.parse(row.tags || '[]');
686
- for (const tag of tags) {
687
- counts.set(tag, (counts.get(tag) ?? 0) + 1);
688
- }
689
- }
690
- return Array.from(counts.entries())
691
- .map(([tag, count]) => ({ tag, count }))
692
- .sort((a, b) => b.count - a.count);
170
+ return entries.getTags(this.provider);
693
171
  }
694
-
695
172
  getDomains(): Array<{ domain: string; count: number }> {
696
- return this.provider.all<{ domain: string; count: number }>(
697
- 'SELECT domain, COUNT(*) as count FROM entries GROUP BY domain ORDER BY count DESC',
698
- );
173
+ return entries.getDomains(this.provider);
699
174
  }
700
-
701
175
  getRecent(limit: number = 20): IntelligenceEntry[] {
702
- const rows = this.provider.all<Record<string, unknown>>(
703
- 'SELECT * FROM entries ORDER BY updated_at DESC LIMIT ?',
704
- [limit],
705
- );
706
- return rows.map(rowToEntry);
176
+ return entries.getRecent(this.provider, limit);
177
+ }
178
+ findByContentHash(hash: string): string | null {
179
+ return entries.findByContentHash(this.provider, hash);
180
+ }
181
+ contentHashStats(): { total: number; hashed: number; uniqueHashes: number } {
182
+ return entries.contentHashStats(this.provider);
707
183
  }
708
184
 
185
+ // ── Maintenance operations (vault-maintenance.ts) ─────────────────────
186
+
709
187
  exportAll(): { entries: IntelligenceEntry[]; exportedAt: number; count: number } {
710
- const rows = this.provider.all<Record<string, unknown>>(
711
- 'SELECT * FROM entries ORDER BY domain, title',
712
- );
713
- const entries = rows.map(rowToEntry);
714
- return { entries, exportedAt: Math.floor(Date.now() / 1000), count: entries.length };
188
+ return maintenance.exportAll(this.provider);
715
189
  }
716
-
717
190
  getAgeReport(): {
718
191
  total: number;
719
192
  buckets: Array<{ label: string; count: number; minDays: number; maxDays: number }>;
720
193
  oldestTimestamp: number | null;
721
194
  newestTimestamp: number | null;
722
195
  } {
723
- const rows = this.provider.all<{ created_at: number; updated_at: number }>(
724
- 'SELECT created_at, updated_at FROM entries',
725
- );
726
- const now = Math.floor(Date.now() / 1000);
727
- const bucketDefs = [
728
- { label: 'today', minDays: 0, maxDays: 1 },
729
- { label: 'this_week', minDays: 1, maxDays: 7 },
730
- { label: 'this_month', minDays: 7, maxDays: 30 },
731
- { label: 'this_quarter', minDays: 30, maxDays: 90 },
732
- { label: 'older', minDays: 90, maxDays: Infinity },
733
- ];
734
- const counts = new Array(bucketDefs.length).fill(0) as number[];
735
- let oldest: number | null = null;
736
- let newest: number | null = null;
737
- for (const row of rows) {
738
- const ts = row.created_at;
739
- if (oldest === null || ts < oldest) oldest = ts;
740
- if (newest === null || ts > newest) newest = ts;
741
- const ageDays = (now - ts) / 86400;
742
- for (let i = 0; i < bucketDefs.length; i++) {
743
- if (ageDays >= bucketDefs[i].minDays && ageDays < bucketDefs[i].maxDays) {
744
- counts[i]++;
745
- break;
746
- }
747
- }
748
- }
749
- return {
750
- total: rows.length,
751
- buckets: bucketDefs.map((b, i) => Object.assign({}, b, { count: counts[i] })),
752
- oldestTimestamp: oldest,
753
- newestTimestamp: newest,
754
- };
196
+ return maintenance.getAgeReport(this.provider);
197
+ }
198
+ archive(options: { olderThanDays: number; reason?: string }): { archived: number } {
199
+ return maintenance.archive(this.provider, options);
200
+ }
201
+ restore(id: string): boolean {
202
+ return maintenance.restore(this.provider, id);
203
+ }
204
+ rebuildFtsIndex(): void {
205
+ maintenance.rebuildFtsIndex(this.provider);
755
206
  }
207
+ optimize(): { vacuumed: boolean; analyzed: boolean; ftsRebuilt: boolean } {
208
+ return maintenance.optimize(this.provider);
209
+ }
210
+
211
+ // ── Project operations (vault-maintenance.ts) ─────────────────────────
756
212
 
757
213
  registerProject(path: string, name?: string): ProjectInfo {
758
- const projectName = name ?? path.replace(/\/$/, '').split('/').pop() ?? path;
759
- const existing = this.getProject(path);
760
- if (existing) {
761
- this.provider.run(
762
- 'UPDATE projects SET last_seen_at = unixepoch(), session_count = session_count + 1 WHERE path = ?',
763
- [path],
764
- );
765
- return this.getProject(path)!;
766
- }
767
- this.provider.run('INSERT INTO projects (path, name) VALUES (?, ?)', [path, projectName]);
768
- return this.getProject(path)!;
214
+ return maintenance.registerProject(this.provider, path, name);
769
215
  }
770
-
771
216
  getProject(path: string): ProjectInfo | null {
772
- const row = this.provider.get<Record<string, unknown>>(
773
- 'SELECT * FROM projects WHERE path = ?',
774
- [path],
775
- );
776
- if (!row) return null;
777
- return {
778
- path: row.path as string,
779
- name: row.name as string,
780
- registeredAt: row.registered_at as number,
781
- lastSeenAt: row.last_seen_at as number,
782
- sessionCount: row.session_count as number,
783
- };
217
+ return maintenance.getProject(this.provider, path);
784
218
  }
785
-
786
219
  listProjects(): ProjectInfo[] {
787
- const rows = this.provider.all<Record<string, unknown>>(
788
- 'SELECT * FROM projects ORDER BY last_seen_at DESC',
789
- );
790
- return rows.map((row) => ({
791
- path: row.path as string,
792
- name: row.name as string,
793
- registeredAt: row.registered_at as number,
794
- lastSeenAt: row.last_seen_at as number,
795
- sessionCount: row.session_count as number,
796
- }));
220
+ return maintenance.listProjects(this.provider);
797
221
  }
798
222
 
223
+ // ── Memory operations (vault-memories.ts) ─────────────────────────────
224
+
799
225
  captureMemory(memory: Omit<Memory, 'id' | 'createdAt' | 'archivedAt'>): Memory {
800
- const id = `mem-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
801
- this.provider.run(
802
- `INSERT INTO memories (id, project_path, type, context, summary, topics, files_modified, tools_used, intent, decisions, current_state, next_steps, vault_entries_referenced)
803
- VALUES (@id, @projectPath, @type, @context, @summary, @topics, @filesModified, @toolsUsed, @intent, @decisions, @currentState, @nextSteps, @vaultEntriesReferenced)`,
804
- {
805
- id,
806
- projectPath: memory.projectPath,
807
- type: memory.type,
808
- context: memory.context,
809
- summary: memory.summary,
810
- topics: JSON.stringify(memory.topics),
811
- filesModified: JSON.stringify(memory.filesModified),
812
- toolsUsed: JSON.stringify(memory.toolsUsed),
813
- intent: memory.intent ?? null,
814
- decisions: JSON.stringify(memory.decisions ?? []),
815
- currentState: memory.currentState ?? null,
816
- nextSteps: JSON.stringify(memory.nextSteps ?? []),
817
- vaultEntriesReferenced: JSON.stringify(memory.vaultEntriesReferenced ?? []),
818
- },
819
- );
820
- return this.getMemory(id)!;
226
+ return memories.captureMemory(this.provider, memory);
227
+ }
228
+ getMemory(id: string): Memory | null {
229
+ return memories.getMemory(this.provider, id);
230
+ }
231
+ deleteMemory(id: string): boolean {
232
+ return memories.deleteMemory(this.provider, id);
821
233
  }
822
-
823
234
  searchMemories(
824
235
  query: string,
825
236
  options?: { type?: string; projectPath?: string; intent?: string; limit?: number },
826
237
  ): Memory[] {
827
- const limit = options?.limit ?? 10;
828
- const filters: string[] = ['m.archived_at IS NULL'];
829
- const fp: Record<string, unknown> = {};
830
- if (options?.type) {
831
- filters.push('m.type = @type');
832
- fp.type = options.type;
833
- }
834
- if (options?.projectPath) {
835
- filters.push('m.project_path = @projectPath');
836
- fp.projectPath = options.projectPath;
837
- }
838
- if (options?.intent) {
839
- filters.push('m.intent = @intent');
840
- fp.intent = options.intent;
841
- }
842
- const wc = filters.length > 0 ? `AND ${filters.join(' AND ')}` : '';
843
- try {
844
- const rows = this.provider.all<Record<string, unknown>>(
845
- `SELECT m.* FROM memories_fts fts JOIN memories m ON m.rowid = fts.rowid WHERE memories_fts MATCH @query ${wc} ORDER BY rank LIMIT @limit`,
846
- { query, limit, ...fp },
847
- );
848
- return rows.map(rowToMemory);
849
- } catch {
850
- return [];
851
- }
238
+ return memories.searchMemories(this.provider, query, options);
852
239
  }
853
-
854
240
  listMemories(options?: {
855
241
  type?: string;
856
242
  projectPath?: string;
857
243
  limit?: number;
858
244
  offset?: number;
859
245
  }): Memory[] {
860
- const filters: string[] = ['archived_at IS NULL'];
861
- const params: Record<string, unknown> = {};
862
- if (options?.type) {
863
- filters.push('type = @type');
864
- params.type = options.type;
865
- }
866
- if (options?.projectPath) {
867
- filters.push('project_path = @projectPath');
868
- params.projectPath = options.projectPath;
869
- }
870
- const wc = `WHERE ${filters.join(' AND ')}`;
871
- const rows = this.provider.all<Record<string, unknown>>(
872
- `SELECT * FROM memories ${wc} ORDER BY created_at DESC LIMIT @limit OFFSET @offset`,
873
- { ...params, limit: options?.limit ?? 50, offset: options?.offset ?? 0 },
874
- );
875
- return rows.map(rowToMemory);
246
+ return memories.listMemories(this.provider, options);
876
247
  }
877
-
878
248
  memoryStats(): MemoryStats {
879
- const total = this.provider.get<{ count: number }>(
880
- 'SELECT COUNT(*) as count FROM memories WHERE archived_at IS NULL',
881
- )!.count;
882
- const byTypeRows = this.provider.all<{ key: string; count: number }>(
883
- 'SELECT type as key, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY type',
884
- );
885
- const byProjectRows = this.provider.all<{ key: string; count: number }>(
886
- 'SELECT project_path as key, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY project_path',
887
- );
888
- return {
889
- total,
890
- byType: Object.fromEntries(byTypeRows.map((r) => [r.key, r.count])),
891
- byProject: Object.fromEntries(byProjectRows.map((r) => [r.key, r.count])),
892
- };
893
- }
894
-
895
- getMemory(id: string): Memory | null {
896
- const row = this.provider.get<Record<string, unknown>>('SELECT * FROM memories WHERE id = ?', [
897
- id,
898
- ]);
899
- return row ? rowToMemory(row) : null;
900
- }
901
-
902
- deleteMemory(id: string): boolean {
903
- return this.provider.run('DELETE FROM memories WHERE id = ?', [id]).changes > 0;
249
+ return memories.memoryStats(this.provider);
904
250
  }
905
-
906
251
  memoryStatsDetailed(options?: {
907
252
  projectPath?: string;
908
253
  fromDate?: number;
909
254
  toDate?: number;
910
255
  }): MemoryStats & { oldest: number | null; newest: number | null; archivedCount: number } {
911
- const filters: string[] = [];
912
- const params: Record<string, unknown> = {};
913
- if (options?.projectPath) {
914
- filters.push('project_path = @projectPath');
915
- params.projectPath = options.projectPath;
916
- }
917
- if (options?.fromDate) {
918
- filters.push('created_at >= @fromDate');
919
- params.fromDate = options.fromDate;
920
- }
921
- if (options?.toDate) {
922
- filters.push('created_at <= @toDate');
923
- params.toDate = options.toDate;
924
- }
925
- const wc = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
926
-
927
- const total = this.provider.get<{ count: number }>(
928
- `SELECT COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL`,
929
- params,
930
- )!.count;
931
-
932
- const archivedCount = this.provider.get<{ count: number }>(
933
- `SELECT COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NOT NULL`,
934
- params,
935
- )!.count;
936
-
937
- const byTypeRows = this.provider.all<{ key: string; count: number }>(
938
- `SELECT type as key, COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL GROUP BY type`,
939
- params,
940
- );
941
-
942
- const byProjectRows = this.provider.all<{ key: string; count: number }>(
943
- `SELECT project_path as key, COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL GROUP BY project_path`,
944
- params,
945
- );
946
-
947
- const dateRange = this.provider.get<{ oldest: number | null; newest: number | null }>(
948
- `SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL`,
949
- params,
950
- )!;
951
-
952
- return {
953
- total,
954
- byType: Object.fromEntries(byTypeRows.map((r) => [r.key, r.count])),
955
- byProject: Object.fromEntries(byProjectRows.map((r) => [r.key, r.count])),
956
- oldest: dateRange.oldest,
957
- newest: dateRange.newest,
958
- archivedCount,
959
- };
256
+ return memories.memoryStatsDetailed(this.provider, options);
960
257
  }
961
-
962
258
  exportMemories(options?: {
963
259
  projectPath?: string;
964
260
  type?: string;
965
261
  includeArchived?: boolean;
966
262
  }): Memory[] {
967
- const filters: string[] = [];
968
- const params: Record<string, unknown> = {};
969
- if (!options?.includeArchived) {
970
- filters.push('archived_at IS NULL');
971
- }
972
- if (options?.projectPath) {
973
- filters.push('project_path = @projectPath');
974
- params.projectPath = options.projectPath;
975
- }
976
- if (options?.type) {
977
- filters.push('type = @type');
978
- params.type = options.type;
979
- }
980
- const wc = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
981
- const rows = this.provider.all<Record<string, unknown>>(
982
- `SELECT * FROM memories ${wc} ORDER BY created_at ASC`,
983
- Object.keys(params).length > 0 ? params : undefined,
984
- );
985
- return rows.map(rowToMemory);
263
+ return memories.exportMemories(this.provider, options);
986
264
  }
987
-
988
- importMemories(memories: Memory[]): { imported: number; skipped: number } {
989
- const sql = `
990
- INSERT OR IGNORE INTO memories (id, project_path, type, context, summary, topics, files_modified, tools_used, created_at, archived_at)
991
- VALUES (@id, @projectPath, @type, @context, @summary, @topics, @filesModified, @toolsUsed, @createdAt, @archivedAt)
992
- `;
993
- let imported = 0;
994
- let skipped = 0;
995
- this.provider.transaction(() => {
996
- for (const m of memories) {
997
- const result = this.provider.run(sql, {
998
- id: m.id,
999
- projectPath: m.projectPath,
1000
- type: m.type,
1001
- context: m.context,
1002
- summary: m.summary,
1003
- topics: JSON.stringify(m.topics),
1004
- filesModified: JSON.stringify(m.filesModified),
1005
- toolsUsed: JSON.stringify(m.toolsUsed),
1006
- createdAt: m.createdAt,
1007
- archivedAt: m.archivedAt,
1008
- });
1009
- if (result.changes > 0) imported++;
1010
- else skipped++;
1011
- }
1012
- });
1013
- return { imported, skipped };
265
+ importMemories(memoryList: Memory[]): { imported: number; skipped: number } {
266
+ return memories.importMemories(this.provider, memoryList);
1014
267
  }
1015
-
1016
268
  pruneMemories(olderThanDays: number): { pruned: number } {
1017
- const cutoff = Math.floor(Date.now() / 1000) - olderThanDays * 86400;
1018
- const result = this.provider.run(
1019
- 'DELETE FROM memories WHERE created_at < ? AND archived_at IS NULL',
1020
- [cutoff],
1021
- );
1022
- return { pruned: result.changes };
269
+ return memories.pruneMemories(this.provider, olderThanDays);
1023
270
  }
1024
-
1025
271
  deduplicateMemories(): { removed: number; groups: Array<{ kept: string; removed: string[] }> } {
1026
- const dupeRows = this.provider.all<{ id1: string; id2: string }>(`
1027
- SELECT m1.id as id1, m2.id as id2
1028
- FROM memories m1
1029
- JOIN memories m2 ON m1.summary = m2.summary
1030
- AND m1.project_path = m2.project_path
1031
- AND m1.type = m2.type
1032
- AND m1.id < m2.id
1033
- AND m1.archived_at IS NULL
1034
- AND m2.archived_at IS NULL
1035
- `);
1036
-
1037
- const groupMap = new Map<string, Set<string>>();
1038
- for (const row of dupeRows) {
1039
- if (!groupMap.has(row.id1)) groupMap.set(row.id1, new Set());
1040
- groupMap.get(row.id1)!.add(row.id2);
1041
- }
1042
-
1043
- const groups: Array<{ kept: string; removed: string[] }> = [];
1044
- const toRemove = new Set<string>();
1045
- for (const [kept, removedSet] of groupMap) {
1046
- const removed = [...removedSet].filter((id) => !toRemove.has(id));
1047
- if (removed.length > 0) {
1048
- groups.push({ kept, removed });
1049
- for (const id of removed) toRemove.add(id);
1050
- }
1051
- }
1052
-
1053
- if (toRemove.size > 0) {
1054
- this.provider.transaction(() => {
1055
- for (const id of toRemove) {
1056
- this.provider.run('DELETE FROM memories WHERE id = ?', [id]);
1057
- }
1058
- });
1059
- }
1060
-
1061
- return { removed: toRemove.size, groups };
272
+ return memories.deduplicateMemories(this.provider);
1062
273
  }
1063
-
1064
274
  memoryTopics(): Array<{ topic: string; count: number }> {
1065
- const rows = this.provider.all<{ topics: string }>(
1066
- 'SELECT topics FROM memories WHERE archived_at IS NULL',
1067
- );
1068
-
1069
- const topicCounts = new Map<string, number>();
1070
- for (const row of rows) {
1071
- const topics: string[] = JSON.parse(row.topics || '[]');
1072
- for (const topic of topics) {
1073
- topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + 1);
1074
- }
1075
- }
1076
-
1077
- return [...topicCounts.entries()]
1078
- .map(([topic, count]) => ({ topic, count }))
1079
- .sort((a, b) => b.count - a.count);
275
+ return memories.memoryTopics(this.provider);
1080
276
  }
1081
-
1082
277
  memoriesByProject(): Array<{ project: string; count: number; memories: Memory[] }> {
1083
- const rows = this.provider.all<{ project: string; count: number }>(
1084
- 'SELECT project_path as project, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY project_path ORDER BY count DESC',
1085
- );
1086
-
1087
- return rows.map((row) => {
1088
- const memories = this.provider.all<Record<string, unknown>>(
1089
- 'SELECT * FROM memories WHERE project_path = ? AND archived_at IS NULL ORDER BY created_at DESC',
1090
- [row.project],
1091
- );
1092
- return {
1093
- project: row.project,
1094
- count: row.count,
1095
- memories: memories.map(rowToMemory),
1096
- };
1097
- });
1098
- }
1099
-
1100
- /**
1101
- * Rebuild the FTS5 index for the entries table.
1102
- * Useful after bulk operations or if the index gets out of sync.
1103
- */
1104
- rebuildFtsIndex(): void {
1105
- try {
1106
- this.provider.run("INSERT INTO entries_fts(entries_fts) VALUES('rebuild')");
1107
- } catch {
1108
- // Graceful degradation — FTS rebuild failed (e.g. table doesn't exist yet)
1109
- }
1110
- }
1111
-
1112
- /**
1113
- * Archive entries older than N days. Moves them to entries_archive.
1114
- */
1115
- archive(options: { olderThanDays: number; reason?: string }): { archived: number } {
1116
- const cutoff = Math.floor(Date.now() / 1000) - options.olderThanDays * 86400;
1117
- const reason = options.reason ?? `Archived: older than ${options.olderThanDays} days`;
1118
-
1119
- return this.provider.transaction(() => {
1120
- // Find candidates
1121
- const candidates = this.provider.all<{ id: string }>(
1122
- 'SELECT id FROM entries WHERE updated_at < ?',
1123
- [cutoff],
1124
- );
1125
-
1126
- if (candidates.length === 0) return { archived: 0 };
1127
-
1128
- let archived = 0;
1129
- for (const { id } of candidates) {
1130
- // Copy to archive
1131
- this.provider.run(
1132
- `INSERT OR IGNORE INTO entries_archive (id, type, domain, title, severity, description, context, example, counter_example, why, tags, applies_to, created_at, updated_at, valid_from, valid_until, archive_reason)
1133
- SELECT id, type, domain, title, severity, description, context, example, counter_example, why, tags, applies_to, created_at, updated_at, valid_from, valid_until, ?
1134
- FROM entries WHERE id = ?`,
1135
- [reason, id],
1136
- );
1137
- // Delete from active
1138
- const result = this.provider.run('DELETE FROM entries WHERE id = ?', [id]);
1139
- archived += result.changes;
1140
- }
1141
-
1142
- return { archived };
1143
- });
278
+ return memories.memoriesByProject(this.provider);
1144
279
  }
1145
280
 
1146
- /**
1147
- * Restore an archived entry back to the active table.
1148
- */
1149
- restore(id: string): boolean {
1150
- return this.provider.transaction(() => {
1151
- const archived = this.provider.get<Record<string, unknown>>(
1152
- 'SELECT * FROM entries_archive WHERE id = ?',
1153
- [id],
1154
- );
1155
- if (!archived) return false;
1156
-
1157
- this.provider.run(
1158
- `INSERT OR REPLACE INTO entries (id, type, domain, title, severity, description, context, example, counter_example, why, tags, applies_to, created_at, updated_at, valid_from, valid_until)
1159
- SELECT id, type, domain, title, severity, description, context, example, counter_example, why, tags, applies_to, created_at, updated_at, valid_from, valid_until
1160
- FROM entries_archive WHERE id = ?`,
1161
- [id],
1162
- );
1163
- this.provider.run('DELETE FROM entries_archive WHERE id = ?', [id]);
1164
- return true;
1165
- });
1166
- }
1167
-
1168
- /**
1169
- * Optimize the database: VACUUM (SQLite only), ANALYZE, and FTS rebuild.
1170
- */
1171
- optimize(): { vacuumed: boolean; analyzed: boolean; ftsRebuilt: boolean } {
1172
- let vacuumed = false;
1173
- let analyzed = false;
1174
- let ftsRebuilt = false;
1175
-
1176
- // VACUUM only for SQLite
1177
- if (this.provider.backend === 'sqlite') {
1178
- try {
1179
- this.provider.execSql('VACUUM');
1180
- vacuumed = true;
1181
- } catch {
1182
- // VACUUM may fail inside a transaction
1183
- }
1184
- }
1185
-
1186
- try {
1187
- this.provider.execSql('ANALYZE');
1188
- analyzed = true;
1189
- } catch {
1190
- // Non-critical
1191
- }
1192
-
1193
- try {
1194
- this.provider.ftsRebuild('entries');
1195
- this.provider.ftsRebuild('memories');
1196
- ftsRebuilt = true;
1197
- } catch {
1198
- // Non-critical
1199
- }
1200
-
1201
- return { vacuumed, analyzed, ftsRebuilt };
1202
- }
281
+ // ── Provider access ───────────────────────────────────────────────────
1203
282
 
1204
- /**
1205
- * Get the underlying persistence provider.
1206
- */
1207
283
  getProvider(): PersistenceProvider {
1208
284
  return this.provider;
1209
285
  }
1210
-
1211
- /**
1212
- * Get the raw better-sqlite3 Database (backward compat).
1213
- * Throws if the provider is not SQLite.
1214
- */
1215
286
  getDb(): import('better-sqlite3').Database {
1216
287
  if (process.env.NODE_ENV !== 'test' && process.env.VITEST !== 'true') {
1217
288
  console.warn('Vault.getDb() is deprecated. Use vault.getProvider() instead.');
1218
289
  }
1219
- if (this.sqliteProvider) {
1220
- return this.sqliteProvider.getDatabase();
1221
- }
290
+ if (this.sqliteProvider) return this.sqliteProvider.getDatabase();
1222
291
  throw new Error('getDb() is only available with SQLite provider');
1223
292
  }
1224
-
1225
- /** Check if an entry with this content hash already exists. Returns the existing ID or null. */
1226
- findByContentHash(hash: string): string | null {
1227
- const row = this.provider.get<{ id: string }>(
1228
- 'SELECT id FROM entries WHERE content_hash = @hash',
1229
- { hash },
1230
- );
1231
- return row?.id ?? null;
1232
- }
1233
-
1234
- /** Get content hash stats for dedup reporting. */
1235
- contentHashStats(): { total: number; hashed: number; uniqueHashes: number } {
1236
- const total = this.provider.get<{ c: number }>('SELECT COUNT(*) as c FROM entries')?.c ?? 0;
1237
- const hashed =
1238
- this.provider.get<{ c: number }>(
1239
- 'SELECT COUNT(*) as c FROM entries WHERE content_hash IS NOT NULL',
1240
- )?.c ?? 0;
1241
- const uniqueHashes =
1242
- this.provider.get<{ c: number }>(
1243
- 'SELECT COUNT(DISTINCT content_hash) as c FROM entries WHERE content_hash IS NOT NULL',
1244
- )?.c ?? 0;
1245
- return { total, hashed, uniqueHashes };
1246
- }
1247
-
1248
293
  close(): void {
1249
294
  this.provider.close();
1250
295
  }
1251
296
  }
1252
-
1253
- function gc(provider: PersistenceProvider, col: string): Record<string, number> {
1254
- const rows = provider.all<{ key: string; count: number }>(
1255
- `SELECT ${col} as key, COUNT(*) as count FROM entries GROUP BY ${col}`,
1256
- );
1257
- return Object.fromEntries(rows.map((r) => [r.key, r.count]));
1258
- }
1259
-
1260
- function rowToEntry(row: Record<string, unknown>): IntelligenceEntry {
1261
- return {
1262
- id: row.id as string,
1263
- type: row.type as IntelligenceEntry['type'],
1264
- domain: row.domain as IntelligenceEntry['domain'],
1265
- title: row.title as string,
1266
- severity: row.severity as IntelligenceEntry['severity'],
1267
- description: row.description as string,
1268
- context: (row.context as string) ?? undefined,
1269
- example: (row.example as string) ?? undefined,
1270
- counterExample: (row.counter_example as string) ?? undefined,
1271
- why: (row.why as string) ?? undefined,
1272
- tags: JSON.parse((row.tags as string) || '[]'),
1273
- appliesTo: JSON.parse((row.applies_to as string) || '[]'),
1274
- tier: (row.tier as IntelligenceEntry['tier']) ?? undefined,
1275
- origin: (row.origin as IntelligenceEntry['origin']) ?? undefined,
1276
- validFrom: (row.valid_from as number) ?? undefined,
1277
- validUntil: (row.valid_until as number) ?? undefined,
1278
- };
1279
- }
1280
-
1281
- function rowToSearchResult(row: Record<string, unknown>): SearchResult {
1282
- // bm25() returns negative scores (lower = better), normalize to positive
1283
- const rawScore = row.score as number;
1284
- const score = rawScore < 0 ? -rawScore : rawScore;
1285
- return { entry: rowToEntry(row), score };
1286
- }
1287
-
1288
- /**
1289
- * Build an FTS5 query from natural language input.
1290
- *
1291
- * Converts "React render performance memo" to:
1292
- * {title}: (react OR render OR performance OR memo) OR (react OR render OR performance OR memo)
1293
- *
1294
- * Uses OR matching (not AND) so results include partial matches.
1295
- * FTS5 BM25 ranks documents with more matching terms higher.
1296
- * Title column is boosted via bm25() weights in the SQL query.
1297
- */
1298
- function buildFtsQuery(query: string): string {
1299
- const terms = query
1300
- .toLowerCase()
1301
- .split(/\s+/)
1302
- .filter((t) => t.length >= 2)
1303
- .map((t) => t.replace(/[^a-z0-9]/g, ''))
1304
- .filter(Boolean);
1305
-
1306
- if (terms.length === 0) return query;
1307
- if (terms.length === 1) return terms[0];
1308
-
1309
- // Use OR to match any term — BM25 ranks by how many terms match
1310
- const orTerms = terms.join(' OR ');
1311
- return orTerms;
1312
- }
1313
-
1314
- function rowToMemory(row: Record<string, unknown>): Memory {
1315
- return {
1316
- id: row.id as string,
1317
- projectPath: row.project_path as string,
1318
- type: row.type as Memory['type'],
1319
- context: row.context as string,
1320
- summary: row.summary as string,
1321
- topics: JSON.parse((row.topics as string) || '[]'),
1322
- filesModified: JSON.parse((row.files_modified as string) || '[]'),
1323
- toolsUsed: JSON.parse((row.tools_used as string) || '[]'),
1324
- intent: (row.intent as string) ?? null,
1325
- decisions: JSON.parse((row.decisions as string) || '[]'),
1326
- currentState: (row.current_state as string) ?? null,
1327
- nextSteps: JSON.parse((row.next_steps as string) || '[]'),
1328
- vaultEntriesReferenced: JSON.parse((row.vault_entries_referenced as string) || '[]'),
1329
- createdAt: row.created_at as number,
1330
- archivedAt: (row.archived_at as number) ?? null,
1331
- };
1332
- }