@soleri/core 9.0.4 → 9.2.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 (701) hide show
  1. package/dist/brain/intelligence.d.ts +27 -0
  2. package/dist/brain/intelligence.d.ts.map +1 -1
  3. package/dist/brain/intelligence.js +160 -14
  4. package/dist/brain/intelligence.js.map +1 -1
  5. package/dist/brain/learning-radar.d.ts +4 -0
  6. package/dist/brain/learning-radar.d.ts.map +1 -1
  7. package/dist/brain/learning-radar.js +20 -1
  8. package/dist/brain/learning-radar.js.map +1 -1
  9. package/dist/brain/strength-scorer.d.ts +31 -0
  10. package/dist/brain/strength-scorer.d.ts.map +1 -0
  11. package/dist/brain/strength-scorer.js +264 -0
  12. package/dist/brain/strength-scorer.js.map +1 -0
  13. package/dist/chat/agent-loop.d.ts.map +1 -1
  14. package/dist/chat/agent-loop.js +2 -0
  15. package/dist/chat/agent-loop.js.map +1 -1
  16. package/dist/chat/notifications.d.ts.map +1 -1
  17. package/dist/chat/notifications.js +2 -0
  18. package/dist/chat/notifications.js.map +1 -1
  19. package/dist/claudemd/compose.js +1 -1
  20. package/dist/claudemd/compose.js.map +1 -1
  21. package/dist/control/intent-router.d.ts.map +1 -1
  22. package/dist/control/intent-router.js +12 -4
  23. package/dist/control/intent-router.js.map +1 -1
  24. package/dist/curator/contradiction-detector.d.ts +27 -0
  25. package/dist/curator/contradiction-detector.d.ts.map +1 -0
  26. package/dist/curator/contradiction-detector.js +62 -0
  27. package/dist/curator/contradiction-detector.js.map +1 -0
  28. package/dist/curator/curator.d.ts +3 -4
  29. package/dist/curator/curator.d.ts.map +1 -1
  30. package/dist/curator/curator.js +90 -525
  31. package/dist/curator/curator.js.map +1 -1
  32. package/dist/curator/duplicate-detector.d.ts +14 -0
  33. package/dist/curator/duplicate-detector.d.ts.map +1 -0
  34. package/dist/curator/duplicate-detector.js +77 -0
  35. package/dist/curator/duplicate-detector.js.map +1 -0
  36. package/dist/curator/health-audit.d.ts +15 -0
  37. package/dist/curator/health-audit.d.ts.map +1 -0
  38. package/dist/curator/health-audit.js +97 -0
  39. package/dist/curator/health-audit.js.map +1 -0
  40. package/dist/curator/metadata-enricher.d.ts +17 -0
  41. package/dist/curator/metadata-enricher.d.ts.map +1 -0
  42. package/dist/curator/metadata-enricher.js +60 -0
  43. package/dist/curator/metadata-enricher.js.map +1 -0
  44. package/dist/curator/schema.d.ts +7 -0
  45. package/dist/curator/schema.d.ts.map +1 -0
  46. package/dist/curator/schema.js +62 -0
  47. package/dist/curator/schema.js.map +1 -0
  48. package/dist/curator/tag-manager.d.ts +36 -0
  49. package/dist/curator/tag-manager.d.ts.map +1 -0
  50. package/dist/curator/tag-manager.js +78 -0
  51. package/dist/curator/tag-manager.js.map +1 -0
  52. package/dist/engine/bin/soleri-engine.js +24 -3
  53. package/dist/engine/bin/soleri-engine.js.map +1 -1
  54. package/dist/engine/core-ops.d.ts.map +1 -1
  55. package/dist/engine/core-ops.js +23 -8
  56. package/dist/engine/core-ops.js.map +1 -1
  57. package/dist/engine/module-manifest.d.ts.map +1 -1
  58. package/dist/engine/module-manifest.js +22 -2
  59. package/dist/engine/module-manifest.js.map +1 -1
  60. package/dist/engine/register-engine.d.ts.map +1 -1
  61. package/dist/engine/register-engine.js +26 -2
  62. package/dist/engine/register-engine.js.map +1 -1
  63. package/dist/errors/retry.d.ts.map +1 -1
  64. package/dist/errors/retry.js +2 -0
  65. package/dist/errors/retry.js.map +1 -1
  66. package/dist/facades/types.d.ts +1 -1
  67. package/dist/flows/chain-types.d.ts +18 -18
  68. package/dist/flows/gate-evaluator.d.ts.map +1 -1
  69. package/dist/flows/gate-evaluator.js +22 -0
  70. package/dist/flows/gate-evaluator.js.map +1 -1
  71. package/dist/flows/types.d.ts +157 -28
  72. package/dist/flows/types.d.ts.map +1 -1
  73. package/dist/flows/types.js +4 -0
  74. package/dist/flows/types.js.map +1 -1
  75. package/dist/index.d.ts +10 -2
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +9 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/intake/intake-pipeline.d.ts.map +1 -1
  80. package/dist/intake/intake-pipeline.js +1 -0
  81. package/dist/intake/intake-pipeline.js.map +1 -1
  82. package/dist/intake/text-ingester.d.ts.map +1 -1
  83. package/dist/intake/text-ingester.js +2 -0
  84. package/dist/intake/text-ingester.js.map +1 -1
  85. package/dist/llm/key-pool.d.ts +1 -1
  86. package/dist/llm/key-pool.d.ts.map +1 -1
  87. package/dist/llm/key-pool.js +3 -4
  88. package/dist/llm/key-pool.js.map +1 -1
  89. package/dist/llm/utils.d.ts.map +1 -1
  90. package/dist/llm/utils.js +2 -0
  91. package/dist/llm/utils.js.map +1 -1
  92. package/dist/migrations/migration-runner.test-helpers.d.ts +13 -0
  93. package/dist/migrations/migration-runner.test-helpers.d.ts.map +1 -0
  94. package/dist/migrations/migration-runner.test-helpers.js +47 -0
  95. package/dist/migrations/migration-runner.test-helpers.js.map +1 -0
  96. package/dist/operator/operator-profile.d.ts +44 -0
  97. package/dist/operator/operator-profile.d.ts.map +1 -0
  98. package/dist/operator/operator-profile.js +377 -0
  99. package/dist/operator/operator-profile.js.map +1 -0
  100. package/dist/operator/operator-signals.d.ts +45 -0
  101. package/dist/operator/operator-signals.d.ts.map +1 -0
  102. package/dist/operator/operator-signals.js +228 -0
  103. package/dist/operator/operator-signals.js.map +1 -0
  104. package/dist/operator/operator-types.d.ts +360 -0
  105. package/dist/operator/operator-types.d.ts.map +1 -0
  106. package/dist/operator/operator-types.js +24 -0
  107. package/dist/operator/operator-types.js.map +1 -0
  108. package/dist/packs/types.d.ts +27 -27
  109. package/dist/paths.d.ts +40 -0
  110. package/dist/paths.d.ts.map +1 -0
  111. package/dist/paths.js +98 -0
  112. package/dist/paths.js.map +1 -0
  113. package/dist/persistence/index.d.ts +1 -1
  114. package/dist/persistence/index.d.ts.map +1 -1
  115. package/dist/persistence/index.js +1 -1
  116. package/dist/persistence/index.js.map +1 -1
  117. package/dist/persistence/sqlite-provider.d.ts +2 -0
  118. package/dist/persistence/sqlite-provider.d.ts.map +1 -1
  119. package/dist/persistence/sqlite-provider.js +8 -5
  120. package/dist/persistence/sqlite-provider.js.map +1 -1
  121. package/dist/planning/evidence-collector.d.ts +13 -1
  122. package/dist/planning/evidence-collector.d.ts.map +1 -1
  123. package/dist/planning/evidence-collector.js +33 -0
  124. package/dist/planning/evidence-collector.js.map +1 -1
  125. package/dist/planning/gap-analysis.d.ts +5 -4
  126. package/dist/planning/gap-analysis.d.ts.map +1 -1
  127. package/dist/planning/gap-analysis.js +7 -341
  128. package/dist/planning/gap-analysis.js.map +1 -1
  129. package/dist/planning/gap-passes.d.ts +19 -0
  130. package/dist/planning/gap-passes.d.ts.map +1 -0
  131. package/dist/planning/gap-passes.js +157 -0
  132. package/dist/planning/gap-passes.js.map +1 -0
  133. package/dist/planning/gap-patterns.d.ts +29 -0
  134. package/dist/planning/gap-patterns.d.ts.map +1 -0
  135. package/dist/planning/gap-patterns.js +129 -0
  136. package/dist/planning/gap-patterns.js.map +1 -0
  137. package/dist/planning/gap-types.d.ts +1 -1
  138. package/dist/planning/gap-types.d.ts.map +1 -1
  139. package/dist/planning/gap-types.js +1 -0
  140. package/dist/planning/gap-types.js.map +1 -1
  141. package/dist/planning/github-projection.d.ts +122 -0
  142. package/dist/planning/github-projection.d.ts.map +1 -0
  143. package/dist/planning/github-projection.js +294 -0
  144. package/dist/planning/github-projection.js.map +1 -0
  145. package/dist/planning/impact-analyzer.d.ts +26 -0
  146. package/dist/planning/impact-analyzer.d.ts.map +1 -0
  147. package/dist/planning/impact-analyzer.js +199 -0
  148. package/dist/planning/impact-analyzer.js.map +1 -0
  149. package/dist/planning/plan-lifecycle.d.ts +136 -0
  150. package/dist/planning/plan-lifecycle.d.ts.map +1 -0
  151. package/dist/planning/plan-lifecycle.js +296 -0
  152. package/dist/planning/plan-lifecycle.js.map +1 -0
  153. package/dist/planning/planner-types.d.ts +202 -0
  154. package/dist/planning/planner-types.d.ts.map +1 -0
  155. package/dist/planning/planner-types.js +6 -0
  156. package/dist/planning/planner-types.js.map +1 -0
  157. package/dist/planning/planner.d.ts +31 -383
  158. package/dist/planning/planner.d.ts.map +1 -1
  159. package/dist/planning/planner.js +154 -878
  160. package/dist/planning/planner.js.map +1 -1
  161. package/dist/planning/rationalization-detector.d.ts +32 -0
  162. package/dist/planning/rationalization-detector.d.ts.map +1 -0
  163. package/dist/planning/rationalization-detector.js +89 -0
  164. package/dist/planning/rationalization-detector.js.map +1 -0
  165. package/dist/planning/reconciliation-engine.d.ts +47 -0
  166. package/dist/planning/reconciliation-engine.d.ts.map +1 -0
  167. package/dist/planning/reconciliation-engine.js +128 -0
  168. package/dist/planning/reconciliation-engine.js.map +1 -0
  169. package/dist/planning/task-verifier.d.ts +85 -0
  170. package/dist/planning/task-verifier.d.ts.map +1 -0
  171. package/dist/planning/task-verifier.js +227 -0
  172. package/dist/planning/task-verifier.js.map +1 -0
  173. package/dist/plugins/types.d.ts +4 -4
  174. package/dist/runtime/admin-ops.d.ts +2 -2
  175. package/dist/runtime/admin-ops.d.ts.map +1 -1
  176. package/dist/runtime/admin-ops.js +44 -17
  177. package/dist/runtime/admin-ops.js.map +1 -1
  178. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  179. package/dist/runtime/admin-setup-ops.js +21 -46
  180. package/dist/runtime/admin-setup-ops.js.map +1 -1
  181. package/dist/runtime/archive-ops.d.ts +10 -0
  182. package/dist/runtime/archive-ops.d.ts.map +1 -0
  183. package/dist/runtime/archive-ops.js +310 -0
  184. package/dist/runtime/archive-ops.js.map +1 -0
  185. package/dist/runtime/capture-ops.d.ts.map +1 -1
  186. package/dist/runtime/capture-ops.js +42 -7
  187. package/dist/runtime/capture-ops.js.map +1 -1
  188. package/dist/runtime/claude-md-helpers.js +1 -1
  189. package/dist/runtime/claude-md-helpers.js.map +1 -1
  190. package/dist/runtime/context-health.d.ts +31 -0
  191. package/dist/runtime/context-health.d.ts.map +1 -0
  192. package/dist/runtime/context-health.js +57 -0
  193. package/dist/runtime/context-health.js.map +1 -0
  194. package/dist/runtime/facades/archive-facade.d.ts +10 -0
  195. package/dist/runtime/facades/archive-facade.d.ts.map +1 -0
  196. package/dist/runtime/facades/archive-facade.js +11 -0
  197. package/dist/runtime/facades/archive-facade.js.map +1 -0
  198. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  199. package/dist/runtime/facades/brain-facade.js +2 -0
  200. package/dist/runtime/facades/brain-facade.js.map +1 -1
  201. package/dist/runtime/facades/chat-facade.d.ts +7 -0
  202. package/dist/runtime/facades/chat-facade.d.ts.map +1 -1
  203. package/dist/runtime/facades/chat-facade.js +15 -800
  204. package/dist/runtime/facades/chat-facade.js.map +1 -1
  205. package/dist/runtime/facades/chat-service-ops.d.ts +9 -0
  206. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -0
  207. package/dist/runtime/facades/chat-service-ops.js +330 -0
  208. package/dist/runtime/facades/chat-service-ops.js.map +1 -0
  209. package/dist/runtime/facades/chat-session-ops.d.ts +8 -0
  210. package/dist/runtime/facades/chat-session-ops.d.ts.map +1 -0
  211. package/dist/runtime/facades/chat-session-ops.js +136 -0
  212. package/dist/runtime/facades/chat-session-ops.js.map +1 -0
  213. package/dist/runtime/facades/chat-state.d.ts +31 -0
  214. package/dist/runtime/facades/chat-state.d.ts.map +1 -0
  215. package/dist/runtime/facades/chat-state.js +32 -0
  216. package/dist/runtime/facades/chat-state.js.map +1 -0
  217. package/dist/runtime/facades/chat-transport-ops.d.ts +9 -0
  218. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -0
  219. package/dist/runtime/facades/chat-transport-ops.js +337 -0
  220. package/dist/runtime/facades/chat-transport-ops.js.map +1 -0
  221. package/dist/runtime/facades/control-facade.d.ts.map +1 -1
  222. package/dist/runtime/facades/control-facade.js +4 -1
  223. package/dist/runtime/facades/control-facade.js.map +1 -1
  224. package/dist/runtime/facades/index.d.ts.map +1 -1
  225. package/dist/runtime/facades/index.js +6 -0
  226. package/dist/runtime/facades/index.js.map +1 -1
  227. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  228. package/dist/runtime/facades/memory-facade.js +75 -6
  229. package/dist/runtime/facades/memory-facade.js.map +1 -1
  230. package/dist/runtime/facades/operator-facade.d.ts +8 -0
  231. package/dist/runtime/facades/operator-facade.d.ts.map +1 -0
  232. package/dist/runtime/facades/operator-facade.js +220 -0
  233. package/dist/runtime/facades/operator-facade.js.map +1 -0
  234. package/dist/runtime/facades/orchestrate-facade.js +3 -3
  235. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  236. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  237. package/dist/runtime/facades/plan-facade.js +39 -6
  238. package/dist/runtime/facades/plan-facade.js.map +1 -1
  239. package/dist/runtime/facades/review-facade.d.ts +7 -0
  240. package/dist/runtime/facades/review-facade.d.ts.map +1 -0
  241. package/dist/runtime/facades/review-facade.js +8 -0
  242. package/dist/runtime/facades/review-facade.js.map +1 -0
  243. package/dist/runtime/facades/sync-facade.d.ts +7 -0
  244. package/dist/runtime/facades/sync-facade.d.ts.map +1 -0
  245. package/dist/runtime/facades/sync-facade.js +8 -0
  246. package/dist/runtime/facades/sync-facade.js.map +1 -0
  247. package/dist/runtime/facades/vault-facade.d.ts +4 -1
  248. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  249. package/dist/runtime/facades/vault-facade.js +13 -66
  250. package/dist/runtime/facades/vault-facade.js.map +1 -1
  251. package/dist/runtime/github-integration.d.ts +49 -0
  252. package/dist/runtime/github-integration.d.ts.map +1 -0
  253. package/dist/runtime/github-integration.js +113 -0
  254. package/dist/runtime/github-integration.js.map +1 -0
  255. package/dist/runtime/grading-ops.js +1 -1
  256. package/dist/runtime/grading-ops.js.map +1 -1
  257. package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
  258. package/dist/runtime/memory-extra-ops.js +6 -2
  259. package/dist/runtime/memory-extra-ops.js.map +1 -1
  260. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  261. package/dist/runtime/orchestrate-ops.js +367 -40
  262. package/dist/runtime/orchestrate-ops.js.map +1 -1
  263. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  264. package/dist/runtime/planning-extra-ops.js +69 -4
  265. package/dist/runtime/planning-extra-ops.js.map +1 -1
  266. package/dist/runtime/review-ops.d.ts +10 -0
  267. package/dist/runtime/review-ops.d.ts.map +1 -0
  268. package/dist/runtime/review-ops.js +97 -0
  269. package/dist/runtime/review-ops.js.map +1 -0
  270. package/dist/runtime/runtime.d.ts.map +1 -1
  271. package/dist/runtime/runtime.js +27 -12
  272. package/dist/runtime/runtime.js.map +1 -1
  273. package/dist/runtime/session-briefing.d.ts +3 -0
  274. package/dist/runtime/session-briefing.d.ts.map +1 -1
  275. package/dist/runtime/session-briefing.js +68 -1
  276. package/dist/runtime/session-briefing.js.map +1 -1
  277. package/dist/runtime/sync-ops.d.ts +12 -0
  278. package/dist/runtime/sync-ops.d.ts.map +1 -0
  279. package/dist/runtime/sync-ops.js +288 -0
  280. package/dist/runtime/sync-ops.js.map +1 -0
  281. package/dist/runtime/types.d.ts +10 -4
  282. package/dist/runtime/types.d.ts.map +1 -1
  283. package/dist/runtime/vault-extra-ops.d.ts +5 -4
  284. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  285. package/dist/runtime/vault-extra-ops.js +5 -300
  286. package/dist/runtime/vault-extra-ops.js.map +1 -1
  287. package/dist/runtime/vault-sharing-ops.d.ts +4 -4
  288. package/dist/runtime/vault-sharing-ops.d.ts.map +1 -1
  289. package/dist/runtime/vault-sharing-ops.js +5 -300
  290. package/dist/runtime/vault-sharing-ops.js.map +1 -1
  291. package/dist/skills/sync-skills.d.ts +27 -0
  292. package/dist/skills/sync-skills.d.ts.map +1 -0
  293. package/dist/skills/sync-skills.js +81 -0
  294. package/dist/skills/sync-skills.js.map +1 -0
  295. package/dist/update-check.d.ts +14 -0
  296. package/dist/update-check.d.ts.map +1 -0
  297. package/dist/update-check.js +96 -0
  298. package/dist/update-check.js.map +1 -0
  299. package/dist/vault/linking.d.ts +10 -12
  300. package/dist/vault/linking.d.ts.map +1 -1
  301. package/dist/vault/linking.js +104 -161
  302. package/dist/vault/linking.js.map +1 -1
  303. package/dist/vault/vault-entries.d.ts +69 -0
  304. package/dist/vault/vault-entries.d.ts.map +1 -0
  305. package/dist/vault/vault-entries.js +257 -0
  306. package/dist/vault/vault-entries.js.map +1 -0
  307. package/dist/vault/vault-interfaces.d.ts +153 -0
  308. package/dist/vault/vault-interfaces.d.ts.map +1 -0
  309. package/dist/vault/vault-interfaces.js +2 -0
  310. package/dist/vault/vault-interfaces.js.map +1 -0
  311. package/dist/vault/vault-maintenance.d.ts +40 -0
  312. package/dist/vault/vault-maintenance.d.ts.map +1 -0
  313. package/dist/vault/vault-maintenance.js +142 -0
  314. package/dist/vault/vault-maintenance.js.map +1 -0
  315. package/dist/vault/vault-markdown-sync.d.ts +22 -0
  316. package/dist/vault/vault-markdown-sync.d.ts.map +1 -0
  317. package/dist/vault/vault-markdown-sync.js +143 -0
  318. package/dist/vault/vault-markdown-sync.js.map +1 -0
  319. package/dist/vault/vault-memories.d.ts +61 -0
  320. package/dist/vault/vault-memories.d.ts.map +1 -0
  321. package/dist/vault/vault-memories.js +240 -0
  322. package/dist/vault/vault-memories.js.map +1 -0
  323. package/dist/vault/vault-schema.d.ts +9 -0
  324. package/dist/vault/vault-schema.d.ts.map +1 -0
  325. package/dist/vault/vault-schema.js +179 -0
  326. package/dist/vault/vault-schema.js.map +1 -0
  327. package/dist/vault/vault.d.ts +29 -81
  328. package/dist/vault/vault.d.ts.map +1 -1
  329. package/dist/vault/vault.js +78 -931
  330. package/dist/vault/vault.js.map +1 -1
  331. package/package.json +1 -1
  332. package/src/agency/agency-manager.test.ts +600 -0
  333. package/src/agency/default-rules.test.ts +228 -0
  334. package/src/{__tests__ → brain}/brain-intelligence.test.ts +37 -14
  335. package/src/{__tests__ → brain}/brain.test.ts +1 -1
  336. package/src/brain/intelligence.ts +196 -15
  337. package/src/brain/learning-radar.ts +22 -1
  338. package/src/{__tests__ → brain}/second-brain-features.test.ts +4 -4
  339. package/src/{__tests__ → brain}/session-lifecycle.test.ts +2 -2
  340. package/src/brain/strength-scorer.ts +404 -0
  341. package/src/capabilities/chain-mapping.test.ts +66 -0
  342. package/src/capabilities/registry.test.ts +369 -0
  343. package/src/chat/agent-loop.test.ts +394 -0
  344. package/src/chat/agent-loop.ts +2 -0
  345. package/src/{__tests__ → chat}/chat-differentiators.test.ts +3 -3
  346. package/src/{__tests__ → chat}/chat-enhanced.test.ts +4 -4
  347. package/src/{__tests__ → chat}/chat-transport.test.ts +6 -6
  348. package/src/chat/mcp-bridge.test.ts +173 -0
  349. package/src/chat/notifications.ts +2 -0
  350. package/src/chat/output-compressor.test.ts +164 -0
  351. package/src/claudemd/compose.test.ts +178 -0
  352. package/src/claudemd/compose.ts +1 -1
  353. package/src/claudemd/inject.test.ts +211 -0
  354. package/src/context/context-engine.test.ts +461 -0
  355. package/src/control/identity-manager.test.ts +305 -0
  356. package/src/control/intent-router.test.ts +360 -0
  357. package/src/control/intent-router.ts +13 -4
  358. package/src/curator/classifier.test.ts +104 -0
  359. package/src/curator/contradiction-detector.test.ts +180 -0
  360. package/src/curator/contradiction-detector.ts +87 -0
  361. package/src/{__tests__ → curator}/curator-pipeline-e2e.test.ts +10 -10
  362. package/src/{__tests__ → curator}/curator.test.ts +77 -1
  363. package/src/curator/curator.ts +115 -777
  364. package/src/curator/duplicate-detector.test.ts +183 -0
  365. package/src/curator/duplicate-detector.ts +103 -0
  366. package/src/curator/health-audit.ts +126 -0
  367. package/src/curator/metadata-enricher.ts +84 -0
  368. package/src/curator/quality-gate.test.ts +135 -0
  369. package/src/curator/schema.ts +65 -0
  370. package/src/curator/tag-manager.test.ts +165 -0
  371. package/src/curator/tag-manager.ts +109 -0
  372. package/src/domain-packs/inject-rules.test.ts +117 -0
  373. package/src/domain-packs/knowledge-installer.test.ts +171 -0
  374. package/src/domain-packs/loader.test.ts +86 -0
  375. package/src/domain-packs/pack-runtime.test.ts +140 -0
  376. package/src/domain-packs/skills-installer.test.ts +135 -0
  377. package/src/domain-packs/token-resolver.test.ts +150 -0
  378. package/src/domain-packs/types.test.ts +130 -0
  379. package/src/enforcement/adapters/claude-code.test.ts +216 -0
  380. package/src/enforcement/registry.test.ts +264 -0
  381. package/src/engine/bin/soleri-engine.ts +28 -4
  382. package/src/engine/core-ops.test.ts +254 -0
  383. package/src/engine/core-ops.ts +25 -8
  384. package/src/engine/module-manifest.test.ts +124 -0
  385. package/src/engine/module-manifest.ts +22 -2
  386. package/src/engine/register-engine.test.ts +230 -0
  387. package/src/engine/register-engine.ts +26 -2
  388. package/src/errors/classify.test.ts +199 -0
  389. package/src/errors/retry.test.ts +156 -0
  390. package/src/errors/retry.ts +2 -0
  391. package/src/errors/types.test.ts +108 -0
  392. package/src/events/event-bus.test.ts +149 -0
  393. package/src/extensions/middleware.test.ts +234 -0
  394. package/src/facades/facade-factory.test.ts +424 -0
  395. package/src/flows/chain-runner.test.ts +273 -0
  396. package/src/flows/context-router.test.ts +52 -0
  397. package/src/flows/dispatch-registry.test.ts +128 -0
  398. package/src/flows/epilogue.test.ts +107 -0
  399. package/src/flows/executor.test.ts +263 -0
  400. package/src/flows/gate-evaluator.test.ts +194 -0
  401. package/src/flows/gate-evaluator.ts +25 -0
  402. package/src/flows/types.ts +4 -0
  403. package/src/governance/governance.test.ts +726 -0
  404. package/src/health/health-registry.test.ts +186 -0
  405. package/src/health/vault-integrity.test.ts +110 -0
  406. package/src/index.ts +92 -0
  407. package/src/intake/content-classifier.test.ts +209 -0
  408. package/src/intake/dedup-gate.test.ts +131 -0
  409. package/src/intake/intake-pipeline.test.ts +506 -0
  410. package/src/intake/intake-pipeline.ts +1 -0
  411. package/src/intake/text-ingester.test.ts +194 -0
  412. package/src/intake/text-ingester.ts +2 -0
  413. package/src/llm/key-pool.test.ts +236 -0
  414. package/src/llm/key-pool.ts +3 -4
  415. package/src/llm/llm-client.test.ts +345 -0
  416. package/src/llm/oauth-discovery.test.ts +180 -0
  417. package/src/llm/utils.test.ts +327 -0
  418. package/src/llm/utils.ts +2 -0
  419. package/src/{__tests__ → logging}/logger.test.ts +41 -62
  420. package/src/loop/loop-manager.test.ts +519 -0
  421. package/src/migrations/migration-runner.edge-cases.test.ts +319 -0
  422. package/src/migrations/migration-runner.test-helpers.ts +64 -0
  423. package/src/migrations/migration-runner.test.ts +385 -0
  424. package/src/operator/auto-signal-pipeline.test.ts +207 -0
  425. package/src/operator/operator-profile-extended.test.ts +320 -0
  426. package/src/operator/operator-profile.test.ts +314 -0
  427. package/src/operator/operator-profile.ts +469 -0
  428. package/src/operator/operator-signals-extended.test.ts +245 -0
  429. package/src/operator/operator-signals.test.ts +281 -0
  430. package/src/operator/operator-signals.ts +261 -0
  431. package/src/operator/operator-types.ts +444 -0
  432. package/src/operator/prompts/hook-precompact-operator-dispatch.md +94 -0
  433. package/src/operator/prompts/subagent-soft-signal-extractor.md +125 -0
  434. package/src/operator/prompts/subagent-synthesis-cognition.md +181 -0
  435. package/src/operator/prompts/subagent-synthesis-communication.md +140 -0
  436. package/src/operator/prompts/subagent-synthesis-technical.md +160 -0
  437. package/src/operator/prompts/subagent-synthesis-trust.md +143 -0
  438. package/src/{__tests__ → packs}/pack-lockfile.test.ts +3 -3
  439. package/src/{__tests__ → packs}/pack-system.test.ts +2 -2
  440. package/src/paths.ts +115 -0
  441. package/src/persistence/index.ts +1 -1
  442. package/src/persistence/sqlite-provider.test.ts +540 -0
  443. package/src/persistence/sqlite-provider.ts +8 -5
  444. package/src/persona/defaults.test.ts +59 -0
  445. package/src/persona/loader.test.ts +67 -0
  446. package/src/persona/prompt-generator.test.ts +127 -0
  447. package/src/planning/evidence-collector.test.ts +406 -0
  448. package/src/planning/evidence-collector.ts +50 -0
  449. package/src/planning/gap-analysis-alternatives.test.ts +169 -0
  450. package/src/planning/gap-analysis.ts +21 -636
  451. package/src/planning/gap-passes.test.ts +372 -0
  452. package/src/planning/gap-passes.ts +298 -0
  453. package/src/planning/gap-patterns.test.ts +320 -0
  454. package/src/planning/gap-patterns.ts +234 -0
  455. package/src/planning/gap-types.ts +4 -1
  456. package/src/planning/github-projection.test.ts +177 -0
  457. package/src/planning/github-projection.ts +425 -0
  458. package/src/planning/impact-analyzer.test.ts +180 -0
  459. package/src/planning/impact-analyzer.ts +264 -0
  460. package/src/planning/plan-lifecycle.test.ts +312 -0
  461. package/src/planning/plan-lifecycle.ts +346 -0
  462. package/src/planning/planner-types.ts +215 -0
  463. package/src/{__tests__ → planning}/planner.test.ts +169 -15
  464. package/src/planning/planner.ts +197 -1228
  465. package/src/planning/rationalization-detector.test.ts +171 -0
  466. package/src/planning/rationalization-detector.ts +138 -0
  467. package/src/planning/reconciliation-engine.test.ts +141 -0
  468. package/src/planning/reconciliation-engine.ts +162 -0
  469. package/src/planning/task-verifier.test.ts +235 -0
  470. package/src/planning/task-verifier.ts +303 -0
  471. package/src/planning/verification-protocol.test.ts +201 -0
  472. package/src/playbooks/generic/generic-playbooks.test.ts +438 -0
  473. package/src/playbooks/index.test.ts +77 -0
  474. package/src/playbooks/playbook-executor.test.ts +255 -0
  475. package/src/playbooks/playbook-registry.test.ts +232 -0
  476. package/src/playbooks/playbook-seeder.test.ts +153 -0
  477. package/src/plugins/plugin-loader.test.ts +212 -0
  478. package/src/plugins/plugin-registry.test.ts +272 -0
  479. package/src/project/project-registry.test.ts +428 -0
  480. package/src/prompts/parser.test.ts +100 -0
  481. package/src/prompts/template-manager.test.ts +109 -0
  482. package/src/{__tests__ → queue}/async-infrastructure.test.ts +3 -3
  483. package/src/queue/job-queue.test.ts +331 -0
  484. package/src/queue/pipeline-runner.test.ts +209 -0
  485. package/src/runtime/admin-extra-ops.test.ts +527 -0
  486. package/src/runtime/admin-ops.test.ts +257 -0
  487. package/src/runtime/admin-ops.ts +45 -17
  488. package/src/runtime/admin-setup-ops.test.ts +328 -0
  489. package/src/runtime/admin-setup-ops.ts +20 -43
  490. package/src/runtime/archive-ops.test.ts +269 -0
  491. package/src/runtime/archive-ops.ts +347 -0
  492. package/src/runtime/capture-ops.test.ts +433 -0
  493. package/src/runtime/capture-ops.ts +50 -8
  494. package/src/runtime/chain-ops.test.ts +149 -0
  495. package/src/runtime/claude-md-helpers.test.ts +191 -0
  496. package/src/runtime/claude-md-helpers.ts +1 -1
  497. package/src/runtime/context-health.test.ts +78 -0
  498. package/src/runtime/context-health.ts +85 -0
  499. package/src/runtime/curator-extra-ops.test.ts +202 -0
  500. package/src/runtime/deprecation.test.ts +98 -0
  501. package/src/runtime/domain-ops.test.ts +268 -0
  502. package/src/runtime/facades/admin-facade.test.ts +333 -0
  503. package/src/runtime/facades/agency-facade.test.ts +278 -0
  504. package/src/runtime/facades/archive-facade.test.ts +294 -0
  505. package/src/runtime/facades/archive-facade.ts +14 -0
  506. package/src/runtime/facades/brain-facade.test.ts +714 -0
  507. package/src/runtime/facades/brain-facade.ts +2 -0
  508. package/src/runtime/facades/chat-facade.test.ts +166 -0
  509. package/src/runtime/facades/chat-facade.ts +15 -906
  510. package/src/runtime/facades/chat-service-ops.test.ts +276 -0
  511. package/src/runtime/facades/chat-service-ops.ts +374 -0
  512. package/src/runtime/facades/chat-session-ops.test.ts +197 -0
  513. package/src/runtime/facades/chat-session-ops.ts +146 -0
  514. package/src/runtime/facades/chat-state.ts +60 -0
  515. package/src/runtime/facades/chat-transport-ops.test.ts +269 -0
  516. package/src/runtime/facades/chat-transport-ops.ts +380 -0
  517. package/src/runtime/facades/context-facade.test.ts +108 -0
  518. package/src/runtime/facades/control-facade.test.ts +436 -0
  519. package/src/runtime/facades/control-facade.ts +6 -1
  520. package/src/runtime/facades/curator-facade.test.ts +303 -0
  521. package/src/runtime/facades/index.ts +6 -0
  522. package/src/runtime/facades/loop-facade.test.ts +245 -0
  523. package/src/runtime/facades/memory-facade.test.ts +269 -0
  524. package/src/runtime/facades/memory-facade.ts +78 -6
  525. package/src/runtime/facades/operator-facade.test.ts +208 -0
  526. package/src/runtime/facades/operator-facade.ts +236 -0
  527. package/src/runtime/facades/orchestrate-facade.test.ts +185 -0
  528. package/src/runtime/facades/orchestrate-facade.ts +3 -3
  529. package/src/runtime/facades/plan-facade.test.ts +266 -0
  530. package/src/runtime/facades/plan-facade.ts +42 -6
  531. package/src/runtime/facades/review-facade.test.ts +82 -0
  532. package/src/runtime/facades/review-facade.ts +11 -0
  533. package/src/runtime/facades/sync-facade.test.ts +113 -0
  534. package/src/runtime/facades/sync-facade.ts +11 -0
  535. package/src/runtime/facades/vault-facade.test.ts +631 -0
  536. package/src/runtime/facades/vault-facade.ts +15 -70
  537. package/src/runtime/feature-flags.test.ts +140 -0
  538. package/src/runtime/github-integration.test.ts +89 -0
  539. package/src/runtime/github-integration.ts +159 -0
  540. package/src/runtime/grading-ops.test.ts +141 -0
  541. package/src/runtime/grading-ops.ts +1 -1
  542. package/src/runtime/intake-ops.test.ts +208 -0
  543. package/src/runtime/loop-ops.test.ts +238 -0
  544. package/src/runtime/memory-cross-project-ops.test.ts +177 -0
  545. package/src/runtime/memory-extra-ops.test.ts +453 -0
  546. package/src/runtime/memory-extra-ops.ts +6 -2
  547. package/src/runtime/orchestrate-ops.test.ts +302 -0
  548. package/src/runtime/orchestrate-ops.ts +435 -46
  549. package/src/runtime/pack-ops.test.ts +158 -0
  550. package/src/runtime/planning-extra-ops.test.ts +583 -0
  551. package/src/runtime/planning-extra-ops.ts +72 -4
  552. package/src/{__tests__ → runtime}/playbook-ops-execution.test.ts +3 -3
  553. package/src/runtime/playbook-ops.test.ts +262 -0
  554. package/src/runtime/plugin-ops.test.ts +201 -0
  555. package/src/runtime/project-ops.test.ts +235 -0
  556. package/src/runtime/review-ops.test.ts +142 -0
  557. package/src/runtime/review-ops.ts +99 -0
  558. package/src/runtime/runtime.test.ts +363 -0
  559. package/src/runtime/runtime.ts +39 -12
  560. package/src/runtime/session-briefing.test.ts +302 -0
  561. package/src/runtime/session-briefing.ts +80 -1
  562. package/src/runtime/sync-ops.test.ts +221 -0
  563. package/src/runtime/sync-ops.ts +325 -0
  564. package/src/runtime/telemetry-ops.test.ts +132 -0
  565. package/src/runtime/types.ts +10 -4
  566. package/src/runtime/vault-extra-ops.test.ts +246 -0
  567. package/src/runtime/vault-extra-ops.ts +5 -332
  568. package/src/runtime/vault-linking-ops.test.ts +237 -0
  569. package/src/runtime/vault-sharing-ops.test.ts +130 -0
  570. package/src/runtime/vault-sharing-ops.ts +5 -329
  571. package/src/skills/sync-skills.ts +108 -0
  572. package/src/streams/normalize.test.ts +95 -0
  573. package/src/streams/replayable-stream.test.ts +166 -0
  574. package/src/telemetry/telemetry.test.ts +143 -0
  575. package/src/transport/http-server.test.ts +394 -0
  576. package/src/transport/lsp-server.test.ts +458 -0
  577. package/src/transport/rate-limiter.test.ts +126 -0
  578. package/src/transport/session-manager.test.ts +133 -0
  579. package/src/transport/token-auth.test.ts +136 -0
  580. package/src/transport/ws-server.test.ts +294 -0
  581. package/src/update-check.ts +111 -0
  582. package/src/vault/__tests__/vault-characterization.test.ts +168 -0
  583. package/src/vault/content-hash.test.ts +78 -0
  584. package/src/vault/git-vault-sync.test.ts +234 -0
  585. package/src/vault/knowledge-review.test.ts +269 -0
  586. package/src/vault/linking.test.ts +358 -0
  587. package/src/vault/linking.ts +149 -183
  588. package/src/vault/obsidian-sync.test.ts +342 -0
  589. package/src/vault/playbook.test.ts +152 -0
  590. package/src/vault/scope-detector.test.ts +187 -0
  591. package/src/vault/vault-branching.test.ts +250 -0
  592. package/src/{__tests__ → vault}/vault-connect.test.ts +1 -1
  593. package/src/vault/vault-entries.ts +282 -0
  594. package/src/vault/vault-interfaces.ts +56 -0
  595. package/src/vault/vault-maintenance.ts +205 -0
  596. package/src/vault/vault-manager.test.ts +206 -0
  597. package/src/vault/vault-markdown-sync.test.ts +203 -0
  598. package/src/vault/vault-markdown-sync.ts +160 -0
  599. package/src/vault/vault-memories.ts +339 -0
  600. package/src/{__tests__ → vault}/vault-scaling.test.ts +1 -1
  601. package/src/vault/vault-schema.ts +181 -0
  602. package/src/{__tests__ → vault}/vault-sharing.test.ts +4 -4
  603. package/src/{__tests__ → vault}/vault.test.ts +2 -2
  604. package/src/vault/vault.ts +89 -1171
  605. package/dist/cognee/client.d.ts +0 -43
  606. package/dist/cognee/client.d.ts.map +0 -1
  607. package/dist/cognee/client.js +0 -375
  608. package/dist/cognee/client.js.map +0 -1
  609. package/dist/cognee/sync-manager.d.ts +0 -153
  610. package/dist/cognee/sync-manager.d.ts.map +0 -1
  611. package/dist/cognee/sync-manager.js +0 -390
  612. package/dist/cognee/sync-manager.js.map +0 -1
  613. package/dist/cognee/types.d.ts +0 -62
  614. package/dist/cognee/types.d.ts.map +0 -1
  615. package/dist/cognee/types.js +0 -3
  616. package/dist/cognee/types.js.map +0 -1
  617. package/dist/governance/index.d.ts +0 -3
  618. package/dist/governance/index.d.ts.map +0 -1
  619. package/dist/governance/index.js +0 -2
  620. package/dist/governance/index.js.map +0 -1
  621. package/dist/health/doctor-checks.d.ts +0 -15
  622. package/dist/health/doctor-checks.d.ts.map +0 -1
  623. package/dist/health/doctor-checks.js +0 -98
  624. package/dist/health/doctor-checks.js.map +0 -1
  625. package/dist/persistence/postgres-provider.d.ts +0 -81
  626. package/dist/persistence/postgres-provider.d.ts.map +0 -1
  627. package/dist/persistence/postgres-provider.js +0 -256
  628. package/dist/persistence/postgres-provider.js.map +0 -1
  629. package/dist/runtime/cognee-sync-ops.d.ts +0 -12
  630. package/dist/runtime/cognee-sync-ops.d.ts.map +0 -1
  631. package/dist/runtime/cognee-sync-ops.js +0 -93
  632. package/dist/runtime/cognee-sync-ops.js.map +0 -1
  633. package/dist/runtime/core-ops.d.ts +0 -23
  634. package/dist/runtime/core-ops.d.ts.map +0 -1
  635. package/dist/runtime/core-ops.js +0 -1296
  636. package/dist/runtime/core-ops.js.map +0 -1
  637. package/dist/runtime/facades/cognee-facade.d.ts +0 -8
  638. package/dist/runtime/facades/cognee-facade.d.ts.map +0 -1
  639. package/dist/runtime/facades/cognee-facade.js +0 -156
  640. package/dist/runtime/facades/cognee-facade.js.map +0 -1
  641. package/src/__tests__/admin-extra-ops.test.ts +0 -484
  642. package/src/__tests__/admin-ops.test.ts +0 -268
  643. package/src/__tests__/admin-setup-ops.test.ts +0 -355
  644. package/src/__tests__/agency-manager.test.ts +0 -374
  645. package/src/__tests__/agent-loop.test.ts +0 -256
  646. package/src/__tests__/capture-ops.test.ts +0 -784
  647. package/src/__tests__/claudemd.test.ts +0 -282
  648. package/src/__tests__/content-hash.test.ts +0 -60
  649. package/src/__tests__/context-engine.test.ts +0 -251
  650. package/src/__tests__/core-ops.test.ts +0 -550
  651. package/src/__tests__/curator-extra-ops.test.ts +0 -383
  652. package/src/__tests__/deprecation.test.ts +0 -78
  653. package/src/__tests__/domain-ops.test.ts +0 -226
  654. package/src/__tests__/domain-packs.test.ts +0 -421
  655. package/src/__tests__/enforcement.test.ts +0 -153
  656. package/src/__tests__/errors.test.ts +0 -388
  657. package/src/__tests__/extensions.test.ts +0 -233
  658. package/src/__tests__/facade-factory.test.ts +0 -271
  659. package/src/__tests__/feature-flags.test.ts +0 -137
  660. package/src/__tests__/flows.test.ts +0 -604
  661. package/src/__tests__/git-vault-sync.test.ts +0 -230
  662. package/src/__tests__/governance.test.ts +0 -522
  663. package/src/__tests__/grading-ops.test.ts +0 -361
  664. package/src/__tests__/health-registry.test.ts +0 -173
  665. package/src/__tests__/identity-manager.test.ts +0 -243
  666. package/src/__tests__/intake-pipeline.test.ts +0 -162
  667. package/src/__tests__/intent-router.test.ts +0 -222
  668. package/src/__tests__/knowledge-review.test.ts +0 -104
  669. package/src/__tests__/llm-client.test.ts +0 -69
  670. package/src/__tests__/llm.test.ts +0 -556
  671. package/src/__tests__/loader.test.ts +0 -176
  672. package/src/__tests__/loop-ops.test.ts +0 -469
  673. package/src/__tests__/lsp-transport.test.ts +0 -442
  674. package/src/__tests__/memory-cross-project-ops.test.ts +0 -248
  675. package/src/__tests__/memory-extra-ops.test.ts +0 -352
  676. package/src/__tests__/migration-runner.test.ts +0 -170
  677. package/src/__tests__/module-manifest-drift.test.ts +0 -59
  678. package/src/__tests__/normalize.test.ts +0 -85
  679. package/src/__tests__/obsidian-sync.test.ts +0 -354
  680. package/src/__tests__/orchestrate-ops.test.ts +0 -289
  681. package/src/__tests__/pack-ops.test.ts +0 -146
  682. package/src/__tests__/persistence.test.ts +0 -291
  683. package/src/__tests__/planning-extra-ops.test.ts +0 -706
  684. package/src/__tests__/playbook-executor.test.ts +0 -249
  685. package/src/__tests__/playbook-registry.test.ts +0 -326
  686. package/src/__tests__/playbook-seeder.test.ts +0 -163
  687. package/src/__tests__/playbook.test.ts +0 -389
  688. package/src/__tests__/plugin-ops.test.ts +0 -411
  689. package/src/__tests__/plugin-system.test.ts +0 -509
  690. package/src/__tests__/project-ops.test.ts +0 -381
  691. package/src/__tests__/replayable-stream.test.ts +0 -177
  692. package/src/__tests__/runtime.test.ts +0 -95
  693. package/src/__tests__/scope-detector.test.ts +0 -121
  694. package/src/__tests__/template-manager.test.ts +0 -222
  695. package/src/__tests__/token-resolver.test.ts +0 -79
  696. package/src/__tests__/transport.test.ts +0 -758
  697. package/src/__tests__/vault-branching.test.ts +0 -274
  698. package/src/__tests__/vault-extra-ops.test.ts +0 -482
  699. package/src/__tests__/vault-integrity.test.ts +0 -71
  700. package/src/__tests__/vault-manager.test.ts +0 -238
  701. package/src/__tests__/ws-transport.test.ts +0 -479
