claude-memory-layer 1.0.9 → 1.0.10

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.
@@ -1033,6 +1033,28 @@ var SQLiteEventStore = class {
1033
1033
  CREATE INDEX IF NOT EXISTS idx_consolidated_confidence ON consolidated_memories(confidence);
1034
1034
  CREATE INDEX IF NOT EXISTS idx_continuity_created ON continuity_log(created_at);
1035
1035
  CREATE INDEX IF NOT EXISTS idx_embedding_outbox_status ON embedding_outbox(status);
1036
+
1037
+ -- FTS5 Full-Text Search for fast keyword search
1038
+ CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
1039
+ content,
1040
+ event_id UNINDEXED,
1041
+ content='events',
1042
+ content_rowid='rowid'
1043
+ );
1044
+
1045
+ -- Triggers to keep FTS in sync with events table
1046
+ CREATE TRIGGER IF NOT EXISTS events_fts_insert AFTER INSERT ON events BEGIN
1047
+ INSERT INTO events_fts(rowid, content, event_id) VALUES (NEW.rowid, NEW.content, NEW.id);
1048
+ END;
1049
+
1050
+ CREATE TRIGGER IF NOT EXISTS events_fts_delete AFTER DELETE ON events BEGIN
1051
+ INSERT INTO events_fts(events_fts, rowid, content, event_id) VALUES('delete', OLD.rowid, OLD.content, OLD.id);
1052
+ END;
1053
+
1054
+ CREATE TRIGGER IF NOT EXISTS events_fts_update AFTER UPDATE ON events BEGIN
1055
+ INSERT INTO events_fts(events_fts, rowid, content, event_id) VALUES('delete', OLD.rowid, OLD.content, OLD.id);
1056
+ INSERT INTO events_fts(rowid, content, event_id) VALUES (NEW.rowid, NEW.content, NEW.id);
1057
+ END;
1036
1058
  `);
1037
1059
  const tableInfo = sqliteAll(this.db, "PRAGMA table_info(events)", []);
1038
1060
  const columnNames = tableInfo.map((col) => col.name);
@@ -1473,6 +1495,62 @@ var SQLiteEventStore = class {
1473
1495
  );
1474
1496
  return rows.map((row) => this.rowToEvent(row));
1475
1497
  }
1498
+ /**
1499
+ * Fast keyword search using FTS5
1500
+ * Returns events matching the search query, ranked by relevance
1501
+ */
1502
+ async keywordSearch(query, limit = 10) {
1503
+ await this.initialize();
1504
+ const searchTerms = query.replace(/['"(){}[\]^~*?:\\/-]/g, " ").split(/\s+/).filter((term) => term.length > 1).map((term) => `"${term}"*`).join(" OR ");
1505
+ if (!searchTerms) {
1506
+ return [];
1507
+ }
1508
+ try {
1509
+ const rows = sqliteAll(
1510
+ this.db,
1511
+ `SELECT e.*, fts.rank
1512
+ FROM events_fts fts
1513
+ JOIN events e ON e.id = fts.event_id
1514
+ WHERE events_fts MATCH ?
1515
+ ORDER BY fts.rank
1516
+ LIMIT ?`,
1517
+ [searchTerms, limit]
1518
+ );
1519
+ return rows.map((row) => ({
1520
+ event: this.rowToEvent(row),
1521
+ rank: row.rank
1522
+ }));
1523
+ } catch (error) {
1524
+ const likePattern = `%${query}%`;
1525
+ const rows = sqliteAll(
1526
+ this.db,
1527
+ `SELECT *, 0 as rank FROM events
1528
+ WHERE content LIKE ?
1529
+ ORDER BY timestamp DESC
1530
+ LIMIT ?`,
1531
+ [likePattern, limit]
1532
+ );
1533
+ return rows.map((row) => ({
1534
+ event: this.rowToEvent(row),
1535
+ rank: 0
1536
+ }));
1537
+ }
1538
+ }
1539
+ /**
1540
+ * Rebuild FTS index from existing events
1541
+ * Call this once after upgrading to FTS5
1542
+ */
1543
+ async rebuildFtsIndex() {
1544
+ await this.initialize();
1545
+ const countRow = sqliteGet(this.db, "SELECT COUNT(*) as count FROM events", []);
1546
+ const totalEvents = countRow?.count ?? 0;
1547
+ sqliteExec(this.db, `
1548
+ DELETE FROM events_fts;
1549
+ INSERT INTO events_fts(rowid, content, event_id)
1550
+ SELECT rowid, content, id FROM events;
1551
+ `);
1552
+ return totalEvents;
1553
+ }
1476
1554
  /**
1477
1555
  * Get database instance for direct access
1478
1556
  */
@@ -4571,9 +4649,11 @@ var MemoryService = class {
4571
4649
  sharedStoreConfig = null;
4572
4650
  projectHash = null;
4573
4651
  readOnly;
4652
+ lightweightMode;
4574
4653
  constructor(config) {
4575
4654
  const storagePath = this.expandPath(config.storagePath);
4576
4655
  this.readOnly = config.readOnly ?? false;
4656
+ this.lightweightMode = config.lightweightMode ?? false;
4577
4657
  if (!this.readOnly && !fs.existsSync(storagePath)) {
4578
4658
  fs.mkdirSync(storagePath, { recursive: true });
4579
4659
  }
@@ -4620,6 +4700,10 @@ var MemoryService = class {
4620
4700
  if (this.initialized)
4621
4701
  return;
4622
4702
  await this.sqliteStore.initialize();
4703
+ if (this.lightweightMode) {
4704
+ this.initialized = true;
4705
+ return;
4706
+ }
4623
4707
  if (this.analyticsStore) {
4624
4708
  try {
4625
4709
  await this.analyticsStore.initialize();
@@ -4789,9 +4873,6 @@ var MemoryService = class {
4789
4873
  */
4790
4874
  async retrieveMemories(query, options) {
4791
4875
  await this.initialize();
4792
- if (this.vectorWorker) {
4793
- await this.vectorWorker.processAll();
4794
- }
4795
4876
  if (options?.includeShared && this.sharedStore) {
4796
4877
  return this.retriever.retrieveUnified(query, {
4797
4878
  ...options,
@@ -4801,6 +4882,29 @@ var MemoryService = class {
4801
4882
  }
4802
4883
  return this.retriever.retrieve(query, options);
4803
4884
  }
4885
+ /**
4886
+ * Fast keyword search using SQLite FTS5
4887
+ * Much faster than vector search - no embedding model needed
4888
+ */
4889
+ async keywordSearch(query, options) {
4890
+ await this.initialize();
4891
+ const results = await this.sqliteStore.keywordSearch(query, options?.topK ?? 10);
4892
+ const maxRank = Math.min(...results.map((r) => r.rank), -1e-3);
4893
+ const minRank = Math.max(...results.map((r) => r.rank), -1e3);
4894
+ const rankRange = maxRank - minRank || 1;
4895
+ return results.map((r) => ({
4896
+ event: r.event,
4897
+ score: 1 - (r.rank - minRank) / rankRange
4898
+ // Normalize to 0-1
4899
+ })).filter((r) => !options?.minScore || r.score >= options.minScore);
4900
+ }
4901
+ /**
4902
+ * Rebuild FTS index (call after database upgrade)
4903
+ */
4904
+ async rebuildFtsIndex() {
4905
+ await this.initialize();
4906
+ return this.sqliteStore.rebuildFtsIndex();
4907
+ }
4804
4908
  /**
4805
4909
  * Get session history
4806
4910
  */
@@ -5269,6 +5373,22 @@ function getMemoryServiceForSession(sessionId) {
5269
5373
  }
5270
5374
  return getDefaultMemoryService();
5271
5375
  }
5376
+ function getLightweightMemoryService(sessionId) {
5377
+ const projectInfo = getSessionProject(sessionId);
5378
+ const key = projectInfo ? `lightweight_${projectInfo.projectHash}` : "lightweight_global";
5379
+ if (!serviceCache.has(key)) {
5380
+ const storagePath = projectInfo ? getProjectStoragePath(projectInfo.projectPath) : path.join(os.homedir(), ".claude-code", "memory");
5381
+ serviceCache.set(key, new MemoryService({
5382
+ storagePath,
5383
+ projectHash: projectInfo?.projectHash,
5384
+ lightweightMode: true,
5385
+ // Skip embedder/vector/workers
5386
+ analyticsEnabled: false,
5387
+ sharedStoreConfig: { enabled: false }
5388
+ }));
5389
+ }
5390
+ return serviceCache.get(key);
5391
+ }
5272
5392
  function createMemoryService(config) {
5273
5393
  return new MemoryService(config);
5274
5394
  }
@@ -5276,6 +5396,7 @@ export {
5276
5396
  MemoryService,
5277
5397
  createMemoryService,
5278
5398
  getDefaultMemoryService,
5399
+ getLightweightMemoryService,
5279
5400
  getMemoryServiceForProject,
5280
5401
  getMemoryServiceForSession,
5281
5402
  getProjectStoragePath,