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.
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "claude-memory-layer",
3
+ "version": "1.0.10",
4
+ "description": "Claude Code plugin that learns from conversations to provide personalized assistance",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "claude-memory-layer": "dist/cli/index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsx scripts/build.ts",
12
+ "dev": "tsx src/cli/index.ts",
13
+ "test": "vitest",
14
+ "test:coverage": "vitest --coverage",
15
+ "lint": "eslint src/**/*.ts",
16
+ "typecheck": "tsc --noEmit"
17
+ },
18
+ "keywords": [
19
+ "claude-code",
20
+ "plugin",
21
+ "memory",
22
+ "learning",
23
+ "personalization",
24
+ "context"
25
+ ],
26
+ "author": "Buzzni",
27
+ "license": "MIT",
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "dependencies": {
32
+ "@hono/node-server": "^1.13.0",
33
+ "@lancedb/lancedb": "^0.5.0",
34
+ "@xenova/transformers": "^2.17.0",
35
+ "better-sqlite3": "^12.6.2",
36
+ "commander": "^12.0.0",
37
+ "duckdb": "^0.10.0",
38
+ "hono": "^4.0.0",
39
+ "zod": "^3.22.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/better-sqlite3": "^7.6.13",
43
+ "@types/node": "^20.11.0",
44
+ "esbuild": "^0.20.0",
45
+ "tsx": "^4.7.0",
46
+ "typescript": "^5.4.0",
47
+ "vitest": "^1.4.0"
48
+ }
49
+ }
package/dist/cli/index.js CHANGED
@@ -1041,6 +1041,28 @@ var SQLiteEventStore = class {
1041
1041
  CREATE INDEX IF NOT EXISTS idx_consolidated_confidence ON consolidated_memories(confidence);
1042
1042
  CREATE INDEX IF NOT EXISTS idx_continuity_created ON continuity_log(created_at);
1043
1043
  CREATE INDEX IF NOT EXISTS idx_embedding_outbox_status ON embedding_outbox(status);
1044
+
1045
+ -- FTS5 Full-Text Search for fast keyword search
1046
+ CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
1047
+ content,
1048
+ event_id UNINDEXED,
1049
+ content='events',
1050
+ content_rowid='rowid'
1051
+ );
1052
+
1053
+ -- Triggers to keep FTS in sync with events table
1054
+ CREATE TRIGGER IF NOT EXISTS events_fts_insert AFTER INSERT ON events BEGIN
1055
+ INSERT INTO events_fts(rowid, content, event_id) VALUES (NEW.rowid, NEW.content, NEW.id);
1056
+ END;
1057
+
1058
+ CREATE TRIGGER IF NOT EXISTS events_fts_delete AFTER DELETE ON events BEGIN
1059
+ INSERT INTO events_fts(events_fts, rowid, content, event_id) VALUES('delete', OLD.rowid, OLD.content, OLD.id);
1060
+ END;
1061
+
1062
+ CREATE TRIGGER IF NOT EXISTS events_fts_update AFTER UPDATE ON events BEGIN
1063
+ INSERT INTO events_fts(events_fts, rowid, content, event_id) VALUES('delete', OLD.rowid, OLD.content, OLD.id);
1064
+ INSERT INTO events_fts(rowid, content, event_id) VALUES (NEW.rowid, NEW.content, NEW.id);
1065
+ END;
1044
1066
  `);
1045
1067
  const tableInfo = sqliteAll(this.db, "PRAGMA table_info(events)", []);
1046
1068
  const columnNames = tableInfo.map((col) => col.name);
@@ -1481,6 +1503,62 @@ var SQLiteEventStore = class {
1481
1503
  );
1482
1504
  return rows.map((row) => this.rowToEvent(row));
1483
1505
  }
1506
+ /**
1507
+ * Fast keyword search using FTS5
1508
+ * Returns events matching the search query, ranked by relevance
1509
+ */
1510
+ async keywordSearch(query, limit = 10) {
1511
+ await this.initialize();
1512
+ const searchTerms = query.replace(/['"(){}[\]^~*?:\\/-]/g, " ").split(/\s+/).filter((term) => term.length > 1).map((term) => `"${term}"*`).join(" OR ");
1513
+ if (!searchTerms) {
1514
+ return [];
1515
+ }
1516
+ try {
1517
+ const rows = sqliteAll(
1518
+ this.db,
1519
+ `SELECT e.*, fts.rank
1520
+ FROM events_fts fts
1521
+ JOIN events e ON e.id = fts.event_id
1522
+ WHERE events_fts MATCH ?
1523
+ ORDER BY fts.rank
1524
+ LIMIT ?`,
1525
+ [searchTerms, limit]
1526
+ );
1527
+ return rows.map((row) => ({
1528
+ event: this.rowToEvent(row),
1529
+ rank: row.rank
1530
+ }));
1531
+ } catch (error) {
1532
+ const likePattern = `%${query}%`;
1533
+ const rows = sqliteAll(
1534
+ this.db,
1535
+ `SELECT *, 0 as rank FROM events
1536
+ WHERE content LIKE ?
1537
+ ORDER BY timestamp DESC
1538
+ LIMIT ?`,
1539
+ [likePattern, limit]
1540
+ );
1541
+ return rows.map((row) => ({
1542
+ event: this.rowToEvent(row),
1543
+ rank: 0
1544
+ }));
1545
+ }
1546
+ }
1547
+ /**
1548
+ * Rebuild FTS index from existing events
1549
+ * Call this once after upgrading to FTS5
1550
+ */
1551
+ async rebuildFtsIndex() {
1552
+ await this.initialize();
1553
+ const countRow = sqliteGet(this.db, "SELECT COUNT(*) as count FROM events", []);
1554
+ const totalEvents = countRow?.count ?? 0;
1555
+ sqliteExec(this.db, `
1556
+ DELETE FROM events_fts;
1557
+ INSERT INTO events_fts(rowid, content, event_id)
1558
+ SELECT rowid, content, id FROM events;
1559
+ `);
1560
+ return totalEvents;
1561
+ }
1484
1562
  /**
1485
1563
  * Get database instance for direct access
1486
1564
  */
@@ -4539,9 +4617,11 @@ var MemoryService = class {
4539
4617
  sharedStoreConfig = null;
4540
4618
  projectHash = null;
4541
4619
  readOnly;
4620
+ lightweightMode;
4542
4621
  constructor(config) {
4543
4622
  const storagePath = this.expandPath(config.storagePath);
4544
4623
  this.readOnly = config.readOnly ?? false;
4624
+ this.lightweightMode = config.lightweightMode ?? false;
4545
4625
  if (!this.readOnly && !fs.existsSync(storagePath)) {
4546
4626
  fs.mkdirSync(storagePath, { recursive: true });
4547
4627
  }
@@ -4588,6 +4668,10 @@ var MemoryService = class {
4588
4668
  if (this.initialized)
4589
4669
  return;
4590
4670
  await this.sqliteStore.initialize();
4671
+ if (this.lightweightMode) {
4672
+ this.initialized = true;
4673
+ return;
4674
+ }
4591
4675
  if (this.analyticsStore) {
4592
4676
  try {
4593
4677
  await this.analyticsStore.initialize();
@@ -4757,9 +4841,6 @@ var MemoryService = class {
4757
4841
  */
4758
4842
  async retrieveMemories(query, options) {
4759
4843
  await this.initialize();
4760
- if (this.vectorWorker) {
4761
- await this.vectorWorker.processAll();
4762
- }
4763
4844
  if (options?.includeShared && this.sharedStore) {
4764
4845
  return this.retriever.retrieveUnified(query, {
4765
4846
  ...options,
@@ -4769,6 +4850,29 @@ var MemoryService = class {
4769
4850
  }
4770
4851
  return this.retriever.retrieve(query, options);
4771
4852
  }
4853
+ /**
4854
+ * Fast keyword search using SQLite FTS5
4855
+ * Much faster than vector search - no embedding model needed
4856
+ */
4857
+ async keywordSearch(query, options) {
4858
+ await this.initialize();
4859
+ const results = await this.sqliteStore.keywordSearch(query, options?.topK ?? 10);
4860
+ const maxRank = Math.min(...results.map((r) => r.rank), -1e-3);
4861
+ const minRank = Math.max(...results.map((r) => r.rank), -1e3);
4862
+ const rankRange = maxRank - minRank || 1;
4863
+ return results.map((r) => ({
4864
+ event: r.event,
4865
+ score: 1 - (r.rank - minRank) / rankRange
4866
+ // Normalize to 0-1
4867
+ })).filter((r) => !options?.minScore || r.score >= options.minScore);
4868
+ }
4869
+ /**
4870
+ * Rebuild FTS index (call after database upgrade)
4871
+ */
4872
+ async rebuildFtsIndex() {
4873
+ await this.initialize();
4874
+ return this.sqliteStore.rebuildFtsIndex();
4875
+ }
4772
4876
  /**
4773
4877
  * Get session history
4774
4878
  */