brainbank 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +76 -1398
  2. package/bin/brainbank +5 -1
  3. package/dist/{chunk-N2OJRXSB.js → chunk-3HVCONGF.js} +1 -1
  4. package/dist/{chunk-N2OJRXSB.js.map → chunk-3HVCONGF.js.map} +1 -1
  5. package/dist/{chunk-CCXVL56V.js → chunk-3JZIM5AU.js} +6 -3
  6. package/dist/chunk-3JZIM5AU.js.map +1 -0
  7. package/dist/{chunk-6XOXM7MI.js → chunk-5KU2PP34.js} +2 -2
  8. package/dist/{chunk-6XOXM7MI.js.map → chunk-5KU2PP34.js.map} +1 -1
  9. package/dist/chunk-7JDCHUJV.js +89 -0
  10. package/dist/chunk-7JDCHUJV.js.map +1 -0
  11. package/dist/{chunk-B77KABWH.js → chunk-7T2ZCZQA.js} +17 -15
  12. package/dist/chunk-7T2ZCZQA.js.map +1 -0
  13. package/dist/chunk-E3J37GDA.js +74 -0
  14. package/dist/chunk-E3J37GDA.js.map +1 -0
  15. package/dist/chunk-JEFWMS5Z.js +217 -0
  16. package/dist/chunk-JEFWMS5Z.js.map +1 -0
  17. package/dist/chunk-JRVYSTMP.js +3256 -0
  18. package/dist/chunk-JRVYSTMP.js.map +1 -0
  19. package/dist/chunk-OLDHLOMT.js +69 -0
  20. package/dist/chunk-OLDHLOMT.js.map +1 -0
  21. package/dist/{chunk-424UFCY7.js → chunk-QTZNB6AK.js} +6 -2
  22. package/dist/chunk-QTZNB6AK.js.map +1 -0
  23. package/dist/chunk-RFF7HMP6.js +109 -0
  24. package/dist/chunk-RFF7HMP6.js.map +1 -0
  25. package/dist/{chunk-ZNLN2VWV.js → chunk-TD3TEFI3.js} +1 -1
  26. package/dist/chunk-TD3TEFI3.js.map +1 -0
  27. package/dist/cli.js +1036 -341
  28. package/dist/cli.js.map +1 -1
  29. package/dist/haiku-expander-WOVJIVXD.js +8 -0
  30. package/dist/haiku-pruner-DB77ZQLJ.js +8 -0
  31. package/dist/http-server-GIRELCCL.js +9 -0
  32. package/dist/index.d.ts +1774 -611
  33. package/dist/index.js +282 -70
  34. package/dist/index.js.map +1 -1
  35. package/dist/{local-embedding-ZIMTK6PU.js → local-embedding-2RNCC5EU.js} +2 -2
  36. package/dist/{openai-embedding-VQZCZQYT.js → openai-embedding-Z5I4K4CN.js} +2 -2
  37. package/dist/perplexity-context-embedding-V5YUMXDR.js +9 -0
  38. package/dist/{perplexity-embedding-227WQY4R.js → perplexity-embedding-X2S72OAC.js} +2 -2
  39. package/dist/plugin-FF4Q34TI.js +32 -0
  40. package/dist/{qwen3-reranker-3MHEENT5.js → qwen3-reranker-HVIQOLKS.js} +2 -2
  41. package/dist/{resolve-CUJWY6HP.js → resolve-Q5D6HECY.js} +2 -2
  42. package/package.json +25 -52
  43. package/src/brainbank.ts +620 -0
  44. package/src/cli/commands/collection.ts +77 -0
  45. package/src/cli/commands/context.ts +171 -0
  46. package/src/cli/commands/daemon.ts +100 -0
  47. package/src/cli/commands/docs.ts +71 -0
  48. package/src/cli/commands/files.ts +69 -0
  49. package/src/cli/commands/help.ts +72 -0
  50. package/src/cli/commands/index.ts +282 -0
  51. package/src/cli/commands/kv.ts +140 -0
  52. package/src/cli/commands/mcp.ts +13 -0
  53. package/src/cli/commands/reembed.ts +30 -0
  54. package/src/cli/commands/scan.ts +365 -0
  55. package/src/cli/commands/search.ts +130 -0
  56. package/src/cli/commands/stats.ts +44 -0
  57. package/src/cli/commands/status.ts +47 -0
  58. package/src/cli/commands/watch.ts +43 -0
  59. package/src/cli/factory/brain-context.ts +43 -0
  60. package/src/cli/factory/builtin-registration.ts +123 -0
  61. package/src/cli/factory/config-loader.ts +72 -0
  62. package/src/cli/factory/index.ts +65 -0
  63. package/src/cli/factory/plugin-loader.ts +146 -0
  64. package/src/cli/index.ts +63 -0
  65. package/src/cli/server-client.ts +135 -0
  66. package/src/cli/utils.ts +121 -0
  67. package/src/config.ts +50 -0
  68. package/src/constants.ts +13 -0
  69. package/src/db/adapter.ts +112 -0
  70. package/src/db/metadata.ts +130 -0
  71. package/src/db/migrations.ts +66 -0
  72. package/src/db/sqlite-adapter.ts +208 -0
  73. package/src/db/tracker.ts +91 -0
  74. package/src/engine/index-api.ts +85 -0
  75. package/src/engine/reembed.ts +206 -0
  76. package/src/engine/search-api.ts +222 -0
  77. package/src/index.ts +159 -0
  78. package/src/lib/fts.ts +57 -0
  79. package/src/lib/languages.ts +180 -0
  80. package/src/lib/logger.ts +125 -0
  81. package/src/lib/math.ts +87 -0
  82. package/src/lib/provider-key.ts +20 -0
  83. package/src/lib/prune.ts +71 -0
  84. package/src/lib/rerank.ts +33 -0
  85. package/src/lib/rrf.ts +133 -0
  86. package/src/lib/write-lock.ts +108 -0
  87. package/src/plugin.ts +323 -0
  88. package/src/providers/embeddings/embedding-worker-thread.ts +95 -0
  89. package/src/providers/embeddings/embedding-worker.ts +141 -0
  90. package/src/providers/embeddings/local-embedding.ts +115 -0
  91. package/src/providers/embeddings/openai-embedding.ts +167 -0
  92. package/src/providers/embeddings/perplexity-context-embedding.ts +195 -0
  93. package/src/providers/embeddings/perplexity-embedding.ts +165 -0
  94. package/src/providers/embeddings/resolve.ts +34 -0
  95. package/src/providers/pruners/haiku-expander.ts +152 -0
  96. package/src/providers/pruners/haiku-pruner.ts +112 -0
  97. package/src/providers/rerankers/qwen3-reranker.ts +180 -0
  98. package/src/providers/vector/hnsw-index.ts +174 -0
  99. package/src/providers/vector/hnsw-loader.ts +129 -0
  100. package/src/search/bm25-boost.ts +61 -0
  101. package/src/search/context-builder.ts +298 -0
  102. package/src/search/keyword/composite-bm25-search.ts +62 -0
  103. package/src/search/types.ts +35 -0
  104. package/src/search/vector/composite-vector-search.ts +76 -0
  105. package/src/search/vector/mmr.ts +64 -0
  106. package/src/services/collection.ts +405 -0
  107. package/src/services/daemon.ts +87 -0
  108. package/src/services/http-server.ts +288 -0
  109. package/src/services/kv-service.ts +65 -0
  110. package/src/services/plugin-registry.ts +109 -0
  111. package/src/services/watch.ts +348 -0
  112. package/src/services/webhook-server.ts +100 -0
  113. package/src/types.ts +504 -0
  114. package/dist/base-3SNc_CeY.d.ts +0 -593
  115. package/dist/chunk-424UFCY7.js.map +0 -1
  116. package/dist/chunk-7EZR47JV.js +0 -232
  117. package/dist/chunk-7EZR47JV.js.map +0 -1
  118. package/dist/chunk-B77KABWH.js.map +0 -1
  119. package/dist/chunk-CCXVL56V.js.map +0 -1
  120. package/dist/chunk-DI3H6JVZ.js +0 -2432
  121. package/dist/chunk-DI3H6JVZ.js.map +0 -1
  122. package/dist/chunk-FGL32LUJ.js +0 -754
  123. package/dist/chunk-FGL32LUJ.js.map +0 -1
  124. package/dist/chunk-JRSKWF6K.js +0 -313
  125. package/dist/chunk-JRSKWF6K.js.map +0 -1
  126. package/dist/chunk-U2Q2XGPZ.js +0 -42
  127. package/dist/chunk-U2Q2XGPZ.js.map +0 -1
  128. package/dist/chunk-VQ27YUHH.js +0 -629
  129. package/dist/chunk-VQ27YUHH.js.map +0 -1
  130. package/dist/chunk-VVXYZIIB.js +0 -304
  131. package/dist/chunk-VVXYZIIB.js.map +0 -1
  132. package/dist/chunk-YOLKSYWK.js +0 -79
  133. package/dist/chunk-YOLKSYWK.js.map +0 -1
  134. package/dist/chunk-ZNLN2VWV.js.map +0 -1
  135. package/dist/code.d.ts +0 -33
  136. package/dist/code.js +0 -9
  137. package/dist/docs.d.ts +0 -21
  138. package/dist/docs.js +0 -9
  139. package/dist/git.d.ts +0 -33
  140. package/dist/git.js +0 -9
  141. package/dist/memory.d.ts +0 -17
  142. package/dist/memory.js +0 -9
  143. package/dist/notes.d.ts +0 -17
  144. package/dist/notes.js +0 -10
  145. package/dist/perplexity-context-embedding-KSVSZXMD.js +0 -9
  146. package/dist/resolve-CUJWY6HP.js.map +0 -1
  147. /package/dist/{code.js.map → haiku-expander-WOVJIVXD.js.map} +0 -0
  148. /package/dist/{docs.js.map → haiku-pruner-DB77ZQLJ.js.map} +0 -0
  149. /package/dist/{git.js.map → http-server-GIRELCCL.js.map} +0 -0
  150. /package/dist/{local-embedding-ZIMTK6PU.js.map → local-embedding-2RNCC5EU.js.map} +0 -0
  151. /package/dist/{memory.js.map → openai-embedding-Z5I4K4CN.js.map} +0 -0
  152. /package/dist/{notes.js.map → perplexity-context-embedding-V5YUMXDR.js.map} +0 -0
  153. /package/dist/{openai-embedding-VQZCZQYT.js.map → perplexity-embedding-X2S72OAC.js.map} +0 -0
  154. /package/dist/{perplexity-context-embedding-KSVSZXMD.js.map → plugin-FF4Q34TI.js.map} +0 -0
  155. /package/dist/{perplexity-embedding-227WQY4R.js.map → qwen3-reranker-HVIQOLKS.js.map} +0 -0
  156. /package/dist/{qwen3-reranker-3MHEENT5.js.map → resolve-Q5D6HECY.js.map} +0 -0
