brainbank 0.1.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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1059 -0
  3. package/assets/architecture.png +0 -0
  4. package/bin/brainbank +11 -0
  5. package/dist/chunk-2P3EGY6S.js +37 -0
  6. package/dist/chunk-2P3EGY6S.js.map +1 -0
  7. package/dist/chunk-3GAIDXRW.js +105 -0
  8. package/dist/chunk-3GAIDXRW.js.map +1 -0
  9. package/dist/chunk-4ZKBQ33J.js +56 -0
  10. package/dist/chunk-4ZKBQ33J.js.map +1 -0
  11. package/dist/chunk-7QVYU63E.js +7 -0
  12. package/dist/chunk-7QVYU63E.js.map +1 -0
  13. package/dist/chunk-EDKSKLX4.js +490 -0
  14. package/dist/chunk-EDKSKLX4.js.map +1 -0
  15. package/dist/chunk-GOUBW7UA.js +373 -0
  16. package/dist/chunk-GOUBW7UA.js.map +1 -0
  17. package/dist/chunk-MJ3Y24H6.js +185 -0
  18. package/dist/chunk-MJ3Y24H6.js.map +1 -0
  19. package/dist/chunk-N6ZMBFDE.js +224 -0
  20. package/dist/chunk-N6ZMBFDE.js.map +1 -0
  21. package/dist/chunk-YGSEUWLV.js +2053 -0
  22. package/dist/chunk-YGSEUWLV.js.map +1 -0
  23. package/dist/chunk-Z5SU54HP.js +171 -0
  24. package/dist/chunk-Z5SU54HP.js.map +1 -0
  25. package/dist/cli.d.ts +1 -0
  26. package/dist/cli.js +731 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/code.d.ts +31 -0
  29. package/dist/code.js +8 -0
  30. package/dist/code.js.map +1 -0
  31. package/dist/docs.d.ts +19 -0
  32. package/dist/docs.js +8 -0
  33. package/dist/docs.js.map +1 -0
  34. package/dist/git.d.ts +31 -0
  35. package/dist/git.js +8 -0
  36. package/dist/git.js.map +1 -0
  37. package/dist/index.d.ts +845 -0
  38. package/dist/index.js +80 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/memory.d.ts +19 -0
  41. package/dist/memory.js +146 -0
  42. package/dist/memory.js.map +1 -0
  43. package/dist/notes.d.ts +19 -0
  44. package/dist/notes.js +57 -0
  45. package/dist/notes.js.map +1 -0
  46. package/dist/openai-PCTYLOWI.js +8 -0
  47. package/dist/openai-PCTYLOWI.js.map +1 -0
  48. package/dist/types-Da_zLLOl.d.ts +474 -0
  49. package/package.json +91 -0
