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.
- package/README.md +76 -1398
- package/bin/brainbank +5 -1
- package/dist/{chunk-N2OJRXSB.js → chunk-3HVCONGF.js} +1 -1
- package/dist/{chunk-N2OJRXSB.js.map → chunk-3HVCONGF.js.map} +1 -1
- package/dist/{chunk-CCXVL56V.js → chunk-3JZIM5AU.js} +6 -3
- package/dist/chunk-3JZIM5AU.js.map +1 -0
- package/dist/{chunk-6XOXM7MI.js → chunk-5KU2PP34.js} +2 -2
- package/dist/{chunk-6XOXM7MI.js.map → chunk-5KU2PP34.js.map} +1 -1
- package/dist/chunk-7JDCHUJV.js +89 -0
- package/dist/chunk-7JDCHUJV.js.map +1 -0
- package/dist/{chunk-B77KABWH.js → chunk-7T2ZCZQA.js} +17 -15
- package/dist/chunk-7T2ZCZQA.js.map +1 -0
- package/dist/chunk-E3J37GDA.js +74 -0
- package/dist/chunk-E3J37GDA.js.map +1 -0
- package/dist/chunk-JEFWMS5Z.js +217 -0
- package/dist/chunk-JEFWMS5Z.js.map +1 -0
- package/dist/chunk-JRVYSTMP.js +3256 -0
- package/dist/chunk-JRVYSTMP.js.map +1 -0
- package/dist/chunk-OLDHLOMT.js +69 -0
- package/dist/chunk-OLDHLOMT.js.map +1 -0
- package/dist/{chunk-424UFCY7.js → chunk-QTZNB6AK.js} +6 -2
- package/dist/chunk-QTZNB6AK.js.map +1 -0
- package/dist/chunk-RFF7HMP6.js +109 -0
- package/dist/chunk-RFF7HMP6.js.map +1 -0
- package/dist/{chunk-ZNLN2VWV.js → chunk-TD3TEFI3.js} +1 -1
- package/dist/chunk-TD3TEFI3.js.map +1 -0
- package/dist/cli.js +1036 -341
- package/dist/cli.js.map +1 -1
- package/dist/haiku-expander-WOVJIVXD.js +8 -0
- package/dist/haiku-pruner-DB77ZQLJ.js +8 -0
- package/dist/http-server-GIRELCCL.js +9 -0
- package/dist/index.d.ts +1774 -611
- package/dist/index.js +282 -70
- package/dist/index.js.map +1 -1
- package/dist/{local-embedding-ZIMTK6PU.js → local-embedding-2RNCC5EU.js} +2 -2
- package/dist/{openai-embedding-VQZCZQYT.js → openai-embedding-Z5I4K4CN.js} +2 -2
- package/dist/perplexity-context-embedding-V5YUMXDR.js +9 -0
- package/dist/{perplexity-embedding-227WQY4R.js → perplexity-embedding-X2S72OAC.js} +2 -2
- package/dist/plugin-FF4Q34TI.js +32 -0
- package/dist/{qwen3-reranker-3MHEENT5.js → qwen3-reranker-HVIQOLKS.js} +2 -2
- package/dist/{resolve-CUJWY6HP.js → resolve-Q5D6HECY.js} +2 -2
- package/package.json +25 -52
- package/src/brainbank.ts +620 -0
- package/src/cli/commands/collection.ts +77 -0
- package/src/cli/commands/context.ts +171 -0
- package/src/cli/commands/daemon.ts +100 -0
- package/src/cli/commands/docs.ts +71 -0
- package/src/cli/commands/files.ts +69 -0
- package/src/cli/commands/help.ts +72 -0
- package/src/cli/commands/index.ts +282 -0
- package/src/cli/commands/kv.ts +140 -0
- package/src/cli/commands/mcp.ts +13 -0
- package/src/cli/commands/reembed.ts +30 -0
- package/src/cli/commands/scan.ts +365 -0
- package/src/cli/commands/search.ts +130 -0
- package/src/cli/commands/stats.ts +44 -0
- package/src/cli/commands/status.ts +47 -0
- package/src/cli/commands/watch.ts +43 -0
- package/src/cli/factory/brain-context.ts +43 -0
- package/src/cli/factory/builtin-registration.ts +123 -0
- package/src/cli/factory/config-loader.ts +72 -0
- package/src/cli/factory/index.ts +65 -0
- package/src/cli/factory/plugin-loader.ts +146 -0
- package/src/cli/index.ts +63 -0
- package/src/cli/server-client.ts +135 -0
- package/src/cli/utils.ts +121 -0
- package/src/config.ts +50 -0
- package/src/constants.ts +13 -0
- package/src/db/adapter.ts +112 -0
- package/src/db/metadata.ts +130 -0
- package/src/db/migrations.ts +66 -0
- package/src/db/sqlite-adapter.ts +208 -0
- package/src/db/tracker.ts +91 -0
- package/src/engine/index-api.ts +85 -0
- package/src/engine/reembed.ts +206 -0
- package/src/engine/search-api.ts +222 -0
- package/src/index.ts +159 -0
- package/src/lib/fts.ts +57 -0
- package/src/lib/languages.ts +180 -0
- package/src/lib/logger.ts +125 -0
- package/src/lib/math.ts +87 -0
- package/src/lib/provider-key.ts +20 -0
- package/src/lib/prune.ts +71 -0
- package/src/lib/rerank.ts +33 -0
- package/src/lib/rrf.ts +133 -0
- package/src/lib/write-lock.ts +108 -0
- package/src/plugin.ts +323 -0
- package/src/providers/embeddings/embedding-worker-thread.ts +95 -0
- package/src/providers/embeddings/embedding-worker.ts +141 -0
- package/src/providers/embeddings/local-embedding.ts +115 -0
- package/src/providers/embeddings/openai-embedding.ts +167 -0
- package/src/providers/embeddings/perplexity-context-embedding.ts +195 -0
- package/src/providers/embeddings/perplexity-embedding.ts +165 -0
- package/src/providers/embeddings/resolve.ts +34 -0
- package/src/providers/pruners/haiku-expander.ts +152 -0
- package/src/providers/pruners/haiku-pruner.ts +112 -0
- package/src/providers/rerankers/qwen3-reranker.ts +180 -0
- package/src/providers/vector/hnsw-index.ts +174 -0
- package/src/providers/vector/hnsw-loader.ts +129 -0
- package/src/search/bm25-boost.ts +61 -0
- package/src/search/context-builder.ts +298 -0
- package/src/search/keyword/composite-bm25-search.ts +62 -0
- package/src/search/types.ts +35 -0
- package/src/search/vector/composite-vector-search.ts +76 -0
- package/src/search/vector/mmr.ts +64 -0
- package/src/services/collection.ts +405 -0
- package/src/services/daemon.ts +87 -0
- package/src/services/http-server.ts +288 -0
- package/src/services/kv-service.ts +65 -0
- package/src/services/plugin-registry.ts +109 -0
- package/src/services/watch.ts +348 -0
- package/src/services/webhook-server.ts +100 -0
- package/src/types.ts +504 -0
- package/dist/base-3SNc_CeY.d.ts +0 -593
- package/dist/chunk-424UFCY7.js.map +0 -1
- package/dist/chunk-7EZR47JV.js +0 -232
- package/dist/chunk-7EZR47JV.js.map +0 -1
- package/dist/chunk-B77KABWH.js.map +0 -1
- package/dist/chunk-CCXVL56V.js.map +0 -1
- package/dist/chunk-DI3H6JVZ.js +0 -2432
- package/dist/chunk-DI3H6JVZ.js.map +0 -1
- package/dist/chunk-FGL32LUJ.js +0 -754
- package/dist/chunk-FGL32LUJ.js.map +0 -1
- package/dist/chunk-JRSKWF6K.js +0 -313
- package/dist/chunk-JRSKWF6K.js.map +0 -1
- package/dist/chunk-U2Q2XGPZ.js +0 -42
- package/dist/chunk-U2Q2XGPZ.js.map +0 -1
- package/dist/chunk-VQ27YUHH.js +0 -629
- package/dist/chunk-VQ27YUHH.js.map +0 -1
- package/dist/chunk-VVXYZIIB.js +0 -304
- package/dist/chunk-VVXYZIIB.js.map +0 -1
- package/dist/chunk-YOLKSYWK.js +0 -79
- package/dist/chunk-YOLKSYWK.js.map +0 -1
- package/dist/chunk-ZNLN2VWV.js.map +0 -1
- package/dist/code.d.ts +0 -33
- package/dist/code.js +0 -9
- package/dist/docs.d.ts +0 -21
- package/dist/docs.js +0 -9
- package/dist/git.d.ts +0 -33
- package/dist/git.js +0 -9
- package/dist/memory.d.ts +0 -17
- package/dist/memory.js +0 -9
- package/dist/notes.d.ts +0 -17
- package/dist/notes.js +0 -10
- package/dist/perplexity-context-embedding-KSVSZXMD.js +0 -9
- package/dist/resolve-CUJWY6HP.js.map +0 -1
- /package/dist/{code.js.map → haiku-expander-WOVJIVXD.js.map} +0 -0
- /package/dist/{docs.js.map → haiku-pruner-DB77ZQLJ.js.map} +0 -0
- /package/dist/{git.js.map → http-server-GIRELCCL.js.map} +0 -0
- /package/dist/{local-embedding-ZIMTK6PU.js.map → local-embedding-2RNCC5EU.js.map} +0 -0
- /package/dist/{memory.js.map → openai-embedding-Z5I4K4CN.js.map} +0 -0
- /package/dist/{notes.js.map → perplexity-context-embedding-V5YUMXDR.js.map} +0 -0
- /package/dist/{openai-embedding-VQZCZQYT.js.map → perplexity-embedding-X2S72OAC.js.map} +0 -0
- /package/dist/{perplexity-context-embedding-KSVSZXMD.js.map → plugin-FF4Q34TI.js.map} +0 -0
- /package/dist/{perplexity-embedding-227WQY4R.js.map → qwen3-reranker-HVIQOLKS.js.map} +0 -0
- /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
|
-
- **
|
|
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
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-

|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
27
|
+
> If you get `ERESOLVE` errors, use `npm i --legacy-peer-deps` — tree-sitter grammars have overlapping peer dep ranges.
|
|
95
28
|
|
|
96
|
-
|
|
29
|
+
### CLI — zero code
|
|
97
30
|
|
|
98
31
|
```bash
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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();
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
66
|
+
### Indexer Plugins
|
|
550
67
|
|
|
551
|
-
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
76
|
+
### Integrations
|
|
563
77
|
|
|
564
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
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
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
1352
|
-
|
|
|
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
|
-
|
|
129
|
+
**Pipeline progression** — each stage's impact on the custom eval:
|
|
1355
130
|
|
|
1356
|
-
|
|
131
|
+
| Stage | R@5 | Δ |
|
|
132
|
+
|-------|:---:|---|
|
|
133
|
+
| Vector-only (HNSW) | 57% | — |
|
|
134
|
+
| + BM25 → RRF | 78% | +21pp |
|
|
135
|
+
| + Qwen3 reranker | 83% | +5pp |
|
|
1357
136
|
|
|
1358
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|