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.
- package/dist/{chunk-WR4WXKJT.js → chunk-4DM3XWO6.js} +9 -1
- package/dist/{chunk-WR4WXKJT.js.map → chunk-4DM3XWO6.js.map} +1 -1
- package/dist/{chunk-V4UJKXPK.js → chunk-FEAMUZGJ.js} +7 -2
- package/dist/{chunk-V4UJKXPK.js.map → chunk-FEAMUZGJ.js.map} +1 -1
- package/dist/{chunk-FJJY4H2Y.js → chunk-OPQ3ZIPV.js} +2 -2
- package/dist/{chunk-F6SJ3U4H.js → chunk-T2VXF5S5.js} +3 -7
- package/dist/chunk-T2VXF5S5.js.map +1 -0
- package/dist/{chunk-6MFTQV3O.js → chunk-ZJ5LLMGM.js} +35 -3
- package/dist/chunk-ZJ5LLMGM.js.map +1 -0
- package/dist/cli.js +32 -9
- package/dist/cli.js.map +1 -1
- package/dist/code.js +1 -1
- package/dist/docs.js +1 -1
- package/dist/index.js +5 -5
- package/dist/notes.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-6MFTQV3O.js.map +0 -1
- package/dist/chunk-F6SJ3U4H.js.map +0 -1
- /package/dist/{chunk-FJJY4H2Y.js.map → chunk-OPQ3ZIPV.js.map} +0 -0
|
@@ -491,6 +491,10 @@ var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
|
491
491
|
".nuxt",
|
|
492
492
|
".output",
|
|
493
493
|
".svelte-kit",
|
|
494
|
+
// Auto-generated code
|
|
495
|
+
"generated",
|
|
496
|
+
"sdk",
|
|
497
|
+
"openapi",
|
|
494
498
|
// Version control
|
|
495
499
|
".git",
|
|
496
500
|
".hg",
|
|
@@ -515,6 +519,10 @@ var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
|
515
519
|
".cargo",
|
|
516
520
|
"vendor",
|
|
517
521
|
// Go, PHP
|
|
522
|
+
// Database (auto-generated migrations, dumps, seeds)
|
|
523
|
+
"migrations",
|
|
524
|
+
"db_dumps",
|
|
525
|
+
"seeds",
|
|
518
526
|
// AI / Model cache
|
|
519
527
|
".model-cache",
|
|
520
528
|
".brainbank",
|
|
@@ -720,4 +728,4 @@ export {
|
|
|
720
728
|
CodeIndexer,
|
|
721
729
|
code
|
|
722
730
|
};
|
|
723
|
-
//# sourceMappingURL=chunk-
|
|
731
|
+
//# sourceMappingURL=chunk-4DM3XWO6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/indexers/code/code-indexer.ts","../src/indexers/code/code-chunker.ts","../src/indexers/code/grammars.ts","../src/indexers/languages.ts","../src/indexers/code/code-plugin.ts"],"sourcesContent":["/**\n * BrainBank — Code Indexer\n * \n * Walks a repository, chunks source files semantically,\n * embeds each chunk, and stores in SQLite + HNSW.\n * Incremental: only re-indexes files that changed (by content hash).\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { CodeChunker } from './code-chunker.ts';\nimport { SUPPORTED_EXTENSIONS, IGNORE_DIRS, isIgnoredDir, isIgnoredFile } from '../languages.ts';\nimport type { Database } from '../../db/database.ts';\nimport type { EmbeddingProvider, ProgressCallback, IndexResult } from '../../types.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\n\nexport interface CodeIndexerDeps {\n db: Database;\n hnsw: HNSWIndex;\n vectorCache: Map<number, Float32Array>;\n embedding: EmbeddingProvider;\n}\n\nexport interface CodeIndexOptions {\n forceReindex?: boolean;\n onProgress?: ProgressCallback;\n}\n\nexport class CodeIndexer {\n private _chunker = new CodeChunker();\n private _deps: CodeIndexerDeps;\n private _repoPath: string;\n private _maxFileSize: number;\n\n constructor(repoPath: string, deps: CodeIndexerDeps, maxFileSize: number = 512_000) {\n this._deps = deps;\n this._repoPath = repoPath;\n this._maxFileSize = maxFileSize;\n }\n\n /**\n * Index all supported files in the repository.\n * Skips unchanged files (same content hash).\n */\n async index(options: CodeIndexOptions = {}): Promise<IndexResult> {\n const { forceReindex = false, onProgress } = options;\n const files = this._walkRepo(this._repoPath);\n let indexed = 0, skipped = 0, totalChunks = 0;\n\n for (let i = 0; i < files.length; i++) {\n const filePath = files[i];\n const rel = path.relative(this._repoPath, filePath);\n onProgress?.(rel, i + 1, files.length);\n\n let content: string;\n try { content = fs.readFileSync(filePath, 'utf-8'); }\n catch { continue; }\n\n const hash = this._hash(content);\n const existing = this._deps.db.prepare(\n 'SELECT file_hash FROM indexed_files WHERE file_path = ?'\n ).get(rel) as any;\n\n if (!forceReindex && existing?.file_hash === hash) {\n skipped++;\n continue;\n }\n\n // Always clean up old chunks for this file (catches orphaned chunks from interrupted first-run)\n const oldChunks = this._deps.db.prepare(\n 'SELECT id FROM code_chunks WHERE file_path = ?'\n ).all(rel) as any[];\n if (oldChunks.length > 0) {\n for (const { id } of oldChunks) {\n this._deps.hnsw.remove(id);\n this._deps.vectorCache.delete(id);\n }\n this._deps.db.prepare('DELETE FROM code_chunks WHERE file_path = ?').run(rel);\n }\n\n const ext = path.extname(filePath).toLowerCase();\n const language = SUPPORTED_EXTENSIONS[ext] ?? 'text';\n const chunks = await this._chunker.chunk(rel, content, language);\n\n // Build embedding texts for all chunks and batch embed (10-50x faster)\n const embeddingTexts = chunks.map(chunk => [\n `File: ${rel}`,\n chunk.name ? `${chunk.chunkType}: ${chunk.name}` : chunk.chunkType,\n chunk.content,\n ].join('\\n'));\n\n const vecs = await this._deps.embedding.embedBatch(embeddingTexts);\n\n // Insert chunks + vectors individually (preserves auto-increment ID ordering)\n for (let ci = 0; ci < chunks.length; ci++) {\n const chunk = chunks[ci];\n const result = this._deps.db.prepare(\n `INSERT INTO code_chunks (file_path, chunk_type, name, start_line, end_line, content, language, file_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n ).run(rel, chunk.chunkType, chunk.name ?? null, chunk.startLine, chunk.endLine, chunk.content, language, hash);\n\n const id = Number(result.lastInsertRowid);\n this._deps.db.prepare(\n 'INSERT INTO code_vectors (chunk_id, embedding) VALUES (?, ?)'\n ).run(id, Buffer.from(vecs[ci].buffer));\n\n this._deps.hnsw.add(vecs[ci], id);\n this._deps.vectorCache.set(id, vecs[ci]);\n totalChunks++;\n }\n\n // Mark file as indexed\n this._deps.db.prepare(\n 'INSERT OR REPLACE INTO indexed_files (file_path, file_hash) VALUES (?, ?)'\n ).run(rel, hash);\n indexed++;\n }\n\n return { indexed, skipped, chunks: totalChunks };\n }\n\n // ── File Walker ─────────────────────────────────\n\n private _walkRepo(dir: string, files: string[] = []): string[] {\n let entries: fs.Dirent[];\n try { entries = fs.readdirSync(dir, { withFileTypes: true }); }\n catch { return files; }\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (isIgnoredDir(entry.name)) continue;\n this._walkRepo(path.join(dir, entry.name), files);\n } else if (entry.isFile()) {\n if (isIgnoredFile(entry.name)) continue;\n const ext = path.extname(entry.name).toLowerCase();\n if (!(ext in SUPPORTED_EXTENSIONS)) continue;\n\n const full = path.join(dir, entry.name);\n try {\n if (fs.statSync(full).size <= this._maxFileSize) {\n files.push(full);\n }\n } catch {}\n }\n }\n return files;\n }\n\n // ── FNV-1a Hash ─────────────────────────────────\n\n private _hash(content: string): string {\n let h = 2166136261;\n for (let i = 0; i < content.length; i++) {\n h ^= content.charCodeAt(i);\n h = (h * 16777619) >>> 0;\n }\n return h.toString(16);\n }\n}\n","/**\n * BrainBank — Tree-Sitter Code Chunker\n * \n * AST-aware code splitting using native tree-sitter bindings.\n * Extracts semantic blocks (functions, classes, methods, interfaces)\n * from the AST. Falls back to sliding window for unsupported languages.\n */\n\nimport { createRequire } from 'node:module';\nimport type { CodeChunk } from '../../types.ts';\nimport { GRAMMARS, type LangGrammar } from './grammars.ts';\n\nconst require = createRequire(import.meta.url);\n\n// ── Configuration ───────────────────────────────────\n\nexport interface ChunkerConfig {\n /** Max lines per chunk. Default: 80 */\n maxLines?: number;\n /** Min lines for a detected block to be a chunk. Default: 3 */\n minLines?: number;\n /** Overlap between adjacent generic chunks. Default: 5 */\n overlap?: number;\n}\n\n// ── CodeChunker ─────────────────────────────────────\n\nexport class CodeChunker {\n private MAX: number;\n private MIN: number;\n private OVERLAP: number;\n private _parser: any = null;\n private _langCache = new Map<string, LangGrammar | null>();\n\n constructor(config: ChunkerConfig = {}) {\n this.MAX = config.maxLines ?? 80;\n this.MIN = config.minLines ?? 3;\n this.OVERLAP = config.overlap ?? 5;\n }\n\n /** Lazy-init tree-sitter parser. */\n private _ensureParser(): any {\n if (!this._parser) {\n try {\n const Parser = require('tree-sitter');\n this._parser = new Parser();\n } catch {\n this._parser = false; // Mark as unavailable\n }\n }\n return this._parser || null;\n }\n\n /** Load a language grammar (cached). */\n private _loadGrammar(language: string): LangGrammar | null {\n if (this._langCache.has(language)) return this._langCache.get(language)!;\n\n const factory = GRAMMARS[language];\n const grammar = factory ? factory() : null;\n this._langCache.set(language, grammar);\n return grammar;\n }\n\n /**\n * Split file content into semantic chunks using tree-sitter AST.\n * Falls back to sliding window if grammar isn't available.\n */\n async chunk(filePath: string, content: string, language: string): Promise<CodeChunk[]> {\n const lines = content.split('\\n');\n\n // Small file → single chunk\n if (lines.length <= this.MAX) {\n return [{\n filePath,\n chunkType: 'file',\n startLine: 1,\n endLine: lines.length,\n content: content.trim(),\n language,\n }];\n }\n\n // Try tree-sitter AST chunking\n const parser = this._ensureParser();\n const langConfig = this._loadGrammar(language);\n\n if (parser && langConfig) {\n try {\n parser.setLanguage(langConfig.grammar);\n const tree = parser.parse(content);\n const chunks = this._extractChunks(filePath, lines, tree.rootNode, langConfig, language);\n\n if (chunks.length > 0) {\n return chunks.filter(c => c.content.length > 20);\n }\n } catch {\n // Tree-sitter failed — fall through to generic\n }\n }\n\n // Fallback to sliding window\n return this._chunkGeneric(filePath, lines, language);\n }\n\n /** Walk AST and extract top-level semantic blocks. */\n private _extractChunks(\n filePath: string, lines: string[],\n rootNode: any, langConfig: LangGrammar, language: string,\n ): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const seen = new Set<string>();\n\n for (let i = 0; i < rootNode.childCount; i++) {\n const child = rootNode.child(i);\n this._processNode(filePath, lines, child, langConfig, language, chunks, seen);\n }\n\n return chunks;\n }\n\n /** Classify and process a single AST node. */\n private _processNode(\n filePath: string, lines: string[], node: any,\n langConfig: LangGrammar, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n const type = node.type;\n\n // Handle export_statement: process what it wraps\n if (type === 'export_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n const category = this._categorize(child.type, langConfig);\n if (category) {\n this._processDeclaration(filePath, lines, node, child, category, langConfig, language, chunks, seen);\n return;\n }\n }\n // Export with no recognized declaration — chunk the whole thing if big enough\n const nodeLines = node.endPosition.row - node.startPosition.row + 1;\n if (nodeLines >= this.MIN) {\n this._addChunk(filePath, lines, node, 'function', this._extractName(node), language, chunks, seen);\n }\n return;\n }\n\n // Python decorated definitions (@decorator + class/def)\n if (type === 'decorated_definition') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n const category = this._categorize(child.type, langConfig);\n if (category) {\n this._processDeclaration(filePath, lines, node, child, category, langConfig, language, chunks, seen);\n return;\n }\n }\n }\n\n // Direct match\n const category = this._categorize(type, langConfig);\n if (category) {\n this._processDeclaration(filePath, lines, node, node, category, langConfig, language, chunks, seen);\n }\n }\n\n /** Check which category a node type belongs to. */\n private _categorize(nodeType: string, langConfig: LangGrammar): string | null {\n for (const [category, types] of Object.entries(langConfig.nodeTypes)) {\n if (types && types.includes(nodeType)) return category;\n }\n return null;\n }\n\n /** Process a matched declaration: class → split by methods, else → chunk directly. */\n private _processDeclaration(\n filePath: string, lines: string[],\n outerNode: any, innerNode: any, category: string,\n langConfig: LangGrammar, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n const nodeLines = outerNode.endPosition.row - outerNode.startPosition.row + 1;\n const name = this._extractName(innerNode);\n const chunkType = this._toChunkType(category);\n\n // Large class → split into methods\n if ((category === 'class' || category === 'struct' || category === 'impl') && nodeLines > this.MAX) {\n this._splitClassIntoMethods(filePath, lines, outerNode, innerNode, name, langConfig, language, chunks, seen);\n return;\n }\n\n // Large non-class → split with overlap\n if (nodeLines > this.MAX) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n outerNode.startPosition.row, outerNode.endPosition.row,\n name, chunkType, language));\n return;\n }\n\n // Normal-sized node\n if (nodeLines >= this.MIN) {\n this._addChunk(filePath, lines, outerNode, chunkType, name, language, chunks, seen);\n }\n }\n\n /** Split a large class into individual method chunks. */\n private _splitClassIntoMethods(\n filePath: string, lines: string[],\n outerNode: any, classNode: any, className: string,\n langConfig: LangGrammar, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n // Find class body\n const body = this._findClassBody(classNode);\n if (!body) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n outerNode.startPosition.row, outerNode.endPosition.row,\n className, 'class', language));\n return;\n }\n\n // Get method node types\n const methodTypes = new Set([\n ...(langConfig.nodeTypes.function || []),\n ...(langConfig.nodeTypes.method || []),\n ]);\n\n let methodsFound = false;\n for (let i = 0; i < body.childCount; i++) {\n const child = body.child(i);\n let methodNode = child;\n\n // Decorated methods\n if (child.type === 'decorated_definition') {\n for (let j = 0; j < child.childCount; j++) {\n if (methodTypes.has(child.child(j).type)) {\n methodNode = child.child(j);\n break;\n }\n }\n }\n\n if (methodTypes.has(methodNode.type) || methodTypes.has(child.type)) {\n const methodName = this._extractName(methodNode);\n const nodeToChunk = child.type === 'decorated_definition' ? child : methodNode;\n const methodLineCount = nodeToChunk.endPosition.row - nodeToChunk.startPosition.row + 1;\n\n if (methodLineCount >= this.MIN) {\n methodsFound = true;\n const fullName = `${className}.${methodName}`;\n\n if (methodLineCount > this.MAX) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n nodeToChunk.startPosition.row, nodeToChunk.endPosition.row,\n fullName, 'method', language));\n } else {\n this._addChunk(filePath, lines, nodeToChunk, 'method', fullName, language, chunks, seen);\n }\n }\n }\n }\n\n // Fallback: no methods found → split the whole class\n if (!methodsFound) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n outerNode.startPosition.row, outerNode.endPosition.row,\n className, 'class', language));\n }\n }\n\n /** Find the class body node. */\n private _findClassBody(classNode: any): any | null {\n const bodyTypes = ['class_body', 'block', 'declaration_list', 'body'];\n for (let i = 0; i < classNode.childCount; i++) {\n const child = classNode.child(i);\n if (bodyTypes.includes(child.type)) return child;\n }\n return null;\n }\n\n /** Extract name from an AST node. */\n private _extractName(node: any): string {\n // Try childForFieldName('name')\n if (typeof node.childForFieldName === 'function') {\n const nameNode = node.childForFieldName('name');\n if (nameNode) return nameNode.text;\n }\n // Try identifier children\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (['identifier', 'type_identifier', 'property_identifier'].includes(child.type)) {\n return child.text;\n }\n }\n // For variable declarations, dig into declarators\n if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (child.type === 'variable_declarator') {\n const nameNode = child.childForFieldName('name');\n if (nameNode) return nameNode.text;\n }\n }\n }\n return 'anonymous';\n }\n\n /** Map category to chunk type. */\n private _toChunkType(category: string): string {\n if (category === 'class' || category === 'struct' || category === 'impl') return 'class';\n if (category === 'interface') return 'interface';\n if (category === 'variable') return 'function';\n return category;\n }\n\n /** Add a node as a chunk, avoiding duplicates. */\n private _addChunk(\n filePath: string, lines: string[], node: any,\n chunkType: string, name: string, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n const start = node.startPosition.row;\n const end = node.endPosition.row;\n const key = `${start}-${end}`;\n if (seen.has(key)) return;\n seen.add(key);\n\n const content = lines.slice(start, end + 1).join('\\n').trim();\n if (content.length <= 20) return;\n\n chunks.push({\n filePath,\n chunkType,\n name,\n startLine: start + 1,\n endLine: end + 1,\n content,\n language,\n });\n }\n\n // ── Fallback: Generic sliding window ────────────\n\n private _chunkGeneric(filePath: string, lines: string[], language: string): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const step = Math.max(this.MAX - this.OVERLAP, 1);\n\n for (let s = 0; s < lines.length; s += step) {\n const e = Math.min(s + this.MAX, lines.length);\n const content = lines.slice(s, e).join('\\n').trim();\n if (content.length > 20) {\n chunks.push({\n filePath,\n chunkType: 'block',\n startLine: s + 1,\n endLine: e,\n content,\n language,\n });\n }\n if (e >= lines.length) break;\n }\n\n return chunks;\n }\n\n /** Split a large block into overlapping sub-chunks. */\n private _splitLargeBlock(\n filePath: string, lines: string[],\n start: number, end: number,\n name: string, type: string, language: string,\n ): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const step = Math.max(this.MAX - this.OVERLAP, 1);\n let part = 1;\n\n for (let s = start; s <= end; s += step) {\n const e = Math.min(s + this.MAX, end + 1);\n const content = lines.slice(s, e).join('\\n').trim();\n if (content.length > 20) {\n chunks.push({\n filePath,\n chunkType: type,\n name: `${name} (part ${part++})`,\n startLine: s + 1,\n endLine: e,\n content,\n language,\n });\n }\n if (e > end) break;\n }\n\n return chunks;\n }\n}\n","/**\n * BrainBank — Tree-Sitter Grammar Registry\n *\n * Maps language names to their tree-sitter grammar packages\n * and the AST node types that represent semantic blocks.\n */\n\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n// ── Types ───────────────────────────────────────────\n\nexport interface LangGrammar {\n grammar: any;\n nodeTypes: {\n class?: string[];\n function?: string[];\n interface?: string[];\n variable?: string[];\n method?: string[];\n struct?: string[];\n impl?: string[];\n };\n}\n\n// ── Loader ──────────────────────────────────────────\n\n/** Try to require a grammar, return null if not installed. */\nfunction tryGrammar(pkg: string, nodeTypes: LangGrammar['nodeTypes'], accessor?: string): () => LangGrammar | null {\n return () => {\n try {\n const mod = require(pkg);\n return { grammar: accessor ? mod[accessor] : mod, nodeTypes };\n } catch { return null; }\n };\n}\n\n// ── Grammar Table ───────────────────────────────────\n\nexport const GRAMMARS: Record<string, () => LangGrammar | null> = {\n // ── Web ──────────────────────────────────────────\n typescript: tryGrammar('tree-sitter-typescript', {\n class: ['class_declaration'],\n interface: ['interface_declaration', 'type_alias_declaration'],\n function: ['function_declaration', 'method_definition'],\n variable: ['lexical_declaration'],\n }, 'typescript'),\n javascript: tryGrammar('tree-sitter-javascript', {\n class: ['class_declaration'],\n function: ['function_declaration', 'method_definition'],\n variable: ['lexical_declaration'],\n }),\n html: tryGrammar('tree-sitter-html', {}),\n css: tryGrammar('tree-sitter-css', {}),\n\n // ── Systems ──────────────────────────────────────\n go: tryGrammar('tree-sitter-go', {\n function: ['function_declaration', 'method_declaration'],\n struct: ['type_declaration'],\n }),\n rust: tryGrammar('tree-sitter-rust', {\n function: ['function_item'],\n struct: ['struct_item'],\n impl: ['impl_item'],\n }),\n c: tryGrammar('tree-sitter-c', {\n function: ['function_definition'],\n struct: ['struct_specifier'],\n }),\n cpp: tryGrammar('tree-sitter-cpp', {\n class: ['class_specifier'],\n function: ['function_definition'],\n }),\n swift: tryGrammar('tree-sitter-swift', {\n class: ['class_declaration'],\n function: ['function_declaration'],\n struct: ['struct_declaration'],\n }),\n\n // ── JVM ──────────────────────────────────────────\n java: tryGrammar('tree-sitter-java', {\n class: ['class_declaration'],\n interface: ['interface_declaration'],\n method: ['method_declaration'],\n }),\n kotlin: tryGrammar('tree-sitter-kotlin', {\n class: ['class_declaration'],\n function: ['function_declaration'],\n }),\n scala: tryGrammar('tree-sitter-scala', {\n class: ['class_definition'],\n function: ['function_definition'],\n }),\n\n // ── Scripting ────────────────────────────────────\n python: tryGrammar('tree-sitter-python', {\n class: ['class_definition'],\n function: ['function_definition'],\n }),\n ruby: tryGrammar('tree-sitter-ruby', {\n class: ['class'],\n method: ['method', 'singleton_method'],\n }),\n php: tryGrammar('tree-sitter-php', {\n class: ['class_declaration'],\n function: ['function_definition', 'method_declaration'],\n }, 'php'),\n lua: tryGrammar('tree-sitter-lua', {\n function: ['function_declaration'],\n }),\n bash: tryGrammar('tree-sitter-bash', {\n function: ['function_definition'],\n }),\n elixir: tryGrammar('tree-sitter-elixir', {\n function: ['call'], // defmodule, def, defp\n }),\n\n // ── .NET ─────────────────────────────────────────\n c_sharp: tryGrammar('tree-sitter-c-sharp', {\n class: ['class_declaration'],\n interface: ['interface_declaration'],\n method: ['method_declaration'],\n }),\n};\n","/**\n * BrainBank — Language Registry\n * \n * Supported file extensions, language mappings, and ignore lists.\n * Controls which files get indexed and how they're chunked.\n */\n\n// ── Supported Extensions ────────────────────────────\n\nexport const SUPPORTED_EXTENSIONS: Record<string, string> = {\n // TypeScript / JavaScript\n '.ts': 'typescript',\n '.tsx': 'typescript',\n '.js': 'javascript',\n '.jsx': 'javascript',\n '.mjs': 'javascript',\n '.cjs': 'javascript',\n\n // Systems\n '.go': 'go',\n '.rs': 'rust',\n '.cpp': 'cpp',\n '.cc': 'cpp',\n '.c': 'c',\n '.h': 'c',\n '.hpp': 'cpp',\n\n // JVM\n '.java': 'java',\n '.kt': 'kotlin',\n '.scala': 'scala',\n\n // Scripting\n '.py': 'python',\n '.rb': 'ruby',\n '.php': 'php',\n '.lua': 'lua',\n '.sh': 'bash',\n '.bash': 'bash',\n '.zsh': 'bash',\n\n // Web\n '.html': 'html',\n '.css': 'css',\n '.scss': 'scss',\n '.less': 'less',\n '.svelte': 'svelte',\n '.vue': 'vue',\n\n // Data / Config\n '.json': 'json',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n '.toml': 'toml',\n '.xml': 'xml',\n '.graphql': 'graphql',\n '.gql': 'graphql',\n\n // Docs\n '.md': 'markdown',\n '.mdx': 'markdown',\n\n // Database\n '.sql': 'sql',\n '.prisma': 'prisma',\n\n // Other\n '.swift': 'swift',\n '.dart': 'dart',\n '.r': 'r',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.erl': 'erlang',\n '.zig': 'zig',\n};\n\n// ── Ignore Directories ──────────────────────────────\n\nexport const IGNORE_DIRS = new Set([\n // Package managers\n 'node_modules',\n 'bower_components',\n '.pnpm',\n\n // Build output\n 'dist',\n 'build',\n 'out',\n '.next',\n '.nuxt',\n '.output',\n '.svelte-kit',\n\n // Version control\n '.git',\n '.hg',\n '.svn',\n\n // IDE / Editor\n '.idea',\n '.vscode',\n\n // Runtime / Cache\n '__pycache__',\n '.pytest_cache',\n 'venv',\n '.venv',\n '.env',\n '.tox',\n\n // Coverage / Test artifacts\n 'coverage',\n '.nyc_output',\n 'htmlcov',\n\n // Compiled\n 'target', // Rust, Java\n '.cargo',\n 'vendor', // Go, PHP\n\n // AI / Model cache\n '.model-cache',\n '.brainbank',\n\n // OS\n '.DS_Store',\n]);\n\n// ── Ignore Files ────────────────────────────────────\n\nexport const IGNORE_FILES = new Set([\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n 'bun.lockb',\n 'Cargo.lock',\n 'Gemfile.lock',\n 'poetry.lock',\n 'composer.lock',\n 'go.sum',\n]);\n\n// ── Helpers ─────────────────────────────────────────\n\nimport path from 'node:path';\n\n/** Check if a file extension is supported for indexing. */\nexport function isSupported(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return ext in SUPPORTED_EXTENSIONS;\n}\n\n/** Get the language name for a file. Returns undefined if not supported. */\nexport function getLanguage(filePath: string): string | undefined {\n const ext = path.extname(filePath).toLowerCase();\n return SUPPORTED_EXTENSIONS[ext];\n}\n\n/** Check if a directory name should be ignored. */\nexport function isIgnoredDir(dirName: string): boolean {\n return IGNORE_DIRS.has(dirName);\n}\n\n/** Check if a filename should be ignored. */\nexport function isIgnoredFile(fileName: string): boolean {\n return IGNORE_FILES.has(fileName);\n}\n","/**\n * BrainBank — Code Module\n * \n * Language-aware code indexing for 30+ languages.\n * \n * import { BrainBank } from 'brainbank';\n * import { code } from 'brainbank/code';\n * \n * const brain = new BrainBank().use(code({ repoPath: '.' }));\n * \n * // Multi-repo: namespace to avoid key collisions\n * brain\n * .use(code({ repoPath: './frontend', name: 'code:frontend' }))\n * .use(code({ repoPath: './backend', name: 'code:backend' }));\n */\n\nimport type { Indexer, IndexerContext } from '../base.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\nimport { CodeIndexer } from './code-indexer.ts';\nimport type { IndexResult, ProgressCallback } from '../../types.ts';\n\nexport interface CodePluginOptions {\n /** Repository path to index. Default: '.' */\n repoPath?: string;\n /** Maximum file size in bytes. Default: from config */\n maxFileSize?: number;\n /** Custom indexer name for multi-repo (e.g. 'code:frontend'). Default: 'code' */\n name?: string;\n}\n\nclass CodePlugin implements Indexer {\n readonly name: string;\n hnsw!: HNSWIndex;\n indexer!: CodeIndexer;\n vecCache = new Map<number, Float32Array>();\n\n constructor(private opts: CodePluginOptions = {}) {\n this.name = opts.name ?? 'code';\n }\n\n async initialize(ctx: IndexerContext): Promise<void> {\n // Use shared HNSW so all code indexers (code, code:frontend, etc.) share one index\n const shared = await ctx.getOrCreateSharedHnsw('code');\n this.hnsw = shared.hnsw;\n this.vecCache = shared.vecCache;\n\n // Only load vectors once (first code indexer to initialize)\n if (shared.isNew) {\n ctx.loadVectors('code_vectors', 'chunk_id', this.hnsw, this.vecCache);\n }\n\n const repoPath = this.opts.repoPath ?? ctx.config.repoPath;\n this.indexer = new CodeIndexer(repoPath, {\n db: ctx.db,\n hnsw: this.hnsw,\n vectorCache: this.vecCache,\n embedding: ctx.embedding,\n }, this.opts.maxFileSize ?? ctx.config.maxFileSize);\n }\n\n async index(options: {\n forceReindex?: boolean;\n onProgress?: ProgressCallback;\n } = {}): Promise<IndexResult> {\n return this.indexer.index(options);\n }\n\n stats(): Record<string, any> {\n return { hnswSize: this.hnsw.size };\n }\n}\n\n/** Create a code indexing plugin. */\nexport function code(opts?: CodePluginOptions): Indexer {\n return new CodePlugin(opts);\n}\n"],"mappings":";;;;;AAQA,OAAO,QAAQ;AACf,OAAOA,WAAU;;;ACDjB,SAAS,iBAAAC,sBAAqB;;;ACD9B,SAAS,qBAAqB;AAE9B,IAAMC,WAAU,cAAc,YAAY,GAAG;AAoB7C,SAAS,WAAW,KAAa,WAAqC,UAA6C;AAC/G,SAAO,MAAM;AACT,QAAI;AACA,YAAM,MAAMA,SAAQ,GAAG;AACvB,aAAO,EAAE,SAAS,WAAW,IAAI,QAAQ,IAAI,KAAK,UAAU;AAAA,IAChE,QAAQ;AAAE,aAAO;AAAA,IAAM;AAAA,EAC3B;AACJ;AAPS;AAWF,IAAM,WAAqD;AAAA;AAAA,EAE9D,YAAY,WAAW,0BAA0B;AAAA,IAC7C,OAAO,CAAC,mBAAmB;AAAA,IAC3B,WAAW,CAAC,yBAAyB,wBAAwB;AAAA,IAC7D,UAAU,CAAC,wBAAwB,mBAAmB;AAAA,IACtD,UAAU,CAAC,qBAAqB;AAAA,EACpC,GAAG,YAAY;AAAA,EACf,YAAY,WAAW,0BAA0B;AAAA,IAC7C,OAAO,CAAC,mBAAmB;AAAA,IAC3B,UAAU,CAAC,wBAAwB,mBAAmB;AAAA,IACtD,UAAU,CAAC,qBAAqB;AAAA,EACpC,CAAC;AAAA,EACD,MAAM,WAAW,oBAAoB,CAAC,CAAC;AAAA,EACvC,KAAK,WAAW,mBAAmB,CAAC,CAAC;AAAA;AAAA,EAGrC,IAAI,WAAW,kBAAkB;AAAA,IAC7B,UAAU,CAAC,wBAAwB,oBAAoB;AAAA,IACvD,QAAQ,CAAC,kBAAkB;AAAA,EAC/B,CAAC;AAAA,EACD,MAAM,WAAW,oBAAoB;AAAA,IACjC,UAAU,CAAC,eAAe;AAAA,IAC1B,QAAQ,CAAC,aAAa;AAAA,IACtB,MAAM,CAAC,WAAW;AAAA,EACtB,CAAC;AAAA,EACD,GAAG,WAAW,iBAAiB;AAAA,IAC3B,UAAU,CAAC,qBAAqB;AAAA,IAChC,QAAQ,CAAC,kBAAkB;AAAA,EAC/B,CAAC;AAAA,EACD,KAAK,WAAW,mBAAmB;AAAA,IAC/B,OAAO,CAAC,iBAAiB;AAAA,IACzB,UAAU,CAAC,qBAAqB;AAAA,EACpC,CAAC;AAAA,EACD,OAAO,WAAW,qBAAqB;AAAA,IACnC,OAAO,CAAC,mBAAmB;AAAA,IAC3B,UAAU,CAAC,sBAAsB;AAAA,IACjC,QAAQ,CAAC,oBAAoB;AAAA,EACjC,CAAC;AAAA;AAAA,EAGD,MAAM,WAAW,oBAAoB;AAAA,IACjC,OAAO,CAAC,mBAAmB;AAAA,IAC3B,WAAW,CAAC,uBAAuB;AAAA,IACnC,QAAQ,CAAC,oBAAoB;AAAA,EACjC,CAAC;AAAA,EACD,QAAQ,WAAW,sBAAsB;AAAA,IACrC,OAAO,CAAC,mBAAmB;AAAA,IAC3B,UAAU,CAAC,sBAAsB;AAAA,EACrC,CAAC;AAAA,EACD,OAAO,WAAW,qBAAqB;AAAA,IACnC,OAAO,CAAC,kBAAkB;AAAA,IAC1B,UAAU,CAAC,qBAAqB;AAAA,EACpC,CAAC;AAAA;AAAA,EAGD,QAAQ,WAAW,sBAAsB;AAAA,IACrC,OAAO,CAAC,kBAAkB;AAAA,IAC1B,UAAU,CAAC,qBAAqB;AAAA,EACpC,CAAC;AAAA,EACD,MAAM,WAAW,oBAAoB;AAAA,IACjC,OAAO,CAAC,OAAO;AAAA,IACf,QAAQ,CAAC,UAAU,kBAAkB;AAAA,EACzC,CAAC;AAAA,EACD,KAAK,WAAW,mBAAmB;AAAA,IAC/B,OAAO,CAAC,mBAAmB;AAAA,IAC3B,UAAU,CAAC,uBAAuB,oBAAoB;AAAA,EAC1D,GAAG,KAAK;AAAA,EACR,KAAK,WAAW,mBAAmB;AAAA,IAC/B,UAAU,CAAC,sBAAsB;AAAA,EACrC,CAAC;AAAA,EACD,MAAM,WAAW,oBAAoB;AAAA,IACjC,UAAU,CAAC,qBAAqB;AAAA,EACpC,CAAC;AAAA,EACD,QAAQ,WAAW,sBAAsB;AAAA,IACrC,UAAU,CAAC,MAAM;AAAA;AAAA,EACrB,CAAC;AAAA;AAAA,EAGD,SAAS,WAAW,uBAAuB;AAAA,IACvC,OAAO,CAAC,mBAAmB;AAAA,IAC3B,WAAW,CAAC,uBAAuB;AAAA,IACnC,QAAQ,CAAC,oBAAoB;AAAA,EACjC,CAAC;AACL;;;ADhHA,IAAMC,WAAUC,eAAc,YAAY,GAAG;AAetC,IAAM,cAAN,MAAkB;AAAA,EA3BzB,OA2ByB;AAAA;AAAA;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAe;AAAA,EACf,aAAa,oBAAI,IAAgC;AAAA,EAEzD,YAAY,SAAwB,CAAC,GAAG;AACpC,SAAK,MAAM,OAAO,YAAY;AAC9B,SAAK,MAAM,OAAO,YAAY;AAC9B,SAAK,UAAU,OAAO,WAAW;AAAA,EACrC;AAAA;AAAA,EAGQ,gBAAqB;AACzB,QAAI,CAAC,KAAK,SAAS;AACf,UAAI;AACA,cAAM,SAASD,SAAQ,aAAa;AACpC,aAAK,UAAU,IAAI,OAAO;AAAA,MAC9B,QAAQ;AACJ,aAAK,UAAU;AAAA,MACnB;AAAA,IACJ;AACA,WAAO,KAAK,WAAW;AAAA,EAC3B;AAAA;AAAA,EAGQ,aAAa,UAAsC;AACvD,QAAI,KAAK,WAAW,IAAI,QAAQ,EAAG,QAAO,KAAK,WAAW,IAAI,QAAQ;AAEtE,UAAM,UAAU,SAAS,QAAQ;AACjC,UAAM,UAAU,UAAU,QAAQ,IAAI;AACtC,SAAK,WAAW,IAAI,UAAU,OAAO;AACrC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,UAAkB,SAAiB,UAAwC;AACnF,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAGhC,QAAI,MAAM,UAAU,KAAK,KAAK;AAC1B,aAAO,CAAC;AAAA,QACJ;AAAA,QACA,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS,MAAM;AAAA,QACf,SAAS,QAAQ,KAAK;AAAA,QACtB;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,UAAM,SAAS,KAAK,cAAc;AAClC,UAAM,aAAa,KAAK,aAAa,QAAQ;AAE7C,QAAI,UAAU,YAAY;AACtB,UAAI;AACA,eAAO,YAAY,WAAW,OAAO;AACrC,cAAM,OAAO,OAAO,MAAM,OAAO;AACjC,cAAM,SAAS,KAAK,eAAe,UAAU,OAAO,KAAK,UAAU,YAAY,QAAQ;AAEvF,YAAI,OAAO,SAAS,GAAG;AACnB,iBAAO,OAAO,OAAO,OAAK,EAAE,QAAQ,SAAS,EAAE;AAAA,QACnD;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACJ;AAGA,WAAO,KAAK,cAAc,UAAU,OAAO,QAAQ;AAAA,EACvD;AAAA;AAAA,EAGQ,eACJ,UAAkB,OAClB,UAAe,YAAyB,UAC7B;AACX,UAAM,SAAsB,CAAC;AAC7B,UAAM,OAAO,oBAAI,IAAY;AAE7B,aAAS,IAAI,GAAG,IAAI,SAAS,YAAY,KAAK;AAC1C,YAAM,QAAQ,SAAS,MAAM,CAAC;AAC9B,WAAK,aAAa,UAAU,OAAO,OAAO,YAAY,UAAU,QAAQ,IAAI;AAAA,IAChF;AAEA,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,aACJ,UAAkB,OAAiB,MACnC,YAAyB,UACzB,QAAqB,MACjB;AACJ,UAAM,OAAO,KAAK;AAGlB,QAAI,SAAS,oBAAoB;AAC7B,eAAS,IAAI,GAAG,IAAI,KAAK,YAAY,KAAK;AACtC,cAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,cAAME,YAAW,KAAK,YAAY,MAAM,MAAM,UAAU;AACxD,YAAIA,WAAU;AACV,eAAK,oBAAoB,UAAU,OAAO,MAAM,OAAOA,WAAU,YAAY,UAAU,QAAQ,IAAI;AACnG;AAAA,QACJ;AAAA,MACJ;AAEA,YAAM,YAAY,KAAK,YAAY,MAAM,KAAK,cAAc,MAAM;AAClE,UAAI,aAAa,KAAK,KAAK;AACvB,aAAK,UAAU,UAAU,OAAO,MAAM,YAAY,KAAK,aAAa,IAAI,GAAG,UAAU,QAAQ,IAAI;AAAA,MACrG;AACA;AAAA,IACJ;AAGA,QAAI,SAAS,wBAAwB;AACjC,eAAS,IAAI,GAAG,IAAI,KAAK,YAAY,KAAK;AACtC,cAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,cAAMA,YAAW,KAAK,YAAY,MAAM,MAAM,UAAU;AACxD,YAAIA,WAAU;AACV,eAAK,oBAAoB,UAAU,OAAO,MAAM,OAAOA,WAAU,YAAY,UAAU,QAAQ,IAAI;AACnG;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,WAAW,KAAK,YAAY,MAAM,UAAU;AAClD,QAAI,UAAU;AACV,WAAK,oBAAoB,UAAU,OAAO,MAAM,MAAM,UAAU,YAAY,UAAU,QAAQ,IAAI;AAAA,IACtG;AAAA,EACJ;AAAA;AAAA,EAGQ,YAAY,UAAkB,YAAwC;AAC1E,eAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,WAAW,SAAS,GAAG;AAClE,UAAI,SAAS,MAAM,SAAS,QAAQ,EAAG,QAAO;AAAA,IAClD;AACA,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,oBACJ,UAAkB,OAClB,WAAgB,WAAgB,UAChC,YAAyB,UACzB,QAAqB,MACjB;AACJ,UAAM,YAAY,UAAU,YAAY,MAAM,UAAU,cAAc,MAAM;AAC5E,UAAM,OAAO,KAAK,aAAa,SAAS;AACxC,UAAM,YAAY,KAAK,aAAa,QAAQ;AAG5C,SAAK,aAAa,WAAW,aAAa,YAAY,aAAa,WAAW,YAAY,KAAK,KAAK;AAChG,WAAK,uBAAuB,UAAU,OAAO,WAAW,WAAW,MAAM,YAAY,UAAU,QAAQ,IAAI;AAC3G;AAAA,IACJ;AAGA,QAAI,YAAY,KAAK,KAAK;AACtB,aAAO,KAAK,GAAG,KAAK;AAAA,QAAiB;AAAA,QAAU;AAAA,QAC3C,UAAU,cAAc;AAAA,QAAK,UAAU,YAAY;AAAA,QACnD;AAAA,QAAM;AAAA,QAAW;AAAA,MAAQ,CAAC;AAC9B;AAAA,IACJ;AAGA,QAAI,aAAa,KAAK,KAAK;AACvB,WAAK,UAAU,UAAU,OAAO,WAAW,WAAW,MAAM,UAAU,QAAQ,IAAI;AAAA,IACtF;AAAA,EACJ;AAAA;AAAA,EAGQ,uBACJ,UAAkB,OAClB,WAAgB,WAAgB,WAChC,YAAyB,UACzB,QAAqB,MACjB;AAEJ,UAAM,OAAO,KAAK,eAAe,SAAS;AAC1C,QAAI,CAAC,MAAM;AACP,aAAO,KAAK,GAAG,KAAK;AAAA,QAAiB;AAAA,QAAU;AAAA,QAC3C,UAAU,cAAc;AAAA,QAAK,UAAU,YAAY;AAAA,QACnD;AAAA,QAAW;AAAA,QAAS;AAAA,MAAQ,CAAC;AACjC;AAAA,IACJ;AAGA,UAAM,cAAc,oBAAI,IAAI;AAAA,MACxB,GAAI,WAAW,UAAU,YAAY,CAAC;AAAA,MACtC,GAAI,WAAW,UAAU,UAAU,CAAC;AAAA,IACxC,CAAC;AAED,QAAI,eAAe;AACnB,aAAS,IAAI,GAAG,IAAI,KAAK,YAAY,KAAK;AACtC,YAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,UAAI,aAAa;AAGjB,UAAI,MAAM,SAAS,wBAAwB;AACvC,iBAAS,IAAI,GAAG,IAAI,MAAM,YAAY,KAAK;AACvC,cAAI,YAAY,IAAI,MAAM,MAAM,CAAC,EAAE,IAAI,GAAG;AACtC,yBAAa,MAAM,MAAM,CAAC;AAC1B;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAEA,UAAI,YAAY,IAAI,WAAW,IAAI,KAAK,YAAY,IAAI,MAAM,IAAI,GAAG;AACjE,cAAM,aAAa,KAAK,aAAa,UAAU;AAC/C,cAAM,cAAc,MAAM,SAAS,yBAAyB,QAAQ;AACpE,cAAM,kBAAkB,YAAY,YAAY,MAAM,YAAY,cAAc,MAAM;AAEtF,YAAI,mBAAmB,KAAK,KAAK;AAC7B,yBAAe;AACf,gBAAM,WAAW,GAAG,SAAS,IAAI,UAAU;AAE3C,cAAI,kBAAkB,KAAK,KAAK;AAC5B,mBAAO,KAAK,GAAG,KAAK;AAAA,cAAiB;AAAA,cAAU;AAAA,cAC3C,YAAY,cAAc;AAAA,cAAK,YAAY,YAAY;AAAA,cACvD;AAAA,cAAU;AAAA,cAAU;AAAA,YAAQ,CAAC;AAAA,UACrC,OAAO;AACH,iBAAK,UAAU,UAAU,OAAO,aAAa,UAAU,UAAU,UAAU,QAAQ,IAAI;AAAA,UAC3F;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAGA,QAAI,CAAC,cAAc;AACf,aAAO,KAAK,GAAG,KAAK;AAAA,QAAiB;AAAA,QAAU;AAAA,QAC3C,UAAU,cAAc;AAAA,QAAK,UAAU,YAAY;AAAA,QACnD;AAAA,QAAW;AAAA,QAAS;AAAA,MAAQ,CAAC;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA,EAGQ,eAAe,WAA4B;AAC/C,UAAM,YAAY,CAAC,cAAc,SAAS,oBAAoB,MAAM;AACpE,aAAS,IAAI,GAAG,IAAI,UAAU,YAAY,KAAK;AAC3C,YAAM,QAAQ,UAAU,MAAM,CAAC;AAC/B,UAAI,UAAU,SAAS,MAAM,IAAI,EAAG,QAAO;AAAA,IAC/C;AACA,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,aAAa,MAAmB;AAEpC,QAAI,OAAO,KAAK,sBAAsB,YAAY;AAC9C,YAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,UAAI,SAAU,QAAO,SAAS;AAAA,IAClC;AAEA,aAAS,IAAI,GAAG,IAAI,KAAK,iBAAiB,KAAK;AAC3C,YAAM,QAAQ,KAAK,WAAW,CAAC;AAC/B,UAAI,CAAC,cAAc,mBAAmB,qBAAqB,EAAE,SAAS,MAAM,IAAI,GAAG;AAC/E,eAAO,MAAM;AAAA,MACjB;AAAA,IACJ;AAEA,QAAI,KAAK,SAAS,yBAAyB,KAAK,SAAS,wBAAwB;AAC7E,eAAS,IAAI,GAAG,IAAI,KAAK,iBAAiB,KAAK;AAC3C,cAAM,QAAQ,KAAK,WAAW,CAAC;AAC/B,YAAI,MAAM,SAAS,uBAAuB;AACtC,gBAAM,WAAW,MAAM,kBAAkB,MAAM;AAC/C,cAAI,SAAU,QAAO,SAAS;AAAA,QAClC;AAAA,MACJ;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,aAAa,UAA0B;AAC3C,QAAI,aAAa,WAAW,aAAa,YAAY,aAAa,OAAQ,QAAO;AACjF,QAAI,aAAa,YAAa,QAAO;AACrC,QAAI,aAAa,WAAY,QAAO;AACpC,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,UACJ,UAAkB,OAAiB,MACnC,WAAmB,MAAc,UACjC,QAAqB,MACjB;AACJ,UAAM,QAAQ,KAAK,cAAc;AACjC,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,MAAM,GAAG,KAAK,IAAI,GAAG;AAC3B,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AAEZ,UAAM,UAAU,MAAM,MAAM,OAAO,MAAM,CAAC,EAAE,KAAK,IAAI,EAAE,KAAK;AAC5D,QAAI,QAAQ,UAAU,GAAI;AAE1B,WAAO,KAAK;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,QAAQ;AAAA,MACnB,SAAS,MAAM;AAAA,MACf;AAAA,MACA;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA,EAIQ,cAAc,UAAkB,OAAiB,UAA+B;AACpF,UAAM,SAAsB,CAAC;AAC7B,UAAM,OAAO,KAAK,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC;AAEhD,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,MAAM;AACzC,YAAM,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,MAAM;AAC7C,YAAM,UAAU,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,KAAK;AAClD,UAAI,QAAQ,SAAS,IAAI;AACrB,eAAO,KAAK;AAAA,UACR;AAAA,UACA,WAAW;AAAA,UACX,WAAW,IAAI;AAAA,UACf,SAAS;AAAA,UACT;AAAA,UACA;AAAA,QACJ,CAAC;AAAA,MACL;AACA,UAAI,KAAK,MAAM,OAAQ;AAAA,IAC3B;AAEA,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,iBACJ,UAAkB,OAClB,OAAe,KACf,MAAc,MAAc,UACjB;AACX,UAAM,SAAsB,CAAC;AAC7B,UAAM,OAAO,KAAK,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC;AAChD,QAAI,OAAO;AAEX,aAAS,IAAI,OAAO,KAAK,KAAK,KAAK,MAAM;AACrC,YAAM,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC;AACxC,YAAM,UAAU,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,KAAK;AAClD,UAAI,QAAQ,SAAS,IAAI;AACrB,eAAO,KAAK;AAAA,UACR;AAAA,UACA,WAAW;AAAA,UACX,MAAM,GAAG,IAAI,UAAU,MAAM;AAAA,UAC7B,WAAW,IAAI;AAAA,UACf,SAAS;AAAA,UACT;AAAA,UACA;AAAA,QACJ,CAAC;AAAA,MACL;AACA,UAAI,IAAI,IAAK;AAAA,IACjB;AAEA,WAAO;AAAA,EACX;AACJ;;;AE1PA,OAAO,UAAU;AAvIV,IAAM,uBAA+C;AAAA;AAAA,EAExD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA;AAAA,EAGR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA;AAAA,EAGR,SAAS;AAAA,EACT,OAAO;AAAA,EACP,UAAU;AAAA;AAAA,EAGV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA;AAAA,EAGR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EAGR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ;AAAA;AAAA,EAGR,OAAO;AAAA,EACP,QAAQ;AAAA;AAAA,EAGR,QAAQ;AAAA,EACR,WAAW;AAAA;AAAA,EAGX,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AACZ;AAIO,IAAM,cAAc,oBAAI,IAAI;AAAA;AAAA,EAE/B;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AACJ,CAAC;AAIM,IAAM,eAAe,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAOM,SAAS,YAAY,UAA2B;AACnD,QAAM,MAAM,KAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,SAAO,OAAO;AAClB;AAHgB;AAMT,SAAS,YAAY,UAAsC;AAC9D,QAAM,MAAM,KAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,SAAO,qBAAqB,GAAG;AACnC;AAHgB;AAMT,SAAS,aAAa,SAA0B;AACnD,SAAO,YAAY,IAAI,OAAO;AAClC;AAFgB;AAKT,SAAS,cAAc,UAA2B;AACrD,SAAO,aAAa,IAAI,QAAQ;AACpC;AAFgB;;;AHxIT,IAAM,cAAN,MAAkB;AAAA,EA5BzB,OA4ByB;AAAA;AAAA;AAAA,EACb,WAAW,IAAI,YAAY;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,UAAkB,MAAuB,cAAsB,OAAS;AAChF,SAAK,QAAQ;AACb,SAAK,YAAY;AACjB,SAAK,eAAe;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,UAA4B,CAAC,GAAyB;AAC9D,UAAM,EAAE,eAAe,OAAO,WAAW,IAAI;AAC7C,UAAM,QAAQ,KAAK,UAAU,KAAK,SAAS;AAC3C,QAAI,UAAU,GAAG,UAAU,GAAG,cAAc;AAE5C,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,YAAM,WAAW,MAAM,CAAC;AACxB,YAAM,MAAMC,MAAK,SAAS,KAAK,WAAW,QAAQ;AAClD,mBAAa,KAAK,IAAI,GAAG,MAAM,MAAM;AAErC,UAAI;AACJ,UAAI;AAAE,kBAAU,GAAG,aAAa,UAAU,OAAO;AAAA,MAAG,QAC9C;AAAE;AAAA,MAAU;AAElB,YAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,YAAM,WAAW,KAAK,MAAM,GAAG;AAAA,QAC3B;AAAA,MACJ,EAAE,IAAI,GAAG;AAET,UAAI,CAAC,gBAAgB,UAAU,cAAc,MAAM;AAC/C;AACA;AAAA,MACJ;AAGA,YAAM,YAAY,KAAK,MAAM,GAAG;AAAA,QAC5B;AAAA,MACJ,EAAE,IAAI,GAAG;AACT,UAAI,UAAU,SAAS,GAAG;AACtB,mBAAW,EAAE,GAAG,KAAK,WAAW;AAC5B,eAAK,MAAM,KAAK,OAAO,EAAE;AACzB,eAAK,MAAM,YAAY,OAAO,EAAE;AAAA,QACpC;AACA,aAAK,MAAM,GAAG,QAAQ,6CAA6C,EAAE,IAAI,GAAG;AAAA,MAChF;AAEA,YAAM,MAAMA,MAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,YAAM,WAAW,qBAAqB,GAAG,KAAK;AAC9C,YAAM,SAAS,MAAM,KAAK,SAAS,MAAM,KAAK,SAAS,QAAQ;AAG/D,YAAM,iBAAiB,OAAO,IAAI,WAAS;AAAA,QACvC,SAAS,GAAG;AAAA,QACZ,MAAM,OAAO,GAAG,MAAM,SAAS,KAAK,MAAM,IAAI,KAAK,MAAM;AAAA,QACzD,MAAM;AAAA,MACV,EAAE,KAAK,IAAI,CAAC;AAEZ,YAAM,OAAO,MAAM,KAAK,MAAM,UAAU,WAAW,cAAc;AAGjE,eAAS,KAAK,GAAG,KAAK,OAAO,QAAQ,MAAM;AACvC,cAAM,QAAQ,OAAO,EAAE;AACvB,cAAM,SAAS,KAAK,MAAM,GAAG;AAAA,UACzB;AAAA;AAAA,QAEJ,EAAE,IAAI,KAAK,MAAM,WAAW,MAAM,QAAQ,MAAM,MAAM,WAAW,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAE7G,cAAM,KAAK,OAAO,OAAO,eAAe;AACxC,aAAK,MAAM,GAAG;AAAA,UACV;AAAA,QACJ,EAAE,IAAI,IAAI,OAAO,KAAK,KAAK,EAAE,EAAE,MAAM,CAAC;AAEtC,aAAK,MAAM,KAAK,IAAI,KAAK,EAAE,GAAG,EAAE;AAChC,aAAK,MAAM,YAAY,IAAI,IAAI,KAAK,EAAE,CAAC;AACvC;AAAA,MACJ;AAGA,WAAK,MAAM,GAAG;AAAA,QACV;AAAA,MACJ,EAAE,IAAI,KAAK,IAAI;AACf;AAAA,IACJ;AAEA,WAAO,EAAE,SAAS,SAAS,QAAQ,YAAY;AAAA,EACnD;AAAA;AAAA,EAIQ,UAAU,KAAa,QAAkB,CAAC,GAAa;AAC3D,QAAI;AACJ,QAAI;AAAE,gBAAU,GAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IAAG,QACxD;AAAE,aAAO;AAAA,IAAO;AAEtB,eAAW,SAAS,SAAS;AACzB,UAAI,MAAM,YAAY,GAAG;AACrB,YAAI,aAAa,MAAM,IAAI,EAAG;AAC9B,aAAK,UAAUA,MAAK,KAAK,KAAK,MAAM,IAAI,GAAG,KAAK;AAAA,MACpD,WAAW,MAAM,OAAO,GAAG;AACvB,YAAI,cAAc,MAAM,IAAI,EAAG;AAC/B,cAAM,MAAMA,MAAK,QAAQ,MAAM,IAAI,EAAE,YAAY;AACjD,YAAI,EAAE,OAAO,sBAAuB;AAEpC,cAAM,OAAOA,MAAK,KAAK,KAAK,MAAM,IAAI;AACtC,YAAI;AACA,cAAI,GAAG,SAAS,IAAI,EAAE,QAAQ,KAAK,cAAc;AAC7C,kBAAM,KAAK,IAAI;AAAA,UACnB;AAAA,QACJ,QAAQ;AAAA,QAAC;AAAA,MACb;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA;AAAA,EAIQ,MAAM,SAAyB;AACnC,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,WAAK,QAAQ,WAAW,CAAC;AACzB,UAAK,IAAI,aAAc;AAAA,IAC3B;AACA,WAAO,EAAE,SAAS,EAAE;AAAA,EACxB;AACJ;;;AIhIA,IAAM,aAAN,MAAoC;AAAA,EAMhC,YAAoB,OAA0B,CAAC,GAAG;AAA9B;AAChB,SAAK,OAAO,KAAK,QAAQ;AAAA,EAC7B;AAAA,EAtCJ,OA8BoC;AAAA;AAAA;AAAA,EACvB;AAAA,EACT;AAAA,EACA;AAAA,EACA,WAAW,oBAAI,IAA0B;AAAA,EAMzC,MAAM,WAAW,KAAoC;AAEjD,UAAM,SAAS,MAAM,IAAI,sBAAsB,MAAM;AACrD,SAAK,OAAO,OAAO;AACnB,SAAK,WAAW,OAAO;AAGvB,QAAI,OAAO,OAAO;AACd,UAAI,YAAY,gBAAgB,YAAY,KAAK,MAAM,KAAK,QAAQ;AAAA,IACxE;AAEA,UAAM,WAAW,KAAK,KAAK,YAAY,IAAI,OAAO;AAClD,SAAK,UAAU,IAAI,YAAY,UAAU;AAAA,MACrC,IAAI,IAAI;AAAA,MACR,MAAM,KAAK;AAAA,MACX,aAAa,KAAK;AAAA,MAClB,WAAW,IAAI;AAAA,IACnB,GAAG,KAAK,KAAK,eAAe,IAAI,OAAO,WAAW;AAAA,EACtD;AAAA,EAEA,MAAM,MAAM,UAGR,CAAC,GAAyB;AAC1B,WAAO,KAAK,QAAQ,MAAM,OAAO;AAAA,EACrC;AAAA,EAEA,QAA6B;AACzB,WAAO,EAAE,UAAU,KAAK,KAAK,KAAK;AAAA,EACtC;AACJ;AAGO,SAAS,KAAK,MAAmC;AACpD,SAAO,IAAI,WAAW,IAAI;AAC9B;AAFgB;","names":["path","createRequire","require","require","createRequire","category","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/indexers/code/code-indexer.ts","../src/indexers/code/code-chunker.ts","../src/indexers/code/grammars.ts","../src/indexers/languages.ts","../src/indexers/code/code-plugin.ts"],"sourcesContent":["/**\n * BrainBank — Code Indexer\n * \n * Walks a repository, chunks source files semantically,\n * embeds each chunk, and stores in SQLite + HNSW.\n * Incremental: only re-indexes files that changed (by content hash).\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { CodeChunker } from './code-chunker.ts';\nimport { SUPPORTED_EXTENSIONS, IGNORE_DIRS, isIgnoredDir, isIgnoredFile } from '../languages.ts';\nimport type { Database } from '../../db/database.ts';\nimport type { EmbeddingProvider, ProgressCallback, IndexResult } from '../../types.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\n\nexport interface CodeIndexerDeps {\n db: Database;\n hnsw: HNSWIndex;\n vectorCache: Map<number, Float32Array>;\n embedding: EmbeddingProvider;\n}\n\nexport interface CodeIndexOptions {\n forceReindex?: boolean;\n onProgress?: ProgressCallback;\n}\n\nexport class CodeIndexer {\n private _chunker = new CodeChunker();\n private _deps: CodeIndexerDeps;\n private _repoPath: string;\n private _maxFileSize: number;\n\n constructor(repoPath: string, deps: CodeIndexerDeps, maxFileSize: number = 512_000) {\n this._deps = deps;\n this._repoPath = repoPath;\n this._maxFileSize = maxFileSize;\n }\n\n /**\n * Index all supported files in the repository.\n * Skips unchanged files (same content hash).\n */\n async index(options: CodeIndexOptions = {}): Promise<IndexResult> {\n const { forceReindex = false, onProgress } = options;\n const files = this._walkRepo(this._repoPath);\n let indexed = 0, skipped = 0, totalChunks = 0;\n\n for (let i = 0; i < files.length; i++) {\n const filePath = files[i];\n const rel = path.relative(this._repoPath, filePath);\n onProgress?.(rel, i + 1, files.length);\n\n let content: string;\n try { content = fs.readFileSync(filePath, 'utf-8'); }\n catch { continue; }\n\n const hash = this._hash(content);\n const existing = this._deps.db.prepare(\n 'SELECT file_hash FROM indexed_files WHERE file_path = ?'\n ).get(rel) as any;\n\n if (!forceReindex && existing?.file_hash === hash) {\n skipped++;\n continue;\n }\n\n // Always clean up old chunks for this file (catches orphaned chunks from interrupted first-run)\n const oldChunks = this._deps.db.prepare(\n 'SELECT id FROM code_chunks WHERE file_path = ?'\n ).all(rel) as any[];\n if (oldChunks.length > 0) {\n for (const { id } of oldChunks) {\n this._deps.hnsw.remove(id);\n this._deps.vectorCache.delete(id);\n }\n this._deps.db.prepare('DELETE FROM code_chunks WHERE file_path = ?').run(rel);\n }\n\n const ext = path.extname(filePath).toLowerCase();\n const language = SUPPORTED_EXTENSIONS[ext] ?? 'text';\n const chunks = await this._chunker.chunk(rel, content, language);\n\n // Build embedding texts for all chunks and batch embed (10-50x faster)\n const embeddingTexts = chunks.map(chunk => [\n `File: ${rel}`,\n chunk.name ? `${chunk.chunkType}: ${chunk.name}` : chunk.chunkType,\n chunk.content,\n ].join('\\n'));\n\n const vecs = await this._deps.embedding.embedBatch(embeddingTexts);\n\n // Insert chunks + vectors individually (preserves auto-increment ID ordering)\n for (let ci = 0; ci < chunks.length; ci++) {\n const chunk = chunks[ci];\n const result = this._deps.db.prepare(\n `INSERT INTO code_chunks (file_path, chunk_type, name, start_line, end_line, content, language, file_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n ).run(rel, chunk.chunkType, chunk.name ?? null, chunk.startLine, chunk.endLine, chunk.content, language, hash);\n\n const id = Number(result.lastInsertRowid);\n this._deps.db.prepare(\n 'INSERT INTO code_vectors (chunk_id, embedding) VALUES (?, ?)'\n ).run(id, Buffer.from(vecs[ci].buffer));\n\n this._deps.hnsw.add(vecs[ci], id);\n this._deps.vectorCache.set(id, vecs[ci]);\n totalChunks++;\n }\n\n // Mark file as indexed\n this._deps.db.prepare(\n 'INSERT OR REPLACE INTO indexed_files (file_path, file_hash) VALUES (?, ?)'\n ).run(rel, hash);\n indexed++;\n }\n\n return { indexed, skipped, chunks: totalChunks };\n }\n\n // ── File Walker ─────────────────────────────────\n\n private _walkRepo(dir: string, files: string[] = []): string[] {\n let entries: fs.Dirent[];\n try { entries = fs.readdirSync(dir, { withFileTypes: true }); }\n catch { return files; }\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (isIgnoredDir(entry.name)) continue;\n this._walkRepo(path.join(dir, entry.name), files);\n } else if (entry.isFile()) {\n if (isIgnoredFile(entry.name)) continue;\n const ext = path.extname(entry.name).toLowerCase();\n if (!(ext in SUPPORTED_EXTENSIONS)) continue;\n\n const full = path.join(dir, entry.name);\n try {\n if (fs.statSync(full).size <= this._maxFileSize) {\n files.push(full);\n }\n } catch {}\n }\n }\n return files;\n }\n\n // ── FNV-1a Hash ─────────────────────────────────\n\n private _hash(content: string): string {\n let h = 2166136261;\n for (let i = 0; i < content.length; i++) {\n h ^= content.charCodeAt(i);\n h = (h * 16777619) >>> 0;\n }\n return h.toString(16);\n }\n}\n","/**\n * BrainBank — Tree-Sitter Code Chunker\n * \n * AST-aware code splitting using native tree-sitter bindings.\n * Extracts semantic blocks (functions, classes, methods, interfaces)\n * from the AST. Falls back to sliding window for unsupported languages.\n */\n\nimport { createRequire } from 'node:module';\nimport type { CodeChunk } from '../../types.ts';\nimport { GRAMMARS, type LangGrammar } from './grammars.ts';\n\nconst require = createRequire(import.meta.url);\n\n// ── Configuration ───────────────────────────────────\n\nexport interface ChunkerConfig {\n /** Max lines per chunk. Default: 80 */\n maxLines?: number;\n /** Min lines for a detected block to be a chunk. Default: 3 */\n minLines?: number;\n /** Overlap between adjacent generic chunks. Default: 5 */\n overlap?: number;\n}\n\n// ── CodeChunker ─────────────────────────────────────\n\nexport class CodeChunker {\n private MAX: number;\n private MIN: number;\n private OVERLAP: number;\n private _parser: any = null;\n private _langCache = new Map<string, LangGrammar | null>();\n\n constructor(config: ChunkerConfig = {}) {\n this.MAX = config.maxLines ?? 80;\n this.MIN = config.minLines ?? 3;\n this.OVERLAP = config.overlap ?? 5;\n }\n\n /** Lazy-init tree-sitter parser. */\n private _ensureParser(): any {\n if (!this._parser) {\n try {\n const Parser = require('tree-sitter');\n this._parser = new Parser();\n } catch {\n this._parser = false; // Mark as unavailable\n }\n }\n return this._parser || null;\n }\n\n /** Load a language grammar (cached). */\n private _loadGrammar(language: string): LangGrammar | null {\n if (this._langCache.has(language)) return this._langCache.get(language)!;\n\n const factory = GRAMMARS[language];\n const grammar = factory ? factory() : null;\n this._langCache.set(language, grammar);\n return grammar;\n }\n\n /**\n * Split file content into semantic chunks using tree-sitter AST.\n * Falls back to sliding window if grammar isn't available.\n */\n async chunk(filePath: string, content: string, language: string): Promise<CodeChunk[]> {\n const lines = content.split('\\n');\n\n // Small file → single chunk\n if (lines.length <= this.MAX) {\n return [{\n filePath,\n chunkType: 'file',\n startLine: 1,\n endLine: lines.length,\n content: content.trim(),\n language,\n }];\n }\n\n // Try tree-sitter AST chunking\n const parser = this._ensureParser();\n const langConfig = this._loadGrammar(language);\n\n if (parser && langConfig) {\n try {\n parser.setLanguage(langConfig.grammar);\n const tree = parser.parse(content);\n const chunks = this._extractChunks(filePath, lines, tree.rootNode, langConfig, language);\n\n if (chunks.length > 0) {\n return chunks.filter(c => c.content.length > 20);\n }\n } catch {\n // Tree-sitter failed — fall through to generic\n }\n }\n\n // Fallback to sliding window\n return this._chunkGeneric(filePath, lines, language);\n }\n\n /** Walk AST and extract top-level semantic blocks. */\n private _extractChunks(\n filePath: string, lines: string[],\n rootNode: any, langConfig: LangGrammar, language: string,\n ): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const seen = new Set<string>();\n\n for (let i = 0; i < rootNode.childCount; i++) {\n const child = rootNode.child(i);\n this._processNode(filePath, lines, child, langConfig, language, chunks, seen);\n }\n\n return chunks;\n }\n\n /** Classify and process a single AST node. */\n private _processNode(\n filePath: string, lines: string[], node: any,\n langConfig: LangGrammar, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n const type = node.type;\n\n // Handle export_statement: process what it wraps\n if (type === 'export_statement') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n const category = this._categorize(child.type, langConfig);\n if (category) {\n this._processDeclaration(filePath, lines, node, child, category, langConfig, language, chunks, seen);\n return;\n }\n }\n // Export with no recognized declaration — chunk the whole thing if big enough\n const nodeLines = node.endPosition.row - node.startPosition.row + 1;\n if (nodeLines >= this.MIN) {\n this._addChunk(filePath, lines, node, 'function', this._extractName(node), language, chunks, seen);\n }\n return;\n }\n\n // Python decorated definitions (@decorator + class/def)\n if (type === 'decorated_definition') {\n for (let i = 0; i < node.childCount; i++) {\n const child = node.child(i);\n const category = this._categorize(child.type, langConfig);\n if (category) {\n this._processDeclaration(filePath, lines, node, child, category, langConfig, language, chunks, seen);\n return;\n }\n }\n }\n\n // Direct match\n const category = this._categorize(type, langConfig);\n if (category) {\n this._processDeclaration(filePath, lines, node, node, category, langConfig, language, chunks, seen);\n }\n }\n\n /** Check which category a node type belongs to. */\n private _categorize(nodeType: string, langConfig: LangGrammar): string | null {\n for (const [category, types] of Object.entries(langConfig.nodeTypes)) {\n if (types && types.includes(nodeType)) return category;\n }\n return null;\n }\n\n /** Process a matched declaration: class → split by methods, else → chunk directly. */\n private _processDeclaration(\n filePath: string, lines: string[],\n outerNode: any, innerNode: any, category: string,\n langConfig: LangGrammar, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n const nodeLines = outerNode.endPosition.row - outerNode.startPosition.row + 1;\n const name = this._extractName(innerNode);\n const chunkType = this._toChunkType(category);\n\n // Large class → split into methods\n if ((category === 'class' || category === 'struct' || category === 'impl') && nodeLines > this.MAX) {\n this._splitClassIntoMethods(filePath, lines, outerNode, innerNode, name, langConfig, language, chunks, seen);\n return;\n }\n\n // Large non-class → split with overlap\n if (nodeLines > this.MAX) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n outerNode.startPosition.row, outerNode.endPosition.row,\n name, chunkType, language));\n return;\n }\n\n // Normal-sized node\n if (nodeLines >= this.MIN) {\n this._addChunk(filePath, lines, outerNode, chunkType, name, language, chunks, seen);\n }\n }\n\n /** Split a large class into individual method chunks. */\n private _splitClassIntoMethods(\n filePath: string, lines: string[],\n outerNode: any, classNode: any, className: string,\n langConfig: LangGrammar, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n // Find class body\n const body = this._findClassBody(classNode);\n if (!body) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n outerNode.startPosition.row, outerNode.endPosition.row,\n className, 'class', language));\n return;\n }\n\n // Get method node types\n const methodTypes = new Set([\n ...(langConfig.nodeTypes.function || []),\n ...(langConfig.nodeTypes.method || []),\n ]);\n\n let methodsFound = false;\n for (let i = 0; i < body.childCount; i++) {\n const child = body.child(i);\n let methodNode = child;\n\n // Decorated methods\n if (child.type === 'decorated_definition') {\n for (let j = 0; j < child.childCount; j++) {\n if (methodTypes.has(child.child(j).type)) {\n methodNode = child.child(j);\n break;\n }\n }\n }\n\n if (methodTypes.has(methodNode.type) || methodTypes.has(child.type)) {\n const methodName = this._extractName(methodNode);\n const nodeToChunk = child.type === 'decorated_definition' ? child : methodNode;\n const methodLineCount = nodeToChunk.endPosition.row - nodeToChunk.startPosition.row + 1;\n\n if (methodLineCount >= this.MIN) {\n methodsFound = true;\n const fullName = `${className}.${methodName}`;\n\n if (methodLineCount > this.MAX) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n nodeToChunk.startPosition.row, nodeToChunk.endPosition.row,\n fullName, 'method', language));\n } else {\n this._addChunk(filePath, lines, nodeToChunk, 'method', fullName, language, chunks, seen);\n }\n }\n }\n }\n\n // Fallback: no methods found → split the whole class\n if (!methodsFound) {\n chunks.push(...this._splitLargeBlock(filePath, lines,\n outerNode.startPosition.row, outerNode.endPosition.row,\n className, 'class', language));\n }\n }\n\n /** Find the class body node. */\n private _findClassBody(classNode: any): any | null {\n const bodyTypes = ['class_body', 'block', 'declaration_list', 'body'];\n for (let i = 0; i < classNode.childCount; i++) {\n const child = classNode.child(i);\n if (bodyTypes.includes(child.type)) return child;\n }\n return null;\n }\n\n /** Extract name from an AST node. */\n private _extractName(node: any): string {\n // Try childForFieldName('name')\n if (typeof node.childForFieldName === 'function') {\n const nameNode = node.childForFieldName('name');\n if (nameNode) return nameNode.text;\n }\n // Try identifier children\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (['identifier', 'type_identifier', 'property_identifier'].includes(child.type)) {\n return child.text;\n }\n }\n // For variable declarations, dig into declarators\n if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (child.type === 'variable_declarator') {\n const nameNode = child.childForFieldName('name');\n if (nameNode) return nameNode.text;\n }\n }\n }\n return 'anonymous';\n }\n\n /** Map category to chunk type. */\n private _toChunkType(category: string): string {\n if (category === 'class' || category === 'struct' || category === 'impl') return 'class';\n if (category === 'interface') return 'interface';\n if (category === 'variable') return 'function';\n return category;\n }\n\n /** Add a node as a chunk, avoiding duplicates. */\n private _addChunk(\n filePath: string, lines: string[], node: any,\n chunkType: string, name: string, language: string,\n chunks: CodeChunk[], seen: Set<string>,\n ): void {\n const start = node.startPosition.row;\n const end = node.endPosition.row;\n const key = `${start}-${end}`;\n if (seen.has(key)) return;\n seen.add(key);\n\n const content = lines.slice(start, end + 1).join('\\n').trim();\n if (content.length <= 20) return;\n\n chunks.push({\n filePath,\n chunkType,\n name,\n startLine: start + 1,\n endLine: end + 1,\n content,\n language,\n });\n }\n\n // ── Fallback: Generic sliding window ────────────\n\n private _chunkGeneric(filePath: string, lines: string[], language: string): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const step = Math.max(this.MAX - this.OVERLAP, 1);\n\n for (let s = 0; s < lines.length; s += step) {\n const e = Math.min(s + this.MAX, lines.length);\n const content = lines.slice(s, e).join('\\n').trim();\n if (content.length > 20) {\n chunks.push({\n filePath,\n chunkType: 'block',\n startLine: s + 1,\n endLine: e,\n content,\n language,\n });\n }\n if (e >= lines.length) break;\n }\n\n return chunks;\n }\n\n /** Split a large block into overlapping sub-chunks. */\n private _splitLargeBlock(\n filePath: string, lines: string[],\n start: number, end: number,\n name: string, type: string, language: string,\n ): CodeChunk[] {\n const chunks: CodeChunk[] = [];\n const step = Math.max(this.MAX - this.OVERLAP, 1);\n let part = 1;\n\n for (let s = start; s <= end; s += step) {\n const e = Math.min(s + this.MAX, end + 1);\n const content = lines.slice(s, e).join('\\n').trim();\n if (content.length > 20) {\n chunks.push({\n filePath,\n chunkType: type,\n name: `${name} (part ${part++})`,\n startLine: s + 1,\n endLine: e,\n content,\n language,\n });\n }\n if (e > end) break;\n }\n\n return chunks;\n }\n}\n","/**\n * BrainBank — Tree-Sitter Grammar Registry\n *\n * Maps language names to their tree-sitter grammar packages\n * and the AST node types that represent semantic blocks.\n */\n\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n// ── Types ───────────────────────────────────────────\n\nexport interface LangGrammar {\n grammar: any;\n nodeTypes: {\n class?: string[];\n function?: string[];\n interface?: string[];\n variable?: string[];\n method?: string[];\n struct?: string[];\n impl?: string[];\n };\n}\n\n// ── Loader ──────────────────────────────────────────\n\n/** Try to require a grammar, return null if not installed. */\nfunction tryGrammar(pkg: string, nodeTypes: LangGrammar['nodeTypes'], accessor?: string): () => LangGrammar | null {\n return () => {\n try {\n const mod = require(pkg);\n return { grammar: accessor ? mod[accessor] : mod, nodeTypes };\n } catch { return null; }\n };\n}\n\n// ── Grammar Table ───────────────────────────────────\n\nexport const GRAMMARS: Record<string, () => LangGrammar | null> = {\n // ── Web ──────────────────────────────────────────\n typescript: tryGrammar('tree-sitter-typescript', {\n class: ['class_declaration'],\n interface: ['interface_declaration', 'type_alias_declaration'],\n function: ['function_declaration', 'method_definition'],\n variable: ['lexical_declaration'],\n }, 'typescript'),\n javascript: tryGrammar('tree-sitter-javascript', {\n class: ['class_declaration'],\n function: ['function_declaration', 'method_definition'],\n variable: ['lexical_declaration'],\n }),\n html: tryGrammar('tree-sitter-html', {}),\n css: tryGrammar('tree-sitter-css', {}),\n\n // ── Systems ──────────────────────────────────────\n go: tryGrammar('tree-sitter-go', {\n function: ['function_declaration', 'method_declaration'],\n struct: ['type_declaration'],\n }),\n rust: tryGrammar('tree-sitter-rust', {\n function: ['function_item'],\n struct: ['struct_item'],\n impl: ['impl_item'],\n }),\n c: tryGrammar('tree-sitter-c', {\n function: ['function_definition'],\n struct: ['struct_specifier'],\n }),\n cpp: tryGrammar('tree-sitter-cpp', {\n class: ['class_specifier'],\n function: ['function_definition'],\n }),\n swift: tryGrammar('tree-sitter-swift', {\n class: ['class_declaration'],\n function: ['function_declaration'],\n struct: ['struct_declaration'],\n }),\n\n // ── JVM ──────────────────────────────────────────\n java: tryGrammar('tree-sitter-java', {\n class: ['class_declaration'],\n interface: ['interface_declaration'],\n method: ['method_declaration'],\n }),\n kotlin: tryGrammar('tree-sitter-kotlin', {\n class: ['class_declaration'],\n function: ['function_declaration'],\n }),\n scala: tryGrammar('tree-sitter-scala', {\n class: ['class_definition'],\n function: ['function_definition'],\n }),\n\n // ── Scripting ────────────────────────────────────\n python: tryGrammar('tree-sitter-python', {\n class: ['class_definition'],\n function: ['function_definition'],\n }),\n ruby: tryGrammar('tree-sitter-ruby', {\n class: ['class'],\n method: ['method', 'singleton_method'],\n }),\n php: tryGrammar('tree-sitter-php', {\n class: ['class_declaration'],\n function: ['function_definition', 'method_declaration'],\n }, 'php'),\n lua: tryGrammar('tree-sitter-lua', {\n function: ['function_declaration'],\n }),\n bash: tryGrammar('tree-sitter-bash', {\n function: ['function_definition'],\n }),\n elixir: tryGrammar('tree-sitter-elixir', {\n function: ['call'], // defmodule, def, defp\n }),\n\n // ── .NET ─────────────────────────────────────────\n c_sharp: tryGrammar('tree-sitter-c-sharp', {\n class: ['class_declaration'],\n interface: ['interface_declaration'],\n method: ['method_declaration'],\n }),\n};\n","/**\n * BrainBank — Language Registry\n * \n * Supported file extensions, language mappings, and ignore lists.\n * Controls which files get indexed and how they're chunked.\n */\n\n// ── Supported Extensions ────────────────────────────\n\nexport const SUPPORTED_EXTENSIONS: Record<string, string> = {\n // TypeScript / JavaScript\n '.ts': 'typescript',\n '.tsx': 'typescript',\n '.js': 'javascript',\n '.jsx': 'javascript',\n '.mjs': 'javascript',\n '.cjs': 'javascript',\n\n // Systems\n '.go': 'go',\n '.rs': 'rust',\n '.cpp': 'cpp',\n '.cc': 'cpp',\n '.c': 'c',\n '.h': 'c',\n '.hpp': 'cpp',\n\n // JVM\n '.java': 'java',\n '.kt': 'kotlin',\n '.scala': 'scala',\n\n // Scripting\n '.py': 'python',\n '.rb': 'ruby',\n '.php': 'php',\n '.lua': 'lua',\n '.sh': 'bash',\n '.bash': 'bash',\n '.zsh': 'bash',\n\n // Web\n '.html': 'html',\n '.css': 'css',\n '.scss': 'scss',\n '.less': 'less',\n '.svelte': 'svelte',\n '.vue': 'vue',\n\n // Data / Config\n '.json': 'json',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n '.toml': 'toml',\n '.xml': 'xml',\n '.graphql': 'graphql',\n '.gql': 'graphql',\n\n // Docs\n '.md': 'markdown',\n '.mdx': 'markdown',\n\n // Database\n '.sql': 'sql',\n '.prisma': 'prisma',\n\n // Other\n '.swift': 'swift',\n '.dart': 'dart',\n '.r': 'r',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.erl': 'erlang',\n '.zig': 'zig',\n};\n\n// ── Ignore Directories ──────────────────────────────\n\nexport const IGNORE_DIRS = new Set([\n // Package managers\n 'node_modules',\n 'bower_components',\n '.pnpm',\n\n // Build output\n 'dist',\n 'build',\n 'out',\n '.next',\n '.nuxt',\n '.output',\n '.svelte-kit',\n\n // Auto-generated code\n 'generated',\n 'sdk',\n 'openapi',\n\n // Version control\n '.git',\n '.hg',\n '.svn',\n\n // IDE / Editor\n '.idea',\n '.vscode',\n\n // Runtime / Cache\n '__pycache__',\n '.pytest_cache',\n 'venv',\n '.venv',\n '.env',\n '.tox',\n\n // Coverage / Test artifacts\n 'coverage',\n '.nyc_output',\n 'htmlcov',\n\n // Compiled\n 'target', // Rust, Java\n '.cargo',\n 'vendor', // Go, PHP\n\n // Database (auto-generated migrations, dumps, seeds)\n 'migrations',\n 'db_dumps',\n 'seeds',\n\n // AI / Model cache\n '.model-cache',\n '.brainbank',\n\n // OS\n '.DS_Store',\n]);\n\n// ── Ignore Files ────────────────────────────────────\n\nexport const IGNORE_FILES = new Set([\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n 'bun.lockb',\n 'Cargo.lock',\n 'Gemfile.lock',\n 'poetry.lock',\n 'composer.lock',\n 'go.sum',\n]);\n\n// ── Helpers ─────────────────────────────────────────\n\nimport path from 'node:path';\n\n/** Check if a file extension is supported for indexing. */\nexport function isSupported(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return ext in SUPPORTED_EXTENSIONS;\n}\n\n/** Get the language name for a file. Returns undefined if not supported. */\nexport function getLanguage(filePath: string): string | undefined {\n const ext = path.extname(filePath).toLowerCase();\n return SUPPORTED_EXTENSIONS[ext];\n}\n\n/** Check if a directory name should be ignored. */\nexport function isIgnoredDir(dirName: string): boolean {\n return IGNORE_DIRS.has(dirName);\n}\n\n/** Check if a filename should be ignored. */\nexport function isIgnoredFile(fileName: string): boolean {\n return IGNORE_FILES.has(fileName);\n}\n","/**\n * BrainBank — Code Module\n * \n * Language-aware code indexing for 30+ languages.\n * \n * import { BrainBank } from 'brainbank';\n * import { code } from 'brainbank/code';\n * \n * const brain = new BrainBank().use(code({ repoPath: '.' }));\n * \n * // Multi-repo: namespace to avoid key collisions\n * brain\n * .use(code({ repoPath: './frontend', name: 'code:frontend' }))\n * .use(code({ repoPath: './backend', name: 'code:backend' }));\n */\n\nimport type { Indexer, IndexerContext } from '../base.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\nimport { CodeIndexer } from './code-indexer.ts';\nimport type { IndexResult, ProgressCallback } from '../../types.ts';\n\nexport interface CodePluginOptions {\n /** Repository path to index. Default: '.' */\n repoPath?: string;\n /** Maximum file size in bytes. Default: from config */\n maxFileSize?: number;\n /** Custom indexer name for multi-repo (e.g. 'code:frontend'). Default: 'code' */\n name?: string;\n}\n\nclass CodePlugin implements Indexer {\n readonly name: string;\n hnsw!: HNSWIndex;\n indexer!: CodeIndexer;\n vecCache = new Map<number, Float32Array>();\n\n constructor(private opts: CodePluginOptions = {}) {\n this.name = opts.name ?? 'code';\n }\n\n async initialize(ctx: IndexerContext): Promise<void> {\n // Use shared HNSW so all code indexers (code, code:frontend, etc.) share one index\n const shared = await ctx.getOrCreateSharedHnsw('code');\n this.hnsw = shared.hnsw;\n this.vecCache = shared.vecCache;\n\n // Only load vectors once (first code indexer to initialize)\n if (shared.isNew) {\n ctx.loadVectors('code_vectors', 'chunk_id', this.hnsw, this.vecCache);\n }\n\n const repoPath = this.opts.repoPath ?? ctx.config.repoPath;\n this.indexer = new CodeIndexer(repoPath, {\n db: ctx.db,\n hnsw: this.hnsw,\n vectorCache: this.vecCache,\n embedding: ctx.embedding,\n }, this.opts.maxFileSize ?? ctx.config.maxFileSize);\n }\n\n async index(options: {\n forceReindex?: boolean;\n onProgress?: ProgressCallback;\n } = {}): Promise<IndexResult> {\n return this.indexer.index(options);\n }\n\n stats(): Record<string, any> {\n return { hnswSize: this.hnsw.size };\n }\n}\n\n/** Create a code indexing plugin. */\nexport function code(opts?: CodePluginOptions): Indexer {\n return new CodePlugin(opts);\n}\n"],"mappings":"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","names":["path","createRequire","require","require","createRequire","category","path"]}
|
|
@@ -53,9 +53,14 @@ function resultKey(r) {
|
|
|
53
53
|
__name(resultKey, "resultKey");
|
|
54
54
|
|
|
55
55
|
// src/search/keyword/utils.ts
|
|
56
|
+
function splitCompound(word) {
|
|
57
|
+
return word.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[_\-./\\]/g, " ").trim();
|
|
58
|
+
}
|
|
59
|
+
__name(splitCompound, "splitCompound");
|
|
56
60
|
function sanitizeFTS(query) {
|
|
57
61
|
const clean = query.replace(/[{}[\]()^~*:]/g, " ").replace(/\bAND\b|\bOR\b|\bNOT\b|\bNEAR\b/gi, "").trim();
|
|
58
|
-
const
|
|
62
|
+
const expanded = clean.split(/\s+/).map((w) => splitCompound(w)).join(" ");
|
|
63
|
+
const words = expanded.split(/\s+/).filter((w) => w.length > 1);
|
|
59
64
|
if (words.length === 0) return "";
|
|
60
65
|
return words.map((w) => `"${w}"`).join(" ");
|
|
61
66
|
}
|
|
@@ -71,4 +76,4 @@ export {
|
|
|
71
76
|
sanitizeFTS,
|
|
72
77
|
normalizeBM25
|
|
73
78
|
};
|
|
74
|
-
//# sourceMappingURL=chunk-
|
|
79
|
+
//# sourceMappingURL=chunk-FEAMUZGJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/search/rrf.ts","../src/search/keyword/utils.ts"],"sourcesContent":["/**\n * BrainBank — Reciprocal Rank Fusion (RRF)\n * \n * Combines results from multiple search systems (vector + BM25)\n * using the RRF algorithm: score = Σ 1/(k + rank_i)\n * \n * This is the same algorithm used by Elasticsearch, QMD, and most\n * production hybrid search systems. Simple but very effective.\n * \n * Reference: Cormack et al., \"Reciprocal Rank Fusion outperforms\n * Condorcet and individual Rank Learning Methods\" (2009)\n */\n\nimport type { SearchResult } from '../types.ts';\n\n/**\n * Fuse ranked lists from different search systems into a single ranked list.\n * \n * @param resultSets - Arrays of SearchResult from different systems (e.g. vector, BM25)\n * @param k - Smoothing constant. Default: 60 (standard value). Higher = less emphasis on top ranks.\n * @param maxResults - Maximum results to return.\n */\nexport function reciprocalRankFusion(\n resultSets: SearchResult[][],\n k: number = 60,\n maxResults: number = 15,\n): SearchResult[] {\n // Build a map: unique key → { bestResult, rrfScore }\n const fused = new Map<string, { result: SearchResult; rrfScore: number }>();\n\n for (const results of resultSets) {\n for (let rank = 0; rank < results.length; rank++) {\n const r = results[rank];\n const key = resultKey(r);\n const rrfContribution = 1.0 / (k + rank + 1);\n\n const existing = fused.get(key);\n if (existing) {\n existing.rrfScore += rrfContribution;\n // Keep the result with the higher original score\n if (r.score > existing.result.score) {\n existing.result = { ...r };\n }\n } else {\n fused.set(key, {\n result: { ...r },\n rrfScore: rrfContribution,\n });\n }\n }\n }\n\n // Sort by RRF score descending, normalize, and return\n const sorted = Array.from(fused.values())\n .sort((a, b) => b.rrfScore - a.rrfScore)\n .slice(0, maxResults);\n\n // Normalize RRF scores to 0..1 range\n const maxRRF = sorted[0]?.rrfScore ?? 1;\n return sorted.map(entry => ({\n ...entry.result,\n score: entry.rrfScore / maxRRF,\n metadata: {\n ...entry.result.metadata,\n rrfScore: entry.rrfScore,\n } as any,\n }));\n}\n\n/**\n * Generate a unique key for a search result to detect duplicates across systems.\n */\nfunction resultKey(r: SearchResult): string {\n switch (r.type) {\n case 'code':\n return `code:${r.filePath}:${r.metadata.startLine}-${r.metadata.endLine}`;\n case 'commit':\n return `commit:${r.metadata.hash || r.metadata.shortHash}`;\n case 'pattern':\n return `pattern:${r.metadata.taskType}:${r.content?.slice(0, 60)}`;\n case 'document':\n return `document:${r.filePath ?? ''}:${(r.metadata as any).collection ?? ''}:${(r.metadata as any).seq ?? ''}:${r.content?.slice(0, 80)}`;\n case 'collection':\n return `collection:${(r.metadata as any).id ?? r.content?.slice(0, 80)}`;\n }\n}\n","/**\n * BrainBank — FTS Utilities\n * \n * Shared helpers for SQLite FTS5 query sanitization.\n */\n\n/**\n * Sanitize a user query for FTS5 syntax.\n * Strips operators that would cause parse errors
|
|
1
|
+
{"version":3,"sources":["../src/search/rrf.ts","../src/search/keyword/utils.ts"],"sourcesContent":["/**\n * BrainBank — Reciprocal Rank Fusion (RRF)\n * \n * Combines results from multiple search systems (vector + BM25)\n * using the RRF algorithm: score = Σ 1/(k + rank_i)\n * \n * This is the same algorithm used by Elasticsearch, QMD, and most\n * production hybrid search systems. Simple but very effective.\n * \n * Reference: Cormack et al., \"Reciprocal Rank Fusion outperforms\n * Condorcet and individual Rank Learning Methods\" (2009)\n */\n\nimport type { SearchResult } from '../types.ts';\n\n/**\n * Fuse ranked lists from different search systems into a single ranked list.\n * \n * @param resultSets - Arrays of SearchResult from different systems (e.g. vector, BM25)\n * @param k - Smoothing constant. Default: 60 (standard value). Higher = less emphasis on top ranks.\n * @param maxResults - Maximum results to return.\n */\nexport function reciprocalRankFusion(\n resultSets: SearchResult[][],\n k: number = 60,\n maxResults: number = 15,\n): SearchResult[] {\n // Build a map: unique key → { bestResult, rrfScore }\n const fused = new Map<string, { result: SearchResult; rrfScore: number }>();\n\n for (const results of resultSets) {\n for (let rank = 0; rank < results.length; rank++) {\n const r = results[rank];\n const key = resultKey(r);\n const rrfContribution = 1.0 / (k + rank + 1);\n\n const existing = fused.get(key);\n if (existing) {\n existing.rrfScore += rrfContribution;\n // Keep the result with the higher original score\n if (r.score > existing.result.score) {\n existing.result = { ...r };\n }\n } else {\n fused.set(key, {\n result: { ...r },\n rrfScore: rrfContribution,\n });\n }\n }\n }\n\n // Sort by RRF score descending, normalize, and return\n const sorted = Array.from(fused.values())\n .sort((a, b) => b.rrfScore - a.rrfScore)\n .slice(0, maxResults);\n\n // Normalize RRF scores to 0..1 range\n const maxRRF = sorted[0]?.rrfScore ?? 1;\n return sorted.map(entry => ({\n ...entry.result,\n score: entry.rrfScore / maxRRF,\n metadata: {\n ...entry.result.metadata,\n rrfScore: entry.rrfScore,\n } as any,\n }));\n}\n\n/**\n * Generate a unique key for a search result to detect duplicates across systems.\n */\nfunction resultKey(r: SearchResult): string {\n switch (r.type) {\n case 'code':\n return `code:${r.filePath}:${r.metadata.startLine}-${r.metadata.endLine}`;\n case 'commit':\n return `commit:${r.metadata.hash || r.metadata.shortHash}`;\n case 'pattern':\n return `pattern:${r.metadata.taskType}:${r.content?.slice(0, 60)}`;\n case 'document':\n return `document:${r.filePath ?? ''}:${(r.metadata as any).collection ?? ''}:${(r.metadata as any).seq ?? ''}:${r.content?.slice(0, 80)}`;\n case 'collection':\n return `collection:${(r.metadata as any).id ?? r.content?.slice(0, 80)}`;\n }\n}\n","/**\n * BrainBank — FTS Utilities\n * \n * Shared helpers for SQLite FTS5 query sanitization.\n */\n\n/**\n * Split camelCase, PascalCase, and snake_case into individual words.\n * \"MagicLinkCallback\" → \"Magic Link Callback\"\n * \"tenant_worker\" → \"tenant worker\"\n */\nfunction splitCompound(word: string): string {\n return word\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase → camel Case\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // HTMLParser → HTML Parser\n .replace(/[_\\-./\\\\]/g, ' ') // snake_case, kebab-case, paths\n .trim();\n}\n\n/**\n * Sanitize a user query for FTS5 syntax.\n * Strips operators that would cause parse errors, splits compound words,\n * and converts words to implicit AND with exact-match quoting.\n */\nexport function sanitizeFTS(query: string): string {\n const clean = query\n .replace(/[{}[\\]()^~*:]/g, ' ')\n .replace(/\\bAND\\b|\\bOR\\b|\\bNOT\\b|\\bNEAR\\b/gi, '')\n .trim();\n\n // Split compound words (camelCase, PascalCase, snake_case)\n const expanded = clean.split(/\\s+/)\n .map(w => splitCompound(w))\n .join(' ');\n\n const words = expanded.split(/\\s+/).filter(w => w.length > 1);\n if (words.length === 0) return '';\n\n return words.map(w => `\"${w}\"`).join(' ');\n}\n\n/**\n * Normalize BM25 score from SQLite (negative, lower = better)\n * to 0.0–1.0 (higher = better) for consistency with vector search.\n */\nexport function normalizeBM25(rawScore: number): number {\n const abs = Math.abs(rawScore);\n return 1.0 / (1.0 + Math.exp(-0.3 * (abs - 5)));\n}\n"],"mappings":";;;;;AAsBO,SAAS,qBACZ,YACA,IAAY,IACZ,aAAqB,IACP;AAEd,QAAM,QAAQ,oBAAI,IAAwD;AAE1E,aAAW,WAAW,YAAY;AAC9B,aAAS,OAAO,GAAG,OAAO,QAAQ,QAAQ,QAAQ;AAC9C,YAAM,IAAI,QAAQ,IAAI;AACtB,YAAM,MAAM,UAAU,CAAC;AACvB,YAAM,kBAAkB,KAAO,IAAI,OAAO;AAE1C,YAAM,WAAW,MAAM,IAAI,GAAG;AAC9B,UAAI,UAAU;AACV,iBAAS,YAAY;AAErB,YAAI,EAAE,QAAQ,SAAS,OAAO,OAAO;AACjC,mBAAS,SAAS,EAAE,GAAG,EAAE;AAAA,QAC7B;AAAA,MACJ,OAAO;AACH,cAAM,IAAI,KAAK;AAAA,UACX,QAAQ,EAAE,GAAG,EAAE;AAAA,UACf,UAAU;AAAA,QACd,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AAGA,QAAM,SAAS,MAAM,KAAK,MAAM,OAAO,CAAC,EACnC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,EACtC,MAAM,GAAG,UAAU;AAGxB,QAAM,SAAS,OAAO,CAAC,GAAG,YAAY;AACtC,SAAO,OAAO,IAAI,YAAU;AAAA,IACxB,GAAG,MAAM;AAAA,IACT,OAAO,MAAM,WAAW;AAAA,IACxB,UAAU;AAAA,MACN,GAAG,MAAM,OAAO;AAAA,MAChB,UAAU,MAAM;AAAA,IACpB;AAAA,EACJ,EAAE;AACN;AA7CgB;AAkDhB,SAAS,UAAU,GAAyB;AACxC,UAAQ,EAAE,MAAM;AAAA,IACZ,KAAK;AACD,aAAO,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS,SAAS,IAAI,EAAE,SAAS,OAAO;AAAA,IAC3E,KAAK;AACD,aAAO,UAAU,EAAE,SAAS,QAAQ,EAAE,SAAS,SAAS;AAAA,IAC5D,KAAK;AACD,aAAO,WAAW,EAAE,SAAS,QAAQ,IAAI,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC;AAAA,IACpE,KAAK;AACD,aAAO,YAAY,EAAE,YAAY,EAAE,IAAK,EAAE,SAAiB,cAAc,EAAE,IAAK,EAAE,SAAiB,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC;AAAA,IAC3I,KAAK;AACD,aAAO,cAAe,EAAE,SAAiB,MAAM,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC;AAAA,EAC9E;AACJ;AAbS;;;AC7DT,SAAS,cAAc,MAAsB;AACzC,SAAO,KACF,QAAQ,mBAAmB,OAAO,EAClC,QAAQ,yBAAyB,OAAO,EACxC,QAAQ,cAAc,GAAG,EACzB,KAAK;AACd;AANS;AAaF,SAAS,YAAY,OAAuB;AAC/C,QAAM,QAAQ,MACT,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,qCAAqC,EAAE,EAC/C,KAAK;AAGV,QAAM,WAAW,MAAM,MAAM,KAAK,EAC7B,IAAI,OAAK,cAAc,CAAC,CAAC,EACzB,KAAK,GAAG;AAEb,QAAM,QAAQ,SAAS,MAAM,KAAK,EAAE,OAAO,OAAK,EAAE,SAAS,CAAC;AAC5D,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,SAAO,MAAM,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG;AAC5C;AAfgB;AAqBT,SAAS,cAAc,UAA0B;AACpD,QAAM,MAAM,KAAK,IAAI,QAAQ;AAC7B,SAAO,KAAO,IAAM,KAAK,IAAI,QAAQ,MAAM,EAAE;AACjD;AAHgB;","names":[]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
normalizeBM25,
|
|
3
3
|
reciprocalRankFusion,
|
|
4
4
|
sanitizeFTS
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FEAMUZGJ.js";
|
|
6
6
|
import {
|
|
7
7
|
__name
|
|
8
8
|
} from "./chunk-7QVYU63E.js";
|
|
@@ -182,4 +182,4 @@ ${patterns.join(". ")}`;
|
|
|
182
182
|
export {
|
|
183
183
|
NoteStore
|
|
184
184
|
};
|
|
185
|
-
//# sourceMappingURL=chunk-
|
|
185
|
+
//# sourceMappingURL=chunk-OPQ3ZIPV.js.map
|
|
@@ -33,10 +33,6 @@ var BREAK_SCORES = [
|
|
|
33
33
|
var TARGET_CHARS = 3e3;
|
|
34
34
|
var WINDOW_CHARS = 600;
|
|
35
35
|
var MIN_CHUNK_CHARS = 200;
|
|
36
|
-
function escapeRegex(s) {
|
|
37
|
-
return s.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
38
|
-
}
|
|
39
|
-
__name(escapeRegex, "escapeRegex");
|
|
40
36
|
var DocsIndexer = class {
|
|
41
37
|
constructor(_db, _embedding, _hnsw, _vecCache) {
|
|
42
38
|
this._db = _db;
|
|
@@ -72,8 +68,8 @@ var DocsIndexer = class {
|
|
|
72
68
|
walkDir(path.join(dir, e.name), rel);
|
|
73
69
|
} else if (e.isFile()) {
|
|
74
70
|
const shouldIgnore = options.ignore?.some((ig) => {
|
|
75
|
-
const
|
|
76
|
-
return new RegExp(
|
|
71
|
+
const regex = ig.replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "{{STAR}}").replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\{\{GLOBSTAR\}\}/g, ".*").replace(/\{\{STAR\}\}/g, "[^/]*");
|
|
72
|
+
return new RegExp(regex).test(rel);
|
|
77
73
|
});
|
|
78
74
|
const ext = path.extname(e.name).slice(1);
|
|
79
75
|
if (!shouldIgnore && (!patternExt || ext === patternExt)) {
|
|
@@ -434,4 +430,4 @@ export {
|
|
|
434
430
|
DocsIndexer,
|
|
435
431
|
docs
|
|
436
432
|
};
|
|
437
|
-
//# sourceMappingURL=chunk-
|
|
433
|
+
//# sourceMappingURL=chunk-T2VXF5S5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/indexers/docs/docs-indexer.ts","../src/indexers/docs/docs-plugin.ts"],"sourcesContent":["/**\n * BrainBank — Document Indexer\n * \n * Indexes generic document collections (markdown, text, etc.)\n * with heading-aware smart chunking, inspired by qmd.\n * \n * const indexer = new DocsIndexer(db, embedding, hnsw, vecCache);\n * await indexer.indexCollection('notes', '/path/to/notes', '**\\/*.md');\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { createHash } from 'node:crypto';\n\nimport type { Database } from '../../db/database.ts';\nimport type { EmbeddingProvider, VectorIndex } from '../../types.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\n\n// ── Break Point Scoring (qmd-inspired) ──────────────\n\ninterface BreakPoint {\n pos: number; // character position\n score: number; // break quality (higher = better)\n}\n\nconst BREAK_SCORES: [RegExp, number][] = [\n [/^# /, 100], // H1\n [/^## /, 90], // H2\n [/^### /, 80], // H3\n [/^#### /, 70], // H4\n [/^##### /, 60], // H5\n [/^###### /, 50], // H6\n [/^```/, 80], // Code fence\n [/^---$/, 60], // Horizontal rule\n [/^\\*\\*\\*$/, 60], // Horizontal rule alt\n [/^$/, 20], // Blank line (paragraph break)\n [/^[-*+] /, 5], // List item\n];\n\n// ── Chunk Target ────────────────────────────────────\n\nconst TARGET_CHARS = 3000; // ~900 tokens\nconst WINDOW_CHARS = 600; // search window before cutoff\nconst MIN_CHUNK_CHARS = 200; // don't create tiny chunks\n\n/** Escape special regex characters so user-provided patterns behave as literals. */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n// ── DocsIndexer ──────────────────────────────────────\n\nexport class DocsIndexer {\n constructor(\n private _db: Database,\n private _embedding: EmbeddingProvider,\n private _hnsw: HNSWIndex,\n private _vecCache: Map<number, Float32Array>,\n ) {}\n\n /**\n * Index all documents in a collection.\n * Incremental — skips unchanged files (by content hash).\n */\n async indexCollection(\n collection: string,\n dirPath: string,\n pattern: string = '**/*.md',\n options: {\n ignore?: string[];\n onProgress?: (file: string, current: number, total: number) => void;\n } = {},\n ): Promise<{ indexed: number; skipped: number; chunks: number }> {\n // Resolve absolute path\n const absDir = path.resolve(dirPath);\n if (!fs.existsSync(absDir)) {\n throw new Error(`Collection path does not exist: ${absDir}`);\n }\n\n // Find files matching pattern\n const patternExt = pattern.match(/\\.([\\w]+)$/)?.[1];\n const files: string[] = [];\n\n const walkDir = (dir: string, base: string): void => {\n let entries: fs.Dirent[];\n try { entries = fs.readdirSync(dir, { withFileTypes: true }); }\n catch { return; }\n for (const e of entries) {\n const rel = base ? `${base}/${e.name}` : e.name;\n if (e.isDirectory()) {\n // Skip ignored directories (node_modules, .git, etc.)\n if (this._isIgnoredDocDir(e.name)) continue;\n walkDir(path.join(dir, e.name), rel);\n } else if (e.isFile()) {\n const shouldIgnore = options.ignore?.some(ig => {\n // Convert glob to regex: ** → .*, * → [^/]*, escape the rest\n const regex = ig\n .replace(/\\*\\*/g, '{{GLOBSTAR}}')\n .replace(/\\*/g, '{{STAR}}')\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\{\\{GLOBSTAR\\}\\}/g, '.*')\n .replace(/\\{\\{STAR\\}\\}/g, '[^/]*');\n return new RegExp(regex).test(rel);\n });\n const ext = path.extname(e.name).slice(1);\n if (!shouldIgnore && (!patternExt || ext === patternExt)) {\n files.push(rel);\n }\n }\n }\n };\n walkDir(absDir, '');\n\n let indexed = 0;\n let skipped = 0;\n let totalChunks = 0;\n\n for (let i = 0; i < files.length; i++) {\n const relPath = files[i];\n const absPath = path.join(absDir, relPath);\n\n options.onProgress?.(relPath, i + 1, files.length);\n\n // Read content and hash\n const content = fs.readFileSync(absPath, 'utf-8');\n const hash = createHash('sha256').update(content).digest('hex').slice(0, 16);\n\n // Check if already indexed with same hash AND vectors exist\n const existingChunks = this._db.prepare(\n `SELECT dc.id, dc.content_hash, dv.chunk_id AS has_vector\n FROM doc_chunks dc\n LEFT JOIN doc_vectors dv ON dv.chunk_id = dc.id\n WHERE dc.collection = ? AND dc.file_path = ?`\n ).all(collection, relPath) as any[];\n\n const allMatch = existingChunks.length > 0 &&\n existingChunks.every((c: any) => c.content_hash === hash && c.has_vector != null);\n\n if (allMatch) {\n skipped++;\n continue;\n }\n\n // Remove old chunks + their HNSW vectors\n for (const old of existingChunks) {\n this._hnsw.remove(old.id);\n this._vecCache.delete(old.id);\n }\n this._db.prepare(\n 'DELETE FROM doc_chunks WHERE collection = ? AND file_path = ?'\n ).run(collection, relPath);\n\n // Extract title and chunk\n const title = this._extractTitle(content, relPath);\n const chunks = this._smartChunk(content);\n\n // Insert chunks\n const insertChunk = this._db.prepare(`\n INSERT INTO doc_chunks (collection, file_path, title, content, seq, pos, content_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n const chunkIds: number[] = [];\n\n this._db.transaction(() => {\n for (let seq = 0; seq < chunks.length; seq++) {\n const chunk = chunks[seq];\n const result = insertChunk.run(\n collection, relPath, title, chunk.text, seq, chunk.pos, hash,\n );\n chunkIds.push(Number(result.lastInsertRowid));\n }\n });\n\n // Generate embeddings\n const texts = chunks.map(c => `title: ${title} | text: ${c.text}`);\n const embeddings = await this._embedding.embedBatch(texts);\n\n // Store vectors — DB transaction commits first, then HNSW is updated.\n const insertVec = this._db.prepare(\n 'INSERT OR REPLACE INTO doc_vectors (chunk_id, embedding) VALUES (?, ?)'\n );\n\n this._db.transaction(() => {\n for (let j = 0; j < chunkIds.length; j++) {\n const buf = Buffer.from(embeddings[j].buffer);\n insertVec.run(chunkIds[j], buf);\n }\n });\n\n // Reached only if the transaction committed successfully — no orphan risk.\n for (let j = 0; j < chunkIds.length; j++) {\n this._hnsw.add(embeddings[j], chunkIds[j]);\n this._vecCache.set(chunkIds[j], embeddings[j]);\n }\n\n indexed++;\n totalChunks += chunks.length;\n }\n\n return { indexed, skipped, chunks: totalChunks };\n }\n\n /**\n * Remove all indexed data for a collection.\n */\n removeCollection(collection: string): void {\n // Clean HNSW entries before deleting DB rows\n const chunks = this._db.prepare(\n 'SELECT id FROM doc_chunks WHERE collection = ?'\n ).all(collection) as any[];\n for (const chunk of chunks) {\n this._hnsw.remove(chunk.id);\n this._vecCache.delete(chunk.id);\n }\n\n this._db.prepare('DELETE FROM doc_chunks WHERE collection = ?').run(collection);\n this._db.prepare('DELETE FROM collections WHERE name = ?').run(collection);\n this._db.prepare('DELETE FROM path_contexts WHERE collection = ?').run(collection);\n }\n\n // ── Smart Chunking ──────────────────────────────\n\n /**\n * Split document into chunks at natural markdown boundaries.\n * Uses heading-aware scoring like qmd.\n */\n private _smartChunk(text: string): { text: string; pos: number }[] {\n if (text.length <= TARGET_CHARS) {\n return [{ text, pos: 0 }];\n }\n\n const lines = text.split('\\n');\n const breakPoints = this._findBreakPoints(lines);\n const chunks: { text: string; pos: number }[] = [];\n\n let chunkStart = 0; // char position\n\n while (chunkStart < text.length) {\n const remaining = text.length - chunkStart;\n if (remaining <= TARGET_CHARS + WINDOW_CHARS) {\n // Last chunk — take everything\n const lastText = text.slice(chunkStart).trim();\n if (lastText.length >= MIN_CHUNK_CHARS) {\n chunks.push({ text: lastText, pos: chunkStart });\n } else if (chunks.length > 0) {\n // Merge with previous chunk\n chunks[chunks.length - 1].text += '\\n' + lastText;\n } else {\n chunks.push({ text: lastText, pos: chunkStart });\n }\n break;\n }\n\n // Find best break point in window\n const targetEnd = chunkStart + TARGET_CHARS;\n const windowStart = targetEnd - WINDOW_CHARS;\n\n let bestBreak = targetEnd;\n let bestScore = 0;\n\n for (const bp of breakPoints) {\n if (bp.pos <= chunkStart) continue;\n if (bp.pos > targetEnd + WINDOW_CHARS / 2) break;\n if (bp.pos < windowStart) continue;\n\n // Score decay: prefer closer break points\n const distance = Math.abs(bp.pos - targetEnd);\n const decay = 1 - (distance / WINDOW_CHARS) ** 2 * 0.7;\n const finalScore = bp.score * decay;\n\n if (finalScore > bestScore) {\n bestScore = finalScore;\n bestBreak = bp.pos;\n }\n }\n\n const chunkText = text.slice(chunkStart, bestBreak).trim();\n if (chunkText.length >= MIN_CHUNK_CHARS) {\n chunks.push({ text: chunkText, pos: chunkStart });\n }\n\n chunkStart = bestBreak;\n }\n\n return chunks;\n }\n\n /**\n * Find all potential break points in the document with scores.\n */\n private _findBreakPoints(lines: string[]): BreakPoint[] {\n const points: BreakPoint[] = [];\n let charPos = 0;\n let inCodeBlock = false;\n\n for (const line of lines) {\n // Track code fences\n if (line.trimStart().startsWith('```')) {\n inCodeBlock = !inCodeBlock;\n if (!inCodeBlock) {\n // End of code block is a good break point\n points.push({ pos: charPos + line.length + 1, score: 80 });\n }\n charPos += line.length + 1;\n continue;\n }\n\n // Skip break points inside code blocks\n if (inCodeBlock) {\n charPos += line.length + 1;\n continue;\n }\n\n // Score this line as a potential break point\n for (const [pattern, score] of BREAK_SCORES) {\n if (pattern.test(line.trim())) {\n points.push({ pos: charPos, score });\n break;\n }\n }\n\n charPos += line.length + 1;\n }\n\n return points;\n }\n\n /**\n * Extract document title from first heading or filename.\n */\n private _extractTitle(content: string, filePath: string): string {\n const match = content.match(/^#{1,3}\\s+(.+)$/m);\n if (match) return match[1].trim();\n return path.basename(filePath, path.extname(filePath));\n }\n\n /** Skip well-known output/vendor directories when walking docs. */\n private _isIgnoredDocDir(name: string): boolean {\n const IGNORED = new Set([\n 'node_modules', '.git', '.hg', '.svn',\n 'dist', 'build', 'out', 'coverage', '.next',\n '__pycache__', '.tox', '.venv', 'venv',\n 'vendor', 'target', '.cache', '.turbo',\n ]);\n return IGNORED.has(name);\n }\n}\n","/**\n * BrainBank — Docs Module\n * \n * Index any folder of markdown/text files (notes, docs, wikis).\n * Heading-aware smart chunking inspired by qmd.\n * \n * import { docs } from 'brainbank/docs';\n * brain.use(docs());\n */\n\nimport type { Indexer, IndexerContext } from '../base.ts';\nimport type { HNSWIndex } from '../../providers/vector/hnsw.ts';\nimport type { Database } from '../../db/database.ts';\nimport type { EmbeddingProvider, DocumentCollection, SearchResult } from '../../types.ts';\nimport { DocsIndexer } from './docs-indexer.ts';\n\nclass DocsPlugin implements Indexer {\n readonly name = 'docs';\n hnsw!: HNSWIndex;\n indexer!: DocsIndexer;\n vecCache = new Map<number, Float32Array>();\n private _db!: Database;\n private _embedding!: EmbeddingProvider;\n\n async initialize(ctx: IndexerContext): Promise<void> {\n this._db = ctx.db;\n this._embedding = ctx.embedding;\n this.hnsw = await ctx.createHnsw();\n ctx.loadVectors('doc_vectors', 'chunk_id', this.hnsw, this.vecCache);\n this.indexer = new DocsIndexer(ctx.db, ctx.embedding, this.hnsw, this.vecCache);\n }\n\n /** Register a document collection. */\n addCollection(collection: DocumentCollection): void {\n this._db.prepare(`\n INSERT OR REPLACE INTO collections (name, path, pattern, ignore_json, context)\n VALUES (?, ?, ?, ?, ?)\n `).run(\n collection.name,\n collection.path,\n collection.pattern ?? '**/*.md',\n JSON.stringify(collection.ignore ?? []),\n collection.context ?? null,\n );\n }\n\n /** Remove a collection and its indexed data. */\n removeCollection(name: string): void {\n this.indexer.removeCollection(name);\n }\n\n /** List all registered collections. */\n listCollections(): DocumentCollection[] {\n return (this._db.prepare('SELECT * FROM collections').all() as any[]).map(row => ({\n name: row.name,\n path: row.path,\n pattern: row.pattern,\n ignore: JSON.parse(row.ignore_json),\n context: row.context,\n }));\n }\n\n /** Index all (or specific) collections. Incremental. */\n async indexCollections(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 const allCollections = this.listCollections();\n const toIndex = options.collections\n ? allCollections.filter(c => options.collections!.includes(c.name))\n : allCollections;\n\n const results: Record<string, { indexed: number; skipped: number; chunks: number }> = {};\n\n for (const coll of toIndex) {\n results[coll.name] = await this.indexer.indexCollection(\n coll.name,\n coll.path,\n coll.pattern,\n {\n ignore: coll.ignore,\n onProgress: (file, cur, total) => options.onProgress?.(coll.name, file, cur, total),\n },\n );\n }\n\n return results;\n }\n\n /** Search documents only. */\n async search(query: string, options?: {\n collection?: string;\n k?: number;\n minScore?: number;\n }): Promise<SearchResult[]> {\n const k = options?.k ?? 8;\n const queryVec = await this._embedding.embed(query);\n\n // Over-fetch from shared HNSW when filtering by collection\n // (same pattern as collection.ts ratio scaling)\n let searchK = k;\n if (options?.collection && this.hnsw.size > 0) {\n const collectionCount = (this._db.prepare(\n 'SELECT COUNT(*) as c FROM doc_chunks WHERE collection = ?'\n ).get(options.collection) as any)?.c ?? 0;\n const totalChunks = (this._db.prepare(\n 'SELECT COUNT(*) as c FROM doc_chunks'\n ).get() as any)?.c ?? 1;\n const ratio = collectionCount > 0\n ? Math.max(3, Math.min(50, Math.ceil(totalChunks / collectionCount)))\n : 3;\n searchK = Math.min(k * ratio, this.hnsw.size);\n }\n\n const hits = this.hnsw.search(queryVec, searchK);\n\n const results: SearchResult[] = [];\n for (const hit of hits) {\n if (options?.minScore && hit.score < options.minScore) continue;\n\n const chunk = this._db.prepare(\n 'SELECT * FROM doc_chunks WHERE id = ?'\n ).get(hit.id) as any;\n\n if (!chunk) continue;\n if (options?.collection && chunk.collection !== options.collection) continue;\n\n const ctx = this._getDocContext(chunk.collection, chunk.file_path);\n\n results.push({\n type: 'document',\n score: hit.score,\n filePath: chunk.file_path,\n content: chunk.content,\n context: ctx,\n metadata: {\n collection: chunk.collection,\n title: chunk.title,\n seq: chunk.seq,\n },\n });\n\n // Stop once we have enough results\n if (results.length >= k) break;\n }\n\n return results;\n }\n\n /** Add context description for a document path. */\n addContext(collection: string, path: string, context: string): void {\n this._db.prepare(`\n INSERT OR REPLACE INTO path_contexts (collection, path, context)\n VALUES (?, ?, ?)\n `).run(collection, path, context);\n }\n\n /** Remove context for a path. */\n removeContext(collection: string, path: string): void {\n this._db.prepare(\n 'DELETE FROM path_contexts WHERE collection = ? AND path = ?'\n ).run(collection, path);\n }\n\n /** List all context entries. */\n listContexts(): { collection: string; path: string; context: string }[] {\n return this._db.prepare('SELECT * FROM path_contexts').all() as any[];\n }\n\n stats(): Record<string, any> {\n return {\n collections: (this._db.prepare('SELECT COUNT(*) as c FROM collections').get() as any).c,\n documents: (this._db.prepare('SELECT COUNT(DISTINCT file_path) as c FROM doc_chunks').get() as any).c,\n chunks: (this._db.prepare('SELECT COUNT(*) as c FROM doc_chunks').get() as any).c,\n hnswSize: this.hnsw.size,\n };\n }\n\n /** Resolve context for a document (checks path_contexts tree → collection context). */\n private _getDocContext(collection: string, filePath: string): string | undefined {\n const parts = filePath.split('/');\n for (let i = parts.length; i >= 0; i--) {\n const checkPath = i === 0 ? '/' : '/' + parts.slice(0, i).join('/');\n const ctx = this._db.prepare(\n 'SELECT context FROM path_contexts WHERE collection = ? AND path = ?'\n ).get(collection, checkPath) as any;\n if (ctx) return ctx.context;\n }\n\n const coll = this._db.prepare(\n 'SELECT context FROM collections WHERE name = ?'\n ).get(collection) as any;\n return coll?.context ?? undefined;\n }\n}\n\n/** Create a document collections plugin. */\nexport function docs(): Indexer {\n return new DocsPlugin();\n}\n"],"mappings":"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","names":["path"]}
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
isIgnoredDir,
|
|
3
3
|
isIgnoredFile,
|
|
4
4
|
isSupported
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4DM3XWO6.js";
|
|
6
6
|
import {
|
|
7
7
|
normalizeBM25,
|
|
8
8
|
reciprocalRankFusion,
|
|
9
9
|
sanitizeFTS
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FEAMUZGJ.js";
|
|
11
11
|
import {
|
|
12
12
|
cosineSimilarity
|
|
13
13
|
} from "./chunk-QNHBCOKB.js";
|
|
@@ -652,6 +652,7 @@ var BM25Search = class {
|
|
|
652
652
|
const results = [];
|
|
653
653
|
const ftsQuery = sanitizeFTS(query);
|
|
654
654
|
if (!ftsQuery) return [];
|
|
655
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
655
656
|
if (codeK > 0) {
|
|
656
657
|
try {
|
|
657
658
|
const rows = this._db.prepare(`
|
|
@@ -664,6 +665,7 @@ var BM25Search = class {
|
|
|
664
665
|
LIMIT ?
|
|
665
666
|
`).all(ftsQuery, codeK);
|
|
666
667
|
for (const r of rows) {
|
|
668
|
+
seenIds.add(r.id);
|
|
667
669
|
results.push({
|
|
668
670
|
type: "code",
|
|
669
671
|
score: normalizeBM25(r.score),
|
|
@@ -681,6 +683,36 @@ var BM25Search = class {
|
|
|
681
683
|
}
|
|
682
684
|
} catch {
|
|
683
685
|
}
|
|
686
|
+
try {
|
|
687
|
+
const words = query.replace(/[^a-zA-Z0-9]/g, " ").split(/\s+/).filter((w) => w.length > 2);
|
|
688
|
+
for (const word of words.slice(0, 3)) {
|
|
689
|
+
const pathRows = this._db.prepare(`
|
|
690
|
+
SELECT id, file_path, chunk_type, name, start_line, end_line, content, language
|
|
691
|
+
FROM code_chunks
|
|
692
|
+
WHERE file_path LIKE ? AND chunk_type = 'file'
|
|
693
|
+
LIMIT 3
|
|
694
|
+
`).all(`%${word}%`);
|
|
695
|
+
for (const r of pathRows) {
|
|
696
|
+
if (seenIds.has(r.id)) continue;
|
|
697
|
+
seenIds.add(r.id);
|
|
698
|
+
results.push({
|
|
699
|
+
type: "code",
|
|
700
|
+
score: 0.6,
|
|
701
|
+
filePath: r.file_path,
|
|
702
|
+
content: r.content,
|
|
703
|
+
metadata: {
|
|
704
|
+
chunkType: r.chunk_type,
|
|
705
|
+
name: r.name,
|
|
706
|
+
startLine: r.start_line,
|
|
707
|
+
endLine: r.end_line,
|
|
708
|
+
language: r.language,
|
|
709
|
+
searchType: "bm25-path"
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
} catch {
|
|
715
|
+
}
|
|
684
716
|
}
|
|
685
717
|
if (gitK > 0) {
|
|
686
718
|
try {
|
|
@@ -2287,4 +2319,4 @@ export {
|
|
|
2287
2319
|
ContextBuilder,
|
|
2288
2320
|
BrainBank
|
|
2289
2321
|
};
|
|
2290
|
-
//# sourceMappingURL=chunk-
|
|
2322
|
+
//# sourceMappingURL=chunk-ZJ5LLMGM.js.map
|