@@ -0,0 +1,845 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { P as ProgressCallback, a as BrainBankConfig, I as Indexer, C as Collection, b as IndexResult, D as DocumentCollection, S as SearchResult, c as ContextOptions, d as CoEditSuggestion, e as IndexStats, R as ResolvedConfig, E as EmbeddingProvider, V as VectorIndex, f as SearchHit, g as CodeChunk, h as Database, H as HNSWIndex, M as MemoryPattern, i as Reranker } from './types-Da_zLLOl.js';
3
+ export { B as BrainBankModule, j as CollectionAddOptions, k as CollectionItem, l as CollectionSearchOptions, m as DistilledStrategy, n as DocChunk, G as GitCommitRecord, o as IndexerContext, p as ModuleContext, q as SearchResultType } from './types-Da_zLLOl.js';
4
+ export { code } from './code.js';
5
+ export { git } from './git.js';
6
+ export { docs } from './docs.js';
7
+ import 'better-sqlite3';
8
+
9
+ /**
10
+ * BrainBank — Watch Mode
11
+ *
12
+ * Auto-indexes on file changes using fs.watch.
13
+ * Works with built-in indexers (code, git, docs) and custom indexers.
14
+ *
15
+ * Built-in behavior:
16
+ * - Code files → re-indexes changed file
17
+ * - Doc files → re-indexes changed collection
18
+ *
19
+ * Custom indexers:
20
+ * - Implement `onFileChange(path, event)` to handle changes
21
+ * - Implement `watchPatterns()` to specify which files to watch
22
+ *
23
+ * Usage:
24
+ * const watcher = brain.watch({ paths: ['.'] });
25
+ * watcher.close(); // stop watching
26
+ */
27
+
28
+ interface WatchOptions {
29
+ /** Paths to watch. Default: [config.repoPath] */
30
+ paths?: string[];
31
+ /** Debounce interval in ms. Default: 2000 */
32
+ debounceMs?: number;
33
+ /** Called when a file is re-indexed. */
34
+ onIndex?: (filePath: string, indexer: string) => void;
35
+ /** Called on errors. */
36
+ onError?: (error: Error) => void;
37
+ }
38
+ interface Watcher {
39
+ /** Stop watching. */
40
+ close(): void;
41
+ /** Whether the watcher is active. */
42
+ readonly active: boolean;
43
+ }
44
+
45
+ /**
46
+ * BrainBank — Re-embedding Engine
47
+ *
48
+ * Regenerates all vectors without re-indexing.
49
+ * Reads existing text from SQLite, embeds with the current provider,
50
+ * and replaces vector BLOBs. No file I/O, no git parsing, no re-chunking.
51
+ *
52
+ * Usage:
53
+ * const result = await brain.reembed({ onProgress });
54
+ * // → { code: 1200, git: 500, docs: 80, kv: 45, notes: 12, total: 1837 }
55
+ */
56
+
57
+ interface ReembedResult {
58
+ code: number;
59
+ git: number;
60
+ memory: number;
61
+ notes: number;
62
+ docs: number;
63
+ kv: number;
64
+ total: number;
65
+ }
66
+ interface ReembedOptions {
67
+ /** Progress callback: (tableName, current, total) */
68
+ onProgress?: ProgressCallback;
69
+ /** Batch size for embedBatch. Default: 50 */
70
+ batchSize?: number;
71
+ }
72
+
73
+ /**
74
+ * BrainBank — Main Orchestrator
75
+ *
76
+ * Composable semantic knowledge bank for AI agents.
77
+ * Enable only the modules you need via .use():
78
+ *
79
+ * import { BrainBank } from 'brainbank';
80
+ * import { code } from 'brainbank/code';
81
+ * import { docs } from 'brainbank/docs';
82
+ * import { notes } from 'brainbank/notes';
83
+ * import { memory } from 'brainbank/memory';
84
+ *
85
+ * const brain = new BrainBank()
86
+ * .use(code({ repoPath: '.' }))
87
+ * .use(docs())
88
+ * .use(notes())
89
+ * .use(memory());
90
+ */
91
+
92
+ declare class BrainBank extends EventEmitter {
93
+ private _config;
94
+ private _db;
95
+ private _embedding;
96
+ private _modules;
97
+ private _search?;
98
+ private _bm25?;
99
+ private _contextBuilder?;
100
+ private _initialized;
101
+ private _watcher?;
102
+ private _collections;
103
+ private _kvHnsw?;
104
+ private _kvVecs;
105
+ private _sharedHnsw;
106
+ constructor(config?: BrainBankConfig);
107
+ /**
108
+ * Register an indexer. Chainable.
109
+ *
110
+ * brain.use(code({ repoPath: '.' })).use(docs());
111
+ */
112
+ use(indexer: Indexer): this;
113
+ /** Get the list of registered indexer names. */
114
+ get indexers(): string[];
115
+ /** @deprecated Use .indexers instead. */
116
+ get modules(): string[];
117
+ /** Check if an indexer is loaded. Also matches type prefix (e.g. 'code' matches 'code:frontend'). */
118
+ has(name: string): boolean;
119
+ /** Get an indexer instance. Throws if not loaded. */
120
+ indexer<T extends Indexer = Indexer>(name: string): T;
121
+ /** @deprecated Use .indexer() instead. */
122
+ module(name: string): Indexer;
123
+ /** Find all indexers whose name equals or starts with the type prefix. */
124
+ private _findAllByType;
125
+ /** Find the first indexer that matches the type. */
126
+ private _findFirstByType;
127
+ /**
128
+ * Initialize database, HNSW indices, and load existing vectors.
129
+ * Only initializes registered modules.
130
+ * Automatically called by index/search methods if not yet initialized.
131
+ */
132
+ initialize(): Promise<void>;
133
+ /**
134
+ * Get or create a dynamic collection.
135
+ * Collections are the universal data primitive — store anything, search semantically.
136
+ *
137
+ * const errors = brain.collection('debug_errors');
138
+ * await errors.add('Fixed null check', { file: 'api.ts' });
139
+ * const hits = await errors.search('null pointer');
140
+ */
141
+ collection(name: string): Collection;
142
+ /** List all collection names that have data. */
143
+ listCollectionNames(): string[];
144
+ /**
145
+ * Index code and git history in one call.
146
+ * Incremental — only processes changes since last run.
147
+ */
148
+ index(options?: {
149
+ gitDepth?: number;
150
+ forceReindex?: boolean;
151
+ onProgress?: (stage: string, msg: string) => void;
152
+ }): Promise<{
153
+ code?: IndexResult;
154
+ git?: IndexResult;
155
+ }>;
156
+ /** Index only code files. */
157
+ indexCode(options?: {
158
+ forceReindex?: boolean;
159
+ onProgress?: ProgressCallback;
160
+ }): Promise<IndexResult>;
161
+ /** Index only git history. */
162
+ indexGit(options?: {
163
+ depth?: number;
164
+ onProgress?: ProgressCallback;
165
+ }): Promise<IndexResult>;
166
+ /** Register a document collection. */
167
+ addCollection(collection: DocumentCollection): Promise<void>;
168
+ /** Remove a collection and all its indexed data. */
169
+ removeCollection(name: string): Promise<void>;
170
+ /** List all registered collections. */
171
+ listCollections(): DocumentCollection[];
172
+ /** Index all (or specific) document collections. */
173
+ indexDocs(options?: {
174
+ collections?: string[];
175
+ onProgress?: (collection: string, file: string, current: number, total: number) => void;
176
+ }): Promise<Record<string, {
177
+ indexed: number;
178
+ skipped: number;
179
+ chunks: number;
180
+ }>>;
181
+ /** Search documents only. */
182
+ searchDocs(query: string, options?: {
183
+ collection?: string;
184
+ k?: number;
185
+ minScore?: number;
186
+ }): Promise<SearchResult[]>;
187
+ /** Add context description for a collection path. */
188
+ addContext(collection: string, path: string, context: string): void;
189
+ /** Remove context for a collection path. */
190
+ removeContext(collection: string, path: string): void;
191
+ /** List all context entries. */
192
+ listContexts(): {
193
+ collection: string;
194
+ path: string;
195
+ context: string;
196
+ }[];
197
+ /**
198
+ * Get formatted context for a task.
199
+ * Returns markdown ready for system prompt injection.
200
+ */
201
+ getContext(task: string, options?: ContextOptions): Promise<string>;
202
+ /** Semantic search across all loaded modules. */
203
+ search(query: string, options?: {
204
+ codeK?: number;
205
+ gitK?: number;
206
+ memoryK?: number;
207
+ minScore?: number;
208
+ useMMR?: boolean;
209
+ }): Promise<SearchResult[]>;
210
+ /** Semantic search over code only. */
211
+ searchCode(query: string, k?: number): Promise<SearchResult[]>;
212
+ /** Semantic search over commits only. */
213
+ searchCommits(query: string, k?: number): Promise<SearchResult[]>;
214
+ /**
215
+ * Hybrid search: vector + BM25 fused with Reciprocal Rank Fusion.
216
+ * Best quality — catches both exact keyword matches and conceptual similarities.
217
+ */
218
+ hybridSearch(query: string, options?: {
219
+ /** @deprecated Use collections: { code: N } instead */
220
+ codeK?: number;
221
+ /** @deprecated Use collections: { git: N } instead */
222
+ gitK?: number;
223
+ memoryK?: number;
224
+ minScore?: number;
225
+ useMMR?: boolean;
226
+ /**
227
+ * Sources to include and max results per source.
228
+ * Reserved keys: "code", "git", "docs" control built-in indexers.
229
+ * Any other key is treated as a KV collection name.
230
+ * Example: { code: 8, git: 5, docs: 4, errors: 3, slack: 2 }
231
+ */
232
+ collections?: Record<string, number>;
233
+ }): Promise<SearchResult[]>;
234
+ /** BM25 keyword search only (no embeddings needed). */
235
+ searchBM25(query: string, options?: {
236
+ codeK?: number;
237
+ gitK?: number;
238
+ memoryK?: number;
239
+ }): SearchResult[];
240
+ /** Rebuild FTS5 indices. */
241
+ rebuildFTS(): void;
242
+ /** Get git history for a specific file. */
243
+ fileHistory(filePath: string, limit?: number): Promise<any[]>;
244
+ /** Get co-edit suggestions for a file. */
245
+ coEdits(filePath: string, limit?: number): CoEditSuggestion[];
246
+ /** Get statistics for all loaded modules. */
247
+ stats(): IndexStats;
248
+ /**
249
+ * Start watching for file changes and auto-re-index.
250
+ * Works with built-in and custom indexers.
251
+ *
252
+ * const watcher = brain.watch({
253
+ * onIndex: (file, indexer) => console.log(`${indexer}: ${file}`),
254
+ * });
255
+ * // later: watcher.close();
256
+ */
257
+ watch(options?: WatchOptions): Watcher;
258
+ /**
259
+ * Re-embed all existing text with the current embedding provider.
260
+ * Use this when switching providers (e.g. Local → OpenAI).
261
+ * Does NOT re-parse files, git history, or documents — only regenerates vectors.
262
+ *
263
+ * @example
264
+ * const brain = new BrainBank({ embeddingProvider: new OpenAIEmbedding() });
265
+ * await brain.initialize();
266
+ * const result = await brain.reembed();
267
+ * // → { code: 1200, git: 500, docs: 80, kv: 45, notes: 12, total: 1837 }
268
+ */
269
+ reembed(options?: ReembedOptions): Promise<ReembedResult>;
270
+ /** Close database and release resources. */
271
+ close(): void;
272
+ /** Whether the brainbank has been initialized. */
273
+ get isInitialized(): boolean;
274
+ /** The resolved configuration. */
275
+ get config(): Readonly<ResolvedConfig>;
276
+ /** Load vectors from SQLite into HNSW index. */
277
+ private _loadVectors;
278
+ }
279
+
280
+ /**
281
+ * BrainBank — Local Embedding Provider
282
+ *
283
+ * Uses @xenova/transformers with all-MiniLM-L6-v2 (384 dims, WASM).
284
+ * Downloads ~23MB on first use, cached locally.
285
+ * No external API calls — runs entirely in-process.
286
+ */
287
+
288
+ declare class LocalEmbedding implements EmbeddingProvider {
289
+ readonly dims: number;
290
+ private _pipeline;
291
+ private _modelName;
292
+ private _cacheDir;
293
+ constructor(options?: {
294
+ model?: string;
295
+ cacheDir?: string;
296
+ });
297
+ /**
298
+ * Lazy-load the transformer pipeline.
299
+ * Singleton — created once and reused.
300
+ */
301
+ private _getPipeline;
302
+ /**
303
+ * Embed a single text string.
304
+ * Returns a normalized Float32Array of length 384.
305
+ */
306
+ embed(text: string): Promise<Float32Array>;
307
+ /**
308
+ * Embed multiple texts.
309
+ * Processes sequentially to avoid OOM on large batches.
310
+ */
311
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
312
+ close(): Promise<void>;
313
+ }
314
+
315
+ /**
316
+ * BrainBank — OpenAI Embedding Provider
317
+ *
318
+ * Uses OpenAI's embedding API via fetch (no SDK dependency).
319
+ * Supports text-embedding-3-small, text-embedding-3-large, and ada-002.
320
+ *
321
+ * Usage:
322
+ * const brain = new BrainBank({
323
+ * embeddingProvider: new OpenAIEmbedding({ model: 'text-embedding-3-small' }),
324
+ * });
325
+ */
326
+
327
+ interface OpenAIEmbeddingOptions {
328
+ /** OpenAI API key. Falls back to OPENAI_API_KEY env var. */
329
+ apiKey?: string;
330
+ /** Model name. Default: 'text-embedding-3-small' */
331
+ model?: string;
332
+ /** Vector dimensions. If omitted, uses model default. text-embedding-3-* supports custom dims. */
333
+ dims?: number;
334
+ /** Base URL override (for Azure, proxies, etc.) */
335
+ baseUrl?: string;
336
+ }
337
+ declare class OpenAIEmbedding implements EmbeddingProvider {
338
+ readonly dims: number;
339
+ private _apiKey;
340
+ private _model;
341
+ private _baseUrl;
342
+ private _requestDims;
343
+ private _retrying;
344
+ constructor(options?: OpenAIEmbeddingOptions);
345
+ embed(text: string): Promise<Float32Array>;
346
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
347
+ close(): Promise<void>;
348
+ private _isTokenLimitError;
349
+ private _request;
350
+ }
351
+
352
+ /**
353
+ * BrainBank — Math Utilities
354
+ *
355
+ * Pure vector math functions for similarity calculations.
356
+ * No dependencies — works on Float32Array directly.
357
+ */
358
+ /**
359
+ * Cosine similarity between two vectors.
360
+ * Assumes vectors are already normalized (unit length).
361
+ * Returns value between -1.0 and 1.0.
362
+ */
363
+ declare function cosineSimilarity(a: Float32Array, b: Float32Array): number;
364
+ /**
365
+ * L2-normalize a vector to unit length.
366
+ * Returns a new Float32Array.
367
+ */
368
+ declare function normalize(vec: Float32Array): Float32Array;
369
+
370
+ /**
371
+ * BrainBank — Maximum Marginal Relevance (MMR)
372
+ *
373
+ * Diversifies vector search results to avoid returning redundant items.
374
+ * λ=1.0 → pure relevance, λ=0.0 → pure diversity.
375
+ * Default λ=0.7 balances both.
376
+ */
377
+
378
+ /**
379
+ * Search with Maximum Marginal Relevance for diversified results.
380
+ *
381
+ * Algorithm:
382
+ * 1. Get 3x candidates from HNSW
383
+ * 2. Greedily select items that maximize: λ * relevance - (1-λ) * max_sim_to_selected
384
+ */
385
+ declare function searchMMR(index: VectorIndex, query: Float32Array, vectorCache: Map<number, Float32Array>, k: number, lambda?: number): SearchHit[];
386
+
387
+ /**
388
+ * BrainBank — Code Chunker
389
+ *
390
+ * Language-aware code splitting into semantic blocks.
391
+ * Detects functions, classes, and methods using regex + brace balancing.
392
+ * Falls back to sliding window for unsupported languages.
393
+ */
394
+
395
+ interface ChunkerConfig {
396
+ /** Max lines per chunk. Default: 80 */
397
+ maxLines?: number;
398
+ /** Min lines for a detected block to be a chunk. Default: 3 */
399
+ minLines?: number;
400
+ /** Overlap between adjacent generic chunks. Default: 5 */
401
+ overlap?: number;
402
+ }
403
+ declare class CodeChunker {
404
+ private MAX;
405
+ private MIN;
406
+ private OVERLAP;
407
+ constructor(config?: ChunkerConfig);
408
+ /**
409
+ * Split file content into semantic chunks.
410
+ * Small files (< maxLines) become a single chunk.
411
+ * For JS/TS/Python: detects functions and classes.
412
+ * For other languages: sliding window with overlap.
413
+ */
414
+ chunk(filePath: string, content: string, language: string): CodeChunk[];
415
+ private _chunkJS;
416
+ private _chunkPython;
417
+ private _chunkGeneric;
418
+ private _findBlockEnd;
419
+ private _splitLarge;
420
+ }
421
+
422
+ /**
423
+ * BrainBank — Code Indexer
424
+ *
425
+ * Walks a repository, chunks source files semantically,
426
+ * embeds each chunk, and stores in SQLite + HNSW.
427
+ * Incremental: only re-indexes files that changed (by content hash).
428
+ */
429
+
430
+ interface CodeIndexerDeps {
431
+ db: Database;
432
+ hnsw: HNSWIndex;
433
+ vectorCache: Map<number, Float32Array>;
434
+ embedding: EmbeddingProvider;
435
+ }
436
+ interface CodeIndexOptions {
437
+ forceReindex?: boolean;
438
+ onProgress?: ProgressCallback;
439
+ }
440
+ declare class CodeIndexer {
441
+ private _chunker;
442
+ private _deps;
443
+ private _repoPath;
444
+ private _maxFileSize;
445
+ constructor(repoPath: string, deps: CodeIndexerDeps, maxFileSize?: number);
446
+ /**
447
+ * Index all supported files in the repository.
448
+ * Skips unchanged files (same content hash).
449
+ */
450
+ index(options?: CodeIndexOptions): Promise<IndexResult>;
451
+ private _walkRepo;
452
+ private _hash;
453
+ }
454
+
455
+ /**
456
+ * BrainBank — Git Indexer
457
+ *
458
+ * Reads git history, embeds commit messages + diffs,
459
+ * and computes file co-edit relationships.
460
+ * Incremental: only processes new commits.
461
+ */
462
+
463
+ interface GitIndexerDeps {
464
+ db: Database;
465
+ hnsw: HNSWIndex;
466
+ vectorCache: Map<number, Float32Array>;
467
+ embedding: EmbeddingProvider;
468
+ }
469
+ interface GitIndexOptions {
470
+ depth?: number;
471
+ onProgress?: ProgressCallback;
472
+ }
473
+ declare class GitIndexer {
474
+ private _deps;
475
+ private _repoPath;
476
+ private _maxDiffBytes;
477
+ constructor(repoPath: string, deps: GitIndexerDeps, maxDiffBytes?: number);
478
+ /**
479
+ * Index git history.
480
+ * Only processes commits not already in the database.
481
+ */
482
+ index(options?: GitIndexOptions): Promise<IndexResult>;
483
+ /**
484
+ * Compute which files tend to be edited together.
485
+ * Stored in the co_edits table for later suggestion.
486
+ */
487
+ private _computeCoEdits;
488
+ }
489
+
490
+ /**
491
+ * BrainBank — Document Indexer
492
+ *
493
+ * Indexes generic document collections (markdown, text, etc.)
494
+ * with heading-aware smart chunking, inspired by qmd.
495
+ *
496
+ * const indexer = new DocIndexer(db, embedding, hnsw, vecCache);
497
+ * await indexer.indexCollection('notes', '/path/to/notes', '**\/*.md');
498
+ */
499
+
500
+ declare class DocIndexer {
501
+ private _db;
502
+ private _embedding;
503
+ private _hnsw;
504
+ private _vecCache;
505
+ constructor(_db: Database, _embedding: EmbeddingProvider, _hnsw: HNSWIndex, _vecCache: Map<number, Float32Array>);
506
+ /**
507
+ * Index all documents in a collection.
508
+ * Incremental — skips unchanged files (by content hash).
509
+ */
510
+ indexCollection(collection: string, dirPath: string, pattern?: string, options?: {
511
+ ignore?: string[];
512
+ onProgress?: (file: string, current: number, total: number) => void;
513
+ }): Promise<{
514
+ indexed: number;
515
+ skipped: number;
516
+ chunks: number;
517
+ }>;
518
+ /**
519
+ * Remove all indexed data for a collection.
520
+ */
521
+ removeCollection(collection: string): void;
522
+ /**
523
+ * Split document into chunks at natural markdown boundaries.
524
+ * Uses heading-aware scoring like qmd.
525
+ */
526
+ private _smartChunk;
527
+ /**
528
+ * Find all potential break points in the document with scores.
529
+ */
530
+ private _findBreakPoints;
531
+ /**
532
+ * Extract document title from first heading or filename.
533
+ */
534
+ private _extractTitle;
535
+ }
536
+
537
+ /**
538
+ * BrainBank — Language Registry
539
+ *
540
+ * Supported file extensions, language mappings, and ignore lists.
541
+ * Controls which files get indexed and how they're chunked.
542
+ */
543
+ declare const SUPPORTED_EXTENSIONS: Record<string, string>;
544
+ declare const IGNORE_DIRS: Set<string>;
545
+ /** Check if a file extension is supported for indexing. */
546
+ declare function isSupported(filePath: string): boolean;
547
+ /** Get the language name for a file. Returns undefined if not supported. */
548
+ declare function getLanguage(filePath: string): string | undefined;
549
+
550
+ /**
551
+ * BrainBank — Pattern Store (Agent Memory)
552
+ *
553
+ * Stores what the agent learned from past tasks.
554
+ * Each pattern records task, approach, and success rate.
555
+ * Searchable by semantic similarity via HNSW.
556
+ */
557
+
558
+ interface PatternStoreDeps {
559
+ db: Database;
560
+ hnsw: HNSWIndex;
561
+ vectorCache: Map<number, Float32Array>;
562
+ embedding: EmbeddingProvider;
563
+ }
564
+ declare class PatternStore {
565
+ private _deps;
566
+ constructor(deps: PatternStoreDeps);
567
+ /**
568
+ * Store a learned pattern.
569
+ * Returns the pattern ID.
570
+ */
571
+ learn(pattern: MemoryPattern): Promise<number>;
572
+ /**
573
+ * Search for similar successful patterns.
574
+ * Filters by minimum success rate.
575
+ */
576
+ search(query: string, k?: number, minSuccess?: number): Promise<(MemoryPattern & {
577
+ score: number;
578
+ })[]>;
579
+ /**
580
+ * Get all patterns for a specific task type.
581
+ */
582
+ getByTaskType(taskType: string, limit?: number): MemoryPattern[];
583
+ /** Total number of stored patterns. */
584
+ get count(): number;
585
+ }
586
+
587
+ /**
588
+ * BrainBank — Consolidator
589
+ *
590
+ * Maintenance operations for the agent memory:
591
+ * - prune: remove old failed patterns
592
+ * - dedup: merge near-duplicate patterns (cosine > 0.95)
593
+ * - consolidate: run both
594
+ */
595
+
596
+ declare class Consolidator {
597
+ private _db;
598
+ private _vectorCache;
599
+ constructor(_db: Database, _vectorCache: Map<number, Float32Array>);
600
+ /**
601
+ * Remove old failed patterns.
602
+ * Criteria: success_rate < 0.3 AND created > 90 days ago.
603
+ */
604
+ prune(maxAgeDays?: number, minSuccess?: number): number;
605
+ /**
606
+ * Merge near-duplicate patterns.
607
+ * Keeps the one with higher success_rate.
608
+ * Threshold: cosine similarity > 0.95.
609
+ */
610
+ dedup(threshold?: number): number;
611
+ /**
612
+ * Run full consolidation: prune + dedup.
613
+ */
614
+ consolidate(): {
615
+ pruned: number;
616
+ deduped: number;
617
+ };
618
+ }
619
+
620
+ /**
621
+ * BrainBank — Note Memory Store
622
+ *
623
+ * Stores structured note digests for long-term agent memory.
624
+ * Each digest captures decisions, files changed, patterns, and open questions.
625
+ * Supports vector + BM25 hybrid retrieval via HNSW + FTS5.
626
+ *
627
+ * Memory tiers:
628
+ * - "short" (default): Full digest, last ~20 notes
629
+ * - "long": Compressed to patterns + decisions only
630
+ */
631
+
632
+ interface NoteDigest {
633
+ title: string;
634
+ summary: string;
635
+ decisions?: string[];
636
+ filesChanged?: string[];
637
+ patterns?: string[];
638
+ openQuestions?: string[];
639
+ tags?: string[];
640
+ }
641
+ interface StoredNote extends NoteDigest {
642
+ id: number;
643
+ tier: 'short' | 'long';
644
+ createdAt: number;
645
+ score?: number;
646
+ }
647
+ interface RecallOptions {
648
+ /** Max results. Default: 5 */
649
+ k?: number;
650
+ /** Search mode. Default: 'hybrid' */
651
+ mode?: 'hybrid' | 'vector' | 'keyword';
652
+ /** Minimum score threshold. Default: 0.15 */
653
+ minScore?: number;
654
+ /** Filter by tier. Default: all */
655
+ tier?: 'short' | 'long';
656
+ }
657
+ declare class NoteStore {
658
+ private _db;
659
+ private _embedding;
660
+ private _hnsw;
661
+ private _vecs;
662
+ constructor(db: Database, embedding: EmbeddingProvider, hnsw: HNSWIndex, vecs: Map<number, Float32Array>);
663
+ /**
664
+ * Store a note digest.
665
+ * Embeds title + summary for vector search, auto-indexed in FTS5.
666
+ */
667
+ remember(digest: NoteDigest): Promise<number>;
668
+ /**
669
+ * Recall relevant notes.
670
+ * Supports vector, keyword, or hybrid (default) retrieval.
671
+ */
672
+ recall(query: string, options?: RecallOptions): Promise<StoredNote[]>;
673
+ /**
674
+ * List recent notes.
675
+ */
676
+ list(limit?: number, tier?: 'short' | 'long'): StoredNote[];
677
+ /**
678
+ * Get total count of notes.
679
+ */
680
+ count(): {
681
+ total: number;
682
+ short: number;
683
+ long: number;
684
+ };
685
+ /**
686
+ * Consolidate old short-term notes into long-term.
687
+ * Keeps the most recent `keepRecent` as short-term, compresses the rest.
688
+ */
689
+ consolidate(keepRecent?: number): {
690
+ promoted: number;
691
+ };
692
+ private _searchVector;
693
+ private _searchBM25;
694
+ private _rowToNote;
695
+ }
696
+
697
+ /**
698
+ * BrainBank — Unified Search
699
+ *
700
+ * Searches across all three indices (code, git, memory)
701
+ * and returns typed results sorted by relevance.
702
+ */
703
+
704
+ interface SearchDeps {
705
+ db: Database;
706
+ codeHnsw?: HNSWIndex;
707
+ gitHnsw?: HNSWIndex;
708
+ memHnsw?: HNSWIndex;
709
+ codeVecs: Map<number, Float32Array>;
710
+ gitVecs: Map<number, Float32Array>;
711
+ memVecs: Map<number, Float32Array>;
712
+ embedding: EmbeddingProvider;
713
+ reranker?: Reranker;
714
+ }
715
+ interface SearchOptions {
716
+ /** Max code results. Default: 6 */
717
+ codeK?: number;
718
+ /** Max git results. Default: 5 */
719
+ gitK?: number;
720
+ /** Max memory results. Default: 4 */
721
+ memoryK?: number;
722
+ /** Minimum similarity score. Default: 0.25 */
723
+ minScore?: number;
724
+ /** Use MMR for diversity. Default: true */
725
+ useMMR?: boolean;
726
+ /** MMR lambda. Default: 0.7 */
727
+ mmrLambda?: number;
728
+ }
729
+ declare class UnifiedSearch {
730
+ private _deps;
731
+ constructor(deps: SearchDeps);
732
+ /**
733
+ * Search across all indices.
734
+ * Returns combined results sorted by score.
735
+ */
736
+ search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
737
+ /**
738
+ * Re-rank results using position-aware blending.
739
+ *
740
+ * Top 1-3: 75% retrieval / 25% reranker (preserves exact matches)
741
+ * Top 4-10: 60% retrieval / 40% reranker
742
+ * Top 11+: 40% retrieval / 60% reranker (trust reranker more)
743
+ */
744
+ private _rerank;
745
+ }
746
+
747
+ /**
748
+ * BrainBank — Co-Edit Analyzer
749
+ *
750
+ * Suggests files that historically change together.
751
+ * Based on git commit co-occurrence analysis.
752
+ */
753
+
754
+ declare class CoEditAnalyzer {
755
+ private _db;
756
+ constructor(_db: Database);
757
+ /**
758
+ * Get files that frequently change alongside the given file.
759
+ * Returns sorted by co-edit count (highest first).
760
+ */
761
+ suggest(filePath: string, limit?: number): CoEditSuggestion[];
762
+ }
763
+
764
+ /**
765
+ * BrainBank — Context Builder
766
+ *
767
+ * Builds a formatted markdown context block from search results.
768
+ * Ready for injection into an LLM system prompt.
769
+ * Groups code by file, includes git history and learned patterns.
770
+ */
771
+
772
+ declare class ContextBuilder {
773
+ private _search;
774
+ private _coEdits;
775
+ constructor(_search: UnifiedSearch, _coEdits: CoEditAnalyzer);
776
+ /**
777
+ * Build a full context block for a task.
778
+ * Returns clean markdown ready for system prompt injection.
779
+ */
780
+ build(task: string, options?: ContextOptions): Promise<string>;
781
+ }
782
+
783
+ /**
784
+ * BrainBank — BM25 Full-Text Search
785
+ *
786
+ * Keyword search via SQLite FTS5 with BM25 ranking.
787
+ * Searches across code chunks, git commits, and memory patterns.
788
+ * Uses Porter stemming + unicode61 tokenizer.
789
+ */
790
+
791
+ interface BM25Options {
792
+ /** Max code results. Default: 8 */
793
+ codeK?: number;
794
+ /** Max git results. Default: 5 */
795
+ gitK?: number;
796
+ /** Max memory results. Default: 4 */
797
+ memoryK?: number;
798
+ }
799
+ declare class BM25Search {
800
+ private _db;
801
+ constructor(_db: Database);
802
+ /**
803
+ * Full-text keyword search across all FTS5 indices.
804
+ * Uses BM25 scoring — lower scores = better matches.
805
+ * Query syntax: simple words, OR, NOT, "exact phrases", prefix*
806
+ */
807
+ search(query: string, options?: BM25Options): SearchResult[];
808
+ /**
809
+ * Rebuild the FTS index from scratch.
810
+ * Call this after bulk imports or if FTS gets out of sync.
811
+ */
812
+ rebuild(): void;
813
+ }
814
+
815
+ /**
816
+ * BrainBank — Reciprocal Rank Fusion (RRF)
817
+ *
818
+ * Combines results from multiple search systems (vector + BM25)
819
+ * using the RRF algorithm: score = Σ 1/(k + rank_i)
820
+ *
821
+ * This is the same algorithm used by Elasticsearch, QMD, and most
822
+ * production hybrid search systems. Simple but very effective.
823
+ *
824
+ * Reference: Cormack et al., "Reciprocal Rank Fusion outperforms
825
+ * Condorcet and individual Rank Learning Methods" (2009)
826
+ */
827
+
828
+ /**
829
+ * Fuse ranked lists from different search systems into a single ranked list.
830
+ *
831
+ * @param resultSets - Arrays of SearchResult from different systems (e.g. vector, BM25)
832
+ * @param k - Smoothing constant. Default: 60 (standard value). Higher = less emphasis on top ranks.
833
+ * @param maxResults - Maximum results to return.
834
+ */
835
+ declare function reciprocalRankFusion(resultSets: SearchResult[][], k?: number, maxResults?: number): SearchResult[];
836
+
837
+ declare const DEFAULTS: ResolvedConfig;
838
+ /**
839
+ * Merge partial config with defaults.
840
+ * All fields become required.
841
+ * Relative dbPath is resolved against repoPath.
842
+ */
843
+ declare function resolveConfig(partial?: BrainBankConfig): ResolvedConfig;
844
+
845
+ export { BM25Search, BrainBank, BrainBankConfig, CoEditAnalyzer, CoEditSuggestion, CodeChunk, CodeChunker, CodeIndexer, Collection, Consolidator, ContextBuilder, ContextOptions, DEFAULTS, DocIndexer, DocumentCollection, EmbeddingProvider, GitIndexer, HNSWIndex, IGNORE_DIRS, IndexResult, IndexStats, Indexer, LocalEmbedding, MemoryPattern, type NoteDigest, NoteStore, OpenAIEmbedding, type OpenAIEmbeddingOptions, PatternStore, ProgressCallback, type RecallOptions, type ReembedOptions, type ReembedResult, Reranker, ResolvedConfig, SUPPORTED_EXTENSIONS, SearchHit, SearchResult, type StoredNote, UnifiedSearch, VectorIndex, type WatchOptions, type Watcher, cosineSimilarity, getLanguage, isSupported, normalize, reciprocalRankFusion, resolveConfig, searchMMR };