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 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 system for AI coding agents. Hybrid search (BM25 + vector + LLM reranking) across past sessions, codebase, curated notes, and daily logs.
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, and daily logs into a searchable SQLite database with FTS5 and vector embeddings. Provides 17+ MCP tools for search, retrieval, and memory management using a sophisticated hybrid search pipeline with query expansion, RRF fusion, and neural reranking. Runs as local stdio or remote SSE server for Docker/container environments.
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 │ ← qmd-query-expansion-1.7B (GGUF)
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) │ │(sqlite-
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
- LLM Reranking │ ← bge-reranker-v2-m3 (GGUF)
39
- (optional)
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
- ## How It Works
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
- ### Storage Layer
83
+ **`memory_query`** Full hybrid pipeline with 6 ranking signals:
55
84
 
56
- - **SQLite** via better-sqlite3 for document metadata and content
57
- - **FTS5** virtual table with porter stemming for BM25 full-text search
58
- - **sqlite-vec** extension for vector similarity search (cosine distance)
59
- - **Content-addressed storage** using SHA-256 hash deduplication
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
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
- ### Chunking
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
- ### Search Pipeline (3 Tiers)
175
+ ## Storage & Infrastructure
71
176
 
72
- **`memory_search`** — BM25 only (fast, exact keyword matching)
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
- **`memory_vsearch`** — Vector only (semantic similarity via embeddings)
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
- **`memory_query`** — Full hybrid pipeline:
77
- 1. Query expansion generates 2-3 variants (optional)
78
- 2. Parallel BM25 + vector search
79
- 3. RRF fusion (k=60, original query weighted 2×)
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
- ### Collections
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
- - **YAML-configured** directories of markdown files
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
- ## MCP Tools
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 with expansion + reranking |
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
- | `memory_write` | Write to daily log (tagged with workspace) |
103
- | `memory_status` | Index health, collections, model status |
104
- | `memory_index_codebase` | Index codebase files in current workspace |
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 with cycle detection |
108
- | `memory_symbols` | Cross-repo symbol query (Redis keys, PubSub, MySQL tables, API endpoints) |
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
- Add to your AI agent's MCP config (e.g. `~/.config/opencode/opencode.json`):
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
- For remote mode (e.g., running on host while AI agent runs in Docker):
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
- # Embedding configuration
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: http://localhost:11434 # Auto-detected: localhost natively, host.docker.internal in Docker
173
- model: nomic-embed-text
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
- └── sessions/ # Harvested sessions
400
+ ├── sessions/ # Harvested sessions
401
+ └── logs/ # Application logs
194
402
  ```
195
403
 
196
- ## CLI Usage
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
- # MCP server
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
- # Remote server
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
- # Index management
215
- nano-brain status # Show index health
216
- nano-brain update # Reindex all collections
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
- # Search
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
- # Collections
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
- # Workspace management
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+ tools, stdio/HTTP/SSE)
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 API + GGUF fallback)
246
- ├── reranker.ts # GGUF reranker model (bge-reranker-v2-m3)
247
- ├── expansion.ts # GGUF query expansion (qmd-query-expansion-1.7B)
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
- └── types.ts # TypeScript interfaces
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 # 739 tests (vitest)
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 (739 tests)
562
+ - **vitest** for testing (760+ tests)
267
563
 
268
- ## Models
564
+ ## Embedding & Reranking Providers
269
565
 
270
- Embeddings use Ollama API by default (nomic-embed-text via `http://localhost:11434`). Query expansion and reranking use local GGUF models as fallback:
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
- - **Embeddings:** Ollama API (primary) or nomic-embed-text-v1.5 GGUF (~270MB, fallback)
273
- - **Reranker:** bge-reranker-v2-m3 (~1.1GB, GGUF)
274
- - **Query Expansion:** qmd-query-expansion-1.7B (~1GB, GGUF)
571
+ **Reranking:**
572
+ - **VoyageAI** — rerank-2.5-lite (neural reranking, recommended)
275
573
 
276
- GGUF models are downloaded automatically on first use to `~/.nano-brain/models/`.
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 + LLM reranking) | Vector only | Graph traversal + vector | Semantic + BM25 | Agent-managed | Text file read |
283
- | **Storage** | SQLite (single file) | PostgreSQL + Qdrant | Neo4j | SQLite | PostgreSQL / SQLite | Flat text files |
284
- | **MCP Tools** | 10 | 4-9 | 9-10 | 12 | 7 | 0 |
285
- | **Local-First** | Yes (zero cloud) | Requires OpenAI API key | Requires Docker + Neo4j | Yes | Yes | Yes |
286
- | **AI Models** | Local GGUF (nomic-embed, bge-reranker) | Cloud API (OpenAI) | Cloud API | Local ONNX | Cloud API | None |
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** | Yes (local LLM) | No | No | No | No | No |
290
- | **LLM Reranking** | Yes (bge-reranker-v2-m3) | No | No | No | No | No |
291
- | **Privacy** | 100% local, no data leaves machine | Cloud API calls | Cloud or self-host | 100% local | Self-host or cloud | Local files |
292
- | **Dependencies** | SQLite + GGUF models (~1.5GB) | Docker + PostgreSQL + Qdrant + OpenAI key | Docker + Neo4j | SQLite + ONNX | PostgreSQL | None |
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
- - **Hybrid search pipeline** — the only MCP memory server with BM25 + vector + query expansion + LLM reranking in a single pipeline
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
- - **Zero dependencies** — single SQLite file, local GGUF models, no Docker/PostgreSQL/Neo4j/API keys
302
- - **Privacy** — 100% local processing, your code and conversations never leave your machine
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nano-brain",
3
- "version": "2026.3.21",
3
+ "version": "2026.3.23",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "nano-brain": "./bin/cli.js"
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 !== 'local') {
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
- return Number(result.lastInsertRowid);
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 {