@@ -1,17 +1,10 @@
1
1
  import type { Vault } from '../vault/vault.js';
2
2
  import type { IntelligenceEntry } from '../intelligence/types.js';
3
3
  import type { PersistenceProvider } from '../persistence/types.js';
4
- import {
5
- tokenize,
6
- calculateTfIdf,
7
- cosineSimilarity,
8
- type SparseVector,
9
- } from '../text/similarity.js';
10
4
  import type {
11
5
  CuratorStatus,
12
6
  TagNormalizationResult,
13
7
  CanonicalTag,
14
- DuplicateCandidate,
15
8
  DuplicateDetectionResult,
16
9
  Contradiction,
17
10
  ContradictionStatus,
@@ -23,927 +16,272 @@ import type {
23
16
  HealthAuditResult,
24
17
  } from './types.js';
25
18
 
19
+ import { detectDuplicates as detectDuplicatesPure, DEFAULT_DUPLICATE_THRESHOLD } from './duplicate-detector.js';
20
+ import { findContradictions, DEFAULT_CONTRADICTION_THRESHOLD, type ContradictionCandidate } from './contradiction-detector.js';
21
+ import {
22
+ normalizeTag as normalizeTagPure,
23
+ normalizeAndDedup,
24
+ addTagAlias as addTagAliasPure,
25
+ getCanonicalTags as getCanonicalTagsPure,
26
+ seedDefaultAliases,
27
+ type TagStore,
28
+ } from './tag-manager.js';
29
+ import { initializeTables } from './schema.js';
30
+ import { computeHealthAudit, type HealthDataProvider } from './health-audit.js';
31
+ import { enrichEntryMetadata } from './metadata-enricher.js';
32
+
26
33
  // ─── Constants ──────────────────────────────────────────────────────
27
34
 
28
- const DEFAULT_DUPLICATE_THRESHOLD = 0.45;
29
- const MERGE_SUGGESTION_THRESHOLD = 0.65;
30
- const DEFAULT_CONTRADICTION_THRESHOLD = 0.4;
31
35
  const DEFAULT_STALE_DAYS = 90;
32
36
 
33
- const DEFAULT_TAG_ALIASES: Array<[string, string]> = [
34
- ['a11y', 'accessibility'],
35
- ['ts', 'typescript'],
36
- ['js', 'javascript'],
37
- ['css', 'styling'],
38
- ['tailwind', 'styling'],
39
- ['tw', 'styling'],
40
- ['vitest', 'testing'],
41
- ['jest', 'testing'],
42
- ['perf', 'performance'],
43
- ['sec', 'security'],
44
- ['auth', 'authentication'],
45
- ['i18n', 'internationalization'],
46
- ['l10n', 'localization'],
47
- ];
48
-
49
37
  // ─── Curator Class ──────────────────────────────────────────────────
50
38
 
51
39
  export class Curator {
52
40
  private vault: Vault;
53
41
  private provider: PersistenceProvider;
42
+ private tagStore: TagStore;
54
43
 
55
44
  constructor(vault: Vault) {
56
45
  this.vault = vault;
57
46
  this.provider = vault.getProvider();
58
- this.initializeTables();
59
- this.seedDefaultAliases();
60
- }
61
-
62
- // ─── Schema ─────────────────────────────────────────────────────
63
-
64
- private initializeTables(): void {
65
- this.provider.execSql(`
66
- CREATE TABLE IF NOT EXISTS curator_entry_state (
67
- entry_id TEXT PRIMARY KEY,
68
- status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'stale', 'archived')),
69
- confidence REAL NOT NULL DEFAULT 1.0,
70
- source TEXT NOT NULL DEFAULT 'unknown' CHECK(source IN ('manual', 'capture', 'seed', 'unknown')),
71
- last_groomed_at INTEGER,
72
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
73
- );
74
-
75
- CREATE TABLE IF NOT EXISTS curator_tag_canonical (
76
- tag TEXT PRIMARY KEY,
77
- description TEXT,
78
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
79
- );
80
-
81
- CREATE TABLE IF NOT EXISTS curator_tag_alias (
82
- alias TEXT PRIMARY KEY,
83
- canonical TEXT NOT NULL,
84
- created_at INTEGER NOT NULL DEFAULT (unixepoch()),
85
- FOREIGN KEY (canonical) REFERENCES curator_tag_canonical(tag)
86
- );
87
-
88
- CREATE TABLE IF NOT EXISTS curator_changelog (
89
- id INTEGER PRIMARY KEY AUTOINCREMENT,
90
- action TEXT NOT NULL,
91
- entry_id TEXT NOT NULL,
92
- before_value TEXT,
93
- after_value TEXT,
94
- reason TEXT NOT NULL,
95
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
96
- );
97
-
98
- CREATE TABLE IF NOT EXISTS curator_entry_history (
99
- id INTEGER PRIMARY KEY AUTOINCREMENT,
100
- entry_id TEXT NOT NULL,
101
- snapshot TEXT NOT NULL,
102
- changed_by TEXT DEFAULT 'system',
103
- change_reason TEXT,
104
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
105
- );
106
-
107
- CREATE TABLE IF NOT EXISTS curator_contradictions (
108
- id INTEGER PRIMARY KEY AUTOINCREMENT,
109
- pattern_id TEXT NOT NULL,
110
- antipattern_id TEXT NOT NULL,
111
- similarity REAL NOT NULL,
112
- status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'resolved', 'dismissed')),
113
- created_at INTEGER NOT NULL DEFAULT (unixepoch()),
114
- resolved_at INTEGER,
115
- UNIQUE(pattern_id, antipattern_id)
116
- );
117
- CREATE INDEX IF NOT EXISTS idx_curator_state_status ON curator_entry_state(status);
118
- CREATE INDEX IF NOT EXISTS idx_curator_changelog_entry ON curator_changelog(entry_id);
119
- `);
47
+ this.tagStore = this.createTagStore();
48
+ initializeTables(this.provider);
49
+ this.provider.transaction(() => seedDefaultAliases(this.tagStore));
120
50
  }
121
51
 
122
- private seedDefaultAliases(): void {
123
- this.provider.transaction(() => {
124
- const canonicals = new Set(DEFAULT_TAG_ALIASES.map(([, c]) => c));
125
- for (const tag of canonicals) {
126
- this.provider.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [tag]);
127
- }
128
- for (const [alias, canonical] of DEFAULT_TAG_ALIASES) {
129
- this.provider.run(
130
- 'INSERT OR IGNORE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)',
131
- [alias, canonical],
52
+ private createTagStore(): TagStore {
53
+ const p = this.provider;
54
+ return {
55
+ getAlias(lower: string) {
56
+ return p.get<{ canonical: string }>('SELECT canonical FROM curator_tag_alias WHERE alias = ?', [lower])?.canonical ?? null;
57
+ },
58
+ insertCanonical(tag: string) {
59
+ p.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [tag]);
60
+ },
61
+ upsertAlias(alias: string, canonical: string) {
62
+ p.run('INSERT OR REPLACE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)', [alias, canonical]);
63
+ },
64
+ getCanonicalRows() {
65
+ return p.all<{ tag: string; description: string | null; alias_count: number }>(
66
+ `SELECT c.tag, c.description, (SELECT COUNT(*) FROM curator_tag_alias a WHERE a.canonical = c.tag) as alias_count FROM curator_tag_canonical c ORDER BY c.tag`,
132
67
  );
133
- }
134
- });
68
+ },
69
+ countTagUsage(tag: string) {
70
+ return p.get<{ count: number }>('SELECT COUNT(*) as count FROM entries WHERE tags LIKE ?', [`%"${tag}"%`])?.count ?? 0;
71
+ },
72
+ };
135
73
  }
136
74
 
137
75
  // ─── Status ─────────────────────────────────────────────────────
138
76
 
139
77
  getStatus(): CuratorStatus {
140
78
  const tableCount = (table: string): number =>
141
- (
142
- this.provider.get<{ count: number }>(`SELECT COUNT(*) as count FROM ${table}`) ?? {
143
- count: 0,
144
- }
145
- ).count;
146
-
79
+ (this.provider.get<{ count: number }>(`SELECT COUNT(*) as count FROM ${table}`) ?? { count: 0 }).count;
147
80
  const lastGroomed = this.provider.get<{ ts: number | null }>(
148
81
  'SELECT MAX(last_groomed_at) as ts FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
149
82
  ) ?? { ts: null };
150
-
151
83
  return {
152
84
  initialized: true,
153
- tables: {
154
- entry_state: tableCount('curator_entry_state'),
155
- tag_canonical: tableCount('curator_tag_canonical'),
156
- tag_alias: tableCount('curator_tag_alias'),
157
- changelog: tableCount('curator_changelog'),
158
- contradictions: tableCount('curator_contradictions'),
159
- },
85
+ tables: { entry_state: tableCount('curator_entry_state'), tag_canonical: tableCount('curator_tag_canonical'), tag_alias: tableCount('curator_tag_alias'), changelog: tableCount('curator_changelog'), contradictions: tableCount('curator_contradictions') },
160
86
  lastGroomedAt: lastGroomed.ts,
161
87
  };
162
88
  }
163
89
 
164
- // ─── Tag Normalization ──────────────────────────────────────────
90
+ // ─── Tags (delegates to tag-manager) ──────────────────────────
165
91
 
166
- normalizeTag(tag: string): TagNormalizationResult {
167
- const lower = tag.toLowerCase().trim();
168
- const row = this.provider.get<{ canonical: string }>(
169
- 'SELECT canonical FROM curator_tag_alias WHERE alias = ?',
170
- [lower],
171
- );
172
- if (row) {
173
- return { original: tag, normalized: row.canonical, wasAliased: true };
174
- }
175
- return { original: tag, normalized: lower, wasAliased: false };
176
- }
92
+ normalizeTag(tag: string): TagNormalizationResult { return normalizeTagPure(tag, this.tagStore); }
177
93
 
178
94
  normalizeTags(entryId: string): TagNormalizationResult[] {
179
95
  const entry = this.vault.get(entryId);
180
96
  if (!entry) return [];
181
-
182
- const results: TagNormalizationResult[] = [];
183
- const normalizedTags: string[] = [];
184
- let changed = false;
185
-
186
- for (const tag of entry.tags) {
187
- const result = this.normalizeTag(tag);
188
- results.push(result);
189
- normalizedTags.push(result.normalized);
190
- if (result.normalized !== tag) changed = true;
191
- }
192
-
97
+ const { results, dedupedTags, changed } = normalizeAndDedup(entry.tags, this.tagStore);
193
98
  if (changed) {
194
- const dedupedTags = [...new Set(normalizedTags)];
195
- this.provider.run('UPDATE entries SET tags = ?, updated_at = unixepoch() WHERE id = ?', [
196
- JSON.stringify(dedupedTags),
197
- entryId,
198
- ]);
199
- this.logChange(
200
- 'normalize_tags',
201
- entryId,
202
- JSON.stringify(entry.tags),
203
- JSON.stringify(dedupedTags),
204
- 'Tag normalization',
205
- );
99
+ this.provider.run('UPDATE entries SET tags = ?, updated_at = unixepoch() WHERE id = ?', [JSON.stringify(dedupedTags), entryId]);
100
+ this.logChange('normalize_tags', entryId, JSON.stringify(entry.tags), JSON.stringify(dedupedTags), 'Tag normalization');
206
101
  }
207
-
208
102
  return results;
209
103
  }
210
104
 
211
- addTagAlias(alias: string, canonical: string): void {
212
- const lower = alias.toLowerCase().trim();
213
- const canonicalLower = canonical.toLowerCase().trim();
214
- this.provider.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [
215
- canonicalLower,
216
- ]);
217
- this.provider.run('INSERT OR REPLACE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)', [
218
- lower,
219
- canonicalLower,
220
- ]);
221
- }
105
+ addTagAlias(alias: string, canonical: string): void { addTagAliasPure(alias, canonical, this.tagStore); }
106
+ getCanonicalTags(): CanonicalTag[] { return getCanonicalTagsPure(this.tagStore); }
222
107
 
223
- getCanonicalTags(): CanonicalTag[] {
224
- const rows = this.provider.all<{
225
- tag: string;
226
- description: string | null;
227
- alias_count: number;
228
- }>(
229
- `SELECT c.tag, c.description,
230
- (SELECT COUNT(*) FROM curator_tag_alias a WHERE a.canonical = c.tag) as alias_count
231
- FROM curator_tag_canonical c
232
- ORDER BY c.tag`,
233
- );
234
-
235
- return rows.map((row) => ({
236
- tag: row.tag,
237
- description: row.description,
238
- usageCount: this.countTagUsage(row.tag),
239
- aliasCount: row.alias_count,
240
- }));
241
- }
242
-
243
- private countTagUsage(tag: string): number {
244
- const row = this.provider.get<{ count: number }>(
245
- 'SELECT COUNT(*) as count FROM entries WHERE tags LIKE ?',
246
- [`%"${tag}"%`],
247
- );
248
- return row?.count ?? 0;
249
- }
250
-
251
- // ─── Duplicate Detection ────────────────────────────────────────
108
+ // ─── Duplicates (delegates to duplicate-detector) ─────────────
252
109
 
253
110
  detectDuplicates(entryId?: string, threshold?: number): DuplicateDetectionResult[] {
254
- const effectiveThreshold = threshold ?? DEFAULT_DUPLICATE_THRESHOLD;
255
- const entries = this.vault.list({ limit: 100000 });
256
- if (entries.length === 0) return [];
257
-
258
- // Build transient vocabulary
259
- const vocabulary = this.buildVocabulary(entries);
260
-
261
- // Build vectors for all entries
262
- const vectors = new Map<string, SparseVector>();
263
- for (const entry of entries) {
264
- const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
265
- ' ',
266
- );
267
- vectors.set(entry.id, calculateTfIdf(tokenize(text), vocabulary));
268
- }
269
-
270
- const targetEntries = entryId ? entries.filter((e) => e.id === entryId) : entries;
271
-
272
- const results: DuplicateDetectionResult[] = [];
273
-
274
- for (const entry of targetEntries) {
275
- const entryVec = vectors.get(entry.id)!;
276
- const matches: DuplicateCandidate[] = [];
277
-
278
- for (const other of entries) {
279
- if (other.id === entry.id) continue;
280
- const otherVec = vectors.get(other.id)!;
281
- const similarity = cosineSimilarity(entryVec, otherVec);
282
- if (similarity >= effectiveThreshold) {
283
- matches.push({
284
- entryId: other.id,
285
- title: other.title,
286
- similarity,
287
- suggestMerge: similarity >= MERGE_SUGGESTION_THRESHOLD,
288
- });
289
- }
290
- }
291
-
292
- if (matches.length > 0) {
293
- matches.sort((a, b) => b.similarity - a.similarity);
294
- results.push({
295
- entryId: entry.id,
296
- matches,
297
- scannedCount: entries.length - 1,
298
- });
299
- }
300
- }
301
-
302
- return results;
111
+ return detectDuplicatesPure(this.vault.list({ limit: 100000 }), entryId, threshold);
303
112
  }
