@199-bio/engram 0.1.0 → 0.2.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 CHANGED
@@ -31,18 +31,19 @@ Engram connects the dots—*avoid seafood restaurants, book Sarah a window seat,
31
31
  ### Install
32
32
 
33
33
  ```bash
34
- npm install -g engram-mcp
34
+ npm install -g @199-bio/engram
35
35
  ```
36
36
 
37
37
  ### Add to Your MCP Client
38
38
 
39
- **Claude Desktop** — add to `~/.claude/claude_desktop_config.json`:
39
+ **Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
40
40
 
41
41
  ```json
42
42
  {
43
43
  "mcpServers": {
44
44
  "engram": {
45
- "command": "engram"
45
+ "command": "npx",
46
+ "args": ["-y", "@199-bio/engram"]
46
47
  }
47
48
  }
48
49
  }
@@ -51,7 +52,7 @@ npm install -g engram-mcp
51
52
  **Claude Code:**
52
53
 
53
54
  ```bash
54
- claude mcp add engram -- engram
55
+ claude mcp add engram -- npx -y @199-bio/engram
55
56
  ```
56
57
 
57
58
  **Other MCP clients** — point to the `engram` command. It speaks standard MCP over stdio.
package/dist/index.js CHANGED
@@ -40,13 +40,13 @@ async function initialize() {
40
40
  // ============ MCP Server ============
41
41
  const server = new Server({
42
42
  name: "engram",
43
- version: "0.1.0",
43
+ version: "0.2.0",
44
44
  }, {
45
45
  capabilities: {
46
46
  tools: {},
47
47
  },
48
48
  });
49
- // Tool definitions
49
+ // Tool definitions with MCP 2025-06-18 annotations
50
50
  const TOOLS = [
51
51
  {
52
52
  name: "remember",
@@ -73,6 +73,13 @@ const TOOLS = [
73
73
  },
74
74
  required: ["content"],
75
75
  },
76
+ annotations: {
77
+ title: "Store Memory",
78
+ readOnlyHint: false,
79
+ destructiveHint: false,
80
+ idempotentHint: false,
81
+ openWorldHint: false,
82
+ },
76
83
  },
77
84
  {
78
85
  name: "recall",
@@ -97,6 +104,13 @@ const TOOLS = [
97
104
  },
98
105
  required: ["query"],
99
106
  },
107
+ annotations: {
108
+ title: "Search Memories",
109
+ readOnlyHint: true,
110
+ destructiveHint: false,
111
+ idempotentHint: true,
112
+ openWorldHint: false,
113
+ },
100
114
  },
101
115
  {
102
116
  name: "forget",
@@ -111,6 +125,13 @@ const TOOLS = [
111
125
  },
112
126
  required: ["id"],
113
127
  },
128
+ annotations: {
129
+ title: "Delete Memory",
130
+ readOnlyHint: false,
131
+ destructiveHint: true,
132
+ idempotentHint: true,
133
+ openWorldHint: false,
134
+ },
114
135
  },
115
136
  {
116
137
  name: "create_entity",
@@ -130,6 +151,13 @@ const TOOLS = [
130
151
  },
131
152
  required: ["name", "type"],
132
153
  },
154
+ annotations: {
155
+ title: "Create Entity",
156
+ readOnlyHint: false,
157
+ destructiveHint: false,
158
+ idempotentHint: true,
159
+ openWorldHint: false,
160
+ },
133
161
  },
134
162
  {
135
163
  name: "observe",
@@ -153,6 +181,13 @@ const TOOLS = [
153
181
  },
154
182
  required: ["entity", "observation"],
155
183
  },
184
+ annotations: {
185
+ title: "Add Observation",
186
+ readOnlyHint: false,
187
+ destructiveHint: false,
188
+ idempotentHint: false,
189
+ openWorldHint: false,
190
+ },
156
191
  },
157
192
  {
158
193
  name: "relate",
@@ -175,6 +210,13 @@ const TOOLS = [
175
210
  },
176
211
  required: ["from", "to", "relation"],
177
212
  },
213
+ annotations: {
214
+ title: "Create Relationship",
215
+ readOnlyHint: false,
216
+ destructiveHint: false,
217
+ idempotentHint: true,
218
+ openWorldHint: false,
219
+ },
178
220
  },
179
221
  {
180
222
  name: "query_entity",
@@ -189,6 +231,13 @@ const TOOLS = [
189
231
  },
190
232
  required: ["entity"],
191
233
  },
234
+ annotations: {
235
+ title: "Query Entity",
236
+ readOnlyHint: true,
237
+ destructiveHint: false,
238
+ idempotentHint: true,
239
+ openWorldHint: false,
240
+ },
192
241
  },
193
242
  {
194
243
  name: "list_entities",
@@ -208,6 +257,13 @@ const TOOLS = [
208
257
  },
209
258
  },
210
259
  },
260
+ annotations: {
261
+ title: "List Entities",
262
+ readOnlyHint: true,
263
+ destructiveHint: false,
264
+ idempotentHint: true,
265
+ openWorldHint: false,
266
+ },
211
267
  },
212
268
  {
213
269
  name: "stats",
@@ -216,6 +272,13 @@ const TOOLS = [
216
272
  type: "object",
217
273
  properties: {},
218
274
  },
275
+ annotations: {
276
+ title: "Get Statistics",
277
+ readOnlyHint: true,
278
+ destructiveHint: false,
279
+ idempotentHint: true,
280
+ openWorldHint: false,
281
+ },
219
282
  },
220
283
  ];
221
284
  // List available tools
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAGzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;OAEG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACjB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAyGhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;OAEG;YACW,WAAW;IAezB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
1
+ {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAGzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAqHhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;OAEG;YACW,WAAW;IAezB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@199-bio/engram",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Give Claude a perfect memory. Local-first MCP server with hybrid search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -46,11 +46,11 @@
46
46
  "node": ">=18.0.0"
47
47
  },
48
48
  "dependencies": {
49
- "@modelcontextprotocol/sdk": "^1.0.0",
50
- "better-sqlite3": "^11.0.0",
49
+ "@modelcontextprotocol/sdk": "^1.25.0",
50
+ "better-sqlite3": "^11.10.0",
51
51
  "chrono-node": "^2.7.0",
52
52
  "uuid": "^10.0.0",
53
- "zod": "^3.23.0"
53
+ "zod": "^3.24.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/better-sqlite3": "^7.6.0",
package/src/index.ts CHANGED
@@ -58,7 +58,7 @@ async function initialize(): Promise<void> {
58
58
  const server = new Server(
59
59
  {
60
60
  name: "engram",
61
- version: "0.1.0",
61
+ version: "0.2.0",
62
62
  },
63
63
  {
64
64
  capabilities: {
@@ -67,7 +67,7 @@ const server = new Server(
67
67
  }
68
68
  );
69
69
 
70
- // Tool definitions
70
+ // Tool definitions with MCP 2025-06-18 annotations
71
71
  const TOOLS = [
72
72
  {
73
73
  name: "remember",
@@ -95,6 +95,13 @@ const TOOLS = [
95
95
  },
96
96
  required: ["content"],
97
97
  },
98
+ annotations: {
99
+ title: "Store Memory",
100
+ readOnlyHint: false,
101
+ destructiveHint: false,
102
+ idempotentHint: false,
103
+ openWorldHint: false,
104
+ },
98
105
  },
99
106
  {
100
107
  name: "recall",
@@ -120,6 +127,13 @@ const TOOLS = [
120
127
  },
121
128
  required: ["query"],
122
129
  },
130
+ annotations: {
131
+ title: "Search Memories",
132
+ readOnlyHint: true,
133
+ destructiveHint: false,
134
+ idempotentHint: true,
135
+ openWorldHint: false,
136
+ },
123
137
  },
124
138
  {
125
139
  name: "forget",
@@ -134,6 +148,13 @@ const TOOLS = [
134
148
  },
135
149
  required: ["id"],
136
150
  },
151
+ annotations: {
152
+ title: "Delete Memory",
153
+ readOnlyHint: false,
154
+ destructiveHint: true,
155
+ idempotentHint: true,
156
+ openWorldHint: false,
157
+ },
137
158
  },
138
159
  {
139
160
  name: "create_entity",
@@ -153,6 +174,13 @@ const TOOLS = [
153
174
  },
154
175
  required: ["name", "type"],
155
176
  },
177
+ annotations: {
178
+ title: "Create Entity",
179
+ readOnlyHint: false,
180
+ destructiveHint: false,
181
+ idempotentHint: true,
182
+ openWorldHint: false,
183
+ },
156
184
  },
157
185
  {
158
186
  name: "observe",
@@ -176,6 +204,13 @@ const TOOLS = [
176
204
  },
177
205
  required: ["entity", "observation"],
178
206
  },
207
+ annotations: {
208
+ title: "Add Observation",
209
+ readOnlyHint: false,
210
+ destructiveHint: false,
211
+ idempotentHint: false,
212
+ openWorldHint: false,
213
+ },
179
214
  },
180
215
  {
181
216
  name: "relate",
@@ -198,6 +233,13 @@ const TOOLS = [
198
233
  },
199
234
  required: ["from", "to", "relation"],
200
235
  },
236
+ annotations: {
237
+ title: "Create Relationship",
238
+ readOnlyHint: false,
239
+ destructiveHint: false,
240
+ idempotentHint: true,
241
+ openWorldHint: false,
242
+ },
201
243
  },
202
244
  {
203
245
  name: "query_entity",
@@ -212,6 +254,13 @@ const TOOLS = [
212
254
  },
213
255
  required: ["entity"],
214
256
  },
257
+ annotations: {
258
+ title: "Query Entity",
259
+ readOnlyHint: true,
260
+ destructiveHint: false,
261
+ idempotentHint: true,
262
+ openWorldHint: false,
263
+ },
215
264
  },
216
265
  {
217
266
  name: "list_entities",
@@ -231,6 +280,13 @@ const TOOLS = [
231
280
  },
232
281
  },
233
282
  },
283
+ annotations: {
284
+ title: "List Entities",
285
+ readOnlyHint: true,
286
+ destructiveHint: false,
287
+ idempotentHint: true,
288
+ openWorldHint: false,
289
+ },
234
290
  },
235
291
  {
236
292
  name: "stats",
@@ -239,6 +295,13 @@ const TOOLS = [
239
295
  type: "object" as const,
240
296
  properties: {},
241
297
  },
298
+ annotations: {
299
+ title: "Get Statistics",
300
+ readOnlyHint: true,
301
+ destructiveHint: false,
302
+ idempotentHint: true,
303
+ openWorldHint: false,
304
+ },
242
305
  },
243
306
  ];
244
307
 
@@ -27,6 +27,9 @@ export class HybridSearch {
27
27
 
28
28
  /**
29
29
  * Search using all available methods and fuse results
30
+ *
31
+ * Strategy: BM25 gets candidates fast, ColBERT reranks for quality
32
+ * This is both FASTER (fewer ColBERT computations) and BETTER (combines keyword + semantic)
30
33
  */
31
34
  async search(
32
35
  query: string,
@@ -36,6 +39,7 @@ export class HybridSearch {
36
39
  bm25Weight?: number;
37
40
  semanticWeight?: number;
38
41
  graphWeight?: number;
42
+ useReranking?: boolean; // Use ColBERT to rerank BM25 results
39
43
  } = {}
40
44
  ): Promise<HybridSearchResult[]> {
41
45
  const {
@@ -44,18 +48,30 @@ export class HybridSearch {
44
48
  bm25Weight = 1.0,
45
49
  semanticWeight = 1.0,
46
50
  graphWeight = 0.5,
51
+ useReranking = true, // Default: reranking mode for better quality
47
52
  } = options;
48
53
 
49
54
  // Fetch more candidates than needed for fusion
50
55
  const candidateLimit = Math.max(limit * 3, 30);
51
56
 
52
- // Run searches in parallel
53
- const [bm25Results, semanticResults, graphMemoryIds] = await Promise.all([
57
+ // Run BM25 and graph search in parallel (fast)
58
+ const [bm25Results, graphMemoryIds] = await Promise.all([
54
59
  this.searchBM25(query, candidateLimit),
55
- this.searchSemantic(query, candidateLimit),
56
60
  includeGraph ? this.searchGraph(query) : Promise.resolve([]),
57
61
  ]);
58
62
 
63
+ // For semantic: either rerank BM25 results (faster+better) or search full index
64
+ let semanticResults: Array<{ id: string; score: number }>;
65
+ if (useReranking && bm25Results.length > 0) {
66
+ // Rerank BM25 candidates with ColBERT - faster AND better quality
67
+ const docs = bm25Results.map(r => ({ id: r.id, content: this.db.getMemory(r.id)?.content || '' }));
68
+ const reranked = await this.retriever.rerank(query, docs, candidateLimit);
69
+ semanticResults = reranked.map(r => ({ id: r.id, score: r.score }));
70
+ } else {
71
+ // Full semantic search
72
+ semanticResults = await this.searchSemantic(query, candidateLimit);
73
+ }
74
+
59
75
  // Fetch graph memories
60
76
  const graphMemories = graphMemoryIds.length > 0
61
77
  ? graphMemoryIds.map(id => this.db.getMemory(id)).filter(Boolean) as Memory[]
@@ -47,6 +47,7 @@ export interface Relation {
47
47
 
48
48
  export class EngramDatabase {
49
49
  private db: Database.Database;
50
+ private stmtCache: Map<string, Database.Statement> = new Map();
50
51
 
51
52
  constructor(dbPath: string) {
52
53
  // Ensure directory exists
@@ -56,8 +57,15 @@ export class EngramDatabase {
56
57
  }
57
58
 
58
59
  this.db = new Database(dbPath);
59
- this.db.pragma("journal_mode = WAL"); // Better concurrent access
60
+
61
+ // Performance optimizations - all improve speed with no quality trade-off
62
+ this.db.pragma("journal_mode = WAL"); // Better concurrent access
63
+ this.db.pragma("synchronous = NORMAL"); // Faster writes, WAL provides safety
64
+ this.db.pragma("cache_size = -64000"); // 64MB cache (negative = KB)
65
+ this.db.pragma("mmap_size = 268435456"); // 256MB memory-mapped I/O
66
+ this.db.pragma("temp_store = MEMORY"); // Keep temp tables in RAM
60
67
  this.db.pragma("foreign_keys = ON");
68
+
61
69
  this.initialize();
62
70
  }
63
71
 
@@ -165,8 +173,7 @@ export class EngramDatabase {
165
173
  }
166
174
 
167
175
  getMemory(id: string): Memory | null {
168
- const stmt = this.db.prepare("SELECT * FROM memories WHERE id = ?");
169
- const row = stmt.get(id) as Record<string, unknown> | undefined;
176
+ const row = this.stmt("SELECT * FROM memories WHERE id = ?").get(id) as Record<string, unknown> | undefined;
170
177
  return row ? this.rowToMemory(row) : null;
171
178
  }
172
179
 
@@ -198,35 +205,27 @@ export class EngramDatabase {
198
205
  }
199
206
 
200
207
  touchMemory(id: string): void {
201
- const stmt = this.db.prepare(`
202
- UPDATE memories
203
- SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP
204
- WHERE id = ?
205
- `);
206
- stmt.run(id);
208
+ this.stmt(`UPDATE memories SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP WHERE id = ?`).run(id);
207
209
  }
208
210
 
209
211
  getAllMemories(limit: number = 1000): Memory[] {
210
- const stmt = this.db.prepare("SELECT * FROM memories ORDER BY timestamp DESC LIMIT ?");
211
- const rows = stmt.all(limit) as Record<string, unknown>[];
212
+ const rows = this.stmt("SELECT * FROM memories ORDER BY timestamp DESC LIMIT ?").all(limit) as Record<string, unknown>[];
212
213
  return rows.map((row) => this.rowToMemory(row));
213
214
  }
214
215
 
215
216
  // ============ BM25 Search ============
216
217
 
217
218
  searchBM25(query: string, limit: number = 20): Array<Memory & { score: number }> {
218
- const stmt = this.db.prepare(`
219
+ // Escape special FTS5 characters and format query
220
+ const escapedQuery = this.escapeFTS5Query(query);
221
+ const rows = this.stmt(`
219
222
  SELECT m.*, bm25(memories_fts) as score
220
223
  FROM memories_fts fts
221
224
  JOIN memories m ON fts.rowid = m.rowid
222
225
  WHERE memories_fts MATCH ?
223
226
  ORDER BY score
224
227
  LIMIT ?
225
- `);
226
-
227
- // Escape special FTS5 characters and format query
228
- const escapedQuery = this.escapeFTS5Query(query);
229
- const rows = stmt.all(escapedQuery, limit) as Array<Record<string, unknown>>;
228
+ `).all(escapedQuery, limit) as Array<Record<string, unknown>>;
230
229
 
231
230
  return rows.map((row) => ({
232
231
  ...this.rowToMemory(row),
@@ -262,14 +261,12 @@ export class EngramDatabase {
262
261
  }
263
262
 
264
263
  getEntity(id: string): Entity | null {
265
- const stmt = this.db.prepare("SELECT * FROM entities WHERE id = ?");
266
- const row = stmt.get(id) as Record<string, unknown> | undefined;
264
+ const row = this.stmt("SELECT * FROM entities WHERE id = ?").get(id) as Record<string, unknown> | undefined;
267
265
  return row ? this.rowToEntity(row) : null;
268
266
  }
269
267
 
270
268
  findEntityByName(name: string): Entity | null {
271
- const stmt = this.db.prepare("SELECT * FROM entities WHERE LOWER(name) = LOWER(?)");
272
- const row = stmt.get(name) as Record<string, unknown> | undefined;
269
+ const row = this.stmt("SELECT * FROM entities WHERE LOWER(name) = LOWER(?)").get(name) as Record<string, unknown> | undefined;
273
270
  return row ? this.rowToEntity(row) : null;
274
271
  }
275
272
 
@@ -329,8 +326,7 @@ export class EngramDatabase {
329
326
  }
330
327
 
331
328
  getObservation(id: string): Observation | null {
332
- const stmt = this.db.prepare("SELECT * FROM observations WHERE id = ?");
333
- const row = stmt.get(id) as Record<string, unknown> | undefined;
329
+ const row = this.stmt("SELECT * FROM observations WHERE id = ?").get(id) as Record<string, unknown> | undefined;
334
330
  return row ? this.rowToObservation(row) : null;
335
331
  }
336
332
 
@@ -369,8 +365,7 @@ export class EngramDatabase {
369
365
  }
370
366
 
371
367
  getRelation(id: string): Relation | null {
372
- const stmt = this.db.prepare("SELECT * FROM relations WHERE id = ?");
373
- const row = stmt.get(id) as Record<string, unknown> | undefined;
368
+ const row = this.stmt("SELECT * FROM relations WHERE id = ?").get(id) as Record<string, unknown> | undefined;
374
369
  return row ? this.rowToRelation(row) : null;
375
370
  }
376
371
 
@@ -466,12 +461,16 @@ export class EngramDatabase {
466
461
  relations: number;
467
462
  observations: number;
468
463
  } {
469
- const memories = (this.db.prepare("SELECT COUNT(*) as count FROM memories").get() as { count: number }).count;
470
- const entities = (this.db.prepare("SELECT COUNT(*) as count FROM entities").get() as { count: number }).count;
471
- const relations = (this.db.prepare("SELECT COUNT(*) as count FROM relations").get() as { count: number }).count;
472
- const observations = (this.db.prepare("SELECT COUNT(*) as count FROM observations").get() as { count: number }).count;
464
+ // Single query for all stats - much faster than 4 separate queries
465
+ const row = this.stmt(`
466
+ SELECT
467
+ (SELECT COUNT(*) FROM memories) as memories,
468
+ (SELECT COUNT(*) FROM entities) as entities,
469
+ (SELECT COUNT(*) FROM relations) as relations,
470
+ (SELECT COUNT(*) FROM observations) as observations
471
+ `).get() as { memories: number; entities: number; relations: number; observations: number };
473
472
 
474
- return { memories, entities, relations, observations };
473
+ return row;
475
474
  }
476
475
 
477
476
  // ============ Utilities ============
@@ -480,6 +479,18 @@ export class EngramDatabase {
480
479
  this.db.close();
481
480
  }
482
481
 
482
+ /**
483
+ * Get a cached prepared statement - avoids re-parsing SQL
484
+ */
485
+ private stmt(sql: string): Database.Statement {
486
+ let cached = this.stmtCache.get(sql);
487
+ if (!cached) {
488
+ cached = this.db.prepare(sql);
489
+ this.stmtCache.set(sql, cached);
490
+ }
491
+ return cached;
492
+ }
493
+
483
494
  private rowToMemory(row: Record<string, unknown>): Memory {
484
495
  return {
485
496
  id: row.id as string,