claude-memory-layer 1.0.7 → 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 (53) hide show
  1. package/.claude/settings.local.json +10 -1
  2. package/.claude-memory/test.sqlite +0 -0
  3. package/.history/package_20260201192048.json +47 -0
  4. package/.history/package_20260202114053.json +49 -0
  5. package/HANDOFF.md +92 -0
  6. package/dist/cli/index.js +1711 -102
  7. package/dist/cli/index.js.map +4 -4
  8. package/dist/core/index.js +1257 -84
  9. package/dist/core/index.js.map +4 -4
  10. package/dist/hooks/post-tool-use.js +5589 -0
  11. package/dist/hooks/post-tool-use.js.map +7 -0
  12. package/dist/hooks/session-end.js +1382 -85
  13. package/dist/hooks/session-end.js.map +4 -4
  14. package/dist/hooks/session-start.js +1377 -84
  15. package/dist/hooks/session-start.js.map +4 -4
  16. package/dist/hooks/stop.js +1383 -86
  17. package/dist/hooks/stop.js.map +4 -4
  18. package/dist/hooks/user-prompt-submit.js +1412 -84
  19. package/dist/hooks/user-prompt-submit.js.map +4 -4
  20. package/dist/server/api/index.js +1576 -136
  21. package/dist/server/api/index.js.map +4 -4
  22. package/dist/server/index.js +1585 -143
  23. package/dist/server/index.js.map +4 -4
  24. package/dist/services/memory-service.js +1392 -84
  25. package/dist/services/memory-service.js.map +4 -4
  26. package/dist/ui/app.js +304 -0
  27. package/dist/ui/index.html +202 -715
  28. package/dist/ui/style.css +595 -0
  29. package/package.json +4 -1
  30. package/scripts/build.ts +5 -2
  31. package/src/cli/index.ts +226 -0
  32. package/src/core/db-wrapper.ts +8 -1
  33. package/src/core/event-store.ts +70 -3
  34. package/src/core/graduation-worker.ts +171 -0
  35. package/src/core/graduation.ts +15 -2
  36. package/src/core/index.ts +4 -0
  37. package/src/core/retriever.ts +21 -0
  38. package/src/core/sqlite-event-store.ts +849 -0
  39. package/src/core/sqlite-wrapper.ts +108 -0
  40. package/src/core/sync-worker.ts +228 -0
  41. package/src/core/vector-worker.ts +44 -14
  42. package/src/hooks/user-prompt-submit.ts +53 -4
  43. package/src/server/api/citations.ts +7 -3
  44. package/src/server/api/events.ts +7 -3
  45. package/src/server/api/search.ts +7 -3
  46. package/src/server/api/sessions.ts +7 -3
  47. package/src/server/api/stats.ts +159 -12
  48. package/src/server/index.ts +18 -9
  49. package/src/services/memory-service.ts +263 -46
  50. package/src/ui/app.js +304 -0
  51. package/src/ui/index.html +202 -715
  52. package/src/ui/style.css +595 -0
  53. package/test_access.js +49 -0
