nano-brain 2026.3.21 → 2026.3.23
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/AGENTS.md +0 -5
- package/README.md +410 -106
- package/package.json +1 -1
- package/src/reranker.ts +5 -0
- package/src/server.ts +5 -2
- package/src/store.ts +8 -1
package/AGENTS.md
CHANGED
|
@@ -19,11 +19,6 @@ All commands use the CLI via Bash tool:
|
|
|
19
19
|
|
|
20
20
|
### Session Workflow
|
|
21
21
|
|
|
22
|
-
**Start of session:** Check memory for relevant past context before exploring the codebase.
|
|
23
|
-
```
|
|
24
|
-
npx nano-brain query "what have we done regarding {current task topic}"
|
|
25
|
-
```
|
|
26
|
-
|
|
27
22
|
**End of session:** Save key decisions, patterns discovered, and debugging insights.
|
|
28
23
|
```bash
|
|
29
24
|
cat > ~/.nano-brain/memory/$(date +%Y-%m-%d)-summary.md << 'EOF'
|
package/README.md
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
# nano-brain
|
|
2
2
|
|
|
3
|
-
Persistent memory
|
|
3
|
+
Persistent memory and code intelligence for AI coding agents.
|
|
4
4
|
|
|
5
5
|
## What It Does
|
|
6
6
|
|
|
7
|
-
An MCP server that gives AI coding agents persistent memory across sessions. Indexes markdown documents, past sessions,
|
|
7
|
+
An MCP server that gives AI coding agents persistent memory across sessions. Indexes markdown documents, past sessions, daily logs, and codebase symbols into a searchable SQLite database with FTS5 and vector embeddings. Provides 17 MCP tools for search, retrieval, code intelligence, and memory management using a hybrid search pipeline with RRF fusion and VoyageAI neural reranking.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Hybrid search pipeline** — BM25 + vector + RRF fusion + VoyageAI neural reranking with 6 ranking signals
|
|
12
|
+
- **Code intelligence** — symbol graph, call flow detection, impact analysis, change detection via Tree-sitter AST
|
|
13
|
+
- **Automatic data ingestion** — session harvesting (2min poll), file watching (chokidar), codebase indexing
|
|
14
|
+
- **Multi-workspace isolation** — per-workspace SQLite databases, cross-workspace search with `--scope=all`
|
|
15
|
+
- **Flexible embedding providers** — VoyageAI, Ollama, OpenAI-compatible
|
|
16
|
+
- **Dual vector stores** — Qdrant (production) or sqlite-vec (embedded)
|
|
17
|
+
- **Privacy-first** — 100% local processing option, your code never leaves your machine
|
|
18
|
+
- **MCP + CLI** — stdio/HTTP/SSE transports for local or containerized environments
|
|
8
19
|
|
|
9
20
|
Inspired by [QMD](https://github.com/tobi/qmd) and [OpenClaw](https://github.com/openclaw/openclaw).
|
|
10
21
|
|
|
@@ -15,7 +26,7 @@ User Query
|
|
|
15
26
|
│
|
|
16
27
|
▼
|
|
17
28
|
┌─────────────────┐
|
|
18
|
-
│ Query Expansion │ ←
|
|
29
|
+
│ Query Expansion │ ← (currently stubbed, planned)
|
|
19
30
|
│ (optional) │ generates 2-3 query variants
|
|
20
31
|
└────────┬────────┘
|
|
21
32
|
│
|
|
@@ -23,7 +34,9 @@ User Query
|
|
|
23
34
|
▼ ▼
|
|
24
35
|
┌────────┐ ┌──────────┐
|
|
25
36
|
│ BM25 │ │ Vector │
|
|
26
|
-
│ (FTS5) │ │(
|
|
37
|
+
│ (FTS5) │ │ (Qdrant │
|
|
38
|
+
│ │ │ or │
|
|
39
|
+
│ │ │ sqlite- │
|
|
27
40
|
│ │ │ vec) │
|
|
28
41
|
└───┬────┘ └────┬─────┘
|
|
29
42
|
│ │
|
|
@@ -35,8 +48,20 @@ User Query
|
|
|
35
48
|
│
|
|
36
49
|
▼
|
|
37
50
|
┌─────────────────┐
|
|
38
|
-
│
|
|
39
|
-
│
|
|
51
|
+
│ PageRank Boost │ ← Centrality from file dependency graph
|
|
52
|
+
│ │ weight: 0.1 (default)
|
|
53
|
+
└────────┬────────┘
|
|
54
|
+
│
|
|
55
|
+
▼
|
|
56
|
+
┌─────────────────┐
|
|
57
|
+
│ Supersede │ ← 0.3× demotion for replaced documents
|
|
58
|
+
│ Demotion │
|
|
59
|
+
└────────┬────────┘
|
|
60
|
+
│
|
|
61
|
+
▼
|
|
62
|
+
┌─────────────────┐
|
|
63
|
+
│ Neural Reranking│ ← VoyageAI rerank-2.5-lite
|
|
64
|
+
│ (optional) │
|
|
40
65
|
└────────┬────────┘
|
|
41
66
|
│
|
|
42
67
|
▼
|
|
@@ -49,16 +74,95 @@ User Query
|
|
|
49
74
|
Final Results
|
|
50
75
|
```
|
|
51
76
|
|
|
52
|
-
##
|
|
77
|
+
## Search Pipeline (3 Tiers)
|
|
78
|
+
|
|
79
|
+
**`memory_search`** — BM25 only (fast, exact keyword matching)
|
|
80
|
+
|
|
81
|
+
**`memory_vsearch`** — Vector only (semantic similarity via embeddings)
|
|
53
82
|
|
|
54
|
-
|
|
83
|
+
**`memory_query`** — Full hybrid pipeline with 6 ranking signals:
|
|
55
84
|
|
|
56
|
-
- **
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
1. **BM25 full-text scoring** — SQLite FTS5 with porter stemming
|
|
86
|
+
2. **Vector cosine similarity** — Qdrant or sqlite-vec embeddings
|
|
87
|
+
3. **RRF fusion** — k=60, original query weighted 2×
|
|
88
|
+
4. **PageRank centrality boost** — from file dependency graph (weight: 0.1)
|
|
89
|
+
5. **Supersede demotion** — 0.3× penalty for replaced documents
|
|
90
|
+
6. **VoyageAI neural reranking** — rerank-2.5-lite with position-aware blending:
|
|
91
|
+
- Top 3 results: 75% RRF / 25% rerank
|
|
92
|
+
- Ranks 4-10: 60% RRF / 40% rerank
|
|
93
|
+
- Ranks 11+: 40% RRF / 60% rerank
|
|
60
94
|
|
|
61
|
-
|
|
95
|
+
Query expansion generates 2-3 query variants before search. The pipeline supports it, but no expansion provider is currently active.
|
|
96
|
+
|
|
97
|
+
## Code Intelligence
|
|
98
|
+
|
|
99
|
+
Built on Tree-sitter AST parsing for TypeScript, JavaScript, and Python:
|
|
100
|
+
|
|
101
|
+
**`code_context`** — 360° view of a code symbol:
|
|
102
|
+
- Direct callers and callees
|
|
103
|
+
- Transitive call flows (upstream/downstream)
|
|
104
|
+
- File location, definition, and references
|
|
105
|
+
- Centrality score (PageRank) and cluster membership
|
|
106
|
+
|
|
107
|
+
**`code_impact`** — Change impact analysis:
|
|
108
|
+
- Upstream dependencies (what calls this?)
|
|
109
|
+
- Downstream dependencies (what does this call?)
|
|
110
|
+
- BFS traversal with configurable depth
|
|
111
|
+
- Risk assessment for refactoring
|
|
112
|
+
|
|
113
|
+
**`code_detect_changes`** — Map git diff to affected symbols:
|
|
114
|
+
- Parses `git diff` output
|
|
115
|
+
- Identifies modified symbols via Tree-sitter
|
|
116
|
+
- Returns symbol names, types, and file locations
|
|
117
|
+
- Scope: `staged`, `unstaged`, or `all`
|
|
118
|
+
|
|
119
|
+
**`memory_focus`** — File dependency context:
|
|
120
|
+
- Import/export graph for a file
|
|
121
|
+
- Centrality score (PageRank)
|
|
122
|
+
- Cluster membership (Louvain algorithm)
|
|
123
|
+
- Direct dependencies and dependents
|
|
124
|
+
|
|
125
|
+
**`memory_graph_stats`** — Dependency graph overview:
|
|
126
|
+
- Total files, symbols, edges
|
|
127
|
+
- Cycle detection
|
|
128
|
+
- Clustering coefficient
|
|
129
|
+
- Top central files
|
|
130
|
+
|
|
131
|
+
**Symbol tracking** — Cross-repo symbol queries:
|
|
132
|
+
- Redis keys, PubSub channels
|
|
133
|
+
- MySQL tables, columns
|
|
134
|
+
- API endpoints (Express, FastAPI)
|
|
135
|
+
- Bull/BullMQ queues
|
|
136
|
+
- GraphQL types, queries, mutations
|
|
137
|
+
|
|
138
|
+
## Data Ingestion
|
|
139
|
+
|
|
140
|
+
All data sources are indexed automatically:
|
|
141
|
+
|
|
142
|
+
**Session harvesting** — Converts OpenCode JSON sessions into searchable markdown:
|
|
143
|
+
- Polls `~/.opencode/sessions/` every 2 minutes
|
|
144
|
+
- Extracts user queries, assistant responses, tool calls
|
|
145
|
+
- Incremental append (hash-based deduplication)
|
|
146
|
+
|
|
147
|
+
**File watching** — Monitors collections for changes:
|
|
148
|
+
- Chokidar watches configured directories
|
|
149
|
+
- Dirty-flag tracking for incremental updates
|
|
150
|
+
- Reindexes every 5 minutes if changes detected
|
|
151
|
+
|
|
152
|
+
**Codebase indexing** — Tree-sitter AST → symbol graph:
|
|
153
|
+
- Parses TS/JS/Python files
|
|
154
|
+
- Extracts functions, classes, methods, variables
|
|
155
|
+
- Builds call graph (caller → callee edges)
|
|
156
|
+
- Computes PageRank centrality
|
|
157
|
+
- Detects clusters via Louvain algorithm
|
|
158
|
+
- Identifies call flows (entry points → leaf functions)
|
|
159
|
+
|
|
160
|
+
**Incremental behavior**:
|
|
161
|
+
- Hash-based file skipping (SHA-256 content addressing)
|
|
162
|
+
- Adaptive embedding backoff (exponential retry)
|
|
163
|
+
- Batch processing for large codebases
|
|
164
|
+
|
|
165
|
+
## Chunking Strategy
|
|
62
166
|
|
|
63
167
|
Heading-aware markdown chunking that respects document structure:
|
|
64
168
|
|
|
@@ -66,60 +170,86 @@ Heading-aware markdown chunking that respects document structure:
|
|
|
66
170
|
- **Overlap:** 15% between chunks (~540 characters)
|
|
67
171
|
- **Respects boundaries:** Code fences, headings, paragraphs
|
|
68
172
|
- **Break point scoring:** h1=100, h2=90, h3=80, code-fence=80, hr=60, blank-line=40
|
|
173
|
+
- **Content-addressed storage:** SHA-256 hash deduplication
|
|
69
174
|
|
|
70
|
-
|
|
175
|
+
## Storage & Infrastructure
|
|
71
176
|
|
|
72
|
-
|
|
177
|
+
**SQLite** (via better-sqlite3):
|
|
178
|
+
- `documents` — metadata, content, embeddings
|
|
179
|
+
- `chunks` — heading-aware markdown chunks (900 tokens, 15% overlap)
|
|
180
|
+
- `fts_index` — FTS5 virtual table with porter stemming
|
|
181
|
+
- `vec_index` — sqlite-vec extension (cosine distance)
|
|
182
|
+
- `symbols` — code symbols (functions, classes, variables)
|
|
183
|
+
- `call_edges` — caller → callee relationships
|
|
184
|
+
- `file_deps` — import/export graph
|
|
185
|
+
- `clusters` — Louvain clustering results
|
|
186
|
+
- `flows` — detected call flows (entry → leaf)
|
|
73
187
|
|
|
74
|
-
|
|
188
|
+
**Qdrant** (optional, production vector store):
|
|
189
|
+
- Managed via `qdrant up/down/status/migrate/verify/activate/cleanup` commands
|
|
190
|
+
- Docker-based deployment
|
|
191
|
+
- Automatic migration from sqlite-vec
|
|
192
|
+
- Verification and cleanup tools
|
|
75
193
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
4. LLM reranking with bge-reranker-v2-m3 (optional)
|
|
81
|
-
5. Position-aware blending:
|
|
82
|
-
- Top 3 results: 75% RRF / 25% rerank
|
|
83
|
-
- Ranks 4-10: 60% RRF / 40% rerank
|
|
84
|
-
- Ranks 11+: 40% RRF / 60% rerank
|
|
194
|
+
**Embedding providers**:
|
|
195
|
+
- **VoyageAI** — voyage-code-3 (1024 dims, code-optimized)
|
|
196
|
+
- **Ollama** — local models (nomic-embed-text, etc.)
|
|
197
|
+
- **OpenAI-compatible** — Azure, LM Studio, custom endpoints
|
|
85
198
|
|
|
86
|
-
|
|
199
|
+
**Reranking**:
|
|
200
|
+
- **VoyageAI** — rerank-2.5-lite (neural reranking)
|
|
201
|
+
|
|
202
|
+
**Storage management**:
|
|
203
|
+
- Per-workspace SQLite databases (isolated)
|
|
204
|
+
- Content-addressed storage (SHA-256 deduplication)
|
|
205
|
+
- Retention policies (maxSize budget, auto-cleanup)
|
|
206
|
+
- Disk space checks before indexing
|
|
87
207
|
|
|
88
|
-
|
|
89
|
-
- **Auto-indexing** via chokidar file watcher
|
|
90
|
-
- **Incremental updates** using dirty-flag tracking
|
|
91
|
-
- **Session harvesting** converts OpenCode JSON sessions into searchable markdown
|
|
208
|
+
## MCP Tools (17 Total)
|
|
92
209
|
|
|
93
|
-
|
|
210
|
+
### Search & Retrieval
|
|
94
211
|
|
|
95
212
|
| Tool | Description |
|
|
96
213
|
|------|-------------|
|
|
97
|
-
| `memory_search` | BM25 keyword search (fast) |
|
|
98
|
-
| `memory_vsearch` | Semantic vector search |
|
|
99
|
-
| `memory_query` | Full hybrid search
|
|
214
|
+
| `memory_search` | BM25 keyword search (fast, exact matching) |
|
|
215
|
+
| `memory_vsearch` | Semantic vector search (embeddings) |
|
|
216
|
+
| `memory_query` | Full hybrid search (BM25 + vector + RRF + reranking) |
|
|
100
217
|
| `memory_get` | Retrieve document by path or docid (#abc123) |
|
|
101
218
|
| `memory_multi_get` | Batch retrieve by glob pattern |
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
219
|
+
|
|
220
|
+
### Memory Management
|
|
221
|
+
|
|
222
|
+
| Tool | Description |
|
|
223
|
+
|------|-------------|
|
|
224
|
+
| `memory_write` | Write to daily log (supports tags, supersedes) |
|
|
225
|
+
| `memory_tags` | List all tags with document counts |
|
|
226
|
+
| `memory_status` | Index health, collections, model status, graph stats |
|
|
105
227
|
| `memory_update` | Trigger reindex of all collections |
|
|
228
|
+
|
|
229
|
+
### Code Intelligence
|
|
230
|
+
|
|
231
|
+
| Tool | Description |
|
|
232
|
+
|------|-------------|
|
|
233
|
+
| `code_context` | 360° view of a code symbol (callers, callees, flows, centrality) |
|
|
234
|
+
| `code_impact` | Change impact analysis (upstream/downstream BFS) |
|
|
235
|
+
| `code_detect_changes` | Map git diff to affected symbols (staged/unstaged/all) |
|
|
236
|
+
| `memory_index_codebase` | Index codebase files in current workspace (Tree-sitter AST) |
|
|
237
|
+
|
|
238
|
+
### Dependency Graph
|
|
239
|
+
|
|
240
|
+
| Tool | Description |
|
|
241
|
+
|------|-------------|
|
|
106
242
|
| `memory_focus` | File dependency context (imports/exports, centrality, cluster) |
|
|
107
|
-
| `memory_graph_stats` | Dependency graph overview
|
|
108
|
-
| `memory_symbols` | Cross-repo symbol query (Redis
|
|
243
|
+
| `memory_graph_stats` | Dependency graph overview (files, symbols, edges, cycles) |
|
|
244
|
+
| `memory_symbols` | Cross-repo symbol query (Redis, MySQL, API endpoints, queues) |
|
|
109
245
|
| `memory_impact` | Cross-repo impact analysis (writers vs readers) |
|
|
110
|
-
| `memory_tags` | List all tags with document counts |
|
|
111
|
-
| `code_context` | 360° view of a code symbol (callers, callees, flows) |
|
|
112
|
-
| `code_impact` | Change impact analysis (upstream/downstream dependencies) |
|
|
113
|
-
| `code_detect_changes` | Map git diff to affected symbols |
|
|
114
246
|
|
|
115
|
-
## Installation
|
|
247
|
+
## Installation & Quick Start
|
|
248
|
+
|
|
116
249
|
```bash
|
|
250
|
+
# Install globally
|
|
117
251
|
npm install -g nano-brain
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Quick Start
|
|
121
252
|
|
|
122
|
-
```bash
|
|
123
253
|
# Initialize (creates config, indexes codebase, generates embeddings)
|
|
124
254
|
npx nano-brain init --root=/path/to/your/project
|
|
125
255
|
|
|
@@ -127,7 +257,11 @@ npx nano-brain init --root=/path/to/your/project
|
|
|
127
257
|
npx nano-brain status
|
|
128
258
|
```
|
|
129
259
|
|
|
130
|
-
|
|
260
|
+
### MCP Configuration
|
|
261
|
+
|
|
262
|
+
Add to your AI agent's MCP config (e.g., `~/.config/opencode/opencode.json`):
|
|
263
|
+
|
|
264
|
+
**Local mode (stdio):**
|
|
131
265
|
```json
|
|
132
266
|
{
|
|
133
267
|
"mcp": {
|
|
@@ -140,7 +274,7 @@ Add to your AI agent's MCP config (e.g. `~/.config/opencode/opencode.json`):
|
|
|
140
274
|
}
|
|
141
275
|
```
|
|
142
276
|
|
|
143
|
-
|
|
277
|
+
**Remote mode (HTTP/SSE, for Docker/containers):**
|
|
144
278
|
```json
|
|
145
279
|
{
|
|
146
280
|
"mcp": {
|
|
@@ -153,9 +287,20 @@ For remote mode (e.g., running on host while AI agent runs in Docker):
|
|
|
153
287
|
}
|
|
154
288
|
```
|
|
155
289
|
|
|
290
|
+
Start the remote server:
|
|
291
|
+
```bash
|
|
292
|
+
npx nano-brain serve # Background daemon (port 3100)
|
|
293
|
+
npx nano-brain serve --foreground # Foreground (for debugging)
|
|
294
|
+
npx nano-brain serve status # Check if running
|
|
295
|
+
npx nano-brain serve stop # Stop daemon
|
|
296
|
+
```
|
|
297
|
+
|
|
156
298
|
## Configuration
|
|
299
|
+
|
|
157
300
|
Create `~/.nano-brain/config.yml` (auto-generated by `init`):
|
|
301
|
+
|
|
158
302
|
```yaml
|
|
303
|
+
# Collections (directories to index)
|
|
159
304
|
collections:
|
|
160
305
|
memory:
|
|
161
306
|
path: ~/.nano-brain/memory
|
|
@@ -166,93 +311,242 @@ collections:
|
|
|
166
311
|
pattern: "**/*.md"
|
|
167
312
|
update: auto
|
|
168
313
|
|
|
169
|
-
#
|
|
314
|
+
# Vector store (qdrant or sqlite-vec)
|
|
315
|
+
vector:
|
|
316
|
+
provider: qdrant
|
|
317
|
+
url: http://localhost:6333
|
|
318
|
+
collection: nano_brain
|
|
319
|
+
# OR: provider: sqlite-vec (embedded, no external service)
|
|
320
|
+
|
|
321
|
+
# Embedding provider
|
|
170
322
|
embedding:
|
|
171
|
-
provider: ollama
|
|
172
|
-
url:
|
|
173
|
-
model:
|
|
323
|
+
provider: openai # 'ollama' or 'openai' (OpenAI-compatible)
|
|
324
|
+
url: https://api.voyageai.com # VoyageAI, Azure, LM Studio, etc.
|
|
325
|
+
model: voyage-code-3
|
|
326
|
+
apiKey: ${VOYAGE_API_KEY}
|
|
327
|
+
# OR: provider: ollama, url: http://localhost:11434, model: nomic-embed-text
|
|
328
|
+
|
|
329
|
+
# Reranker (uses embedding.apiKey if not set separately)
|
|
330
|
+
reranker:
|
|
331
|
+
model: rerank-2.5-lite
|
|
332
|
+
# apiKey: ${VOYAGE_API_KEY} # optional, falls back to embedding.apiKey
|
|
333
|
+
|
|
334
|
+
# Codebase indexing
|
|
335
|
+
codebase:
|
|
336
|
+
enabled: true
|
|
337
|
+
languages: [typescript, javascript, python]
|
|
338
|
+
exclude: [node_modules, dist, build, .git]
|
|
339
|
+
maxFileSize: 1048576 # 1MB
|
|
340
|
+
|
|
341
|
+
# File watcher
|
|
342
|
+
watcher:
|
|
343
|
+
enabled: true
|
|
344
|
+
debounce: 300 # ms
|
|
345
|
+
reindexInterval: 300 # seconds (5 minutes)
|
|
346
|
+
|
|
347
|
+
# Search configuration
|
|
348
|
+
search:
|
|
349
|
+
rrf_k: 60
|
|
350
|
+
top_k: 30
|
|
351
|
+
expansion:
|
|
352
|
+
enabled: true
|
|
353
|
+
weight: 1
|
|
354
|
+
reranking:
|
|
355
|
+
enabled: true
|
|
356
|
+
blending:
|
|
357
|
+
top3:
|
|
358
|
+
rrf: 0.75
|
|
359
|
+
rerank: 0.25
|
|
360
|
+
mid:
|
|
361
|
+
rrf: 0.60
|
|
362
|
+
rerank: 0.40
|
|
363
|
+
tail:
|
|
364
|
+
rrf: 0.40
|
|
365
|
+
rerank: 0.60
|
|
366
|
+
centrality_weight: 0.1
|
|
367
|
+
supersede_demotion: 0.3
|
|
368
|
+
|
|
369
|
+
# Polling intervals
|
|
370
|
+
intervals:
|
|
371
|
+
sessionHarvest: 120 # seconds (2 minutes)
|
|
372
|
+
healthCheck: 60 # seconds
|
|
373
|
+
|
|
374
|
+
# Storage management
|
|
375
|
+
storage:
|
|
376
|
+
maxSize: 10737418240 # 10GB
|
|
377
|
+
retention:
|
|
378
|
+
sessions: 90 # days
|
|
379
|
+
logs: 30 # days
|
|
380
|
+
|
|
381
|
+
# Workspaces
|
|
382
|
+
workspaces:
|
|
383
|
+
isolation: true # Per-workspace SQLite databases
|
|
384
|
+
defaultScope: current # or 'all' for cross-workspace search
|
|
385
|
+
|
|
386
|
+
# Logging
|
|
387
|
+
logging:
|
|
388
|
+
level: info # debug, info, warn, error
|
|
389
|
+
file: ~/.nano-brain/logs/nano-brain.log
|
|
390
|
+
maxSize: 10485760 # 10MB
|
|
391
|
+
maxFiles: 5
|
|
174
392
|
```
|
|
175
393
|
|
|
176
|
-
**Collection options:**
|
|
177
|
-
- `path` — Directory to index
|
|
178
|
-
- `pattern` — Glob pattern for files
|
|
179
|
-
- `update` — `auto` (watch for changes) or `manual`
|
|
180
|
-
|
|
181
|
-
**Embedding options:**
|
|
182
|
-
- `provider` — `ollama` (default)
|
|
183
|
-
- `url` — Ollama API URL (auto-detected during `init`)
|
|
184
|
-
- `model` — Embedding model name (default: `nomic-embed-text`)
|
|
185
|
-
|
|
186
394
|
**Data directory layout (`~/.nano-brain/`):**
|
|
187
395
|
```
|
|
188
396
|
~/.nano-brain/
|
|
189
397
|
├── config.yml # Configuration
|
|
190
398
|
├── data/ # SQLite databases (per-workspace)
|
|
191
|
-
├── models/ # Embedding model cache
|
|
192
399
|
├── memory/ # Curated notes
|
|
193
|
-
|
|
400
|
+
├── sessions/ # Harvested sessions
|
|
401
|
+
└── logs/ # Application logs
|
|
194
402
|
```
|
|
195
403
|
|
|
196
|
-
## CLI
|
|
404
|
+
## CLI Commands (24 Total)
|
|
405
|
+
|
|
406
|
+
### Setup & Initialization
|
|
197
407
|
|
|
198
408
|
```bash
|
|
199
|
-
# Setup
|
|
200
409
|
nano-brain init # Full initialization (config, index, embed, AGENTS.md)
|
|
201
410
|
nano-brain init --root=/path # Initialize for specific project
|
|
411
|
+
nano-brain status # Show index health, collections, model status
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### MCP Server
|
|
202
415
|
|
|
203
|
-
|
|
416
|
+
```bash
|
|
204
417
|
nano-brain mcp # Start MCP server (stdio)
|
|
205
418
|
nano-brain mcp --http --port=3100 --host=0.0.0.0 # Start MCP server (HTTP/SSE)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Remote Server (Daemon)
|
|
206
422
|
|
|
207
|
-
|
|
423
|
+
```bash
|
|
208
424
|
nano-brain serve # Start SSE server as background daemon (port 3100)
|
|
209
425
|
nano-brain serve status # Check if server is running
|
|
210
426
|
nano-brain serve stop # Stop the daemon
|
|
211
427
|
nano-brain serve --foreground # Run in foreground (for debugging)
|
|
212
428
|
nano-brain serve --port=8080 # Custom port
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Search
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
nano-brain search "query" # BM25 keyword search
|
|
435
|
+
nano-brain vsearch "query" # Vector semantic search
|
|
436
|
+
nano-brain query "query" # Hybrid search (BM25 + vector + reranking)
|
|
437
|
+
nano-brain query "query" --tags=bug,fix # Filter by tags
|
|
438
|
+
nano-brain query "query" --scope=all # Cross-workspace search
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Memory Management
|
|
213
442
|
|
|
214
|
-
|
|
215
|
-
nano-brain
|
|
216
|
-
nano-brain
|
|
443
|
+
```bash
|
|
444
|
+
nano-brain write "content" # Write to daily log
|
|
445
|
+
nano-brain write "content" --tags=decision,architecture
|
|
446
|
+
nano-brain write "content" --supersedes=abc123 # Mark as replacement
|
|
447
|
+
nano-brain get <path> # Retrieve document by path
|
|
448
|
+
nano-brain get "#abc123" # Retrieve by docid
|
|
449
|
+
nano-brain tags # List all tags with counts
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Index Management
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
nano-brain update # Reindex all collections
|
|
456
|
+
nano-brain index-codebase # Index codebase in current workspace
|
|
457
|
+
nano-brain reset --confirm # Reset all data (requires confirmation)
|
|
458
|
+
nano-brain reset --dry-run # Preview what would be deleted
|
|
459
|
+
```
|
|
217
460
|
|
|
218
|
-
|
|
219
|
-
nano-brain search "query" # BM25 search
|
|
220
|
-
nano-brain vsearch "query" # Vector search
|
|
221
|
-
nano-brain query "query" # Hybrid search
|
|
461
|
+
### Collections
|
|
222
462
|
|
|
223
|
-
|
|
463
|
+
```bash
|
|
224
464
|
nano-brain collection add <name> <path> # Add collection
|
|
225
465
|
nano-brain collection remove <name> # Remove collection
|
|
226
466
|
nano-brain collection list # List collections
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Workspace Management
|
|
227
470
|
|
|
228
|
-
|
|
471
|
+
```bash
|
|
229
472
|
nano-brain rm --list # List all workspaces
|
|
230
473
|
nano-brain rm <workspace> --dry-run # Preview what would be deleted
|
|
231
474
|
nano-brain rm <workspace> # Remove workspace and all its data
|
|
232
475
|
# <workspace> can be: absolute path, hash prefix, or workspace name
|
|
233
476
|
```
|
|
234
477
|
|
|
478
|
+
### Qdrant Management
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
nano-brain qdrant up # Start Qdrant Docker container
|
|
482
|
+
nano-brain qdrant down # Stop Qdrant container
|
|
483
|
+
nano-brain qdrant status # Check Qdrant status
|
|
484
|
+
nano-brain qdrant migrate # Migrate from sqlite-vec to Qdrant
|
|
485
|
+
nano-brain qdrant verify # Verify Qdrant data integrity
|
|
486
|
+
nano-brain qdrant activate # Switch to Qdrant (update config)
|
|
487
|
+
nano-brain qdrant cleanup # Remove orphaned vectors
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Cache Management
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
nano-brain cache clear # Clear all caches
|
|
494
|
+
nano-brain cache clear --type=embeddings # Clear specific cache type
|
|
495
|
+
nano-brain cache stats # Show cache statistics
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Benchmarking
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
nano-brain bench # Run default benchmark suite
|
|
502
|
+
nano-brain bench --suite=search # Run specific suite
|
|
503
|
+
nano-brain bench --iterations=100 --json --save
|
|
504
|
+
nano-brain bench --compare=baseline.json # Compare with baseline
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Logging
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
nano-brain logs # Show recent logs (last 50 lines)
|
|
511
|
+
nano-brain logs -f # Tail logs in real-time
|
|
512
|
+
nano-brain logs -n 100 # Show last 100 lines
|
|
513
|
+
nano-brain logs --date=2026-03-01 # Show log for specific date
|
|
514
|
+
nano-brain logs --clear # Delete all log files
|
|
515
|
+
nano-brain logs path # Print log directory path
|
|
516
|
+
```
|
|
517
|
+
|
|
235
518
|
## Project Structure
|
|
236
519
|
|
|
237
520
|
```
|
|
238
521
|
src/
|
|
239
522
|
├── index.ts # CLI entry point
|
|
240
|
-
├── server.ts # MCP server (17
|
|
523
|
+
├── server.ts # MCP server (17 tools, stdio/HTTP/SSE)
|
|
241
524
|
├── store.ts # SQLite storage (FTS5 + sqlite-vec)
|
|
525
|
+
├── storage.ts # Storage management (retention, disk space)
|
|
526
|
+
├── vector-store.ts # Vector store abstraction (Qdrant + sqlite-vec)
|
|
242
527
|
├── search.ts # Hybrid search pipeline (RRF, reranking, blending)
|
|
243
528
|
├── chunker.ts # Heading-aware markdown chunking
|
|
244
529
|
├── collections.ts # YAML config, collection scanning
|
|
245
|
-
├── embeddings.ts # Embedding providers (Ollama
|
|
246
|
-
├── reranker.ts #
|
|
247
|
-
├── expansion.ts #
|
|
530
|
+
├── embeddings.ts # Embedding providers (VoyageAI, Ollama, OpenAI-compatible)
|
|
531
|
+
├── reranker.ts # VoyageAI reranker
|
|
532
|
+
├── expansion.ts # Query expansion (interface only, no active provider)
|
|
248
533
|
├── harvester.ts # OpenCode session → markdown converter
|
|
249
534
|
├── watcher.ts # File watcher (chokidar, dirty flags)
|
|
250
|
-
|
|
535
|
+
├── codebase.ts # Codebase indexing orchestrator
|
|
536
|
+
├── treesitter.ts # Tree-sitter AST parsing
|
|
537
|
+
├── symbols.ts # Symbol extraction (functions, classes, variables)
|
|
538
|
+
├── graph.ts # File dependency graph (imports/exports)
|
|
539
|
+
├── symbol-graph.ts # Symbol call graph (caller → callee)
|
|
540
|
+
├── flow-detection.ts # Call flow detection (entry → leaf)
|
|
541
|
+
├── types.ts # TypeScript interfaces
|
|
542
|
+
└── providers/ # Vector store implementations
|
|
543
|
+
├── qdrant.ts # Qdrant vector store
|
|
544
|
+
└── sqlite-vec.ts # sqlite-vec vector store
|
|
251
545
|
bin/
|
|
252
546
|
└── cli.js # CLI wrapper
|
|
253
547
|
|
|
254
548
|
test/
|
|
255
|
-
└── *.test.ts #
|
|
549
|
+
└── *.test.ts # 760+ tests (vitest)
|
|
256
550
|
SKILL.md # AI agent routing instructions (auto-loaded by OpenCode)
|
|
257
551
|
AGENTS_SNIPPET.md # Optional project-level AGENTS.md managed block
|
|
258
552
|
```
|
|
@@ -260,46 +554,53 @@ AGENTS_SNIPPET.md # Optional project-level AGENTS.md managed block
|
|
|
260
554
|
## Tech Stack
|
|
261
555
|
|
|
262
556
|
- **TypeScript + Node.js** (via tsx)
|
|
263
|
-
- **better-sqlite3** + **sqlite-vec** for storage
|
|
557
|
+
- **better-sqlite3** + **sqlite-vec** for embedded storage
|
|
558
|
+
- **Qdrant** for production vector store (optional)
|
|
559
|
+
- **Tree-sitter** for AST parsing (TS, JS, Python)
|
|
264
560
|
- **@modelcontextprotocol/sdk** for MCP server (stdio/HTTP/SSE transports)
|
|
265
561
|
- **chokidar** for file watching
|
|
266
|
-
- **vitest** for testing (
|
|
562
|
+
- **vitest** for testing (760+ tests)
|
|
267
563
|
|
|
268
|
-
##
|
|
564
|
+
## Embedding & Reranking Providers
|
|
269
565
|
|
|
270
|
-
Embeddings
|
|
566
|
+
**Embeddings:**
|
|
567
|
+
- **VoyageAI** — voyage-code-3 (1024 dims, code-optimized, recommended)
|
|
568
|
+
- **Ollama** — nomic-embed-text, mxbai-embed-large, etc. (local, free)
|
|
569
|
+
- **OpenAI-compatible** — Azure OpenAI, LM Studio, custom endpoints
|
|
271
570
|
|
|
272
|
-
|
|
273
|
-
- **
|
|
274
|
-
- **Query Expansion:** qmd-query-expansion-1.7B (~1GB, GGUF)
|
|
571
|
+
**Reranking:**
|
|
572
|
+
- **VoyageAI** — rerank-2.5-lite (neural reranking, recommended)
|
|
275
573
|
|
|
276
|
-
|
|
574
|
+
**Query Expansion:**
|
|
575
|
+
- Pipeline support exists but no active provider. The interface is ready for future integration.
|
|
277
576
|
|
|
278
577
|
## How nano-brain Compares
|
|
279
578
|
|
|
280
579
|
| | nano-brain | Mem0 / OpenMemory | Zep / Graphiti | OMEGA | Letta (MemGPT) | Claude Native |
|
|
281
580
|
|---|---|---|---|---|---|---|
|
|
282
|
-
| **Search** | Hybrid (BM25 + vector +
|
|
283
|
-
| **Storage** | SQLite (
|
|
284
|
-
| **MCP Tools** |
|
|
285
|
-
| **
|
|
286
|
-
| **
|
|
287
|
-
| **Codebase Indexing** | Yes (structural boundary detection) | No | No | No | No | No |
|
|
581
|
+
| **Search** | Hybrid (BM25 + vector + 6 ranking signals) | Vector only | Graph traversal + vector | Semantic + BM25 | Agent-managed | Text file read |
|
|
582
|
+
| **Storage** | SQLite + Qdrant (optional) | PostgreSQL + Qdrant | Neo4j | SQLite | PostgreSQL / SQLite | Flat text files |
|
|
583
|
+
| **MCP Tools** | 17 | 4-9 | 9-10 | 12 | 7 | 0 |
|
|
584
|
+
| **Code Intelligence** | Yes (Tree-sitter AST, symbol graph, impact analysis) | No | No | No | No | No |
|
|
585
|
+
| **Codebase Indexing** | Yes (AST → symbols → call graph → flows) | No | No | No | No | No |
|
|
288
586
|
| **Session Recall** | Yes (auto-harvests past sessions) | No | No | No | No | Limited (CLAUDE.md) |
|
|
289
|
-
| **Query Expansion** |
|
|
290
|
-
| **
|
|
291
|
-
| **
|
|
292
|
-
| **
|
|
587
|
+
| **Query Expansion** | Pipeline ready (no active provider) | No | No | No | No | No |
|
|
588
|
+
| **Neural Reranking** | Yes (VoyageAI rerank-2.5-lite) | No | No | No | No | No |
|
|
589
|
+
| **Local-First** | Yes (Ollama + sqlite-vec) | Requires OpenAI API key | Requires Docker + Neo4j | Yes | Yes | Yes |
|
|
590
|
+
| **Cloud Option** | Yes (VoyageAI, OpenAI-compatible) | Cloud API (OpenAI) | Cloud API | Local ONNX | Cloud API | None |
|
|
591
|
+
| **Privacy** | 100% local option available | Cloud API calls | Cloud or self-host | 100% local | Self-host or cloud | Local files |
|
|
592
|
+
| **Dependencies** | SQLite + embedding API (+ optional Qdrant) | Docker + PostgreSQL + Qdrant + OpenAI key | Docker + Neo4j | SQLite + ONNX | PostgreSQL | None |
|
|
293
593
|
| **Pricing** | Free (open source, MIT) | Free tier / Pro $249/mo | Free self-host / Cloud $25-475/mo | Free (Apache-2.0) | Free (Apache-2.0) | Free (with Claude) |
|
|
294
594
|
| **GitHub Stars** | New | ~47K | ~23K | ~25 | ~21K | N/A |
|
|
295
595
|
|
|
296
596
|
### Where nano-brain shines
|
|
297
597
|
|
|
298
|
-
- **
|
|
598
|
+
- **6-signal hybrid search** — BM25 + vector + RRF + PageRank + supersede + neural reranking in a single pipeline
|
|
599
|
+
- **Code intelligence** — Tree-sitter AST parsing, symbol graph, call flow detection, impact analysis
|
|
299
600
|
- **Codebase indexing** — index your source files with structural boundary detection, not just conversations
|
|
300
601
|
- **Session recall** — automatically harvests and indexes past AI coding sessions
|
|
301
|
-
- **
|
|
302
|
-
- **Privacy** —
|
|
602
|
+
- **Flexible deployment** — 100% local (Ollama + sqlite-vec) or cloud (VoyageAI + Qdrant)
|
|
603
|
+
- **Privacy-first** — local processing option, your code never leaves your machine
|
|
303
604
|
|
|
304
605
|
### Consider alternatives if
|
|
305
606
|
|
|
@@ -315,6 +616,7 @@ nano-brain ships with a SKILL.md that teaches AI agents when and how to use memo
|
|
|
315
616
|
- **Check memory before starting work** — recall past decisions, patterns, and context
|
|
316
617
|
- **Save context after completing work** — persist key decisions and debugging insights
|
|
317
618
|
- **Route queries to the right search tool** — BM25 for exact terms, vector for concepts, hybrid for best quality
|
|
619
|
+
- **Use code intelligence** — understand symbol relationships, assess change impact, detect affected code
|
|
318
620
|
|
|
319
621
|
### SKILL.md (Auto-loaded)
|
|
320
622
|
|
|
@@ -328,6 +630,8 @@ For project-level integration, `AGENTS_SNIPPET.md` provides a managed block that
|
|
|
328
630
|
npx nano-brain init --root=/path/to/project
|
|
329
631
|
```
|
|
330
632
|
|
|
633
|
+
This adds a managed block to your project's `AGENTS.md` with quick reference tables for CLI commands and MCP tools (if available).
|
|
634
|
+
|
|
331
635
|
See [SKILL.md](./SKILL.md) for full routing rules and [AGENTS_SNIPPET.md](./AGENTS_SNIPPET.md) for the project-level snippet.
|
|
332
636
|
|
|
333
637
|
## License
|
package/package.json
CHANGED
package/src/reranker.ts
CHANGED
|
@@ -63,6 +63,11 @@ class VoyageAIReranker implements Reranker {
|
|
|
63
63
|
this.onTokenUsage(this.model, data.total_tokens);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
if (!data.results || !Array.isArray(data.results)) {
|
|
67
|
+
log('reranker', `VoyageAI rerank returned unexpected response: ${JSON.stringify(data).slice(0, 200)}`, 'warn');
|
|
68
|
+
return { results: [], model: this.model };
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
const results = data.results.map(r => ({
|
|
67
72
|
file: documents[r.index].file,
|
|
68
73
|
score: r.relevance_score,
|
package/src/server.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { findCycles } from './graph.js';
|
|
|
16
16
|
import { createStore, extractProjectHashFromPath, resolveWorkspaceDbPath } from './store.js';
|
|
17
17
|
import { log, initLogger } from './logger.js';
|
|
18
18
|
import { loadCollectionConfig, getCollections, scanCollectionFiles, getWorkspaceConfig } from './collections.js';
|
|
19
|
-
import { createEmbeddingProvider, detectOllamaUrl, checkOllamaHealth } from './embeddings.js';
|
|
19
|
+
import { createEmbeddingProvider, detectOllamaUrl, checkOllamaHealth, checkOpenAIHealth } from './embeddings.js';
|
|
20
20
|
import { createReranker } from './reranker.js';
|
|
21
21
|
import { startWatcher } from './watcher.js';
|
|
22
22
|
import { parseStorageConfig } from './storage.js';
|
|
@@ -512,7 +512,10 @@ export function createMcpServer(deps: ServerDeps): McpServer {
|
|
|
512
512
|
const provider = embeddingConfig?.provider || 'ollama'
|
|
513
513
|
let embeddingHealth: { provider: string; url: string; model: string; reachable: boolean; models?: string[]; error?: string } | undefined
|
|
514
514
|
|
|
515
|
-
if (provider
|
|
515
|
+
if (provider === 'openai' && embeddingConfig?.apiKey) {
|
|
516
|
+
const openaiHealth = await checkOpenAIHealth(ollamaUrl, embeddingConfig.apiKey, ollamaModel)
|
|
517
|
+
embeddingHealth = { provider, url: ollamaUrl, model: ollamaModel, ...openaiHealth }
|
|
518
|
+
} else if (provider !== 'local') {
|
|
516
519
|
const ollamaHealth = await checkOllamaHealth(ollamaUrl)
|
|
517
520
|
embeddingHealth = { provider, url: ollamaUrl, model: ollamaModel, ...ollamaHealth }
|
|
518
521
|
} else {
|
package/src/store.ts
CHANGED
|
@@ -486,7 +486,14 @@ export function createStore(dbPath: string): Store {
|
|
|
486
486
|
doc.active ? 1 : 0,
|
|
487
487
|
doc.projectHash ?? 'global'
|
|
488
488
|
);
|
|
489
|
-
|
|
489
|
+
// For UPSERT (ON CONFLICT DO UPDATE), lastInsertRowid returns a phantom
|
|
490
|
+
// autoincrement value that doesn't correspond to any actual row.
|
|
491
|
+
// Always verify via lookup to get the real id.
|
|
492
|
+
const existing = findDocumentByPathStmt.get(doc.path) as { id: number } | undefined;
|
|
493
|
+
if (existing) return existing.id;
|
|
494
|
+
const rowid = Number(result.lastInsertRowid);
|
|
495
|
+
if (rowid > 0) return rowid;
|
|
496
|
+
return 0;
|
|
490
497
|
},
|
|
491
498
|
|
|
492
499
|
findDocument(pathOrDocid: string): Document | null {
|