304
113
 
305
- // ─── Contradictions ─────────────────────────────────────────────
114
+ // ─── Contradictions (delegates to contradiction-detector) ─────
306
115
 
307
116
  detectContradictions(threshold?: number): Contradiction[] {
308
- const effectiveThreshold = threshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
309
- const entries = this.vault.list({ limit: 100000 });
310
- const antipatterns = entries.filter((e) => e.type === 'anti-pattern');
311
- const patterns = entries.filter((e) => e.type === 'pattern');
312
-
313
- if (antipatterns.length === 0 || patterns.length === 0) return [];
314
-
315
- const vocabulary = this.buildVocabulary(entries);
316
- const detected: Contradiction[] = [];
317
-
318
- for (const ap of antipatterns) {
319
- // Stage 1: FTS5 candidate retrieval (fall back to all patterns if FTS returns empty)
320
- let candidates: IntelligenceEntry[];
321
- try {
322
- const searchResults = this.vault.search(ap.title, { type: 'pattern', limit: 20 });
323
- candidates = searchResults.length > 0 ? searchResults.map((r) => r.entry) : patterns;
324
- } catch {
325
- candidates = patterns;
326
- }
327
-
328
- // Stage 2: TF-IDF cosine similarity
329
- const apText = [ap.title, ap.description, ap.context ?? ''].join(' ');
330
- const apVec = calculateTfIdf(tokenize(apText), vocabulary);
331
-
332
- for (const pattern of candidates) {
333
- const pText = [pattern.title, pattern.description, pattern.context ?? ''].join(' ');
334
- const pVec = calculateTfIdf(tokenize(pText), vocabulary);
335
- const similarity = cosineSimilarity(apVec, pVec);
336
-
337
- if (similarity >= effectiveThreshold) {
338
- const result = this.provider.run(
339
- 'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
340
- [pattern.id, ap.id, similarity],
341
- );
342
- if (result.changes > 0) {
343
- const row = this.provider.get<Record<string, unknown>>(
344
- 'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
345
- [pattern.id, ap.id],
346
- );
347
- if (row) detected.push(this.rowToContradiction(row));
348
- }
349
- }
350
- }
351
- }
352
-
353
- return detected;
117
+ const searchFn = (title: string) => this.vault.search(title, { type: 'pattern', limit: 20 }).map((r) => r.entry);
118
+ return this.persistContradictions(findContradictions(this.vault.list({ limit: 100000 }), threshold, searchFn));
354
119
  }