package/README.md CHANGED
@@ -4,1465 +4,143 @@
4
4
 
5
5
  BrainBank gives LLMs a long-term memory that persists between sessions.
6
6
 
7
- - **All-in-one** — core + code + git + docs + CLI in a single `brainbank` package
8
- - **Pluggable plugins** — `.use()` only what you need (code, git, docs, or custom)
9
- - **Dynamic collections** — `brain.collection('errors')` for any structured data
7
+ - **Pluggable** — `.use()` only what you need: [code](#packages), [git](#packages), [docs](#packages), or [custom](docs/custom-plugins.md)
10
8
  - **Hybrid search** — vector + BM25 fused with Reciprocal Rank Fusion
11
- - **Pluggable embeddings** — local WASM (free), OpenAI, or Perplexity (standard & contextualized)
12
- - **Multi-repo** — index multiple repositories into one shared database
13
- - **Portable** — single `.brainbank/brainbank.db` file
14
- - **Optional packages** — [`@brainbank/memory`](#memory) (fact extraction + entity graph), [`@brainbank/mcp`](#mcp-server) (MCP server)
15
- - **Optional reranker** — Qwen3-0.6B cross-encoder via `Qwen3Reranker` (opt-in)
16
-
17
- ![BrainBank Architecture](assets/architecture.png)
9
+ - **Dynamic collections** — `brain.collection('errors')` for any structured data
10
+ - **Pluggable embeddings** — local WASM (free), OpenAI, or Perplexity
11
+ - **Multi-process safe** — concurrent CLI, MCP, and watch processes with automatic hot-reload
12
+ - **Portable** — single `.brainbank/brainbank.db` SQLite file
13
+ - **Modular** — lightweight core + optional [`@brainbank/*`](#packages) packages
18
14
 
19
15
  ---
20
16
 
21
- ## Why BrainBank?
22
-
23
- BrainBank is a **code-aware knowledge engine** — not just a memory layer. It parses your codebase with tree-sitter ASTs, indexes git history and co-edit patterns, and makes everything searchable with hybrid vector + keyword retrieval. Optional packages add conversational memory (`@brainbank/memory`) and MCP integration (`@brainbank/mcp`).
24
-
25
- | | **BrainBank** | **QMD** | **mem0 / Zep** | **LangChain** |
26
- |---|:---:|:---:|:---:|:---:|
27
- | Code-aware (AST) | **19 languages** (tree-sitter) | ✗ | ✗ | ✗ |
28
- | Git + co-edits | ✓ | ✗ | ✗ | ✗ |
29
- | Search | **Vector + BM25 + RRF** | Vector + reranker | Vector + graph | Vector only |
30
- | Infra | **SQLite file** | Local GGUF | Vector DB + cloud | Vector DB |
31
- | Plugins | **`.use()` builder** | ✗ | ✗ | ✗ |
32
- | Memory | **`@brainbank/memory`** (opt-in) | ✗ | **Core feature** | ✗ |
33
-
34
- ### Table of Contents
35
-
36
- - [Why BrainBank?](#why-brainbank)
37
- - [Installation](#installation)
38
- - [Quick Start](#quick-start)
39
- - [CLI](#cli)
40
- - [Programmatic API](#programmatic-api)
41
- - [Plugins](#plugins)
42
- - [Collections](#collections)
43
- - [Search](#search)
44
- - [Document Collections](#document-collections)
45
- - [Context Generation](#context-generation)
46
- - [Custom Plugins](#custom-plugins)
47
- - [AI Agent Integration](#ai-agent-integration)
48
- - [Examples](#examples)
49
- - [Watch Mode](#watch-mode)
50
- - [MCP Server](#mcp-server)
51
- - [Project Config](#project-config)
52
- - [Configuration](#configuration)
53
- - [Embedding Providers](#embedding-providers)
54
- - [Reranker](#reranker)
55
- - [Memory](#memory)
56
- - [Multi-Repository Indexing](#multi-repository-indexing)
57
- - [Indexing](#indexing-1)
58
- - [Incremental Indexing](#incremental-indexing)
59
- - [Re-embedding](#re-embedding)
60
- - [Architecture](#architecture)
61
- - [Search Pipeline](#search-pipeline)
62
- - [Benchmarks](#benchmarks)
63
- - [Search Quality: AST vs Sliding Window](#search-quality-ast-vs-sliding-window)
64
- - [Grammar Support](#grammar-support)
65
- - [RAG Retrieval Quality](#rag-retrieval-quality)
66
- · [Full Results →](./BENCHMARKS.md)
17
+ <img src="assets/architecture.png" alt="BrainBank Architecture" width="600">
67
18
 
68
19
  ---
69
20
 
70
- ## Installation
71
-
72
- ```bash
73
- npm install brainbank
74
- ```
75
-
76
- ### Optional Packages
77
-
78
- | Package | When to install |
79
- |---------|----------------|
80
- | `@brainbank/memory` | Deterministic memory extraction + entity graph for LLM conversations |
81
- | `@brainbank/mcp` | MCP server for AI tool integration |
21
+ ## Quick Start
82
22
 
83
23
  ```bash
84
- # Memory automatic fact extraction & dedup for chatbots/agents
85
- npm install @brainbank/memory
86
-
87
- # Reranker — built-in, install the runtime dependency to enable
88
- npm install node-llama-cpp
89
-
90
- # MCP server — for Antigravity, Claude Desktop, etc.
91
- npm install @brainbank/mcp
24
+ npm i -g brainbank @brainbank/code @brainbank/git @brainbank/docs
92
25
  ```
93
26
 
94
- ### Tree-Sitter Grammars
27
+ > If you get `ERESOLVE` errors, use `npm i --legacy-peer-deps` — tree-sitter grammars have overlapping peer dep ranges.
95
28
 
96
- BrainBank uses [tree-sitter](https://tree-sitter.github.io/) for AST-aware code chunking. **JavaScript, TypeScript, and Python grammars are included by default.** Other languages require installing the corresponding grammar package:
29
+ ### CLI zero code
97
30
 
98
31
  ```bash
99
- # Install only the grammars you need
100
- npm install tree-sitter-go tree-sitter-rust tree-sitter-ruby
101
- ```
102
-
103
- If you index a file whose grammar isn't installed, BrainBank will throw a clear error:
104
-
105
- ```
106
- BrainBank: Grammar 'tree-sitter-go' is not installed. Run: npm install tree-sitter-go
32
+ brainbank index . # scans repo interactive select → index
33
+ brainbank index . --yes # skip prompts, auto-select all
34
+ brainbank hsearch "rate limiting" # hybrid search
35
+ brainbank kv add decisions "Use Redis..." # store a memory
36
+ brainbank kv search decisions "caching" # recall it
107
37
  ```
108
38
 
109
- <details>
110
- <summary>All available grammars (19 languages)</summary>
111
-
112
- | Category | Packages |
113
- |----------|----------|
114
- | **Included** | `tree-sitter-javascript`, `tree-sitter-typescript`, `tree-sitter-python` |
115
- | Web | `tree-sitter-html`, `tree-sitter-css` |
116
- | Systems | `tree-sitter-go`, `tree-sitter-rust`, `tree-sitter-c`, `tree-sitter-cpp`, `tree-sitter-swift` |
117
- | JVM | `tree-sitter-java`, `tree-sitter-kotlin`, `tree-sitter-scala` |
118
- | Scripting | `tree-sitter-ruby`, `tree-sitter-php`, `tree-sitter-lua`, `tree-sitter-bash`, `tree-sitter-elixir` |
119
- | .NET | `tree-sitter-c-sharp` |
120
-
121
- </details>
122
-
123
- ---
124
-
125
- ## Quick Start
126
-
127
- Get semantic search over your codebase in under a minute:
39
+ ### Programmatic API
128
40
 
129
41
  ```typescript
130
42
  import { BrainBank } from 'brainbank';
131
- import { code } from 'brainbank/code';
132
- import { git } from 'brainbank/git';
43
+ import { code } from '@brainbank/code';
44
+ import { git } from '@brainbank/git';
133
45
 
134
46
  const brain = new BrainBank({ repoPath: '.' })
135
47
  .use(code())
136
48
  .use(git());
137
49
 
138
- await brain.index(); // indexes code + git history (incremental)
50
+ await brain.index();
139
51
 
140
- // Search across everything
141
52
  const results = await brain.hybridSearch('authentication middleware');
142
- console.log(results.map(r => `${r.filePath}:L${r.metadata?.startLine} (${r.score.toFixed(2)})`));
143
53
 
144
- // Store agent memory
145
54
  const log = brain.collection('decisions');
146
- await log.add(
147
- 'Switched from bcrypt to argon2id for password hashing. ' +
148
- 'Argon2id is memory-hard and recommended by OWASP for new projects. ' +
149
- 'Updated src/auth/hash.ts and all tests.',
150
- { tags: ['security', 'auth'] }
151
- );
152
-
153
- // Recall later: "what did we decide about password hashing?"
154
- const hits = await log.search('password hashing decision');
155
-
156
- brain.close();
157
- ```
158
-
159
- Or use the CLI — zero code:
160
-
161
- ```bash
162
- npm install -g brainbank
163
- brainbank index . # index code + git
164
- brainbank hsearch "rate limiting" # hybrid search
165
- brainbank kv add decisions "Use Redis..." # store a memory
166
- brainbank kv search decisions "caching" # recall it
167
- ```
168
-
169
- ## CLI
170
-
171
- BrainBank can be used entirely from the command line — no config file needed.
172
-
173
- ### Indexing
174
-
175
- `index` processes **code files + git history** by default. Use `--only` to select specific modules, and `--docs` to include document collections.
176
-
177
- ```bash
178
- brainbank index [path] # Index code + git history
179
- brainbank index [path] --force # Force re-index everything
180
- brainbank index [path] --depth 200 # Limit git commit depth
181
- brainbank index [path] --only code # Index only code (skip git)
182
- brainbank index [path] --only git # Index only git history
183
- brainbank index [path] --docs ~/docs # Include a docs folder
184
- brainbank docs [--collection <name>] # Index document collections
185
- ```
186
-
187
- > **Multi-repo:** If `[path]` contains multiple Git subdirectories (no root `.git/`), BrainBank auto-detects them and indexes all into one shared DB. See [Multi-Repository Indexing](#multi-repository-indexing).
188
-
189
- ### Watch Mode
190
-
191
- Auto-re-index code files when they change. Watches for file changes and re-indexes incrementally:
192
-
193
- ```bash
194
- brainbank watch # Watch repo, auto re-index on save
195
- # ━━━ BrainBank Watch ━━━
196
- # Watching /path/to/repo for changes...
197
- # 14:30:02 ✓ code: src/api.ts
198
- # 14:30:05 ✓ code: src/routes.ts
199
- # 14:30:08 ✓ csv: data/metrics.csv ← custom plugin
200
- ```
201
-
202
- > Watch mode monitors **code files** by default. [Custom plugins](#custom-plugins) that implement `watchPatterns()` and `onFileChange()` are automatically picked up — their name appears in the console output alongside the built-in `code` plugin. Git history and document collections are not affected by file-system changes and must be re-indexed explicitly with `brainbank index` / `brainbank docs`.
203
-
204
- ### Document Collections
205
-
206
- ```bash
207
- brainbank collection add <path> --name docs # Register a document folder
208
- brainbank collection list # List registered collections
209
- brainbank collection remove <name> # Remove a collection
210
- ```
211
-
212
- ### Search
213
-
214
- ```bash
215
- brainbank search <query> # Semantic search (vector)
216
- brainbank hsearch <query> # Hybrid search (best quality)
217
- brainbank ksearch <query> # Keyword search (BM25, instant)
218
- brainbank dsearch <query> # Document search
219
- ```
220
-
221
- ### Context
222
-
223
- ```bash
224
- brainbank context <task> # Get formatted context for a task
225
- brainbank context add <col> <path> <desc> # Add context metadata
226
- brainbank context list # List context metadata
227
- ```
228
-
229
- ### KV Store (dynamic collections)
230
-
231
- ```bash
232
- brainbank kv add <coll> <content> # Add item to a collection
233
- brainbank kv search <coll> <query> # Search a collection
234
- brainbank kv list [coll] # List collections or items
235
- brainbank kv trim <coll> --keep <n> # Keep only N most recent
236
- brainbank kv clear <coll> # Clear all items
237
- ```
238
-
239
- ### Utility
240
-
241
- ```bash
242
- brainbank stats # Show index statistics
243
- brainbank reembed # Re-embed all vectors (provider switch)
244
- brainbank watch # Watch files, auto re-index on change
245
- brainbank serve # Start MCP server (stdio)
246
- ```
247
-
248
- **Global options:** `--repo <path>`, `--force`, `--depth <n>`, `--collection <name>`, `--pattern <glob>`, `--context <desc>`, `--reranker <name>`
249
-
250
- ---
251
-
252
- ## Programmatic API
253
-
254
- Use BrainBank as a library in your TypeScript/Node.js project.
255
-
256
- ### Plugins
257
-
258
- BrainBank uses pluggable plugins. Register only what you need with `.use()`:
259
-
260
- | Plugin | Import | Description |
261
- |---------|--------|-------------|
262
- | `code` | `brainbank/code` | AST-aware code chunking via tree-sitter (19 languages) |
263
- | `git` | `brainbank/git` | Git commit history, diffs, co-edit relationships |
264
- | `docs` | `brainbank/docs` | Document collections (markdown, wikis) |
265
-
266
- ```typescript
267
- import { BrainBank, OpenAIEmbedding } from 'brainbank';
268
- import { code } from 'brainbank/code';
269
- import { git } from 'brainbank/git';
270
- import { docs } from 'brainbank/docs';
271
-
272
- // Each plugin can use a different embedding provider
273
- const brain = new BrainBank({ repoPath: '.' }) // default: local WASM (384d, free)
274
- .use(code({ embeddingProvider: new OpenAIEmbedding() })) // code: OpenAI (1536d)
275
- .use(git()) // git: local (384d)
276
- .use(docs()); // docs: local (384d)
277
-
278
- // Index code + git (incremental — only processes changes)
279
- await brain.index();
280
-
281
- // Register and index document collections
282
- await brain.addCollection({ name: 'wiki', path: '~/docs', pattern: '**/*.md' });
283
- await brain.indexDocs();
284
-
285
- // Dynamic collections — store anything
286
- const decisions = brain.collection('decisions');
287
- await decisions.add(
288
- 'Use SQLite with WAL mode instead of PostgreSQL. Portable, zero infra.',
289
- { tags: ['architecture'] }
290
- );
291
- const hits = await decisions.search('why not postgres');
55
+ await log.add('Switched to argon2id for password hashing', { tags: ['security'] });
292
56
 
293
57
  brain.close();
294
58
  ```
295
59
 
296
- ### Collections
297
-
298
- Dynamic key-value collections with semantic search — the building block for agent memory:
299
-
300
- ```typescript
301
- const decisions = brain.collection('decisions');
302
-
303
- // Store rich content (auto-embedded for vector search)
304
- await decisions.add(
305
- 'Use SQLite with WAL mode instead of PostgreSQL. Portable single-file ' +
306
- 'storage, works offline, zero infrastructure.',
307
- { tags: ['architecture'], metadata: { files: ['src/db.ts'] } }
308
- );
309
-
310
- // Semantic search — finds by meaning, not keywords
311
- const hits = await decisions.search('why not postgres');
312
- // → [{ content: 'Use SQLite with WAL...', score: 0.95, tags: [...], metadata: {...} }]
313
-
314
- // Management
315
- decisions.list({ limit: 20 }); // newest first
316
- decisions.list({ tags: ['architecture'] }); // filter by tags
317
- decisions.count(); // total items
318
- decisions.trim({ keep: 50 }); // keep N most recent
319
- decisions.prune({ olderThan: '30d' }); // remove older than 30 days
320
- brain.listCollectionNames(); // → ['decisions', ...]
321
- ```
322
-
323
- > 📂 See [examples/collection](examples/collection/) for a complete runnable demo with cross-collection linking and metadata.
324
-
325
- ### Watch Mode
326
-
327
- Auto-re-index when files change:
328
-
329
- ```typescript
330
- // API
331
- const watcher = brain.watch({
332
- debounceMs: 2000,
333
- onIndex: (file, plugin) => console.log(`${plugin}: ${file}`),
334
- onError: (err) => console.error(err.message),
335
- });
336
-
337
- // Later: watcher.close();
338
- ```
339
-
340
- ```bash
341
- # CLI
342
- brainbank watch
343
- # ━━━ BrainBank Watch ━━━
344
- # Watching /path/to/repo for changes...
345
- # 14:30:02 ✓ code: src/api.ts
346
- # 14:30:05 ✓ code: src/routes.ts
347
- ```
348
-
349
- #### Custom Plugin Watch
350
-
351
- Custom plugins can hook into watch mode by implementing `onFileChange` and `watchPatterns`:
352
-
353
- ```typescript
354
- import type { Plugin, PluginContext } from 'brainbank';
355
-
356
- function csvPlugin(): Plugin {
357
- let ctx: PluginContext;
358
-
359
- return {
360
- name: 'csv',
361
-
362
- async initialize(context) {
363
- ctx = context;
364
- },
365
-
366
- // Tell watch which files this plugin cares about
367
- watchPatterns() {
368
- return ['**/*.csv', '**/*.tsv'];
369
- },
370
-
371
- // Called when a watched file changes
372
- async onFileChange(filePath, event) {
373
- if (event === 'delete') return true;
374
-
375
- const data = fs.readFileSync(filePath, 'utf-8');
376
- const col = ctx.collection('csv_data');
377
- await col.add(data, {
378
- tags: ['csv'],
379
- metadata: { file: filePath },
380
- });
381
- return true; // handled
382
- },
383
- };
384
- }
385
-
386
- const brain = new BrainBank({ dbPath: './brain.db' })
387
- .use(code())
388
- .use(csvPlugin());
389
-
390
- await brain.initialize();
391
- brain.watch(); // Now watches .ts, .py, etc. AND .csv, .tsv
392
- ```
393
-
394
- ### Search
395
-
396
- Three modes, from fastest to best quality:
397
-
398
- | Mode | Method | Speed | Quality |
399
- |------|--------|-------|---------|
400
- | Keyword | `searchBM25(q)` | ⚡ instant | Good for exact terms |
401
- | Vector | `search(q)` | ~50ms | Good for concepts |
402
- | **Hybrid** | `hybridSearch(q)` | ~100ms | **Best — catches both** |
403
-
404
- ```typescript
405
- // Hybrid search (recommended default)
406
- const results = await brain.hybridSearch('authentication middleware');
407
-
408
- // Scoped search
409
- const codeHits = await brain.searchCode('parse JSON config', 8);
410
- const commitHits = await brain.searchCommits('fix auth bug', 5);
411
- const docHits = await brain.searchDocs('getting started', { collection: 'wiki' });
412
- ```
413
-
414
- | Score | Meaning |
415
- |-------|---------|
416
- | 0.8+ | Near-exact match |
417
- | 0.5–0.8 | Strongly related |
418
- | 0.3–0.5 | Somewhat related |
419
- | < 0.3 | Weak match |
420
-
421
- ### Document Collections
422
-
423
- Register folders of documents. Files are chunked by heading structure:
424
-
425
- ```typescript
426
- await brain.addCollection({
427
- name: 'docs',
428
- path: '~/project/docs',
429
- pattern: '**/*.md',
430
- ignore: ['**/drafts/**'],
431
- context: 'Project documentation',
432
- });
433
-
434
- await brain.indexDocs();
435
-
436
- // Add context metadata (helps LLM understand what documents are about)
437
- brain.addContext('docs', '/api', 'REST API reference');
438
- brain.addContext('docs', '/guides', 'Step-by-step tutorials');
439
- ```
440
-
441
- ### Context Generation
442
-
443
- Get formatted markdown ready for system prompt injection:
444
-
445
- ```typescript
446
- const context = await brain.getContext('add rate limiting to the API', {
447
- codeResults: 6,
448
- gitResults: 5,
449
- affectedFiles: ['src/api/routes.ts'],
450
- useMMR: true,
451
- });
452
- // Returns: ## Relevant Code, ## Git History, ## Relevant Documents
453
- ```
454
-
455
- ### Custom Plugins
456
-
457
- Implement the `Plugin` interface to build your own:
458
-
459
- ```typescript
460
- import type { Plugin, PluginContext } from 'brainbank';
461
-
462
- const myPlugin: Plugin = {
463
- name: 'custom',
464
- async initialize(ctx: PluginContext) {
465
- // ctx.db — shared SQLite database
466
- // ctx.embedding — shared embedding provider
467
- // ctx.collection() — create dynamic collections
468
- const store = ctx.collection('my_data');
469
- await store.add('indexed content', { source: 'custom' });
470
- },
471
- };
472
-
473
- brain.use(myPlugin);
474
- ```
475
-
476
- #### Using custom plugins with the CLI
477
-
478
- Drop `.ts` files into `.brainbank/indexers/` — the CLI auto-discovers them:
479
-
480
- ```
481
- .brainbank/
482
- ├── brainbank.db
483
- └── indexers/
484
- ├── slack.ts
485
- └── jira.ts
486
- ```
487
-
488
- Each file exports a default `Plugin`:
489
-
490
- ```typescript
491
- // .brainbank/indexers/slack.ts
492
- import type { Plugin } from 'brainbank';
493
-
494
- export default {
495
- name: 'slack',
496
- async initialize(ctx) {
497
- const msgs = ctx.collection('slack_messages');
498
- // ... fetch and index slack messages
499
- },
500
- } satisfies Plugin;
501
- ```
502
-
503
- That's it — all CLI commands automatically pick up your plugins:
504
-
505
- ```bash
506
- brainbank index # runs code + git + docs + slack + jira
507
- brainbank stats # shows all plugins
508
- brainbank kv search slack_messages "deploy" # search slack data
509
- ```
510
-
511
60
  ---
512
61
 
513
- ## Project Config
514
-
515
- Drop a `.brainbank/config.json` in your repo root. Every `brainbank index` reads it automatically — no CLI flags needed.
516
-
517
- ```jsonc
518
- // .brainbank/config.json
519
- {
520
- // Which built-in plugins to load (default: all three)
521
- "plugins": ["code", "git", "docs"],
522
-
523
- // Per-plugin options
524
- "code": {
525
- "embedding": "openai", // use OpenAI embeddings for code
526
- "maxFileSize": 512000
527
- },
528
- "git": {
529
- "depth": 200 // index last 200 commits
530
- },
531
- "docs": {
532
- "embedding": "perplexity-context",
533
- "collections": [
534
- { "name": "docs", "path": "./docs", "pattern": "**/*.md" },
535
- { "name": "wiki", "path": "~/team-wiki", "pattern": "**/*.md", "ignore": ["drafts/**"] }
536
- ]
537
- },
538
-
539
- // Global defaults
540
- "embedding": "local", // default for plugins without their own
541
- "reranker": "qwen3"
542
- }
543
- ```
544
-
545
- **Embedding keys:** `"local"` (default, free), `"openai"`, `"perplexity"`, `"perplexity-context"`.
62
+ ## Packages
546
63
 
547
- **Per-plugin embeddings**each plugin creates its own HNSW index with the correct dimensions. A plugin without an `embedding` key uses the global default.
64
+ `brainbank` is the core framework strictly plugin-agnostic. Plugins are separate `@brainbank/*` packages that own their database schema, search strategies, and context formatting. Install only what you need:
548
65
 
549
- **Docs collections** — registered automatically on every `brainbank index` run. No need for `--docs` flags.
66
+ ### Indexer Plugins
550
67
 
551
- **Custom plugins** auto-discovered from `.brainbank/indexers/`:
68
+ Data sources that feed into BrainBank's hybrid search engine. Each plugin manages its own tables via the built-in migration system.
552
69
 
553
- ```
554
- .brainbank/
555
- ├── brainbank.db # SQLite database (auto-created)
556
- ├── config.json # Project config (optional)
557
- └── indexers/ # Custom plugin files (optional)
558
- ├── slack.ts
559
- └── jira.ts
560
- ```
70
+ | Package | Description | Install |
71
+ |---------|-------------|----------|
72
+ | [`@brainbank/code`](packages/code/) | AST chunking, import graph, symbol index (20 languages) | `npm i @brainbank/code` |
73
+ | [`@brainbank/git`](packages/git/) | Git history indexing + co-edit analysis | `npm i @brainbank/git` |
74
+ | [`@brainbank/docs`](packages/docs/) | Document collection search with smart chunking | `npm i @brainbank/docs` |
561
75
 
562
- Custom plugins can also have their own config section:
76
+ ### Integrations
563
77
 
564
- ```jsonc
565
- {
566
- "plugins": ["code", "git"],
567
- "slack": { "embedding": "openai" }, // matched by plugin name
568
- "jira": { "embedding": "perplexity" }
569
- }
570
- ```
78
+ Extensions that connect BrainBank to external tools and workflows.
571
79
 
572
- **Config priority:** CLI flags > `config.json` > auto-resolve from DB > defaults.
573
-
574
- > `.brainbank/config.ts` (or `.js`, `.mjs`) is still supported for programmatic config with custom plugin instances. JSON is preferred for declarative setups.
575
-
576
- No config file? The CLI uses all built-in plugins with local embeddings — zero config required.
80
+ | Package | Description | Install |
81
+ |---------|-------------|----------|
82
+ | [`@brainbank/mcp`](packages/mcp/) | MCP server for Antigravity, Claude, Cursor | `npm i @brainbank/mcp` |
577
83
 
578
84
  ---
579
85
 
580
- ### AI Agent Integration
581
-
582
- Teach your AI coding agent to use BrainBank as persistent memory. Add an `AGENTS.md` (or `.cursor/rules`) to your project root — works with **Antigravity**, **Claude Code**, **Cursor**, and anything that reads project-level instructions.
86
+ ## Documentation
583
87
 
584
- <details>
585
- <summary><strong>Option A: CLI commands</strong> (zero setup)</summary>
586
-
587
- > **Memory BrainBank**
588
- >
589
- > **Store** a conversation summary after each task:
590
- > `brainbank kv add conversations "Refactored auth to AuthService with DI. JWT + refresh tokens + RBAC."`
591
- >
592
- > **Record** architecture decisions:
593
- > `brainbank kv add decisions "ADR: Fastify over Express. 2x throughput, schema validation, native TS."`
594
- >
595
- > **Search** before starting work:
596
- > `brainbank hsearch "auth middleware"` · `brainbank kv search decisions "auth"`
597
-
598
- </details>
599
-
600
- <details>
601
- <summary><strong>Option B: MCP tools</strong> (richer integration)</summary>
602
-
603
- > **Memory — BrainBank (MCP)**
604
- >
605
- > Use the BrainBank MCP tools for persistent agent memory:
606
- >
607
- > **Store** via `brainbank_kv_add`:
608
- > `{ collection: "conversations", content: "Refactored auth to AuthService with DI.", tags: ["auth"] }`
609
- >
610
- > **Search** via `brainbank_kv_search`:
611
- > `{ collection: "decisions", query: "authentication approach" }`
612
- >
613
- > **Code search** via `brainbank_hybrid_search`:
614
- > `{ query: "auth middleware", repo: "." }`
615
-
616
- </details>
617
-
618
- #### Setup
619
-
620
- | Agent | How to connect |
621
- |-------|---------------|
622
- | **Antigravity** | Add `AGENTS.md` to project root |
623
- | **Claude Code** | Add `AGENTS.md` to project root |
624
- | **Cursor** | Add rules in `.cursor/rules` |
625
- | **MCP** (any agent) | See [MCP Server](#mcp-server) config below |
626
-
627
- #### Custom Plugin: Auto-Ingest Conversation Logs
628
-
629
- For agents that produce structured logs (e.g. Antigravity's `brain/` directory), auto-index them:
630
-
631
- ```typescript
632
- // .brainbank/indexers/conversations.ts
633
- import type { Plugin, PluginContext } from 'brainbank';
634
- import * as fs from 'node:fs';
635
- import * as path from 'node:path';
636
-
637
- export default {
638
- name: 'conversations',
639
- async initialize(ctx: PluginContext) {
640
- const conversations = ctx.collection('conversations');
641
- const logsDir = path.join(ctx.config.repoPath, '.gemini/antigravity/brain');
642
- if (!fs.existsSync(logsDir)) return;
643
-
644
- for (const dir of fs.readdirSync(logsDir)) {
645
- const file = path.join(logsDir, dir, '.system_generated/logs/overview.txt');
646
- if (!fs.existsSync(file)) continue;
647
- const content = fs.readFileSync(file, 'utf-8');
648
- if (content.length < 100) continue;
649
- await conversations.add(content, {
650
- tags: ['auto'],
651
- metadata: { session: dir, source: 'antigravity' },
652
- });
653
- }
654
- },
655
- } satisfies Plugin;
656
- ```
657
-
658
- ```bash
659
- brainbank index # now auto-indexes conversation logs alongside code + git
660
- brainbank kv search conversations "what did we decide about auth"
661
- ```
662
-
663
- ### Examples
664
-
665
- | Example | Description | Run |
666
- |---------|-------------|-----|
667
- | [rag](examples/rag/) | RAG chatbot — docs retrieval + generation | `OPENAI_API_KEY=sk-... PERPLEXITY_API_KEY=pplx-... npx tsx examples/rag/rag.ts --docs <path>` |
668
- | [memory](examples/memory/) | Memory chatbot — fact extraction + entity graph | `OPENAI_API_KEY=sk-... npx tsx examples/memory/memory.ts` |
669
- | [collection](examples/collection/) | Collections, semantic search, tags, metadata linking | `npx tsx examples/collection/collection.ts` |
88
+ | Guide | Description |
89
+ |-------|-------------|
90
+ | **[Getting Started](docs/getting-started.md)** | Installation, quick start, first search |
91
+ | **[CLI Reference](docs/cli.md)** | Complete command reference |
92
+ | **[Plugins](docs/plugins.md)** | Built-in plugins overview + configuration |
93
+ | **[Collections](docs/collections.md)** | Dynamic KV store with semantic search |
94
+ | **[Search](docs/search.md)** | Hybrid search, scoped queries, context generation |
95
+ | **[Custom Plugins](docs/custom-plugins.md)** | Build plugins + publish as npm packages |
96
+ | **[Configuration](docs/config.md)** | `.brainbank/config.json`, env vars |
97
+ | **[Embeddings, Reranker & Pruner](docs/embeddings.md)** | Providers, benchmarks, per-plugin overrides, LLM noise filter |
98
+ | **[Multi-Repo](docs/multi-repo.md)** | Index multiple repositories into one DB |
99
+ | **[MCP Server](docs/mcp.md)** | AI tool integration (stdio) |
100
+ | **[Indexing](docs/indexing.md)** | Code graph, incremental indexing, re-embedding |
101
+ | **[Migrations](docs/migrations.md)** | Plugin schema migrations, built-in schemas |
102
+ | **[Architecture](docs/architecture.md)** | System internals, data flows, design patterns |
670
103
 
671
104
  ---
672
105
 
673
- ## MCP Server
674
-
675
- BrainBank ships with an MCP server (stdio) for AI tool integration.
676
-
677
- ```bash
678
- brainbank serve
679
- ```
680
-
681
- ### Antigravity / Claude Desktop
682
-
683
- Add to your MCP config (`~/.gemini/antigravity/mcp_config.json` or Claude Desktop settings):
684
-
685
- ```json
686
- {
687
- "mcpServers": {
688
- "brainbank": {
689
- "command": "npx",
690
- "args": ["-y", "@brainbank/mcp"]
691
- }
692
- }
693
- }
694
- ```
695
-
696
- **Zero-config.** The MCP server auto-detects:
697
- - **Repo path** — from `repo` tool param > `BRAINBANK_REPO` env > `findRepoRoot(cwd)`
698
- - **Embedding provider** — from `provider_key` stored in the DB (set during `brainbank index --embedding openai`)
699
-
700
- > [!TIP]
701
- > Index your repo once with the CLI to set up the embedding provider:
702
- > ```bash
703
- > brainbank index . --embedding openai # stores provider_key=openai in DB
704
- > ```
705
- > After that, the MCP server (and any future CLI runs) auto-resolve the correct provider from the DB — no env vars needed.
706
-
707
- > [!NOTE]
708
- > If you switch embedding providers (e.g. local → OpenAI), run `brainbank reembed` to regenerate all vectors. BrainBank auto-detects dimension mismatches and warns you.
106
+ ## Examples
709
107
 
710
- ### Available Tools
108
+ | Example | Description |
109
+ |---------|-------------|
110
+ | [notes-plugin](examples/notes-plugin/) | Programmatic plugin — reads `.txt` files |
111
+ | [custom-plugin](examples/custom-plugin/) | CLI auto-discovery plugin |
112
+ | [custom-package](examples/custom-package/) | Standalone npm package scaffold |
113
+ | [collection](examples/collection/) | Collections, search, tags, metadata |
114
+ | [rag](examples/rag/) | RAG chatbot — docs retrieval + generation ¹ |
711
115
 
712
- | Tool | Description |
713
- |------|-------------|
714
- | `brainbank_search` | Unified search — `mode: hybrid` (default), `vector`, or `keyword` |
715
- | `brainbank_context` | Formatted context block for a task (code + git + co-edits) |
716
- | `brainbank_index` | Trigger incremental code/git/docs indexing |
717
- | `brainbank_stats` | Index statistics (files, commits, chunks, collections) |
718
- | `brainbank_history` | Git history for a specific file |
719
- | `brainbank_collection` | KV collection ops — `action: add`, `search`, or `trim` |
720
-
721
- ---
722
-
723
- ## Configuration
724
-
725
- ```typescript
726
- import { BrainBank, OpenAIEmbedding } from 'brainbank';
727
- import { Qwen3Reranker } from 'brainbank'; // built-in, requires node-llama-cpp
728
-
729
- const brain = new BrainBank({
730
- repoPath: '.',
731
- dbPath: '.brainbank/brainbank.db',
732
- gitDepth: 500,
733
- maxFileSize: 512_000,
734
- embeddingDims: 1536,
735
- maxElements: 2_000_000,
736
- embeddingProvider: new OpenAIEmbedding(), // or: omit for free local WASM (384d)
737
- reranker: new Qwen3Reranker(), // local cross-encoder (auto-downloads ~640MB)
738
- });
739
- ```
740
-
741
- ### Embedding Providers
742
-
743
- | Provider | Import | Dims | Speed | Cost |
744
- |----------|--------|------|-------|------|
745
- | **Local (default)** | built-in | 384 | ⚡ 0ms | Free |
746
- | **OpenAI** | `OpenAIEmbedding` | 1536 | ~100ms | $0.02/1M tokens |
747
- | **Perplexity** | `PerplexityEmbedding` | 2560 (4b) / 1024 (0.6b) | ~100ms | $0.02/1M tokens |
748
- | **Perplexity Context** | `PerplexityContextEmbedding` | 2560 (4b) / 1024 (0.6b) | ~100ms | $0.06/1M tokens |
749
-
750
- #### How It Works
751
-
752
- BrainBank **auto-resolves** the embedding provider. Set it once → it's stored in the DB → every future run uses the same provider automatically.
753
-
754
- **Programmatic API** — pass `embeddingProvider` to the constructor:
755
-
756
- ```typescript
757
- import { BrainBank, OpenAIEmbedding } from 'brainbank';
758
-
759
- const brain = new BrainBank({
760
- repoPath: '.',
761
- embeddingProvider: new OpenAIEmbedding(), // stored in DB on first index
762
- });
763
- ```
764
-
765
- **CLI** — use the `--embedding` flag on first index:
766
-
767
- ```bash
768
- brainbank index . --embedding openai # stores provider_key=openai in DB
769
- brainbank index . # auto-resolves openai from DB
770
- brainbank hsearch "auth middleware" # uses the same provider
771
- ```
772
-
773
- **MCP** — zero-config. Reads the provider from the DB automatically.
774
-
775
- > The provider key is persisted in the `embedding_meta` table. Priority on startup: explicit `embeddingProvider` in config > stored `provider_key` in DB > local WASM (default).
776
-
777
- **Per-plugin override** — each plugin can use a different embedding provider:
778
-
779
- ```typescript
780
- import { BrainBank, OpenAIEmbedding } from 'brainbank';
781
- import { PerplexityContextEmbedding } from 'brainbank';
782
- import { code } from 'brainbank/code';
783
- import { git } from 'brainbank/git';
784
- import { docs } from 'brainbank/docs';
785
-
786
- const brain = new BrainBank({ repoPath: '.' }) // default: local WASM (384d)
787
- .use(code({ embeddingProvider: new OpenAIEmbedding() })) // code: OpenAI (1536d)
788
- .use(git()) // git: local (384d)
789
- .use(docs({ embeddingProvider: new PerplexityContextEmbedding() })); // docs: Perplexity (2560d)
790
- ```
791
-
792
- > Each plugin creates its own HNSW index with the correct dimensions. The global `embeddingProvider` (or local default) is used for any plugin that doesn't specify one.
793
-
794
- #### OpenAI
795
-
796
- ```typescript
797
- import { OpenAIEmbedding } from 'brainbank';
798
-
799
- new OpenAIEmbedding(); // uses OPENAI_API_KEY env var
800
- new OpenAIEmbedding({
801
- model: 'text-embedding-3-large',
802
- dims: 512, // Matryoshka reduction
803
- apiKey: 'sk-...',
804
- baseUrl: 'https://my-proxy.com/v1/embeddings',
805
- });
806
- ```
807
-
808
- #### Perplexity (Standard)
809
-
810
- Best for independent texts, queries, and code chunks.
811
-
812
- ```typescript
813
- import { PerplexityEmbedding } from 'brainbank';
814
-
815
- new PerplexityEmbedding(); // uses PERPLEXITY_API_KEY env var
816
- new PerplexityEmbedding({
817
- model: 'pplx-embed-v1-0.6b', // smaller, faster (1024d)
818
- dims: 512, // Matryoshka reduction
819
- });
820
- ```
821
-
822
- #### Perplexity (Contextualized)
823
-
824
- Chunks share document context → better retrieval for related code/docs.
825
-
826
- ```typescript
827
- import { PerplexityContextEmbedding } from 'brainbank';
828
-
829
- new PerplexityContextEmbedding(); // uses PERPLEXITY_API_KEY env var
830
- new PerplexityContextEmbedding({
831
- model: 'pplx-embed-context-v1-0.6b', // smaller, faster (1024d)
832
- dims: 512, // Matryoshka reduction
833
- });
834
- ```
835
-
836
- #### Benchmarks
837
-
838
- Real benchmarks on a production NestJS backend (1052 code chunks + git history):
839
-
840
- | Provider | Dims | Index Time | Avg Search | Cost |
841
- |----------|------|------------|------------|------|
842
- | **Local WASM** | 384 | 87s | **8ms** | Free |
843
- | **OpenAI** | 1536 | 106s | 202ms | $0.02/1M tok |
844
- | **Perplexity** | 2560 | **66s** ⚡ | 168ms | $0.02/1M tok |
845
- | **Perplexity Context** | 2560 | 78s | 135ms | $0.06/1M tok |
846
-
847
- - **Fastest indexing:** Perplexity standard — 38% faster than OpenAI
848
- - **Fastest search (API):** Perplexity Context — 33% faster than OpenAI
849
- - **Fastest search (total):** Local WASM — no network latency
850
- - **Best context awareness:** Perplexity Context — finds semantically related chunks others miss
851
-
852
- > [!WARNING]
853
- > Switching embedding provider (e.g. local → OpenAI) changes the vector dimensions. BrainBank will **refuse to initialize** if the stored dimensions don't match the current provider. Use `initialize({ force: true })` and then `reembed()` to migrate, or switch back to the original provider.
854
-
855
- ### Reranker
856
-
857
- BrainBank ships with an optional cross-encoder reranker using **Qwen3-Reranker-0.6B** via `node-llama-cpp`. It runs 100% locally — no API keys needed. The reranker is **disabled by default**.
858
-
859
- ```bash
860
- # Only requirement — the LLM runtime (model auto-downloads on first use)
861
- npm install node-llama-cpp
862
- ```
863
-
864
- #### When to Use It
865
-
866
- The reranker runs local neural inference on every search result, which improves ranking precision but adds significant latency. Here are real benchmarks on a ~2100 file / 4000+ chunk codebase:
867
-
868
- | Metric | Without Reranker | With Reranker |
869
- |--------|-----------------|---------------|
870
- | **Warm query time** | ~480ms | ~5500ms |
871
- | **Cold start** | ~7s | ~12s |
872
- | **Memory overhead** | — | +640MB (model) |
873
- | **Ranking quality** | Good (RRF) | Slightly better |
874
-
875
- **Recommended:** Leave it disabled for interactive use (MCP, IDE integrations). The RRF fusion of vector + BM25 already produces high-quality results. Enable it only for:
876
-
877
- - Batch processing where latency doesn't matter
878
- - Very large codebases (50k+ files) where false positives are costly
879
- - Server environments with RAM to spare
880
-
881
- #### Enabling the Reranker
882
-
883
- ```typescript
884
- import { BrainBank } from 'brainbank';
885
- import { Qwen3Reranker } from 'brainbank';
886
-
887
- const brain = new BrainBank({
888
- reranker: new Qwen3Reranker(), // ~640MB model, auto-downloaded on first use
889
- });
890
- ```
891
-
892
- Or from the CLI:
893
-
894
- ```bash
895
- brainbank hsearch "auth middleware" --reranker qwen3
896
- ```
897
-
898
- Or via environment variable:
899
-
900
- ```bash
901
- BRAINBANK_RERANKER=qwen3 brainbank serve
902
- ```
903
-
904
- The model is cached at `~/.cache/brainbank/models/` after first download.
905
-
906
- #### Position-Aware Score Blending
907
-
908
- When enabled, the reranker uses position-aware blending — trusting retrieval scores more for top results and the reranker more for lower-ranked results:
909
-
910
- | Position | Retrieval (RRF) | Reranker | Rationale |
911
- |----------|----------------|----------|----------|
912
- | 1–3 | **75%** | 25% | Preserves exact keyword matches |
913
- | 4–10 | **60%** | 40% | Balanced blend |
914
- | 11+ | 40% | **60%** | Trust reranker for uncertain results |
915
-
916
- #### Custom Reranker
917
-
918
- Implement the `Reranker` interface to use your own:
919
-
920
- ```typescript
921
- import type { Reranker } from 'brainbank';
922
-
923
- const myReranker: Reranker = {
924
- async rank(query: string, documents: string[]): Promise<number[]> {
925
- // Return relevance scores 0.0-1.0 for each document
926
- },
927
- async close() { /* optional cleanup */ },
928
- };
929
- ```
930
-
931
- Without a reranker, BrainBank uses pure RRF fusion — which is already production-quality for most use cases.
932
-
933
- ### Notes
934
-
935
- The notes plugin gives your agent **persistent conversation memory** — store structured digests of past sessions and recall them via hybrid search.
936
-
937
- ```typescript
938
- import { BrainBank } from 'brainbank';
939
- import { notes } from 'brainbank/notes';
940
-
941
- const brain = new BrainBank({ repoPath: '.' });
942
- brain.use(notes());
943
- await brain.initialize();
944
-
945
- const notesPlugin = brain.plugin('notes');
946
-
947
- // Store a conversation digest
948
- await notesPlugin.remember({
949
- title: 'Refactored auth module',
950
- summary: 'Extracted JWT validation into middleware, added refresh token rotation',
951
- decisions: ['Use RS256 over HS256', 'Refresh tokens stored in httpOnly cookie'],
952
- filesChanged: ['src/auth/jwt.ts', 'src/middleware/auth.ts'],
953
- patterns: ['Always validate token expiry before DB lookup'],
954
- openQuestions: ['Should we add rate limiting to the refresh endpoint?'],
955
- tags: ['auth', 'security'],
956
- });
957
-
958
- // Recall relevant notes
959
- const relevant = await notesPlugin.recall('JWT token validation', { k: 3 });
960
-
961
- // List recent notes
962
- const recent = notesPlugin.list(10);
963
- const longTermOnly = notesPlugin.list(10, 'long');
964
-
965
- // Consolidate: promote old short-term notes to long-term (keeps last 20 as short)
966
- const { promoted } = notesPlugin.consolidate(20);
967
- ```
968
-
969
- **Memory tiers:**
970
- - **`short`** (default) — Full digest with all fields, kept for recent sessions
971
- - **`long`** — Compressed: only title, summary, decisions, and patterns preserved. Files and open questions dropped
972
-
973
- Consolidation automatically promotes notes beyond the keep window from `short` → `long`, reducing storage while preserving key learnings.
974
-
975
- ### Agent Memory (Patterns)
976
-
977
- The memory plugin enables **learning from experience** — your agent records what worked (and what didn't) across tasks, then distills patterns into reusable strategies.
978
-
979
- ```typescript
980
- import { BrainBank } from 'brainbank';
981
- import { memory } from 'brainbank/memory';
982
-
983
- const brain = new BrainBank({ repoPath: '.' });
984
- brain.use(memory());
985
- await brain.initialize();
986
-
987
- const mem = brain.plugin('memory');
988
-
989
- // Record a learning pattern
990
- await mem.learn({
991
- taskType: 'refactor',
992
- task: 'Extract auth logic into middleware',
993
- approach: 'Created Express middleware, moved JWT validation from routes',
994
- outcome: 'Reduced route handler size by 60%, improved testability',
995
- successRate: 0.95,
996
- critique: 'Should have added integration tests before refactoring',
997
- });
998
-
999
- // Search for similar patterns before starting a new task
1000
- const patterns = await mem.search('refactor database queries');
1001
-
1002
- // Consolidate: prune old failures + merge duplicates
1003
- const { pruned, deduped } = mem.consolidate();
1004
-
1005
- // Distill top patterns into a strategy
1006
- const strategy = mem.distill('refactor');
1007
- // → "Strategy for 'refactor' (5 patterns, avg success 88%):
1008
- // • Created middleware, moved validation from routes (95%)
1009
- // └ Should have added integration tests before refactoring"
1010
- ```
1011
-
1012
- **How it works:**
1013
- 1. **Learn** — Records task, approach, outcome, and success rate. Embeds for semantic search
1014
- 2. **Search** — Finds similar successful patterns (filters by `successRate ≥ 0.5`)
1015
- 3. **Consolidate** — Auto-runs every 50 patterns: prunes failures older than 90 days, deduplicates (cosine > 0.95)
1016
- 4. **Distill** — Aggregates top patterns per task type into a single strategy text with confidence score
1017
-
1018
- ---
1019
-
1020
- ## Memory
1021
-
1022
- `@brainbank/memory` adds **deterministic memory extraction** to any LLM conversation. After every turn, it automatically extracts facts, deduplicates against existing memories, and decides `ADD` / `UPDATE` / `NONE` — no function calling needed.
1023
-
1024
- Optionally extracts **entities and relationships** (knowledge graph) from the same LLM call — no extra cost. Includes **LLM-powered entity resolution** to merge aliases (e.g. "TS" → "TypeScript").
1025
-
1026
- Inspired by [mem0](https://github.com/mem0ai/mem0)'s pipeline, but framework-agnostic and built on BrainBank collections.
1027
-
1028
- ```bash
1029
- npm install @brainbank/memory
1030
- ```
1031
-
1032
- ```typescript
1033
- import { BrainBank } from 'brainbank';
1034
- import { Memory, EntityStore, OpenAIProvider } from '@brainbank/memory';
1035
-
1036
- const brain = new BrainBank({ dbPath: './memory.db' });
1037
- await brain.initialize();
1038
-
1039
- const llm = new OpenAIProvider({ model: 'gpt-4.1-nano' });
1040
-
1041
- // Opt-in entity extraction (knowledge graph)
1042
- const entityStore = new EntityStore(brain, {
1043
- onEntity: (op) => console.log(`${op.action}: ${op.name}`),
1044
- });
1045
-
1046
- const memory = new Memory(brain, {
1047
- llm, // auto-shared with EntityStore
1048
- entityStore, // optional — omit for facts-only mode
1049
- onOperation: (op) => console.log(`${op.action}: ${op.fact}`),
1050
- });
1051
-
1052
- // After every conversation turn
1053
- const result = await memory.process(userMessage, assistantResponse);
1054
- // result.operations → [{ fact, action: "ADD", reason }]
1055
- // result.entities → { entitiesProcessed: 2, relationshipsProcessed: 1 }
1056
-
1057
- // System prompt with memories + entities
1058
- const context = memory.buildContext();
1059
- // → "## Memories\n- User's name is Berna\n\n## Known Entities\n- Berna (person, 3x)\n..."
1060
- ```
1061
-
1062
- The `LLMProvider` interface works with any framework:
1063
-
1064
- | Framework | Adapter |
1065
- |-----------|--------|
1066
- | OpenAI | Built-in `OpenAIProvider` |
1067
- | LangChain | `ChatOpenAI.invoke()` → string |
1068
- | Vercel AI SDK | `generateText()` → string |
1069
- | Any LLM | Implement `{ generate(messages) → string }` |
1070
-
1071
- > 📂 See [examples/memory](examples/memory/) for a runnable demo. All three LLM backends supported via `--llm` flag.
1072
-
1073
- > 📦 Full docs: [packages/memory/README.md](packages/memory/README.md)
1074
-
1075
- ---
1076
-
1077
- ### Environment Variables
1078
-
1079
- | Variable | Description |
1080
- |----------|-------------|
1081
- | `BRAINBANK_REPO` | Default repository path (optional — auto-detected from `.git/` or passed per tool call) |
1082
- | `BRAINBANK_RERANKER` | Reranker: `none` (default), `qwen3` |
1083
- | `BRAINBANK_DEBUG` | Show full stack traces |
1084
- | `OPENAI_API_KEY` | Required when using `--embedding openai` |
1085
- | `PERPLEXITY_API_KEY` | Required when using `--embedding perplexity` or `perplexity-context` |
1086
-
1087
- > **Note:** `BRAINBANK_EMBEDDING` env var has been removed. Use `brainbank index --embedding <provider>` on first index — the provider is stored in the DB and auto-resolved on subsequent runs.
1088
-
1089
- ---
1090
-
1091
- ## Multi-Repository Indexing
1092
-
1093
- BrainBank can index multiple repositories into a **single shared database**. This is useful for monorepos, microservices, or any project split across multiple Git repositories.
1094
-
1095
- ### How It Works
1096
-
1097
- When you point BrainBank at a directory that contains multiple Git repositories (subdirectories with `.git/`), the CLI **auto-detects** them and creates namespaced plugins:
1098
-
1099
- ```bash
1100
- ~/projects/
1101
- ├── webapp-frontend/ # .git/
1102
- ├── webapp-backend/ # .git/
1103
- └── webapp-shared/ # .git/
1104
- ```
1105
-
1106
- ```bash
1107
- brainbank index ~/projects --depth 200
1108
- ```
1109
-
1110
- ```
1111
- ━━━ BrainBank Index ━━━
1112
- Repo: /Users/you/projects
1113
- Multi-repo: found 3 git repos: webapp-frontend, webapp-backend, webapp-shared
1114
- CODE:WEBAPP-BACKEND [0/1075] ...
1115
- CODE:WEBAPP-FRONTEND [0/719] ...
1116
- GIT:WEBAPP-SHARED [0/200] ...
1117
-
1118
- Code: 2107 indexed, 4084 chunks
1119
- Git: 600 indexed (200 per repo)
1120
- Co-edit pairs: 1636
1121
- ```
1122
-
1123
- All code, git history, and co-edit relationships from every sub-repository go into **one** `.brainbank/brainbank.db` at the parent directory. Search queries automatically return results across all repositories:
1124
-
1125
- ```bash
1126
- brainbank hsearch "cancel job confirmation" --repo ~/projects
1127
- # → Results from frontend components, backend controllers,
1128
- # and shared utilities — all in one search.
1129
- ```
1130
-
1131
- ### Namespaced Plugins
1132
-
1133
- Each sub-repository gets its own namespaced plugin instances (e.g., `code:frontend`, `git:backend`). Same-type plugins share a single HNSW vector index for efficient memory usage and unified search.
1134
-
1135
- ### Programmatic API
1136
-
1137
- ```typescript
1138
- import { BrainBank } from 'brainbank';
1139
- import { code } from 'brainbank/code';
1140
- import { git } from 'brainbank/git';
1141
-
1142
- const brain = new BrainBank({ repoPath: '~/projects' })
1143
- .use(code({ name: 'code:frontend', repoPath: '~/projects/webapp-frontend' }))
1144
- .use(code({ name: 'code:backend', repoPath: '~/projects/webapp-backend' }))
1145
- .use(git({ name: 'git:frontend', repoPath: '~/projects/webapp-frontend' }))
1146
- .use(git({ name: 'git:backend', repoPath: '~/projects/webapp-backend' }));
1147
-
1148
- await brain.initialize();
1149
- await brain.index();
1150
-
1151
- // Cross-repo search
1152
- const results = await brain.hybridSearch('authentication guard');
1153
- // → Results from both frontend and backend
1154
- ```
1155
-
1156
- ### MCP Multi-Workspace
1157
-
1158
- The MCP server maintains a pool of BrainBank instances — one per unique `repo` path. Each tool call can target a different workspace:
1159
-
1160
- ```typescript
1161
- // Agent working in one workspace
1162
- brainbank_hybrid_search({ query: "login form", repo: "/Users/you/projects" })
1163
-
1164
- // Agent switches to a different project
1165
- brainbank_hybrid_search({ query: "API routes", repo: "/Users/you/other-project" })
1166
- ```
1167
-
1168
- Instances are cached in memory after first initialization, so subsequent queries to the same repo are fast (~480ms).
1169
-
1170
- ---
1171
-
1172
- ## Indexing
1173
-
1174
- ### Code Chunking (tree-sitter)
1175
-
1176
- BrainBank uses **native tree-sitter** to parse source code into ASTs and extract semantic blocks — functions, classes, methods, interfaces — as individual chunks. This produces dramatically better embeddings than naive line-based splitting.
1177
-
1178
- **Supported languages (AST-parsed):**
1179
-
1180
- | Category | Languages |
1181
- |----------|-----------|
1182
- | Web | TypeScript, JavaScript, HTML, CSS |
1183
- | Systems | Go, Rust, C, C++, Swift |
1184
- | JVM | Java, Kotlin, Scala |
1185
- | Scripting | Python, Ruby, PHP, Lua, Bash, Elixir |
1186
- | .NET | C# |
1187
-
1188
- For large classes (>80 lines), the chunker descends into the class body and extracts each method as a separate chunk. For unsupported languages, it falls back to a sliding window with overlap.
1189
-
1190
- > Tree-sitter grammars are **optional dependencies** (except JS and TS, which are included). If you index a file whose grammar isn't installed, BrainBank throws a clear error with the exact `npm install` command. See [Tree-Sitter Grammars](#tree-sitter-grammars) for the full list.
1191
-
1192
- ### Incremental Indexing
1193
-
1194
- All indexing is **incremental by default** — only new or changed content is processed:
1195
-
1196
- | Plugin | How it detects changes | What gets skipped |
1197
- |---------|----------------------|-------------------|
1198
- | **Code** | FNV-1a hash of file content | Unchanged files |
1199
- | **Git** | Unique commit hash | Already-indexed commits |
1200
- | **Docs** | SHA-256 of file content | Unchanged documents |
1201
-
1202
- ```typescript
1203
- // First run: indexes everything
1204
- await brain.index(); // → { indexed: 500, skipped: 0 }
1205
-
1206
- // Second run: skips everything unchanged
1207
- await brain.index(); // → { indexed: 0, skipped: 500 }
1208
-
1209
- // Changed 1 file? Only that file re-indexes
1210
- await brain.index(); // → { indexed: 1, skipped: 499 }
1211
- ```
1212
-
1213
- Use `--force` to re-index everything:
1214
-
1215
- ```bash
1216
- brainbank index --force
1217
- ```
1218
-
1219
- ### Re-embedding
1220
-
1221
- When switching embedding providers (e.g. Local → OpenAI), you **don't need to re-index**. The `reembed()` method regenerates only the vectors — no file I/O, no git parsing, no re-chunking:
1222
-
1223
- ```typescript
1224
- import { BrainBank, OpenAIEmbedding } from 'brainbank';
1225
-
1226
- // Previously indexed with local embeddings.
1227
- // Now switch to OpenAI:
1228
- const brain = new BrainBank({
1229
- embeddingProvider: new OpenAIEmbedding(),
1230
- });
1231
-
1232
- // force: true bypasses the dimension mismatch check for recovery
1233
- await brain.initialize({ force: true });
1234
-
1235
- const result = await brain.reembed({
1236
- onProgress: (table, current, total) => {
1237
- console.log(`${table}: ${current}/${total}`);
1238
- },
1239
- });
1240
- // → { code: 1200, git: 500, docs: 80, kv: 45, notes: 12, total: 1837 }
1241
- ```
1242
-
1243
- Or from the CLI:
1244
-
1245
- ```bash
1246
- brainbank reembed
1247
- ```
1248
-
1249
- | Full re-index | `reembed()` |
1250
- |---|---|
1251
- | Walks all files | **Skipped** |
1252
- | Parses git history | **Skipped** |
1253
- | Re-chunks documents | **Skipped** |
1254
- | Embeds text | ✓ |
1255
- | Replaces vectors | ✓ |
1256
- | Rebuilds HNSW | ✓ |
1257
-
1258
- > BrainBank tracks provider metadata in `embedding_meta` table. It auto-detects mismatches and warns you to run `reembed()`.
116
+ > ¹ Requires `OPENAI_API_KEY`. RAG also requires `PERPLEXITY_API_KEY`.
1259
117
 
1260
118
  ---
1261
119
 
1262
120
  ## Benchmarks
1263
121
 
1264
- BrainBank includes benchmark scripts to validate chunking quality and search relevance. Run them against your own codebase to see the impact.
1265
-
1266
- ### Search Quality: AST vs Sliding Window
1267
-
1268
- We compared BrainBank's **tree-sitter AST chunker** against the traditional **sliding window** (80-line blocks) on a production NestJS backend (3,753 lines across 8 service files). Both strategies chunk the same files; all chunks are embedded and searched with the same 10 domain-specific queries.
1269
-
1270
- #### How It Works
1271
-
1272
- ```
1273
- Sliding Window Tree-Sitter AST
1274
- ┌────────────────────┐ ┌────────────────────┐
1275
- │ import { ... } │ │ ✓ constructor() │ → named chunk
1276
- │ @Injectable() │ → L1-80 block │ ✓ findAll() │ → named chunk
1277
- │ class JobsService {│ │ ✓ createJob() │ → named chunk
1278
- │ constructor() │ │ ✓ cancelJob() │ → named chunk
1279
- │ findAll() { ... }│ │ ✓ updateStatus() │ → named chunk
1280
- │ createJob() │ └────────────────────┘
1281
- │ ... │
1282
- │ ────────────────── │ overlaps ↕
1283
- │ cancelJob() │ → L75-155 block
1284
- │ updateStatus() │
1285
- │ ... │
1286
- └────────────────────┘
1287
- ```
1288
-
1289
- **Sliding window** mixes imports, constructors, and multiple methods into one embedding. Search for "cancel a job" and you get a generic block.
1290
- **AST chunking** gives each method its own embedding. Search for "cancel a job" → direct hit on `cancelJob()`.
1291
-
1292
- #### Results (Production NestJS Backend — 3,753 lines)
1293
-
1294
- Tested with 10 domain-specific queries on 8 service files (`orders.service.ts`, `bookings.service.ts`, `notifications.service.ts`, etc.):
1295
-
1296
- | Metric | Sliding Window | Tree-Sitter AST |
1297
- |--------|:-:|:-:|
1298
- | **Query Wins** | 0/10 | **8/10** (2 ties) |
1299
- | **Top-1 Relevant** | 3/10 | **8/10** |
1300
- | **Avg Precision@3** | 1.1/3 | **1.7/3** |
1301
- | **Avg Score Delta** | — | **+0.035** |
1302
-
1303
- #### Per-Query Breakdown
1304
-
1305
- | Query | SW Top Result | AST Top Result | Δ Score |
1306
- |-------|:---:|:---:|:---:|
1307
- | cancel an order | generic `L451-458` | **`updateOrderStatus`** | +0.005 |
1308
- | create a booking | generic `L451-458` | **`createInstantBooking`** | +0.068 |
1309
- | confirm booking | generic `L451-458` | **`confirm`** | +0.034 |
1310
- | send notification | generic `L226-305` | **`publishNotificationEvent`** | +0.034 |
1311
- | authenticate JWT | generic `L1-80` | **`AuthModule`** | +0.032 |
1312
- | tenant DB connection | `L76-155` | **`onModuleDestroy`** | +0.037 |
1313
- | list orders paginated | `L76-155` | **`findAllActive`** | +0.045 |
1314
- | reject booking | generic `L451-458` | **`reject`** | +0.090 |
1315
-
1316
- > Notice how the sliding window returns the **same generic block `L451-458`** for 4 different queries. The AST chunker returns a different, correctly named method each time.
1317
-
1318
- #### Chunk Quality Comparison
1319
-
1320
- | | Sliding Window | Tree-Sitter AST |
1321
- |---|:-:|:-:|
1322
- | Total chunks | 53 | **83** |
1323
- | Avg lines/chunk | 75 | **39** |
1324
- | Named chunks | 0 | **83** (100%) |
1325
- | Chunk types | `block` | `method`, `interface`, `class` |
1326
-
1327
- ### Grammar Support
1328
-
1329
- All 9 core grammars verified, each parsing in **<0.05ms**:
1330
-
1331
- | Language | AST Nodes Extracted | Parse Time |
1332
- |----------|:---:|:---:|
1333
- | TypeScript | `export_statement`, `interface_declaration` | 0.04ms |
1334
- | JavaScript | `function_declaration` × 3 | 0.04ms |
1335
- | Python | `class_definition`, `function_definition` × 2 | 0.03ms |
1336
- | Go | `function_declaration`, `method_declaration` × 3 | 0.04ms |
1337
- | Rust | `struct_item`, `impl_item`, `function_item` | 0.03ms |
1338
- | Ruby | `class`, `method` | 0.03ms |
1339
- | Java | `class_declaration` | 0.02ms |
1340
- | C | `function_definition` × 3 | 0.05ms |
1341
- | PHP | `class_declaration` | 0.03ms |
1342
-
1343
- > Additional grammars available: C++, Swift, C#, Kotlin, Scala, Lua, Elixir, Bash, HTML, CSS
1344
-
1345
- ### RAG Retrieval Quality
1346
-
1347
- BrainBank's hybrid search pipeline (Vector + BM25 → RRF) with Perplexity Context embeddings (2560d):
122
+ Early benchmarks on Apple Silicon single SQLite file, no external vector DB.
1348
123
 
1349
- | Benchmark | Metric | Score |
1350
- |---|---|:---:|
1351
- | **BEIR SciFact** (5,183 docs, 300 queries) | NDCG@10 | **0.761** |
1352
- | **Custom semantic** (69 docs, 20 queries) | R@5 | **83%** |
124
+ | Benchmark | Corpus | Metric | Score |
125
+ |-----------|--------|--------|:-----:|
126
+ | [BEIR SciFact](https://github.com/beir-cellar/beir) | 5,183 scientific abstracts, 300 queries | NDCG@10 | **0.761** |
127
+ | Custom RAG eval | 127 Pinecall.io docs, 20 queries — 1 miss | R@5 | **83%** |
1353
128
 
1354
- The hybrid pipeline improved R@5 by **+26pp over vector-only** retrieval on our custom eval.
129
+ **Pipeline progression** each stage's impact on the custom eval:
1355
130
 
1356
- See **[BENCHMARKS.md](./BENCHMARKS.md)** for full pipeline progression, per-technique impact, and reproduction instructions.
131
+ | Stage | R@5 | Δ |
132
+ |-------|:---:|---|
133
+ | Vector-only (HNSW) | 57% | — |
134
+ | + BM25 → RRF | 78% | +21pp |
135
+ | + Qwen3 reranker | 83% | +5pp |
1357
136
 
1358
- #### Running the RAG Eval
137
+ > More benchmarks (code+graph retrieval, large-scale stress tests, multi-provider comparisons) are in progress.
138
+ > Full methodology and reproduction commands → [docs/benchmarks.md](docs/benchmarks.md)
1359
139
 
1360
- ```bash
1361
- # Custom eval on your own docs
1362
- PERPLEXITY_API_KEY=pplx-... npx tsx test/benchmarks/rag/eval.ts --docs ~/path/to/docs
1363
-
1364
- # BEIR standard benchmark
1365
- PERPLEXITY_API_KEY=pplx-... npx tsx test/benchmarks/rag/beir-eval.ts --dataset scifact
1366
- ```
1367
-
1368
- ### Running Benchmarks
140
+ ## Contributing
1369
141
 
1370
- ```bash
1371
- # Grammar support (9 languages, parse speed)
1372
- node test/benchmarks/grammar-support.mjs
1373
-
1374
- # Search quality A/B (uses BrainBank's own source files)
1375
- node test/benchmarks/search-quality.mjs
1376
-
1377
- # RAG retrieval quality (requires Perplexity API key + docs folder)
1378
- PERPLEXITY_API_KEY=pplx-... npx tsx test/benchmarks/rag/eval.ts --docs ~/path/to/docs
1379
- ```
1380
-
1381
- ---
1382
-
1383
- ## Architecture
1384
-
1385
- <details>
1386
- <summary>Text version</summary>
1387
-
1388
- ```
1389
- ┌──────────────────────────────────────────────────────┐
1390
- │ BrainBank Core │
1391
- │ .use(code) .use(git) .use(docs) │
1392
- │ .collection('name') │
1393
- ├──────────────────────────────────────────────────────┤
1394
- │ │
1395
- │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────────┐│
1396
- │ │ Code │ │ Git │ │ Docs │ │ Collection ││
1397
- │ │ Plugin │ │ Indexer │ │ Indexer │ │ (dynamic) ││
1398
- │ └────┬────┘ └────┬────┘ └────┬────┘ └─────┬──────┘│
1399
- │ │ │ │ │ │
1400
- │ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌─────▼──────┐│
1401
- │ │ HNSW │ │ HNSW │ │ HNSW │ │ Shared KV ││
1402
- │ │ Index │ │ Index │ │ Index │ │ HNSW Index ││
1403
- │ └─────────┘ └─────────┘ └─────────┘ └────────────┘│
1404
- │ │
1405
- │ ┌──────────────────────────────────────────────────┐│
1406
- │ │ SQLite (.brainbank/brainbank.db) ││
1407
- │ │ code_chunks │ git_commits │ doc_chunks ││
1408
- │ │ kv_data │ FTS5 full-text │ vectors │ co_edits ││
1409
- │ └──────────────────────────────────────────────────┘│
1410
- │ │
1411
- │ ┌──────────────────────────────────────────────────┐│
1412
- │ │ Embedding (Local 384d│OpenAI 1536d│Perplexity) ││
1413
- │ └──────────────────────────────────────────────────┘│
1414
- │ ┌──────────────────────────────────────────────────┐│
1415
- │ │ Qwen3-Reranker (opt-in cross-encoder) ││
1416
- │ └──────────────────────────────────────────────────┘│
1417
- └──────────────────────────────────────────────────────┘
1418
- ```
1419
- </details>
1420
-
1421
- ### Search Pipeline
1422
-
1423
- ```
1424
- Query
1425
-
1426
- ├──► Vector Search (HNSW k-NN) ──► candidates
1427
- ├──► Keyword Search (BM25/FTS5) ──► candidates
1428
-
1429
-
1430
- Reciprocal Rank Fusion (RRF, k=60)
1431
-
1432
-
1433
- Qwen3-Reranker (yes/no + logprobs → score 0-1)
1434
-
1435
-
1436
- Position-Aware Blend
1437
- Top 1-3: 75% RRF / 25% reranker
1438
- Top 4-10: 60% RRF / 40% reranker
1439
- Top 11+: 40% RRF / 60% reranker
1440
-
1441
-
1442
- Final results (sorted by blended score)
1443
- ```
1444
-
1445
- ### Data Flow
1446
-
1447
- 1. **Index** — Plugins parse files into chunks (tree-sitter AST for code, heading-based for docs)
1448
- 2. **Embed** — Each chunk gets a vector (local WASM or OpenAI)
1449
- 3. **Store** — Chunks + vectors → SQLite, vectors → HNSW index
1450
- 4. **Search** — Query → HNSW k-NN + BM25 keyword → RRF fusion → optional reranker
1451
- 5. **Context** — Top results formatted as markdown for system prompts
1452
-
1453
- ---
1454
-
1455
- ## Testing
1456
-
1457
- ```bash
1458
- npm test # Unit tests (172 tests)
1459
- npm test -- --integration # Full suite (includes real models + all domains)
1460
- npm test -- --filter code # Filter by test name
1461
- npm test -- --verbose # Show assertion details
1462
- ```
1463
-
1464
- ---
142
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
1465
143
 
1466
144
  ## License
1467
145
 
1468
- MIT
146
+ [MIT](LICENSE)