@vheins/local-memory-mcp 0.7.4 → 0.8.1

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.
@@ -172,40 +172,28 @@ function addLogSink(sink) {
172
172
  var LOG_LEVEL_VALUES = Object.keys(LEVELS);
173
173
 
174
174
  // src/mcp/storage/sqlite.ts
175
- import initSqlJs from "sql.js";
175
+ import Database from "better-sqlite3";
176
176
  import path2 from "path";
177
177
  import fs2 from "fs";
178
178
  import os from "os";
179
179
 
180
180
  // src/mcp/storage/migrations.ts
181
181
  var MigrationManager = class {
182
- constructor(db, saveDb) {
182
+ constructor(db) {
183
183
  this.db = db;
184
- this.saveDb = saveDb;
185
184
  }
186
185
  db;
187
- saveDb;
188
186
  run(sql) {
189
- this.db.run(sql);
187
+ this.db.prepare(sql).run();
190
188
  }
191
189
  exec(sql) {
192
190
  this.db.exec(sql);
193
191
  }
194
192
  all(sql) {
195
- const result = this.db.exec(sql);
196
- if (result.length === 0) return [];
197
- const { columns, values } = result[0];
198
- return values.map((row) => {
199
- const obj = {};
200
- columns.forEach((col, i) => {
201
- obj[col] = row[i];
202
- });
203
- return obj;
204
- });
193
+ return this.db.prepare(sql).all();
205
194
  }
206
195
  get(sql) {
207
- const rows = this.all(sql);
208
- return rows[0];
196
+ return this.db.prepare(sql).get();
209
197
  }
210
198
  migrate() {
211
199
  this.exec(`
@@ -427,7 +415,6 @@ var MigrationManager = class {
427
415
  this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
428
416
  } catch {
429
417
  }
430
- if (this.saveDb) this.saveDb();
431
418
  }
432
419
  ensureMemoryTypeConstraint() {
433
420
  const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'");
@@ -531,7 +518,7 @@ var MigrationManager = class {
531
518
  }
532
519
  ensureMemoryStatusConstraintRemoved() {
533
520
  const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'");
534
- if (tableSql?.sql?.includes("status TEXT NOT NULL DEFAULT 'active' CHECK")) {
521
+ if (typeof tableSql?.sql === "string" && tableSql.sql.includes("status TEXT NOT NULL DEFAULT 'active' CHECK")) {
535
522
  this.ensureMemoryTypeConstraint();
536
523
  }
537
524
  }
@@ -792,36 +779,28 @@ function tokenize(text) {
792
779
 
793
780
  // src/mcp/storage/base.ts
794
781
  var BaseEntity = class {
795
- constructor(db, saveDb) {
782
+ constructor(db) {
796
783
  this.db = db;
797
- this.saveDb = saveDb;
798
784
  }
799
785
  db;
800
- saveDb;
786
+ transaction(fn) {
787
+ return this.db.transaction(fn)();
788
+ }
801
789
  run(sql, params = []) {
802
- this.db.run(sql, params);
803
- if (this.saveDb) this.saveDb();
804
- return { changes: this.db.getRowsModified() };
790
+ const stmt = this.db.prepare(sql);
791
+ const result = stmt.run(...params);
792
+ return { changes: result.changes };
805
793
  }
806
794
  exec(sql) {
807
795
  this.db.exec(sql);
808
- if (this.saveDb) this.saveDb();
809
796
  }
810
797
  all(sql, params = []) {
811
- const result = this.db.exec(sql, params);
812
- if (result.length === 0) return [];
813
- const { columns, values } = result[0];
814
- return values.map((row) => {
815
- const obj = {};
816
- columns.forEach((col, i) => {
817
- obj[col] = row[i];
818
- });
819
- return obj;
820
- });
798
+ const stmt = this.db.prepare(sql);
799
+ return stmt.all(...params);
821
800
  }
822
801
  get(sql, params = []) {
823
- const rows = this.all(sql, params);
824
- return rows[0];
802
+ const stmt = this.db.prepare(sql);
803
+ return stmt.get(...params);
825
804
  }
826
805
  safeJSONParse(json, defaultValue) {
827
806
  if (!json) return defaultValue;
@@ -1055,40 +1034,42 @@ var MemoryEntity = class extends BaseEntity {
1055
1034
  return rows.map((row) => this.rowToMemoryEntry(row));
1056
1035
  }
1057
1036
  bulkInsertMemories(entries) {
1058
- let count = 0;
1059
- for (const entry of entries) {
1060
- this.run(
1061
- `INSERT INTO memories (
1062
- id, repo, type, title, content, importance, folder, language,
1063
- created_at, updated_at, hit_count, recall_count, last_used_at, expires_at,
1064
- supersedes, status, is_global, tags, metadata, agent, role, model, completed_at
1065
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1066
- [
1067
- entry.id,
1068
- entry.scope.repo,
1069
- entry.type,
1070
- entry.title || null,
1071
- entry.content,
1072
- entry.importance,
1073
- entry.scope.folder || null,
1074
- entry.scope.language || null,
1075
- entry.created_at,
1076
- entry.updated_at,
1077
- entry.expires_at ?? null,
1078
- entry.supersedes ?? null,
1079
- entry.status || "active",
1080
- entry.is_global ? 1 : 0,
1081
- entry.tags ? JSON.stringify(entry.tags) : null,
1082
- entry.metadata ? JSON.stringify(entry.metadata) : null,
1083
- entry.agent || "unknown",
1084
- entry.role || "unknown",
1085
- entry.model || "unknown",
1086
- entry.completed_at || null
1087
- ]
1088
- );
1089
- count++;
1090
- }
1091
- return count;
1037
+ return this.transaction(() => {
1038
+ let count = 0;
1039
+ for (const entry of entries) {
1040
+ this.run(
1041
+ `INSERT INTO memories (
1042
+ id, repo, type, title, content, importance, folder, language,
1043
+ created_at, updated_at, hit_count, recall_count, last_used_at, expires_at,
1044
+ supersedes, status, is_global, tags, metadata, agent, role, model, completed_at
1045
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1046
+ [
1047
+ entry.id,
1048
+ entry.scope.repo,
1049
+ entry.type,
1050
+ entry.title || null,
1051
+ entry.content,
1052
+ entry.importance,
1053
+ entry.scope.folder || null,
1054
+ entry.scope.language || null,
1055
+ entry.created_at,
1056
+ entry.updated_at,
1057
+ entry.expires_at ?? null,
1058
+ entry.supersedes ?? null,
1059
+ entry.status || "active",
1060
+ entry.is_global ? 1 : 0,
1061
+ entry.tags ? JSON.stringify(entry.tags) : null,
1062
+ entry.metadata ? JSON.stringify(entry.metadata) : null,
1063
+ entry.agent || "unknown",
1064
+ entry.role || "unknown",
1065
+ entry.model || "unknown",
1066
+ entry.completed_at || null
1067
+ ]
1068
+ );
1069
+ count++;
1070
+ }
1071
+ return count;
1072
+ });
1092
1073
  }
1093
1074
  bulkUpdateMemories(ids, updates) {
1094
1075
  if (ids.length === 0) return 0;
@@ -1112,30 +1093,34 @@ var MemoryEntity = class extends BaseEntity {
1112
1093
  if (fields.length === 0) return 0;
1113
1094
  fields.push("updated_at = ?");
1114
1095
  values.push((/* @__PURE__ */ new Date()).toISOString());
1115
- let count = 0;
1116
- const chunkSize = 500;
1117
- for (let i = 0; i < ids.length; i += chunkSize) {
1118
- const chunk = ids.slice(i, i + chunkSize);
1119
- const result = this.run(
1120
- `UPDATE memories SET ${fields.join(", ")} WHERE id IN (${chunk.map(() => "?").join(",")})`,
1121
- [...values, ...chunk]
1122
- );
1123
- count += result.changes;
1124
- }
1125
- return count;
1096
+ return this.transaction(() => {
1097
+ let count = 0;
1098
+ const chunkSize = 500;
1099
+ for (let i = 0; i < ids.length; i += chunkSize) {
1100
+ const chunk = ids.slice(i, i + chunkSize);
1101
+ const result = this.run(
1102
+ `UPDATE memories SET ${fields.join(", ")} WHERE id IN (${chunk.map(() => "?").join(",")})`,
1103
+ [...values, ...chunk]
1104
+ );
1105
+ count += result.changes;
1106
+ }
1107
+ return count;
1108
+ });
1126
1109
  }
1127
1110
  bulkDeleteMemories(ids) {
1128
1111
  if (ids.length === 0) return 0;
1129
- let count = 0;
1130
- const chunkSize = 500;
1131
- for (let i = 0; i < ids.length; i += chunkSize) {
1132
- const chunk = ids.slice(i, i + chunkSize);
1133
- const result = this.run(`DELETE FROM memories WHERE id IN (${chunk.map(() => "?").join(",")})`, chunk);
1134
- count += result.changes;
1135
- }
1136
- return count;
1112
+ return this.transaction(() => {
1113
+ let count = 0;
1114
+ const chunkSize = 500;
1115
+ for (let i = 0; i < ids.length; i += chunkSize) {
1116
+ const chunk = ids.slice(i, i + chunkSize);
1117
+ const result = this.run(`DELETE FROM memories WHERE id IN (${chunk.map(() => "?").join(",")})`, chunk);
1118
+ count += result.changes;
1119
+ }
1120
+ return count;
1121
+ });
1137
1122
  }
1138
- getRecentMemories(repo, limit, offset = 0, includeArchived = false, excludeTypes = []) {
1123
+ getRecentMemories(repo, limit, offset = 0, includeArchived = false, excludeTypes = [], sortOrder = "DESC") {
1139
1124
  let query = "SELECT * FROM memories WHERE repo = ?";
1140
1125
  const params = [repo];
1141
1126
  if (!includeArchived) {
@@ -1145,7 +1130,7 @@ var MemoryEntity = class extends BaseEntity {
1145
1130
  query += ` AND type NOT IN (${excludeTypes.map(() => "?").join(",")})`;
1146
1131
  params.push(...excludeTypes);
1147
1132
  }
1148
- query += " ORDER BY created_at DESC LIMIT ? OFFSET ?";
1133
+ query += ` ORDER BY importance DESC, created_at ${sortOrder} LIMIT ? OFFSET ?`;
1149
1134
  params.push(limit, offset);
1150
1135
  const rows = this.all(query, params);
1151
1136
  return rows.map((row) => this.rowToMemoryEntry(row));
@@ -1380,8 +1365,28 @@ var TaskEntity = class extends BaseEntity {
1380
1365
  const fields = [];
1381
1366
  const values = [];
1382
1367
  const anyUpdates = updates;
1368
+ const VALID_COLUMNS = /* @__PURE__ */ new Set([
1369
+ "repo",
1370
+ "task_code",
1371
+ "phase",
1372
+ "title",
1373
+ "description",
1374
+ "status",
1375
+ "priority",
1376
+ "agent",
1377
+ "role",
1378
+ "doc_path",
1379
+ "finished_at",
1380
+ "canceled_at",
1381
+ "tags",
1382
+ "metadata",
1383
+ "parent_id",
1384
+ "depends_on",
1385
+ "est_tokens",
1386
+ "in_progress_at"
1387
+ ]);
1383
1388
  Object.keys(updates).forEach((key) => {
1384
- if (key !== "comment" && key !== "model" && anyUpdates[key] !== void 0) {
1389
+ if (VALID_COLUMNS.has(key) && anyUpdates[key] !== void 0) {
1385
1390
  if (key === "tags" || key === "metadata") {
1386
1391
  fields.push(`${key} = ?`);
1387
1392
  values.push(JSON.stringify(anyUpdates[key]));
@@ -1525,40 +1530,42 @@ var TaskEntity = class extends BaseEntity {
1525
1530
  return (row?.count ?? 0) > 0;
1526
1531
  }
1527
1532
  bulkInsertTasks(tasks) {
1528
- let count = 0;
1529
- for (const task of tasks) {
1530
- this.run(
1531
- `INSERT INTO tasks (
1532
- id, repo, task_code, phase, title, description, status, priority,
1533
- agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at
1534
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1535
- [
1536
- task.id,
1537
- task.repo,
1538
- task.task_code,
1539
- task.phase || null,
1540
- task.title,
1541
- task.description || null,
1542
- task.status || "backlog",
1543
- task.priority || 3,
1544
- task.agent || "unknown",
1545
- task.role || "unknown",
1546
- task.doc_path || null,
1547
- task.created_at,
1548
- task.updated_at,
1549
- task.finished_at || null,
1550
- task.canceled_at || null,
1551
- task.tags ? JSON.stringify(task.tags) : null,
1552
- task.metadata ? JSON.stringify(task.metadata) : null,
1553
- task.parent_id || null,
1554
- task.depends_on || null,
1555
- task.est_tokens || 0,
1556
- task.in_progress_at || null
1557
- ]
1558
- );
1559
- count++;
1560
- }
1561
- return count;
1533
+ return this.transaction(() => {
1534
+ let count = 0;
1535
+ for (const task of tasks) {
1536
+ this.run(
1537
+ `INSERT INTO tasks (
1538
+ id, repo, task_code, phase, title, description, status, priority,
1539
+ agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at
1540
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1541
+ [
1542
+ task.id,
1543
+ task.repo,
1544
+ task.task_code,
1545
+ task.phase || null,
1546
+ task.title,
1547
+ task.description || null,
1548
+ task.status || "backlog",
1549
+ task.priority || 3,
1550
+ task.agent || "unknown",
1551
+ task.role || "unknown",
1552
+ task.doc_path || null,
1553
+ task.created_at,
1554
+ task.updated_at,
1555
+ task.finished_at || null,
1556
+ task.canceled_at || null,
1557
+ task.tags ? JSON.stringify(task.tags) : null,
1558
+ task.metadata ? JSON.stringify(task.metadata) : null,
1559
+ task.parent_id || null,
1560
+ task.depends_on || null,
1561
+ task.est_tokens || 0,
1562
+ task.in_progress_at || null
1563
+ ]
1564
+ );
1565
+ count++;
1566
+ }
1567
+ return count;
1568
+ });
1562
1569
  }
1563
1570
  insertTaskComment(comment) {
1564
1571
  this.run(
@@ -1899,32 +1906,6 @@ function resolveDbPath() {
1899
1906
  return standardPath;
1900
1907
  }
1901
1908
  var DB_PATH = resolveDbPath();
1902
- var sqlJsReady = null;
1903
- var sqlJsModule = null;
1904
- async function getSqlJs() {
1905
- if (!sqlJsModule) {
1906
- sqlJsModule = await initSqlJs();
1907
- }
1908
- await sqlJsReady;
1909
- return sqlJsModule;
1910
- }
1911
- function createSaveFunction(db, dbPath) {
1912
- return () => {
1913
- if (dbPath && dbPath !== ":memory:") {
1914
- const data = db.export();
1915
- const buffer = Buffer.from(data);
1916
- fs2.writeFileSync(dbPath, buffer);
1917
- }
1918
- };
1919
- }
1920
- function warmUpSqlJs() {
1921
- if (!sqlJsReady) {
1922
- sqlJsReady = (async () => {
1923
- sqlJsModule = await initSqlJs();
1924
- })();
1925
- }
1926
- return sqlJsReady;
1927
- }
1928
1909
  var SQLiteStore = class _SQLiteStore {
1929
1910
  db;
1930
1911
  memories;
@@ -1932,106 +1913,43 @@ var SQLiteStore = class _SQLiteStore {
1932
1913
  actions;
1933
1914
  system;
1934
1915
  summaries;
1935
- _ready;
1936
- lastLoadedAt = 0;
1937
- saveDbPtr;
1938
1916
  dbPathInstance;
1939
1917
  constructor(dbPath) {
1940
1918
  const finalPath = dbPath ?? DB_PATH;
1941
1919
  this.dbPathInstance = finalPath;
1942
- warmUpSqlJs();
1943
- this.db = {};
1944
- this.memories = {};
1945
- this.tasks = {};
1946
- this.actions = {};
1947
- this.system = {};
1948
- this.summaries = {};
1949
- this._ready = this._init(finalPath);
1950
- }
1951
- async _init(finalPath) {
1952
- const SQL = await getSqlJs();
1953
- let db;
1954
- if (finalPath === ":memory:") {
1955
- db = new SQL.Database();
1956
- } else {
1920
+ if (finalPath !== ":memory:") {
1957
1921
  const dbDir = path2.dirname(finalPath);
1958
1922
  if (!fs2.existsSync(dbDir)) {
1959
1923
  fs2.mkdirSync(dbDir, { recursive: true });
1960
1924
  }
1961
- if (fs2.existsSync(finalPath)) {
1962
- const fileBuffer = fs2.readFileSync(finalPath);
1963
- db = new SQL.Database(fileBuffer);
1964
- } else {
1965
- db = new SQL.Database();
1966
- }
1967
1925
  }
1968
- this.saveDbPtr = createSaveFunction(db, finalPath);
1969
- const wrappedSaveDb = () => {
1970
- if (this.saveDbPtr) {
1971
- this.saveDbPtr();
1972
- this.lastLoadedAt = Date.now();
1973
- }
1974
- };
1975
- db.run("PRAGMA journal_mode = WAL");
1976
- db.run("PRAGMA synchronous = NORMAL");
1977
- db.run("PRAGMA busy_timeout = 5000");
1978
- const migrator = new MigrationManager(db, wrappedSaveDb);
1926
+ this.db = new Database(finalPath);
1927
+ this.db.pragma("journal_mode = WAL");
1928
+ this.db.pragma("synchronous = FULL");
1929
+ this.db.pragma("busy_timeout = 10000");
1930
+ this.db.pragma("foreign_keys = ON");
1931
+ this.db.pragma("wal_autocheckpoint = 1000");
1932
+ const migrator = new MigrationManager(this.db);
1979
1933
  migrator.migrate();
1980
1934
  migrator.addMemoryCodeColumn();
1981
- Object.assign(this, {
1982
- db,
1983
- memories: new MemoryEntity(db, wrappedSaveDb),
1984
- tasks: new TaskEntity(db, wrappedSaveDb),
1985
- actions: new ActionEntity(db, wrappedSaveDb),
1986
- system: new SystemEntity(db, wrappedSaveDb),
1987
- summaries: new SummaryEntity(db, wrappedSaveDb)
1988
- });
1989
- this.lastLoadedAt = Date.now();
1990
- if (finalPath !== ":memory:") {
1991
- wrappedSaveDb();
1992
- }
1993
- }
1994
- async refresh(force = false) {
1995
- const path5 = this.getDbPath();
1996
- if (path5 === ":memory:") return;
1997
- try {
1998
- const stats = fs2.statSync(path5);
1999
- const mtime = stats.mtimeMs;
2000
- if (force || mtime > this.lastLoadedAt) {
2001
- const SQL = await getSqlJs();
2002
- const fileBuffer = fs2.readFileSync(path5);
2003
- const newDb = new SQL.Database(fileBuffer);
2004
- const wrappedSaveDb = () => {
2005
- if (this.saveDbPtr) {
2006
- this.saveDbPtr();
2007
- this.lastLoadedAt = Date.now();
2008
- }
2009
- };
2010
- this.db = newDb;
2011
- this.memories = new MemoryEntity(newDb, wrappedSaveDb);
2012
- this.tasks = new TaskEntity(newDb, wrappedSaveDb);
2013
- this.actions = new ActionEntity(newDb, wrappedSaveDb);
2014
- this.system = new SystemEntity(newDb, wrappedSaveDb);
2015
- this.summaries = new SummaryEntity(newDb, wrappedSaveDb);
2016
- this.lastLoadedAt = Date.now();
2017
- }
2018
- } catch (e) {
2019
- console.error("Failed to refresh database from disk:", e);
2020
- }
1935
+ this.memories = new MemoryEntity(this.db);
1936
+ this.tasks = new TaskEntity(this.db);
1937
+ this.actions = new ActionEntity(this.db);
1938
+ this.system = new SystemEntity(this.db);
1939
+ this.summaries = new SummaryEntity(this.db);
2021
1940
  }
2022
1941
  async ready() {
2023
- await this._ready;
1942
+ }
1943
+ async refresh() {
2024
1944
  }
2025
1945
  static async create(dbPath) {
2026
- const store = new _SQLiteStore(dbPath);
2027
- await store.ready();
2028
- return store;
1946
+ return new _SQLiteStore(dbPath);
2029
1947
  }
2030
1948
  getDbPath() {
2031
1949
  return this.dbPathInstance;
2032
1950
  }
2033
1951
  close() {
2034
- if (this.db && this.db.close) {
1952
+ if (this.db && this.db.open) {
2035
1953
  this.db.close();
2036
1954
  }
2037
1955
  }
@@ -2057,7 +1975,8 @@ function updateSessionFromInitialize(session, params) {
2057
1975
  session.clientCapabilities = capabilities;
2058
1976
  session.supportsRoots = Boolean(capabilities.roots);
2059
1977
  session.supportsSampling = Boolean(capabilities.sampling);
2060
- session.supportsSamplingTools = Boolean(capabilities.sampling?.tools);
1978
+ const sampling = capabilities.sampling;
1979
+ session.supportsSamplingTools = Boolean(sampling?.tools);
2061
1980
  session.supportsElicitation = Boolean(capabilities.elicitation);
2062
1981
  session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
2063
1982
  session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");