claude-brain 0.15.2 → 0.16.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 (249) hide show
  1. package/README.md +191 -191
  2. package/VERSION +1 -1
  3. package/assets/CLAUDE-unified.md +11 -11
  4. package/assets/CLAUDE.md +29 -11
  5. package/bunfig.toml +8 -8
  6. package/package.json +82 -82
  7. package/packs/backend/node.json +173 -173
  8. package/packs/core/javascript.json +176 -176
  9. package/packs/core/typescript.json +222 -222
  10. package/packs/frontend/react.json +254 -254
  11. package/packs/meta/testing.json +172 -172
  12. package/scripts/postinstall.mjs +341 -341
  13. package/src/automation/auto-context.ts +240 -240
  14. package/src/automation/decision-detector.ts +452 -452
  15. package/src/automation/index.ts +11 -11
  16. package/src/automation/phase12-manager.ts +456 -456
  17. package/src/automation/proactive-recall.ts +373 -373
  18. package/src/automation/project-detector.ts +310 -310
  19. package/src/automation/repo-scanner.ts +205 -205
  20. package/src/cli/auto-setup.ts +82 -82
  21. package/src/cli/bin.ts +209 -202
  22. package/src/cli/commands/chroma.ts +573 -573
  23. package/src/cli/commands/git-hook.ts +189 -189
  24. package/src/cli/commands/hooks.ts +213 -213
  25. package/src/cli/commands/init.ts +122 -122
  26. package/src/cli/commands/install-mcp.ts +92 -92
  27. package/src/cli/commands/pack.ts +197 -197
  28. package/src/cli/commands/refresh.ts +323 -0
  29. package/src/cli/commands/serve.ts +167 -173
  30. package/src/cli/commands/start.ts +42 -42
  31. package/src/cli/commands/uninstall-mcp.ts +41 -41
  32. package/src/cli/commands/update.ts +124 -121
  33. package/src/cli/diagnose.ts +4 -4
  34. package/src/cli/health-check.ts +4 -4
  35. package/src/cli/migrate-chroma.ts +106 -106
  36. package/src/cli/setup.ts +4 -4
  37. package/src/cli/ui/animations.ts +80 -80
  38. package/src/cli/ui/components.ts +82 -82
  39. package/src/cli/ui/index.ts +4 -4
  40. package/src/cli/ui/logo.ts +36 -36
  41. package/src/cli/ui/theme.ts +55 -55
  42. package/src/config/defaults.ts +50 -50
  43. package/src/config/home.ts +55 -55
  44. package/src/config/index.ts +7 -7
  45. package/src/config/loader.ts +166 -166
  46. package/src/config/migration.ts +76 -76
  47. package/src/config/schema.ts +360 -360
  48. package/src/config/validator.ts +184 -184
  49. package/src/config/watcher.ts +86 -86
  50. package/src/context/assembler.ts +398 -398
  51. package/src/context/cache-manager.ts +101 -101
  52. package/src/context/formatter.ts +84 -84
  53. package/src/context/hierarchy.ts +85 -85
  54. package/src/context/index.ts +83 -83
  55. package/src/context/progress-tracker.ts +174 -174
  56. package/src/context/standards-manager.ts +287 -287
  57. package/src/context/types.ts +252 -252
  58. package/src/context/validator.ts +58 -58
  59. package/src/diagnostics/index.ts +123 -123
  60. package/src/health/index.ts +229 -229
  61. package/src/hooks/brain-hook.ts +128 -112
  62. package/src/hooks/capture.ts +168 -205
  63. package/src/hooks/deduplicator.ts +72 -72
  64. package/src/hooks/git-capture.ts +109 -109
  65. package/src/hooks/git-hook-installer.ts +207 -207
  66. package/src/hooks/index.ts +20 -20
  67. package/src/hooks/installer.ts +194 -194
  68. package/src/hooks/passive-classifier.ts +404 -723
  69. package/src/hooks/queue.ts +129 -129
  70. package/src/hooks/session-tracker.ts +312 -275
  71. package/src/hooks/types.ts +47 -47
  72. package/src/index.ts +7 -7
  73. package/src/intelligence/cross-project/affinity.ts +162 -162
  74. package/src/intelligence/cross-project/generalizer.ts +283 -283
  75. package/src/intelligence/cross-project/index.ts +13 -13
  76. package/src/intelligence/cross-project/transfer.ts +201 -201
  77. package/src/intelligence/index.ts +24 -24
  78. package/src/intelligence/optimization/index.ts +10 -10
  79. package/src/intelligence/optimization/precompute.ts +202 -202
  80. package/src/intelligence/optimization/semantic-cache.ts +207 -207
  81. package/src/intelligence/prediction/context-anticipator.ts +198 -198
  82. package/src/intelligence/prediction/decision-predictor.ts +184 -184
  83. package/src/intelligence/prediction/index.ts +13 -13
  84. package/src/intelligence/prediction/recommender.ts +268 -268
  85. package/src/intelligence/reasoning/chain-retrieval.ts +247 -247
  86. package/src/intelligence/reasoning/counterfactual.ts +248 -248
  87. package/src/intelligence/reasoning/index.ts +13 -13
  88. package/src/intelligence/reasoning/synthesizer.ts +169 -169
  89. package/src/intelligence/temporal/evolution.ts +197 -197
  90. package/src/intelligence/temporal/index.ts +16 -16
  91. package/src/intelligence/temporal/query-processor.ts +190 -190
  92. package/src/intelligence/temporal/timeline.ts +259 -259
  93. package/src/intelligence/temporal/trends.ts +263 -263
  94. package/src/knowledge/entity-extractor.ts +416 -416
  95. package/src/knowledge/graph/builder.ts +185 -185
  96. package/src/knowledge/graph/linker.ts +201 -201
  97. package/src/knowledge/graph/memory-graph.ts +359 -359
  98. package/src/knowledge/graph/schema.ts +99 -99
  99. package/src/knowledge/graph/search.ts +168 -168
  100. package/src/knowledge/relationship-extractor.ts +108 -108
  101. package/src/memory/chroma/client.ts +174 -174
  102. package/src/memory/chroma/collection-manager.ts +94 -94
  103. package/src/memory/chroma/config.ts +57 -57
  104. package/src/memory/chroma/embeddings.ts +155 -155
  105. package/src/memory/chroma/index.ts +82 -82
  106. package/src/memory/chroma/migration.ts +270 -270
  107. package/src/memory/chroma/schemas.ts +69 -69
  108. package/src/memory/chroma/search.ts +315 -315
  109. package/src/memory/chroma/store.ts +741 -741
  110. package/src/memory/consolidation/archiver.ts +164 -164
  111. package/src/memory/consolidation/merger.ts +186 -186
  112. package/src/memory/consolidation/scorer.ts +138 -138
  113. package/src/memory/context-builder.ts +236 -236
  114. package/src/memory/database.ts +169 -169
  115. package/src/memory/embedding-utils.ts +156 -156
  116. package/src/memory/embeddings.ts +226 -226
  117. package/src/memory/episodic/detector.ts +108 -108
  118. package/src/memory/episodic/manager.ts +351 -351
  119. package/src/memory/episodic/summarizer.ts +179 -179
  120. package/src/memory/episodic/types.ts +52 -52
  121. package/src/memory/index.ts +582 -582
  122. package/src/memory/knowledge-extractor.ts +455 -455
  123. package/src/memory/learning.ts +378 -378
  124. package/src/memory/patterns.ts +396 -396
  125. package/src/memory/schema.ts +88 -88
  126. package/src/memory/search.ts +309 -309
  127. package/src/memory/store.ts +787 -787
  128. package/src/memory/types.ts +121 -121
  129. package/src/orchestrator/coordinator.ts +272 -272
  130. package/src/orchestrator/decision-logger.ts +228 -228
  131. package/src/orchestrator/event-emitter.ts +198 -198
  132. package/src/orchestrator/event-queue.ts +184 -184
  133. package/src/orchestrator/handlers/base-handler.ts +70 -70
  134. package/src/orchestrator/handlers/context-handler.ts +73 -73
  135. package/src/orchestrator/handlers/decision-handler.ts +204 -204
  136. package/src/orchestrator/handlers/index.ts +10 -10
  137. package/src/orchestrator/handlers/status-handler.ts +131 -131
  138. package/src/orchestrator/handlers/task-handler.ts +171 -171
  139. package/src/orchestrator/index.ts +275 -275
  140. package/src/orchestrator/task-parser.ts +284 -284
  141. package/src/orchestrator/types.ts +98 -98
  142. package/src/packs/index.ts +9 -9
  143. package/src/packs/loader.ts +134 -134
  144. package/src/packs/manager.ts +204 -204
  145. package/src/packs/ranker.ts +78 -78
  146. package/src/packs/types.ts +81 -81
  147. package/src/phase12/index.ts +5 -5
  148. package/src/retrieval/bm25/index.ts +300 -300
  149. package/src/retrieval/bm25/tokenizer.ts +184 -184
  150. package/src/retrieval/feedback/adaptive.ts +223 -223
  151. package/src/retrieval/feedback/index.ts +16 -16
  152. package/src/retrieval/feedback/metrics.ts +223 -223
  153. package/src/retrieval/feedback/store.ts +283 -283
  154. package/src/retrieval/fusion/index.ts +194 -194
  155. package/src/retrieval/fusion/rrf.ts +163 -163
  156. package/src/retrieval/index.ts +12 -12
  157. package/src/retrieval/pipeline.ts +375 -375
  158. package/src/retrieval/query/expander.ts +198 -198
  159. package/src/retrieval/query/index.ts +27 -27
  160. package/src/retrieval/query/intent-classifier.ts +236 -236
  161. package/src/retrieval/query/temporal-parser.ts +295 -295
  162. package/src/retrieval/reranker/index.ts +188 -188
  163. package/src/retrieval/reranker/model.ts +95 -95
  164. package/src/retrieval/service.ts +125 -125
  165. package/src/retrieval/types.ts +162 -162
  166. package/src/routing/entity-extractor.ts +428 -428
  167. package/src/routing/intent-classifier.ts +450 -436
  168. package/src/routing/response-filter.ts +261 -258
  169. package/src/routing/router.ts +1441 -1322
  170. package/src/routing/search-engine.ts +515 -475
  171. package/src/routing/types.ts +94 -94
  172. package/src/scripts/health-check.ts +118 -118
  173. package/src/scripts/setup.ts +122 -122
  174. package/src/server/handlers/call-tool.ts +156 -156
  175. package/src/server/handlers/index.ts +9 -9
  176. package/src/server/handlers/list-tools.ts +35 -35
  177. package/src/server/handlers/tools/analyze-decision-evolution.ts +151 -151
  178. package/src/server/handlers/tools/auto-remember.ts +200 -200
  179. package/src/server/handlers/tools/brain.ts +85 -85
  180. package/src/server/handlers/tools/create-project.ts +135 -135
  181. package/src/server/handlers/tools/detect-trends.ts +144 -144
  182. package/src/server/handlers/tools/find-cross-project-patterns.ts +168 -168
  183. package/src/server/handlers/tools/get-activity-log.ts +194 -194
  184. package/src/server/handlers/tools/get-code-standards.ts +124 -124
  185. package/src/server/handlers/tools/get-corrections.ts +154 -154
  186. package/src/server/handlers/tools/get-decision-timeline.ts +172 -172
  187. package/src/server/handlers/tools/get-episode.ts +103 -103
  188. package/src/server/handlers/tools/get-patterns.ts +158 -158
  189. package/src/server/handlers/tools/get-phase12-status.ts +63 -63
  190. package/src/server/handlers/tools/get-project-context.ts +75 -75
  191. package/src/server/handlers/tools/get-recommendations.ts +145 -145
  192. package/src/server/handlers/tools/index.ts +31 -31
  193. package/src/server/handlers/tools/init-project.ts +757 -757
  194. package/src/server/handlers/tools/list-episodes.ts +90 -90
  195. package/src/server/handlers/tools/list-projects.ts +125 -125
  196. package/src/server/handlers/tools/rate-memory.ts +101 -101
  197. package/src/server/handlers/tools/recall-similar.ts +87 -87
  198. package/src/server/handlers/tools/recognize-pattern.ts +126 -126
  199. package/src/server/handlers/tools/record-correction.ts +125 -125
  200. package/src/server/handlers/tools/remember-decision.ts +153 -153
  201. package/src/server/handlers/tools/schemas.ts +253 -253
  202. package/src/server/handlers/tools/search-knowledge-graph.ts +102 -102
  203. package/src/server/handlers/tools/smart-context.ts +146 -146
  204. package/src/server/handlers/tools/update-progress.ts +131 -131
  205. package/src/server/handlers/tools/what-if-analysis.ts +135 -135
  206. package/src/server/http-api.ts +693 -693
  207. package/src/server/index.ts +40 -40
  208. package/src/server/mcp-server.ts +283 -283
  209. package/src/server/providers/index.ts +7 -7
  210. package/src/server/providers/prompts.ts +327 -327
  211. package/src/server/providers/resources.ts +622 -622
  212. package/src/server/services.ts +468 -468
  213. package/src/server/types.ts +39 -39
  214. package/src/server/utils/error-handler.ts +155 -155
  215. package/src/server/utils/index.ts +13 -13
  216. package/src/server/utils/memory-indicator.ts +83 -83
  217. package/src/server/utils/request-context.ts +122 -122
  218. package/src/server/utils/response-formatter.ts +129 -129
  219. package/src/server/utils/validators.ts +210 -210
  220. package/src/setup/index.ts +48 -48
  221. package/src/setup/wizard.ts +461 -461
  222. package/src/tools/index.ts +24 -24
  223. package/src/tools/registry.ts +115 -115
  224. package/src/tools/schemas.test.ts +30 -30
  225. package/src/tools/schemas.ts +617 -617
  226. package/src/tools/types.ts +412 -412
  227. package/src/utils/circuit-breaker.ts +130 -130
  228. package/src/utils/cleanup.ts +34 -34
  229. package/src/utils/error-handler.ts +132 -132
  230. package/src/utils/error-messages.ts +60 -60
  231. package/src/utils/fallback.ts +45 -45
  232. package/src/utils/index.ts +54 -54
  233. package/src/utils/logger-utils.ts +80 -80
  234. package/src/utils/logger.ts +88 -88
  235. package/src/utils/phase12-helper.ts +56 -56
  236. package/src/utils/retry.ts +94 -94
  237. package/src/utils/timing.ts +47 -47
  238. package/src/utils/transaction.ts +63 -63
  239. package/src/vault/frontmatter.ts +264 -264
  240. package/src/vault/index.ts +318 -318
  241. package/src/vault/paths.ts +106 -106
  242. package/src/vault/query.ts +422 -422
  243. package/src/vault/reader.ts +264 -264
  244. package/src/vault/templates.ts +186 -186
  245. package/src/vault/types.ts +73 -73
  246. package/src/vault/watcher.ts +277 -277
  247. package/src/vault/writer.ts +413 -413
  248. package/tsconfig.json +30 -30
  249. package/src/cli/auto-update.ts +0 -157
