@membank/core 0.0.0-dev-20260427133418

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/dist/index.cjs ADDED
@@ -0,0 +1,377 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let node_fs = require("node:fs");
25
+ let node_os = require("node:os");
26
+ let node_path = require("node:path");
27
+ let better_sqlite3 = require("better-sqlite3");
28
+ better_sqlite3 = __toESM(better_sqlite3, 1);
29
+ let sqlite_vec = require("sqlite-vec");
30
+ sqlite_vec = __toESM(sqlite_vec, 1);
31
+ let _huggingface_transformers = require("@huggingface/transformers");
32
+ let node_crypto = require("node:crypto");
33
+ let node_child_process = require("node:child_process");
34
+ let node_util = require("node:util");
35
+ //#region src/db/errors.ts
36
+ var MembankError = class extends Error {
37
+ constructor(message, options) {
38
+ super(message, options);
39
+ this.name = "MembankError";
40
+ }
41
+ };
42
+ var DatabaseError = class extends MembankError {
43
+ constructor(message, options) {
44
+ super(message, options);
45
+ this.name = "DatabaseError";
46
+ }
47
+ };
48
+ //#endregion
49
+ //#region src/db/manager.ts
50
+ const DEFAULT_DB_PATH = (0, node_path.join)((0, node_os.homedir)(), ".membank", "memory.db");
51
+ const MIGRATIONS = [[1, `
52
+ CREATE TABLE IF NOT EXISTS memories (
53
+ id TEXT PRIMARY KEY,
54
+ content TEXT NOT NULL,
55
+ type TEXT NOT NULL,
56
+ tags TEXT NOT NULL DEFAULT '[]',
57
+ scope TEXT NOT NULL,
58
+ source TEXT,
59
+ access_count INTEGER NOT NULL DEFAULT 0,
60
+ pinned INTEGER NOT NULL DEFAULT 0,
61
+ needs_review INTEGER NOT NULL DEFAULT 0,
62
+ created_at TEXT NOT NULL,
63
+ updated_at TEXT NOT NULL
64
+ );
65
+
66
+ CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
67
+ embedding FLOAT[384]
68
+ );
69
+ `]];
70
+ var DatabaseManager = class DatabaseManager {
71
+ #db;
72
+ constructor(db) {
73
+ this.#db = db;
74
+ }
75
+ static open(dbPath) {
76
+ const resolvedPath = dbPath ?? DEFAULT_DB_PATH;
77
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(resolvedPath), { recursive: true });
78
+ const db = new better_sqlite3.default(resolvedPath);
79
+ return DatabaseManager.#init(db, sqlite_vec.load);
80
+ }
81
+ static openInMemory() {
82
+ return DatabaseManager.#initInMemory(sqlite_vec.load);
83
+ }
84
+ /** For testing: inject a custom vec loader (e.g. a throwing stub). */
85
+ static _openInMemoryWithLoader(loader) {
86
+ return DatabaseManager.#initInMemory(loader);
87
+ }
88
+ static #initInMemory(loader) {
89
+ const db = new better_sqlite3.default(":memory:");
90
+ return DatabaseManager.#init(db, loader);
91
+ }
92
+ static #init(db, loader) {
93
+ try {
94
+ loader(db);
95
+ } catch (err) {
96
+ throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
97
+ }
98
+ db.pragma("journal_mode = WAL");
99
+ const manager = new DatabaseManager(db);
100
+ manager.#runMigrations();
101
+ return manager;
102
+ }
103
+ #runMigrations() {
104
+ this.#db.exec(`
105
+ CREATE TABLE IF NOT EXISTS meta (
106
+ key TEXT PRIMARY KEY,
107
+ value TEXT NOT NULL
108
+ );
109
+ `);
110
+ const row = this.#db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
111
+ const currentVersion = row ? Number.parseInt(row.value, 10) : 0;
112
+ for (const [targetVersion, sql] of MIGRATIONS) if (currentVersion < targetVersion) {
113
+ this.#db.exec(sql);
114
+ this.#db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)").run(String(targetVersion));
115
+ }
116
+ }
117
+ get db() {
118
+ return this.#db;
119
+ }
120
+ close() {
121
+ this.#db.close();
122
+ }
123
+ };
124
+ //#endregion
125
+ //#region src/embedding/service.ts
126
+ var EmbeddingService = class {
127
+ modelCachePath;
128
+ onProgress;
129
+ pipelineInstance = null;
130
+ constructor(modelCachePath, onProgress) {
131
+ this.modelCachePath = modelCachePath ?? (0, node_path.join)((0, node_os.homedir)(), ".membank", "models");
132
+ this.onProgress = onProgress;
133
+ }
134
+ async getPipeline() {
135
+ if (this.pipelineInstance === null) this.pipelineInstance = await (0, _huggingface_transformers.pipeline)("feature-extraction", "Xenova/bge-small-en-v1.5", {
136
+ cache_dir: this.modelCachePath,
137
+ progress_callback: this.onProgress
138
+ });
139
+ return this.pipelineInstance;
140
+ }
141
+ async embed(text) {
142
+ const flat = (await (await this.getPipeline())(text, {
143
+ pooling: "mean",
144
+ normalize: true
145
+ })).data;
146
+ return flat instanceof Float32Array ? flat : new Float32Array(flat);
147
+ }
148
+ };
149
+ //#endregion
150
+ //#region src/memory/repository.ts
151
+ var MemoryRepository = class {
152
+ #db;
153
+ #embedding;
154
+ constructor(db, embeddingService) {
155
+ this.#db = db;
156
+ this.#embedding = embeddingService;
157
+ }
158
+ async save(options) {
159
+ const { content, type, tags = [], scope = "global", sourceHarness } = options;
160
+ const embedding = await this.#embedding.embed(content);
161
+ const embeddingBlob = Buffer.from(embedding.buffer);
162
+ const top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
163
+ FROM memories m JOIN embeddings e ON e.rowid = m.rowid
164
+ WHERE m.type = ? AND m.scope = ?
165
+ ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, scope);
166
+ const now = (/* @__PURE__ */ new Date()).toISOString();
167
+ if (top !== void 0 && top.similarity > .92) {
168
+ this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
169
+ this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
170
+ const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id);
171
+ return this.#rowToMemory(updated);
172
+ }
173
+ if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
174
+ const id = (0, node_crypto.randomUUID)();
175
+ this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)
176
+ VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);
177
+ this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
178
+ const row = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
179
+ return this.#rowToMemory(row);
180
+ }
181
+ async update(id, patch) {
182
+ const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
183
+ if (existing === void 0) throw new Error(`Memory not found: ${id}`);
184
+ const now = (/* @__PURE__ */ new Date()).toISOString();
185
+ const sets = ["updated_at = ?"];
186
+ const values = [now];
187
+ if (patch.content !== void 0) {
188
+ sets.push("content = ?");
189
+ values.push(patch.content);
190
+ }
191
+ if (patch.tags !== void 0) {
192
+ sets.push("tags = ?");
193
+ values.push(JSON.stringify(patch.tags));
194
+ }
195
+ values.push(id);
196
+ this.#db.db.prepare(`UPDATE memories SET ${sets.join(", ")} WHERE id = ?`).run(...values);
197
+ if (patch.content !== void 0) {
198
+ const embedding = await this.#embedding.embed(patch.content);
199
+ const embeddingBlob = Buffer.from(embedding.buffer);
200
+ this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
201
+ }
202
+ const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
203
+ return this.#rowToMemory(updated);
204
+ }
205
+ delete(id) {
206
+ const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
207
+ if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
208
+ this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
209
+ return Promise.resolve();
210
+ }
211
+ incrementAccessCount(id) {
212
+ this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
213
+ }
214
+ #rowToMemory(row) {
215
+ return {
216
+ id: row.id,
217
+ content: row.content,
218
+ type: row.type,
219
+ tags: JSON.parse(row.tags),
220
+ scope: row.scope,
221
+ sourceHarness: row.source,
222
+ accessCount: row.access_count,
223
+ pinned: row.pinned !== 0,
224
+ needsReview: row.needs_review !== 0,
225
+ createdAt: row.created_at,
226
+ updatedAt: row.updated_at
227
+ };
228
+ }
229
+ };
230
+ //#endregion
231
+ //#region src/query/engine.ts
232
+ const TYPE_WEIGHTS = {
233
+ correction: 1,
234
+ preference: .8,
235
+ decision: .6,
236
+ learning: .4,
237
+ fact: .2
238
+ };
239
+ var QueryEngine = class {
240
+ #db;
241
+ #embedding;
242
+ #repo;
243
+ constructor(db, embeddingService, repo) {
244
+ this.#db = db;
245
+ this.#embedding = embeddingService;
246
+ this.#repo = repo;
247
+ }
248
+ async query(options) {
249
+ const { query, type, scope, limit = 10 } = options;
250
+ const queryEmbedding = await this.#embedding.embed(query);
251
+ const queryBlob = Buffer.from(queryEmbedding.buffer);
252
+ const whereClauses = [];
253
+ const params = [queryBlob];
254
+ if (type !== void 0) {
255
+ whereClauses.push("m.type = ?");
256
+ params.push(type);
257
+ }
258
+ if (scope !== void 0) {
259
+ whereClauses.push("m.scope = ?");
260
+ params.push(scope);
261
+ }
262
+ const sql = `
263
+ SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
264
+ FROM memories m JOIN embeddings e ON e.rowid = m.rowid
265
+ ${whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : ""}
266
+ `;
267
+ const rows = this.#db.db.prepare(sql).all(...params);
268
+ const now = Date.now();
269
+ const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
270
+ const memory = this.#rowToMemory(row);
271
+ const score = this.#computeScore(memory, now);
272
+ return {
273
+ ...memory,
274
+ score
275
+ };
276
+ });
277
+ scored.sort((a, b) => b.score - a.score);
278
+ const results = scored.slice(0, limit);
279
+ for (const result of results) this.#repo.incrementAccessCount(result.id);
280
+ return results;
281
+ }
282
+ #computeScore(memory, now) {
283
+ const typeWeight = TYPE_WEIGHTS[memory.type];
284
+ const accessCountNorm = memory.accessCount / (memory.accessCount + 10);
285
+ const recencyNorm = 1 / (1 + (now - new Date(memory.updatedAt).getTime()) / 864e5);
286
+ const pinned = memory.pinned ? 1 : 0;
287
+ return typeWeight * .4 + accessCountNorm * .3 + recencyNorm * .2 + pinned * .1;
288
+ }
289
+ #rowToMemory(row) {
290
+ return {
291
+ id: row.id,
292
+ content: row.content,
293
+ type: row.type,
294
+ tags: JSON.parse(row.tags),
295
+ scope: row.scope,
296
+ sourceHarness: row.source,
297
+ accessCount: row.access_count,
298
+ pinned: row.pinned !== 0,
299
+ needsReview: row.needs_review !== 0,
300
+ createdAt: row.created_at,
301
+ updatedAt: row.updated_at
302
+ };
303
+ }
304
+ };
305
+ //#endregion
306
+ //#region src/scope/resolver.ts
307
+ const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
308
+ function sha256Truncated(input) {
309
+ return (0, node_crypto.createHash)("sha256").update(input).digest("hex").slice(0, 16);
310
+ }
311
+ async function resolveScope() {
312
+ try {
313
+ const { stdout } = await execFileAsync("git", [
314
+ "remote",
315
+ "get-url",
316
+ "origin"
317
+ ]);
318
+ const url = stdout.trim();
319
+ if (url) return sha256Truncated(url);
320
+ } catch {}
321
+ return sha256Truncated(process.cwd());
322
+ }
323
+ //#endregion
324
+ //#region src/session/builder.ts
325
+ const MEMORY_TYPES = [
326
+ "correction",
327
+ "preference",
328
+ "decision",
329
+ "learning",
330
+ "fact"
331
+ ];
332
+ function rowToMemory(row) {
333
+ return {
334
+ id: row.id,
335
+ content: row.content,
336
+ type: row.type,
337
+ tags: JSON.parse(row.tags),
338
+ scope: row.scope,
339
+ sourceHarness: row.source,
340
+ accessCount: row.access_count,
341
+ pinned: row.pinned !== 0,
342
+ needsReview: row.needs_review !== 0,
343
+ createdAt: row.created_at,
344
+ updatedAt: row.updated_at
345
+ };
346
+ }
347
+ function listMemoryTypes() {
348
+ return [...MEMORY_TYPES];
349
+ }
350
+ var SessionContextBuilder = class {
351
+ #db;
352
+ constructor(db) {
353
+ this.#db = db;
354
+ }
355
+ getSessionContext(projectScope) {
356
+ const pinnedGlobal = this.#db.db.prepare("SELECT * FROM memories WHERE scope = 'global' AND pinned = 1").all().map(rowToMemory);
357
+ const pinnedProject = this.#db.db.prepare("SELECT * FROM memories WHERE scope = ? AND pinned = 1").all(projectScope).map(rowToMemory);
358
+ const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
359
+ const stats = Object.fromEntries(MEMORY_TYPES.map((t) => [t, 0]));
360
+ for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
361
+ return {
362
+ stats,
363
+ pinnedGlobal,
364
+ pinnedProject
365
+ };
366
+ }
367
+ };
368
+ //#endregion
369
+ exports.DatabaseError = DatabaseError;
370
+ exports.DatabaseManager = DatabaseManager;
371
+ exports.EmbeddingService = EmbeddingService;
372
+ exports.MembankError = MembankError;
373
+ exports.MemoryRepository = MemoryRepository;
374
+ exports.QueryEngine = QueryEngine;
375
+ exports.SessionContextBuilder = SessionContextBuilder;
376
+ exports.listMemoryTypes = listMemoryTypes;
377
+ exports.resolveScope = resolveScope;
@@ -0,0 +1,115 @@
1
+ import BetterSqlite3 from "better-sqlite3";
2
+
3
+ //#region src/db/errors.d.ts
4
+ declare class MembankError extends Error {
5
+ constructor(message: string, options?: ErrorOptions);
6
+ }
7
+ declare class DatabaseError extends MembankError {
8
+ constructor(message: string, options?: ErrorOptions);
9
+ }
10
+ //#endregion
11
+ //#region src/db/manager.d.ts
12
+ type VecLoader = (db: BetterSqlite3.Database) => void;
13
+ declare class DatabaseManager {
14
+ #private;
15
+ private constructor();
16
+ static open(dbPath?: string): DatabaseManager;
17
+ static openInMemory(): DatabaseManager;
18
+ /** For testing: inject a custom vec loader (e.g. a throwing stub). */
19
+ static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager;
20
+ get db(): BetterSqlite3.Database;
21
+ close(): void;
22
+ }
23
+ //#endregion
24
+ //#region src/embedding/service.d.ts
25
+ type ProgressCallback = (progress: {
26
+ status: string;
27
+ progress?: number;
28
+ }) => void;
29
+ declare class EmbeddingService {
30
+ private readonly modelCachePath;
31
+ private readonly onProgress;
32
+ private pipelineInstance;
33
+ constructor(modelCachePath?: string, onProgress?: ProgressCallback);
34
+ private getPipeline;
35
+ embed(text: string): Promise<Float32Array>;
36
+ }
37
+ //#endregion
38
+ //#region src/types.d.ts
39
+ type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
40
+ interface Memory {
41
+ id: string;
42
+ content: string;
43
+ type: MemoryType;
44
+ tags: string[];
45
+ scope: string;
46
+ sourceHarness: string | null;
47
+ accessCount: number;
48
+ pinned: boolean;
49
+ needsReview: boolean;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ }
53
+ interface QueryOptions {
54
+ query: string;
55
+ type?: MemoryType;
56
+ scope?: string;
57
+ limit?: number;
58
+ }
59
+ interface SaveOptions {
60
+ content: string;
61
+ type: MemoryType;
62
+ tags?: string[];
63
+ scope?: string;
64
+ sourceHarness?: string;
65
+ }
66
+ interface SessionContext {
67
+ stats: Record<MemoryType, number>;
68
+ pinnedGlobal: Memory[];
69
+ pinnedProject: Memory[];
70
+ }
71
+ //#endregion
72
+ //#region src/memory/repository.d.ts
73
+ declare class MemoryRepository {
74
+ #private;
75
+ constructor(db: DatabaseManager, embeddingService: EmbeddingService);
76
+ save(options: SaveOptions): Promise<Memory>;
77
+ update(id: string, patch: {
78
+ content?: string;
79
+ tags?: string[];
80
+ }): Promise<Memory>;
81
+ delete(id: string): Promise<void>;
82
+ list(opts?: {
83
+ type?: MemoryType;
84
+ pinned?: boolean;
85
+ }): Memory[];
86
+ stats(): {
87
+ byType: Record<MemoryType, number>;
88
+ total: number;
89
+ needsReview: number;
90
+ };
91
+ incrementAccessCount(id: string): void;
92
+ }
93
+ //#endregion
94
+ //#region src/query/engine.d.ts
95
+ declare class QueryEngine {
96
+ #private;
97
+ constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository);
98
+ query(options: QueryOptions): Promise<Array<Memory & {
99
+ score: number;
100
+ }>>;
101
+ }
102
+ //#endregion
103
+ //#region src/scope/resolver.d.ts
104
+ declare function resolveScope(): Promise<string>;
105
+ //#endregion
106
+ //#region src/session/builder.d.ts
107
+ declare function listMemoryTypes(): MemoryType[];
108
+ declare class SessionContextBuilder {
109
+ #private;
110
+ constructor(db: DatabaseManager);
111
+ getSessionContext(projectScope: string): SessionContext;
112
+ }
113
+ //#endregion
114
+ export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, Memory, MemoryRepository, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope };
115
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/embedding/service.ts","../src/types.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"mappings":";;;cAAa,YAAA,SAAqB,KAAA;cACpB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;AAAA,cAM5B,aAAA,SAAsB,YAAA;cACrB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;KCCpC,SAAA,IAAa,EAAA,EAAI,aAAA,CAAc,QAAA;AAAA,cA4BvB,eAAA;EAAA;UAGJ,WAAA,CAAA;EAAA,OAIA,IAAA,CAAK,MAAA,YAAkB,eAAA;EAAA,OAOvB,YAAA,CAAA,GAAgB,eAAA;EDnDS;EAAA,OCwDzB,uBAAA,CAAwB,MAAA,EAAQ,SAAA,GAAY,eAAA;EAAA,IAkD/C,EAAA,CAAA,GAAM,aAAA,CAAc,QAAA;EAIxB,KAAA,CAAA;AAAA;;;KC1GU,gBAAA,IAAoB,QAAA;EAAY,MAAA;EAAgB,QAAA;AAAA;AAAA,cAE/C,gBAAA;EAAA,iBACM,cAAA;EAAA,iBACA,UAAA;EAAA,QACT,gBAAA;cAEI,cAAA,WAAyB,UAAA,GAAa,gBAAA;EAAA,QAKpC,WAAA;EAUR,KAAA,CAAM,IAAA,WAAe,OAAA,CAAQ,YAAA;AAAA;;;KC1BzB,UAAA;AAAA,UAEK,MAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;EACA,WAAA;EACA,MAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA;EACA,IAAA,GAAO,UAAA;EACP,KAAA;EACA,KAAA;AAAA;AAAA,UAGe,WAAA;EACf,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA,EAAO,MAAA,CAAO,UAAA;EACd,YAAA,EAAc,MAAA;EACd,aAAA,EAAe,MAAA;AAAA;;;cCVJ,gBAAA;EAAA;cAIC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA;EAK7C,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,MAAA;EAgEpC,MAAA,CAAO,EAAA,UAAY,KAAA;IAAS,OAAA;IAAkB,IAAA;EAAA,IAAoB,OAAA,CAAQ,MAAA;EA2ChF,MAAA,CAAO,EAAA,WAAa,OAAA;EAcpB,IAAA,CAAK,IAAA;IAAS,IAAA,GAAO,UAAA;IAAY,MAAA;EAAA,IAAqB,MAAA;EAqBtD,KAAA,CAAA;IAAW,MAAA,EAAQ,MAAA,CAAO,UAAA;IAAqB,KAAA;IAAe,WAAA;EAAA;EA2B9D,oBAAA,CAAqB,EAAA;AAAA;;;cC9KV,WAAA;EAAA;cAKC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA,EAAkB,IAAA,EAAM,gBAAA;EAMrE,KAAA,CAAM,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,MAAA;IAAW,KAAA;EAAA;AAAA;;;iBC7BzC,YAAA,CAAA,GAAgB,OAAA;;;iBC8BtB,eAAA,CAAA,GAAmB,UAAA;AAAA,cAItB,qBAAA;EAAA;cAGC,EAAA,EAAI,eAAA;EAIhB,iBAAA,CAAkB,YAAA,WAAuB,cAAA;AAAA"}
@@ -0,0 +1,115 @@
1
+ import BetterSqlite3 from "better-sqlite3";
2
+
3
+ //#region src/db/errors.d.ts
4
+ declare class MembankError extends Error {
5
+ constructor(message: string, options?: ErrorOptions);
6
+ }
7
+ declare class DatabaseError extends MembankError {
8
+ constructor(message: string, options?: ErrorOptions);
9
+ }
10
+ //#endregion
11
+ //#region src/db/manager.d.ts
12
+ type VecLoader = (db: BetterSqlite3.Database) => void;
13
+ declare class DatabaseManager {
14
+ #private;
15
+ private constructor();
16
+ static open(dbPath?: string): DatabaseManager;
17
+ static openInMemory(): DatabaseManager;
18
+ /** For testing: inject a custom vec loader (e.g. a throwing stub). */
19
+ static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager;
20
+ get db(): BetterSqlite3.Database;
21
+ close(): void;
22
+ }
23
+ //#endregion
24
+ //#region src/embedding/service.d.ts
25
+ type ProgressCallback = (progress: {
26
+ status: string;
27
+ progress?: number;
28
+ }) => void;
29
+ declare class EmbeddingService {
30
+ private readonly modelCachePath;
31
+ private readonly onProgress;
32
+ private pipelineInstance;
33
+ constructor(modelCachePath?: string, onProgress?: ProgressCallback);
34
+ private getPipeline;
35
+ embed(text: string): Promise<Float32Array>;
36
+ }
37
+ //#endregion
38
+ //#region src/types.d.ts
39
+ type MemoryType = "correction" | "preference" | "decision" | "learning" | "fact";
40
+ interface Memory {
41
+ id: string;
42
+ content: string;
43
+ type: MemoryType;
44
+ tags: string[];
45
+ scope: string;
46
+ sourceHarness: string | null;
47
+ accessCount: number;
48
+ pinned: boolean;
49
+ needsReview: boolean;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ }
53
+ interface QueryOptions {
54
+ query: string;
55
+ type?: MemoryType;
56
+ scope?: string;
57
+ limit?: number;
58
+ }
59
+ interface SaveOptions {
60
+ content: string;
61
+ type: MemoryType;
62
+ tags?: string[];
63
+ scope?: string;
64
+ sourceHarness?: string;
65
+ }
66
+ interface SessionContext {
67
+ stats: Record<MemoryType, number>;
68
+ pinnedGlobal: Memory[];
69
+ pinnedProject: Memory[];
70
+ }
71
+ //#endregion
72
+ //#region src/memory/repository.d.ts
73
+ declare class MemoryRepository {
74
+ #private;
75
+ constructor(db: DatabaseManager, embeddingService: EmbeddingService);
76
+ save(options: SaveOptions): Promise<Memory>;
77
+ update(id: string, patch: {
78
+ content?: string;
79
+ tags?: string[];
80
+ }): Promise<Memory>;
81
+ delete(id: string): Promise<void>;
82
+ list(opts?: {
83
+ type?: MemoryType;
84
+ pinned?: boolean;
85
+ }): Memory[];
86
+ stats(): {
87
+ byType: Record<MemoryType, number>;
88
+ total: number;
89
+ needsReview: number;
90
+ };
91
+ incrementAccessCount(id: string): void;
92
+ }
93
+ //#endregion
94
+ //#region src/query/engine.d.ts
95
+ declare class QueryEngine {
96
+ #private;
97
+ constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository);
98
+ query(options: QueryOptions): Promise<Array<Memory & {
99
+ score: number;
100
+ }>>;
101
+ }
102
+ //#endregion
103
+ //#region src/scope/resolver.d.ts
104
+ declare function resolveScope(): Promise<string>;
105
+ //#endregion
106
+ //#region src/session/builder.d.ts
107
+ declare function listMemoryTypes(): MemoryType[];
108
+ declare class SessionContextBuilder {
109
+ #private;
110
+ constructor(db: DatabaseManager);
111
+ getSessionContext(projectScope: string): SessionContext;
112
+ }
113
+ //#endregion
114
+ export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, Memory, MemoryRepository, MemoryType, ProgressCallback, QueryEngine, QueryOptions, SaveOptions, SessionContext, SessionContextBuilder, listMemoryTypes, resolveScope };
115
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/embedding/service.ts","../src/types.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"mappings":";;;cAAa,YAAA,SAAqB,KAAA;cACpB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;AAAA,cAM5B,aAAA,SAAsB,YAAA;cACrB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;KCCpC,SAAA,IAAa,EAAA,EAAI,aAAA,CAAc,QAAA;AAAA,cA4BvB,eAAA;EAAA;UAGJ,WAAA,CAAA;EAAA,OAIA,IAAA,CAAK,MAAA,YAAkB,eAAA;EAAA,OAOvB,YAAA,CAAA,GAAgB,eAAA;EDnDS;EAAA,OCwDzB,uBAAA,CAAwB,MAAA,EAAQ,SAAA,GAAY,eAAA;EAAA,IAkD/C,EAAA,CAAA,GAAM,aAAA,CAAc,QAAA;EAIxB,KAAA,CAAA;AAAA;;;KC1GU,gBAAA,IAAoB,QAAA;EAAY,MAAA;EAAgB,QAAA;AAAA;AAAA,cAE/C,gBAAA;EAAA,iBACM,cAAA;EAAA,iBACA,UAAA;EAAA,QACT,gBAAA;cAEI,cAAA,WAAyB,UAAA,GAAa,gBAAA;EAAA,QAKpC,WAAA;EAUR,KAAA,CAAM,IAAA,WAAe,OAAA,CAAQ,YAAA;AAAA;;;KC1BzB,UAAA;AAAA,UAEK,MAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;EACA,WAAA;EACA,MAAA;EACA,WAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA;EACA,IAAA,GAAO,UAAA;EACP,KAAA;EACA,KAAA;AAAA;AAAA,UAGe,WAAA;EACf,OAAA;EACA,IAAA,EAAM,UAAA;EACN,IAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA,EAAO,MAAA,CAAO,UAAA;EACd,YAAA,EAAc,MAAA;EACd,aAAA,EAAe,MAAA;AAAA;;;cCVJ,gBAAA;EAAA;cAIC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA;EAK7C,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,MAAA;EAgEpC,MAAA,CAAO,EAAA,UAAY,KAAA;IAAS,OAAA;IAAkB,IAAA;EAAA,IAAoB,OAAA,CAAQ,MAAA;EA2ChF,MAAA,CAAO,EAAA,WAAa,OAAA;EAcpB,IAAA,CAAK,IAAA;IAAS,IAAA,GAAO,UAAA;IAAY,MAAA;EAAA,IAAqB,MAAA;EAqBtD,KAAA,CAAA;IAAW,MAAA,EAAQ,MAAA,CAAO,UAAA;IAAqB,KAAA;IAAe,WAAA;EAAA;EA2B9D,oBAAA,CAAqB,EAAA;AAAA;;;cC9KV,WAAA;EAAA;cAKC,EAAA,EAAI,eAAA,EAAiB,gBAAA,EAAkB,gBAAA,EAAkB,IAAA,EAAM,gBAAA;EAMrE,KAAA,CAAM,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,MAAA;IAAW,KAAA;EAAA;AAAA;;;iBC7BzC,YAAA,CAAA,GAAgB,OAAA;;;iBC8BtB,eAAA,CAAA,GAAmB,UAAA;AAAA,cAItB,qBAAA;EAAA;cAGC,EAAA,EAAI,eAAA;EAIhB,iBAAA,CAAkB,YAAA,WAAuB,cAAA;AAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,377 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import BetterSqlite3 from "better-sqlite3";
5
+ import * as sqliteVec from "sqlite-vec";
6
+ import { pipeline } from "@huggingface/transformers";
7
+ import { createHash, randomUUID } from "node:crypto";
8
+ import { execFile } from "node:child_process";
9
+ import { promisify } from "node:util";
10
+ //#region src/db/errors.ts
11
+ var MembankError = class extends Error {
12
+ constructor(message, options) {
13
+ super(message, options);
14
+ this.name = "MembankError";
15
+ }
16
+ };
17
+ var DatabaseError = class extends MembankError {
18
+ constructor(message, options) {
19
+ super(message, options);
20
+ this.name = "DatabaseError";
21
+ }
22
+ };
23
+ //#endregion
24
+ //#region src/db/manager.ts
25
+ const DEFAULT_DB_PATH = join(homedir(), ".membank", "memory.db");
26
+ const MIGRATIONS = [[1, `
27
+ CREATE TABLE IF NOT EXISTS memories (
28
+ id TEXT PRIMARY KEY,
29
+ content TEXT NOT NULL,
30
+ type TEXT NOT NULL,
31
+ tags TEXT NOT NULL DEFAULT '[]',
32
+ scope TEXT NOT NULL,
33
+ source TEXT,
34
+ access_count INTEGER NOT NULL DEFAULT 0,
35
+ pinned INTEGER NOT NULL DEFAULT 0,
36
+ needs_review INTEGER NOT NULL DEFAULT 0,
37
+ created_at TEXT NOT NULL,
38
+ updated_at TEXT NOT NULL
39
+ );
40
+
41
+ CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
42
+ embedding FLOAT[384]
43
+ );
44
+ `]];
45
+ var DatabaseManager = class DatabaseManager {
46
+ #db;
47
+ constructor(db) {
48
+ this.#db = db;
49
+ }
50
+ static open(dbPath) {
51
+ const resolvedPath = dbPath ?? DEFAULT_DB_PATH;
52
+ mkdirSync(dirname(resolvedPath), { recursive: true });
53
+ const db = new BetterSqlite3(resolvedPath);
54
+ return DatabaseManager.#init(db, sqliteVec.load);
55
+ }
56
+ static openInMemory() {
57
+ return DatabaseManager.#initInMemory(sqliteVec.load);
58
+ }
59
+ /** For testing: inject a custom vec loader (e.g. a throwing stub). */
60
+ static _openInMemoryWithLoader(loader) {
61
+ return DatabaseManager.#initInMemory(loader);
62
+ }
63
+ static #initInMemory(loader) {
64
+ const db = new BetterSqlite3(":memory:");
65
+ return DatabaseManager.#init(db, loader);
66
+ }
67
+ static #init(db, loader) {
68
+ try {
69
+ loader(db);
70
+ } catch (err) {
71
+ throw new DatabaseError("Failed to load sqlite-vec extension", { cause: err });
72
+ }
73
+ db.pragma("journal_mode = WAL");
74
+ const manager = new DatabaseManager(db);
75
+ manager.#runMigrations();
76
+ return manager;
77
+ }
78
+ #runMigrations() {
79
+ this.#db.exec(`
80
+ CREATE TABLE IF NOT EXISTS meta (
81
+ key TEXT PRIMARY KEY,
82
+ value TEXT NOT NULL
83
+ );
84
+ `);
85
+ const row = this.#db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
86
+ const currentVersion = row ? Number.parseInt(row.value, 10) : 0;
87
+ for (const [targetVersion, sql] of MIGRATIONS) if (currentVersion < targetVersion) {
88
+ this.#db.exec(sql);
89
+ this.#db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)").run(String(targetVersion));
90
+ }
91
+ }
92
+ get db() {
93
+ return this.#db;
94
+ }
95
+ close() {
96
+ this.#db.close();
97
+ }
98
+ };
99
+ //#endregion
100
+ //#region src/embedding/service.ts
101
+ var EmbeddingService = class {
102
+ modelCachePath;
103
+ onProgress;
104
+ pipelineInstance = null;
105
+ constructor(modelCachePath, onProgress) {
106
+ this.modelCachePath = modelCachePath ?? join(homedir(), ".membank", "models");
107
+ this.onProgress = onProgress;
108
+ }
109
+ async getPipeline() {
110
+ if (this.pipelineInstance === null) this.pipelineInstance = await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5", {
111
+ cache_dir: this.modelCachePath,
112
+ progress_callback: this.onProgress
113
+ });
114
+ return this.pipelineInstance;
115
+ }
116
+ async embed(text) {
117
+ const flat = (await (await this.getPipeline())(text, {
118
+ pooling: "mean",
119
+ normalize: true
120
+ })).data;
121
+ return flat instanceof Float32Array ? flat : new Float32Array(flat);
122
+ }
123
+ };
124
+ //#endregion
125
+ //#region src/memory/repository.ts
126
+ var MemoryRepository = class {
127
+ #db;
128
+ #embedding;
129
+ constructor(db, embeddingService) {
130
+ this.#db = db;
131
+ this.#embedding = embeddingService;
132
+ }
133
+ async save(options) {
134
+ const { content, type, tags = [], scope = "global", sourceHarness } = options;
135
+ const embedding = await this.#embedding.embed(content);
136
+ const embeddingBlob = Buffer.from(embedding.buffer);
137
+ const top = this.#db.db.prepare(`SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity
138
+ FROM memories m JOIN embeddings e ON e.rowid = m.rowid
139
+ WHERE m.type = ? AND m.scope = ?
140
+ ORDER BY similarity DESC LIMIT 1`).get(embeddingBlob, type, scope);
141
+ const now = (/* @__PURE__ */ new Date()).toISOString();
142
+ if (top !== void 0 && top.similarity > .92) {
143
+ this.#db.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(content, now, top.id);
144
+ this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, top.rowid);
145
+ const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(top.id);
146
+ return this.#rowToMemory(updated);
147
+ }
148
+ if (top !== void 0 && top.similarity >= .75) this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);
149
+ const id = randomUUID();
150
+ this.#db.db.prepare(`INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)
151
+ VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`).run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);
152
+ this.#db.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(embeddingBlob, id);
153
+ const row = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
154
+ return this.#rowToMemory(row);
155
+ }
156
+ async update(id, patch) {
157
+ const existing = this.#db.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(id);
158
+ if (existing === void 0) throw new Error(`Memory not found: ${id}`);
159
+ const now = (/* @__PURE__ */ new Date()).toISOString();
160
+ const sets = ["updated_at = ?"];
161
+ const values = [now];
162
+ if (patch.content !== void 0) {
163
+ sets.push("content = ?");
164
+ values.push(patch.content);
165
+ }
166
+ if (patch.tags !== void 0) {
167
+ sets.push("tags = ?");
168
+ values.push(JSON.stringify(patch.tags));
169
+ }
170
+ values.push(id);
171
+ this.#db.db.prepare(`UPDATE memories SET ${sets.join(", ")} WHERE id = ?`).run(...values);
172
+ if (patch.content !== void 0) {
173
+ const embedding = await this.#embedding.embed(patch.content);
174
+ const embeddingBlob = Buffer.from(embedding.buffer);
175
+ this.#db.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(embeddingBlob, existing.rowid);
176
+ }
177
+ const updated = this.#db.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(id);
178
+ return this.#rowToMemory(updated);
179
+ }
180
+ delete(id) {
181
+ const row = this.#db.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(id);
182
+ if (row !== void 0) this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);
183
+ this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
184
+ return Promise.resolve();
185
+ }
186
+ list(opts) {
187
+ const conditions = [];
188
+ const params = [];
189
+ if (opts?.type !== void 0) {
190
+ conditions.push("type = ?");
191
+ params.push(opts.type);
192
+ }
193
+ if (opts?.pinned === true) conditions.push("pinned = 1");
194
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
195
+ return this.#db.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC`).all(...params).map((r) => this.#rowToMemory(r));
196
+ }
197
+ stats() {
198
+ const byType = Object.fromEntries([
199
+ "correction",
200
+ "preference",
201
+ "decision",
202
+ "learning",
203
+ "fact"
204
+ ].map((t) => [t, 0]));
205
+ const typeRows = this.#db.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();
206
+ for (const row of typeRows) if (row.type in byType) byType[row.type] = row.count;
207
+ const totals = this.#db.db.prepare(`SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`).get() ?? {
208
+ total: 0,
209
+ needsReview: 0
210
+ };
211
+ return {
212
+ byType,
213
+ total: totals.total,
214
+ needsReview: totals.needsReview ?? 0
215
+ };
216
+ }
217
+ incrementAccessCount(id) {
218
+ this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);
219
+ }
220
+ #rowToMemory(row) {
221
+ return {
222
+ id: row.id,
223
+ content: row.content,
224
+ type: row.type,
225
+ tags: JSON.parse(row.tags),
226
+ scope: row.scope,
227
+ sourceHarness: row.source,
228
+ accessCount: row.access_count,
229
+ pinned: row.pinned !== 0,
230
+ needsReview: row.needs_review !== 0,
231
+ createdAt: row.created_at,
232
+ updatedAt: row.updated_at
233
+ };
234
+ }
235
+ };
236
+ //#endregion
237
+ //#region src/query/engine.ts
238
+ const TYPE_WEIGHTS = {
239
+ correction: 1,
240
+ preference: .8,
241
+ decision: .6,
242
+ learning: .4,
243
+ fact: .2
244
+ };
245
+ var QueryEngine = class {
246
+ #db;
247
+ #embedding;
248
+ #repo;
249
+ constructor(db, embeddingService, repo) {
250
+ this.#db = db;
251
+ this.#embedding = embeddingService;
252
+ this.#repo = repo;
253
+ }
254
+ async query(options) {
255
+ const { query, type, scope, limit = 10 } = options;
256
+ const queryEmbedding = await this.#embedding.embed(query);
257
+ const queryBlob = Buffer.from(queryEmbedding.buffer);
258
+ const whereClauses = [];
259
+ const params = [queryBlob];
260
+ if (type !== void 0) {
261
+ whereClauses.push("m.type = ?");
262
+ params.push(type);
263
+ }
264
+ if (scope !== void 0) {
265
+ whereClauses.push("m.scope = ?");
266
+ params.push(scope);
267
+ }
268
+ const sql = `
269
+ SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim
270
+ FROM memories m JOIN embeddings e ON e.rowid = m.rowid
271
+ ${whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : ""}
272
+ `;
273
+ const rows = this.#db.db.prepare(sql).all(...params);
274
+ const now = Date.now();
275
+ const scored = rows.filter((row) => row.cosine_sim > 0).map((row) => {
276
+ const memory = this.#rowToMemory(row);
277
+ const score = this.#computeScore(memory, now);
278
+ return {
279
+ ...memory,
280
+ score
281
+ };
282
+ });
283
+ scored.sort((a, b) => b.score - a.score);
284
+ const results = scored.slice(0, limit);
285
+ for (const result of results) this.#repo.incrementAccessCount(result.id);
286
+ return results;
287
+ }
288
+ #computeScore(memory, now) {
289
+ const typeWeight = TYPE_WEIGHTS[memory.type];
290
+ const accessCountNorm = memory.accessCount / (memory.accessCount + 10);
291
+ const recencyNorm = 1 / (1 + (now - new Date(memory.updatedAt).getTime()) / 864e5);
292
+ const pinned = memory.pinned ? 1 : 0;
293
+ return typeWeight * .4 + accessCountNorm * .3 + recencyNorm * .2 + pinned * .1;
294
+ }
295
+ #rowToMemory(row) {
296
+ return {
297
+ id: row.id,
298
+ content: row.content,
299
+ type: row.type,
300
+ tags: JSON.parse(row.tags),
301
+ scope: row.scope,
302
+ sourceHarness: row.source,
303
+ accessCount: row.access_count,
304
+ pinned: row.pinned !== 0,
305
+ needsReview: row.needs_review !== 0,
306
+ createdAt: row.created_at,
307
+ updatedAt: row.updated_at
308
+ };
309
+ }
310
+ };
311
+ //#endregion
312
+ //#region src/scope/resolver.ts
313
+ const execFileAsync = promisify(execFile);
314
+ function sha256Truncated(input) {
315
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
316
+ }
317
+ async function resolveScope() {
318
+ try {
319
+ const { stdout } = await execFileAsync("git", [
320
+ "remote",
321
+ "get-url",
322
+ "origin"
323
+ ]);
324
+ const url = stdout.trim();
325
+ if (url) return sha256Truncated(url);
326
+ } catch {}
327
+ return sha256Truncated(process.cwd());
328
+ }
329
+ //#endregion
330
+ //#region src/session/builder.ts
331
+ const MEMORY_TYPES = [
332
+ "correction",
333
+ "preference",
334
+ "decision",
335
+ "learning",
336
+ "fact"
337
+ ];
338
+ function rowToMemory(row) {
339
+ return {
340
+ id: row.id,
341
+ content: row.content,
342
+ type: row.type,
343
+ tags: JSON.parse(row.tags),
344
+ scope: row.scope,
345
+ sourceHarness: row.source,
346
+ accessCount: row.access_count,
347
+ pinned: row.pinned !== 0,
348
+ needsReview: row.needs_review !== 0,
349
+ createdAt: row.created_at,
350
+ updatedAt: row.updated_at
351
+ };
352
+ }
353
+ function listMemoryTypes() {
354
+ return [...MEMORY_TYPES];
355
+ }
356
+ var SessionContextBuilder = class {
357
+ #db;
358
+ constructor(db) {
359
+ this.#db = db;
360
+ }
361
+ getSessionContext(projectScope) {
362
+ const pinnedGlobal = this.#db.db.prepare("SELECT * FROM memories WHERE scope = 'global' AND pinned = 1").all().map(rowToMemory);
363
+ const pinnedProject = this.#db.db.prepare("SELECT * FROM memories WHERE scope = ? AND pinned = 1").all(projectScope).map(rowToMemory);
364
+ const typeCounts = this.#db.db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type").all();
365
+ const stats = Object.fromEntries(MEMORY_TYPES.map((t) => [t, 0]));
366
+ for (const row of typeCounts) if (stats[row.type] !== void 0) stats[row.type] = row.count;
367
+ return {
368
+ stats,
369
+ pinnedGlobal,
370
+ pinnedProject
371
+ };
372
+ }
373
+ };
374
+ //#endregion
375
+ export { DatabaseError, DatabaseManager, EmbeddingService, MembankError, MemoryRepository, QueryEngine, SessionContextBuilder, listMemoryTypes, resolveScope };
376
+
377
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["#db","#init","#initInMemory","#runMigrations","#db","#embedding","#rowToMemory","#db","#embedding","#repo","#rowToMemory","#computeScore","#db"],"sources":["../src/db/errors.ts","../src/db/manager.ts","../src/embedding/service.ts","../src/memory/repository.ts","../src/query/engine.ts","../src/scope/resolver.ts","../src/session/builder.ts"],"sourcesContent":["export class MembankError extends Error {\r\n constructor(message: string, options?: ErrorOptions) {\r\n super(message, options);\r\n this.name = \"MembankError\";\r\n }\r\n}\r\n\r\nexport class DatabaseError extends MembankError {\r\n constructor(message: string, options?: ErrorOptions) {\r\n super(message, options);\r\n this.name = \"DatabaseError\";\r\n }\r\n}\r\n","import { mkdirSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join } from \"node:path\";\r\nimport BetterSqlite3 from \"better-sqlite3\";\r\nimport * as sqliteVec from \"sqlite-vec\";\r\nimport { DatabaseError } from \"./errors.js\";\r\n\r\nconst DEFAULT_DB_PATH = join(homedir(), \".membank\", \"memory.db\");\r\n\r\ntype VecLoader = (db: BetterSqlite3.Database) => void;\r\n\r\n// Each entry is [targetVersion, sql]. Migrations are applied in ascending order.\r\nconst MIGRATIONS: [number, string][] = [\r\n [\r\n 1,\r\n `\r\nCREATE TABLE IF NOT EXISTS memories (\r\n id TEXT PRIMARY KEY,\r\n content TEXT NOT NULL,\r\n type TEXT NOT NULL,\r\n tags TEXT NOT NULL DEFAULT '[]',\r\n scope TEXT NOT NULL,\r\n source TEXT,\r\n access_count INTEGER NOT NULL DEFAULT 0,\r\n pinned INTEGER NOT NULL DEFAULT 0,\r\n needs_review INTEGER NOT NULL DEFAULT 0,\r\n created_at TEXT NOT NULL,\r\n updated_at TEXT NOT NULL\r\n);\r\n\r\nCREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(\r\n embedding FLOAT[384]\r\n);\r\n`,\r\n ],\r\n];\r\n\r\nexport class DatabaseManager {\r\n readonly #db: BetterSqlite3.Database;\r\n\r\n private constructor(db: BetterSqlite3.Database) {\r\n this.#db = db;\r\n }\r\n\r\n static open(dbPath?: string): DatabaseManager {\r\n const resolvedPath = dbPath ?? DEFAULT_DB_PATH;\r\n mkdirSync(dirname(resolvedPath), { recursive: true });\r\n const db = new BetterSqlite3(resolvedPath);\r\n return DatabaseManager.#init(db, sqliteVec.load);\r\n }\r\n\r\n static openInMemory(): DatabaseManager {\r\n return DatabaseManager.#initInMemory(sqliteVec.load);\r\n }\r\n\r\n /** For testing: inject a custom vec loader (e.g. a throwing stub). */\r\n static _openInMemoryWithLoader(loader: VecLoader): DatabaseManager {\r\n return DatabaseManager.#initInMemory(loader);\r\n }\r\n\r\n static #initInMemory(loader: VecLoader): DatabaseManager {\r\n const db = new BetterSqlite3(\":memory:\");\r\n return DatabaseManager.#init(db, loader);\r\n }\r\n\r\n static #init(db: BetterSqlite3.Database, loader: VecLoader): DatabaseManager {\r\n try {\r\n loader(db);\r\n } catch (err) {\r\n throw new DatabaseError(\"Failed to load sqlite-vec extension\", {\r\n cause: err,\r\n });\r\n }\r\n\r\n db.pragma(\"journal_mode = WAL\");\r\n\r\n const manager = new DatabaseManager(db);\r\n manager.#runMigrations();\r\n return manager;\r\n }\r\n\r\n #runMigrations(): void {\r\n // Bootstrap the meta table before reading schema_version from it\r\n this.#db.exec(`\r\n CREATE TABLE IF NOT EXISTS meta (\r\n key TEXT PRIMARY KEY,\r\n value TEXT NOT NULL\r\n );\r\n `);\r\n\r\n const row = this.#db\r\n .prepare<[], { value: string }>(\"SELECT value FROM meta WHERE key = 'schema_version'\")\r\n .get();\r\n\r\n const currentVersion = row ? Number.parseInt(row.value, 10) : 0;\r\n\r\n for (const [targetVersion, sql] of MIGRATIONS) {\r\n if (currentVersion < targetVersion) {\r\n this.#db.exec(sql);\r\n this.#db\r\n .prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\r\n .run(String(targetVersion));\r\n }\r\n }\r\n }\r\n\r\n get db(): BetterSqlite3.Database {\r\n return this.#db;\r\n }\r\n\r\n close(): void {\r\n this.#db.close();\r\n }\r\n}\r\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"@huggingface/transformers\";\n\nexport type ProgressCallback = (progress: { status: string; progress?: number }) => void;\n\nexport class EmbeddingService {\n private readonly modelCachePath: string;\n private readonly onProgress: ProgressCallback | undefined;\n private pipelineInstance: Awaited<ReturnType<typeof pipeline>> | null = null;\n\n constructor(modelCachePath?: string, onProgress?: ProgressCallback) {\n this.modelCachePath = modelCachePath ?? join(homedir(), \".membank\", \"models\");\n this.onProgress = onProgress;\n }\n\n private async getPipeline(): Promise<Awaited<ReturnType<typeof pipeline>>> {\n if (this.pipelineInstance === null) {\n this.pipelineInstance = await pipeline(\"feature-extraction\", \"Xenova/bge-small-en-v1.5\", {\n cache_dir: this.modelCachePath,\n progress_callback: this.onProgress,\n });\n }\n return this.pipelineInstance;\n }\n\n async embed(text: string): Promise<Float32Array> {\n const pipe = await this.getPipeline();\n // Shape: [1, seq_len, 384]. Cast to any to bypass the non-unified pipeline union signature.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const output = await (\n pipe as (input: string, opts: Record<string, unknown>) => Promise<unknown>\n )(text, { pooling: \"mean\", normalize: true });\n\n // @huggingface/transformers Tensor has a .data property with the flat array\n const tensor = output as { data: Float32Array | number[] };\n const flat = tensor.data;\n\n return flat instanceof Float32Array ? flat : new Float32Array(flat);\n }\n}\n","import { randomUUID } from \"node:crypto\";\r\nimport type { DatabaseManager } from \"../db/manager.js\";\r\nimport type { EmbeddingService } from \"../embedding/service.js\";\r\nimport type { Memory, MemoryType, SaveOptions } from \"../types.js\";\r\n\r\ninterface MemoryRow {\r\n id: string;\r\n content: string;\r\n type: string;\r\n tags: string;\r\n scope: string;\r\n source: string | null;\r\n access_count: number;\r\n pinned: number;\r\n needs_review: number;\r\n created_at: string;\r\n updated_at: string;\r\n}\r\n\r\ninterface SimilarityRow extends MemoryRow {\r\n rowid: number;\r\n similarity: number;\r\n}\r\n\r\nexport class MemoryRepository {\r\n readonly #db: DatabaseManager;\r\n readonly #embedding: EmbeddingService;\r\n\r\n constructor(db: DatabaseManager, embeddingService: EmbeddingService) {\r\n this.#db = db;\r\n this.#embedding = embeddingService;\r\n }\r\n\r\n async save(options: SaveOptions): Promise<Memory> {\r\n const { content, type, tags = [], scope = \"global\", sourceHarness } = options;\r\n\r\n const embedding = await this.#embedding.embed(content);\r\n const embeddingBlob = Buffer.from(embedding.buffer);\r\n\r\n const top = this.#db.db\r\n .prepare<[Buffer, string, string], SimilarityRow>(\r\n `SELECT m.rowid, m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS similarity\r\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\r\n WHERE m.type = ? AND m.scope = ?\r\n ORDER BY similarity DESC LIMIT 1`\r\n )\r\n .get(embeddingBlob, type, scope);\r\n\r\n const now = new Date().toISOString();\r\n\r\n if (top !== undefined && top.similarity > 0.92) {\r\n // Overwrite existing record\r\n this.#db.db\r\n .prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`)\r\n .run(content, now, top.id);\r\n\r\n this.#db.db\r\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\r\n .run(embeddingBlob, top.rowid);\r\n\r\n const updated = this.#db.db\r\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\r\n .get(top.id);\r\n\r\n // updated is guaranteed to exist since we just updated it\r\n return this.#rowToMemory(updated as MemoryRow);\r\n }\r\n\r\n if (top !== undefined && top.similarity >= 0.75) {\r\n // Flag existing as needs_review\r\n this.#db.db.prepare(`UPDATE memories SET needs_review = 1 WHERE id = ?`).run(top.id);\r\n }\r\n\r\n // Insert new record\r\n const id = randomUUID();\r\n this.#db.db\r\n .prepare(\r\n `INSERT INTO memories (id, content, type, tags, scope, source, access_count, pinned, needs_review, created_at, updated_at)\r\n VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?)`\r\n )\r\n .run(id, content, type, JSON.stringify(tags), scope, sourceHarness ?? null, now, now);\r\n\r\n // sqlite-vec v0.1.9 does not accept parameterized rowid on INSERT into vec0 tables.\r\n // Use a SELECT subquery to copy the rowid from the memories row we just inserted.\r\n this.#db.db\r\n .prepare(\r\n `INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`\r\n )\r\n .run(embeddingBlob, id);\r\n\r\n const row = this.#db.db\r\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\r\n .get(id) as MemoryRow;\r\n\r\n return this.#rowToMemory(row);\r\n }\r\n\r\n async update(id: string, patch: { content?: string; tags?: string[] }): Promise<Memory> {\r\n const existing = this.#db.db\r\n .prepare<[string], MemoryRow & { rowid: number }>(\r\n `SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`\r\n )\r\n .get(id);\r\n\r\n if (existing === undefined) {\r\n throw new Error(`Memory not found: ${id}`);\r\n }\r\n\r\n const now = new Date().toISOString();\r\n const sets: string[] = [\"updated_at = ?\"];\r\n const values: string[] = [now];\r\n\r\n if (patch.content !== undefined) {\r\n sets.push(\"content = ?\");\r\n values.push(patch.content);\r\n }\r\n\r\n if (patch.tags !== undefined) {\r\n sets.push(\"tags = ?\");\r\n values.push(JSON.stringify(patch.tags));\r\n }\r\n\r\n values.push(id);\r\n this.#db.db.prepare(`UPDATE memories SET ${sets.join(\", \")} WHERE id = ?`).run(...values);\r\n\r\n if (patch.content !== undefined) {\r\n const embedding = await this.#embedding.embed(patch.content);\r\n const embeddingBlob = Buffer.from(embedding.buffer);\r\n this.#db.db\r\n .prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`)\r\n .run(embeddingBlob, existing.rowid);\r\n }\r\n\r\n const updated = this.#db.db\r\n .prepare<[string], MemoryRow>(`SELECT * FROM memories WHERE id = ?`)\r\n .get(id) as MemoryRow;\r\n\r\n return this.#rowToMemory(updated);\r\n }\r\n\r\n delete(id: string): Promise<void> {\r\n const row = this.#db.db\r\n .prepare<[string], { rowid: number }>(`SELECT rowid FROM memories WHERE id = ?`)\r\n .get(id);\r\n\r\n if (row !== undefined) {\r\n this.#db.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(row.rowid);\r\n }\r\n\r\n this.#db.db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n list(opts?: { type?: MemoryType; pinned?: boolean }): Memory[] {\r\n const conditions: string[] = [];\r\n const params: (string | number)[] = [];\r\n\r\n if (opts?.type !== undefined) {\r\n conditions.push(\"type = ?\");\r\n params.push(opts.type);\r\n }\r\n\r\n if (opts?.pinned === true) {\r\n conditions.push(\"pinned = 1\");\r\n }\r\n\r\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\r\n const rows = this.#db.db\r\n .prepare<(string | number)[], MemoryRow>(`SELECT * FROM memories ${where} ORDER BY created_at DESC`)\r\n .all(...params);\r\n\r\n return rows.map((r) => this.#rowToMemory(r));\r\n }\r\n\r\n stats(): { byType: Record<MemoryType, number>; total: number; needsReview: number } {\r\n const allTypes: MemoryType[] = [\"correction\", \"preference\", \"decision\", \"learning\", \"fact\"];\r\n const byType = Object.fromEntries(allTypes.map((t) => [t, 0])) as Record<MemoryType, number>;\r\n\r\n const typeRows = this.#db.db\r\n .prepare<[], { type: string; count: number }>(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`)\r\n .all();\r\n\r\n for (const row of typeRows) {\r\n if (row.type in byType) {\r\n byType[row.type as MemoryType] = row.count;\r\n }\r\n }\r\n\r\n const totals = this.#db.db\r\n .prepare<[], { total: number; needsReview: number }>(\r\n `SELECT COUNT(*) as total, SUM(needs_review) as needsReview FROM memories`\r\n )\r\n .get() ?? { total: 0, needsReview: 0 };\r\n\r\n return {\r\n byType,\r\n total: totals.total,\r\n needsReview: totals.needsReview ?? 0,\r\n };\r\n }\r\n\r\n incrementAccessCount(id: string): void {\r\n this.#db.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(id);\r\n }\r\n\r\n #rowToMemory(row: MemoryRow): Memory {\r\n return {\r\n id: row.id,\r\n content: row.content,\r\n type: row.type as MemoryType,\r\n tags: JSON.parse(row.tags) as string[],\r\n scope: row.scope,\r\n sourceHarness: row.source,\r\n accessCount: row.access_count,\r\n pinned: row.pinned !== 0,\r\n needsReview: row.needs_review !== 0,\r\n createdAt: row.created_at,\r\n updatedAt: row.updated_at,\r\n };\r\n }\r\n}\r\n","import type { DatabaseManager } from \"../db/manager.js\";\nimport type { EmbeddingService } from \"../embedding/service.js\";\nimport type { MemoryRepository } from \"../memory/repository.js\";\nimport type { Memory, MemoryType, QueryOptions } from \"../types.js\";\n\ninterface MemoryRow {\n id: string;\n content: string;\n type: string;\n tags: string;\n scope: string;\n source: string | null;\n access_count: number;\n pinned: number;\n needs_review: number;\n created_at: string;\n updated_at: string;\n cosine_sim: number;\n}\n\nconst TYPE_WEIGHTS: Record<MemoryType, number> = {\n correction: 1.0,\n preference: 0.8,\n decision: 0.6,\n learning: 0.4,\n fact: 0.2,\n};\n\nexport class QueryEngine {\n readonly #db: DatabaseManager;\n readonly #embedding: EmbeddingService;\n readonly #repo: MemoryRepository;\n\n constructor(db: DatabaseManager, embeddingService: EmbeddingService, repo: MemoryRepository) {\n this.#db = db;\n this.#embedding = embeddingService;\n this.#repo = repo;\n }\n\n async query(options: QueryOptions): Promise<Array<Memory & { score: number }>> {\n const { query, type, scope, limit = 10 } = options;\n\n const queryEmbedding = await this.#embedding.embed(query);\n const queryBlob = Buffer.from(queryEmbedding.buffer);\n\n const whereClauses: string[] = [];\n const params: unknown[] = [queryBlob];\n\n if (type !== undefined) {\n whereClauses.push(\"m.type = ?\");\n params.push(type);\n }\n\n if (scope !== undefined) {\n whereClauses.push(\"m.scope = ?\");\n params.push(scope);\n }\n\n const whereSQL = whereClauses.length > 0 ? `WHERE ${whereClauses.join(\" AND \")}` : \"\";\n\n const sql = `\n SELECT m.*, (1 - vec_distance_cosine(e.embedding, ?)) AS cosine_sim\n FROM memories m JOIN embeddings e ON e.rowid = m.rowid\n ${whereSQL}\n `;\n\n const rows = this.#db.db.prepare<unknown[], MemoryRow>(sql).all(...params);\n\n const now = Date.now();\n\n const scored = rows\n .filter((row) => row.cosine_sim > 0)\n .map((row) => {\n const memory = this.#rowToMemory(row);\n const score = this.#computeScore(memory, now);\n return { ...memory, score };\n });\n\n scored.sort((a, b) => b.score - a.score);\n\n const results = scored.slice(0, limit);\n\n for (const result of results) {\n this.#repo.incrementAccessCount(result.id);\n }\n\n return results;\n }\n\n #computeScore(memory: Memory, now: number): number {\n const typeWeight = TYPE_WEIGHTS[memory.type];\n const accessCountNorm = memory.accessCount / (memory.accessCount + 10);\n const daysSinceUpdate = (now - new Date(memory.updatedAt).getTime()) / 86400000;\n const recencyNorm = 1 / (1 + daysSinceUpdate);\n const pinned = memory.pinned ? 1.0 : 0.0;\n\n return typeWeight * 0.4 + accessCountNorm * 0.3 + recencyNorm * 0.2 + pinned * 0.1;\n }\n\n #rowToMemory(row: MemoryRow): Memory {\n return {\n id: row.id,\n content: row.content,\n type: row.type as MemoryType,\n tags: JSON.parse(row.tags) as string[],\n scope: row.scope,\n sourceHarness: row.source,\n accessCount: row.access_count,\n pinned: row.pinned !== 0,\n needsReview: row.needs_review !== 0,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nfunction sha256Truncated(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\").slice(0, 16);\n}\n\nexport async function resolveScope(): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"remote\", \"get-url\", \"origin\"]);\n const url = stdout.trim();\n if (url) {\n return sha256Truncated(url);\n }\n } catch {\n // git not available, not a repo, or no remote — fall through to cwd fallback\n }\n\n return sha256Truncated(process.cwd());\n}\n","import type { DatabaseManager } from \"../db/manager.js\";\nimport type { Memory, MemoryType, SessionContext } from \"../types.js\";\n\ninterface MemoryRow {\n id: string;\n content: string;\n type: string;\n tags: string;\n scope: string;\n source: string | null;\n access_count: number;\n pinned: number;\n needs_review: number;\n created_at: string;\n updated_at: string;\n}\n\ninterface TypeCountRow {\n type: string;\n count: number;\n}\n\nconst MEMORY_TYPES: MemoryType[] = [\"correction\", \"preference\", \"decision\", \"learning\", \"fact\"];\n\nfunction rowToMemory(row: MemoryRow): Memory {\n return {\n id: row.id,\n content: row.content,\n type: row.type as MemoryType,\n tags: JSON.parse(row.tags) as string[],\n scope: row.scope,\n sourceHarness: row.source,\n accessCount: row.access_count,\n pinned: row.pinned !== 0,\n needsReview: row.needs_review !== 0,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport function listMemoryTypes(): MemoryType[] {\n return [...MEMORY_TYPES];\n}\n\nexport class SessionContextBuilder {\n readonly #db: DatabaseManager;\n\n constructor(db: DatabaseManager) {\n this.#db = db;\n }\n\n getSessionContext(projectScope: string): SessionContext {\n const pinnedGlobal = this.#db.db\n .prepare<[], MemoryRow>(\"SELECT * FROM memories WHERE scope = 'global' AND pinned = 1\")\n .all()\n .map(rowToMemory);\n\n const pinnedProject = this.#db.db\n .prepare<[string], MemoryRow>(\"SELECT * FROM memories WHERE scope = ? AND pinned = 1\")\n .all(projectScope)\n .map(rowToMemory);\n\n const typeCounts = this.#db.db\n .prepare<[], TypeCountRow>(\"SELECT type, COUNT(*) as count FROM memories GROUP BY type\")\n .all();\n\n const stats = Object.fromEntries(MEMORY_TYPES.map((t) => [t, 0])) as Record<MemoryType, number>;\n for (const row of typeCounts) {\n if (stats[row.type as MemoryType] !== undefined) {\n stats[row.type as MemoryType] = row.count;\n }\n }\n\n return { stats, pinnedGlobal, pinnedProject };\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@membank/core",
3
+ "version": "0.0.0-dev-20260427133418",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.mjs",
8
+ "require": "./dist/index.cjs",
9
+ "types": "./dist/index.d.mts"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "dependencies": {
16
+ "@huggingface/transformers": "^4.2.0",
17
+ "better-sqlite3": "^12.9.0",
18
+ "sqlite-vec": "^0.1.9"
19
+ },
20
+ "devDependencies": {
21
+ "@types/better-sqlite3": "^7.6.13",
22
+ "@types/node": "^25.6.0",
23
+ "typescript": "^6.0.3",
24
+ "vitest": "^3.2.4"
25
+ },
26
+ "scripts": {
27
+ "build": "tsdown",
28
+ "dev": "tsdown --watch",
29
+ "typecheck": "tsc --noEmit",
30
+ "lint": "biome check ./src",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "test:integration": "vitest run --reporter=verbose src/**/*.integration.test.ts",
34
+ "clean": "rm -rf dist *.tsbuildinfo"
35
+ }
36
+ }