brainbank 0.2.0 → 0.2.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/defaults.ts","../src/app/collection.ts","../src/providers/vector/hnsw.ts","../src/providers/embeddings/local.ts","../src/search/vector/mmr.ts","../src/search/vector/multi-index.ts","../src/search/keyword/bm25.ts","../src/app/context-builder.ts","../src/app/brainbank.ts","../src/app/registry.ts","../src/db/database.ts","../src/db/schema.ts","../src/services/reembed.ts","../src/app/initializer.ts","../src/app/search-api.ts","../src/app/index-api.ts","../src/services/watch.ts"],"sourcesContent":["import type { BrainBankConfig, ResolvedConfig } from '../types.ts';\nimport * as path from 'node:path';\n\n// ── Defaults ────────────────────────────────────────\n\nexport const DEFAULTS: ResolvedConfig = {\n repoPath: '.',\n dbPath: '.brainbank/brainbank.db',\n gitDepth: 500,\n maxFileSize: 512_000, // 500KB\n maxDiffBytes: 8192,\n hnswM: 16,\n hnswEfConstruction: 200,\n hnswEfSearch: 50,\n embeddingDims: 384,\n maxElements: 2_000_000,\n};\n\n// ── Resolver ────────────────────────────────────────\n\n/**\n * Merge partial config with defaults.\n * All fields become required.\n * Relative dbPath is resolved against repoPath.\n */\nexport function resolveConfig(partial: BrainBankConfig = {}): ResolvedConfig {\n const repoPath = path.resolve(partial.repoPath ?? DEFAULTS.repoPath);\n const rawDbPath = partial.dbPath ?? DEFAULTS.dbPath;\n // Resolve relative dbPath against repoPath so DB lives alongside the repo\n const dbPath = path.isAbsolute(rawDbPath) ? rawDbPath : path.join(repoPath, rawDbPath);\n\n return {\n repoPath,\n dbPath,\n gitDepth: partial.gitDepth ?? DEFAULTS.gitDepth,\n maxFileSize: partial.maxFileSize ?? DEFAULTS.maxFileSize,\n maxDiffBytes: partial.maxDiffBytes ?? DEFAULTS.maxDiffBytes,\n hnswM: partial.hnswM ?? DEFAULTS.hnswM,\n hnswEfConstruction: partial.hnswEfConstruction ?? DEFAULTS.hnswEfConstruction,\n hnswEfSearch: partial.hnswEfSearch ?? DEFAULTS.hnswEfSearch,\n embeddingDims: partial.embeddingDims ?? DEFAULTS.embeddingDims,\n maxElements: partial.maxElements ?? DEFAULTS.maxElements,\n embeddingProvider: partial.embeddingProvider,\n reranker: partial.reranker,\n };\n}\n\n","/**\n * BrainBank — Collection\n * \n * Universal key-value store with vector + BM25 hybrid search.\n * The foundation primitive — store anything, search semantically.\n * \n * const errors = brain.collection('debug_errors');\n * await errors.add('Fixed null check in api handler', { file: 'api.ts' });\n * const hits = await errors.search('null pointer');\n */\n\nimport type { Database } from '../db/database.ts';\nimport type { EmbeddingProvider, Reranker } from '../types.ts';\nimport type { HNSWIndex } from '../providers/vector/hnsw.ts';\nimport { reciprocalRankFusion } from '../search/rrf.ts';\nimport { sanitizeFTS, normalizeBM25 } from '../search/keyword/utils.ts';\n\nexport interface CollectionItem {\n id: number;\n collection: string;\n content: string;\n metadata: Record<string, any>;\n tags: string[];\n createdAt: number;\n expiresAt?: number;\n score?: number;\n}\n\nexport interface CollectionSearchOptions {\n /** Max results. Default: 5 */\n k?: number;\n /** Search mode. Default: 'hybrid' */\n mode?: 'hybrid' | 'vector' | 'keyword';\n /** Minimum score threshold. Default: 0.15 */\n minScore?: number;\n /** Filter by tags (item must have ALL specified tags). */\n tags?: string[];\n}\n\nexport interface CollectionAddOptions {\n /** Metadata key-value pairs. */\n metadata?: Record<string, any>;\n /** Tags for filtering. */\n tags?: string[];\n /** Time-to-live duration string (e.g. '7d', '24h', '30m'). */\n ttl?: string;\n}\n\nexport class Collection {\n constructor(\n private _name: string,\n private _db: Database,\n private _embedding: EmbeddingProvider,\n private _hnsw: HNSWIndex,\n private _vecs: Map<number, Float32Array>,\n private _reranker?: Reranker,\n ) {}\n\n /** Collection name. */\n get name(): string { return this._name; }\n\n /** Add an item. Returns its ID. */\n async add(content: string, options: CollectionAddOptions | Record<string, any> = {}): Promise<number> {\n // Support both signatures: add(content, { metadata, tags, ttl }) and add(content, metadata)\n const opts = 'tags' in options || 'ttl' in options || 'metadata' in options\n ? options as CollectionAddOptions\n : { metadata: options as Record<string, any> };\n\n const metadata = opts.metadata ?? {};\n const tags = opts.tags ?? [];\n const expiresAt = opts.ttl ? Math.floor(Date.now() / 1000) + parseDuration(opts.ttl) : null;\n\n // Embed FIRST — if this throws, no orphaned rows are left in kv_data\n const vec = await this._embedding.embed(content);\n\n const result = this._db.prepare(\n 'INSERT INTO kv_data (collection, content, meta_json, tags_json, expires_at) VALUES (?, ?, ?, ?, ?)'\n ).run(this._name, content, JSON.stringify(metadata), JSON.stringify(tags), expiresAt);\n\n const id = Number(result.lastInsertRowid);\n this._db.prepare(\n 'INSERT INTO kv_vectors (data_id, embedding) VALUES (?, ?)'\n ).run(id, Buffer.from(vec.buffer));\n\n this._hnsw.add(vec, id);\n this._vecs.set(id, vec);\n\n return id;\n }\n\n /** Add multiple items. Returns their IDs. */\n async addMany(items: { content: string; metadata?: Record<string, any>; tags?: string[]; ttl?: string }[]): Promise<number[]> {\n if (items.length === 0) return [];\n\n // Batch embed all texts at once\n const texts = items.map(i => i.content);\n const vecs = await this._embedding.embedBatch(texts);\n\n // Commit DB rows atomically. HNSW is updated ONLY after this succeeds.\n // If the transaction throws, execution never reaches the HNSW loop below.\n const ids: number[] = [];\n const insertData = this._db.prepare(\n 'INSERT INTO kv_data (collection, content, meta_json, tags_json, expires_at) VALUES (?, ?, ?, ?, ?)'\n );\n const insertVec = this._db.prepare(\n 'INSERT INTO kv_vectors (data_id, embedding) VALUES (?, ?)'\n );\n\n this._db.transaction(() => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n const expiresAt = item.ttl ? Math.floor(Date.now() / 1000) + parseDuration(item.ttl) : null;\n\n const result = insertData.run(\n this._name,\n item.content,\n JSON.stringify(item.metadata ?? {}),\n JSON.stringify(item.tags ?? []),\n expiresAt,\n );\n\n const id = Number(result.lastInsertRowid);\n insertVec.run(id, Buffer.from(vecs[i].buffer));\n ids.push(id);\n }\n });\n\n // HNSW + cache updated after successful commit — no orphan risk on rollback.\n for (let i = 0; i < ids.length; i++) {\n this._hnsw.add(vecs[i], ids[i]);\n this._vecs.set(ids[i], vecs[i]);\n }\n\n return ids;\n }\n\n /** Search this collection. */\n async search(query: string, options: CollectionSearchOptions = {}): Promise<CollectionItem[]> {\n const { k = 5, mode = 'hybrid', minScore = 0.15, tags } = options;\n\n // Auto-prune expired items before search\n this._pruneExpired();\n\n if (mode === 'keyword') return this._filterByTags(this._searchBM25(query, k, minScore), tags);\n if (mode === 'vector') return this._filterByTags(await this._searchVector(query, k, minScore), tags);\n\n // Hybrid: vector + BM25 → RRF\n const [vectorHits, bm25Hits] = await Promise.all([\n this._searchVector(query, k, 0),\n Promise.resolve(this._searchBM25(query, k, 0)),\n ]);\n\n const fused = reciprocalRankFusion([\n vectorHits.map(h => ({ type: 'document' as const, score: h.score ?? 0, content: h.content, metadata: { id: h.id } as any })),\n bm25Hits.map(h => ({ type: 'document' as const, score: h.score ?? 0, content: h.content, metadata: { id: h.id } as any })),\n ]);\n\n const allById = new Map<number, CollectionItem>();\n for (const h of [...vectorHits, ...bm25Hits]) allById.set(h.id, h);\n\n const results: CollectionItem[] = [];\n for (const r of fused) {\n const item = allById.get((r.metadata as any).id);\n if (!item) continue;\n const scored = { ...item, score: r.score };\n if (scored.score >= minScore) results.push(scored);\n if (results.length >= k) break;\n }\n\n // Apply re-ranking if available\n if (this._reranker && results.length > 1) {\n const documents = results.map(r => r.content);\n const scores = await this._reranker.rank(query, documents);\n const blended = results.map((r, i) => ({\n ...r,\n score: 0.6 * (r.score ?? 0) + 0.4 * (scores[i] ?? 0),\n }));\n return this._filterByTags(\n blended.sort((a, b) => (b.score ?? 0) - (a.score ?? 0)),\n tags,\n );\n }\n\n return this._filterByTags(results, tags);\n }\n\n /** List items (newest first). */\n list(options: { limit?: number; offset?: number; tags?: string[] } = {}): CollectionItem[] {\n const { limit = 20, offset = 0, tags } = options;\n\n // Auto-prune expired items\n this._pruneExpired();\n\n const rows = this._db.prepare(\n 'SELECT * FROM kv_data WHERE collection = ? AND (expires_at IS NULL OR expires_at > ?) ORDER BY created_at DESC, id DESC LIMIT ? OFFSET ?'\n ).all(this._name, Math.floor(Date.now() / 1000), limit, offset) as any[];\n return this._filterByTags(rows.map(r => this._rowToItem(r)), tags);\n }\n\n /** Count items in this collection. */\n count(): number {\n return (this._db.prepare(\n 'SELECT COUNT(*) as c FROM kv_data WHERE collection = ? AND (expires_at IS NULL OR expires_at > ?)'\n ).get(this._name, Math.floor(Date.now() / 1000)) as any).c;\n }\n\n /** Keep only the N most recent items, remove the rest. */\n async trim(options: { keep: number }): Promise<{ removed: number }> {\n const before = this.count();\n if (before <= options.keep) return { removed: 0 };\n\n // Get IDs to remove (oldest first, beyond the keep window)\n const toRemove = this._db.prepare(`\n SELECT id FROM kv_data \n WHERE collection = ? \n ORDER BY created_at DESC, id DESC \n LIMIT -1 OFFSET ?\n `).all(this._name, options.keep) as any[];\n\n for (const row of toRemove) {\n this._removeById(row.id);\n }\n\n return { removed: toRemove.length };\n }\n\n /** Remove items older than a duration string (e.g. '30d', '12h'). */\n async prune(options: { olderThan: string }): Promise<{ removed: number }> {\n const seconds = parseDuration(options.olderThan);\n const cutoff = Math.floor(Date.now() / 1000) - seconds;\n\n const toRemove = this._db.prepare(\n 'SELECT id FROM kv_data WHERE collection = ? AND created_at < ?'\n ).all(this._name, cutoff) as any[];\n\n for (const row of toRemove) {\n this._removeById(row.id);\n }\n\n return { removed: toRemove.length };\n }\n\n /** Remove a specific item by ID. */\n remove(id: number): void {\n this._removeById(id);\n }\n\n /** Clear all items in this collection. */\n clear(): void {\n const rows = this._db.prepare(\n 'SELECT id FROM kv_data WHERE collection = ?'\n ).all(this._name) as any[];\n\n for (const row of rows) {\n this._removeById(row.id);\n }\n }\n\n // ── Private ──────────────────────────────────────\n\n private _removeById(id: number): void {\n // Remove from vector cache\n this._vecs.delete(id);\n // Mark as deleted in HNSW (prevents ghost entries in search)\n this._hnsw.remove(id);\n // Remove from DB (cascades to kv_vectors, FTS trigger handles fts_kv)\n this._db.prepare('DELETE FROM kv_data WHERE id = ?').run(id);\n }\n\n private async _searchVector(query: string, k: number, minScore: number): Promise<CollectionItem[]> {\n if (this._hnsw.size === 0) return [];\n\n const queryVec = await this._embedding.embed(query);\n // Scale search by HNSW-to-collection ratio to ensure enough candidates\n // when many collections share the same HNSW index\n const now = Math.floor(Date.now() / 1000);\n const collectionCount = (this._db.prepare(\n 'SELECT COUNT(*) as c FROM kv_data WHERE collection = ? AND (expires_at IS NULL OR expires_at > ?)'\n ).get(this._name, now) as any)?.c ?? 0;\n const ratio = collectionCount > 0 ? Math.max(3, Math.min(50, Math.ceil(this._hnsw.size / collectionCount))) : 3;\n const searchK = Math.min(k * ratio, this._hnsw.size);\n const hits = this._hnsw.search(queryVec, searchK);\n\n const ids = hits.map(h => h.id);\n if (ids.length === 0) return [];\n\n const scoreMap = new Map(hits.map(h => [h.id, h.score]));\n const placeholders = ids.map(() => '?').join(',');\n\n const rows = this._db.prepare(\n `SELECT * FROM kv_data WHERE id IN (${placeholders}) AND collection = ?`\n ).all(...ids, this._name) as any[];\n\n return rows\n .map(r => ({ ...this._rowToItem(r), score: scoreMap.get(r.id) ?? 0 }))\n .filter(r => r.score >= minScore)\n .sort((a, b) => (b.score ?? 0) - (a.score ?? 0))\n .slice(0, k);\n }\n\n private _searchBM25(query: string, k: number, minScore: number): CollectionItem[] {\n const ftsQuery = sanitizeFTS(query);\n if (!ftsQuery) return [];\n\n try {\n const rows = this._db.prepare(`\n SELECT d.*, bm25(fts_kv, 5.0, 1.0) AS score\n FROM fts_kv f\n JOIN kv_data d ON d.id = f.rowid\n WHERE fts_kv MATCH ? AND d.collection = ?\n ORDER BY score ASC\n LIMIT ?\n `).all(ftsQuery, this._name, k) as any[];\n\n return rows\n .map(r => ({\n ...this._rowToItem(r),\n score: normalizeBM25(r.score),\n }))\n .filter(r => (r.score ?? 0) >= minScore);\n } catch {\n return [];\n }\n }\n\n private _rowToItem(r: any): CollectionItem {\n return {\n id: r.id,\n collection: r.collection,\n content: r.content,\n metadata: JSON.parse(r.meta_json || '{}'),\n tags: JSON.parse(r.tags_json || '[]'),\n createdAt: r.created_at,\n expiresAt: r.expires_at ?? undefined,\n };\n }\n\n /** Filter results by tags (item must have ALL specified tags). */\n private _filterByTags(items: CollectionItem[], tags?: string[]): CollectionItem[] {\n if (!tags || tags.length === 0) return items;\n return items.filter(item =>\n tags.every(t => item.tags.includes(t))\n );\n }\n\n /** Remove expired items (TTL). Called automatically on search/list. */\n private _pruneExpired(): void {\n const now = Math.floor(Date.now() / 1000);\n const expired = this._db.prepare(\n 'SELECT id FROM kv_data WHERE collection = ? AND expires_at IS NOT NULL AND expires_at <= ?'\n ).all(this._name, now) as any[];\n\n for (const row of expired) {\n this._removeById(row.id);\n }\n }\n}\n\n/** Parse a duration string like '30d', '12h', '5m' to seconds. */\nfunction parseDuration(s: string): number {\n const match = s.match(/^(\\d+)([dhms])$/);\n if (!match) throw new Error(`Invalid duration: \"${s}\". Use format like '30d', '12h', '5m'.`);\n\n const n = parseInt(match[1], 10);\n switch (match[2]) {\n case 'd': return n * 86400;\n case 'h': return n * 3600;\n case 'm': return n * 60;\n case 's': return n;\n default: return n;\n }\n}\n","/**\n * BrainBank — HNSW Vector Index\n * \n * Wraps hnswlib-node for O(log n) approximate nearest neighbor search.\n * M=16 connections, ef=200 construction, ef=50 search by default.\n * 150x faster than brute force at 1M vectors.\n */\n\nimport type { VectorIndex, SearchHit } from '../../types.ts';\n\nexport class HNSWIndex implements VectorIndex {\n private _index: any = null;\n private _lib: any = null;\n private _ids = new Set<number>();\n\n constructor(\n private _dims: number,\n private _maxElements: number = 2_000_000,\n private _M: number = 16,\n private _efConstruction: number = 200,\n private _efSearch: number = 50,\n ) {}\n\n /**\n * Initialize the HNSW index.\n * Must be called before add/search.\n */\n async init(): Promise<this> {\n this._lib = await import('hnswlib-node');\n this._createIndex();\n return this;\n }\n\n /**\n * Reinitialize the index in-place, clearing all vectors.\n * Required after reembed or full re-index to avoid duplicate IDs.\n * init() must have been called first.\n */\n reinit(): void {\n if (!this._lib) throw new Error('HNSW not initialized — call init() first');\n this._createIndex();\n }\n\n private _createIndex(): void {\n const HNSW = this._lib.default?.HierarchicalNSW ?? this._lib.HierarchicalNSW;\n this._index = new HNSW('cosine', this._dims);\n this._index.initIndex(this._maxElements, this._M, this._efConstruction);\n this._index.setEf(this._efSearch);\n this._ids = new Set();\n }\n\n /** Maximum capacity of this index. */\n get maxElements(): number { return this._maxElements; }\n\n /**\n * Add a vector with an integer ID.\n * The vector should be pre-normalized for cosine distance.\n */\n add(vector: Float32Array, id: number): void {\n if (!this._index) throw new Error('HNSW index not initialized — call init() first');\n if (this._ids.has(id)) return; // idempotent: skip duplicates\n if (this._ids.size >= this._maxElements) {\n throw new Error(\n `HNSW index full (${this._maxElements} elements). ` +\n `Increase maxElements in config or prune old data.`\n );\n }\n this._index.addPoint(Array.from(vector), id);\n this._ids.add(id);\n }\n\n /**\n * Mark a vector as deleted so it no longer appears in searches.\n * Uses hnswlib-node markDelete under the hood.\n * Safe to call with an ID that doesn't exist.\n */\n remove(id: number): void {\n if (!this._index || this._ids.size === 0) return;\n if (!this._ids.has(id)) return;\n try {\n this._index.markDelete(id);\n this._ids.delete(id);\n } catch {\n // ID not found — ignore silently\n }\n }\n\n /**\n * Search for the k nearest neighbors.\n * Returns results sorted by score (highest first).\n * Score is 1 - cosine_distance (1.0 = identical).\n */\n search(query: Float32Array, k: number): SearchHit[] {\n if (!this._index || this._ids.size === 0) return [];\n\n const actualK = Math.min(k, this._ids.size);\n const result = this._index.searchKnn(Array.from(query), actualK);\n\n return result.neighbors.map((id: number, i: number) => ({\n id,\n score: 1 - result.distances[i],\n }));\n }\n\n /** Number of vectors in the index. */\n get size(): number {\n return this._ids.size;\n }\n}\n","/**\n * BrainBank — Local Embedding Provider\n * \n * Uses @xenova/transformers with all-MiniLM-L6-v2 (384 dims, WASM).\n * Downloads ~23MB on first use, cached locally.\n * No external API calls — runs entirely in-process.\n */\n\nimport type { EmbeddingProvider } from '../../types.ts';\n\nexport class LocalEmbedding implements EmbeddingProvider {\n readonly dims: number = 384;\n\n private _pipeline: any = null;\n private _modelName: string;\n private _cacheDir: string;\n\n constructor(options: { model?: string; cacheDir?: string } = {}) {\n this._modelName = options.model ?? 'Xenova/all-MiniLM-L6-v2';\n this._cacheDir = options.cacheDir ?? '.model-cache';\n }\n\n private _pipelinePromise: Promise<any> | null = null;\n\n /**\n * Lazy-load the transformer pipeline.\n * Singleton — created once and reused.\n * Promise-deduped to prevent concurrent downloads.\n */\n private async _getPipeline(): Promise<any> {\n if (this._pipeline) return this._pipeline;\n if (this._pipelinePromise) return this._pipelinePromise;\n\n this._pipelinePromise = (async () => {\n const { pipeline, env } = await import('@xenova/transformers' as any);\n env.cacheDir = this._cacheDir;\n env.allowLocalModels = true;\n\n this._pipeline = await pipeline('feature-extraction', this._modelName, {\n quantized: true,\n });\n\n return this._pipeline;\n })();\n\n try {\n return await this._pipelinePromise;\n } finally {\n this._pipelinePromise = null;\n }\n }\n\n /**\n * Embed a single text string.\n * Returns a normalized Float32Array of length 384.\n */\n async embed(text: string): Promise<Float32Array> {\n const pipe = await this._getPipeline();\n const output = await pipe(text, { pooling: 'mean', normalize: true });\n return output.data as Float32Array;\n }\n\n /**\n * Embed multiple texts.\n * Processes sequentially to avoid OOM on large batches.\n */\n async embedBatch(texts: string[]): Promise<Float32Array[]> {\n const results: Float32Array[] = [];\n for (const text of texts) {\n results.push(await this.embed(text));\n }\n return results;\n }\n\n async close(): Promise<void> {\n this._pipeline = null;\n }\n}\n","/**\n * BrainBank — Maximum Marginal Relevance (MMR)\n * \n * Diversifies vector search results to avoid returning redundant items.\n * λ=1.0 → pure relevance, λ=0.0 → pure diversity.\n * Default λ=0.7 balances both.\n */\n\nimport type { VectorIndex, SearchHit } from '../../types.ts';\nimport { cosineSimilarity } from '../../lib/math.ts';\n\n/**\n * Search with Maximum Marginal Relevance for diversified results.\n * \n * Algorithm:\n * 1. Get 3x candidates from HNSW\n * 2. Greedily select items that maximize: λ * relevance - (1-λ) * max_sim_to_selected\n */\nexport function searchMMR(\n index: VectorIndex,\n query: Float32Array,\n vectorCache: Map<number, Float32Array>,\n k: number,\n lambda: number = 0.7,\n): SearchHit[] {\n // Get more candidates than needed\n const candidates = index.search(query, k * 3);\n if (candidates.length <= k) return candidates;\n\n const selected: SearchHit[] = [];\n const remaining = [...candidates];\n\n while (selected.length < k && remaining.length > 0) {\n let bestScore = -Infinity;\n let bestIdx = 0;\n\n for (let i = 0; i < remaining.length; i++) {\n const relevance = remaining[i].score;\n\n // Max similarity to any already-selected item\n let maxSim = 0;\n for (const sel of selected) {\n const candidateVec = vectorCache.get(remaining[i].id);\n const selectedVec = vectorCache.get(sel.id);\n if (candidateVec && selectedVec) {\n maxSim = Math.max(maxSim, cosineSimilarity(candidateVec, selectedVec));\n }\n }\n\n // MMR score: balance relevance vs diversity\n const mmrScore = lambda * relevance - (1 - lambda) * maxSim;\n\n if (mmrScore > bestScore) {\n bestScore = mmrScore;\n bestIdx = i;\n }\n }\n\n selected.push(remaining[bestIdx]);\n remaining.splice(bestIdx, 1);\n }\n\n return selected;\n}\n","/**\n * BrainBank — Multi-Index Search\n * \n * Searches across all three indices (code, git, memory patterns)\n * and returns typed results sorted by relevance.\n */\n\nimport type { Database } from '../../db/database.ts';\nimport type { EmbeddingProvider, Reranker, SearchResult } from '../../types.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\nimport { searchMMR } from './mmr.ts';\n\nexport interface SearchConfig {\n db: Database;\n codeHnsw?: HNSWIndex;\n gitHnsw?: HNSWIndex;\n patternHnsw?: HNSWIndex;\n codeVecs: Map<number, Float32Array>;\n gitVecs: Map<number, Float32Array>;\n patternVecs: Map<number, Float32Array>;\n embedding: EmbeddingProvider;\n reranker?: Reranker;\n}\n\nexport interface SearchOptions {\n /** Max code results. Default: 6 */\n codeK?: number;\n /** Max git results. Default: 5 */\n gitK?: number;\n /** Max pattern results. Default: 4 */\n patternK?: number;\n /** Minimum similarity score. Default: 0.25 */\n minScore?: number;\n /** Use MMR for diversity. Default: true */\n useMMR?: boolean;\n /** MMR lambda. Default: 0.7 */\n mmrLambda?: number;\n}\n\nexport class MultiIndexSearch {\n private _config: SearchConfig;\n\n constructor(config: SearchConfig) {\n this._config = config;\n }\n\n /**\n * Search across all indices.\n * Returns combined results sorted by score.\n */\n async search(query: string, options: SearchOptions = {}): Promise<SearchResult[]> {\n const {\n codeK = 6,\n gitK = 5,\n patternK = 4,\n minScore = 0.25,\n useMMR = true,\n mmrLambda = 0.7,\n } = options;\n\n const queryVec = await this._config.embedding.embed(query);\n const results: SearchResult[] = [];\n\n // ── Code search ────────────────────────────\n if (this._config.codeHnsw && this._config.codeHnsw.size > 0) {\n const hits = useMMR\n ? searchMMR(this._config.codeHnsw, queryVec, this._config.codeVecs, codeK, mmrLambda)\n : this._config.codeHnsw.search(queryVec, codeK);\n\n if (hits.length > 0) {\n const ids = hits.map(h => h.id);\n const scoreMap = new Map(hits.map(h => [h.id, h.score]));\n const placeholders = ids.map(() => '?').join(',');\n\n const rows = this._config.db.prepare(\n `SELECT * FROM code_chunks WHERE id IN (${placeholders})`\n ).all(...ids) as any[];\n\n for (const r of rows) {\n const score = scoreMap.get(r.id) ?? 0;\n if (score >= minScore) {\n results.push({\n type: 'code',\n score,\n filePath: r.file_path,\n content: r.content,\n metadata: {\n chunkType: r.chunk_type,\n name: r.name,\n startLine: r.start_line,\n endLine: r.end_line,\n language: r.language,\n },\n });\n }\n }\n }\n }\n\n // ── Git search ─────────────────────────────\n if (this._config.gitHnsw && this._config.gitHnsw.size > 0) {\n const hits = this._config.gitHnsw.search(queryVec, gitK * 2);\n\n if (hits.length > 0) {\n const ids = hits.map(h => h.id);\n const scoreMap = new Map(hits.map(h => [h.id, h.score]));\n const placeholders = ids.map(() => '?').join(',');\n\n const rows = this._config.db.prepare(\n `SELECT * FROM git_commits WHERE id IN (${placeholders}) AND is_merge = 0`\n ).all(...ids) as any[];\n\n for (const r of rows) {\n const score = scoreMap.get(r.id) ?? 0;\n if (score >= minScore) {\n results.push({\n type: 'commit',\n score,\n content: r.message,\n metadata: {\n hash: r.hash,\n shortHash: r.short_hash,\n author: r.author,\n date: r.date,\n files: JSON.parse(r.files_json ?? '[]'),\n additions: r.additions,\n deletions: r.deletions,\n diff: r.diff,\n },\n });\n }\n }\n }\n }\n\n // ── Pattern search ──────────────────────────\n if (this._config.patternHnsw && this._config.patternHnsw.size > 0) {\n const hits = useMMR\n ? searchMMR(this._config.patternHnsw, queryVec, this._config.patternVecs, patternK, mmrLambda)\n : this._config.patternHnsw.search(queryVec, patternK);\n\n if (hits.length > 0) {\n const ids = hits.map(h => h.id);\n const scoreMap = new Map(hits.map(h => [h.id, h.score]));\n const placeholders = ids.map(() => '?').join(',');\n\n const rows = this._config.db.prepare(\n `SELECT * FROM memory_patterns WHERE id IN (${placeholders}) AND success_rate >= 0.5`\n ).all(...ids) as any[];\n\n for (const r of rows) {\n const score = scoreMap.get(r.id) ?? 0;\n if (score >= minScore) {\n results.push({\n type: 'pattern',\n score,\n content: r.approach,\n metadata: {\n taskType: r.task_type,\n task: r.task,\n outcome: r.outcome,\n successRate: r.success_rate,\n critique: r.critique,\n },\n });\n }\n }\n }\n }\n\n // Sort by score descending\n results.sort((a, b) => b.score - a.score);\n\n // Apply re-ranking if available\n if (this._config.reranker && results.length > 1) {\n return this._rerank(query, results);\n }\n\n return results;\n }\n\n /**\n * Re-rank results using position-aware blending.\n * \n * Top 1-3: 75% retrieval / 25% reranker (preserves exact matches)\n * Top 4-10: 60% retrieval / 40% reranker\n * Top 11+: 40% retrieval / 60% reranker (trust reranker more)\n */\n private async _rerank(query: string, results: SearchResult[]): Promise<SearchResult[]> {\n const reranker = this._config.reranker!;\n const documents = results.map(r => r.content);\n const scores = await reranker.rank(query, documents);\n\n const blended = results.map((r, i) => {\n const pos = i + 1;\n const rrfWeight = pos <= 3 ? 0.75 : pos <= 10 ? 0.60 : 0.40;\n return {\n ...r,\n score: rrfWeight * r.score + (1 - rrfWeight) * (scores[i] ?? 0),\n };\n });\n\n return blended.sort((a, b) => b.score - a.score);\n }\n}\n","/**\n * BrainBank — BM25 Full-Text Search\n * \n * Keyword search via SQLite FTS5 with BM25 ranking.\n * Searches across code chunks, git commits, and memory patterns.\n * Uses Porter stemming + unicode61 tokenizer.\n */\n\nimport type { Database } from '../../db/database.ts';\nimport type { SearchResult } from '../../types.ts';\nimport { sanitizeFTS, normalizeBM25 } from './utils.ts';\n\nexport interface BM25Options {\n /** Max code results. Default: 8 */\n codeK?: number;\n /** Max git results. Default: 5 */\n gitK?: number;\n /** Max pattern results. Default: 4 */\n patternK?: number;\n}\n\nexport class BM25Search {\n constructor(private _db: Database) {}\n\n /**\n * Full-text keyword search across all FTS5 indices.\n * Uses BM25 scoring — lower scores = better matches.\n * Query syntax: simple words, OR, NOT, \"exact phrases\", prefix*\n */\n search(query: string, options: BM25Options = {}): SearchResult[] {\n const { codeK = 8, gitK = 5, patternK = 4 } = options;\n const results: SearchResult[] = [];\n\n const ftsQuery = sanitizeFTS(query);\n if (!ftsQuery) return [];\n\n // ── Code search ────────────────────────────\n const seenIds = new Set<number>();\n if (codeK > 0) {\n try {\n const rows = this._db.prepare(`\n SELECT c.id, c.file_path, c.chunk_type, c.name, c.start_line, c.end_line,\n c.content, c.language, bm25(fts_code, 5.0, 3.0, 1.0) AS score\n FROM fts_code f\n JOIN code_chunks c ON c.id = f.rowid\n WHERE fts_code MATCH ?\n ORDER BY score ASC\n LIMIT ?\n `).all(ftsQuery, codeK) as any[];\n\n for (const r of rows) {\n seenIds.add(r.id);\n results.push({\n type: 'code',\n score: normalizeBM25(r.score),\n filePath: r.file_path,\n content: r.content,\n metadata: {\n chunkType: r.chunk_type,\n name: r.name,\n startLine: r.start_line,\n endLine: r.end_line,\n language: r.language,\n searchType: 'bm25',\n },\n });\n }\n } catch {}\n\n // ── File-path fallback: match filenames via LIKE ──\n try {\n const words = query.replace(/[^a-zA-Z0-9]/g, ' ').split(/\\s+/).filter(w => w.length > 2);\n for (const word of words.slice(0, 3)) {\n const pathRows = this._db.prepare(`\n SELECT id, file_path, chunk_type, name, start_line, end_line, content, language\n FROM code_chunks\n WHERE file_path LIKE ? AND chunk_type = 'file'\n LIMIT 3\n `).all(`%${word}%`) as any[];\n\n for (const r of pathRows) {\n if (seenIds.has(r.id)) continue;\n seenIds.add(r.id);\n results.push({\n type: 'code',\n score: 0.6,\n filePath: r.file_path,\n content: r.content,\n metadata: {\n chunkType: r.chunk_type,\n name: r.name,\n startLine: r.start_line,\n endLine: r.end_line,\n language: r.language,\n searchType: 'bm25-path',\n },\n });\n }\n }\n } catch {}\n }\n\n // ── Git search ─────────────────────────────\n if (gitK > 0) {\n try {\n const rows = this._db.prepare(`\n SELECT c.id, c.hash, c.short_hash, c.message, c.author, c.date,\n c.files_json, c.diff, c.additions, c.deletions,\n bm25(fts_commits, 5.0, 2.0, 1.0) AS score\n FROM fts_commits f\n JOIN git_commits c ON c.id = f.rowid\n WHERE fts_commits MATCH ? AND c.is_merge = 0\n ORDER BY score ASC\n LIMIT ?\n `).all(ftsQuery, gitK) as any[];\n\n for (const r of rows) {\n results.push({\n type: 'commit',\n score: normalizeBM25(r.score),\n content: r.message,\n metadata: {\n hash: r.hash,\n shortHash: r.short_hash,\n author: r.author,\n date: r.date,\n files: JSON.parse(r.files_json ?? '[]'),\n additions: r.additions,\n deletions: r.deletions,\n diff: r.diff,\n searchType: 'bm25',\n },\n });\n }\n } catch {}\n }\n\n // ── Pattern search ──────────────────────────\n if (patternK > 0) {\n try {\n const rows = this._db.prepare(`\n SELECT p.id, p.task_type, p.task, p.approach, p.outcome,\n p.success_rate, p.critique,\n bm25(fts_patterns, 3.0, 5.0, 5.0, 1.0) AS score\n FROM fts_patterns f\n JOIN memory_patterns p ON p.id = f.rowid\n WHERE fts_patterns MATCH ? AND p.success_rate >= 0.5\n ORDER BY score ASC\n LIMIT ?\n `).all(ftsQuery, patternK) as any[];\n\n for (const r of rows) {\n results.push({\n type: 'pattern',\n score: normalizeBM25(r.score),\n content: r.approach,\n metadata: {\n taskType: r.task_type,\n task: r.task,\n outcome: r.outcome,\n successRate: r.success_rate,\n critique: r.critique,\n searchType: 'bm25',\n },\n });\n }\n } catch {}\n }\n\n return results.sort((a, b) => b.score - a.score);\n }\n\n /**\n * Rebuild the FTS index from scratch.\n * Call this after bulk imports or if FTS gets out of sync.\n */\n rebuild(): void {\n try {\n this._db.prepare(\"INSERT INTO fts_code(fts_code) VALUES('rebuild')\").run();\n this._db.prepare(\"INSERT INTO fts_commits(fts_commits) VALUES('rebuild')\").run();\n this._db.prepare(\"INSERT INTO fts_patterns(fts_patterns) VALUES('rebuild')\").run();\n } catch {}\n }\n\n}\n","/**\n * BrainBank — Context Builder\n * \n * Builds a formatted markdown context block from search results.\n * Ready for injection into an LLM system prompt.\n * Groups code by file, includes git history and learned patterns.\n */\n\nimport type { SearchResult, CoEditSuggestion, ContextOptions } from '../types.ts';\nimport type { MultiIndexSearch } from '../search/vector/multi-index.ts';\nimport type { CoEditAnalyzer } from '../indexers/git/co-edit-analyzer.ts';\n\nexport class ContextBuilder {\n constructor(\n private _search: MultiIndexSearch,\n private _coEdits?: CoEditAnalyzer,\n ) {}\n\n /**\n * Build a full context block for a task.\n * Returns clean markdown ready for system prompt injection.\n */\n async build(task: string, options: ContextOptions = {}): Promise<string> {\n const {\n codeResults = 6,\n gitResults = 5,\n patternResults = 4,\n affectedFiles = [],\n minScore = 0.25,\n useMMR = true,\n mmrLambda = 0.7,\n } = options;\n\n // Run unified search\n const results = await this._search.search(task, {\n codeK: codeResults,\n gitK: gitResults,\n patternK: patternResults,\n minScore,\n useMMR,\n mmrLambda,\n });\n\n const parts: string[] = [`# Context for: \"${task}\"\\n`];\n\n // ── Code Results ────────────────────────────\n const codeHits = results.filter(r => r.type === 'code').slice(0, codeResults);\n if (codeHits.length > 0) {\n parts.push('## Relevant Code\\n');\n\n // Group by file\n const byFile = new Map<string, typeof codeHits>();\n for (const r of codeHits) {\n const key = r.filePath ?? 'unknown';\n if (!byFile.has(key)) byFile.set(key, []);\n byFile.get(key)!.push(r);\n }\n\n for (const [file, chunks] of byFile) {\n parts.push(`### ${file}`);\n for (const c of chunks) {\n const m = c.metadata;\n const label = m.name\n ? `${m.chunkType} \\`${m.name}\\` (L${m.startLine}-${m.endLine})`\n : `L${m.startLine}-${m.endLine}`;\n parts.push(`**${label}** — ${Math.round(c.score * 100)}% match`);\n parts.push('```' + (m.language || ''));\n parts.push(c.content);\n parts.push('```\\n');\n }\n }\n }\n\n // ── Git Results ─────────────────────────────\n const gitHits = results.filter(r => r.type === 'commit').slice(0, gitResults);\n if (gitHits.length > 0) {\n parts.push('## Related Git History\\n');\n for (const c of gitHits) {\n const m = c.metadata;\n const score = Math.round(c.score * 100);\n const files = (m.files ?? []).slice(0, 4).join(', ');\n parts.push(`**[${m.shortHash}]** ${c.content} *(${m.author}, ${m.date?.slice(0, 10)}, ${score}%)*`);\n if (files) parts.push(` Files: ${files}`);\n if (m.diff) {\n const snippet = m.diff\n .split('\\n')\n .filter((l: string) => l.startsWith('+') || l.startsWith('-') || l.startsWith('@@'))\n .slice(0, 10)\n .join('\\n');\n if (snippet) {\n parts.push('```diff');\n parts.push(snippet);\n parts.push('```');\n }\n }\n parts.push('');\n }\n }\n\n // ── Co-Edits ────────────────────────────────\n if (affectedFiles.length > 0 && this._coEdits) {\n const coEditLines: string[] = [];\n for (const file of affectedFiles.slice(0, 3)) {\n const suggestions = this._coEdits.suggest(file, 4);\n if (suggestions.length > 0) {\n coEditLines.push(\n `- **${file}** → also tends to change: ${suggestions.map(s => `${s.file} (${s.count}x)`).join(', ')}`\n );\n }\n }\n if (coEditLines.length > 0) {\n parts.push('## Co-Edit Patterns\\n');\n parts.push(...coEditLines);\n parts.push('');\n }\n }\n\n // ── Memory Results ──────────────────────────\n const memHits = results.filter(r => r.type === 'pattern').slice(0, patternResults);\n if (memHits.length > 0) {\n parts.push('## Learned Patterns\\n');\n for (const p of memHits) {\n const m = p.metadata;\n const score = Math.round(p.score * 100);\n const success = Math.round((m.successRate ?? 0) * 100);\n parts.push(`**${m.taskType}** — ${success}% success, ${score}% match`);\n parts.push(`Task: ${m.task}`);\n parts.push(`Approach: ${p.content}`);\n if (m.critique) parts.push(`Lesson: ${m.critique}`);\n parts.push('');\n }\n }\n\n return parts.join('\\n');\n }\n}\n","/**\n * BrainBank — Main Orchestrator\n *\n * Thin facade that composes four services:\n * IndexerRegistry — registration + lookup\n * Initializer — two-phase startup (earlyInit / lateInit)\n * SearchAPI — all search + context logic\n * IndexAPI — code / git / docs indexing orchestration\n *\n * All heavy logic lives in those modules; BrainBank owns state,\n * guards (requireInit / initialize()), and public API shape.\n */\n\nimport { EventEmitter } from 'node:events';\nimport { resolveConfig } from '../config/defaults.ts';\nimport { Database } from '../db/database.ts';\nimport { HNSWIndex } from '../providers/vector/hnsw.ts';\nimport { Collection } from './collection.ts';\nimport { IndexerRegistry } from './registry.ts';\nimport { earlyInit, lateInit } from './initializer.ts';\nimport { SearchAPI } from './search-api.ts';\nimport { IndexAPI } from './index-api.ts';\nimport { reembedAll } from '../services/reembed.ts';\nimport { createWatcher, type WatchOptions, type Watcher } from '../services/watch.ts';\nimport type { ReembedResult, ReembedOptions } from '../services/reembed.ts';\nimport type { Indexer } from '../indexers/base.ts';\nimport type {\n BrainBankConfig, ResolvedConfig, EmbeddingProvider,\n IndexResult, IndexStats, SearchResult,\n ContextOptions, CoEditSuggestion, ProgressCallback, StageProgressCallback,\n DocumentCollection,\n} from '../types.ts';\n\nexport class BrainBank extends EventEmitter {\n // ── State ───────────────────────────────────────\n private _config: ResolvedConfig;\n private _db!: Database;\n private _embedding!: EmbeddingProvider;\n private _registry = new IndexerRegistry();\n private _searchAPI?: SearchAPI;\n private _indexAPI?: IndexAPI;\n private _initialized = false;\n private _initPromise: Promise<void> | null = null;\n private _watcher?: Watcher;\n\n // Collections (KV store)\n private _collections = new Map<string, Collection>();\n private _kvHnsw?: HNSWIndex;\n private _kvVecs = new Map<number, Float32Array>();\n\n // Shared HNSW pool — code:frontend + code:backend share one index\n private _sharedHnsw = new Map<string, { hnsw: HNSWIndex; vecCache: Map<number, Float32Array> }>();\n\n constructor(config: BrainBankConfig = {}) {\n super();\n this._config = resolveConfig(config);\n }\n\n // ── Indexer registration ─────────────────────────\n\n /**\n * Register an indexer. Chainable.\n *\n * brain.use(code({ repoPath: '.' })).use(docs());\n */\n use(indexer: Indexer): this {\n if (this._initialized)\n throw new Error(`BrainBank: Cannot add indexer '${indexer.name}' after initialization. Call .use() before any operations.`);\n this._registry.register(indexer);\n return this;\n }\n\n /** Get the list of registered indexer names. */\n get indexers(): string[] { return this._registry.names; }\n\n /** Check if an indexer is loaded. Also matches type prefix (e.g. 'code' matches 'code:frontend'). */\n has(name: string): boolean { return this._registry.has(name); }\n\n /** Get an indexer instance. Throws if not loaded. */\n indexer<T extends Indexer = Indexer>(n: string): T { return this._registry.get<T>(n); }\n\n // ── Initialization ───────────────────────────────\n\n /**\n * Initialize database, HNSW indices, and load existing vectors.\n * Only initializes registered modules.\n * Automatically called by index/search methods if not yet initialized.\n */\n async initialize(): Promise<void> {\n if (this._initialized) return;\n if (this._initPromise) return this._initPromise;\n\n this._initPromise = this._runInitialize()\n .catch(err => {\n // Reset shared state so a retry starts clean\n for (const { hnsw } of this._sharedHnsw.values()) try { hnsw.reinit(); } catch {}\n this._kvVecs.clear();\n if (this._kvHnsw) try { this._kvHnsw.reinit(); } catch {}\n try { this._db?.close(); } catch {}\n this._db = undefined as any;\n this._kvHnsw = undefined as any;\n this._searchAPI = undefined;\n this._indexAPI = undefined;\n throw err;\n })\n .finally(() => { this._initPromise = null; });\n\n return this._initPromise;\n }\n\n private async _runInitialize(): Promise<void> {\n if (this._initialized) return;\n\n // Phase 1: set this._kvHnsw BEFORE phase 2 so collection() works\n // when indexers call ctx.collection() during their initialize()\n const early = await earlyInit(this._config, (e, d) => this.emit(e, d));\n this._db = early.db;\n this._embedding = early.embedding;\n this._kvHnsw = early.kvHnsw;\n\n // Phase 2: load vectors, run indexers, build search services\n const late = await lateInit(\n early,\n this._config,\n this._registry,\n this._sharedHnsw,\n this._kvVecs,\n (name) => this.collection(name),\n );\n\n this._searchAPI = new SearchAPI({\n ...late,\n registry: this._registry,\n config: this._config,\n searchDocs: (q, o) => this.searchDocs(q, o),\n collection: (n) => this.collection(n),\n });\n\n this._indexAPI = new IndexAPI({\n registry: this._registry,\n gitDepth: this._config.gitDepth,\n emit: (e, d) => this.emit(e, d),\n });\n\n this._initialized = true;\n this.emit('initialized', { indexers: this.indexers });\n }\n\n // ── Collections (KV) ────────────────────────────\n\n /**\n * Get or create a dynamic collection.\n * Collections are the universal data primitive — store anything, search semantically.\n *\n * const errors = brain.collection('debug_errors');\n * await errors.add('Fixed null check', { file: 'api.ts' });\n * const hits = await errors.search('null pointer');\n */\n collection(name: string): Collection {\n if (this._collections.has(name)) return this._collections.get(name)!;\n if (!this._kvHnsw)\n throw new Error('BrainBank: Collections not ready. Call await brain.initialize() first.');\n const coll = new Collection(name, this._db, this._embedding, this._kvHnsw, this._kvVecs, this._config.reranker);\n this._collections.set(name, coll);\n return coll;\n }\n\n /** List all collection names that have data. */\n listCollectionNames(): string[] {\n this._requireInit('listCollectionNames');\n return (this._db.prepare('SELECT DISTINCT collection FROM kv_data ORDER BY collection').all() as any[])\n .map(r => r.collection);\n }\n\n // ── Indexing (delegated to IndexAPI) ─────────────\n\n async index(options: {\n modules?: ('code' | 'git' | 'docs')[];\n gitDepth?: number; forceReindex?: boolean; onProgress?: StageProgressCallback;\n } = {}): Promise<{ code?: IndexResult; git?: IndexResult; docs?: Record<string, { indexed: number; skipped: number; chunks: number }> }> {\n await this.initialize();\n return this._indexAPI!.index(options);\n }\n\n /** Index only code files (all repos in multi-repo mode). */\n async indexCode(options: { forceReindex?: boolean; onProgress?: ProgressCallback } = {}): Promise<IndexResult> {\n await this.initialize();\n return this._indexAPI!.indexCode(options);\n }\n\n /** Index only git history (all repos in multi-repo mode). */\n async indexGit(options: { depth?: number; onProgress?: ProgressCallback } = {}): Promise<IndexResult> {\n await this.initialize();\n return this._indexAPI!.indexGit(options);\n }\n\n // ── Document collections ─────────────────────────\n\n /** Register a document collection. */\n async addCollection(collection: DocumentCollection): Promise<void> {\n await this.initialize();\n this.indexer('docs').addCollection!(collection);\n }\n\n /** Remove a collection and all its indexed data. */\n async removeCollection(name: string): Promise<void> {\n await this.initialize();\n this.indexer('docs').removeCollection!(name);\n }\n\n /** List all registered collections. */\n listCollections(): DocumentCollection[] {\n this._requireInit('listCollections');\n return this.indexer('docs').listCollections!();\n }\n\n /** Index all (or specific) document collections. */\n async indexDocs(options: {\n collections?: string[];\n onProgress?: (collection: string, file: string, current: number, total: number) => void;\n } = {}): Promise<Record<string, { indexed: number; skipped: number; chunks: number }>> {\n await this.initialize();\n const results = await this.indexer('docs').indexCollections!(options);\n this.emit('docsIndexed', results);\n return results;\n }\n\n /** Search documents only. */\n async searchDocs(query: string, options?: { collection?: string; k?: number; minScore?: number }): Promise<SearchResult[]> {\n await this.initialize();\n return this.indexer('docs').search!(query, options);\n }\n\n // ── Context metadata ─────────────────────────────\n\n /** Add context description for a collection path. */\n addContext(collection: string, path: string, context: string): void {\n this.indexer('docs').addContext!(collection, path, context);\n }\n\n /** Remove context for a collection path. */\n removeContext(collection: string, path: string): void {\n this.indexer('docs').removeContext!(collection, path);\n }\n\n /** List all context entries. */\n listContexts(): { collection: string; path: string; context: string }[] {\n return this.indexer('docs').listContexts!();\n }\n\n // ── Search (delegated to SearchAPI) ─────────────\n\n /**\n * Get formatted context for a task.\n * Returns markdown ready for system prompt injection.\n */\n async getContext(task: string, options: ContextOptions = {}): Promise<string> {\n await this.initialize();\n return this._searchAPI!.getContext(task, options);\n }\n\n /** Semantic search across all loaded modules. */\n async search(query: string, options?: {\n codeK?: number; gitK?: number; patternK?: number;\n minScore?: number; useMMR?: boolean;\n }): Promise<SearchResult[]> {\n await this.initialize();\n return this._searchAPI!.search(query, options);\n }\n\n /** Semantic search over code only. */\n async searchCode(query: string, k = 8): Promise<SearchResult[]> {\n await this.initialize();\n return this._searchAPI!.searchCode(query, k);\n }\n\n /** Semantic search over commits only. */\n async searchCommits(query: string, k = 8): Promise<SearchResult[]> {\n await this.initialize();\n return this._searchAPI!.searchCommits(query, k);\n }\n\n /**\n * Hybrid search: vector + BM25 fused with Reciprocal Rank Fusion.\n * Best quality — catches both exact keyword matches and conceptual similarities.\n */\n async hybridSearch(query: string, options?: {\n codeK?: number; gitK?: number; patternK?: number;\n minScore?: number; useMMR?: boolean;\n collections?: Record<string, number>;\n }): Promise<SearchResult[]> {\n await this.initialize();\n return this._searchAPI!.hybridSearch(query, options);\n }\n\n /** BM25 keyword search only (no embeddings needed). */\n searchBM25(query: string, options?: { codeK?: number; gitK?: number; patternK?: number }): SearchResult[] {\n this._requireInit('searchBM25');\n return this._searchAPI!.searchBM25(query, options);\n }\n\n /** Rebuild FTS5 indices. */\n rebuildFTS(): void {\n this._requireInit('rebuildFTS');\n this._searchAPI!.rebuildFTS();\n }\n\n // ── Queries ──────────────────────────────────────\n\n /** Get git history for a specific file. */\n async fileHistory(filePath: string, limit = 20): Promise<any[]> {\n this.indexer('git'); // throws if not loaded\n await this.initialize();\n return this._db.prepare(`\n SELECT c.short_hash, c.message, c.author, c.date, c.additions, c.deletions\n FROM git_commits c\n INNER JOIN commit_files cf ON c.id = cf.commit_id\n WHERE cf.file_path LIKE ? AND c.is_merge = 0\n ORDER BY c.timestamp DESC LIMIT ?\n `).all(`%${filePath}%`, limit) as any[];\n }\n\n /** Get co-edit suggestions for a file. */\n coEdits(filePath: string, limit = 5): CoEditSuggestion[] {\n this._requireInit('coEdits');\n return (this.indexer('git') as any).suggestCoEdits(filePath, limit);\n }\n\n // ── Stats ────────────────────────────────────────\n\n /** Get statistics for all loaded modules. */\n stats(): IndexStats {\n this._requireInit('stats');\n const result: IndexStats = {};\n\n if (this.has('code')) {\n const sh = this._sharedHnsw.get('code');\n result.code = {\n files: (this._db.prepare('SELECT COUNT(DISTINCT file_path) as c FROM code_chunks').get() as any).c,\n chunks: (this._db.prepare('SELECT COUNT(*) as c FROM code_chunks').get() as any).c,\n hnswSize: sh?.hnsw.size ?? 0,\n };\n }\n\n if (this.has('git')) {\n const sh = this._sharedHnsw.get('git');\n result.git = {\n commits: (this._db.prepare('SELECT COUNT(*) as c FROM git_commits').get() as any).c,\n filesTracked: (this._db.prepare('SELECT COUNT(DISTINCT file_path) as c FROM commit_files').get() as any).c,\n coEdits: (this._db.prepare('SELECT COUNT(*) as c FROM co_edits').get() as any).c,\n hnswSize: sh?.hnsw.size ?? 0,\n };\n }\n\n if (this.has('docs')) {\n result.documents = (this.indexer('docs') as any).stats();\n }\n\n return result;\n }\n\n // ── Watch ────────────────────────────────────────\n\n /**\n * Start watching for file changes and auto-re-index.\n * Works with built-in and custom indexers.\n */\n watch(options: WatchOptions = {}): Watcher {\n this._requireInit('watch');\n this._watcher?.close();\n this._watcher = createWatcher(\n async () => { await this.index(); },\n this._registry.raw,\n this._config.repoPath,\n options,\n );\n return this._watcher;\n }\n\n // ── Re-embed ─────────────────────────────────────\n\n /**\n * Re-embed all existing text with the current embedding provider.\n * Use this when switching providers (e.g. Local → OpenAI).\n */\n async reembed(options: ReembedOptions = {}): Promise<ReembedResult> {\n this._requireInit('reembed');\n\n const hnswMap = new Map<string, { hnsw: HNSWIndex; vecs: Map<number, Float32Array> }>();\n\n if (this._kvHnsw) hnswMap.set('kv', { hnsw: this._kvHnsw, vecs: this._kvVecs });\n\n for (const [type, shared] of this._sharedHnsw) {\n hnswMap.set(type, { hnsw: shared.hnsw, vecs: shared.vecCache });\n }\n\n for (const type of ['memory', 'notes', 'docs'] as const) {\n const mod = this._registry.firstByType(type) as any;\n if (mod?.hnsw) hnswMap.set(type, { hnsw: mod.hnsw, vecs: mod.vecCache });\n }\n\n const result = await reembedAll(this._db, this._embedding, hnswMap, options);\n this.emit('reembedded', result);\n return result;\n }\n\n // ── Lifecycle ────────────────────────────────────\n\n /** Close database and release resources. */\n close(): void {\n this._watcher?.close();\n for (const indexer of this._registry.all) indexer.close?.();\n this._db?.close();\n this._initialized = false;\n this._collections.clear();\n this._sharedHnsw.clear();\n this._kvVecs.clear();\n this._kvHnsw = undefined as any;\n this._searchAPI = undefined;\n this._indexAPI = undefined;\n this._registry.clear();\n }\n\n /** Whether the brainbank has been initialized. */\n get isInitialized(): boolean { return this._initialized; }\n\n /** The resolved configuration. */\n get config(): Readonly<ResolvedConfig> { return this._config; }\n\n // ── Internal guard ───────────────────────────────\n\n private _requireInit(method: string): void {\n if (!this._initialized)\n throw new Error(`BrainBank: Not initialized. Call await brain.initialize() before ${method}().`);\n }\n}\n","/**\n * BrainBank — Indexer Registry\n *\n * Manages registration and lookup of indexers.\n * Extracted from BrainBank so the facade stays focused on orchestration.\n *\n * Responsibilities:\n * - Store indexers by name\n * - Type-prefix matching ('code' finds 'code:frontend', 'code:backend')\n * - Alias resolution (currently none; add here if needed)\n * - Consistent error messages on missing indexers\n */\n\nimport type { Indexer } from '../indexers/base.ts';\n\n/** Shorthand aliases that map public names to canonical indexer names. */\nconst ALIASES: Readonly<Record<string, string>> = {\n};\n\nexport class IndexerRegistry {\n private _map = new Map<string, Indexer>();\n\n // ── Registration ────────────────────────────────\n\n /** Store an indexer. Duplicate names silently overwrite. */\n register(indexer: Indexer): void {\n this._map.set(indexer.name, indexer);\n }\n\n // ── Lookup ──────────────────────────────────────\n\n /**\n * Check whether an indexer is registered.\n * Supports type-prefix matching: `has('code')` returns true if\n * 'code', 'code:frontend', or 'code:backend' is registered.\n */\n has(name: string): boolean {\n if (this._map.has(name)) return true;\n for (const key of this._map.keys()) {\n if (key.startsWith(name + ':')) return true;\n }\n return false;\n }\n\n /**\n * Get an indexer by name. Throws a descriptive error if not found.\n *\n * Resolution order:\n * 1. Alias map (currently empty)\n * 2. Exact match\n * 3. First type-prefix match ('code' → 'code:frontend')\n */\n get<T extends Indexer = Indexer>(name: string): T {\n const resolved = ALIASES[name] ?? name;\n\n const exact = this._map.get(resolved);\n if (exact) return exact as T;\n\n const prefixed = this.firstByType(name);\n if (prefixed) return prefixed as T;\n\n throw new Error(\n `BrainBank: Indexer '${name}' is not loaded. ` +\n `Add .use(${name}()) to your BrainBank instance.`,\n );\n }\n\n /**\n * Return every indexer whose name equals `type` or starts with `type + ':'`.\n * Example: allByType('code') → [code, code:frontend, code:backend]\n */\n allByType(type: string): Indexer[] {\n return [...this._map.values()].filter(\n m => m.name === type || m.name.startsWith(type + ':'),\n );\n }\n\n /** Return the first indexer that matches the type prefix, or undefined. */\n firstByType(type: string): Indexer | undefined {\n for (const m of this._map.values()) {\n if (m.name === type || m.name.startsWith(type + ':')) return m;\n }\n return undefined;\n }\n\n // ── Accessors ───────────────────────────────────\n\n /** All registered indexer names (insertion order). */\n get names(): string[] {\n return [...this._map.keys()];\n }\n\n /** All registered indexer instances (insertion order). */\n get all(): Indexer[] {\n return [...this._map.values()];\n }\n\n /**\n * Underlying Map.\n * Prefer `all`, `allByType`, or `firstByType` everywhere else.\n */\n get raw(): Map<string, Indexer> {\n return this._map;\n }\n\n // ── Lifecycle ───────────────────────────────────\n\n /** Remove all registered indexers. Called by BrainBank.close(). */\n clear(): void {\n this._map.clear();\n }\n}\n","/**\n * BrainBank — Database\n * \n * Thin wrapper over better-sqlite3.\n * Handles WAL mode, directory creation, schema init, and transactions.\n */\n\nimport BetterSqlite3 from 'better-sqlite3';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { createSchema } from './schema.ts';\n\nexport class Database {\n readonly db: BetterSqlite3.Database;\n\n constructor(dbPath: string) {\n // Ensure parent directory exists\n const dir = path.dirname(dbPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n this.db = new BetterSqlite3(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('foreign_keys = ON');\n\n // Initialize schema\n createSchema(this.db);\n }\n\n /**\n * Run a function inside a transaction.\n * Auto-commits on success, auto-rollbacks on error.\n */\n transaction<T>(fn: () => T): T {\n const tx = this.db.transaction(fn);\n return tx();\n }\n\n /**\n * Run a prepared statement on multiple rows.\n * Wraps in a single transaction for performance.\n */\n batch<T extends any[]>(sql: string, rows: T[]): void {\n const stmt = this.db.prepare(sql);\n const tx = this.db.transaction(() => {\n for (const row of rows) {\n stmt.run(...row);\n }\n });\n tx();\n }\n\n /** Prepare a reusable statement. */\n prepare(sql: string): BetterSqlite3.Statement {\n return this.db.prepare(sql);\n }\n\n /** Execute raw SQL (no results). */\n exec(sql: string): void {\n this.db.exec(sql);\n }\n\n /** Close the database. */\n close(): void {\n this.db.close();\n }\n}\n","/**\n * BrainBank — SQLite Schema\n * \n * Idempotent schema creation for the knowledge database.\n * Uses better-sqlite3 directly.\n */\n\nimport type Database from 'better-sqlite3';\n\nexport const SCHEMA_VERSION = 4;\n\n/**\n * Create all tables and indices.\n * Safe to call multiple times — uses IF NOT EXISTS.\n */\nexport function createSchema(db: Database.Database): void {\n db.exec(`\n -- ── Schema versioning ──────────────────────────\n CREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n INSERT OR IGNORE INTO schema_version (version) VALUES (${SCHEMA_VERSION});\n\n -- ── Code chunks ────────────────────────────────\n CREATE TABLE IF NOT EXISTS code_chunks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n file_path TEXT NOT NULL,\n chunk_type TEXT NOT NULL,\n name TEXT,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n content TEXT NOT NULL,\n language TEXT NOT NULL,\n file_hash TEXT,\n indexed_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS code_vectors (\n chunk_id INTEGER PRIMARY KEY REFERENCES code_chunks(id) ON DELETE CASCADE,\n embedding BLOB NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS indexed_files (\n file_path TEXT PRIMARY KEY,\n file_hash TEXT NOT NULL,\n indexed_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n -- ── Git history ────────────────────────────────\n CREATE TABLE IF NOT EXISTS git_commits (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n hash TEXT UNIQUE NOT NULL,\n short_hash TEXT NOT NULL,\n message TEXT NOT NULL,\n author TEXT NOT NULL,\n date TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n files_json TEXT NOT NULL,\n diff TEXT,\n additions INTEGER DEFAULT 0,\n deletions INTEGER DEFAULT 0,\n is_merge INTEGER DEFAULT 0\n );\n\n CREATE TABLE IF NOT EXISTS commit_files (\n commit_id INTEGER NOT NULL REFERENCES git_commits(id),\n file_path TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS co_edits (\n file_a TEXT NOT NULL,\n file_b TEXT NOT NULL,\n count INTEGER NOT NULL DEFAULT 1,\n PRIMARY KEY (file_a, file_b)\n );\n\n CREATE TABLE IF NOT EXISTS git_vectors (\n commit_id INTEGER PRIMARY KEY REFERENCES git_commits(id) ON DELETE CASCADE,\n embedding BLOB NOT NULL\n );\n\n -- ── Agent memory ───────────────────────────────\n CREATE TABLE IF NOT EXISTS memory_patterns (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n task_type TEXT NOT NULL,\n task TEXT NOT NULL,\n approach TEXT NOT NULL,\n outcome TEXT,\n success_rate REAL NOT NULL DEFAULT 0.5,\n critique TEXT,\n tokens_used INTEGER,\n latency_ms INTEGER,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS memory_vectors (\n pattern_id INTEGER PRIMARY KEY REFERENCES memory_patterns(id) ON DELETE CASCADE,\n embedding BLOB NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS distilled_strategies (\n task_type TEXT PRIMARY KEY,\n strategy TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.8,\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n -- ── Indices ────────────────────────────────────\n CREATE INDEX IF NOT EXISTS idx_cc_file ON code_chunks(file_path);\n CREATE INDEX IF NOT EXISTS idx_cf_path ON commit_files(file_path);\n CREATE INDEX IF NOT EXISTS idx_gc_ts ON git_commits(timestamp DESC);\n CREATE INDEX IF NOT EXISTS idx_gc_hash ON git_commits(hash);\n CREATE INDEX IF NOT EXISTS idx_mp_type ON memory_patterns(task_type);\n CREATE INDEX IF NOT EXISTS idx_mp_success ON memory_patterns(success_rate);\n CREATE INDEX IF NOT EXISTS idx_mp_created ON memory_patterns(created_at);\n\n -- ── FTS5 Full-Text Search ─────────────────────\n -- Code chunks: search by file path, name, and content\n CREATE VIRTUAL TABLE IF NOT EXISTS fts_code USING fts5(\n file_path,\n name,\n content,\n content='code_chunks',\n content_rowid='id',\n tokenize='porter unicode61'\n );\n\n -- Git commits: search by message, author, and diff\n CREATE VIRTUAL TABLE IF NOT EXISTS fts_commits USING fts5(\n message,\n author,\n diff,\n content='git_commits',\n content_rowid='id',\n tokenize='porter unicode61'\n );\n\n -- Memory patterns: search by task type, task, approach, and critique\n CREATE VIRTUAL TABLE IF NOT EXISTS fts_patterns USING fts5(\n task_type,\n task,\n approach,\n critique,\n content='memory_patterns',\n content_rowid='id',\n tokenize='porter unicode61'\n );\n\n -- ── FTS5 Sync Triggers ────────────────────────\n -- Auto-sync FTS indices on INSERT/UPDATE/DELETE\n\n CREATE TRIGGER IF NOT EXISTS trg_fts_code_insert AFTER INSERT ON code_chunks BEGIN\n INSERT INTO fts_code(rowid, file_path, name, content)\n VALUES (new.id, new.file_path, COALESCE(new.name, ''), new.content);\n END;\n CREATE TRIGGER IF NOT EXISTS trg_fts_code_delete AFTER DELETE ON code_chunks BEGIN\n INSERT INTO fts_code(fts_code, rowid, file_path, name, content)\n VALUES ('delete', old.id, old.file_path, COALESCE(old.name, ''), old.content);\n END;\n\n CREATE TRIGGER IF NOT EXISTS trg_fts_commits_insert AFTER INSERT ON git_commits BEGIN\n INSERT INTO fts_commits(rowid, message, author, diff)\n VALUES (new.id, new.message, new.author, COALESCE(new.diff, ''));\n END;\n CREATE TRIGGER IF NOT EXISTS trg_fts_commits_delete AFTER DELETE ON git_commits BEGIN\n INSERT INTO fts_commits(fts_commits, rowid, message, author, diff)\n VALUES ('delete', old.id, old.message, old.author, COALESCE(old.diff, ''));\n END;\n\n CREATE TRIGGER IF NOT EXISTS trg_fts_patterns_insert AFTER INSERT ON memory_patterns BEGIN\n INSERT INTO fts_patterns(rowid, task_type, task, approach, critique)\n VALUES (new.id, new.task_type, new.task, new.approach, COALESCE(new.critique, ''));\n END;\n CREATE TRIGGER IF NOT EXISTS trg_fts_patterns_delete AFTER DELETE ON memory_patterns BEGIN\n INSERT INTO fts_patterns(fts_patterns, rowid, task_type, task, approach, critique)\n VALUES ('delete', old.id, old.task_type, old.task, old.approach, COALESCE(old.critique, ''));\n END;\n\n -- ── Note Memory ───────────────────────────────\n CREATE TABLE IF NOT EXISTS note_memories (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n title TEXT NOT NULL,\n summary TEXT NOT NULL,\n decisions_json TEXT NOT NULL DEFAULT '[]',\n files_json TEXT NOT NULL DEFAULT '[]',\n patterns_json TEXT NOT NULL DEFAULT '[]',\n open_json TEXT NOT NULL DEFAULT '[]',\n tags_json TEXT NOT NULL DEFAULT '[]',\n tier TEXT NOT NULL DEFAULT 'short',\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS note_vectors (\n note_id INTEGER PRIMARY KEY REFERENCES note_memories(id) ON DELETE CASCADE,\n embedding BLOB NOT NULL\n );\n\n CREATE VIRTUAL TABLE IF NOT EXISTS fts_notes USING fts5(\n title,\n summary,\n decisions,\n patterns,\n tags,\n content='note_memories',\n content_rowid='id',\n tokenize='porter unicode61'\n );\n\n CREATE TRIGGER IF NOT EXISTS trg_fts_notes_insert AFTER INSERT ON note_memories BEGIN\n INSERT INTO fts_notes(rowid, title, summary, decisions, patterns, tags)\n VALUES (new.id, new.title, new.summary, new.decisions_json, new.patterns_json, new.tags_json);\n END;\n CREATE TRIGGER IF NOT EXISTS trg_fts_notes_delete AFTER DELETE ON note_memories BEGIN\n INSERT INTO fts_notes(fts_notes, rowid, title, summary, decisions, patterns, tags)\n VALUES ('delete', old.id, old.title, old.summary, old.decisions_json, old.patterns_json, old.tags_json);\n END;\n\n CREATE INDEX IF NOT EXISTS idx_nm_tier ON note_memories(tier);\n CREATE INDEX IF NOT EXISTS idx_nm_created ON note_memories(created_at DESC);\n\n -- ── Document Collections ──────────────────────\n CREATE TABLE IF NOT EXISTS collections (\n name TEXT PRIMARY KEY,\n path TEXT NOT NULL,\n pattern TEXT NOT NULL DEFAULT '**/*.md',\n ignore_json TEXT NOT NULL DEFAULT '[]',\n context TEXT,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS doc_chunks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n collection TEXT NOT NULL REFERENCES collections(name) ON DELETE CASCADE,\n file_path TEXT NOT NULL,\n title TEXT NOT NULL,\n content TEXT NOT NULL,\n seq INTEGER NOT NULL DEFAULT 0,\n pos INTEGER NOT NULL DEFAULT 0,\n content_hash TEXT NOT NULL,\n indexed_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS doc_vectors (\n chunk_id INTEGER PRIMARY KEY REFERENCES doc_chunks(id) ON DELETE CASCADE,\n embedding BLOB NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_dc_collection ON doc_chunks(collection);\n CREATE INDEX IF NOT EXISTS idx_dc_file ON doc_chunks(file_path);\n CREATE INDEX IF NOT EXISTS idx_dc_hash ON doc_chunks(content_hash);\n\n CREATE VIRTUAL TABLE IF NOT EXISTS fts_docs USING fts5(\n title,\n content,\n file_path,\n collection,\n content='doc_chunks',\n content_rowid='id',\n tokenize='porter unicode61'\n );\n\n CREATE TRIGGER IF NOT EXISTS trg_fts_docs_insert AFTER INSERT ON doc_chunks BEGIN\n INSERT INTO fts_docs(rowid, title, content, file_path, collection)\n VALUES (new.id, new.title, new.content, new.file_path, new.collection);\n END;\n CREATE TRIGGER IF NOT EXISTS trg_fts_docs_delete AFTER DELETE ON doc_chunks BEGIN\n INSERT INTO fts_docs(fts_docs, rowid, title, content, file_path, collection)\n VALUES ('delete', old.id, old.title, old.content, old.file_path, old.collection);\n END;\n\n -- ── Path Contexts ─────────────────────────────\n CREATE TABLE IF NOT EXISTS path_contexts (\n collection TEXT NOT NULL,\n path TEXT NOT NULL,\n context TEXT NOT NULL,\n PRIMARY KEY (collection, path)\n );\n\n -- ── Dynamic Collections (KV Store) ───────────\n CREATE TABLE IF NOT EXISTS kv_data (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n collection TEXT NOT NULL,\n content TEXT NOT NULL,\n meta_json TEXT NOT NULL DEFAULT '{}',\n tags_json TEXT NOT NULL DEFAULT '[]',\n expires_at INTEGER,\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\n );\n\n CREATE TABLE IF NOT EXISTS kv_vectors (\n data_id INTEGER PRIMARY KEY REFERENCES kv_data(id) ON DELETE CASCADE,\n embedding BLOB NOT NULL\n );\n\n CREATE VIRTUAL TABLE IF NOT EXISTS fts_kv USING fts5(\n content,\n collection,\n content='kv_data',\n content_rowid='id',\n tokenize='porter unicode61'\n );\n\n CREATE TRIGGER IF NOT EXISTS trg_fts_kv_insert AFTER INSERT ON kv_data BEGIN\n INSERT INTO fts_kv(rowid, content, collection)\n VALUES (new.id, new.content, new.collection);\n END;\n CREATE TRIGGER IF NOT EXISTS trg_fts_kv_delete AFTER DELETE ON kv_data BEGIN\n INSERT INTO fts_kv(fts_kv, rowid, content, collection)\n VALUES ('delete', old.id, old.content, old.collection);\n END;\n\n CREATE INDEX IF NOT EXISTS idx_kv_collection ON kv_data(collection);\n CREATE INDEX IF NOT EXISTS idx_kv_created ON kv_data(created_at DESC);\n\n -- ── Embedding Metadata ───────────────────────\n CREATE TABLE IF NOT EXISTS embedding_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n}\n\n/**\n * Get the current schema version from the database.\n */\nexport function getSchemaVersion(db: Database.Database): number {\n try {\n const row = db.prepare('SELECT MAX(version) as v FROM schema_version').get() as any;\n return row?.v ?? 0;\n } catch {\n return 0;\n }\n}\n","/**\n * BrainBank — Re-embedding Engine\n * \n * Regenerates all vectors without re-indexing.\n * Reads existing text from SQLite, embeds with the current provider,\n * and replaces vector BLOBs. No file I/O, no git parsing, no re-chunking.\n * \n * Usage:\n * const result = await brain.reembed({ onProgress });\n * // → { code: 1200, git: 500, docs: 80, kv: 45, notes: 12, total: 1837 }\n */\n\nimport type { Database } from '../db/database.ts';\nimport type { EmbeddingProvider, ProgressCallback } from '../types.ts';\nimport type { HNSWIndex } from '../providers/vector/hnsw.ts';\n\n// ── Table Definitions ───────────────────────────────\n\ninterface ReembedTable {\n /** Human-readable name (for progress) */\n name: string;\n /** Table with text content */\n textTable: string;\n /** Table with vector BLOBs */\n vectorTable: string;\n /** PK column in text table */\n idColumn: string;\n /** FK column in vector table */\n fkColumn: string;\n /** Build the embedding text from a row (same logic as each indexer) */\n textBuilder: (row: any) => string;\n}\n\nconst TABLES: ReembedTable[] = [\n {\n name: 'code',\n textTable: 'code_chunks',\n vectorTable: 'code_vectors',\n idColumn: 'id',\n fkColumn: 'chunk_id',\n textBuilder: (r) => [\n `File: ${r.file_path}`,\n r.name ? `${r.chunk_type}: ${r.name}` : r.chunk_type,\n r.content,\n ].join('\\n'),\n },\n {\n name: 'git',\n textTable: 'git_commits',\n vectorTable: 'git_vectors',\n idColumn: 'id',\n fkColumn: 'commit_id',\n // Must match git-engine.ts:119-125 exactly\n textBuilder: (r) => [\n `Commit: ${r.message}`,\n `Author: ${r.author}`,\n `Date: ${r.date}`,\n r.files_json && r.files_json !== '[]'\n ? `Files: ${JSON.parse(r.files_json).join(', ')}`\n : '',\n r.diff ? `Changes:\\n${r.diff.slice(0, 2000)}` : '',\n ].filter(Boolean).join('\\n'),\n },\n {\n name: 'memory',\n textTable: 'memory_patterns',\n vectorTable: 'memory_vectors',\n idColumn: 'id',\n fkColumn: 'pattern_id',\n // Must match memory/pattern-store.ts:49 exactly\n textBuilder: (r) => `${r.task_type} ${r.task} ${r.approach}`,\n },\n {\n name: 'notes',\n textTable: 'note_memories',\n vectorTable: 'note_vectors',\n idColumn: 'id',\n fkColumn: 'note_id',\n // Must match notes/engine.ts:90 exactly\n textBuilder: (r) => {\n const decisions = (JSON.parse(r.decisions_json || '[]') as string[]).join('. ');\n const patterns = (JSON.parse(r.patterns_json || '[]') as string[]).join('. ');\n return `${r.title}\\n${r.summary}\\n${decisions}\\n${patterns}`;\n },\n },\n {\n name: 'docs',\n textTable: 'doc_chunks',\n vectorTable: 'doc_vectors',\n idColumn: 'id',\n fkColumn: 'chunk_id',\n // Must match docs-engine.ts:160 exactly\n textBuilder: (r) => `title: ${r.title ?? ''} | text: ${r.content}`,\n },\n {\n name: 'kv',\n textTable: 'kv_data',\n vectorTable: 'kv_vectors',\n idColumn: 'id',\n fkColumn: 'data_id',\n textBuilder: (r) => r.content,\n },\n];\n\n// ── Result ──────────────────────────────────────────\n\nexport interface ReembedResult {\n code: number;\n git: number;\n memory: number;\n notes: number;\n docs: number;\n kv: number;\n total: number;\n}\n\nexport interface ReembedOptions {\n /** Progress callback: (tableName, current, total) */\n onProgress?: ProgressCallback;\n /** Batch size for embedBatch. Default: 50 */\n batchSize?: number;\n}\n\n// ── Engine ──────────────────────────────────────────\n\n/**\n * Re-embed all existing text with the current embedding provider.\n * Does NOT re-parse files, git, or documents — only replaces vectors.\n */\nexport async function reembedAll(\n db: Database,\n embedding: EmbeddingProvider,\n hnswMap: Map<string, { hnsw: HNSWIndex; vecs: Map<number, Float32Array> }>,\n options: ReembedOptions = {},\n): Promise<ReembedResult> {\n const { batchSize = 50, onProgress } = options;\n const result: Record<string, number> = {};\n let total = 0;\n\n for (const table of TABLES) {\n const count = await reembedTable(db, embedding, table, batchSize, onProgress);\n result[table.name] = count;\n total += count;\n\n // Rebuild HNSW if available\n const entry = hnswMap.get(table.name);\n if (entry && count > 0) {\n await rebuildHnsw(db, table, entry.hnsw, entry.vecs);\n }\n }\n\n // Update embedding metadata\n const meta = {\n provider: embedding.constructor?.name ?? 'unknown',\n dims: String(embedding.dims),\n reembedded_at: new Date().toISOString(),\n };\n const upsert = db.prepare(\n 'INSERT OR REPLACE INTO embedding_meta (key, value) VALUES (?, ?)'\n );\n for (const [k, v] of Object.entries(meta)) {\n upsert.run(k, v);\n }\n\n return {\n code: result.code ?? 0,\n git: result.git ?? 0,\n memory: result.memory ?? 0,\n notes: result.notes ?? 0,\n docs: result.docs ?? 0,\n kv: result.kv ?? 0,\n total,\n };\n}\n\n/** Re-embed a single table. Returns count of vectors regenerated. */\nasync function reembedTable(\n db: Database,\n embedding: EmbeddingProvider,\n table: ReembedTable,\n batchSize: number,\n onProgress?: ProgressCallback,\n): Promise<number> {\n // Use pagination to avoid OOM on large tables\n const totalCount = (db.prepare(\n `SELECT COUNT(*) as c FROM ${table.textTable}`\n ).get() as any).c;\n\n if (totalCount === 0) return 0;\n\n // Generate ALL new vectors first, before touching the database.\n // This prevents data loss if embedBatch() fails partway through.\n const allNewVectors: { id: number; vec: Float32Array }[] = [];\n\n let processed = 0;\n for (let offset = 0; offset < totalCount; offset += batchSize) {\n const batch = db.prepare(\n `SELECT * FROM ${table.textTable} LIMIT ? OFFSET ?`\n ).all(batchSize, offset) as any[];\n const texts = batch.map((r: any) => table.textBuilder(r));\n const vectors = await embedding.embedBatch(texts);\n\n for (let j = 0; j < batch.length; j++) {\n allNewVectors.push({ id: batch[j][table.idColumn], vec: vectors[j] });\n }\n\n processed += batch.length;\n onProgress?.(table.name, processed, totalCount);\n }\n\n // All embeddings succeeded — now atomically swap old → new\n const insertVec = db.prepare(\n `INSERT INTO ${table.vectorTable} (${table.fkColumn}, embedding) VALUES (?, ?)`\n );\n\n db.transaction(() => {\n db.prepare(`DELETE FROM ${table.vectorTable}`).run();\n for (const { id, vec } of allNewVectors) {\n insertVec.run(id, Buffer.from(vec.buffer));\n }\n });\n\n return processed;\n}\n\n/** Rebuild HNSW index from vector table. */\nasync function rebuildHnsw(\n db: Database,\n table: ReembedTable,\n hnsw: HNSWIndex,\n vecs: Map<number, Float32Array>,\n): Promise<void> {\n // Wipe stale vectors before repopulating\n vecs.clear();\n hnsw.reinit();\n\n const rows = db.prepare(\n `SELECT ${table.fkColumn} as id, embedding FROM ${table.vectorTable}`\n ).all() as any[];\n\n for (const row of rows) {\n const buf = Buffer.from(row.embedding);\n const vec = new Float32Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));\n hnsw.add(vec, row.id);\n vecs.set(row.id, vec);\n }\n}\n\n// ── Provider Detection ──────────────────────────────\n\n/** Get stored embedding metadata. Returns null if not set. */\nexport function getEmbeddingMeta(db: Database): { provider: string; dims: number } | null {\n try {\n const provider = db.prepare(\n \"SELECT value FROM embedding_meta WHERE key = 'provider'\"\n ).get() as any;\n const dims = db.prepare(\n \"SELECT value FROM embedding_meta WHERE key = 'dims'\"\n ).get() as any;\n\n if (!provider || !dims) return null;\n return { provider: provider.value, dims: Number(dims.value) };\n } catch {\n return null;\n }\n}\n\n/** Store current provider info. */\nexport function setEmbeddingMeta(db: Database, embedding: EmbeddingProvider): void {\n const upsert = db.prepare(\n 'INSERT OR REPLACE INTO embedding_meta (key, value) VALUES (?, ?)'\n );\n upsert.run('provider', embedding.constructor?.name ?? 'unknown');\n upsert.run('dims', String(embedding.dims));\n upsert.run('indexed_at', new Date().toISOString());\n}\n\n/** Check if the configured provider differs from what's stored. */\nexport function detectProviderMismatch(\n db: Database,\n embedding: EmbeddingProvider,\n): { mismatch: boolean; stored: string; current: string } | null {\n const meta = getEmbeddingMeta(db);\n if (!meta) return null; // First time, no mismatch\n\n const currentName = embedding.constructor?.name ?? 'unknown';\n const mismatch = meta.dims !== embedding.dims || meta.provider !== currentName;\n\n return {\n mismatch,\n stored: `${meta.provider}/${meta.dims}`,\n current: `${currentName}/${embedding.dims}`,\n };\n}\n","/**\n * BrainBank — Initializer\n *\n * Two-phase initialization keeps the dependency ordering correct:\n * Phase 1 (earlyInit) — db, embedding, kvHnsw. Must be assigned to\n * `this` on BrainBank before phase 2, so that collection()\n * works when indexers call ctx.collection() during initialize().\n * Phase 2 (lateInit) — loads vectors, runs indexers, builds search.\n */\n\nimport { Database } from '../db/database.ts';\nimport { HNSWIndex } from '../providers/vector/hnsw.ts';\nimport { LocalEmbedding } from '../providers/embeddings/local.ts';\nimport { MultiIndexSearch } from '../search/vector/multi-index.ts';\nimport { BM25Search } from '../search/keyword/bm25.ts';\nimport { ContextBuilder } from './context-builder.ts';\nimport { setEmbeddingMeta, detectProviderMismatch } from '../services/reembed.ts';\nimport type { IndexerRegistry } from './registry.ts';\nimport type { Collection } from './collection.ts';\nimport type { ResolvedConfig, EmbeddingProvider } from '../types.ts';\nimport type { IndexerContext } from '../indexers/base.ts';\n\n// ── Result types ─────────────────────────────────────\n\n/** Available after phase 1 — before indexers run. */\nexport interface EarlyInit {\n db: Database;\n embedding: EmbeddingProvider;\n kvHnsw: HNSWIndex;\n skipVectorLoad: boolean;\n}\n\n/** Available after phase 2 — once indexers have initialized. */\nexport interface LateInit {\n search?: MultiIndexSearch;\n bm25?: BM25Search;\n contextBuilder?: ContextBuilder;\n}\n\n// ── Phase 1 ──────────────────────────────────────────\n\nexport async function earlyInit(\n config: ResolvedConfig,\n emit: (event: string, data: any) => void,\n): Promise<EarlyInit> {\n const db = new Database(config.dbPath);\n const embedding: EmbeddingProvider = config.embeddingProvider ?? new LocalEmbedding();\n\n // Must check BEFORE setEmbeddingMeta overwrites stored values\n const mismatch = detectProviderMismatch(db, embedding);\n const skipVectorLoad = !!mismatch?.mismatch;\n\n if (skipVectorLoad) {\n emit('warning', {\n type: 'provider_mismatch',\n previous: mismatch!.stored,\n current: mismatch!.current,\n message: 'Embedding provider changed — vectors not loaded. Run brain.reembed() to regenerate.',\n });\n }\n\n setEmbeddingMeta(db, embedding);\n\n const kvHnsw = new HNSWIndex(\n config.embeddingDims,\n config.maxElements ?? 500_000,\n config.hnswM,\n config.hnswEfConstruction,\n config.hnswEfSearch,\n );\n await kvHnsw.init();\n\n return { db, embedding, kvHnsw, skipVectorLoad };\n}\n\n// ── Phase 2 ──────────────────────────────────────────\n\nexport async function lateInit(\n early: EarlyInit,\n config: ResolvedConfig,\n registry: IndexerRegistry,\n sharedHnsw: Map<string, { hnsw: HNSWIndex; vecCache: Map<number, Float32Array> }>,\n kvVecs: Map<number, Float32Array>,\n getCollection: (name: string) => Collection,\n): Promise<LateInit> {\n const { db, embedding, kvHnsw, skipVectorLoad } = early;\n\n if (!skipVectorLoad) {\n loadVectors(db, 'kv_vectors', 'data_id', kvHnsw, kvVecs);\n }\n\n const ctx: IndexerContext = {\n db,\n embedding,\n config,\n\n createHnsw: (maxElements?: number) =>\n new HNSWIndex(\n config.embeddingDims,\n maxElements ?? config.maxElements,\n config.hnswM,\n config.hnswEfConstruction,\n config.hnswEfSearch,\n ).init(),\n\n loadVectors: (table, idCol, hnsw, cache) => {\n if (skipVectorLoad) return;\n loadVectors(db, table, idCol, hnsw, cache);\n },\n\n getOrCreateSharedHnsw: async (type, maxElements) => {\n const existing = sharedHnsw.get(type);\n if (existing) return { ...existing, isNew: false };\n\n const hnsw = await new HNSWIndex(\n config.embeddingDims,\n maxElements ?? config.maxElements,\n config.hnswM,\n config.hnswEfConstruction,\n config.hnswEfSearch,\n ).init();\n\n const vecCache = new Map<number, Float32Array>();\n sharedHnsw.set(type, { hnsw, vecCache });\n return { hnsw, vecCache, isNew: true };\n },\n\n collection: getCollection,\n };\n\n for (const mod of registry.all) {\n await mod.initialize(ctx);\n }\n\n // ── Cross-module search ──────────────────────────\n const codeMod = sharedHnsw.get('code');\n const gitMod = sharedHnsw.get('git');\n const memMod = registry.firstByType('memory') as any;\n\n let search: MultiIndexSearch | undefined;\n let bm25: BM25Search | undefined;\n let contextBuilder: ContextBuilder | undefined;\n\n if (codeMod || gitMod || memMod) {\n search = new MultiIndexSearch({\n db,\n codeHnsw: codeMod?.hnsw,\n gitHnsw: gitMod?.hnsw,\n patternHnsw: memMod?.hnsw,\n codeVecs: codeMod?.vecCache ?? new Map(),\n gitVecs: gitMod?.vecCache ?? new Map(),\n patternVecs: memMod?.vecCache ?? new Map(),\n embedding,\n reranker: config.reranker,\n });\n bm25 = new BM25Search(db);\n }\n\n if (search) {\n const firstGit = registry.firstByType('git') as any;\n contextBuilder = new ContextBuilder(search, firstGit?.coEdits);\n }\n\n return { search, bm25, contextBuilder };\n}\n\n// ── Shared helper ─────────────────────────────────────\n\nexport function loadVectors(\n db: Database,\n table: string,\n idCol: string,\n hnsw: HNSWIndex,\n cache: Map<number, Float32Array>,\n): void {\n const rows = db.prepare(`SELECT ${idCol}, embedding FROM ${table}`).all() as any[];\n for (const row of rows) {\n const vec = new Float32Array(\n row.embedding.buffer.slice(\n row.embedding.byteOffset,\n row.embedding.byteOffset + row.embedding.byteLength,\n ),\n );\n hnsw.add(vec, row[idCol]);\n cache.set(row[idCol], vec);\n }\n}\n","/**\n * BrainBank — Search API\n *\n * All search and context operations in one place.\n * Composed from MultiIndexSearch, BM25, ContextBuilder, and KV collections.\n * Always created after initialization (even when search services are absent),\n * so BrainBank can unconditionally delegate to it.\n */\n\nimport type { MultiIndexSearch } from '../search/vector/multi-index.ts';\nimport type { BM25Search } from '../search/keyword/bm25.ts';\nimport type { ContextBuilder } from './context-builder.ts';\nimport type { Collection } from './collection.ts';\nimport type { IndexerRegistry } from './registry.ts';\nimport type { ResolvedConfig, SearchResult, ContextOptions } from '../types.ts';\nimport { reciprocalRankFusion } from '../search/rrf.ts';\n\nexport interface SearchAPIDeps {\n search?: MultiIndexSearch;\n bm25?: BM25Search;\n contextBuilder?: ContextBuilder;\n registry: IndexerRegistry;\n config: ResolvedConfig;\n searchDocs(query: string, options?: { collection?: string; k?: number; minScore?: number }): Promise<SearchResult[]>;\n collection(name: string): Collection;\n}\n\nexport class SearchAPI {\n constructor(private _d: SearchAPIDeps) {}\n\n // ── Vector ──────────────────────────────────────\n\n async search(query: string, options?: {\n codeK?: number; gitK?: number; patternK?: number;\n minScore?: number; useMMR?: boolean;\n }): Promise<SearchResult[]> {\n if (!this._d.search) {\n return this._d.registry.has('docs')\n ? this._d.searchDocs(query, { k: 8 })\n : [];\n }\n return this._d.search.search(query, options);\n }\n\n async searchCode(query: string, k = 8): Promise<SearchResult[]> {\n if (!this._d.registry.firstByType('code'))\n throw new Error(\"BrainBank: Indexer 'code' is not loaded. Add .use(code()) to your BrainBank instance.\");\n if (!this._d.search)\n throw new Error('BrainBank: MultiIndexSearch not available. Ensure code indexer is loaded.');\n return this._d.search.search(query, { codeK: k, gitK: 0, patternK: 0 });\n }\n\n async searchCommits(query: string, k = 8): Promise<SearchResult[]> {\n if (!this._d.registry.firstByType('git'))\n throw new Error(\"BrainBank: Indexer 'git' is not loaded. Add .use(git()) to your BrainBank instance.\");\n if (!this._d.search)\n throw new Error('BrainBank: MultiIndexSearch not available. Ensure git indexer is loaded.');\n return this._d.search.search(query, { codeK: 0, gitK: k, patternK: 0 });\n }\n\n // ── Hybrid ──────────────────────────────────────\n\n async hybridSearch(query: string, options?: {\n codeK?: number; gitK?: number; patternK?: number;\n minScore?: number; useMMR?: boolean;\n collections?: Record<string, number>;\n }): Promise<SearchResult[]> {\n const cols = options?.collections ?? {};\n const codeK = cols.code ?? options?.codeK ?? 6;\n const gitK = cols.git ?? options?.gitK ?? 5;\n const docsK = cols.docs ?? 8;\n\n const resultLists: SearchResult[][] = [];\n\n if (this._d.search) {\n const [vec, kw] = await Promise.all([\n this._d.search.search(query, { ...options, codeK, gitK }),\n Promise.resolve(this._d.bm25!.search(query, { codeK, gitK })),\n ]);\n resultLists.push(vec, kw);\n }\n\n if (this._d.registry.has('docs')) {\n const docs = await this._d.searchDocs(query, { k: docsK });\n if (docs.length > 0) resultLists.push(docs);\n }\n\n // KV collections (any non-reserved key in `collections`)\n const reserved = new Set(['code', 'git', 'docs']);\n for (const [name, k] of Object.entries(cols)) {\n if (reserved.has(name)) continue;\n const hits = await this._d.collection(name).search(query, { k });\n if (hits.length > 0) {\n resultLists.push(hits.map(h => ({\n type: 'collection' as const,\n score: h.score ?? 0,\n content: h.content,\n metadata: { collection: name, id: h.id, ...h.metadata },\n })));\n }\n }\n\n if (resultLists.length === 0) return [];\n\n const fused = reciprocalRankFusion(resultLists);\n\n if (this._d.config.reranker && fused.length > 1) {\n const scores = await this._d.config.reranker.rank(query, fused.map(r => r.content));\n return fused\n .map((r, i) => {\n const w = (i < 3) ? 0.75 : (i < 10) ? 0.60 : 0.40;\n return { ...r, score: w * r.score + (1 - w) * (scores[i] ?? 0) };\n })\n .sort((a, b) => b.score - a.score);\n }\n\n return fused;\n }\n\n // ── Keyword ─────────────────────────────────────\n\n searchBM25(query: string, options?: { codeK?: number; gitK?: number; patternK?: number }): SearchResult[] {\n return this._d.bm25?.search(query, options) ?? [];\n }\n\n rebuildFTS(): void { this._d.bm25?.rebuild(); }\n\n // ── Context ─────────────────────────────────────\n\n async getContext(task: string, options: ContextOptions = {}): Promise<string> {\n const sections: string[] = [];\n\n if (this._d.contextBuilder) {\n const core = await this._d.contextBuilder.build(task, options);\n if (core) sections.push(core);\n }\n\n if (this._d.registry.has('docs')) {\n const docs = await this._d.searchDocs(task, { k: options.codeResults ?? 4 });\n if (docs.length > 0) {\n const body = docs.map(r => {\n const m = r.metadata as Record<string, any>;\n const h = r.context\n ? `**[${m.collection}]** ${m.title} — _${r.context}_`\n : `**[${m.collection}]** ${m.title}`;\n return `${h}\\n\\n${r.content}`;\n }).join('\\n\\n---\\n\\n');\n sections.push(`## Relevant Documents\\n\\n${body}`);\n }\n }\n\n return sections.join('\\n\\n');\n }\n}\n","/**\n * BrainBank — Index API\n *\n * Orchestrates indexing across code, git, and document indexers.\n * BrainBank delegates here after auto-initialization.\n */\n\nimport type { IndexerRegistry } from './registry.ts';\nimport type { IndexResult, StageProgressCallback, ProgressCallback } from '../types.ts';\n\nexport interface IndexAPIDeps {\n registry: IndexerRegistry;\n gitDepth: number;\n emit: (event: string, data: any) => void;\n}\n\nexport class IndexAPI {\n constructor(private _d: IndexAPIDeps) {}\n\n async index(options: {\n modules?: ('code' | 'git' | 'docs')[];\n gitDepth?: number;\n forceReindex?: boolean;\n onProgress?: StageProgressCallback;\n } = {}): Promise<{ code?: IndexResult; git?: IndexResult; docs?: Record<string, { indexed: number; skipped: number; chunks: number }> }> {\n const want = new Set(options.modules ?? ['code', 'git', 'docs']);\n const result: { code?: IndexResult; git?: IndexResult; docs?: Record<string, any> } = {};\n\n if (want.has('code')) {\n for (const mod of this._d.registry.allByType('code')) {\n const label = mod.name === 'code' ? 'code' : mod.name;\n options.onProgress?.(label, 'Starting...');\n const r = await mod.index!({\n forceReindex: options.forceReindex,\n onProgress: (f: string, i: number, t: number) => options.onProgress?.(label, `[${i}/${t}] ${f}`),\n });\n if (result.code) {\n result.code.indexed += r.indexed;\n result.code.skipped += r.skipped;\n result.code.chunks = (result.code.chunks ?? 0) + (r.chunks ?? 0);\n } else {\n result.code = r;\n }\n }\n }\n\n if (want.has('git')) {\n for (const mod of this._d.registry.allByType('git')) {\n const label = mod.name === 'git' ? 'git' : mod.name;\n options.onProgress?.(label, 'Starting...');\n const r = await mod.index!({\n depth: options.gitDepth ?? this._d.gitDepth,\n onProgress: (f: string, i: number, t: number) => options.onProgress?.(label, `[${i}/${t}] ${f}`),\n });\n if (result.git) {\n result.git.indexed += r.indexed;\n result.git.skipped += r.skipped;\n } else {\n result.git = r;\n }\n }\n }\n\n if (want.has('docs') && this._d.registry.has('docs')) {\n options.onProgress?.('docs', 'Starting...');\n result.docs = await this._d.registry.get('docs').indexCollections!({\n onProgress: (coll: string, file: string, cur: number, total: number) =>\n options.onProgress?.('docs', `[${coll}] ${cur}/${total}: ${file}`),\n });\n }\n\n this._d.emit('indexed', result);\n return result;\n }\n\n async indexCode(options: { forceReindex?: boolean; onProgress?: ProgressCallback } = {}): Promise<IndexResult> {\n const mods = this._d.registry.allByType('code');\n if (!mods.length) throw new Error(\"BrainBank: Indexer 'code' is not loaded. Add .use(code()) to your BrainBank instance.\");\n\n const acc: IndexResult = { indexed: 0, skipped: 0, chunks: 0 };\n for (const mod of mods) {\n const r = await mod.index!(options);\n acc.indexed += r.indexed;\n acc.skipped += r.skipped;\n acc.chunks = (acc.chunks ?? 0) + (r.chunks ?? 0);\n }\n return acc;\n }\n\n async indexGit(options: { depth?: number; onProgress?: ProgressCallback } = {}): Promise<IndexResult> {\n const mods = this._d.registry.allByType('git');\n if (!mods.length) throw new Error(\"BrainBank: Indexer 'git' is not loaded. Add .use(git()) to your BrainBank instance.\");\n\n const acc: IndexResult = { indexed: 0, skipped: 0 };\n for (const mod of mods) {\n const r = await mod.index!(options);\n acc.indexed += r.indexed;\n acc.skipped += r.skipped;\n }\n return acc;\n }\n}\n","/**\n * BrainBank — Watch Mode\n * \n * Auto-indexes on file changes using fs.watch.\n * Works with built-in indexers (code, git, docs) and custom indexers.\n * \n * Built-in behavior:\n * - Code files → re-indexes changed file\n * - Doc files → re-indexes changed collection\n * \n * Custom indexers:\n * - Implement `onFileChange(path, event)` to handle changes\n * - Implement `watchPatterns()` to specify which files to watch\n * \n * Usage:\n * const watcher = brain.watch({ paths: ['.'] });\n * watcher.close(); // stop watching\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { isSupported, isIgnoredDir, isIgnoredFile } from '../indexers/languages.ts';\nimport type { Indexer } from '../indexers/base.ts';\n\n// ── Types ───────────────────────────────────────────\n\nexport interface WatchOptions {\n /** Paths to watch. Default: [config.repoPath] */\n paths?: string[];\n /** Debounce interval in ms. Default: 2000 */\n debounceMs?: number;\n /** Called when a file is re-indexed. */\n onIndex?: (filePath: string, indexer: string) => void;\n /** Called on errors. */\n onError?: (error: Error) => void;\n}\n\nexport interface Watcher {\n /** Stop watching. */\n close(): void;\n /** Whether the watcher is active. */\n readonly active: boolean;\n}\n\n// ── Watch Engine ────────────────────────────────────\n\n/**\n * Create a file watcher that auto-re-indexes on changes.\n * \n * @param reindexFn — called to re-index code+git (brain.index())\n * @param indexers — registered indexers (for custom onFileChange hooks)\n * @param repoPath — base repo path (for resolving relative paths)\n * @param options — watch configuration\n */\nexport function createWatcher(\n reindexFn: () => Promise<void>,\n indexers: Map<string, Indexer>,\n repoPath: string,\n options: WatchOptions = {},\n): Watcher {\n const {\n paths = [repoPath],\n debounceMs = 2000,\n onIndex,\n onError,\n } = options;\n\n let active = true;\n const watchers: fs.FSWatcher[] = [];\n const pending = new Set<string>();\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n // Collect custom watch patterns from indexers\n const customPatterns: { indexer: Indexer; patterns: string[] }[] = [];\n for (const indexer of indexers.values()) {\n if (indexer.watchPatterns) {\n customPatterns.push({ indexer, patterns: indexer.watchPatterns() });\n }\n }\n\n // Check if a file matches any custom indexer pattern\n function matchCustomIndexer(filePath: string): Indexer | null {\n const rel = path.relative(repoPath, filePath);\n for (const { indexer, patterns } of customPatterns) {\n for (const pattern of patterns) {\n if (matchGlob(rel, pattern)) return indexer;\n }\n }\n return null;\n }\n\n // Simple glob matching (supports **, *, and extension matching)\n function matchGlob(filePath: string, pattern: string): boolean {\n // **/*.ext → match any file with that extension\n if (pattern.startsWith('**/')) {\n const suffix = pattern.slice(3); // e.g. \"*.csv\"\n const ext = suffix.startsWith('*.') ? suffix.slice(1) : null; // e.g. \".csv\"\n if (ext) return filePath.endsWith(ext);\n return path.basename(filePath) === suffix;\n }\n // *.ext → match extension in any directory\n if (pattern.startsWith('*.')) {\n return filePath.endsWith(pattern.slice(1));\n }\n return filePath === pattern;\n }\n\n // Process pending file changes\n async function flush() {\n if (pending.size === 0) return;\n\n const files = [...pending];\n pending.clear();\n\n // Group by handler\n let needsReindex = false;\n\n for (const filePath of files) {\n const absPath = path.resolve(repoPath, filePath);\n\n // Try custom indexers first\n const customIndexer = matchCustomIndexer(absPath);\n if (customIndexer?.onFileChange) {\n try {\n const handled = await customIndexer.onFileChange(absPath, detectEvent(absPath));\n if (handled) {\n onIndex?.(filePath, customIndexer.name);\n continue;\n }\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n // Fall back to built-in re-index for supported code files\n if (isSupported(filePath)) {\n needsReindex = true;\n onIndex?.(filePath, 'code');\n }\n }\n\n // Batch re-index if any code files changed\n if (needsReindex) {\n try {\n await reindexFn();\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n }\n }\n\n function detectEvent(filePath: string): 'create' | 'update' | 'delete' {\n try {\n fs.accessSync(filePath);\n return 'update';\n } catch {\n return 'delete';\n }\n }\n\n // Should we watch this file?\n function shouldWatch(filename: string): boolean {\n if (!filename) return false;\n const parts = filename.split(path.sep);\n\n // Skip ignored directories\n for (const part of parts) {\n if (isIgnoredDir(part)) return false;\n }\n\n // Skip ignored files\n if (isIgnoredFile(path.basename(filename))) return false;\n\n // Accept supported code files\n if (isSupported(filename)) return true;\n\n // Accept files matching custom indexer patterns\n if (matchCustomIndexer(path.resolve(repoPath, filename))) return true;\n\n return false;\n }\n\n // Set up watchers\n for (const watchPath of paths) {\n const resolved = path.resolve(watchPath);\n try {\n // { recursive: true } only works on macOS + Windows; Linux needs chokidar or per-dir watchers\n const supportsRecursive = process.platform === 'darwin' || process.platform === 'win32';\n const watcher = fs.watch(resolved, { recursive: supportsRecursive }, (_event, filename) => {\n if (!active || !filename) return;\n if (!shouldWatch(filename)) return;\n\n pending.add(filename);\n\n // Debounce\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => flush(), debounceMs);\n });\n\n watcher.on('error', (err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n });\n\n watchers.push(watcher);\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n return {\n close() {\n active = false;\n if (timer) clearTimeout(timer);\n for (const w of watchers) w.close();\n watchers.length = 0;\n },\n get active() { return active; },\n };\n}\n"],"mappings":"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","names":["path","fs","path","path"]}
package/dist/cli.js CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  BrainBank
4
- } from "./chunk-6MFTQV3O.js";
4
+ } from "./chunk-ZJ5LLMGM.js";
5
5
  import {
6
6
  code
7
- } from "./chunk-WR4WXKJT.js";
7
+ } from "./chunk-4DM3XWO6.js";
8
8
  import {
9
9
  git
10
10
  } from "./chunk-7JCEW7LT.js";