@@ -1,582 +1,582 @@
1
- /**
2
- * Memory System - Main Module
3
- * Phase 3: Memory and Embedding System
4
- *
5
- * Unified memory system manager that combines all components
6
- */
7
-
8
- import type { Logger } from 'pino'
9
- import { MemoryDatabase } from './database'
10
- import { EmbeddingService } from './embeddings'
11
- import { MemoryStore } from './store'
12
- import { SemanticSearch } from './search'
13
- import { MemoryContextBuilder } from './context-builder'
14
- import type { MemorySystemStats } from './types'
15
- import { ChromaManager, DEFAULT_CHROMA_CONFIG, getChromaConfigFromEnv, ChromaMigration, type MigrationOptions } from './chroma'
16
-
17
- // Re-export all types and classes for external use
18
- export * from './types'
19
- export { MemoryDatabase } from './database'
20
- export { EmbeddingService } from './embeddings'
21
- export { MemoryStore } from './store'
22
- export { SemanticSearch } from './search'
23
- export { MemoryContextBuilder, type ContextOptions } from './context-builder'
24
- export {
25
- embeddingToBuffer,
26
- bufferToEmbedding,
27
- normalizeEmbedding,
28
- euclideanDistance,
29
- cosineSimilarity,
30
- dotProduct,
31
- magnitude,
32
- averageEmbeddings,
33
- topKSimilar
34
- } from './embedding-utils'
35
-
36
- // Phase 12: Advanced Memory Features
37
- export { PatternRecognizer, type Pattern } from './patterns'
38
- export { LearningSystem, type Correction, type Preference, type LearningInsights } from './learning'
39
- export { KnowledgeExtractor, type ExtractedKnowledge, type ExtractionResult } from './knowledge-extractor'
40
-
41
- /**
42
- * Unified memory system manager
43
- * Combines database, embeddings, store, search, and context building
44
- */
45
- export class MemoryManager {
46
- readonly database: MemoryDatabase
47
- readonly embeddings: EmbeddingService
48
- readonly contextBuilder: MemoryContextBuilder
49
- readonly chroma: ChromaManager
50
-
51
- // Store and search are initialized after database is ready
52
- private _store: MemoryStore | null = null
53
- private _search: SemanticSearch | null = null
54
-
55
- private logger: Logger
56
- private initialized: boolean = false
57
- private useChromaDB: boolean = true
58
- private onDecisionStoredCallbacks: ((input: any) => void)[] = []
59
- private onDecisionDeletedCallbacks: ((id: string) => void)[] = []
60
-
61
- constructor(
62
- dbPath: string,
63
- logger: Logger,
64
- useChromaDB: boolean = true,
65
- chromaConfig?: any,
66
- customEmbeddings?: EmbeddingService
67
- ) {
68
- this.logger = logger.child({ component: 'memory-manager' })
69
- this.useChromaDB = useChromaDB
70
-
71
- this.database = new MemoryDatabase(dbPath, logger)
72
- this.embeddings = customEmbeddings || new EmbeddingService(logger)
73
- this.contextBuilder = new MemoryContextBuilder(logger)
74
-
75
- const envConfig = getChromaConfigFromEnv()
76
- const config = { ...DEFAULT_CHROMA_CONFIG, ...envConfig, ...chromaConfig }
77
- this.chroma = new ChromaManager(logger, config)
78
- }
79
-
80
- /**
81
- * Initialize memory system
82
- * Must be called before using store or search
83
- */
84
- async initialize(): Promise<void> {
85
- if (this.initialized) {
86
- this.logger.warn('Memory system already initialized')
87
- return
88
- }
89
-
90
- try {
91
- this.logger.info('Initializing memory system...')
92
-
93
- await this.database.initialize()
94
-
95
- await this.embeddings.initialize()
96
-
97
- const db = this.database.getDb()
98
- this._store = new MemoryStore(db, this.embeddings, this.logger)
99
- this._search = new SemanticSearch(db, this.embeddings, this.logger)
100
-
101
- if (this.useChromaDB) {
102
- try {
103
- await this.chroma.initialize()
104
- this.logger.info('ChromaDB backend initialized successfully')
105
- } catch (error) {
106
- this.logger.warn({ error }, 'Failed to initialize ChromaDB, falling back to SQLite backend')
107
- this.useChromaDB = false
108
- }
109
- }
110
-
111
- this.initialized = true
112
- this.logger.info('Memory system initialized successfully')
113
- } catch (error) {
114
- this.logger.error({ error }, 'Failed to initialize memory system')
115
- throw error
116
- }
117
- }
118
-
119
- /**
120
- * Get the memory store (throws if not initialized)
121
- */
122
- get store(): MemoryStore {
123
- if (!this._store) {
124
- throw new Error('Memory system not initialized. Call initialize() first.')
125
- }
126
- return this._store
127
- }
128
-
129
- /**
130
- * Get the semantic search engine (throws if not initialized)
131
- */
132
- get search(): SemanticSearch {
133
- if (!this._search) {
134
- throw new Error('Memory system not initialized. Call initialize() first.')
135
- }
136
- return this._search
137
- }
138
-
139
- /**
140
- * Check if memory system is initialized
141
- */
142
- isInitialized(): boolean {
143
- return this.initialized
144
- }
145
-
146
- close(): void {
147
- if (this.useChromaDB) {
148
- this.chroma.close()
149
- }
150
- this.database.close()
151
- this._store = null
152
- this._search = null
153
- this.initialized = false
154
- this.logger.info('Memory system closed')
155
- }
156
-
157
- /**
158
- * Get system statistics
159
- */
160
- getStats(): MemorySystemStats {
161
- if (!this.initialized) {
162
- throw new Error('Memory system not initialized')
163
- }
164
-
165
- return {
166
- database: this.database.getStats(),
167
- embeddings: this.embeddings.getCacheStats()
168
- }
169
- }
170
-
171
- /**
172
- * Health check
173
- */
174
- async healthCheck(): Promise<boolean> {
175
- if (!this.initialized) {
176
- return false
177
- }
178
- return this.database.healthCheck()
179
- }
180
-
181
- /**
182
- * Check if ChromaDB backend is enabled and connected
183
- */
184
- isChromaDBEnabled(): boolean {
185
- return this.useChromaDB
186
- }
187
-
188
- /**
189
- * Add a listener that fires when a decision is stored (from any backend)
190
- */
191
- addDecisionStoredListener(callback: (input: any) => void): void {
192
- this.onDecisionStoredCallbacks.push(callback)
193
- // Also wire to ChromaDB listener if available
194
- if (this.useChromaDB) {
195
- this.chroma.store.addDecisionStoredListener(callback)
196
- }
197
- }
198
-
199
- /**
200
- * Add a listener that fires when a decision is deleted
201
- */
202
- addDecisionDeletedListener(callback: (id: string) => void): void {
203
- this.onDecisionDeletedCallbacks.push(callback)
204
- }
205
-
206
- async rememberDecision(
207
- project: string,
208
- context: string,
209
- decision: string,
210
- reasoning: string,
211
- options?: { alternatives?: string; tags?: string[] }
212
- ): Promise<string> {
213
- if (this.useChromaDB) {
214
- return this.chroma.store.storeDecision({
215
- project,
216
- context,
217
- decision,
218
- reasoning,
219
- alternatives: options?.alternatives,
220
- tags: options?.tags
221
- })
222
- }
223
- const id = await this.store.storeDecision({
224
- project,
225
- context,
226
- decision,
227
- reasoning,
228
- alternatives: options?.alternatives,
229
- tags: options?.tags
230
- })
231
- // Notify listeners (e.g., knowledge graph builder) for SQLite path
232
- for (const cb of this.onDecisionStoredCallbacks) {
233
- try {
234
- cb({ project, context, decision, reasoning, alternatives: options?.alternatives, tags: options?.tags, id })
235
- } catch {}
236
- }
237
- return id
238
- }
239
-
240
- /**
241
- * Get raw search results - routes to ChromaDB when enabled
242
- * Use this for internal operations that need raw results
243
- */
244
- async searchRaw(
245
- query: string,
246
- options?: { project?: string; limit?: number; minSimilarity?: number }
247
- ): Promise<any[]> {
248
- if (this.useChromaDB) {
249
- const chromaResults = await this.chroma.search.searchDecisions(query, {
250
- project: options?.project,
251
- limit: options?.limit || 5,
252
- minSimilarity: options?.minSimilarity || 0.5
253
- })
254
- // Transform ChromaDB results to match MemorySearchResult structure
255
- // Includes flat `content` field for direct access (Phase 19)
256
- return chromaResults.map(r => {
257
- const memoryContent = typeof r.content === 'string' ? r.content : JSON.stringify(r.content)
258
- const decisionObj = r.metadata.decision ? {
259
- id: r.id,
260
- project: r.metadata.project || options?.project || 'unknown',
261
- context: r.metadata.context || '',
262
- decision: r.metadata.decision || memoryContent,
263
- reasoning: r.metadata.reasoning || '',
264
- alternatives: r.metadata.alternatives_considered || '',
265
- tags: r.metadata.tags || [],
266
- outcome: r.metadata.outcome,
267
- createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date()
268
- } : undefined
269
-
270
- return {
271
- // Flat fields for direct access (Phase 19)
272
- id: r.id,
273
- content: decisionObj ? decisionObj.decision : memoryContent,
274
- // Nested fields for backward compatibility
275
- memory: {
276
- id: r.id,
277
- project: r.metadata.project || options?.project || 'unknown',
278
- content: memoryContent,
279
- createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date(),
280
- metadata: r.metadata
281
- },
282
- similarity: r.similarity,
283
- decision: decisionObj,
284
- metadata: r.metadata
285
- }
286
- })
287
- } else {
288
- return await this.search.search(query, {
289
- project: options?.project,
290
- limit: options?.limit || 5,
291
- minSimilarity: options?.minSimilarity || 0.5
292
- })
293
- }
294
- }
295
-
296
- async recallSimilar(
297
- query: string,
298
- options?: { project?: string; limit?: number; minSimilarity?: number }
299
- ): Promise<string> {
300
- const results = await this.searchRaw(query, options)
301
- return this.contextBuilder.buildDecisionContext(results)
302
- }
303
-
304
- async getRecommendations(
305
- currentContext: string,
306
- project: string,
307
- limit: number = 3
308
- ): Promise<string> {
309
- const results = await this.search.getRecommendations(currentContext, project, limit)
310
- return this.contextBuilder.buildRecommendationContext(results)
311
- }
312
-
313
- async migrateToChromaDB(options: MigrationOptions = {}): Promise<any> {
314
- if (!this.useChromaDB) {
315
- throw new Error('ChromaDB is not enabled')
316
- }
317
-
318
- const migration = new ChromaMigration(
319
- this.logger,
320
- this.database.getDb(),
321
- this.chroma.store,
322
- this.chroma.collections
323
- )
324
-
325
- return migration.migrate(options)
326
- }
327
-
328
- /**
329
- * Store a pattern in memory — routes to ChromaDB or SQLite
330
- */
331
- async storePattern(input: {
332
- project: string
333
- pattern_type: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
334
- description: string
335
- example?: string
336
- confidence: number
337
- context?: string
338
- source?: string
339
- }): Promise<string> {
340
- if (this.useChromaDB) {
341
- return this.chroma.store.storePattern(input)
342
- }
343
- return this.store.storePattern(input)
344
- }
345
-
346
- /**
347
- * Store a correction/lesson learned — routes to ChromaDB or SQLite
348
- */
349
- async storeCorrection(input: {
350
- project: string
351
- original: string
352
- correction: string
353
- reasoning: string
354
- context?: string
355
- confidence: number
356
- }): Promise<string> {
357
- if (this.useChromaDB) {
358
- return this.chroma.store.storeCorrection(input)
359
- }
360
- return this.store.storeCorrection(input)
361
- }
362
-
363
- /**
364
- * Get patterns for a project — routes to ChromaDB or SQLite
365
- */
366
- async getPatterns(
367
- project?: string,
368
- options?: {
369
- pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
370
- limit?: number
371
- }
372
- ): Promise<any[]> {
373
- if (this.useChromaDB) {
374
- if (project) {
375
- return this.chroma.store.getPatternsByProject(project, options)
376
- }
377
- return this.chroma.store.searchPatterns('', { limit: options?.limit || 10 })
378
- }
379
- if (project) {
380
- return this.store.getPatternsByProject(project, options)
381
- }
382
- return this.store.searchPatterns('', { limit: options?.limit || 10 })
383
- }
384
-
385
- /**
386
- * Get corrections for a project — routes to ChromaDB or SQLite
387
- */
388
- async getCorrections(
389
- project?: string,
390
- options?: { limit?: number }
391
- ): Promise<any[]> {
392
- if (this.useChromaDB) {
393
- if (project) {
394
- return this.chroma.store.getCorrectionsByProject(project, options?.limit || 10)
395
- }
396
- return this.chroma.store.searchCorrections('', { limit: options?.limit || 10 })
397
- }
398
- if (project) {
399
- return this.store.getCorrectionsByProject(project, options?.limit || 10)
400
- }
401
- return this.store.searchCorrections('', { limit: options?.limit || 10 })
402
- }
403
-
404
- /**
405
- * Fetch all decisions with content — routes to ChromaDB or SQLite
406
- * Used by analytical tools that need bulk access to decision data
407
- */
408
- async fetchAllDecisions(project?: string): Promise<any[]> {
409
- if (this.useChromaDB) {
410
- try {
411
- const collection = await this.chroma.collections.getDecisions()
412
- const results = await collection.get({
413
- where: project ? { project } : undefined
414
- })
415
- if (results && results.ids) {
416
- return results.ids.map((id: string, i: number) => ({
417
- id,
418
- content: results.documents?.[i] || '',
419
- date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
420
- project: results.metadatas?.[i]?.project || project || 'unknown',
421
- context: results.metadatas?.[i]?.context || '',
422
- decision: results.metadatas?.[i]?.decision || results.documents?.[i] || '',
423
- reasoning: results.metadatas?.[i]?.reasoning || '',
424
- alternatives: results.metadatas?.[i]?.alternatives_considered || '',
425
- tags: results.metadatas?.[i]?.tags || []
426
- }))
427
- }
428
- return []
429
- } catch (error) {
430
- this.logger.warn({ error }, 'ChromaDB fetchAllDecisions failed, falling back to SQLite')
431
- }
432
- }
433
- return this.store.getAllDecisionsWithContent(project)
434
- }
435
-
436
- /**
437
- * Fetch all patterns with content — routes to ChromaDB or SQLite
438
- */
439
- async fetchAllPatterns(project?: string): Promise<any[]> {
440
- if (this.useChromaDB) {
441
- try {
442
- const collection = await this.chroma.collections.getPatterns()
443
- const results = await collection.get({
444
- where: project ? { project } : undefined
445
- })
446
- if (results && results.ids) {
447
- return results.ids.map((id: string, i: number) => ({
448
- id,
449
- content: results.documents?.[i] || '',
450
- date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
451
- project: results.metadatas?.[i]?.project || project || 'unknown',
452
- pattern_type: results.metadatas?.[i]?.pattern_type || '',
453
- description: results.metadatas?.[i]?.description || results.documents?.[i] || '',
454
- example: results.metadatas?.[i]?.example || '',
455
- confidence: results.metadatas?.[i]?.confidence || 0,
456
- context: results.metadatas?.[i]?.context || ''
457
- }))
458
- }
459
- return []
460
- } catch (error) {
461
- this.logger.warn({ error }, 'ChromaDB fetchAllPatterns failed, falling back to SQLite')
462
- }
463
- }
464
- return this.store.getAllPatternsWithContent(project)
465
- }
466
-
467
- /**
468
- * Fetch all corrections with content — routes to ChromaDB or SQLite
469
- */
470
- async fetchAllCorrections(project?: string): Promise<any[]> {
471
- if (this.useChromaDB) {
472
- try {
473
- const collection = await this.chroma.collections.getCorrections()
474
- const results = await collection.get({
475
- where: project ? { project } : undefined
476
- })
477
- if (results && results.ids) {
478
- return results.ids.map((id: string, i: number) => ({
479
- id,
480
- content: results.documents?.[i] || '',
481
- date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
482
- project: results.metadatas?.[i]?.project || project || 'unknown',
483
- original: results.metadatas?.[i]?.original || '',
484
- correction: results.metadatas?.[i]?.correction || results.documents?.[i] || '',
485
- reasoning: results.metadatas?.[i]?.reasoning || '',
486
- context: results.metadatas?.[i]?.context || '',
487
- confidence: results.metadatas?.[i]?.confidence || 0
488
- }))
489
- }
490
- return []
491
- } catch (error) {
492
- this.logger.warn({ error }, 'ChromaDB fetchAllCorrections failed, falling back to SQLite')
493
- }
494
- }
495
- return this.store.getAllCorrectionsWithContent(project)
496
- }
497
-
498
- /**
499
- * Search patterns by query — routes to ChromaDB or SQLite
500
- */
501
- async searchPatterns(
502
- query: string,
503
- options?: {
504
- project?: string
505
- pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
506
- limit?: number
507
- minSimilarity?: number
508
- }
509
- ): Promise<any[]> {
510
- if (this.useChromaDB) {
511
- return this.chroma.store.searchPatterns(query, options)
512
- }
513
- return this.store.searchPatterns(query, options)
514
- }
515
-
516
- /**
517
- * Search corrections by query — routes to ChromaDB or SQLite
518
- */
519
- async searchCorrections(
520
- query: string,
521
- options?: {
522
- project?: string
523
- limit?: number
524
- minSimilarity?: number
525
- }
526
- ): Promise<any[]> {
527
- if (this.useChromaDB) {
528
- return this.chroma.store.searchCorrections(query, options)
529
- }
530
- return this.store.searchCorrections(query, options)
531
- }
532
-
533
- /**
534
- * Delete a decision by ID — routes to ChromaDB or SQLite
535
- */
536
- async deleteDecision(id: string): Promise<void> {
537
- if (this.useChromaDB) {
538
- await this.chroma.store.deleteDecision(id)
539
- } else {
540
- this.store.deleteMemory(id)
541
- }
542
-
543
- // Notify listeners (e.g., knowledge graph builder) about deletion
544
- for (const cb of this.onDecisionDeletedCallbacks) {
545
- try {
546
- cb(id)
547
- } catch {}
548
- }
549
- }
550
-
551
- /**
552
- * Update a decision by deleting the old one and storing a new version.
553
- * Phase 20: Ensures both ChromaDB and knowledge graph are atomically updated.
554
- */
555
- async updateDecision(
556
- oldId: string,
557
- project: string,
558
- context: string,
559
- decision: string,
560
- reasoning: string,
561
- options?: { alternatives?: string; tags?: string[] }
562
- ): Promise<string> {
563
- // Delete old version — fires onDecisionDeletedCallbacks (graph + episode cleanup)
564
- try {
565
- await this.deleteDecision(oldId)
566
- this.logger.debug({ oldId }, 'Old decision deleted for update')
567
- } catch (error) {
568
- this.logger.warn({ error, oldId }, 'Failed to delete old decision during update, storing new version anyway')
569
- }
570
- // Store new version — fires onDecisionStoredCallbacks (graph rebuild)
571
- const newId = await this.rememberDecision(project, context, decision, reasoning, options)
572
- this.logger.debug({ oldId, newId }, 'Decision updated: old deleted, new stored')
573
- return newId
574
- }
575
- }
576
-
577
- /**
578
- * Create a memory manager instance
579
- */
580
- export function createMemoryManager(dbPath: string, logger: Logger): MemoryManager {
581
- return new MemoryManager(dbPath, logger)
582
- }
1
+ /**
2
+ * Memory System - Main Module
3
+ * Phase 3: Memory and Embedding System
4
+ *
5
+ * Unified memory system manager that combines all components
6
+ */
7
+
8
+ import type { Logger } from 'pino'
9
+ import { MemoryDatabase } from './database'
10
+ import { EmbeddingService } from './embeddings'
11
+ import { MemoryStore } from './store'
12
+ import { SemanticSearch } from './search'
13
+ import { MemoryContextBuilder } from './context-builder'
14
+ import type { MemorySystemStats } from './types'
15
+ import { ChromaManager, DEFAULT_CHROMA_CONFIG, getChromaConfigFromEnv, ChromaMigration, type MigrationOptions } from './chroma'
16
+
17
+ // Re-export all types and classes for external use
18
+ export * from './types'
19
+ export { MemoryDatabase } from './database'
20
+ export { EmbeddingService } from './embeddings'
21
+ export { MemoryStore } from './store'
22
+ export { SemanticSearch } from './search'
23
+ export { MemoryContextBuilder, type ContextOptions } from './context-builder'
24
+ export {
25
+ embeddingToBuffer,
26
+ bufferToEmbedding,
27
+ normalizeEmbedding,
28
+ euclideanDistance,
29
+ cosineSimilarity,
30
+ dotProduct,
31
+ magnitude,
32
+ averageEmbeddings,
33
+ topKSimilar
34
+ } from './embedding-utils'
35
+
36
+ // Phase 12: Advanced Memory Features
37
+ export { PatternRecognizer, type Pattern } from './patterns'
38
+ export { LearningSystem, type Correction, type Preference, type LearningInsights } from './learning'
39
+ export { KnowledgeExtractor, type ExtractedKnowledge, type ExtractionResult } from './knowledge-extractor'
40
+
41
+ /**
42
+ * Unified memory system manager
43
+ * Combines database, embeddings, store, search, and context building
44
+ */
45
+ export class MemoryManager {
46
+ readonly database: MemoryDatabase
47
+ readonly embeddings: EmbeddingService
48
+ readonly contextBuilder: MemoryContextBuilder
49
+ readonly chroma: ChromaManager
50
+
51
+ // Store and search are initialized after database is ready
52
+ private _store: MemoryStore | null = null
53
+ private _search: SemanticSearch | null = null
54
+
55
+ private logger: Logger
56
+ private initialized: boolean = false
57
+ private useChromaDB: boolean = true
58
+ private onDecisionStoredCallbacks: ((input: any) => void)[] = []
59
+ private onDecisionDeletedCallbacks: ((id: string) => void)[] = []
60
+
61
+ constructor(
62
+ dbPath: string,
63
+ logger: Logger,
64
+ useChromaDB: boolean = true,
65
+ chromaConfig?: any,
66
+ customEmbeddings?: EmbeddingService
67
+ ) {
68
+ this.logger = logger.child({ component: 'memory-manager' })
69
+ this.useChromaDB = useChromaDB
70
+
71
+ this.database = new MemoryDatabase(dbPath, logger)
72
+ this.embeddings = customEmbeddings || new EmbeddingService(logger)
73
+ this.contextBuilder = new MemoryContextBuilder(logger)
74
+
75
+ const envConfig = getChromaConfigFromEnv()
76
+ const config = { ...DEFAULT_CHROMA_CONFIG, ...envConfig, ...chromaConfig }
77
+ this.chroma = new ChromaManager(logger, config)
78
+ }
79
+
80
+ /**
81
+ * Initialize memory system
82
+ * Must be called before using store or search
83
+ */
84
+ async initialize(): Promise<void> {
85
+ if (this.initialized) {
86
+ this.logger.warn('Memory system already initialized')
87
+ return
88
+ }
89
+
90
+ try {
91
+ this.logger.info('Initializing memory system...')
92
+
93
+ await this.database.initialize()
94
+
95
+ await this.embeddings.initialize()
96
+
97
+ const db = this.database.getDb()
98
+ this._store = new MemoryStore(db, this.embeddings, this.logger)
99
+ this._search = new SemanticSearch(db, this.embeddings, this.logger)
100
+
101
+ if (this.useChromaDB) {
102
+ try {
103
+ await this.chroma.initialize()
104
+ this.logger.info('ChromaDB backend initialized successfully')
105
+ } catch (error) {
106
+ this.logger.warn({ error }, 'Failed to initialize ChromaDB, falling back to SQLite backend')
107
+ this.useChromaDB = false
108
+ }
109
+ }
110
+
111
+ this.initialized = true
112
+ this.logger.info('Memory system initialized successfully')
113
+ } catch (error) {
114
+ this.logger.error({ error }, 'Failed to initialize memory system')
115
+ throw error
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Get the memory store (throws if not initialized)
121
+ */
122
+ get store(): MemoryStore {
123
+ if (!this._store) {
124
+ throw new Error('Memory system not initialized. Call initialize() first.')
125
+ }
126
+ return this._store
127
+ }
128
+
129
+ /**
130
+ * Get the semantic search engine (throws if not initialized)
131
+ */
132
+ get search(): SemanticSearch {
133
+ if (!this._search) {
134
+ throw new Error('Memory system not initialized. Call initialize() first.')
135
+ }
136
+ return this._search
137
+ }
138
+
139
+ /**
140
+ * Check if memory system is initialized
141
+ */
142
+ isInitialized(): boolean {
143
+ return this.initialized
144
+ }
145
+
146
+ close(): void {
147
+ if (this.useChromaDB) {
148
+ this.chroma.close()
149
+ }
150
+ this.database.close()
151
+ this._store = null
152
+ this._search = null
153
+ this.initialized = false
154
+ this.logger.info('Memory system closed')
155
+ }
156
+
157
+ /**
158
+ * Get system statistics
159
+ */
160
+ getStats(): MemorySystemStats {
161
+ if (!this.initialized) {
162
+ throw new Error('Memory system not initialized')
163
+ }
164
+
165
+ return {
166
+ database: this.database.getStats(),
167
+ embeddings: this.embeddings.getCacheStats()
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Health check
173
+ */
174
+ async healthCheck(): Promise<boolean> {
175
+ if (!this.initialized) {
176
+ return false
177
+ }
178
+ return this.database.healthCheck()
179
+ }
180
+
181
+ /**
182
+ * Check if ChromaDB backend is enabled and connected
183
+ */
184
+ isChromaDBEnabled(): boolean {
185
+ return this.useChromaDB
186
+ }
187
+
188
+ /**
189
+ * Add a listener that fires when a decision is stored (from any backend)
190
+ */
191
+ addDecisionStoredListener(callback: (input: any) => void): void {
192
+ this.onDecisionStoredCallbacks.push(callback)
193
+ // Also wire to ChromaDB listener if available
194
+ if (this.useChromaDB) {
195
+ this.chroma.store.addDecisionStoredListener(callback)
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Add a listener that fires when a decision is deleted
201
+ */
202
+ addDecisionDeletedListener(callback: (id: string) => void): void {
203
+ this.onDecisionDeletedCallbacks.push(callback)
204
+ }
205
+
206
+ async rememberDecision(
207
+ project: string,
208
+ context: string,
209
+ decision: string,
210
+ reasoning: string,
211
+ options?: { alternatives?: string; tags?: string[] }
212
+ ): Promise<string> {
213
+ if (this.useChromaDB) {
214
+ return this.chroma.store.storeDecision({
215
+ project,
216
+ context,
217
+ decision,
218
+ reasoning,
219
+ alternatives: options?.alternatives,
220
+ tags: options?.tags
221
+ })
222
+ }
223
+ const id = await this.store.storeDecision({
224
+ project,
225
+ context,
226
+ decision,
227
+ reasoning,
228
+ alternatives: options?.alternatives,
229
+ tags: options?.tags
230
+ })
231
+ // Notify listeners (e.g., knowledge graph builder) for SQLite path
232
+ for (const cb of this.onDecisionStoredCallbacks) {
233
+ try {
234
+ cb({ project, context, decision, reasoning, alternatives: options?.alternatives, tags: options?.tags, id })
235
+ } catch {}
236
+ }
237
+ return id
238
+ }
239
+
240
+ /**
241
+ * Get raw search results - routes to ChromaDB when enabled
242
+ * Use this for internal operations that need raw results
243
+ */
244
+ async searchRaw(
245
+ query: string,
246
+ options?: { project?: string; limit?: number; minSimilarity?: number }
247
+ ): Promise<any[]> {
248
+ if (this.useChromaDB) {
249
+ const chromaResults = await this.chroma.search.searchDecisions(query, {
250
+ project: options?.project,
251
+ limit: options?.limit || 5,
252
+ minSimilarity: options?.minSimilarity || 0.5
253
+ })
254
+ // Transform ChromaDB results to match MemorySearchResult structure
255
+ // Includes flat `content` field for direct access (Phase 19)
256
+ return chromaResults.map(r => {
257
+ const memoryContent = typeof r.content === 'string' ? r.content : JSON.stringify(r.content)
258
+ const decisionObj = r.metadata.decision ? {
259
+ id: r.id,
260
+ project: r.metadata.project || options?.project || 'unknown',
261
+ context: r.metadata.context || '',
262
+ decision: r.metadata.decision || memoryContent,
263
+ reasoning: r.metadata.reasoning || '',
264
+ alternatives: r.metadata.alternatives_considered || '',
265
+ tags: r.metadata.tags || [],
266
+ outcome: r.metadata.outcome,
267
+ createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date()
268
+ } : undefined
269
+
270
+ return {
271
+ // Flat fields for direct access (Phase 19)
272
+ id: r.id,
273
+ content: decisionObj ? decisionObj.decision : memoryContent,
274
+ // Nested fields for backward compatibility
275
+ memory: {
276
+ id: r.id,
277
+ project: r.metadata.project || options?.project || 'unknown',
278
+ content: memoryContent,
279
+ createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date(),
280
+ metadata: r.metadata
281
+ },
282
+ similarity: r.similarity,
283
+ decision: decisionObj,
284
+ metadata: r.metadata
285
+ }
286
+ })
287
+ } else {
288
+ return await this.search.search(query, {
289
+ project: options?.project,
290
+ limit: options?.limit || 5,
291
+ minSimilarity: options?.minSimilarity || 0.5
292
+ })
293
+ }
294
+ }
295
+
296
+ async recallSimilar(
297
+ query: string,
298
+ options?: { project?: string; limit?: number; minSimilarity?: number }
299
+ ): Promise<string> {
300
+ const results = await this.searchRaw(query, options)
301
+ return this.contextBuilder.buildDecisionContext(results)
302
+ }
303
+
304
+ async getRecommendations(
305
+ currentContext: string,
306
+ project: string,
307
+ limit: number = 3
308
+ ): Promise<string> {
309
+ const results = await this.search.getRecommendations(currentContext, project, limit)
310
+ return this.contextBuilder.buildRecommendationContext(results)
311
+ }
312
+
313
+ async migrateToChromaDB(options: MigrationOptions = {}): Promise<any> {
314
+ if (!this.useChromaDB) {
315
+ throw new Error('ChromaDB is not enabled')
316
+ }
317
+
318
+ const migration = new ChromaMigration(
319
+ this.logger,
320
+ this.database.getDb(),
321
+ this.chroma.store,
322
+ this.chroma.collections
323
+ )
324
+
325
+ return migration.migrate(options)
326
+ }
327
+
328
+ /**
329
+ * Store a pattern in memory — routes to ChromaDB or SQLite
330
+ */
331
+ async storePattern(input: {
332
+ project: string
333
+ pattern_type: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
334
+ description: string
335
+ example?: string
336
+ confidence: number
337
+ context?: string
338
+ source?: string
339
+ }): Promise<string> {
340
+ if (this.useChromaDB) {
341
+ return this.chroma.store.storePattern(input)
342
+ }
343
+ return this.store.storePattern(input)
344
+ }
345
+
346
+ /**
347
+ * Store a correction/lesson learned — routes to ChromaDB or SQLite
348
+ */
349
+ async storeCorrection(input: {
350
+ project: string
351
+ original: string
352
+ correction: string
353
+ reasoning: string
354
+ context?: string
355
+ confidence: number
356
+ }): Promise<string> {
357
+ if (this.useChromaDB) {
358
+ return this.chroma.store.storeCorrection(input)
359
+ }
360
+ return this.store.storeCorrection(input)
361
+ }
362
+
363
+ /**
364
+ * Get patterns for a project — routes to ChromaDB or SQLite
365
+ */
366
+ async getPatterns(
367
+ project?: string,
368
+ options?: {
369
+ pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
370
+ limit?: number
371
+ }
372
+ ): Promise<any[]> {
373
+ if (this.useChromaDB) {
374
+ if (project) {
375
+ return this.chroma.store.getPatternsByProject(project, options)
376
+ }
377
+ return this.chroma.store.searchPatterns('', { limit: options?.limit || 10 })
378
+ }
379
+ if (project) {
380
+ return this.store.getPatternsByProject(project, options)
381
+ }
382
+ return this.store.searchPatterns('', { limit: options?.limit || 10 })
383
+ }
384
+
385
+ /**
386
+ * Get corrections for a project — routes to ChromaDB or SQLite
387
+ */
388
+ async getCorrections(
389
+ project?: string,
390
+ options?: { limit?: number }
391
+ ): Promise<any[]> {
392
+ if (this.useChromaDB) {
393
+ if (project) {
394
+ return this.chroma.store.getCorrectionsByProject(project, options?.limit || 10)
395
+ }
396
+ return this.chroma.store.searchCorrections('', { limit: options?.limit || 10 })
397
+ }
398
+ if (project) {
399
+ return this.store.getCorrectionsByProject(project, options?.limit || 10)
400
+ }
401
+ return this.store.searchCorrections('', { limit: options?.limit || 10 })
402
+ }
403
+
404
+ /**
405
+ * Fetch all decisions with content — routes to ChromaDB or SQLite
406
+ * Used by analytical tools that need bulk access to decision data
407
+ */
408
+ async fetchAllDecisions(project?: string): Promise<any[]> {
409
+ if (this.useChromaDB) {
410
+ try {
411
+ const collection = await this.chroma.collections.getDecisions()
412
+ const results = await collection.get({
413
+ where: project ? { project } : undefined
414
+ })
415
+ if (results && results.ids) {
416
+ return results.ids.map((id: string, i: number) => ({
417
+ id,
418
+ content: results.documents?.[i] || '',
419
+ date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
420
+ project: results.metadatas?.[i]?.project || project || 'unknown',
421
+ context: results.metadatas?.[i]?.context || '',
422
+ decision: results.metadatas?.[i]?.decision || results.documents?.[i] || '',
423
+ reasoning: results.metadatas?.[i]?.reasoning || '',
424
+ alternatives: results.metadatas?.[i]?.alternatives_considered || '',
425
+ tags: results.metadatas?.[i]?.tags || []
426
+ }))
427
+ }
428
+ return []
429
+ } catch (error) {
430
+ this.logger.warn({ error }, 'ChromaDB fetchAllDecisions failed, falling back to SQLite')
431
+ }
432
+ }
433
+ return this.store.getAllDecisionsWithContent(project)
434
+ }
435
+
436
+ /**
437
+ * Fetch all patterns with content — routes to ChromaDB or SQLite
438
+ */
439
+ async fetchAllPatterns(project?: string): Promise<any[]> {
440
+ if (this.useChromaDB) {
441
+ try {
442
+ const collection = await this.chroma.collections.getPatterns()
443
+ const results = await collection.get({
444
+ where: project ? { project } : undefined
445
+ })
446
+ if (results && results.ids) {
447
+ return results.ids.map((id: string, i: number) => ({
448
+ id,
449
+ content: results.documents?.[i] || '',
450
+ date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
451
+ project: results.metadatas?.[i]?.project || project || 'unknown',
452
+ pattern_type: results.metadatas?.[i]?.pattern_type || '',
453
+ description: results.metadatas?.[i]?.description || results.documents?.[i] || '',
454
+ example: results.metadatas?.[i]?.example || '',
455
+ confidence: results.metadatas?.[i]?.confidence || 0,
456
+ context: results.metadatas?.[i]?.context || ''
457
+ }))
458
+ }
459
+ return []
460
+ } catch (error) {
461
+ this.logger.warn({ error }, 'ChromaDB fetchAllPatterns failed, falling back to SQLite')
462
+ }
463
+ }
464
+ return this.store.getAllPatternsWithContent(project)
465
+ }
466
+
467
+ /**
468
+ * Fetch all corrections with content — routes to ChromaDB or SQLite
469
+ */
470
+ async fetchAllCorrections(project?: string): Promise<any[]> {
471
+ if (this.useChromaDB) {
472
+ try {
473
+ const collection = await this.chroma.collections.getCorrections()
474
+ const results = await collection.get({
475
+ where: project ? { project } : undefined
476
+ })
477
+ if (results && results.ids) {
478
+ return results.ids.map((id: string, i: number) => ({
479
+ id,
480
+ content: results.documents?.[i] || '',
481
+ date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
482
+ project: results.metadatas?.[i]?.project || project || 'unknown',
483
+ original: results.metadatas?.[i]?.original || '',
484
+ correction: results.metadatas?.[i]?.correction || results.documents?.[i] || '',
485
+ reasoning: results.metadatas?.[i]?.reasoning || '',
486
+ context: results.metadatas?.[i]?.context || '',
487
+ confidence: results.metadatas?.[i]?.confidence || 0
488
+ }))
489
+ }
490
+ return []
491
+ } catch (error) {
492
+ this.logger.warn({ error }, 'ChromaDB fetchAllCorrections failed, falling back to SQLite')
493
+ }
494
+ }
495
+ return this.store.getAllCorrectionsWithContent(project)
496
+ }
497
+
498
+ /**
499
+ * Search patterns by query — routes to ChromaDB or SQLite
500
+ */
501
+ async searchPatterns(
502
+ query: string,
503
+ options?: {
504
+ project?: string
505
+ pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
506
+ limit?: number
507
+ minSimilarity?: number
508
+ }
509
+ ): Promise<any[]> {
510
+ if (this.useChromaDB) {
511
+ return this.chroma.store.searchPatterns(query, options)
512
+ }
513
+ return this.store.searchPatterns(query, options)
514
+ }
515
+
516
+ /**
517
+ * Search corrections by query — routes to ChromaDB or SQLite
518
+ */
519
+ async searchCorrections(
520
+ query: string,
521
+ options?: {
522
+ project?: string
523
+ limit?: number
524
+ minSimilarity?: number
525
+ }
526
+ ): Promise<any[]> {
527
+ if (this.useChromaDB) {
528
+ return this.chroma.store.searchCorrections(query, options)
529
+ }
530
+ return this.store.searchCorrections(query, options)
531
+ }
532
+
533
+ /**
534
+ * Delete a decision by ID — routes to ChromaDB or SQLite
535
+ */
536
+ async deleteDecision(id: string): Promise<void> {
537
+ if (this.useChromaDB) {
538
+ await this.chroma.store.deleteDecision(id)
539
+ } else {
540
+ this.store.deleteMemory(id)
541
+ }
542
+
543
+ // Notify listeners (e.g., knowledge graph builder) about deletion
544
+ for (const cb of this.onDecisionDeletedCallbacks) {
545
+ try {
546
+ cb(id)
547
+ } catch {}
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Update a decision by deleting the old one and storing a new version.
553
+ * Phase 20: Ensures both ChromaDB and knowledge graph are atomically updated.
554
+ */
555
+ async updateDecision(
556
+ oldId: string,
557
+ project: string,
558
+ context: string,
559
+ decision: string,
560
+ reasoning: string,
561
+ options?: { alternatives?: string; tags?: string[] }
562
+ ): Promise<string> {
563
+ // Delete old version — fires onDecisionDeletedCallbacks (graph + episode cleanup)
564
+ try {
565
+ await this.deleteDecision(oldId)
566
+ this.logger.debug({ oldId }, 'Old decision deleted for update')
567
+ } catch (error) {
568
+ this.logger.warn({ error, oldId }, 'Failed to delete old decision during update, storing new version anyway')
569
+ }
570
+ // Store new version — fires onDecisionStoredCallbacks (graph rebuild)
571
+ const newId = await this.rememberDecision(project, context, decision, reasoning, options)
572
+ this.logger.debug({ oldId, newId }, 'Decision updated: old deleted, new stored')
573
+ return newId
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Create a memory manager instance
579
+ */
580
+ export function createMemoryManager(dbPath: string, logger: Logger): MemoryManager {
581
+ return new MemoryManager(dbPath, logger)
582
+ }