claude-memory-layer 1.0.8 → 1.0.9

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.
Files changed (43) hide show
  1. package/.claude/settings.local.json +7 -1
  2. package/.claude-memory/test.sqlite +0 -0
  3. package/.history/package_20260202114053.json +49 -0
  4. package/HANDOFF.md +92 -0
  5. package/dist/cli/index.js +1150 -71
  6. package/dist/cli/index.js.map +4 -4
  7. package/dist/core/index.js +1033 -47
  8. package/dist/core/index.js.map +4 -4
  9. package/dist/hooks/post-tool-use.js +5589 -0
  10. package/dist/hooks/post-tool-use.js.map +7 -0
  11. package/dist/hooks/session-end.js +1117 -64
  12. package/dist/hooks/session-end.js.map +4 -4
  13. package/dist/hooks/session-start.js +1112 -63
  14. package/dist/hooks/session-start.js.map +4 -4
  15. package/dist/hooks/stop.js +1117 -64
  16. package/dist/hooks/stop.js.map +4 -4
  17. package/dist/hooks/user-prompt-submit.js +1151 -67
  18. package/dist/hooks/user-prompt-submit.js.map +4 -4
  19. package/dist/server/api/index.js +1145 -70
  20. package/dist/server/api/index.js.map +4 -4
  21. package/dist/server/index.js +1145 -70
  22. package/dist/server/index.js.map +4 -4
  23. package/dist/services/memory-service.js +1122 -65
  24. package/dist/services/memory-service.js.map +4 -4
  25. package/dist/ui/app.js +304 -0
  26. package/dist/ui/index.html +195 -1188
  27. package/dist/ui/style.css +595 -0
  28. package/package.json +3 -1
  29. package/scripts/build.ts +2 -0
  30. package/src/core/event-store.ts +18 -0
  31. package/src/core/index.ts +3 -0
  32. package/src/core/retriever.ts +4 -1
  33. package/src/core/sqlite-event-store.ts +849 -0
  34. package/src/core/sqlite-wrapper.ts +108 -0
  35. package/src/core/sync-worker.ts +228 -0
  36. package/src/core/vector-worker.ts +44 -14
  37. package/src/hooks/user-prompt-submit.ts +53 -4
  38. package/src/server/api/stats.ts +37 -7
  39. package/src/services/memory-service.ts +168 -39
  40. package/src/ui/app.js +304 -0
  41. package/src/ui/index.html +195 -1188
  42. package/src/ui/style.css +595 -0
  43. package/test_access.js +49 -0
