@vheins/local-memory-mcp 0.6.1 → 0.7.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.
@@ -82,6 +82,9 @@ function deriveLoggerName(message) {
82
82
  return "app";
83
83
  }
84
84
  function emitToStderr(level, message, context) {
85
+ if (process.env.MCP_SERVER === "true") {
86
+ return;
87
+ }
85
88
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
86
89
  if (message.startsWith("[MCP]")) {
87
90
  const icon = getMcpIcon(message);
@@ -169,19 +172,43 @@ function addLogSink(sink) {
169
172
  var LOG_LEVEL_VALUES = Object.keys(LEVELS);
170
173
 
171
174
  // src/mcp/storage/sqlite.ts
172
- import Database from "better-sqlite3";
175
+ import initSqlJs from "sql.js";
173
176
  import path2 from "path";
174
177
  import fs2 from "fs";
175
178
  import os from "os";
176
179
 
177
180
  // src/mcp/storage/migrations.ts
178
181
  var MigrationManager = class {
179
- constructor(db) {
182
+ constructor(db, saveDb) {
180
183
  this.db = db;
184
+ this.saveDb = saveDb;
181
185
  }
182
186
  db;
187
+ saveDb;
188
+ run(sql) {
189
+ this.db.run(sql);
190
+ }
191
+ exec(sql) {
192
+ this.db.exec(sql);
193
+ }
194
+ 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
+ });
205
+ }
206
+ get(sql) {
207
+ const rows = this.all(sql);
208
+ return rows[0];
209
+ }
183
210
  migrate() {
184
- this.db.exec(`
211
+ this.exec(`
185
212
  CREATE TABLE IF NOT EXISTS memories (
186
213
  id TEXT PRIMARY KEY,
187
214
  repo TEXT NOT NULL,
@@ -311,35 +338,6 @@ var MigrationManager = class {
311
338
 
312
339
  CREATE INDEX IF NOT EXISTS idx_action_log_repo ON action_log(repo);
313
340
  CREATE INDEX IF NOT EXISTS idx_action_log_created_at ON action_log(created_at);
314
-
315
- -- FTS5 Virtual Table for Memories
316
- -- Note: Only using id, title, and content for search indexing
317
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
318
- id UNINDEXED,
319
- repo,
320
- type,
321
- title,
322
- content,
323
- metadata UNINDEXED,
324
- content='memories',
325
- content_rowid='id'
326
- );
327
-
328
- -- Triggers to keep FTS index in sync with base table
329
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
330
- INSERT INTO memories_fts(id, repo, type, title, content, metadata)
331
- VALUES (new.id, new.repo, new.type, new.title, new.content, new.metadata);
332
- END;
333
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
334
- INSERT INTO memories_fts(memories_fts, id, repo, type, title, content, metadata)
335
- VALUES ('delete', old.id, old.repo, old.type, old.title, old.content, old.metadata);
336
- END;
337
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
338
- INSERT INTO memories_fts(memories_fts, id, repo, type, title, content, metadata)
339
- VALUES ('delete', old.id, old.repo, old.type, old.title, old.content, old.metadata);
340
- INSERT INTO memories_fts(id, repo, type, title, content, metadata)
341
- VALUES (new.id, new.repo, new.type, new.title, new.content, new.metadata);
342
- END;
343
341
  `);
344
342
  const columnsToAdd = [
345
343
  { name: "title", table: "memories", definition: "ALTER TABLE memories ADD COLUMN title TEXT" },
@@ -409,37 +407,34 @@ var MigrationManager = class {
409
407
  ];
410
408
  for (const col of columnsToAdd) {
411
409
  try {
412
- const tableInfo = this.db.prepare(`PRAGMA table_info(${col.table})`).all();
410
+ const tableInfo = this.all(`PRAGMA table_info(${col.table})`);
413
411
  const existingTableColumns = tableInfo.map((c) => c.name);
414
412
  if (tableInfo.length > 0 && !existingTableColumns.includes(col.name)) {
415
- this.db.exec(col.definition);
413
+ this.exec(col.definition);
416
414
  }
417
- } catch (e) {
418
- logger.error(
419
- `Migration step failed for ${col.table}.${col.name}: ${e instanceof Error ? e.message : String(e)}`
420
- );
415
+ } catch {
421
416
  }
422
417
  }
423
418
  this.ensureMemoryTypeConstraint();
424
419
  this.ensureTaskStatusConstraintRemoved();
425
420
  this.ensureMemoryStatusConstraintRemoved();
426
- this.db.exec(`
421
+ this.exec(`
427
422
  CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status);
428
423
  CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes);
429
424
  CREATE INDEX IF NOT EXISTS idx_memories_is_global ON memories(is_global);
430
425
  `);
431
426
  try {
432
- this.db.exec("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
433
- } catch (e) {
434
- logger.error(`Legacy backfill task_code failed: ${e instanceof Error ? e.message : String(e)}`);
427
+ this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
428
+ } catch {
435
429
  }
430
+ if (this.saveDb) this.saveDb();
436
431
  }
437
432
  ensureMemoryTypeConstraint() {
438
- const tableSql = this.db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'").get();
439
- if (!tableSql?.sql || !tableSql.sql.includes("CHECK (type IN")) {
433
+ const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'");
434
+ if (!tableSql?.sql || !String(tableSql.sql).includes("CHECK (type IN")) {
440
435
  return;
441
436
  }
442
- this.db.exec(`
437
+ this.exec(`
443
438
  BEGIN TRANSACTION;
444
439
 
445
440
  CREATE TABLE memories__migrated (
@@ -486,11 +481,11 @@ var MigrationManager = class {
486
481
  `);
487
482
  }
488
483
  ensureTaskStatusConstraintRemoved() {
489
- const tableSql = this.db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'tasks'").get();
490
- if (!tableSql?.sql || !tableSql.sql.includes("CHECK (status IN") && !tableSql.sql.includes("DEFAULT 'pending'")) {
484
+ const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'tasks'");
485
+ if (!tableSql?.sql || !String(tableSql.sql).includes("CHECK (status IN") && !String(tableSql.sql).includes("DEFAULT 'pending'")) {
491
486
  return;
492
487
  }
493
- this.db.exec(`
488
+ this.exec(`
494
489
  BEGIN TRANSACTION;
495
490
 
496
491
  CREATE TABLE tasks__migrated (
@@ -535,11 +530,20 @@ var MigrationManager = class {
535
530
  `);
536
531
  }
537
532
  ensureMemoryStatusConstraintRemoved() {
538
- const tableSql = this.db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'").get();
533
+ const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'");
539
534
  if (tableSql?.sql?.includes("status TEXT NOT NULL DEFAULT 'active' CHECK")) {
540
535
  this.ensureMemoryTypeConstraint();
541
536
  }
542
537
  }
538
+ addMemoryCodeColumn() {
539
+ const tableInfo = this.all("PRAGMA table_info(memories)");
540
+ const hasCode = tableInfo.some((col) => col.name === "code");
541
+ if (!hasCode) {
542
+ this.run("ALTER TABLE memories ADD COLUMN code TEXT");
543
+ this.run("CREATE INDEX IF NOT EXISTS idx_memories_code ON memories(code)");
544
+ this.run("CREATE INDEX IF NOT EXISTS idx_memories_repo_code ON memories(repo, code)");
545
+ }
546
+ }
543
547
  };
544
548
 
545
549
  // src/mcp/utils/normalize.ts
@@ -788,13 +792,37 @@ function tokenize(text) {
788
792
 
789
793
  // src/mcp/storage/base.ts
790
794
  var BaseEntity = class {
791
- constructor(db) {
795
+ constructor(db, saveDb) {
792
796
  this.db = db;
797
+ this.saveDb = saveDb;
793
798
  }
794
799
  db;
795
- /**
796
- * Safe JSON parsing helper to avoid crashes on corrupt data
797
- */
800
+ saveDb;
801
+ run(sql, params = []) {
802
+ this.db.run(sql, params);
803
+ if (this.saveDb) this.saveDb();
804
+ return { changes: this.db.getRowsModified() };
805
+ }
806
+ exec(sql) {
807
+ this.db.exec(sql);
808
+ if (this.saveDb) this.saveDb();
809
+ }
810
+ 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
+ });
821
+ }
822
+ get(sql, params = []) {
823
+ const rows = this.all(sql, params);
824
+ return rows[0];
825
+ }
798
826
  safeJSONParse(json, defaultValue) {
799
827
  if (!json) return defaultValue;
800
828
  try {
@@ -803,13 +831,11 @@ var BaseEntity = class {
803
831
  return defaultValue;
804
832
  }
805
833
  }
806
- /**
807
- * Mapping helper for MemoryEntry
808
- */
809
834
  rowToMemoryEntry(row) {
810
835
  const r = row;
811
836
  return {
812
837
  id: r.id,
838
+ code: r.code || void 0,
813
839
  type: r.type,
814
840
  title: r.title || "Untitled",
815
841
  content: r.content,
@@ -836,9 +862,6 @@ var BaseEntity = class {
836
862
  metadata: this.safeJSONParse(r.metadata, {})
837
863
  };
838
864
  }
839
- /**
840
- * Mapping helper for Task
841
- */
842
865
  rowToTask(row) {
843
866
  const r = row;
844
867
  return {
@@ -866,9 +889,6 @@ var BaseEntity = class {
866
889
  comments_count: r.comments_count || 0
867
890
  };
868
891
  }
869
- /**
870
- * Vector math utilities (simple bag-of-words implementation)
871
- */
872
892
  computeVector(text) {
873
893
  const tokens = tokenize(text);
874
894
  const vector = {};
@@ -897,34 +917,35 @@ var BaseEntity = class {
897
917
  // src/mcp/entities/memory.ts
898
918
  var MemoryEntity = class extends BaseEntity {
899
919
  insert(entry) {
900
- const stmt = this.db.prepare(`
901
- INSERT INTO memories (
902
- id, repo, type, title, content, importance, folder, language,
903
- created_at, updated_at, hit_count, recall_count, last_used_at, expires_at,
904
- supersedes, status, is_global, tags, metadata, agent, role, model, completed_at
905
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
906
- `);
907
- stmt.run(
908
- entry.id,
909
- entry.scope.repo,
910
- entry.type,
911
- entry.title || null,
912
- entry.content,
913
- entry.importance,
914
- entry.scope.folder || null,
915
- entry.scope.language || null,
916
- entry.created_at,
917
- entry.updated_at,
918
- entry.expires_at ?? null,
919
- entry.supersedes ?? null,
920
- entry.status || "active",
921
- entry.is_global ? 1 : 0,
922
- entry.tags ? JSON.stringify(entry.tags) : null,
923
- entry.metadata ? JSON.stringify(entry.metadata) : null,
924
- entry.agent || "unknown",
925
- entry.role || "unknown",
926
- entry.model || "unknown",
927
- entry.completed_at || null
920
+ this.run(
921
+ `INSERT INTO memories (
922
+ id, code, repo, type, title, content, importance, folder, language,
923
+ created_at, updated_at, hit_count, recall_count, last_used_at, expires_at,
924
+ supersedes, status, is_global, tags, metadata, agent, role, model, completed_at
925
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
926
+ [
927
+ entry.id,
928
+ entry.code || null,
929
+ entry.scope.repo,
930
+ entry.type,
931
+ entry.title || null,
932
+ entry.content,
933
+ entry.importance,
934
+ entry.scope.folder || null,
935
+ entry.scope.language || null,
936
+ entry.created_at,
937
+ entry.updated_at,
938
+ entry.expires_at ?? null,
939
+ entry.supersedes ?? null,
940
+ entry.status || "active",
941
+ entry.is_global ? 1 : 0,
942
+ entry.tags ? JSON.stringify(entry.tags) : null,
943
+ entry.metadata ? JSON.stringify(entry.metadata) : null,
944
+ entry.agent || "unknown",
945
+ entry.role || "unknown",
946
+ entry.model || "unknown",
947
+ entry.completed_at || null
948
+ ]
928
949
  );
929
950
  }
930
951
  update(id, updates) {
@@ -964,23 +985,24 @@ var MemoryEntity = class extends BaseEntity {
964
985
  fields.push("updated_at = ?");
965
986
  values.push((/* @__PURE__ */ new Date()).toISOString());
966
987
  values.push(id);
967
- const stmt = this.db.prepare(`UPDATE memories SET ${fields.join(", ")} WHERE id = ?`);
968
- stmt.run(...values);
988
+ this.run(`UPDATE memories SET ${fields.join(", ")} WHERE id = ?`, values);
969
989
  }
970
990
  delete(id) {
971
- this.db.prepare("DELETE FROM memories WHERE id = ?").run(id);
991
+ this.run("DELETE FROM memories WHERE id = ?", [id]);
972
992
  }
973
993
  getById(id) {
974
- const row = this.db.prepare("SELECT * FROM memories WHERE id = ?").get(id);
994
+ const row = this.get("SELECT * FROM memories WHERE id = ?", [id]);
995
+ return row ? this.rowToMemoryEntry(row) : null;
996
+ }
997
+ getByCode(code) {
998
+ const row = this.get("SELECT * FROM memories WHERE code = ?", [code]);
975
999
  return row ? this.rowToMemoryEntry(row) : null;
976
1000
  }
977
1001
  getByIdWithStats(id) {
978
- const row = this.db.prepare(
979
- `
980
- SELECT *, CASE WHEN hit_count > 0 THEN CAST(recall_count AS REAL) / hit_count ELSE 0 END AS recall_rate
981
- FROM memories WHERE id = ?
982
- `
983
- ).get(id);
1002
+ const row = this.get(
1003
+ `SELECT *, CASE WHEN hit_count > 0 THEN CAST(recall_count AS REAL) / hit_count ELSE 0 END AS recall_rate FROM memories WHERE id = ?`,
1004
+ [id]
1005
+ );
984
1006
  if (!row) return null;
985
1007
  return {
986
1008
  ...this.rowToMemoryEntry(row),
@@ -999,7 +1021,7 @@ var MemoryEntity = class extends BaseEntity {
999
1021
  sql += " AND status = ?";
1000
1022
  params.push(options.status);
1001
1023
  }
1002
- const rows = this.db.prepare(sql).all(...params);
1024
+ const rows = this.all(sql, params);
1003
1025
  return rows.map((row) => this.rowToMemoryEntry(row));
1004
1026
  }
1005
1027
  getStats(repo) {
@@ -1010,7 +1032,7 @@ var MemoryEntity = class extends BaseEntity {
1010
1032
  params.push(repo);
1011
1033
  }
1012
1034
  sql += " GROUP BY type";
1013
- const rows = this.db.prepare(sql).all(...params);
1035
+ const rows = this.all(sql, params);
1014
1036
  const byType = {};
1015
1037
  let total = 0;
1016
1038
  rows.forEach((row) => {
@@ -1021,32 +1043,6 @@ var MemoryEntity = class extends BaseEntity {
1021
1043
  }
1022
1044
  searchByRepo(repo, query = "", type, limit = 5) {
1023
1045
  const now = (/* @__PURE__ */ new Date()).toISOString();
1024
- if (query && query.length >= 3) {
1025
- try {
1026
- let sql2 = `
1027
- SELECT m.*
1028
- FROM memories m
1029
- JOIN memories_fts f ON m.id = f.id
1030
- WHERE m.repo = ? AND memories_fts MATCH ? AND m.status = 'active'
1031
- AND (m.expires_at IS NULL OR m.expires_at > ?)
1032
- `;
1033
- const params2 = [repo, query, now];
1034
- if (type) {
1035
- sql2 += " AND m.type = ?";
1036
- params2.push(type);
1037
- }
1038
- sql2 += " ORDER BY rank, m.importance DESC, m.created_at DESC LIMIT ?";
1039
- params2.push(limit);
1040
- const rows2 = this.db.prepare(sql2).all(...params2);
1041
- return rows2.map((row) => this.rowToMemoryEntry(row));
1042
- } catch (e) {
1043
- logger.warn("FTS5 similarity search failed, falling back to LIKE", {
1044
- error: e instanceof Error ? e.message : String(e),
1045
- repo,
1046
- query
1047
- });
1048
- }
1049
- }
1050
1046
  let sql = "SELECT * FROM memories WHERE repo = ? AND (content LIKE ? OR title LIKE ? OR tags LIKE ?) AND status = 'active' AND (expires_at IS NULL OR expires_at > ?)";
1051
1047
  const params = [repo, `%${query}%`, `%${query}%`, `%${query}%`, now];
1052
1048
  if (type) {
@@ -1055,21 +1051,19 @@ var MemoryEntity = class extends BaseEntity {
1055
1051
  }
1056
1052
  sql += " ORDER BY importance DESC, created_at DESC LIMIT ?";
1057
1053
  params.push(limit);
1058
- const rows = this.db.prepare(sql).all(...params);
1054
+ const rows = this.all(sql, params);
1059
1055
  return rows.map((row) => this.rowToMemoryEntry(row));
1060
1056
  }
1061
1057
  bulkInsertMemories(entries) {
1062
- const insert = this.db.prepare(`
1063
- INSERT INTO memories (
1064
- id, repo, type, title, content, importance, folder, language,
1065
- created_at, updated_at, hit_count, recall_count, last_used_at, expires_at,
1066
- supersedes, status, is_global, tags, metadata, agent, role, model, completed_at
1067
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1068
- `);
1069
- const insertMany = this.db.transaction((entries2) => {
1070
- let count = 0;
1071
- for (const entry of entries2) {
1072
- insert.run(
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
+ [
1073
1067
  entry.id,
1074
1068
  entry.scope.repo,
1075
1069
  entry.type,
@@ -1090,12 +1084,11 @@ var MemoryEntity = class extends BaseEntity {
1090
1084
  entry.role || "unknown",
1091
1085
  entry.model || "unknown",
1092
1086
  entry.completed_at || null
1093
- );
1094
- count++;
1095
- }
1096
- return count;
1097
- });
1098
- return insertMany(entries);
1087
+ ]
1088
+ );
1089
+ count++;
1090
+ }
1091
+ return count;
1099
1092
  }
1100
1093
  bulkUpdateMemories(ids, updates) {
1101
1094
  if (ids.length === 0) return 0;
@@ -1119,35 +1112,28 @@ var MemoryEntity = class extends BaseEntity {
1119
1112
  if (fields.length === 0) return 0;
1120
1113
  fields.push("updated_at = ?");
1121
1114
  values.push((/* @__PURE__ */ new Date()).toISOString());
1122
- const updateMany = this.db.transaction((ids2, fields2, values2) => {
1123
- let count = 0;
1124
- const chunkSize = 500;
1125
- for (let i = 0; i < ids2.length; i += chunkSize) {
1126
- const chunk = ids2.slice(i, i + chunkSize);
1127
- const stmt = this.db.prepare(
1128
- `UPDATE memories SET ${fields2.join(", ")} WHERE id IN (${chunk.map(() => "?").join(",")})`
1129
- );
1130
- const result = stmt.run(...values2, ...chunk);
1131
- count += result.changes;
1132
- }
1133
- return count;
1134
- });
1135
- return updateMany(ids, fields, values);
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;
1136
1126
  }
1137
1127
  bulkDeleteMemories(ids) {
1138
1128
  if (ids.length === 0) return 0;
1139
- const deleteMany = this.db.transaction((ids2) => {
1140
- let count = 0;
1141
- const chunkSize = 500;
1142
- for (let i = 0; i < ids2.length; i += chunkSize) {
1143
- const chunk = ids2.slice(i, i + chunkSize);
1144
- const stmt = this.db.prepare(`DELETE FROM memories WHERE id IN (${chunk.map(() => "?").join(",")})`);
1145
- const result = stmt.run(...chunk);
1146
- count += result.changes;
1147
- }
1148
- return count;
1149
- });
1150
- return deleteMany(ids);
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;
1151
1137
  }
1152
1138
  getRecentMemories(repo, limit, offset = 0, includeArchived = false, excludeTypes = []) {
1153
1139
  let query = "SELECT * FROM memories WHERE repo = ?";
@@ -1161,7 +1147,7 @@ var MemoryEntity = class extends BaseEntity {
1161
1147
  }
1162
1148
  query += " ORDER BY created_at DESC LIMIT ? OFFSET ?";
1163
1149
  params.push(limit, offset);
1164
- const rows = this.db.prepare(query).all(...params);
1150
+ const rows = this.all(query, params);
1165
1151
  return rows.map((row) => this.rowToMemoryEntry(row));
1166
1152
  }
1167
1153
  getTotalCount(repo, includeArchived = false, excludeTypes = []) {
@@ -1172,25 +1158,27 @@ var MemoryEntity = class extends BaseEntity {
1172
1158
  sql += ` AND type NOT IN (${excludeTypes.map(() => "?").join(",")})`;
1173
1159
  params.push(...excludeTypes);
1174
1160
  }
1175
- const row = this.db.prepare(sql).get(...params);
1176
- return row.count;
1161
+ const row = this.get(sql, params);
1162
+ return row?.count ?? 0;
1177
1163
  }
1178
1164
  incrementHitCount(id) {
1179
- this.db.prepare("UPDATE memories SET hit_count = hit_count + 1, last_used_at = ? WHERE id = ?").run((/* @__PURE__ */ new Date()).toISOString(), id);
1165
+ this.run("UPDATE memories SET hit_count = hit_count + 1, last_used_at = ? WHERE id = ?", [
1166
+ (/* @__PURE__ */ new Date()).toISOString(),
1167
+ id
1168
+ ]);
1180
1169
  }
1181
1170
  incrementHitCounts(ids) {
1182
1171
  if (!ids || ids.length === 0) return;
1183
- const stmt = this.db.prepare("UPDATE memories SET hit_count = hit_count + 1, last_used_at = ? WHERE id = ?");
1184
1172
  const now = (/* @__PURE__ */ new Date()).toISOString();
1185
- const transaction = this.db.transaction((idsToUpdate) => {
1186
- for (const id of idsToUpdate) {
1187
- stmt.run(now, id);
1188
- }
1189
- });
1190
- transaction(ids);
1173
+ for (const id of ids) {
1174
+ this.run("UPDATE memories SET hit_count = hit_count + 1, last_used_at = ? WHERE id = ?", [now, id]);
1175
+ }
1191
1176
  }
1192
1177
  incrementRecallCount(id) {
1193
- this.db.prepare("UPDATE memories SET recall_count = recall_count + 1, last_used_at = ? WHERE id = ?").run((/* @__PURE__ */ new Date()).toISOString(), id);
1178
+ this.run("UPDATE memories SET recall_count = recall_count + 1, last_used_at = ? WHERE id = ?", [
1179
+ (/* @__PURE__ */ new Date()).toISOString(),
1180
+ id
1181
+ ]);
1194
1182
  }
1195
1183
  getVectorCandidates(repo, limit = 100) {
1196
1184
  let sql = `SELECT mv.memory_id, mv.vector FROM memory_vectors mv JOIN memories m ON mv.memory_id = m.id`;
@@ -1201,41 +1189,38 @@ var MemoryEntity = class extends BaseEntity {
1201
1189
  }
1202
1190
  sql += " LIMIT ?";
1203
1191
  params.push(limit);
1204
- return this.db.prepare(sql).all(...params);
1192
+ return this.all(sql, params);
1205
1193
  }
1206
1194
  upsertVectorEmbedding(memoryId, vector) {
1207
- this.db.prepare(
1208
- `
1209
- INSERT INTO memory_vectors (memory_id, vector, updated_at) VALUES (?, ?, ?)
1210
- ON CONFLICT(memory_id) DO UPDATE SET vector = excluded.vector, updated_at = excluded.updated_at
1211
- `
1212
- ).run(memoryId, JSON.stringify(vector), (/* @__PURE__ */ new Date()).toISOString());
1195
+ this.run(
1196
+ `INSERT INTO memory_vectors (memory_id, vector, updated_at) VALUES (?, ?, ?)
1197
+ ON CONFLICT(memory_id) DO UPDATE SET vector = excluded.vector, updated_at = excluded.updated_at`,
1198
+ [memoryId, JSON.stringify(vector), (/* @__PURE__ */ new Date()).toISOString()]
1199
+ );
1213
1200
  }
1214
1201
  getSummary(repo) {
1215
- const row = this.db.prepare("SELECT summary, updated_at FROM memory_summary WHERE repo = ?").get(repo);
1202
+ const row = this.get(
1203
+ "SELECT summary, updated_at FROM memory_summary WHERE repo = ?",
1204
+ [repo]
1205
+ );
1216
1206
  return row;
1217
1207
  }
1218
1208
  getAllMemoriesWithStats(repo) {
1219
- const rows = this.db.prepare(
1220
- `
1221
- SELECT *, CASE WHEN hit_count > 0 THEN CAST(recall_count AS REAL) / hit_count ELSE 0 END AS recall_rate
1222
- FROM memories
1223
- WHERE repo = ?
1224
- ORDER BY created_at DESC
1225
- `
1226
- ).all(repo);
1209
+ const rows = this.all(
1210
+ `SELECT *, CASE WHEN hit_count > 0 THEN CAST(recall_count AS REAL) / hit_count ELSE 0 END AS recall_rate FROM memories WHERE repo = ? ORDER BY created_at DESC`,
1211
+ [repo]
1212
+ );
1227
1213
  return rows.map((row) => ({
1228
1214
  ...this.rowToMemoryEntry(row),
1229
1215
  recall_rate: row.recall_rate || 0
1230
1216
  }));
1231
1217
  }
1232
1218
  upsertSummary(repo, summary) {
1233
- this.db.prepare(
1234
- `
1235
- INSERT INTO memory_summary (repo, summary, updated_at) VALUES (?, ?, ?)
1236
- ON CONFLICT(repo) DO UPDATE SET summary = excluded.summary, updated_at = excluded.updated_at
1237
- `
1238
- ).run(repo, summary, (/* @__PURE__ */ new Date()).toISOString());
1219
+ this.run(
1220
+ `INSERT INTO memory_summary (repo, summary, updated_at) VALUES (?, ?, ?)
1221
+ ON CONFLICT(repo) DO UPDATE SET summary = excluded.summary, updated_at = excluded.updated_at`,
1222
+ [repo, summary, (/* @__PURE__ */ new Date()).toISOString()]
1223
+ );
1239
1224
  }
1240
1225
  listMemoriesForDashboard(options) {
1241
1226
  const {
@@ -1282,9 +1267,10 @@ ORDER BY created_at DESC
1282
1267
  params.push(`%${search}%`, `%${search}%`);
1283
1268
  }
1284
1269
  const countSql = `SELECT COUNT(*) as count FROM memories WHERE ${where.join(" AND ")}`;
1285
- const total = this.db.prepare(countSql).get(...params).count;
1270
+ const totalRow = this.get(countSql, params);
1271
+ const total = totalRow?.count ?? 0;
1286
1272
  const dataSql = `SELECT *, CASE WHEN hit_count > 0 THEN CAST(recall_count AS REAL) / hit_count ELSE 0 END AS recall_rate FROM memories WHERE ${where.join(" AND ")} ORDER BY ${sortBy} ${sortOrder} LIMIT ? OFFSET ?`;
1287
- const rows = this.db.prepare(dataSql).all(...params, limit, offset);
1273
+ const rows = this.all(dataSql, [...params, limit, offset]);
1288
1274
  const items = rows.map((row) => ({
1289
1275
  ...this.rowToMemoryEntry(row),
1290
1276
  recall_rate: row.recall_rate || 0
@@ -1304,10 +1290,10 @@ ORDER BY created_at DESC
1304
1290
  let sql = `SELECT * FROM memories WHERE (${where.join(" AND ")}) AND (expires_at IS NULL OR expires_at > ?)`;
1305
1291
  if (!includeArchived) sql += " AND status = 'active'";
1306
1292
  sql += ` ORDER BY CASE WHEN repo = ? THEN 0 ELSE 1 END, importance DESC, created_at DESC LIMIT 100`;
1307
- const candidates = this.db.prepare(sql).all(...params, now.toISOString(), repo);
1293
+ const candidates = this.all(sql, [...params, now.toISOString(), repo]);
1308
1294
  if (candidates.length < 5) {
1309
1295
  const recentSql = `SELECT * FROM memories WHERE (${where.join(" OR ")}) AND status = 'active' AND (expires_at IS NULL OR expires_at > ?) ORDER BY created_at DESC LIMIT 10`;
1310
- const recent = this.db.prepare(recentSql).all(...params, now.toISOString());
1296
+ const recent = this.all(recentSql, [...params, now.toISOString()]);
1311
1297
  for (const r of recent) {
1312
1298
  if (!candidates.find((c) => c.id === r.id)) candidates.push(r);
1313
1299
  }
@@ -1338,25 +1324,21 @@ ORDER BY created_at DESC
1338
1324
  archiveExpiredMemories(force = false) {
1339
1325
  if (process.env.ENABLE_AUTO_ARCHIVE !== "true" && !force) return 0;
1340
1326
  const now = (/* @__PURE__ */ new Date()).toISOString();
1341
- const result = this.db.prepare(
1342
- `
1343
- UPDATE memories SET status = 'archived', updated_at = ?
1344
- WHERE expires_at IS NOT NULL AND expires_at <= ? AND status = 'active'
1345
- `
1346
- ).run(now, now);
1327
+ const result = this.run(
1328
+ `UPDATE memories SET status = 'archived', updated_at = ? WHERE expires_at IS NOT NULL AND expires_at <= ? AND status = 'active'`,
1329
+ [now, now]
1330
+ );
1347
1331
  return result.changes;
1348
1332
  }
1349
1333
  archiveLowScoreMemories(force = false) {
1350
1334
  if (process.env.ENABLE_AUTO_ARCHIVE !== "true" && !force) return 0;
1351
- const result = this.db.prepare(
1352
- `
1353
- UPDATE memories SET status = 'archived', updated_at = ?
1354
- WHERE status = 'active' AND (
1355
- (julianday('now') - julianday(COALESCE(last_used_at, created_at)) > 90 AND importance < 3)
1356
- OR (hit_count > 10 AND recall_count = 0)
1357
- )
1358
- `
1359
- ).run((/* @__PURE__ */ new Date()).toISOString());
1335
+ const result = this.run(
1336
+ `UPDATE memories SET status = 'archived', updated_at = ? WHERE status = 'active' AND (
1337
+ (julianday('now') - julianday(COALESCE(last_used_at, created_at)) > 90 AND importance < 3)
1338
+ OR (hit_count > 10 AND recall_count = 0)
1339
+ )`,
1340
+ [(/* @__PURE__ */ new Date()).toISOString()]
1341
+ );
1360
1342
  return result.changes;
1361
1343
  }
1362
1344
  };
@@ -1364,34 +1346,34 @@ ORDER BY created_at DESC
1364
1346
  // src/mcp/entities/task.ts
1365
1347
  var TaskEntity = class extends BaseEntity {
1366
1348
  insertTask(task) {
1367
- const stmt = this.db.prepare(`
1368
- INSERT INTO tasks (
1369
- id, repo, task_code, phase, title, description, status, priority,
1370
- agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at
1371
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1372
- `);
1373
- stmt.run(
1374
- task.id,
1375
- task.repo,
1376
- task.task_code,
1377
- task.phase || null,
1378
- task.title,
1379
- task.description || null,
1380
- task.status || "backlog",
1381
- task.priority || 3,
1382
- task.agent || "unknown",
1383
- task.role || "unknown",
1384
- task.doc_path || null,
1385
- task.created_at,
1386
- task.updated_at,
1387
- task.finished_at || null,
1388
- task.canceled_at || null,
1389
- task.tags ? JSON.stringify(task.tags) : null,
1390
- task.metadata ? JSON.stringify(task.metadata) : null,
1391
- task.parent_id || null,
1392
- task.depends_on || null,
1393
- task.est_tokens || 0,
1394
- task.in_progress_at || null
1349
+ this.run(
1350
+ `INSERT INTO tasks (
1351
+ id, repo, task_code, phase, title, description, status, priority,
1352
+ agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at
1353
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1354
+ [
1355
+ task.id,
1356
+ task.repo,
1357
+ task.task_code,
1358
+ task.phase || null,
1359
+ task.title,
1360
+ task.description || null,
1361
+ task.status || "backlog",
1362
+ task.priority || 3,
1363
+ task.agent || "unknown",
1364
+ task.role || "unknown",
1365
+ task.doc_path || null,
1366
+ task.created_at,
1367
+ task.updated_at,
1368
+ task.finished_at || null,
1369
+ task.canceled_at || null,
1370
+ task.tags ? JSON.stringify(task.tags) : null,
1371
+ task.metadata ? JSON.stringify(task.metadata) : null,
1372
+ task.parent_id || null,
1373
+ task.depends_on || null,
1374
+ task.est_tokens || 0,
1375
+ task.in_progress_at || null
1376
+ ]
1395
1377
  );
1396
1378
  }
1397
1379
  updateTask(id, updates) {
@@ -1413,43 +1395,34 @@ var TaskEntity = class extends BaseEntity {
1413
1395
  fields.push("updated_at = ?");
1414
1396
  values.push((/* @__PURE__ */ new Date()).toISOString());
1415
1397
  values.push(id);
1416
- const stmt = this.db.prepare(`UPDATE tasks SET ${fields.join(", ")} WHERE id = ?`);
1417
- stmt.run(...values);
1398
+ this.run(`UPDATE tasks SET ${fields.join(", ")} WHERE id = ?`, values);
1418
1399
  }
1419
1400
  deleteTask(id) {
1420
- this.db.prepare("DELETE FROM task_comments WHERE task_id = ?").run(id);
1421
- this.db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
1401
+ this.run("DELETE FROM task_comments WHERE task_id = ?", [id]);
1402
+ this.run("DELETE FROM tasks WHERE id = ?", [id]);
1422
1403
  }
1423
1404
  getTaskById(id) {
1424
- const row = this.db.prepare(
1425
- `
1426
- SELECT t.*, d.task_code as depends_on_code
1427
- FROM tasks t
1428
- LEFT JOIN tasks d ON t.depends_on = d.id
1429
- WHERE t.id = ?
1430
- `
1431
- ).get(id);
1405
+ const row = this.get(
1406
+ `SELECT t.*, d.task_code as depends_on_code FROM tasks t LEFT JOIN tasks d ON t.depends_on = d.id WHERE t.id = ?`,
1407
+ [id]
1408
+ );
1432
1409
  return row ? { ...this.rowToTask(row), comments: this.getTaskCommentsByTaskId(id) } : null;
1433
1410
  }
1434
1411
  getTaskByCode(repo, taskCode) {
1435
- const row = this.db.prepare(
1436
- `
1437
- SELECT t.*, d.task_code as depends_on_code
1438
- FROM tasks t
1439
- LEFT JOIN tasks d ON t.depends_on = d.id
1440
- WHERE t.repo = ? AND t.task_code = ?
1441
- `
1442
- ).get(repo, taskCode);
1412
+ const row = this.get(
1413
+ `SELECT t.*, d.task_code as depends_on_code FROM tasks t LEFT JOIN tasks d ON t.depends_on = d.id WHERE t.repo = ? AND t.task_code = ?`,
1414
+ [repo, taskCode]
1415
+ );
1443
1416
  return row ? { ...this.rowToTask(row), comments: this.getTaskCommentsByTaskId(row.id) } : null;
1444
1417
  }
1445
1418
  getTasksByRepo(repo, status, limit, offset, search) {
1446
1419
  let query = `
1447
- SELECT t.*, d.task_code as depends_on_code,
1448
- (SELECT COUNT(*) FROM task_comments WHERE task_id = t.id) as comments_count
1449
- FROM tasks t
1450
- LEFT JOIN tasks d ON t.depends_on = d.id
1451
- WHERE t.repo = ?
1452
- `;
1420
+ SELECT t.*, d.task_code as depends_on_code,
1421
+ (SELECT COUNT(*) FROM task_comments WHERE task_id = t.id) as comments_count
1422
+ FROM tasks t
1423
+ LEFT JOIN tasks d ON t.depends_on = d.id
1424
+ WHERE t.repo = ?
1425
+ `;
1453
1426
  const params = [repo];
1454
1427
  if (status) {
1455
1428
  query += " AND t.status = ?";
@@ -1461,16 +1434,16 @@ var TaskEntity = class extends BaseEntity {
1461
1434
  params.push(searchPattern, searchPattern, searchPattern);
1462
1435
  }
1463
1436
  query += ` ORDER BY
1464
- CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END ASC,
1465
- CASE WHEN t.status = 'completed' THEN t.updated_at ELSE NULL END DESC,
1466
- CASE WHEN t.status = 'in_progress' THEN 0
1467
- WHEN t.status = 'pending' THEN 1
1468
- WHEN t.status = 'backlog' THEN 2
1469
- WHEN t.status = 'blocked' THEN 3
1470
- WHEN t.status = 'canceled' THEN 4
1471
- ELSE 5 END ASC,
1472
- t.priority DESC,
1473
- t.created_at ASC`;
1437
+ CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END ASC,
1438
+ CASE WHEN t.status = 'completed' THEN t.updated_at ELSE NULL END DESC,
1439
+ CASE WHEN t.status = 'in_progress' THEN 0
1440
+ WHEN t.status = 'pending' THEN 1
1441
+ WHEN t.status = 'backlog' THEN 2
1442
+ WHEN t.status = 'blocked' THEN 3
1443
+ WHEN t.status = 'canceled' THEN 4
1444
+ ELSE 5 END ASC,
1445
+ t.priority DESC,
1446
+ t.created_at ASC`;
1474
1447
  if (limit !== void 0) {
1475
1448
  query += " LIMIT ?";
1476
1449
  params.push(limit);
@@ -1479,40 +1452,40 @@ var TaskEntity = class extends BaseEntity {
1479
1452
  params.push(offset);
1480
1453
  }
1481
1454
  }
1482
- const rows = this.db.prepare(query).all(...params);
1455
+ const rows = this.all(query, params);
1483
1456
  return rows.map((r) => this.rowToTask(r));
1484
1457
  }
1485
1458
  listRecentTasks(limit = 50, offset = 0) {
1486
1459
  const query = `
1487
- SELECT t.*, d.task_code as depends_on_code,
1488
- (SELECT COUNT(*) FROM task_comments WHERE task_id = t.id) as comments_count
1489
- FROM tasks t
1490
- LEFT JOIN tasks d ON t.depends_on = d.id
1491
- ORDER BY
1492
- CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END ASC,
1493
- CASE WHEN t.status = 'completed' THEN t.updated_at ELSE NULL END DESC,
1494
- CASE WHEN t.status = 'in_progress' THEN 0
1495
- WHEN t.status = 'pending' THEN 1
1496
- WHEN t.status = 'backlog' THEN 2
1497
- WHEN t.status = 'blocked' THEN 3
1498
- WHEN t.status = 'canceled' THEN 4
1499
- ELSE 5 END ASC,
1500
- t.priority DESC,
1501
- t.created_at ASC
1502
- LIMIT ? OFFSET ?
1503
- `;
1504
- const rows = this.db.prepare(query).all(limit, offset);
1460
+ SELECT t.*, d.task_code as depends_on_code,
1461
+ (SELECT COUNT(*) FROM task_comments WHERE task_id = t.id) as comments_count
1462
+ FROM tasks t
1463
+ LEFT JOIN tasks d ON t.depends_on = d.id
1464
+ ORDER BY
1465
+ CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END ASC,
1466
+ CASE WHEN t.status = 'completed' THEN t.updated_at ELSE NULL END DESC,
1467
+ CASE WHEN t.status = 'in_progress' THEN 0
1468
+ WHEN t.status = 'pending' THEN 1
1469
+ WHEN t.status = 'backlog' THEN 2
1470
+ WHEN t.status = 'blocked' THEN 3
1471
+ WHEN t.status = 'canceled' THEN 4
1472
+ ELSE 5 END ASC,
1473
+ t.priority DESC,
1474
+ t.created_at ASC
1475
+ LIMIT ? OFFSET ?
1476
+ `;
1477
+ const rows = this.all(query, [limit, offset]);
1505
1478
  return rows.map((r) => this.rowToTask(r));
1506
1479
  }
1507
1480
  getTasksByMultipleStatuses(repo, statuses, limit, offset, search) {
1508
1481
  if (!statuses.length) return this.getTasksByRepo(repo, void 0, limit, offset, search);
1509
1482
  let query = `
1510
- SELECT t.*, d.task_code as depends_on_code,
1511
- (SELECT COUNT(*) FROM task_comments WHERE task_id = t.id) as comments_count
1512
- FROM tasks t
1513
- LEFT JOIN tasks d ON t.depends_on = d.id
1514
- WHERE t.repo = ? AND t.status IN (${statuses.map(() => "?").join(",")})
1515
- `;
1483
+ SELECT t.*, d.task_code as depends_on_code,
1484
+ (SELECT COUNT(*) FROM task_comments WHERE task_id = t.id) as comments_count
1485
+ FROM tasks t
1486
+ LEFT JOIN tasks d ON t.depends_on = d.id
1487
+ WHERE t.repo = ? AND t.status IN (${statuses.map(() => "?").join(",")})
1488
+ `;
1516
1489
  const params = [repo, ...statuses];
1517
1490
  if (search) {
1518
1491
  query += " AND (t.title LIKE ? OR t.description LIKE ? OR t.task_code LIKE ?)";
@@ -1520,16 +1493,16 @@ var TaskEntity = class extends BaseEntity {
1520
1493
  params.push(searchPattern, searchPattern, searchPattern);
1521
1494
  }
1522
1495
  query += ` ORDER BY
1523
- CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END ASC,
1524
- CASE WHEN t.status = 'completed' THEN t.updated_at ELSE NULL END DESC,
1525
- CASE WHEN t.status = 'in_progress' THEN 0
1526
- WHEN t.status = 'pending' THEN 1
1527
- WHEN t.status = 'backlog' THEN 2
1528
- WHEN t.status = 'blocked' THEN 3
1529
- WHEN t.status = 'canceled' THEN 4
1530
- ELSE 5 END ASC,
1531
- t.priority DESC,
1532
- t.created_at ASC`;
1496
+ CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END ASC,
1497
+ CASE WHEN t.status = 'completed' THEN t.updated_at ELSE NULL END DESC,
1498
+ CASE WHEN t.status = 'in_progress' THEN 0
1499
+ WHEN t.status = 'pending' THEN 1
1500
+ WHEN t.status = 'backlog' THEN 2
1501
+ WHEN t.status = 'blocked' THEN 3
1502
+ WHEN t.status = 'canceled' THEN 4
1503
+ ELSE 5 END ASC,
1504
+ t.priority DESC,
1505
+ t.created_at ASC`;
1533
1506
  if (limit !== void 0) {
1534
1507
  query += " LIMIT ?";
1535
1508
  params.push(limit);
@@ -1538,7 +1511,7 @@ var TaskEntity = class extends BaseEntity {
1538
1511
  params.push(offset);
1539
1512
  }
1540
1513
  }
1541
- const rows = this.db.prepare(query).all(...params);
1514
+ const rows = this.all(query, params);
1542
1515
  return rows.map((r) => this.rowToTask(r));
1543
1516
  }
1544
1517
  isTaskCodeDuplicate(repo, task_code, excludeId) {
@@ -1548,20 +1521,18 @@ var TaskEntity = class extends BaseEntity {
1548
1521
  query += " AND id != ?";
1549
1522
  params.push(excludeId);
1550
1523
  }
1551
- const row = this.db.prepare(query).get(...params);
1552
- return row.count > 0;
1524
+ const row = this.get(query, params);
1525
+ return (row?.count ?? 0) > 0;
1553
1526
  }
1554
1527
  bulkInsertTasks(tasks) {
1555
- const insert = this.db.prepare(`
1556
- INSERT INTO tasks (
1557
- id, repo, task_code, phase, title, description, status, priority,
1558
- agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at
1559
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1560
- `);
1561
- const insertMany = this.db.transaction((tasks2) => {
1562
- let count = 0;
1563
- for (const task of tasks2) {
1564
- insert.run(
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
+ [
1565
1536
  task.id,
1566
1537
  task.repo,
1567
1538
  task.task_code,
@@ -1583,31 +1554,29 @@ var TaskEntity = class extends BaseEntity {
1583
1554
  task.depends_on || null,
1584
1555
  task.est_tokens || 0,
1585
1556
  task.in_progress_at || null
1586
- );
1587
- count++;
1588
- }
1589
- return count;
1590
- });
1591
- return insertMany(tasks);
1557
+ ]
1558
+ );
1559
+ count++;
1560
+ }
1561
+ return count;
1592
1562
  }
1593
1563
  insertTaskComment(comment) {
1594
- this.db.prepare(
1595
- `
1596
- INSERT INTO task_comments (
1597
- id, task_id, repo, comment, agent, role, model, previous_status, next_status, created_at
1598
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1599
- `
1600
- ).run(
1601
- comment.id,
1602
- comment.task_id,
1603
- comment.repo,
1604
- comment.comment,
1605
- comment.agent || "unknown",
1606
- comment.role || "unknown",
1607
- comment.model || "unknown",
1608
- comment.previous_status || null,
1609
- comment.next_status || null,
1610
- comment.created_at
1564
+ this.run(
1565
+ `INSERT INTO task_comments (
1566
+ id, task_id, repo, comment, agent, role, model, previous_status, next_status, created_at
1567
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1568
+ [
1569
+ comment.id,
1570
+ comment.task_id,
1571
+ comment.repo,
1572
+ comment.comment,
1573
+ comment.agent || "unknown",
1574
+ comment.role || "unknown",
1575
+ comment.model || "unknown",
1576
+ comment.previous_status || null,
1577
+ comment.next_status || null,
1578
+ comment.created_at
1579
+ ]
1611
1580
  );
1612
1581
  }
1613
1582
  updateTaskComment(id, updates) {
@@ -1622,35 +1591,29 @@ var TaskEntity = class extends BaseEntity {
1622
1591
  });
1623
1592
  if (fields.length === 0) return;
1624
1593
  values.push(id);
1625
- const stmt = this.db.prepare(`UPDATE task_comments SET ${fields.join(", ")} WHERE id = ?`);
1626
- stmt.run(...values);
1594
+ this.run(`UPDATE task_comments SET ${fields.join(", ")} WHERE id = ?`, values);
1627
1595
  }
1628
1596
  deleteTaskComment(id) {
1629
- this.db.prepare("DELETE FROM task_comments WHERE id = ?").run(id);
1597
+ this.run("DELETE FROM task_comments WHERE id = ?", [id]);
1630
1598
  }
1631
1599
  getTaskCommentById(id) {
1632
- return this.db.prepare("SELECT * FROM task_comments WHERE id = ?").get(id);
1600
+ return this.get("SELECT * FROM task_comments WHERE id = ?", [id]) ?? null;
1633
1601
  }
1634
1602
  getTaskCommentsByTaskId(taskId) {
1635
- return this.db.prepare(
1636
- `
1637
- SELECT * FROM task_comments
1638
- WHERE task_id = ?
1639
- ORDER BY created_at DESC, id DESC
1640
- `
1641
- ).all(taskId);
1603
+ return this.all(`SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC`, [
1604
+ taskId
1605
+ ]);
1642
1606
  }
1643
1607
  getAllTaskCommentsByRepo(repo) {
1644
- return this.db.prepare(
1645
- `
1646
- SELECT * FROM task_comments
1647
- WHERE repo = ?
1648
- ORDER BY created_at DESC, id DESC
1649
- `
1650
- ).all(repo);
1608
+ return this.all(`SELECT * FROM task_comments WHERE repo = ? ORDER BY created_at DESC, id DESC`, [
1609
+ repo
1610
+ ]);
1651
1611
  }
1652
1612
  getTaskStats(repo) {
1653
- const rows = this.db.prepare("SELECT status, COUNT(*) as count FROM tasks WHERE repo = ? GROUP BY status").all(repo);
1613
+ const rows = this.all(
1614
+ "SELECT status, COUNT(*) as count FROM tasks WHERE repo = ? GROUP BY status",
1615
+ [repo]
1616
+ );
1654
1617
  const stats = { total: 0, backlog: 0, todo: 0, inProgress: 0, completed: 0, blocked: 0, canceled: 0 };
1655
1618
  rows.forEach((r) => {
1656
1619
  const count = r.count;
@@ -1670,35 +1633,30 @@ var TaskEntity = class extends BaseEntity {
1670
1633
  else if (period === "weekly") dateFilter = "AND date(COALESCE(finished_at, updated_at)) >= date('now', '-7 days')";
1671
1634
  else if (period === "monthly")
1672
1635
  dateFilter = "AND date(COALESCE(finished_at, updated_at)) >= date('now', '-30 days')";
1673
- const stats = this.db.prepare(
1674
- `
1675
- SELECT
1676
- COUNT(*) as completed_count,
1677
- SUM(est_tokens) as total_tokens,
1678
- AVG(
1679
- CASE
1680
- WHEN in_progress_at IS NOT NULL AND finished_at IS NOT NULL
1681
- THEN (julianday(finished_at) - julianday(in_progress_at)) * 86400.0
1682
- ELSE NULL
1683
- END
1684
- ) as avg_duration_seconds
1685
- FROM tasks
1686
- WHERE repo = ?
1687
- AND status = 'completed'
1688
- ${dateFilter}
1689
- `
1690
- ).get(repo);
1636
+ const stats = this.get(
1637
+ `SELECT
1638
+ COUNT(*) as completed_count,
1639
+ SUM(est_tokens) as total_tokens,
1640
+ AVG(
1641
+ CASE
1642
+ WHEN in_progress_at IS NOT NULL AND finished_at IS NOT NULL
1643
+ THEN (julianday(finished_at) - julianday(in_progress_at)) * 86400.0
1644
+ ELSE NULL
1645
+ END
1646
+ ) as avg_duration_seconds
1647
+ FROM tasks
1648
+ WHERE repo = ?
1649
+ AND status = 'completed'
1650
+ ${dateFilter}`,
1651
+ [repo]
1652
+ );
1691
1653
  let addedDateFilter = "";
1692
1654
  if (period === "daily") addedDateFilter = "AND date(created_at) = date('now')";
1693
1655
  else if (period === "weekly") addedDateFilter = "AND date(created_at) >= date('now', '-7 days')";
1694
1656
  else if (period === "monthly") addedDateFilter = "AND date(created_at) >= date('now', '-30 days')";
1695
- const added = this.db.prepare(
1696
- `
1697
- SELECT COUNT(*) as count FROM tasks
1698
- WHERE repo = ?
1699
- ${addedDateFilter}
1700
- `
1701
- ).get(repo);
1657
+ const added = this.get(`SELECT COUNT(*) as count FROM tasks WHERE repo = ? ${addedDateFilter}`, [
1658
+ repo
1659
+ ]);
1702
1660
  return {
1703
1661
  completed: stats?.completed_count || 0,
1704
1662
  tokens: stats?.total_tokens || 0,
@@ -1723,33 +1681,31 @@ var TaskEntity = class extends BaseEntity {
1723
1681
  dateFilter = "1=1";
1724
1682
  }
1725
1683
  const query = `
1726
- SELECT label, SUM(created) as created, SUM(completed) as completed
1727
- FROM (
1728
- SELECT strftime(?, created_at) as label, 1 as created, 0 as completed
1729
- FROM tasks
1730
- WHERE repo = ? AND ${dateFilter.replace("COALESCE(finished_at, created_at)", "created_at")}
1731
- UNION ALL
1732
- SELECT strftime(?, COALESCE(finished_at, updated_at)) as label, 0 as created, 1 as completed
1733
- FROM tasks
1734
- WHERE repo = ? AND status = 'completed' AND ${dateFilter.replace("COALESCE(finished_at, created_at)", "COALESCE(finished_at, updated_at)")}
1735
- )
1736
- GROUP BY label
1737
- ORDER BY label ASC
1738
- LIMIT 100
1739
- `;
1740
- return this.db.prepare(query).all(labelFormat, repo, labelFormat, repo);
1684
+ SELECT label, SUM(created) as created, SUM(completed) as completed
1685
+ FROM (
1686
+ SELECT strftime(?, created_at) as label, 1 as created, 0 as completed
1687
+ FROM tasks
1688
+ WHERE repo = ? AND ${dateFilter.replace("COALESCE(finished_at, created_at)", "created_at")}
1689
+ UNION ALL
1690
+ SELECT strftime(?, COALESCE(finished_at, updated_at)) as label, 0 as created, 1 as completed
1691
+ FROM tasks
1692
+ WHERE repo = ? AND status = 'completed' AND ${dateFilter.replace("COALESCE(finished_at, created_at)", "COALESCE(finished_at, updated_at)")}
1693
+ )
1694
+ GROUP BY label
1695
+ ORDER BY label ASC
1696
+ LIMIT 100
1697
+ `;
1698
+ return this.all(query, [
1699
+ labelFormat,
1700
+ repo,
1701
+ labelFormat,
1702
+ repo
1703
+ ]);
1741
1704
  }
1742
1705
  };
1743
1706
 
1744
1707
  // src/mcp/entities/action.ts
1745
1708
  var ActionEntity = class extends BaseEntity {
1746
- constructor(db) {
1747
- super(db);
1748
- }
1749
- /**
1750
- * Log an action to the audit trail.
1751
- * Supports both object-based options (modern) and positional arguments (legacy/internal).
1752
- */
1753
1709
  logAction(action, repo, optionsOrQuery, response, memoryId, taskId, resultCount = 0) {
1754
1710
  let query = typeof optionsOrQuery === "string" ? optionsOrQuery : void 0;
1755
1711
  let finalResponse = response;
@@ -1764,39 +1720,38 @@ var ActionEntity = class extends BaseEntity {
1764
1720
  finalTaskId = optionsOrQuery.taskId || finalTaskId;
1765
1721
  finalResultCount = optionsOrQuery.resultCount !== void 0 ? optionsOrQuery.resultCount : finalResultCount;
1766
1722
  }
1767
- const stmt = this.db.prepare(`
1768
- INSERT INTO action_log (repo, action, query, response, memory_id, task_id, result_count, created_at)
1769
- VALUES (:repo, :action, :query, :response, :memory_id, :task_id, :result_count, :created_at)
1770
- `);
1771
- stmt.run({
1772
- repo: repo || "",
1773
- action: action || "unknown",
1774
- query: query || null,
1775
- response: finalResponse ? typeof finalResponse === "string" ? finalResponse : JSON.stringify(finalResponse) : null,
1776
- memory_id: finalMemoryId || null,
1777
- task_id: finalTaskId || null,
1778
- result_count: finalResultCount ?? 0,
1779
- created_at: (/* @__PURE__ */ new Date()).toISOString()
1780
- });
1723
+ this.run(
1724
+ `INSERT INTO action_log (repo, action, query, response, memory_id, task_id, result_count, created_at)
1725
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
1726
+ [
1727
+ repo || "",
1728
+ action || "unknown",
1729
+ query || null,
1730
+ finalResponse ? typeof finalResponse === "string" ? finalResponse : JSON.stringify(finalResponse) : null,
1731
+ finalMemoryId || null,
1732
+ finalTaskId || null,
1733
+ finalResultCount ?? 0,
1734
+ (/* @__PURE__ */ new Date()).toISOString()
1735
+ ]
1736
+ );
1781
1737
  }
1782
1738
  getLastActionId() {
1783
- const row = this.db.prepare("SELECT MAX(id) as id FROM action_log").get();
1739
+ const row = this.get("SELECT MAX(id) as id FROM action_log");
1784
1740
  return row?.id || 0;
1785
1741
  }
1786
1742
  getActionsAfter(id) {
1787
- return this.db.prepare(
1788
- `
1789
- SELECT a.*, m.title as memory_title, m.type as memory_type
1790
- FROM action_log a LEFT JOIN memories m ON a.memory_id = m.id
1791
- WHERE a.id > ? ORDER BY a.created_at ASC
1792
- `
1793
- ).all(id);
1743
+ return this.all(
1744
+ `SELECT a.*, m.title as memory_title, m.type as memory_type
1745
+ FROM action_log a LEFT JOIN memories m ON a.memory_id = m.id
1746
+ WHERE a.id > ? ORDER BY a.created_at ASC`,
1747
+ [id]
1748
+ );
1794
1749
  }
1795
1750
  getRecentActions(repo, limit = 10, offset = 0) {
1796
1751
  let query = `
1797
- SELECT a.*, m.title as memory_title, m.type as memory_type
1798
- FROM action_log a LEFT JOIN memories m ON a.memory_id = m.id
1799
- `;
1752
+ SELECT a.*, m.title as memory_title, m.type as memory_type
1753
+ FROM action_log a LEFT JOIN memories m ON a.memory_id = m.id
1754
+ `;
1800
1755
  const params = [];
1801
1756
  if (repo) {
1802
1757
  query += " WHERE a.repo = ?";
@@ -1804,69 +1759,81 @@ var ActionEntity = class extends BaseEntity {
1804
1759
  }
1805
1760
  query += " ORDER BY a.created_at DESC, a.id DESC LIMIT ? OFFSET ?";
1806
1761
  params.push(limit, offset);
1807
- return this.db.prepare(query).all(...params);
1762
+ return this.all(query, params);
1808
1763
  }
1809
1764
  getActionStatsByDate(repo) {
1810
- return this.db.prepare(
1811
- `
1812
- SELECT date(created_at) as date, count(*) as count
1813
- FROM action_log
1814
- WHERE repo = ? AND created_at > date('now', '-30 days')
1815
- GROUP BY date(created_at)
1816
- ORDER BY date ASC
1817
- `
1818
- ).all(repo);
1765
+ return this.all(
1766
+ `SELECT date(created_at) as date, count(*) as count
1767
+ FROM action_log
1768
+ WHERE repo = ? AND created_at > date('now', '-30 days')
1769
+ GROUP BY date(created_at)
1770
+ ORDER BY date ASC`,
1771
+ [repo]
1772
+ );
1819
1773
  }
1820
1774
  getActionDistribution(repo) {
1821
- return this.db.prepare(
1822
- `
1823
- SELECT action, count(*) as count
1824
- FROM action_log
1825
- WHERE repo = ?
1826
- GROUP BY action
1827
- `
1828
- ).all(repo);
1775
+ return this.all(
1776
+ `SELECT action, count(*) as count
1777
+ FROM action_log
1778
+ WHERE repo = ?
1779
+ GROUP BY action`,
1780
+ [repo]
1781
+ );
1829
1782
  }
1830
1783
  getActionById(id) {
1831
- return this.db.prepare(
1832
- `
1833
- SELECT a.*, m.title as memory_title, m.type as memory_type
1834
- FROM action_log a LEFT JOIN memories m ON a.memory_id = m.id
1835
- WHERE a.id = ?
1836
- `
1837
- ).get(id);
1784
+ return this.get(
1785
+ `SELECT a.*, m.title as memory_title, m.type as memory_type
1786
+ FROM action_log a LEFT JOIN memories m ON a.memory_id = m.id
1787
+ WHERE a.id = ?`,
1788
+ [id]
1789
+ );
1838
1790
  }
1839
1791
  };
1840
1792
 
1841
1793
  // src/mcp/entities/system.ts
1842
1794
  var SystemEntity = class extends BaseEntity {
1843
1795
  listRepos() {
1844
- const rows = this.db.prepare("SELECT DISTINCT repo FROM memories UNION SELECT DISTINCT repo FROM tasks").all();
1796
+ const rows = this.all("SELECT DISTINCT repo FROM memories UNION SELECT DISTINCT repo FROM tasks");
1845
1797
  return rows.map((r) => r.repo);
1846
1798
  }
1847
1799
  listRepoNavigation() {
1848
1800
  const repos = this.listRepos();
1849
1801
  return repos.map((repo) => {
1850
- const memoryCount = this.db.prepare("SELECT COUNT(*) as count FROM memories WHERE repo = ?").get(repo).count;
1851
- const taskCount = this.db.prepare("SELECT COUNT(*) as count FROM tasks WHERE repo = ?").get(repo).count;
1852
- const lastActivity = this.db.prepare(
1853
- `SELECT MAX(created_at) as last FROM (SELECT created_at FROM memories WHERE repo = ? UNION ALL SELECT created_at FROM tasks WHERE repo = ? UNION ALL SELECT created_at FROM action_log WHERE repo = ?)`
1854
- ).get(repo, repo, repo).last;
1802
+ const memoryCountRow = this.get("SELECT COUNT(*) as count FROM memories WHERE repo = ?", [
1803
+ repo
1804
+ ]);
1805
+ const taskCountRow = this.get("SELECT COUNT(*) as count FROM tasks WHERE repo = ?", [repo]);
1806
+ const lastActivityRow = this.get(
1807
+ `SELECT MAX(created_at) as last FROM (SELECT created_at FROM memories WHERE repo = ? UNION ALL SELECT created_at FROM tasks WHERE repo = ? UNION ALL SELECT created_at FROM action_log WHERE repo = ?)`,
1808
+ [repo, repo, repo]
1809
+ );
1855
1810
  return {
1856
1811
  repo,
1857
- memoryCount,
1858
- taskCount,
1859
- lastActivity
1812
+ memoryCount: memoryCountRow?.count ?? 0,
1813
+ taskCount: taskCountRow?.count ?? 0,
1814
+ lastActivity: lastActivityRow?.last ?? null
1860
1815
  };
1861
1816
  });
1862
1817
  }
1863
1818
  getDashboardStats(repo) {
1864
- const memoryStats = this.db.prepare("SELECT type, COUNT(*) as count FROM memories WHERE repo = ? GROUP BY type").all(repo);
1865
- const taskStats = this.db.prepare("SELECT status, COUNT(*) as count FROM tasks WHERE repo = ? GROUP BY status").all(repo);
1866
- const recentMemories = this.db.prepare("SELECT * FROM memories WHERE repo = ? ORDER BY created_at DESC LIMIT 5").all(repo).map((r) => this.rowToMemoryEntry(r));
1867
- const activeTasks = this.db.prepare(
1868
- "SELECT * FROM tasks WHERE repo = ? AND status IN ('in_progress', 'pending', 'backlog') ORDER BY priority DESC, created_at ASC LIMIT 5"
1869
- ).all(repo).map((r) => this.rowToTask(r));
1819
+ const memoryStats = this.all(
1820
+ "SELECT type, COUNT(*) as count FROM memories WHERE repo = ? GROUP BY type",
1821
+ [repo]
1822
+ );
1823
+ const taskStats = this.all(
1824
+ "SELECT status, COUNT(*) as count FROM tasks WHERE repo = ? GROUP BY status",
1825
+ [repo]
1826
+ );
1827
+ const recentMemoriesRows = this.all(
1828
+ "SELECT * FROM memories WHERE repo = ? ORDER BY created_at DESC LIMIT 5",
1829
+ [repo]
1830
+ );
1831
+ const recentMemories = recentMemoriesRows.map((r) => this.rowToMemoryEntry(r));
1832
+ const activeTasksRows = this.all(
1833
+ "SELECT * FROM tasks WHERE repo = ? AND status IN ('in_progress', 'pending', 'backlog') ORDER BY priority DESC, created_at ASC LIMIT 5",
1834
+ [repo]
1835
+ );
1836
+ const activeTasks = activeTasksRows.map((r) => this.rowToTask(r));
1870
1837
  return {
1871
1838
  memoryStats,
1872
1839
  taskStats,
@@ -1875,23 +1842,27 @@ var SystemEntity = class extends BaseEntity {
1875
1842
  };
1876
1843
  }
1877
1844
  getGlobalStats() {
1878
- const totalMemories = this.db.prepare("SELECT COUNT(*) as count FROM memories").get().count;
1879
- const totalTasks = this.db.prepare("SELECT COUNT(*) as count FROM tasks").get().count;
1845
+ const totalMemoriesRow = this.get("SELECT COUNT(*) as count FROM memories");
1846
+ const totalTasksRow = this.get("SELECT COUNT(*) as count FROM tasks");
1880
1847
  const totalRepos = this.listRepos().length;
1881
1848
  return {
1882
- totalMemories,
1883
- totalTasks,
1849
+ totalMemories: totalMemoriesRow?.count ?? 0,
1850
+ totalTasks: totalTasksRow?.count ?? 0,
1884
1851
  totalRepos
1885
1852
  };
1886
1853
  }
1887
1854
  getRepoDetails(repo) {
1888
- const memoryCount = this.db.prepare("SELECT COUNT(*) as count FROM memories WHERE repo = ?").get(repo).count;
1889
- const taskCount = this.db.prepare("SELECT COUNT(*) as count FROM tasks WHERE repo = ?").get(repo).count;
1890
- const languages = this.db.prepare("SELECT DISTINCT language FROM memories WHERE repo = ? AND language IS NOT NULL").all(repo).map((r) => r.language);
1855
+ const memoryCountRow = this.get("SELECT COUNT(*) as count FROM memories WHERE repo = ?", [repo]);
1856
+ const taskCountRow = this.get("SELECT COUNT(*) as count FROM tasks WHERE repo = ?", [repo]);
1857
+ const languagesRows = this.all(
1858
+ "SELECT DISTINCT language FROM memories WHERE repo = ? AND language IS NOT NULL",
1859
+ [repo]
1860
+ );
1861
+ const languages = languagesRows.map((r) => r.language);
1891
1862
  return {
1892
1863
  repo,
1893
- memoryCount,
1894
- taskCount,
1864
+ memoryCount: memoryCountRow?.count ?? 0,
1865
+ taskCount: taskCountRow?.count ?? 0,
1895
1866
  languages
1896
1867
  };
1897
1868
  }
@@ -1900,16 +1871,18 @@ var SystemEntity = class extends BaseEntity {
1900
1871
  // src/mcp/entities/summary.ts
1901
1872
  var SummaryEntity = class extends BaseEntity {
1902
1873
  getSummary(repo) {
1903
- const row = this.db.prepare("SELECT summary, updated_at FROM memory_summary WHERE repo = ?").get(repo);
1874
+ const row = this.get(
1875
+ "SELECT summary, updated_at FROM memory_summary WHERE repo = ?",
1876
+ [repo]
1877
+ );
1904
1878
  return row || null;
1905
1879
  }
1906
1880
  upsertSummary(repo, summary) {
1907
- this.db.prepare(
1908
- `
1909
- INSERT INTO memory_summary (repo, summary, updated_at) VALUES (?, ?, ?)
1910
- ON CONFLICT(repo) DO UPDATE SET summary = excluded.summary, updated_at = excluded.updated_at
1911
- `
1912
- ).run(repo, summary, (/* @__PURE__ */ new Date()).toISOString());
1881
+ this.run(
1882
+ `INSERT INTO memory_summary (repo, summary, updated_at) VALUES (?, ?, ?)
1883
+ ON CONFLICT(repo) DO UPDATE SET summary = excluded.summary, updated_at = excluded.updated_at`,
1884
+ [repo, summary, (/* @__PURE__ */ new Date()).toISOString()]
1885
+ );
1913
1886
  }
1914
1887
  };
1915
1888
 
@@ -1926,44 +1899,104 @@ function resolveDbPath() {
1926
1899
  return standardPath;
1927
1900
  }
1928
1901
  var DB_PATH = resolveDbPath();
1929
- var SQLiteStore = class {
1902
+ var dbPathInstance = "";
1903
+ var sqlJsReady = null;
1904
+ var sqlJsModule = null;
1905
+ async function getSqlJs() {
1906
+ if (!sqlJsModule) {
1907
+ sqlJsModule = await initSqlJs();
1908
+ }
1909
+ await sqlJsReady;
1910
+ return sqlJsModule;
1911
+ }
1912
+ function createSaveFunction(db, dbPath) {
1913
+ return () => {
1914
+ if (dbPath && dbPath !== ":memory:") {
1915
+ const data = db.export();
1916
+ const buffer = Buffer.from(data);
1917
+ fs2.writeFileSync(dbPath, buffer);
1918
+ }
1919
+ };
1920
+ }
1921
+ function warmUpSqlJs() {
1922
+ if (!sqlJsReady) {
1923
+ sqlJsReady = (async () => {
1924
+ sqlJsModule = await initSqlJs();
1925
+ })();
1926
+ }
1927
+ return sqlJsReady;
1928
+ }
1929
+ var SQLiteStore = class _SQLiteStore {
1930
1930
  db;
1931
1931
  memories;
1932
1932
  tasks;
1933
1933
  actions;
1934
1934
  system;
1935
1935
  summaries;
1936
+ _ready;
1936
1937
  constructor(dbPath) {
1937
1938
  const finalPath = dbPath ?? DB_PATH;
1938
- const dbDir = path2.dirname(finalPath);
1939
- if (!fs2.existsSync(dbDir)) {
1940
- fs2.mkdirSync(dbDir, { recursive: true });
1941
- }
1942
- this.db = new Database(finalPath);
1943
- this.db.pragma("journal_mode = WAL");
1944
- this.db.pragma("synchronous = NORMAL");
1945
- this.db.pragma("busy_timeout = 5000");
1946
- const migrator = new MigrationManager(this.db);
1939
+ dbPathInstance = finalPath;
1940
+ warmUpSqlJs();
1941
+ this.db = {};
1942
+ this.memories = {};
1943
+ this.tasks = {};
1944
+ this.actions = {};
1945
+ this.system = {};
1946
+ this.summaries = {};
1947
+ this._ready = this._init(finalPath);
1948
+ }
1949
+ async _init(finalPath) {
1950
+ const SQL = await getSqlJs();
1951
+ let db;
1952
+ if (finalPath === ":memory:") {
1953
+ db = new SQL.Database();
1954
+ } else {
1955
+ const dbDir = path2.dirname(finalPath);
1956
+ if (!fs2.existsSync(dbDir)) {
1957
+ fs2.mkdirSync(dbDir, { recursive: true });
1958
+ }
1959
+ if (fs2.existsSync(finalPath)) {
1960
+ const fileBuffer = fs2.readFileSync(finalPath);
1961
+ db = new SQL.Database(fileBuffer);
1962
+ } else {
1963
+ db = new SQL.Database();
1964
+ }
1965
+ }
1966
+ const saveDb = createSaveFunction(db, finalPath);
1967
+ db.run("PRAGMA journal_mode = WAL");
1968
+ db.run("PRAGMA synchronous = NORMAL");
1969
+ db.run("PRAGMA busy_timeout = 5000");
1970
+ const migrator = new MigrationManager(db, saveDb);
1947
1971
  migrator.migrate();
1948
- this.memories = new MemoryEntity(this.db);
1949
- this.tasks = new TaskEntity(this.db);
1950
- this.actions = new ActionEntity(this.db);
1951
- this.system = new SystemEntity(this.db);
1952
- this.summaries = new SummaryEntity(this.db);
1953
- process.stderr.write(`${(/* @__PURE__ */ new Date()).toISOString()} [INFO ] SQLiteStore initialized at ${finalPath}
1954
- `);
1972
+ migrator.addMemoryCodeColumn();
1973
+ Object.assign(this, {
1974
+ db,
1975
+ memories: new MemoryEntity(db, saveDb),
1976
+ tasks: new TaskEntity(db, saveDb),
1977
+ actions: new ActionEntity(db, saveDb),
1978
+ system: new SystemEntity(db, saveDb),
1979
+ summaries: new SummaryEntity(db, saveDb)
1980
+ });
1981
+ if (finalPath !== ":memory:") {
1982
+ saveDb();
1983
+ }
1984
+ }
1985
+ async ready() {
1986
+ await this._ready;
1987
+ }
1988
+ static async create(dbPath) {
1989
+ const store = new _SQLiteStore(dbPath);
1990
+ await store.ready();
1991
+ return store;
1955
1992
  }
1956
- /**
1957
- * Returns the current database file path.
1958
- */
1959
1993
  getDbPath() {
1960
- return this.db.name;
1994
+ return dbPathInstance;
1961
1995
  }
1962
- /**
1963
- * Closes the database connection.
1964
- */
1965
1996
  close() {
1966
- this.db.close();
1997
+ if (this.db && this.db.close) {
1998
+ this.db.close();
1999
+ }
1967
2000
  }
1968
2001
  };
1969
2002
 
@@ -2087,8 +2120,9 @@ var MemoryTypeSchema = z.enum([
2087
2120
  "task_archive"
2088
2121
  ]);
2089
2122
  var MemoryStoreSchema = z.object({
2123
+ code: z.string().max(20).optional(),
2090
2124
  type: MemoryTypeSchema,
2091
- title: z.string().min(3).max(100),
2125
+ title: z.string().min(3).max(255),
2092
2126
  content: z.string().min(10),
2093
2127
  importance: z.number().min(1).max(5),
2094
2128
  agent: z.string().min(1),
@@ -2104,7 +2138,7 @@ var MemoryStoreSchema = z.object({
2104
2138
  var MemoryUpdateSchema = z.object({
2105
2139
  id: z.string().uuid(),
2106
2140
  type: MemoryTypeSchema.optional(),
2107
- title: z.string().min(3).max(100).optional(),
2141
+ title: z.string().min(3).max(255).optional(),
2108
2142
  content: z.string().min(10).optional(),
2109
2143
  importance: z.number().min(1).max(5).optional(),
2110
2144
  agent: z.string().optional(),
@@ -2131,7 +2165,8 @@ var MemorySearchSchema = z.object({
2131
2165
  current_file_path: z.string().optional(),
2132
2166
  include_archived: z.boolean().default(false),
2133
2167
  current_tags: z.array(z.string()).optional(),
2134
- scope: MemoryScopeSchema.partial().optional()
2168
+ scope: MemoryScopeSchema.partial().optional(),
2169
+ structured: z.boolean().default(false)
2135
2170
  });
2136
2171
  var MemoryAcknowledgeSchema = z.object({
2137
2172
  memory_id: z.string().uuid(),
@@ -2141,7 +2176,8 @@ var MemoryAcknowledgeSchema = z.object({
2141
2176
  var MemoryRecapSchema = z.object({
2142
2177
  repo: z.string().min(1).transform(normalizeRepo),
2143
2178
  limit: z.number().min(1).max(50).default(20),
2144
- offset: z.number().min(0).default(0)
2179
+ offset: z.number().min(0).default(0),
2180
+ structured: z.boolean().default(false)
2145
2181
  });
2146
2182
  var MemoryDeleteSchema = z.object({
2147
2183
  repo: z.string().min(1).transform(normalizeRepo).optional(),
@@ -2201,10 +2237,13 @@ var TaskCreateSchema = z.object({
2201
2237
  est_tokens: z.number().int().min(0).optional(),
2202
2238
  // Allow bulk tasks
2203
2239
  tasks: z.array(SingleTaskCreateSchema).min(1).optional()
2204
- }).refine((data) => {
2205
- if (data.tasks) return true;
2206
- return !!(data.task_code && data.phase && data.title && data.description);
2207
- }, { message: "Either 'tasks' array or single task fields (task_code, phase, title, description) must be provided" });
2240
+ }).refine(
2241
+ (data) => {
2242
+ if (data.tasks) return true;
2243
+ return !!(data.task_code && data.phase && data.title && data.description);
2244
+ },
2245
+ { message: "Either 'tasks' array or single task fields (task_code, phase, title, description) must be provided" }
2246
+ );
2208
2247
  var TaskCreateInteractiveSchema = SingleTaskCreateSchema.partial().extend({
2209
2248
  repo: z.string().min(1).transform(normalizeRepo).optional()
2210
2249
  });
@@ -2240,14 +2279,16 @@ var TaskListSchema = z.object({
2240
2279
  phase: z.string().optional(),
2241
2280
  query: z.string().optional(),
2242
2281
  limit: z.number().min(1).max(100).default(15),
2243
- offset: z.number().min(0).default(0)
2282
+ offset: z.number().min(0).default(0),
2283
+ structured: z.boolean().default(false)
2244
2284
  });
2245
2285
  var TaskSearchSchema = z.object({
2246
2286
  repo: z.string().min(1).transform(normalizeRepo),
2247
2287
  query: z.string().min(1),
2248
2288
  status: z.string().optional(),
2249
2289
  limit: z.number().min(1).max(100).default(10),
2250
- offset: z.number().min(0).default(0)
2290
+ offset: z.number().min(0).default(0),
2291
+ structured: z.boolean().default(false)
2251
2292
  });
2252
2293
  var TaskDeleteSchema = z.object({
2253
2294
  repo: z.string().min(1).transform(normalizeRepo),
@@ -2257,12 +2298,16 @@ var TaskDeleteSchema = z.object({
2257
2298
  message: "Either 'id' or 'ids' must be provided for deletion"
2258
2299
  });
2259
2300
  var MemoryDetailSchema = z.object({
2260
- id: z.string().uuid()
2301
+ id: z.string().uuid().optional(),
2302
+ code: z.string().max(20).optional()
2303
+ }).refine((data) => data.id !== void 0 || data.code !== void 0, {
2304
+ message: "Either id or code must be provided"
2261
2305
  });
2262
2306
  var TaskGetSchema = z.object({
2263
2307
  repo: z.string().min(1).transform(normalizeRepo),
2264
2308
  id: z.string().uuid().optional(),
2265
- task_code: z.string().optional()
2309
+ task_code: z.string().optional(),
2310
+ structured: z.boolean().default(false)
2266
2311
  }).refine((data) => data.id !== void 0 || data.task_code !== void 0, {
2267
2312
  message: "Either id or task_code must be provided"
2268
2313
  });
@@ -2376,7 +2421,12 @@ var TOOL_DEFINITIONS = [
2376
2421
  properties: {
2377
2422
  repo: { type: "string", description: "Repository name" },
2378
2423
  id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
2379
- task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" }
2424
+ task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
2425
+ structured: {
2426
+ type: "boolean",
2427
+ default: false,
2428
+ description: "If true, returns structured JSON without the text content details."
2429
+ }
2380
2430
  },
2381
2431
  required: ["repo"]
2382
2432
  }
@@ -2470,6 +2520,7 @@ var TOOL_DEFINITIONS = [
2470
2520
  properties: {
2471
2521
  success: { type: "boolean" },
2472
2522
  id: { type: "string" },
2523
+ code: { type: "string" },
2473
2524
  repo: { type: "string" },
2474
2525
  type: { type: "string" },
2475
2526
  title: { type: "string" },
@@ -2612,6 +2663,11 @@ var TOOL_DEFINITIONS = [
2612
2663
  folder: { type: "string" },
2613
2664
  language: { type: "string" }
2614
2665
  }
2666
+ },
2667
+ structured: {
2668
+ type: "boolean",
2669
+ default: false,
2670
+ description: "If true, returns structured JSON without the text content summary."
2615
2671
  }
2616
2672
  },
2617
2673
  required: ["query", "repo"]
@@ -2738,6 +2794,11 @@ var TOOL_DEFINITIONS = [
2738
2794
  minimum: 0,
2739
2795
  default: 0,
2740
2796
  description: "Number of memories to skip for pagination (optional, default 0)"
2797
+ },
2798
+ structured: {
2799
+ type: "boolean",
2800
+ default: false,
2801
+ description: "If true, returns structured JSON without the text content summary."
2741
2802
  }
2742
2803
  },
2743
2804
  required: ["repo"]
@@ -2796,7 +2857,12 @@ var TOOL_DEFINITIONS = [
2796
2857
  repo: { type: "string", description: "Repository name" },
2797
2858
  task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
2798
2859
  phase: { type: "string", description: "Project phase (Required for single task)" },
2799
- title: { type: "string", minLength: 3, maxLength: 100, description: "Task objective (Required for single task)" },
2860
+ title: {
2861
+ type: "string",
2862
+ minLength: 3,
2863
+ maxLength: 100,
2864
+ description: "Task objective (Required for single task)"
2865
+ },
2800
2866
  description: { type: "string", description: "Detailed description (Required for single task)" },
2801
2867
  status: {
2802
2868
  type: "string",
@@ -2845,8 +2911,8 @@ var TOOL_DEFINITIONS = [
2845
2911
  properties: {
2846
2912
  success: { type: "boolean" },
2847
2913
  id: { type: "string" },
2848
- repo: { type: "string" },
2849
2914
  task_code: { type: "string" },
2915
+ repo: { type: "string" },
2850
2916
  phase: { type: "string" },
2851
2917
  title: { type: "string" },
2852
2918
  status: { type: "string" },
@@ -2899,7 +2965,10 @@ var TOOL_DEFINITIONS = [
2899
2965
  minimum: 0,
2900
2966
  description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
2901
2967
  },
2902
- force: { type: "boolean", description: "If true, bypasses status transition validation (e.g. pending -> completed)." }
2968
+ force: {
2969
+ type: "boolean",
2970
+ description: "If true, bypasses status transition validation (e.g. pending -> completed)."
2971
+ }
2903
2972
  },
2904
2973
  required: ["repo"]
2905
2974
  },
@@ -2993,6 +3062,11 @@ var TOOL_DEFINITIONS = [
2993
3062
  minimum: 0,
2994
3063
  default: 0,
2995
3064
  description: "Offset for pagination"
3065
+ },
3066
+ structured: {
3067
+ type: "boolean",
3068
+ default: false,
3069
+ description: "If true, returns structured JSON without the text content summary."
2996
3070
  }
2997
3071
  },
2998
3072
  required: ["repo"]
@@ -3471,7 +3545,29 @@ import { fileURLToPath as fileURLToPath3 } from "url";
3471
3545
  import matter from "gray-matter";
3472
3546
  var __filename = fileURLToPath3(import.meta.url);
3473
3547
  var __dirname2 = path4.dirname(__filename);
3474
- var PROMPT_DIR = path4.join(__dirname2, "definitions");
3548
+ function findPromptDir() {
3549
+ const candidates = [
3550
+ // Production: /dist/prompts (sibling of dist/mcp/)
3551
+ ["../../prompts", "../../prompts"],
3552
+ // Dev: /src/mcp/prompts/definitions (next to loader.ts)
3553
+ ["./definitions", "./definitions"]
3554
+ ].map(([prod, dev]) => {
3555
+ const prodPath = path4.resolve(__dirname2, prod);
3556
+ if (fs3.existsSync(prodPath) && fs3.readdirSync(prodPath).some((f) => f.endsWith(".md"))) {
3557
+ return prodPath;
3558
+ }
3559
+ const devPath = path4.resolve(__dirname2, dev);
3560
+ if (fs3.existsSync(devPath) && fs3.readdirSync(devPath).some((f) => f.endsWith(".md"))) {
3561
+ return devPath;
3562
+ }
3563
+ return null;
3564
+ }).filter(Boolean);
3565
+ if (candidates[0]) {
3566
+ return candidates[0];
3567
+ }
3568
+ return path4.resolve(__dirname2, "./definitions");
3569
+ }
3570
+ var PROMPT_DIR = findPromptDir();
3475
3571
  function listPromptFiles() {
3476
3572
  if (!fs3.existsSync(PROMPT_DIR)) return [];
3477
3573
  return fs3.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();