@nano-step/nano-brain 2.0.0-beta.6
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 +281 -0
- package/npm/postinstall.js +89 -0
- package/npm/run.js +22 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# nano-brain
|
|
2
|
+
|
|
3
|
+
**Persistent memory and code intelligence for AI coding agents.**
|
|
4
|
+
|
|
5
|
+
[](https://go.dev/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/nano-step/nano-brain)
|
|
8
|
+
|
|
9
|
+
## What It Does
|
|
10
|
+
|
|
11
|
+
nano-brain is a persistent memory server for AI coding agents that solves session amnesia. It automatically ingests AI sessions, notes, and codebase files, indexes everything with hybrid search (BM25 + pgvector), and serves memories via MCP tools and REST API. Built in Go with PostgreSQL — single static binary, zero CGO dependencies.
|
|
12
|
+
|
|
13
|
+
## Key Features
|
|
14
|
+
|
|
15
|
+
- **Hybrid search** — BM25 full-text + pgvector HNSW cosine similarity + RRF fusion + recency decay
|
|
16
|
+
- **9 MCP tools** — query, search, vsearch, get, write, tags, status, update, wake_up
|
|
17
|
+
- **Session harvesting** — auto-ingest OpenCode and Claude Code sessions
|
|
18
|
+
- **File watcher** — fsnotify-based directory monitoring with debounce
|
|
19
|
+
- **Content-addressed storage** — SHA-256 deduplication
|
|
20
|
+
- **Heading-aware markdown chunking**
|
|
21
|
+
- **Multi-workspace isolation** with per-workspace data
|
|
22
|
+
- **Config hot-reload** — `POST /api/reload-config`
|
|
23
|
+
- **V1 migration** — import from SQLite (pure Go, no CGO)
|
|
24
|
+
- **Benchmarking suite** — generate, run, compare, stress
|
|
25
|
+
- **Search telemetry** — local-only, 90-day retention, non-blocking
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
- **Go 1.23+** (building from source) OR pre-built binary
|
|
30
|
+
- **PostgreSQL 17** with **pgvector 0.8.2** extension
|
|
31
|
+
- **Embedding provider:** Ollama (default, local) or Voyage AI
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Option A: Via npx (no Go required)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Start PostgreSQL + pgvector
|
|
39
|
+
docker run -d --name nanobrain-pg -p 5432:5432 \
|
|
40
|
+
-e POSTGRES_USER=nanobrain -e POSTGRES_PASSWORD=nanobrain -e POSTGRES_DB=nanobrain_dev \
|
|
41
|
+
pgvector/pgvector:pg17
|
|
42
|
+
|
|
43
|
+
# Start Ollama + pull embedding model
|
|
44
|
+
ollama pull nomic-embed-text
|
|
45
|
+
|
|
46
|
+
# Check prerequisites
|
|
47
|
+
npx @nano-step/nano-brain@beta doctor
|
|
48
|
+
|
|
49
|
+
# Start server
|
|
50
|
+
npx @nano-step/nano-brain@beta
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Also available as:** `npx nano-brain@beta` (unscoped alias)
|
|
54
|
+
>
|
|
55
|
+
> **Note:** Do NOT run `npx nano-brain` from the nano-brain source directory — npm will resolve the local package instead of the registry. Run from any other directory.
|
|
56
|
+
|
|
57
|
+
### Option B: Build from source
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Build
|
|
61
|
+
CGO_ENABLED=0 go build -o nano-brain ./cmd/nano-brain
|
|
62
|
+
|
|
63
|
+
# Start PostgreSQL + pgvector (example with Docker)
|
|
64
|
+
docker run -d --name nanobrain-pg -p 5432:5432 \
|
|
65
|
+
-e POSTGRES_USER=nanobrain -e POSTGRES_PASSWORD=nanobrain -e POSTGRES_DB=nanobrain_dev \
|
|
66
|
+
pgvector/pgvector:pg17
|
|
67
|
+
|
|
68
|
+
# Start server
|
|
69
|
+
DATABASE_URL="postgres://nanobrain:nanobrain@localhost:5432/nanobrain_dev" ./nano-brain
|
|
70
|
+
|
|
71
|
+
# Register workspace
|
|
72
|
+
curl -X POST http://localhost:3100/api/v1/init \
|
|
73
|
+
-H "Content-Type: application/json" \
|
|
74
|
+
-d '{"root_path":"/path/to/project","name":"my-project"}'
|
|
75
|
+
|
|
76
|
+
# Write a document
|
|
77
|
+
curl -X POST http://localhost:3100/api/v1/write \
|
|
78
|
+
-H "Content-Type: application/json" \
|
|
79
|
+
-d '{"workspace":"<hash>","source_path":"notes/decision.md","content":"# Decision\nUse PostgreSQL.","tags":["decision"]}'
|
|
80
|
+
|
|
81
|
+
# Search
|
|
82
|
+
curl -X POST http://localhost:3100/api/v1/query \
|
|
83
|
+
-H "Content-Type: application/json" \
|
|
84
|
+
-d '{"workspace":"<hash>","query":"database decision"}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
Config file: `~/.nano-brain/config.yml`
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
server:
|
|
93
|
+
host: localhost
|
|
94
|
+
port: 3100
|
|
95
|
+
|
|
96
|
+
database:
|
|
97
|
+
url: postgres://nanobrain:nanobrain@localhost:5432/nanobrain_dev
|
|
98
|
+
|
|
99
|
+
embedding:
|
|
100
|
+
provider: ollama # ollama or voyage
|
|
101
|
+
url: http://localhost:11434
|
|
102
|
+
model: nomic-embed-text
|
|
103
|
+
dimension: 0 # auto-detect from provider
|
|
104
|
+
concurrency: 3
|
|
105
|
+
|
|
106
|
+
search:
|
|
107
|
+
rrf_k: 60
|
|
108
|
+
recency_weight: 0.3
|
|
109
|
+
recency_half_life_days: 180
|
|
110
|
+
limit: 20
|
|
111
|
+
|
|
112
|
+
harvester:
|
|
113
|
+
opencode:
|
|
114
|
+
session_dir: "" # e.g., ~/.local/share/opencode/storage
|
|
115
|
+
claudecode:
|
|
116
|
+
enabled: false
|
|
117
|
+
session_dir: ""
|
|
118
|
+
|
|
119
|
+
watcher:
|
|
120
|
+
debounce_ms: 2000
|
|
121
|
+
reindex_interval: 300
|
|
122
|
+
|
|
123
|
+
storage:
|
|
124
|
+
max_file_size: 314572800 # 300MB
|
|
125
|
+
max_size: 10737418240 # 10GB
|
|
126
|
+
|
|
127
|
+
telemetry:
|
|
128
|
+
retention_days: 90
|
|
129
|
+
|
|
130
|
+
logging:
|
|
131
|
+
level: info
|
|
132
|
+
file: "" # empty = stdout only
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Environment Variables
|
|
136
|
+
|
|
137
|
+
| Variable | Description |
|
|
138
|
+
|----------|-------------|
|
|
139
|
+
| `DATABASE_URL` | PostgreSQL connection string |
|
|
140
|
+
| `VOYAGE_API_KEY` | Voyage AI API key |
|
|
141
|
+
| `OPENCODE_STORAGE_DIR` | OpenCode session directory |
|
|
142
|
+
| `NANO_BRAIN_*` | Override any config (e.g., `NANO_BRAIN_SERVER_PORT=3100`) |
|
|
143
|
+
|
|
144
|
+
## REST API
|
|
145
|
+
|
|
146
|
+
### Public Endpoints
|
|
147
|
+
|
|
148
|
+
| Method | Path | Description |
|
|
149
|
+
|--------|------|-------------|
|
|
150
|
+
| GET | `/health` | Health check |
|
|
151
|
+
| GET | `/api/status` | Server status with version, uptime, workspace stats |
|
|
152
|
+
| POST | `/api/v1/init` | Register workspace |
|
|
153
|
+
| GET | `/api/v1/workspaces` | List all workspaces (with doc counts) |
|
|
154
|
+
| GET | `/api/v1/wake-up` | Workspace briefing |
|
|
155
|
+
| POST | `/api/harvest` | Trigger session harvesting |
|
|
156
|
+
| POST | `/api/reload-config` | Hot-reload configuration |
|
|
157
|
+
|
|
158
|
+
### Workspace-Scoped Endpoints
|
|
159
|
+
|
|
160
|
+
Workspace is passed in the JSON body for POST, query param for GET.
|
|
161
|
+
|
|
162
|
+
| Method | Path | Description |
|
|
163
|
+
|--------|------|-------------|
|
|
164
|
+
| POST | `/api/v1/write` | Write/update document |
|
|
165
|
+
| POST | `/api/v1/embed` | Trigger embedding |
|
|
166
|
+
| POST | `/api/v1/search` | BM25 keyword search |
|
|
167
|
+
| POST | `/api/v1/vsearch` | Vector similarity search |
|
|
168
|
+
| POST | `/api/v1/query` | Hybrid search (BM25 + vector + RRF + recency) |
|
|
169
|
+
| POST | `/api/v1/collections` | Add collection |
|
|
170
|
+
| GET | `/api/v1/collections` | List collections |
|
|
171
|
+
| PUT | `/api/v1/collections/:name` | Rename collection |
|
|
172
|
+
| DELETE | `/api/v1/collections/:name` | Remove collection |
|
|
173
|
+
| GET | `/api/v1/tags` | List tags with counts |
|
|
174
|
+
| POST | `/api/v1/reindex` | Queue reindex (202) |
|
|
175
|
+
| POST | `/api/v1/update` | Queue update (202) |
|
|
176
|
+
| POST | `/api/v1/wake-up` | Workspace briefing with session_dir |
|
|
177
|
+
|
|
178
|
+
### MCP Endpoints
|
|
179
|
+
|
|
180
|
+
| Method | Path | Description |
|
|
181
|
+
|--------|------|-------------|
|
|
182
|
+
| GET/POST | `/mcp` | Streamable HTTP (MCP 2025-03-26) |
|
|
183
|
+
| GET/POST | `/sse` | SSE transport (legacy) |
|
|
184
|
+
|
|
185
|
+
## CLI Commands
|
|
186
|
+
|
|
187
|
+
| Command | Description |
|
|
188
|
+
|---------|-------------|
|
|
189
|
+
| `nano-brain` (no args) | Start HTTP server (default: port 3100) |
|
|
190
|
+
| `nano-brain init --root=<path>` | Register workspace |
|
|
191
|
+
| `nano-brain write` | Write document via CLI |
|
|
192
|
+
| `nano-brain query` | Hybrid search |
|
|
193
|
+
| `nano-brain search` | BM25 keyword search |
|
|
194
|
+
| `nano-brain vsearch` | Vector similarity search |
|
|
195
|
+
| `nano-brain collection add\|remove\|list` | Manage collections |
|
|
196
|
+
| `nano-brain harvest` | Trigger session harvesting |
|
|
197
|
+
| `nano-brain bench generate\|run\|compare\|stress` | Benchmarking suite |
|
|
198
|
+
| `nano-brain db:migrate` | Run pending goose migrations |
|
|
199
|
+
| `nano-brain db:migrate --from-v1 <path>` | Import V1 SQLite data |
|
|
200
|
+
| `nano-brain logs [-n 50] [-f]` | Tail log file |
|
|
201
|
+
| `nano-brain docker start\|stop\|status` | Docker compose management |
|
|
202
|
+
| `nano-brain status [--json]` | Server status |
|
|
203
|
+
| `nano-brain doctor [--json]` | Check prerequisites (config, PostgreSQL, pgvector, Ollama, model) |
|
|
204
|
+
|
|
205
|
+
## MCP Tools
|
|
206
|
+
|
|
207
|
+
nano-brain exposes 9 tools via MCP (Model Context Protocol):
|
|
208
|
+
|
|
209
|
+
| Tool | Description |
|
|
210
|
+
|------|-------------|
|
|
211
|
+
| `memory_query` | Hybrid search (BM25 + vector + RRF + recency) |
|
|
212
|
+
| `memory_search` | BM25 keyword search |
|
|
213
|
+
| `memory_vsearch` | Vector similarity search |
|
|
214
|
+
| `memory_get` | Get document by path |
|
|
215
|
+
| `memory_write` | Write/update document |
|
|
216
|
+
| `memory_tags` | List tags with counts |
|
|
217
|
+
| `memory_status` | Server and embedding status |
|
|
218
|
+
| `memory_update` | Trigger re-embedding |
|
|
219
|
+
| `memory_wake_up` | Workspace briefing |
|
|
220
|
+
|
|
221
|
+
### MCP Configuration
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"mcp": {
|
|
226
|
+
"nano-brain": {
|
|
227
|
+
"type": "remote",
|
|
228
|
+
"url": "http://localhost:3100/mcp"
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Search Pipeline
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Query --> BM25 (ts_rank_cd) ---+
|
|
238
|
+
+--> RRF Fusion (k=60) --> Recency Decay --> Results
|
|
239
|
+
Query --> Vector (HNSW cos) ---+
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
- **BM25:** `websearch_to_tsquery` + `ts_rank_cd` on PostgreSQL tsvector
|
|
243
|
+
- **Vector:** pgvector HNSW index with cosine distance
|
|
244
|
+
- **RRF:** Reciprocal Rank Fusion (k=60), scores normalized to [0,1]
|
|
245
|
+
- **Recency:** exponential half-life decay (default 180 days, weight 0.3)
|
|
246
|
+
|
|
247
|
+
## Architecture
|
|
248
|
+
|
|
249
|
+
- 15 internal packages: config, server, handlers, storage, sqlc, embed, search, watcher, harvest, mcp, migrate, telemetry, health, bench
|
|
250
|
+
- 7 goose SQL migrations (embedded)
|
|
251
|
+
- Constructor injection (no DI framework)
|
|
252
|
+
- errgroup + context for goroutine lifecycle
|
|
253
|
+
- Echo v4 middleware: workspace extraction, content-type enforcement, version header
|
|
254
|
+
|
|
255
|
+
## Migration from V1
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Import V1 SQLite data to PostgreSQL
|
|
259
|
+
nano-brain db:migrate --from-v1 /path/to/old/index.db
|
|
260
|
+
|
|
261
|
+
# Idempotent — safe to run multiple times
|
|
262
|
+
# Uses content-addressed SHA-256 hashing
|
|
263
|
+
# Pure Go SQLite reader (modernc.org/sqlite, no CGO)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Tech Stack
|
|
267
|
+
|
|
268
|
+
- **Go 1.23** — compiled to single static binary (`CGO_ENABLED=0`)
|
|
269
|
+
- **PostgreSQL 17** — relational storage + full-text search (tsvector/tsquery)
|
|
270
|
+
- **pgvector 0.8.2** — HNSW vector indexing
|
|
271
|
+
- **Echo v4** — HTTP framework
|
|
272
|
+
- **sqlc** — type-safe SQL code generation
|
|
273
|
+
- **goose v3** — database migrations
|
|
274
|
+
- **zerolog** — structured JSON logging
|
|
275
|
+
- **koanf** — YAML + env configuration
|
|
276
|
+
- **fsnotify** — file system watching
|
|
277
|
+
- **modernc.org/sqlite** — V1 migration reader (pure Go)
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const https = require("https");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
const VERSION = require("../package.json").version;
|
|
11
|
+
const REPO = "nano-step/nano-brain";
|
|
12
|
+
|
|
13
|
+
const PLATFORM_MAP = {
|
|
14
|
+
darwin: "darwin",
|
|
15
|
+
linux: "linux",
|
|
16
|
+
};
|
|
17
|
+
const ARCH_MAP = {
|
|
18
|
+
arm64: "arm64",
|
|
19
|
+
x64: "amd64",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function getPlatformKey() {
|
|
23
|
+
const platform = PLATFORM_MAP[os.platform()];
|
|
24
|
+
const arch = ARCH_MAP[os.arch()];
|
|
25
|
+
if (!platform || !arch) {
|
|
26
|
+
console.error(`Unsupported platform: ${os.platform()}-${os.arch()}`);
|
|
27
|
+
console.error("Build from source: CGO_ENABLED=0 go build -o nano-brain ./cmd/nano-brain");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
return `${platform}-${arch}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function download(url, dest) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const file = fs.createWriteStream(dest);
|
|
36
|
+
https.get(url, (res) => {
|
|
37
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
38
|
+
file.close();
|
|
39
|
+
fs.unlinkSync(dest);
|
|
40
|
+
return download(res.headers.location, dest).then(resolve).catch(reject);
|
|
41
|
+
}
|
|
42
|
+
if (res.statusCode !== 200) {
|
|
43
|
+
file.close();
|
|
44
|
+
fs.unlinkSync(dest);
|
|
45
|
+
return reject(new Error(`Download failed: HTTP ${res.statusCode}`));
|
|
46
|
+
}
|
|
47
|
+
res.pipe(file);
|
|
48
|
+
file.on("finish", () => {
|
|
49
|
+
file.close(resolve);
|
|
50
|
+
});
|
|
51
|
+
}).on("error", (err) => {
|
|
52
|
+
fs.unlinkSync(dest);
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function main() {
|
|
59
|
+
const platformKey = getPlatformKey();
|
|
60
|
+
const binName = os.platform() === "win32" ? "nano-brain.exe" : "nano-brain";
|
|
61
|
+
const binPath = path.join(__dirname, binName);
|
|
62
|
+
|
|
63
|
+
if (fs.existsSync(binPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const output = execSync(`"${binPath}" version --json`, { timeout: 5000 }).toString();
|
|
66
|
+
if (output.includes(VERSION)) {
|
|
67
|
+
console.log(`nano-brain v${VERSION} already installed.`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// Wrong version or can't run — redownload
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/nano-brain-${platformKey}`;
|
|
76
|
+
console.log(`Downloading nano-brain v${VERSION} for ${platformKey}...`);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await download(url, binPath);
|
|
80
|
+
fs.chmodSync(binPath, 0o755);
|
|
81
|
+
console.log(`nano-brain v${VERSION} installed successfully.`);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error(`Failed to download binary: ${err.message}`);
|
|
84
|
+
console.error("Build from source: CGO_ENABLED=0 go build -o npm/nano-brain ./cmd/nano-brain");
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main();
|
package/npm/run.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { execFileSync } = require("child_process");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
|
|
9
|
+
const binName = os.platform() === "win32" ? "nano-brain.exe" : "nano-brain";
|
|
10
|
+
const binPath = path.join(__dirname, binName);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(binPath)) {
|
|
13
|
+
console.error("nano-brain binary not found. Run: npx nano-brain (it will download automatically)");
|
|
14
|
+
console.error("Or build from source: CGO_ENABLED=0 go build -o npm/nano-brain ./cmd/nano-brain");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
execFileSync(binPath, process.argv.slice(2), { stdio: "inherit" });
|
|
20
|
+
} catch (e) {
|
|
21
|
+
process.exit(e.status || 1);
|
|
22
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nano-step/nano-brain",
|
|
3
|
+
"version": "2.0.0-beta.6",
|
|
4
|
+
"description": "Persistent memory and code intelligence for AI coding agents",
|
|
5
|
+
"bin": {
|
|
6
|
+
"nano-brain": "npm/run.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node npm/postinstall.js"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/nano-step/nano-brain.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"memory",
|
|
21
|
+
"mcp",
|
|
22
|
+
"coding-agent",
|
|
23
|
+
"search",
|
|
24
|
+
"pgvector"
|
|
25
|
+
],
|
|
26
|
+
"author": "nano-step",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"files": [
|
|
29
|
+
"npm/",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
]
|
|
33
|
+
}
|