claude-memory-layer 1.0.8 → 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/.claude/settings.local.json +7 -1
- package/.claude-memory/test.sqlite +0 -0
- package/.history/package_20260202114053.json +49 -0
- package/.history/package_20260202121115.json +49 -0
- package/HANDOFF.md +92 -0
- package/dist/cli/index.js +1257 -74
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +1111 -47
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/post-tool-use.js +5693 -0
- package/dist/hooks/post-tool-use.js.map +7 -0
- package/dist/hooks/session-end.js +1224 -67
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +1219 -66
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +1224 -67
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +1252 -98
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +1252 -73
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +1252 -73
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +1246 -68
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/app.js +304 -0
- package/dist/ui/index.html +195 -1188
- package/dist/ui/style.css +595 -0
- package/package.json +3 -1
- package/scripts/build.ts +2 -0
- package/src/core/event-store.ts +18 -0
- package/src/core/index.ts +3 -0
- package/src/core/retriever.ts +4 -1
- package/src/core/sqlite-event-store.ts +947 -0
- package/src/core/sqlite-wrapper.ts +108 -0
- package/src/core/sync-worker.ts +228 -0
- package/src/core/vector-worker.ts +44 -14
- package/src/hooks/user-prompt-submit.ts +40 -17
- package/src/server/api/stats.ts +37 -7
- package/src/services/memory-service.ts +239 -43
- package/src/ui/app.js +304 -0
- package/src/ui/index.html +195 -1188
- package/src/ui/style.css +595 -0
- 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, "&")
|
|
300
|
+
.replace(/</g, "<")
|
|
301
|
+
.replace(/>/g, ">")
|
|
302
|
+
.replace(/"/g, """)
|
|
303
|
+
.replace(/'/g, "'");
|
|
304
|
+
}
|