package/src/ui/app.js ADDED
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Code Memory Dashboard Logic
3
+ * Handles state management, API calls, and UI updates.
4
+ */
5
+
6
+ const API_BASE = '/api';
7
+
8
+ // State
9
+ const state = {
10
+ stats: null,
11
+ sharedStats: null,
12
+ currentLevel: 'L0',
13
+ events: [],
14
+ isLoading: false,
15
+ chartInstance: null
16
+ };
17
+
18
+ // Utils
19
+ const formatNumber = (num) => new Intl.NumberFormat().format(num || 0);
20
+
21
+ // Colors for Chart
22
+ const CHART_COLORS = {
23
+ L0: '#7B61FF',
24
+ L1: '#00F0FF',
25
+ L2: '#00E396',
26
+ L3: '#FEB019',
27
+ L4: '#FF4560'
28
+ };
29
+
30
+ // --- Initialization ---
31
+
32
+ document.addEventListener('DOMContentLoaded', () => {
33
+ initDashboard();
34
+ });
35
+
36
+ async function initDashboard() {
37
+ await refreshData();
38
+ setupEventListeners();
39
+ initActivityChart();
40
+ }
41
+
42
+ function setupEventListeners() {
43
+ // Navigation
44
+ document.querySelectorAll('.p-step').forEach(step => {
45
+ step.addEventListener('click', (e) => {
46
+ const level = e.currentTarget.dataset.level;
47
+ if (level) selectLevel(level);
48
+ });
49
+ });
50
+
51
+ // Search
52
+ const searchInput = document.getElementById('search-input');
53
+ if (searchInput) {
54
+ searchInput.addEventListener('input', debounce((e) => handleSearch(e.target.value), 300));
55
+ }
56
+
57
+ // Refresh
58
+ const refreshBtn = document.getElementById('refresh-btn');
59
+ if (refreshBtn) {
60
+ refreshBtn.addEventListener('click', refreshData);
61
+ }
62
+ }
63
+
64
+ // --- Data Fetching ---
65
+
66
+ async function refreshData() {
67
+ const btn = document.getElementById('refresh-btn');
68
+ if(btn) btn.classList.add('loading');
69
+
70
+ try {
71
+ const [stats, shared] = await Promise.all([
72
+ fetch(`${API_BASE}/stats`).then(r => r.json()).catch(() => null),
73
+ fetch(`${API_BASE}/stats/shared`).then(r => r.json()).catch(() => null)
74
+ ]);
75
+
76
+ state.stats = stats;
77
+ state.sharedStats = shared;
78
+
79
+ updateStatsUI();
80
+ updateSharedUI();
81
+ await loadLevelEvents(state.currentLevel);
82
+
83
+ // Update Endless Mode Status (Mocked if API missing)
84
+ checkEndlessStatus();
85
+
86
+ } catch (error) {
87
+ console.error('Failed to refresh data:', error);
88
+ } finally {
89
+ if(btn) btn.classList.remove('loading');
90
+ }
91
+ }
92
+
93
+ async function loadLevelEvents(level) {
94
+ state.isLoading = true;
95
+ updateEventsListUI(); // Show loading state
96
+
97
+ try {
98
+ // Determine API endpoint based on level
99
+ // L0 -> /events, others might be filtered
100
+ // For now, using the same pattern as original but adapted
101
+ const response = await fetch(`${API_BASE}/events?level=${level}&limit=20`);
102
+ if (response.ok) {
103
+ const data = await response.json();
104
+ state.events = data.events || [];
105
+ } else {
106
+ state.events = [];
107
+ }
108
+ } catch (error) {
109
+ console.error(`Failed to load events for ${level}:`, error);
110
+ state.events = [];
111
+ } finally {
112
+ state.isLoading = false;
113
+ updateEventsListUI();
114
+ }
115
+ }
116
+
117
+ // --- UI Updates ---
118
+
119
+ function updateStatsUI() {
120
+ if (!state.stats) return;
121
+
122
+ const { total_events, total_sessions, total_vectors } = state.stats;
123
+
124
+ document.getElementById('stat-events').textContent = formatNumber(total_events);
125
+ document.getElementById('stat-sessions').textContent = formatNumber(total_sessions);
126
+
127
+ // Consolidating shared stats as a simple sum for the header if needed,
128
+ // or just using the shared object
129
+ const sharedCount = state.sharedStats ?
130
+ (state.sharedStats.troubleshooting + state.sharedStats.best_practices + state.sharedStats.common_errors) : 0;
131
+
132
+ document.getElementById('stat-shared').textContent = formatNumber(sharedCount);
133
+ document.getElementById('stat-vectors').textContent = formatNumber(total_vectors);
134
+
135
+ // Update Pipeline Counts (Mock logic as original didn't have per-level counts easily accessible in stats object usually)
136
+ // If stats has level breakdown use that, otherwise distribute for visual
137
+ updatePipelineCounts(state.stats.level_counts || {});
138
+ }
139
+
140
+ function updatePipelineCounts(counts) {
141
+ document.querySelectorAll('.p-step').forEach(step => {
142
+ const level = step.dataset.level;
143
+ const countEl = step.querySelector('.p-step-count');
144
+ // Default to 0 if not found
145
+ countEl.textContent = formatNumber(counts[level] || 0);
146
+ });
147
+ }
148
+
149
+ function updateSharedUI() {
150
+ if (!state.sharedStats) return;
151
+
152
+ document.getElementById('shared-troubleshooting').textContent = formatNumber(state.sharedStats.troubleshooting);
153
+ document.getElementById('shared-best-practices').textContent = formatNumber(state.sharedStats.best_practices);
154
+ document.getElementById('shared-errors').textContent = formatNumber(state.sharedStats.common_errors);
155
+ }
156
+
157
+ function selectLevel(level) {
158
+ state.currentLevel = level;
159
+
160
+ // Update Visuals
161
+ document.querySelectorAll('.p-step').forEach(step => {
162
+ step.classList.toggle('active', step.dataset.level === level);
163
+ });
164
+
165
+ loadLevelEvents(level);
166
+ }
167
+
168
+ function updateEventsListUI() {
169
+ const container = document.getElementById('event-list-container');
170
+ container.innerHTML = '';
171
+
172
+ if (state.isLoading) {
173
+ container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-muted);">Loading events...</div>';
174
+ return;
175
+ }
176
+
177
+ if (state.events.length === 0) {
178
+ container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-muted);">No events found for this level.</div>';
179
+ return;
180
+ }
181
+
182
+ state.events.forEach(event => {
183
+ const el = document.createElement('div');
184
+ el.className = 'event-item';
185
+
186
+ const time = new Date(event.timestamp).toLocaleString();
187
+ const typeClass = `type-${event.type.toLowerCase().replace('_', '-')}`;
188
+
189
+ el.innerHTML = `
190
+ <div class="event-header">
191
+ <span class="event-type-badge ${typeClass}">${event.type}</span>
192
+ <span class="event-time">${time}</span>
193
+ </div>
194
+ <div class="event-content">${escapeHtml(event.content || '')}</div>
195
+ `;
196
+
197
+ container.appendChild(el);
198
+ });
199
+ }
200
+
201
+ // --- Charts ---
202
+
203
+ function initActivityChart() {
204
+ const chartEl = document.querySelector("#activity-chart");
205
+ if (!chartEl) return;
206
+
207
+ const options = {
208
+ series: [{
209
+ name: 'Events',
210
+ data: [30, 40, 35, 50, 49, 60, 70, 91, 125] // Placeholder data, would populate from API
211
+ }],
212
+ chart: {
213
+ type: 'area',
214
+ height: 300,
215
+ background: 'transparent',
216
+ toolbar: { show: false },
217
+ fontFamily: 'Outfit, sans-serif'
218
+ },
219
+ theme: { mode: 'dark' },
220
+ stroke: {
221
+ curve: 'smooth',
222
+ width: 3,
223
+ colors: [CHART_COLORS.L0]
224
+ },
225
+ fill: {
226
+ type: 'gradient',
227
+ gradient: {
228
+ shadeIntensity: 1,
229
+ opacityFrom: 0.7,
230
+ opacityTo: 0.1,
231
+ stops: [0, 90, 100]
232
+ }
233
+ },
234
+ dataLabels: { enabled: false },
235
+ grid: {
236
+ borderColor: 'rgba(255,255,255,0.05)',
237
+ strokeDashArray: 4,
238
+ },
239
+ xaxis: {
240
+ categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep'], // Placeholder
241
+ labels: { style: { colors: '#8B9BB4' } },
242
+ axisBorder: { show: false },
243
+ axisTicks: { show: false }
244
+ },
245
+ yaxis: {
246
+ labels: { style: { colors: '#8B9BB4' } }
247
+ },
248
+ colors: [CHART_COLORS.L0]
249
+ };
250
+
251
+ state.chartInstance = new ApexCharts(chartEl, options);
252
+ state.chartInstance.render();
253
+ }
254
+
255
+ // --- Endless Mode ---
256
+
257
+ async function checkEndlessStatus() {
258
+ const statusEl = document.getElementById('status-dot');
259
+ const textEl = document.getElementById('status-text');
260
+
261
+ // Mock check - replace with real API if available
262
+ // const isRunning = await fetch('/api/endless/status')...
263
+ const isRunning = false;
264
+
265
+ if (statusEl && textEl) {
266
+ if (isRunning) {
267
+ statusEl.classList.add('active');
268
+ textEl.textContent = 'Active Background Processing';
269
+ textEl.style.color = 'var(--success)';
270
+ } else {
271
+ statusEl.classList.remove('active');
272
+ textEl.textContent = 'Idle';
273
+ textEl.style.color = 'var(--text-muted)';
274
+ }
275
+ }
276
+ }
277
+
278
+ // --- Helpers ---
279
+
280
+ function debounce(func, wait) {
281
+ let timeout;
282
+ return function executedFunction(...args) {
283
+ const later = () => {
284
+ clearTimeout(timeout);
285
+ func(...args);
286
+ };
287
+ clearTimeout(timeout);
288
+ timeout = setTimeout(later, wait);
289
+ };
290
+ }
291
+
292
+ function handleSearch(query) {
293
+ console.log('Searching for:', query);
294
+ // Implement search logic here
295
+ }
296
+
297
+ function escapeHtml(unsafe) {
298
+ return unsafe
299
+ .replace(/&/g, "&amp;")
300
+ .replace(/</g, "&lt;")
301
+ .replace(/>/g, "&gt;")
302
+ .replace(/"/g, "&quot;")
303
+ .replace(/'/g, "&#039;");
304
+ }