11
11
  import {
12
12
  docs
13
- } from "./chunk-F6SJ3U4H.js";
14
- import "./chunk-V4UJKXPK.js";
13
+ } from "./chunk-T2VXF5S5.js";
14
+ import "./chunk-FEAMUZGJ.js";
15
15
  import "./chunk-QNHBCOKB.js";
16
16
  import {
17
17
  __name
@@ -37,6 +37,29 @@ function hasFlag(name) {
37
37
  return args.includes(`--${name}`);
38
38
  }
39
39
  __name(hasFlag, "hasFlag");
40
+ var VALUE_FLAGS = /* @__PURE__ */ new Set([
41
+ "repo",
42
+ "depth",
43
+ "collection",
44
+ "pattern",
45
+ "context",
46
+ "name",
47
+ "keep",
48
+ "reranker"
49
+ ]);
50
+ function stripFlags(argv) {
51
+ const result = [];
52
+ for (let i = 0; i < argv.length; i++) {
53
+ if (argv[i].startsWith("--")) {
54
+ const name = argv[i].slice(2);
55
+ if (VALUE_FLAGS.has(name)) i++;
56
+ continue;
57
+ }
58
+ result.push(argv[i]);
59
+ }
60
+ return result;
61
+ }
62
+ __name(stripFlags, "stripFlags");
40
63
  function printResults(results) {
41
64
  if (results.length === 0) {
42
65
  console.log(c.yellow(" No results found."));
@@ -454,7 +477,7 @@ async function cmdDocs() {
454
477
  }
455
478
  __name(cmdDocs, "cmdDocs");
456
479
  async function cmdDocSearch() {
457
- const query = args.slice(1).filter((a) => !a.startsWith("--")).join(" ");
480
+ const query = stripFlags(args).slice(1).join(" ");
458
481
  if (!query) {
459
482
  console.log(c.red("Usage: brainbank dsearch <query>"));
460
483
  process.exit(1);
@@ -485,7 +508,7 @@ __name(cmdDocSearch, "cmdDocSearch");
485
508
 
486
509
  // src/cli/commands/search.ts
487
510
  async function cmdSearch() {
488
- const query = args.slice(1).join(" ");
511
+ const query = stripFlags(args).slice(1).join(" ");
489
512
  if (!query) {
490
513
  console.log(c.red("Usage: brainbank search <query>"));
491
514
  process.exit(1);
@@ -500,7 +523,7 @@ async function cmdSearch() {
500
523
  }
501
524
  __name(cmdSearch, "cmdSearch");
502
525
  async function cmdHybridSearch() {
503
- const query = args.slice(1).filter((a) => !a.startsWith("--")).join(" ");
526
+ const query = stripFlags(args).slice(1).join(" ");
504
527
  if (!query) {
505
528
  console.log(c.red("Usage: brainbank hsearch <query>"));
506
529
  process.exit(1);
@@ -516,7 +539,7 @@ async function cmdHybridSearch() {
516
539
  }
517
540
  __name(cmdHybridSearch, "cmdHybridSearch");
518
541
  async function cmdKeywordSearch() {
519
- const query = args.slice(1).filter((a) => !a.startsWith("--")).join(" ");
542
+ const query = stripFlags(args).slice(1).join(" ");
520
543
  if (!query) {
521
544
  console.log(c.red("Usage: brainbank ksearch <query>"));
522
545
  process.exit(1);
@@ -566,7 +589,7 @@ async function cmdContext() {
566
589
  brain2.close();
567
590
  return;
568
591
  }
569
- const task = args.slice(1).join(" ");
592
+ const task = stripFlags(args).slice(1).join(" ");
570
593
  if (!task) {
571
594
  console.log(c.red("Usage: brainbank context <task description>"));
572
595
  console.log(c.dim(" brainbank context add <collection> <path> <description>"));