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.
- package/.history/package_20260202121115.json +49 -0
- package/dist/cli/index.js +107 -3
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +78 -0
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +107 -3
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/session-end.js +107 -3
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +107 -3
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +107 -3
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +140 -70
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/server/api/index.js +107 -3
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +107 -3
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +124 -3
- package/dist/services/memory-service.js.map +2 -2
- package/package.json +1 -1
- package/src/core/sqlite-event-store.ts +98 -0
- package/src/hooks/user-prompt-submit.ts +34 -60
- package/src/services/memory-service.ts +71 -4
|
@@ -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
|
*/
|