355
120
 
356
121
  getContradictions(status?: ContradictionStatus): Contradiction[] {
357
- const query = status
358
- ? 'SELECT * FROM curator_contradictions WHERE status = ? ORDER BY similarity DESC'
359
- : 'SELECT * FROM curator_contradictions ORDER BY similarity DESC';
360
- const rows = this.provider.all<Record<string, unknown>>(query, status ? [status] : undefined);
361
- return rows.map((r) => this.rowToContradiction(r));
122
+ const query = status ? 'SELECT * FROM curator_contradictions WHERE status = ? ORDER BY similarity DESC' : 'SELECT * FROM curator_contradictions ORDER BY similarity DESC';
123
+ return this.provider.all<Record<string, unknown>>(query, status ? [status] : undefined).map((r) => this.rowToContradiction(r));
362
124
  }
363
125
 
364
126
  resolveContradiction(id: number, resolution: 'resolved' | 'dismissed'): Contradiction | null {
365
- this.provider.run(
366
- 'UPDATE curator_contradictions SET status = ?, resolved_at = unixepoch() WHERE id = ?',
367
- [resolution, id],
368
- );
369
- const row = this.provider.get<Record<string, unknown>>(
370
- 'SELECT * FROM curator_contradictions WHERE id = ?',
371
- [id],
372
- );
127
+ this.provider.run('UPDATE curator_contradictions SET status = ?, resolved_at = unixepoch() WHERE id = ?', [resolution, id]);
128
+ const row = this.provider.get<Record<string, unknown>>('SELECT * FROM curator_contradictions WHERE id = ?', [id]);
373
129
  return row ? this.rowToContradiction(row) : null;
374
130
  }