@@ -0,0 +1,595 @@
1
+ /*
2
+ Theme: Deep Space (Premium Dark Mode)
3
+ Font: Outfit
4
+ */
5
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap');
6
+
7
+ :root {
8
+ /* Core Palette */
9
+ --bg-dark: #05050A; /* Deepest black/blue */
10
+ --bg-panel: #0F1019; /* Slightly lighter for panels */
11
+ --bg-card: rgba(22, 23, 35, 0.7); /* Glassy card background */
12
+
13
+ /* Accents */
14
+ --accent-primary: #7B61FF; /* Soft Electric Purple */
15
+ --accent-secondary: #00F0FF; /* Cyber Blue */
16
+ --accent-glow: rgba(123, 97, 255, 0.4);
17
+
18
+ /* Status Colors */
19
+ --success: #00E396;
20
+ --warning: #FEB019;
21
+ --error: #FF4560;
22
+
23
+ /* Text */
24
+ --text-primary: #FFFFFF;
25
+ --text-secondary: #8B9BB4;
26
+ --text-muted: #53627C;
27
+
28
+ /* Borders & Effects */
29
+ --border-subtle: rgba(255, 255, 255, 0.08);
30
+ --glass-border: 1px solid rgba(255, 255, 255, 0.05);
31
+ --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
32
+ --backdrop-blur: blur(12px);
33
+
34
+ /* Spacing */
35
+ --sidebar-width: 260px;
36
+ --header-height: 80px;
37
+ }
38
+
39
+ * {
40
+ margin: 0;
41
+ padding: 0;
42
+ box-sizing: border-box;
43
+ }
44
+
45
+ body {
46
+ font-family: 'Outfit', sans-serif;
47
+ background-color: var(--bg-dark);
48
+ color: var(--text-primary);
49
+ min-height: 100vh;
50
+ overflow-x: hidden;
51
+ background-image:
52
+ radial-gradient(circle at 10% 10%, rgba(123, 97, 255, 0.05) 0%, transparent 40%),
53
+ radial-gradient(circle at 90% 90%, rgba(0, 240, 255, 0.05) 0%, transparent 40%);
54
+ }
55
+
56
+ /* Scrollbar */
57
+ ::-webkit-scrollbar {
58
+ width: 6px;
59
+ height: 6px;
60
+ }
61
+ ::-webkit-scrollbar-track {
62
+ background: var(--bg-dark);
63
+ }
64
+ ::-webkit-scrollbar-thumb {
65
+ background: var(--border-subtle);
66
+ border-radius: 3px;
67
+ }
68
+ ::-webkit-scrollbar-thumb:hover {
69
+ background: var(--text-muted);
70
+ }
71
+
72
+ /* Layout */
73
+ .app-container {
74
+ display: flex;
75
+ min-height: 100vh;
76
+ }
77
+
78
+ /* Sidebar */
79
+ .sidebar {
80
+ width: var(--sidebar-width);
81
+ background: var(--bg-panel);
82
+ border-right: var(--glass-border);
83
+ padding: 24px;
84
+ display: flex;
85
+ flex-direction: column;
86
+ position: fixed;
87
+ height: 100vh;
88
+ z-index: 100;
89
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
90
+ }
91
+
92
+ .logo-area {
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 12px;
96
+ margin-bottom: 40px;
97
+ padding-left: 12px;
98
+ }
99
+
100
+ .logo-icon {
101
+ font-size: 24px;
102
+ filter: drop-shadow(0 0 8px var(--accent-glow));
103
+ }
104
+
105
+ .logo-text {
106
+ font-size: 20px;
107
+ font-weight: 700;
108
+ background: linear-gradient(135deg, #FFF 0%, #A5B4FC 100%);
109
+ -webkit-background-clip: text;
110
+ -webkit-text-fill-color: transparent;
111
+ }
112
+
113
+ .nav-menu {
114
+ list-style: none;
115
+ display: flex;
116
+ flex-direction: column;
117
+ gap: 8px;
118
+ }
119
+
120
+ .nav-item {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 12px;
124
+ padding: 14px 16px;
125
+ border-radius: 12px;
126
+ color: var(--text-secondary);
127
+ text-decoration: none;
128
+ font-weight: 500;
129
+ transition: all 0.2s ease;
130
+ cursor: pointer;
131
+ }
132
+
133
+ .nav-item:hover {
134
+ background: rgba(255, 255, 255, 0.03);
135
+ color: var(--text-primary);
136
+ }
137
+
138
+ .nav-item.active {
139
+ background: rgba(123, 97, 255, 0.1);
140
+ color: var(--accent-primary);
141
+ border: 1px solid rgba(123, 97, 255, 0.1);
142
+ }
143
+
144
+ .nav-item i {
145
+ font-size: 1.2rem;
146
+ }
147
+
148
+ /* Main Content */
149
+ .main-content {
150
+ flex: 1;
151
+ margin-left: var(--sidebar-width);
152
+ padding: 32px 40px;
153
+ }
154
+
155
+ /* Header */
156
+ .top-header {
157
+ display: flex;
158
+ justify-content: space-between;
159
+ align-items: center;
160
+ margin-bottom: 32px;
161
+ }
162
+
163
+ .page-title h1 {
164
+ font-size: 28px;
165
+ font-weight: 600;
166
+ letter-spacing: -0.5px;
167
+ }
168
+
169
+ .page-title p {
170
+ color: var(--text-secondary);
171
+ font-size: 14px;
172
+ margin-top: 4px;
173
+ }
174
+
175
+ .header-actions {
176
+ display: flex;
177
+ gap: 16px;
178
+ align-items: center;
179
+ }
180
+
181
+ .btn {
182
+ display: flex;
183
+ align-items: center;
184
+ gap: 8px;
185
+ padding: 10px 20px;
186
+ border-radius: 10px;
187
+ font-family: inherit;
188
+ font-weight: 600;
189
+ font-size: 14px;
190
+ cursor: pointer;
191
+ transition: all 0.2s ease;
192
+ border: none;
193
+ outline: none;
194
+ }
195
+
196
+ .btn-primary {
197
+ background: linear-gradient(135deg, var(--accent-primary) 0%, #6343E0 100%);
198
+ color: white;
199
+ box-shadow: 0 4px 12px var(--accent-glow);
200
+ }
201
+
202
+ .btn-primary:hover {
203
+ transform: translateY(-2px);
204
+ box-shadow: 0 6px 16px var(--accent-glow);
205
+ }
206
+
207
+ .btn-secondary {
208
+ background: rgba(255, 255, 255, 0.05);
209
+ color: var(--text-primary);
210
+ border: 1px solid rgba(255, 255, 255, 0.1);
211
+ }
212
+
213
+ .btn-secondary:hover {
214
+ background: rgba(255, 255, 255, 0.1);
215
+ }
216
+
217
+ .search-wrapper {
218
+ position: relative;
219
+ width: 300px;
220
+ }
221
+
222
+ .search-wrapper i {
223
+ position: absolute;
224
+ left: 14px;
225
+ top: 50%;
226
+ transform: translateY(-50%);
227
+ color: var(--text-secondary);
228
+ }
229
+
230
+ .search-input {
231
+ width: 100%;
232
+ padding: 12px 14px 12px 40px;
233
+ background: var(--bg-panel);
234
+ border: var(--glass-border);
235
+ border-radius: 10px;
236
+ color: var(--text-primary);
237
+ font-family: inherit;
238
+ transition: all 0.2s;
239
+ }
240
+
241
+ .search-input:focus {
242
+ outline: none;
243
+ border-color: var(--accent-primary);
244
+ box-shadow: 0 0 0 2px rgba(123, 97, 255, 0.1);
245
+ }
246
+
247
+ /* Stats Grid */
248
+ .stats-grid {
249
+ display: grid;
250
+ grid-template-columns: repeat(4, 1fr);
251
+ gap: 24px;
252
+ margin-bottom: 32px;
253
+ }
254
+
255
+ .stat-card {
256
+ background: var(--bg-card);
257
+ backdrop-filter: var(--backdrop-blur);
258
+ border: var(--glass-border);
259
+ padding: 24px;
260
+ border-radius: 16px;
261
+ position: relative;
262
+ overflow: hidden;
263
+ transition: transform 0.2s;
264
+ }
265
+
266
+ .stat-card:hover {
267
+ transform: translateY(-4px);
268
+ }
269
+
270
+ .stat-card::before {
271
+ content: '';
272
+ position: absolute;
273
+ top: 0;
274
+ left: 0;
275
+ width: 100%;
276
+ height: 4px;
277
+ background: linear-gradient(90deg, var(--accent-primary), transparent);
278
+ opacity: 0.5;
279
+ }
280
+
281
+ .stat-value {
282
+ font-size: 36px;
283
+ font-weight: 700;
284
+ margin-bottom: 8px;
285
+ background: linear-gradient(180deg, #FFF 0%, #E0E0E0 100%);
286
+ -webkit-background-clip: text;
287
+ -webkit-text-fill-color: transparent;
288
+ }
289
+
290
+ .stat-label {
291
+ color: var(--text-secondary);
292
+ font-size: 13px;
293
+ text-transform: uppercase;
294
+ letter-spacing: 1px;
295
+ display: flex;
296
+ align-items: center;
297
+ gap: 8px;
298
+ }
299
+
300
+ /* Content Grid */
301
+ .dashboard-grid {
302
+ display: grid;
303
+ grid-template-columns: 2fr 1fr;
304
+ gap: 24px;
305
+ }
306
+
307
+ /* Card Common */
308
+ .card {
309
+ background: var(--bg-card);
310
+ border: var(--glass-border);
311
+ border-radius: 16px;
312
+ padding: 24px;
313
+ margin-bottom: 24px;
314
+ }
315
+
316
+ .card-header {
317
+ display: flex;
318
+ justify-content: space-between;
319
+ align-items: center;
320
+ margin-bottom: 20px;
321
+ }
322
+
323
+ .card-title {
324
+ font-size: 18px;
325
+ font-weight: 600;
326
+ display: flex;
327
+ align-items: center;
328
+ gap: 10px;
329
+ }
330
+
331
+ .card-title i {
332
+ color: var(--accent-primary);
333
+ }
334
+
335
+ /* Memory Pipeline */
336
+ .pipeline-container {
337
+ position: relative;
338
+ padding: 20px 0;
339
+ }
340
+
341
+ .pipeline-steps {
342
+ display: flex;
343
+ justify-content: space-between;
344
+ position: relative;
345
+ }
346
+
347
+ .pipeline-steps::before {
348
+ content: '';
349
+ position: absolute;
350
+ top: 50%;
351
+ left: 0;
352
+ width: 100%;
353
+ height: 2px;
354
+ background: var(--bg-panel);
355
+ z-index: 0;
356
+ transform: translateY(-50%);
357
+ }
358
+
359
+ .p-step {
360
+ position: relative;
361
+ z-index: 1;
362
+ background: var(--bg-panel);
363
+ border: 1px solid var(--border-subtle);
364
+ padding: 12px 24px;
365
+ border-radius: 12px;
366
+ display: flex;
367
+ flex-direction: column;
368
+ align-items: center;
369
+ gap: 6px;
370
+ cursor: pointer;
371
+ transition: all 0.3s;
372
+ min-width: 120px;
373
+ }
374
+
375
+ .p-step.active {
376
+ background: rgba(123, 97, 255, 0.1);
377
+ border-color: var(--accent-primary);
378
+ box-shadow: 0 0 20px rgba(123, 97, 255, 0.15);
379
+ }
380
+
381
+ .p-step-name {
382
+ font-weight: 600;
383
+ font-size: 14px;
384
+ }
385
+
386
+ .p-step-count {
387
+ font-size: 12px;
388
+ color: var(--text-secondary);
389
+ }
390
+
391
+ .p-step.active .p-step-name {
392
+ color: var(--accent-primary);
393
+ }
394
+
395
+ .p-step.active .p-step-count {
396
+ color: var(--text-primary);
397
+ }
398
+
399
+ /* Event List */
400
+ .event-list {
401
+ display: flex;
402
+ flex-direction: column;
403
+ gap: 12px;
404
+ max-height: 500px;
405
+ overflow-y: auto;
406
+ padding-right: 8px;
407
+ }
408
+
409
+ .event-item {
410
+ background: rgba(255, 255, 255, 0.02);
411
+ border: 1px solid transparent;
412
+ padding: 16px;
413
+ border-radius: 10px;
414
+ transition: all 0.2s;
415
+ }
416
+
417
+ .event-item:hover {
418
+ background: rgba(255, 255, 255, 0.04);
419
+ border-color: var(--border-subtle);
420
+ }
421
+
422
+ .event-header {
423
+ display: flex;
424
+ justify-content: space-between;
425
+ margin-bottom: 8px;
426
+ }
427
+
428
+ .event-type-badge {
429
+ font-size: 11px;
430
+ padding: 4px 8px;
431
+ border-radius: 6px;
432
+ font-weight: 600;
433
+ text-transform: uppercase;
434
+ }
435
+
436
+ .type-user { background: rgba(59, 130, 246, 0.2); color: #60A5FA; }
437
+ .type-agent { background: rgba(16, 185, 129, 0.2); color: #34D399; }
438
+ .type-tool { background: rgba(245, 158, 11, 0.2); color: #FBBF24; }
439
+ .type-system { background: rgba(139, 92, 246, 0.2); color: #A78BFA; }
440
+
441
+ .event-time {
442
+ font-size: 12px;
443
+ color: var(--text-muted);
444
+ }
445
+
446
+ .event-content {
447
+ font-size: 14px;
448
+ color: var(--text-secondary);
449
+ line-height: 1.5;
450
+ display: -webkit-box;
451
+ -webkit-line-clamp: 2;
452
+ -webkit-box-orient: vertical;
453
+ overflow: hidden;
454
+ }
455
+
456
+ /* Shared Items */
457
+ .shared-list {
458
+ display: flex;
459
+ flex-direction: column;
460
+ gap: 12px;
461
+ }
462
+
463
+ .shared-item {
464
+ display: flex;
465
+ align-items: center;
466
+ justify-content: space-between;
467
+ padding: 16px;
468
+ background: var(--bg-panel);
469
+ border-radius: 10px;
470
+ }
471
+
472
+ .shared-info {
473
+ display: flex;
474
+ align-items: center;
475
+ gap: 12px;
476
+ }
477
+
478
+ .shared-icon {
479
+ width: 40px;
480
+ height: 40px;
481
+ background: rgba(255, 255, 255, 0.05);
482
+ border-radius: 8px;
483
+ display: flex;
484
+ align-items: center;
485
+ justify-content: center;
486
+ font-size: 18px;
487
+ }
488
+
489
+ .shared-count {
490
+ font-family: monospace;
491
+ font-size: 16px;
492
+ font-weight: 700;
493
+ color: var(--accent-secondary);
494
+ }
495
+
496
+ /* Endless Mode */
497
+ .endless-card {
498
+ background: linear-gradient(135deg, rgba(16, 16, 24, 0.6) 0%, rgba(20, 20, 30, 0.6) 100%);
499
+ position: relative;
500
+ overflow: hidden;
501
+ }
502
+
503
+ .endless-card::after {
504
+ content: '';
505
+ position: absolute;
506
+ top: 0;
507
+ right: 0;
508
+ width: 150px;
509
+ height: 150px;
510
+ background: radial-gradient(circle, rgba(123, 97, 255, 0.1) 0%, transparent 70%);
511
+ pointer-events: none;
512
+ }
513
+
514
+ .status-dot {
515
+ width: 8px;
516
+ height: 8px;
517
+ border-radius: 50%;
518
+ background: var(--text-muted);
519
+ box-shadow: 0 0 0 2px rgba(255,255,255,0.05);
520
+ }
521
+
522
+ .status-dot.active {
523
+ background: var(--success);
524
+ box-shadow: 0 0 0 4px rgba(0, 227, 150, 0.1);
525
+ animation: pulse 2s infinite;
526
+ }
527
+
528
+ @keyframes pulse {
529
+ 0% { box-shadow: 0 0 0 0 rgba(0, 227, 150, 0.4); }
530
+ 70% { box-shadow: 0 0 0 10px rgba(0, 227, 150, 0); }
531
+ 100% { box-shadow: 0 0 0 0 rgba(0, 227, 150, 0); }
532
+ }
533
+
534
+ /* Chart Container Overrides */
535
+ .apexcharts-tooltip {
536
+ background: var(--bg-panel) !important;
537
+ border-color: var(--border-subtle) !important;
538
+ color: var(--text-primary) !important;
539
+ box-shadow: var(--glass-shadow) !important;
540
+ }
541
+
542
+ .apexcharts-tooltip-title {
543
+ background: var(--bg-dark) !important;
544
+ border-bottom: 1px solid var(--border-subtle) !important;
545
+ font-family: 'Outfit', sans-serif !important;
546
+ }
547
+
548
+ .apexcharts-text {
549
+ fill: var(--text-muted) !important;
550
+ font-family: 'Outfit', sans-serif !important;
551
+ }
552
+
553
+ .apexcharts-gridline {
554
+ stroke: var(--border-subtle) !important;
555
+ }
556
+
557
+ /* Mobile Responsive */
558
+ @media (max-width: 1024px) {
559
+ .sidebar {
560
+ transform: translateX(-100%);
561
+ }
562
+
563
+ .sidebar.mobile-open {
564
+ transform: translateX(0);
565
+ }
566
+
567
+ .main-content {
568
+ margin-left: 0;
569
+ padding: 20px;
570
+ }
571
+ .stats-grid {
572
+ grid-template-columns: repeat(2, 1fr);
573
+ }
574
+ .dashboard-grid {
575
+ grid-template-columns: 1fr;
576
+ }
577
+ }
578
+
579
+ @media (max-width: 600px) {
580
+ .stats-grid {
581
+ grid-template-columns: 1fr;
582
+ }
583
+ .pipeline-steps {
584
+ flex-direction: column;
585
+ gap: 12px;
586
+ }
587
+ .pipeline-steps::before {
588
+ display: none;
589
+ }
590
+ .p-step {
591
+ width: 100%;
592
+ flex-direction: row;
593
+ justify-content: space-between;
594
+ }
595
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-memory-layer",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Claude Code plugin that learns from conversations to provide personalized assistance",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -32,12 +32,14 @@
32
32
  "@hono/node-server": "^1.13.0",
33
33
  "@lancedb/lancedb": "^0.5.0",
34
34
  "@xenova/transformers": "^2.17.0",
35
+ "better-sqlite3": "^12.6.2",
35
36
  "commander": "^12.0.0",
36
37
  "duckdb": "^0.10.0",
37
38
  "hono": "^4.0.0",
38
39
  "zod": "^3.22.0"
39
40
  },
40
41
  "devDependencies": {
42
+ "@types/better-sqlite3": "^7.6.13",
41
43
  "@types/node": "^20.11.0",
42
44
  "esbuild": "^0.20.0",
43
45
  "tsx": "^4.7.0",
package/scripts/build.ts CHANGED
@@ -28,6 +28,7 @@ const commonOptions: esbuild.BuildOptions = {
28
28
  '@lancedb/lancedb',
29
29
  '@xenova/transformers',
30
30
  'duckdb',
31
+ 'better-sqlite3',
31
32
  'commander',
32
33
  'zod',
33
34
  'hono',
@@ -60,6 +61,7 @@ async function build() {
60
61
  const hooks = [
61
62
  'session-start',
62
63
  'user-prompt-submit',
64
+ 'post-tool-use',
63
65
  'stop',
64
66
  'session-end'
65
67
  ];
@@ -722,6 +722,24 @@ export class EventStore {
722
722
  }));
723
723
  }
724
724
 
725
+ /**
726
+ * Increment access count for events (stub for compatibility)
727
+ */
728
+ async incrementAccessCount(eventIds: string[]): Promise<void> {
729
+ // This is a stub method for compatibility
730
+ // Actual implementation is in SQLiteEventStore
731
+ return Promise.resolve();
732
+ }
733
+
734
+ /**
735
+ * Get most accessed memories (stub for compatibility)
736
+ */
737
+ async getMostAccessed(limit: number = 10): Promise<MemoryEvent[]> {
738
+ // This is a stub method for compatibility
739
+ // Actual implementation is in SQLiteEventStore
740
+ return [];
741
+ }
742
+
725
743
  /**
726
744
  * Close database connection
727
745
  */
package/src/core/index.ts CHANGED
@@ -11,6 +11,9 @@ export * from './canonical-key.js';
11
11
 
12
12
  // Storage
13
13
  export * from './event-store.js';
14
+ export * from './sqlite-wrapper.js';
15
+ export * from './sqlite-event-store.js';
16
+ export * from './sync-worker.js';
14
17
  export * from './entity-repo.js';
15
18
  export * from './edge-repo.js';
16
19
 
@@ -246,7 +246,7 @@ export class Retriever {
246
246
  const event = await this.eventStore.getEvent(result.eventId);
247
247
  if (!event) continue;
248
248
 
249
- // Record access for graduation scoring
249
+ // Record access for graduation scoring (keep this for graduation logic)
250
250
  if (this.graduation) {
251
251
  this.graduation.recordAccess(
252
252
  event.id,
@@ -267,6 +267,9 @@ export class Retriever {
267
267
  });
268
268
  }
269
269
 
270
+ // Note: Access count is NOT incremented here anymore.
271
+ // It should be incremented only when memories are actually used in prompts.
272
+
270
273
  return memories;
271
274
  }
272
275