375
131
 
376
- async detectContradictionsHybrid(threshold?: number): Promise<{
377
- contradictions: Contradiction[];
378
- method: 'tfidf-only';
379
- }> {
380
- const effectiveThreshold = threshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
381
- const entries = this.vault.list({ limit: 100000 });
382
- const antipatterns = entries.filter((e) => e.type === 'anti-pattern');
383
- const patterns = entries.filter((e) => e.type === 'pattern');
384
-
385
- if (antipatterns.length === 0 || patterns.length === 0) {
386
- return { contradictions: [], method: 'tfidf-only' };
387
- }
388
-
389
- const vocabulary = this.buildVocabulary(entries);
390
- const detected: Contradiction[] = [];
391
-
392
- for (const ap of antipatterns) {
393
- let candidates: IntelligenceEntry[];
394
- try {
395
- const searchResults = this.vault.search(ap.title, { type: 'pattern', limit: 20 });
396
- candidates = searchResults.length > 0 ? searchResults.map((r) => r.entry) : patterns;
397
- } catch {
398
- candidates = patterns;
399
- }
400
-
401
- const apText = [ap.title, ap.description, ap.context ?? ''].join(' ');
402
- const apVec = calculateTfIdf(tokenize(apText), vocabulary);
403
-
404
- for (const pattern of candidates) {
405
- const pText = [pattern.title, pattern.description, pattern.context ?? ''].join(' ');
406
- const pVec = calculateTfIdf(tokenize(pText), vocabulary);
407
- const finalScore = cosineSimilarity(apVec, pVec);
408
-
409
- if (finalScore >= effectiveThreshold) {
410
- const result = this.provider.run(
411
- 'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
412
- [pattern.id, ap.id, finalScore],
413
- );
414
- if (result.changes > 0) {
415
- const row = this.provider.get<Record<string, unknown>>(
416
- 'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
417
- [pattern.id, ap.id],
418
- );
419
- if (row) detected.push(this.rowToContradiction(row));
420
- }
421
- }
422
- }
423
- }
424
-
425
- return {
426
- contradictions: detected,
427
- method: 'tfidf-only',
428
- };
132
+ async detectContradictionsHybrid(threshold?: number): Promise<{ contradictions: Contradiction[]; method: 'tfidf-only' }> {
133
+ const searchFn = (title: string) => this.vault.search(title, { type: 'pattern', limit: 20 }).map((r) => r.entry);
134
+ return { contradictions: this.persistContradictions(findContradictions(this.vault.list({ limit: 100000 }), threshold, searchFn)), method: 'tfidf-only' };
429
135
  }
430
136
 
431
- // ─── Grooming ───────────────────────────────────────────────────
137
+ // ─── Grooming ─────────────────────────────────────────────────
432
138
 
433
139
  groomEntry(entryId: string): GroomResult | null {
434
140
  const entry = this.vault.get(entryId);
435
141
  if (!entry) return null;
436
-
437
142
  const tagsNormalized = this.normalizeTags(entryId);
438
-
439
- // Check staleness based on entry's updated_at timestamp
440
- const row = this.provider.get<{ updated_at: number }>(
441
- 'SELECT updated_at FROM entries WHERE id = ?',
442
- [entryId],
443
- );
143
+ const row = this.provider.get<{ updated_at: number }>('SELECT updated_at FROM entries WHERE id = ?', [entryId]);
444
144
  const now = Math.floor(Date.now() / 1000);
445
145
  const stale = row ? now - row.updated_at > DEFAULT_STALE_DAYS * 86400 : false;
446
-
447
146
  const status = stale ? 'stale' : 'active';
448
-
449
- // Upsert entry state
450
- this.provider.run(
451
- `INSERT INTO curator_entry_state (entry_id, status, last_groomed_at)
452
- VALUES (?, ?, unixepoch())
453
- ON CONFLICT(entry_id) DO UPDATE SET status = excluded.status, last_groomed_at = unixepoch()`,
454
- [entryId, status],
455
- );
456
-
147
+ this.provider.run(`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at) VALUES (?, ?, unixepoch()) ON CONFLICT(entry_id) DO UPDATE SET status = excluded.status, last_groomed_at = unixepoch()`, [entryId, status]);
457
148
  this.logChange('groom', entryId, null, `status=${status}`, 'Routine grooming');
458
-
459
- return {
460
- entryId,
461
- tagsNormalized,
462
- stale,
463
- lastGroomedAt: now,
464
- };
149
+ return { entryId, tagsNormalized, stale, lastGroomedAt: now };
465
150
  }
466
151
 
467
152
  groomAll(): GroomAllResult {
468
153
  const start = Date.now();
469
154
  const entries = this.vault.list({ limit: 100000 });
470
- let tagsNormalized = 0;
471
- let staleCount = 0;
472
-
155
+ let tagsNormalized = 0, staleCount = 0;
473
156
  for (const entry of entries) {
474
157
  const result = this.groomEntry(entry.id);
475
- if (result) {
476
- tagsNormalized += result.tagsNormalized.filter((t) => t.wasAliased).length;
477
- if (result.stale) staleCount++;
478
- }
158
+ if (result) { tagsNormalized += result.tagsNormalized.filter((t) => t.wasAliased).length; if (result.stale) staleCount++; }
479
159
  }
480
-
481
- return {
482
- totalEntries: entries.length,
483
- groomedCount: entries.length,
484
- tagsNormalized,
485
- staleCount,
486
- durationMs: Date.now() - start,
487
- };
160
+ return { totalEntries: entries.length, groomedCount: entries.length, tagsNormalized, staleCount, durationMs: Date.now() - start };
488
161
  }
489
162
 
490
- // ─── Consolidation ─────────────────────────────────────────────
163
+ // ─── Consolidation ───────────────────────────────────────────
491
164
 
492
165
  consolidate(options?: ConsolidationOptions): ConsolidationResult {
493
166
  const start = Date.now();
494
167
  const dryRun = options?.dryRun ?? true;
495
168
  const staleDaysThreshold = options?.staleDaysThreshold ?? DEFAULT_STALE_DAYS;
496
169
  const duplicateThreshold = options?.duplicateThreshold ?? DEFAULT_DUPLICATE_THRESHOLD;
497
- const contradictionThreshold =
498
- options?.contradictionThreshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
499
-
500
- // Detect duplicates
170
+ const contradictionThreshold = options?.contradictionThreshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
501
171
  const duplicates = this.detectDuplicates(undefined, duplicateThreshold);
502
-
503
- // Detect stale entries
504
172
  const now = Math.floor(Date.now() / 1000);
505
- const staleThreshold = now - staleDaysThreshold * 86400;
506
- const staleRows = this.provider.all<{ id: string }>(
507
- 'SELECT id FROM entries WHERE updated_at < ?',
508
- [staleThreshold],
509
- );
173
+ const staleRows = this.provider.all<{ id: string }>('SELECT id FROM entries WHERE updated_at < ?', [now - staleDaysThreshold * 86400]);
510
174
  const staleEntries = staleRows.map((r) => r.id);
511
-
512
- // Detect contradictions
513
175
  const contradictions = this.detectContradictions(contradictionThreshold);
514
-
515
176
  let mutations = 0;
516
-
517
177
  if (!dryRun) {
518
- // Archive stale entries
519
178
  for (const entryId of staleEntries) {
520
- this.provider.run(
521
- `INSERT INTO curator_entry_state (entry_id, status, last_groomed_at)
522
- VALUES (?, 'archived', unixepoch())
523
- ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`,
524
- [entryId],
525
- );
526
- this.logChange(
527
- 'archive',
528
- entryId,
529
- 'active',
530
- 'archived',
531
- 'Stale entry archived during consolidation',
532
- );
179
+ this.provider.run(`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at) VALUES (?, 'archived', unixepoch()) ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`, [entryId]);
180
+ this.logChange('archive', entryId, 'active', 'archived', 'Stale entry archived during consolidation');
533
181
  mutations++;
534
182
  }
535
-
536
- // Remove lower-similarity duplicates (keep the first entry, remove matches)
537
183
  const removed = new Set<string>();
538
184
  for (const result of duplicates) {
539
185
  for (const match of result.matches) {
540
186
  if (!removed.has(match.entryId) && match.entryId !== result.entryId) {
541
187
  this.vault.remove(match.entryId);
542
- this.logChange(
543
- 'remove_duplicate',
544
- match.entryId,
545
- null,
546
- null,
547
- `Duplicate of ${result.entryId} (similarity: ${match.similarity.toFixed(3)})`,
548
- );
188
+ this.logChange('remove_duplicate', match.entryId, null, null, `Duplicate of ${result.entryId} (similarity: ${match.similarity.toFixed(3)})`);
549
189
  removed.add(match.entryId);
550
190
  mutations++;
551
191
  }
552
192
  }
553
193
  }
554
194
  }
555
-
556
- return {
557
- dryRun,
558
- duplicates,
559
- staleEntries,
560
- contradictions,
561
- mutations,
562
- durationMs: Date.now() - start,
563
- };
195
+ return { dryRun, duplicates, staleEntries, contradictions, mutations, durationMs: Date.now() - start };
564
196
  }
565
197
 
566
- // ─── Changelog ──────────────────────────────────────────────────
198
+ // ─── Changelog ────────────────────────────────────────────────
567
199
 
568
200
  getEntryHistory(entryId: string, limit?: number): ChangelogEntry[] {
569
- const rows = this.provider.all<Record<string, unknown>>(
570
- 'SELECT * FROM curator_changelog WHERE entry_id = ? ORDER BY created_at DESC, id DESC LIMIT ?',
571
- [entryId, limit ?? 50],
572
- );
573
- return rows.map((r) => this.rowToChangelog(r));
201
+ return this.provider.all<Record<string, unknown>>('SELECT * FROM curator_changelog WHERE entry_id = ? ORDER BY created_at DESC, id DESC LIMIT ?', [entryId, limit ?? 50]).map((r) => this.rowToChangelog(r));
574
202
  }
575
203
 
576
- // ─── Health Audit ───────────────────────────────────────────────
204
+ // ─── Health Audit (delegates to health-audit) ─────────────────
577
205
 
578
206
  healthAudit(): HealthAuditResult {
579
207
  const entries = this.vault.list({ limit: 100000 });
580
- const recommendations: string[] = [];
581
-
582
- if (entries.length === 0) {
583
- return {
584
- score: 100,
585
- metrics: { coverage: 1, freshness: 1, quality: 1, tagHealth: 1 },
586
- recommendations: ['Vault is empty — add knowledge entries to get started.'],
587
- };
588
- }
589
-
590
- let score = 100;
591
-
592
- // Coverage: penalize if no anti-patterns or no patterns
593
- const typeCount: Record<string, number> = { pattern: 0, 'anti-pattern': 0, rule: 0 };
594
- for (const e of entries) {
595
- typeCount[e.type] = (typeCount[e.type] ?? 0) + 1;
596
- }
597
- const hasPatterns = typeCount.pattern > 0;
598
- const hasAntiPatterns = typeCount['anti-pattern'] > 0;
599
- const hasRules = typeCount.rule > 0;
600
- let coverageScore = 1;
601
- if (!hasPatterns) {
602
- score -= 10;
603
- coverageScore -= 0.33;
604
- recommendations.push('No patterns found — add patterns to improve coverage.');
605
- }
606
- if (!hasAntiPatterns) {
607
- score -= 5;
608
- coverageScore -= 0.17;
609
- recommendations.push('No anti-patterns found — add anti-patterns to detect contradictions.');
610
- }
611
- if (!hasRules) {
612
- score -= 5;
613
- coverageScore -= 0.17;
614
- recommendations.push('No rules found — add rules for completeness.');
615
- }
616
- coverageScore = Math.max(0, coverageScore);
617
-
618
- // Freshness: penalize stale entries
619
- const now = Math.floor(Date.now() / 1000);
620
- const staleThreshold = now - DEFAULT_STALE_DAYS * 86400;
621
- const staleCount = (
622
- this.provider.get<{ count: number }>(
623
- 'SELECT COUNT(*) as count FROM entries WHERE updated_at < ?',
624
- [staleThreshold],
625
- ) ?? { count: 0 }
626
- ).count;
627
- const staleRatio = staleCount / entries.length;
628
- const freshnessScore = 1 - staleRatio;
629
- if (staleRatio > 0.3) {
630
- const penalty = Math.min(20, Math.round(staleRatio * 30));
631
- score -= penalty;
632
- recommendations.push(
633
- `${staleCount} stale entries (${Math.round(staleRatio * 100)}%) — run grooming to update.`,
634
- );
635
- }
636
-
637
- // Quality: penalize duplicates and contradictions
638
- const duplicates = this.detectDuplicates();
639
- const contradictions = this.getContradictions('open');
640
- let qualityScore = 1;
641
- if (duplicates.length > 0) {
642
- const penalty = Math.min(15, duplicates.length * 3);
643
- score -= penalty;
644
- qualityScore -= penalty / 30;
645
- recommendations.push(`${duplicates.length} entries have duplicates — run consolidation.`);
646
- }
647
- if (contradictions.length > 0) {
648
- const penalty = Math.min(15, contradictions.length * 5);
649
- score -= penalty;
650
- qualityScore -= penalty / 30;
651
- recommendations.push(`${contradictions.length} open contradictions — resolve or dismiss.`);
652
- }
653
- qualityScore = Math.max(0, qualityScore);
654
-
655
- // Tag health: penalize entries with few or no tags
656
- const lowTagEntries = entries.filter((e) => e.tags.length < 2);
657
- const lowTagRatio = lowTagEntries.length / entries.length;
658
- const tagHealthScore = 1 - lowTagRatio;
659
- if (lowTagRatio > 0.3) {
660
- const penalty = Math.min(10, Math.round(lowTagRatio * 15));
661
- score -= penalty;
662
- recommendations.push(
663
- `${lowTagEntries.length} entries have fewer than 2 tags — improve tagging.`,
664
- );
665
- }
666
-
667
- // Penalize ungroomed entries
668
- const groomedCount = (
669
- this.provider.get<{ count: number }>(
670
- 'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
671
- ) ?? { count: 0 }
672
- ).count;
673
- if (groomedCount < entries.length) {
674
- const ungroomed = entries.length - groomedCount;
675
- const penalty = Math.min(10, Math.round((ungroomed / entries.length) * 10));
676
- score -= penalty;
677
- recommendations.push(`${ungroomed} entries never groomed — run groomAll().`);
678
- }
679
-
680
- score = Math.max(0, score);
681
-
682
- if (recommendations.length === 0) {
683
- recommendations.push('Vault is healthy — no issues detected.');
684
- }
685
-
686
- return {
687
- score,
688
- metrics: {
689
- coverage: coverageScore,
690
- freshness: freshnessScore,
691
- quality: qualityScore,
692
- tagHealth: tagHealthScore,
693
- },
694
- recommendations,
208
+ const dataProvider: HealthDataProvider = {
209
+ getStaleCount: (threshold) => (this.provider.get<{ count: number }>('SELECT COUNT(*) as count FROM entries WHERE updated_at < ?', [threshold]) ?? { count: 0 }).count,
210
+ getGroomedCount: () => (this.provider.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL') ?? { count: 0 }).count,
211
+ getDuplicates: () => this.detectDuplicates(),
212
+ getOpenContradictions: () => this.getContradictions('open'),
695
213
  };
214
+ return computeHealthAudit(entries, dataProvider, DEFAULT_STALE_DAYS);
696
215
  }
697
216
 
698
- // ─── Entry History (Version Snapshots) ─────────────────────────
217
+ // ─── Entry History (Version Snapshots) ────────────────────────
699
218
 
700
- recordSnapshot(
701
- entryId: string,
702
- changedBy?: string,
703
- changeReason?: string,
704
- ): { recorded: boolean; historyId: number } {
219
+ recordSnapshot(entryId: string, changedBy?: string, changeReason?: string): { recorded: boolean; historyId: number } {
705
220
  const entry = this.vault.get(entryId);
706
221
  if (!entry) return { recorded: false, historyId: -1 };
707
-
708
- const result = this.provider.run(
709
- 'INSERT INTO curator_entry_history (entry_id, snapshot, changed_by, change_reason, created_at) VALUES (?, ?, ?, ?, unixepoch())',
710
- [entryId, JSON.stringify(entry), changedBy ?? 'system', changeReason ?? null],
711
- );
712
-
222
+ const result = this.provider.run('INSERT INTO curator_entry_history (entry_id, snapshot, changed_by, change_reason, created_at) VALUES (?, ?, ?, ?, unixepoch())', [entryId, JSON.stringify(entry), changedBy ?? 'system', changeReason ?? null]);
713
223
  return { recorded: true, historyId: Number(result.lastInsertRowid) };
714
224
  }
715
225
 
716
- getVersionHistory(entryId: string): Array<{
717
- historyId: number;
718
- entryId: string;
719
- snapshot: IntelligenceEntry;
720
- changedBy: string;
721
- changeReason: string | null;
722
- createdAt: number;
723
- }> {
724
- const rows = this.provider.all<Record<string, unknown>>(
725
- 'SELECT * FROM curator_entry_history WHERE entry_id = ? ORDER BY created_at ASC, id ASC',
726
- [entryId],
727
- );
728
-
729
- return rows.map((row) => ({
730
- historyId: row.id as number,
731
- entryId: row.entry_id as string,
732
- snapshot: JSON.parse(row.snapshot as string) as IntelligenceEntry,
733
- changedBy: row.changed_by as string,
734
- changeReason: (row.change_reason as string) ?? null,
735
- createdAt: row.created_at as number,
226
+ getVersionHistory(entryId: string): Array<{ historyId: number; entryId: string; snapshot: IntelligenceEntry; changedBy: string; changeReason: string | null; createdAt: number }> {
227
+ return this.provider.all<Record<string, unknown>>('SELECT * FROM curator_entry_history WHERE entry_id = ? ORDER BY created_at ASC, id ASC', [entryId]).map((row) => ({
228
+ historyId: row.id as number, entryId: row.entry_id as string, snapshot: JSON.parse(row.snapshot as string) as IntelligenceEntry, changedBy: row.changed_by as string, changeReason: (row.change_reason as string) ?? null, createdAt: row.created_at as number,
736
229
  }));
737
230
  }
738
231
 
739
- // ─── Queue Stats ──────────────────────────────────────────────
740
-
741
- getQueueStats(): {
742
- totalEntries: number;
743
- groomedEntries: number;
744
- ungroomedEntries: number;
745
- staleEntries: number;
746
- freshEntries: number;
747
- avgDaysSinceGroom: number;
748
- } {
749
- const totalEntries = (
750
- this.provider.get<{ count: number }>('SELECT COUNT(*) as count FROM entries') ?? { count: 0 }
751
- ).count;
752
-
753
- const groomedEntries = (
754
- this.provider.get<{ count: number }>(
755
- 'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
756
- ) ?? { count: 0 }
757
- ).count;
758
-
759
- const ungroomedEntries = totalEntries - groomedEntries;
232
+ // ─── Queue Stats ─────────────────────────────────────────────
760
233
 
234
+ getQueueStats(): { totalEntries: number; groomedEntries: number; ungroomedEntries: number; staleEntries: number; freshEntries: number; avgDaysSinceGroom: number } {
235
+ const p = this.provider;
236
+ const totalEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM entries') ?? { count: 0 }).count;
237
+ const groomedEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL') ?? { count: 0 }).count;
761
238
  const now = Math.floor(Date.now() / 1000);
762
- const staleThreshold = now - 30 * 86400;
763
- const freshThreshold = now - 7 * 86400;
764
-
765
- const staleEntries = (
766
- this.provider.get<{ count: number }>(
767
- 'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at < ?',
768
- [staleThreshold],
769
- ) ?? { count: 0 }
770
- ).count;
771
-
772
- const freshEntries = (
773
- this.provider.get<{ count: number }>(
774
- 'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at >= ?',
775
- [freshThreshold],
776
- ) ?? { count: 0 }
777
- ).count;
778
-
239
+ const staleEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at < ?', [now - 30 * 86400]) ?? { count: 0 }).count;
240
+ const freshEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at >= ?', [now - 7 * 86400]) ?? { count: 0 }).count;
779
241
  let avgDaysSinceGroom = 0;
780
242
  if (groomedEntries > 0) {
781
- const sumRow = this.provider.get<{ total: number | null }>(
782
- 'SELECT SUM(? - last_groomed_at) as total FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
783
- [now],
784
- ) ?? { total: 0 };
785
- const totalSeconds = sumRow.total ?? 0;
243
+ const totalSeconds = (p.get<{ total: number | null }>('SELECT SUM(? - last_groomed_at) as total FROM curator_entry_state WHERE last_groomed_at IS NOT NULL', [now]) ?? { total: 0 }).total ?? 0;
786
244
  avgDaysSinceGroom = Math.round((totalSeconds / groomedEntries / 86400) * 100) / 100;
787
245
  }
788
-
789
- return {
790
- totalEntries,
791
- groomedEntries,
792
- ungroomedEntries,
793
- staleEntries,
794
- freshEntries,
795
- avgDaysSinceGroom,
796
- };
246
+ return { totalEntries, groomedEntries, ungroomedEntries: totalEntries - groomedEntries, staleEntries, freshEntries, avgDaysSinceGroom };
797
247
  }
798
248
 
799
- // ─── Metadata Enrichment ──────────────────────────────────────
249
+ // ─── Metadata Enrichment (delegates to metadata-enricher) ────
800
250
 
801
- enrichMetadata(entryId: string): {
802
- enriched: boolean;
803
- changes: Array<{ field: string; before: string; after: string }>;
804
- } {
251
+ enrichMetadata(entryId: string): { enriched: boolean; changes: Array<{ field: string; before: string; after: string }> } {
805
252
  const entry = this.vault.get(entryId);
806
253
  if (!entry) return { enriched: false, changes: [] };
807
-
808
- const changes: Array<{ field: string; before: string; after: string }> = [];
809
- const updates: Partial<
810
- Pick<IntelligenceEntry, 'title' | 'description' | 'tags' | 'severity' | 'type'>
811
- > = {};
812
-
813
- // Auto-capitalize title
814
- if (entry.title.length > 0 && entry.title[0] !== entry.title[0].toUpperCase()) {
815
- const capitalized = entry.title[0].toUpperCase() + entry.title.slice(1);
816
- changes.push({ field: 'title', before: entry.title, after: capitalized });
817
- updates.title = capitalized;
818
- }
819
-
820
- // Normalize tags: lowercase, trim, dedup
821
- const normalizedTags = [...new Set(entry.tags.map((t) => t.toLowerCase().trim()))];
822
- const tagsChanged =
823
- normalizedTags.length !== entry.tags.length ||
824
- normalizedTags.some((t, i) => t !== entry.tags[i]);
825
- if (tagsChanged) {
826
- changes.push({
827
- field: 'tags',
828
- before: JSON.stringify(entry.tags),
829
- after: JSON.stringify(normalizedTags),
830
- });
831
- updates.tags = normalizedTags;
832
- }
833
-
834
- // Infer severity from keywords if currently 'suggestion'
835
- if (entry.severity === 'suggestion') {
836
- const text = (entry.title + ' ' + entry.description).toLowerCase();
837
- const criticalKeywords = ['never', 'must not', 'critical', 'security', 'vulnerability'];
838
- const warningKeywords = ['avoid', 'should not', 'deprecated', 'careful', 'warning'];
839
- if (criticalKeywords.some((k) => text.includes(k))) {
840
- changes.push({ field: 'severity', before: entry.severity, after: 'critical' });
841
- updates.severity = 'critical';
842
- } else if (warningKeywords.some((k) => text.includes(k))) {
843
- changes.push({ field: 'severity', before: entry.severity, after: 'warning' });
844
- updates.severity = 'warning';
845
- }
846
- }
847
-
848
- // Infer type from title patterns
849
- if (entry.type === 'pattern') {
850
- const titleLower = entry.title.toLowerCase();
851
- if (
852
- titleLower.startsWith('avoid') ||
853
- titleLower.startsWith('never') ||
854
- titleLower.startsWith("don't") ||
855
- titleLower.startsWith('do not')
856
- ) {
857
- changes.push({ field: 'type', before: entry.type, after: 'anti-pattern' });
858
- updates.type = 'anti-pattern';
859
- }
860
- }
861
-
862
- // Trim whitespace from description
863
- const trimmed = entry.description.trim();
864
- if (trimmed !== entry.description) {
865
- changes.push({ field: 'description', before: entry.description, after: trimmed });
866
- updates.description = trimmed;
867
- }
868
-
869
- if (changes.length === 0) {
870
- return { enriched: false, changes: [] };
871
- }
872
-
873
- // Apply updates
254
+ const { changes, updates } = enrichEntryMetadata(entry);
255
+ if (changes.length === 0) return { enriched: false, changes: [] };
874
256
  this.vault.update(entryId, updates);
875
-
876
- // Record snapshot
877
257
  this.recordSnapshot(entryId, 'curator', 'Metadata enrichment');
878
-
879
- // Log change
880
- this.logChange(
881
- 'enrich_metadata',
882
- entryId,
883
- JSON.stringify(changes.map((c) => c.field)),
884
- JSON.stringify(changes.map((c) => c.after)),
885
- 'Rule-based metadata enrichment',
886
- );
887
-
258
+ this.logChange('enrich_metadata', entryId, JSON.stringify(changes.map((c) => c.field)), JSON.stringify(changes.map((c) => c.after)), 'Rule-based metadata enrichment');
888
259
  return { enriched: true, changes };
889
260
  }
890
261
 
891
- // ─── Private Helpers ────────────────────────────────────────────
262
+ // ─── Private Helpers ──────────────────────────────────────────
892
263
 
893
- private buildVocabulary(entries: IntelligenceEntry[]): Map<string, number> {
894
- const docCount = entries.length;
895
- const termDocFreq = new Map<string, number>();
896
- for (const entry of entries) {
897
- const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
898
- ' ',
899
- );
900
- const tokens = new Set(tokenize(text));
901
- for (const token of tokens) {
902
- termDocFreq.set(token, (termDocFreq.get(token) ?? 0) + 1);
264
+ private persistContradictions(candidates: ContradictionCandidate[]): Contradiction[] {
265
+ const detected: Contradiction[] = [];
266
+ for (const c of candidates) {
267
+ const result = this.provider.run('INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)', [c.patternId, c.antipatternId, c.similarity]);
268
+ if (result.changes > 0) {
269
+ const row = this.provider.get<Record<string, unknown>>('SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?', [c.patternId, c.antipatternId]);
270
+ if (row) detected.push(this.rowToContradiction(row));
903
271
  }
904
272
  }
905
- const vocabulary = new Map<string, number>();
906
- for (const [term, df] of termDocFreq) {
907
- const idf = Math.log((docCount + 1) / (df + 1)) + 1;
908
- vocabulary.set(term, idf);
909
- }
910
- return vocabulary;
273
+ return detected;
911
274
  }
912
275
 
913
- private logChange(
914
- action: string,
915
- entryId: string,
916
- beforeValue: string | null,
917
- afterValue: string | null,
918
- reason: string,
919
- ): void {
920
- this.provider.run(
921
- 'INSERT INTO curator_changelog (action, entry_id, before_value, after_value, reason) VALUES (?, ?, ?, ?, ?)',
922
- [action, entryId, beforeValue, afterValue, reason],
923
- );
276
+ private logChange(action: string, entryId: string, beforeValue: string | null, afterValue: string | null, reason: string): void {
277
+ this.provider.run('INSERT INTO curator_changelog (action, entry_id, before_value, after_value, reason) VALUES (?, ?, ?, ?, ?)', [action, entryId, beforeValue, afterValue, reason]);
924
278
  }
925
279
 
926
280
  private rowToContradiction(row: Record<string, unknown>): Contradiction {
927
- return {
928
- id: row.id as number,
929
- patternId: row.pattern_id as string,
930
- antipatternId: row.antipattern_id as string,
931
- similarity: row.similarity as number,
932
- status: row.status as ContradictionStatus,
933
- createdAt: row.created_at as number,
934
- resolvedAt: (row.resolved_at as number) ?? null,
935
- };
281
+ return { id: row.id as number, patternId: row.pattern_id as string, antipatternId: row.antipattern_id as string, similarity: row.similarity as number, status: row.status as ContradictionStatus, createdAt: row.created_at as number, resolvedAt: (row.resolved_at as number) ?? null };
936
282
  }
937
283
 
938
284
  private rowToChangelog(row: Record<string, unknown>): ChangelogEntry {
939
- return {
940
- id: row.id as number,
941
- action: row.action as string,
942
- entryId: row.entry_id as string,
943
- beforeValue: (row.before_value as string) ?? null,
944
- afterValue: (row.after_value as string) ?? null,
945
- reason: row.reason as string,
946
- createdAt: row.created_at as number,
947
- };
285
+ return { id: row.id as number, action: row.action as string, entryId: row.entry_id as string, beforeValue: (row.before_value as string) ?? null, afterValue: (row.after_value as string) ?? null, reason: row.reason as string, createdAt: row.created_at as number };
948
286
  }
949
287
  }