claude-memory-layer 1.0.6 → 1.0.7
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-plugin/plugin.json +3 -3
- package/.history/package_20260201142928.json +46 -0
- package/README.md +26 -26
- package/dist/.claude-plugin/plugin.json +3 -3
- package/dist/cli/index.js +562 -8
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js.map +1 -1
- package/dist/server/api/index.js +4363 -0
- package/dist/server/api/index.js.map +7 -0
- package/dist/server/index.js +4423 -0
- package/dist/server/index.js.map +7 -0
- package/dist/ui/index.html +745 -0
- package/package.json +3 -2
- package/scripts/build.ts +32 -3
- package/src/cli/index.ts +85 -6
- package/src/core/types.ts +1 -1
- package/src/mcp/index.ts +2 -2
- package/src/mcp/tools.ts +1 -1
- package/src/server/api/stats.ts +54 -1
- package/src/ui/index.html +745 -0
package/dist/cli/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const __dirname = dirname(__filename);
|
|
|
8
8
|
|
|
9
9
|
// src/cli/index.ts
|
|
10
10
|
import { Command } from "commander";
|
|
11
|
+
import { exec } from "child_process";
|
|
11
12
|
|
|
12
13
|
// src/services/memory-service.ts
|
|
13
14
|
import * as path from "path";
|
|
@@ -70,8 +71,8 @@ function toDate(value) {
|
|
|
70
71
|
return new Date(value);
|
|
71
72
|
return new Date(String(value));
|
|
72
73
|
}
|
|
73
|
-
function createDatabase(
|
|
74
|
-
return new duckdb.Database(
|
|
74
|
+
function createDatabase(path4) {
|
|
75
|
+
return new duckdb.Database(path4);
|
|
75
76
|
}
|
|
76
77
|
function dbRun(db, sql, params = []) {
|
|
77
78
|
return new Promise((resolve2, reject) => {
|
|
@@ -4158,9 +4159,506 @@ function createSessionHistoryImporter(memoryService) {
|
|
|
4158
4159
|
return new SessionHistoryImporter(memoryService);
|
|
4159
4160
|
}
|
|
4160
4161
|
|
|
4162
|
+
// src/server/index.ts
|
|
4163
|
+
import { Hono as Hono7 } from "hono";
|
|
4164
|
+
import { cors } from "hono/cors";
|
|
4165
|
+
import { logger } from "hono/logger";
|
|
4166
|
+
import { serveStatic } from "hono/bun";
|
|
4167
|
+
import * as path3 from "path";
|
|
4168
|
+
import * as fs3 from "fs";
|
|
4169
|
+
|
|
4170
|
+
// src/server/api/index.ts
|
|
4171
|
+
import { Hono as Hono6 } from "hono";
|
|
4172
|
+
|
|
4173
|
+
// src/server/api/sessions.ts
|
|
4174
|
+
import { Hono } from "hono";
|
|
4175
|
+
var sessionsRouter = new Hono();
|
|
4176
|
+
sessionsRouter.get("/", async (c) => {
|
|
4177
|
+
const page = parseInt(c.req.query("page") || "1", 10);
|
|
4178
|
+
const pageSize = parseInt(c.req.query("pageSize") || "20", 10);
|
|
4179
|
+
try {
|
|
4180
|
+
const memoryService = getDefaultMemoryService();
|
|
4181
|
+
await memoryService.initialize();
|
|
4182
|
+
const recentEvents = await memoryService.getRecentEvents(1e3);
|
|
4183
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
4184
|
+
for (const event of recentEvents) {
|
|
4185
|
+
const existing = sessionMap.get(event.sessionId);
|
|
4186
|
+
if (!existing) {
|
|
4187
|
+
sessionMap.set(event.sessionId, {
|
|
4188
|
+
id: event.sessionId,
|
|
4189
|
+
startedAt: event.timestamp,
|
|
4190
|
+
eventCount: 1,
|
|
4191
|
+
lastEventAt: event.timestamp
|
|
4192
|
+
});
|
|
4193
|
+
} else {
|
|
4194
|
+
existing.eventCount++;
|
|
4195
|
+
if (event.timestamp < existing.startedAt) {
|
|
4196
|
+
existing.startedAt = event.timestamp;
|
|
4197
|
+
}
|
|
4198
|
+
if (event.timestamp > existing.lastEventAt) {
|
|
4199
|
+
existing.lastEventAt = event.timestamp;
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
const sessions = Array.from(sessionMap.values()).sort((a, b) => b.lastEventAt.getTime() - a.lastEventAt.getTime());
|
|
4204
|
+
const total = sessions.length;
|
|
4205
|
+
const start = (page - 1) * pageSize;
|
|
4206
|
+
const end = start + pageSize;
|
|
4207
|
+
const paginatedSessions = sessions.slice(start, end);
|
|
4208
|
+
return c.json({
|
|
4209
|
+
sessions: paginatedSessions,
|
|
4210
|
+
total,
|
|
4211
|
+
page,
|
|
4212
|
+
pageSize,
|
|
4213
|
+
hasMore: end < total
|
|
4214
|
+
});
|
|
4215
|
+
} catch (error) {
|
|
4216
|
+
return c.json({ error: error.message }, 500);
|
|
4217
|
+
}
|
|
4218
|
+
});
|
|
4219
|
+
sessionsRouter.get("/:id", async (c) => {
|
|
4220
|
+
const { id } = c.req.param();
|
|
4221
|
+
try {
|
|
4222
|
+
const memoryService = getDefaultMemoryService();
|
|
4223
|
+
await memoryService.initialize();
|
|
4224
|
+
const events = await memoryService.getSessionHistory(id);
|
|
4225
|
+
if (events.length === 0) {
|
|
4226
|
+
return c.json({ error: "Session not found" }, 404);
|
|
4227
|
+
}
|
|
4228
|
+
const session = {
|
|
4229
|
+
id,
|
|
4230
|
+
startedAt: events[0].timestamp,
|
|
4231
|
+
endedAt: events[events.length - 1].timestamp,
|
|
4232
|
+
eventCount: events.length
|
|
4233
|
+
};
|
|
4234
|
+
const eventsByType = {
|
|
4235
|
+
user_prompt: events.filter((e) => e.eventType === "user_prompt").length,
|
|
4236
|
+
agent_response: events.filter((e) => e.eventType === "agent_response").length,
|
|
4237
|
+
tool_observation: events.filter((e) => e.eventType === "tool_observation").length
|
|
4238
|
+
};
|
|
4239
|
+
return c.json({
|
|
4240
|
+
session,
|
|
4241
|
+
events: events.slice(0, 100).map((e) => ({
|
|
4242
|
+
id: e.id,
|
|
4243
|
+
eventType: e.eventType,
|
|
4244
|
+
timestamp: e.timestamp,
|
|
4245
|
+
preview: e.content.slice(0, 200) + (e.content.length > 200 ? "..." : "")
|
|
4246
|
+
})),
|
|
4247
|
+
stats: eventsByType
|
|
4248
|
+
});
|
|
4249
|
+
} catch (error) {
|
|
4250
|
+
return c.json({ error: error.message }, 500);
|
|
4251
|
+
}
|
|
4252
|
+
});
|
|
4253
|
+
|
|
4254
|
+
// src/server/api/events.ts
|
|
4255
|
+
import { Hono as Hono2 } from "hono";
|
|
4256
|
+
var eventsRouter = new Hono2();
|
|
4257
|
+
eventsRouter.get("/", async (c) => {
|
|
4258
|
+
const sessionId = c.req.query("sessionId");
|
|
4259
|
+
const eventType = c.req.query("type");
|
|
4260
|
+
const limit = parseInt(c.req.query("limit") || "100", 10);
|
|
4261
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
4262
|
+
try {
|
|
4263
|
+
const memoryService = getDefaultMemoryService();
|
|
4264
|
+
await memoryService.initialize();
|
|
4265
|
+
let events = await memoryService.getRecentEvents(limit + offset + 1e3);
|
|
4266
|
+
if (sessionId) {
|
|
4267
|
+
events = events.filter((e) => e.sessionId === sessionId);
|
|
4268
|
+
}
|
|
4269
|
+
if (eventType) {
|
|
4270
|
+
events = events.filter((e) => e.eventType === eventType);
|
|
4271
|
+
}
|
|
4272
|
+
const total = events.length;
|
|
4273
|
+
events = events.slice(offset, offset + limit);
|
|
4274
|
+
return c.json({
|
|
4275
|
+
events: events.map((e) => ({
|
|
4276
|
+
id: e.id,
|
|
4277
|
+
eventType: e.eventType,
|
|
4278
|
+
timestamp: e.timestamp,
|
|
4279
|
+
sessionId: e.sessionId,
|
|
4280
|
+
preview: e.content.slice(0, 200) + (e.content.length > 200 ? "..." : ""),
|
|
4281
|
+
contentLength: e.content.length
|
|
4282
|
+
})),
|
|
4283
|
+
total,
|
|
4284
|
+
limit,
|
|
4285
|
+
offset,
|
|
4286
|
+
hasMore: offset + limit < total
|
|
4287
|
+
});
|
|
4288
|
+
} catch (error) {
|
|
4289
|
+
return c.json({ error: error.message }, 500);
|
|
4290
|
+
}
|
|
4291
|
+
});
|
|
4292
|
+
eventsRouter.get("/:id", async (c) => {
|
|
4293
|
+
const { id } = c.req.param();
|
|
4294
|
+
try {
|
|
4295
|
+
const memoryService = getDefaultMemoryService();
|
|
4296
|
+
await memoryService.initialize();
|
|
4297
|
+
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4298
|
+
const event = recentEvents.find((e) => e.id === id);
|
|
4299
|
+
if (!event) {
|
|
4300
|
+
return c.json({ error: "Event not found" }, 404);
|
|
4301
|
+
}
|
|
4302
|
+
const sessionEvents = recentEvents.filter((e) => e.sessionId === event.sessionId).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
4303
|
+
const eventIndex = sessionEvents.findIndex((e) => e.id === id);
|
|
4304
|
+
const start = Math.max(0, eventIndex - 2);
|
|
4305
|
+
const end = Math.min(sessionEvents.length, eventIndex + 3);
|
|
4306
|
+
const context = sessionEvents.slice(start, end).filter((e) => e.id !== id);
|
|
4307
|
+
return c.json({
|
|
4308
|
+
event: {
|
|
4309
|
+
id: event.id,
|
|
4310
|
+
eventType: event.eventType,
|
|
4311
|
+
timestamp: event.timestamp,
|
|
4312
|
+
sessionId: event.sessionId,
|
|
4313
|
+
content: event.content,
|
|
4314
|
+
metadata: event.metadata
|
|
4315
|
+
},
|
|
4316
|
+
context: context.map((e) => ({
|
|
4317
|
+
id: e.id,
|
|
4318
|
+
eventType: e.eventType,
|
|
4319
|
+
timestamp: e.timestamp,
|
|
4320
|
+
preview: e.content.slice(0, 100) + (e.content.length > 100 ? "..." : "")
|
|
4321
|
+
}))
|
|
4322
|
+
});
|
|
4323
|
+
} catch (error) {
|
|
4324
|
+
return c.json({ error: error.message }, 500);
|
|
4325
|
+
}
|
|
4326
|
+
});
|
|
4327
|
+
|
|
4328
|
+
// src/server/api/search.ts
|
|
4329
|
+
import { Hono as Hono3 } from "hono";
|
|
4330
|
+
var searchRouter = new Hono3();
|
|
4331
|
+
searchRouter.post("/", async (c) => {
|
|
4332
|
+
try {
|
|
4333
|
+
const body = await c.req.json();
|
|
4334
|
+
if (!body.query) {
|
|
4335
|
+
return c.json({ error: "Query is required" }, 400);
|
|
4336
|
+
}
|
|
4337
|
+
const memoryService = getDefaultMemoryService();
|
|
4338
|
+
await memoryService.initialize();
|
|
4339
|
+
const startTime = Date.now();
|
|
4340
|
+
const result = await memoryService.retrieveMemories(body.query, {
|
|
4341
|
+
topK: body.options?.topK ?? 10,
|
|
4342
|
+
minScore: body.options?.minScore ?? 0.7,
|
|
4343
|
+
sessionId: body.options?.sessionId
|
|
4344
|
+
});
|
|
4345
|
+
const searchTime = Date.now() - startTime;
|
|
4346
|
+
return c.json({
|
|
4347
|
+
results: result.memories.map((m) => ({
|
|
4348
|
+
id: m.event.id,
|
|
4349
|
+
eventType: m.event.eventType,
|
|
4350
|
+
timestamp: m.event.timestamp,
|
|
4351
|
+
sessionId: m.event.sessionId,
|
|
4352
|
+
score: m.score,
|
|
4353
|
+
content: m.event.content,
|
|
4354
|
+
preview: m.event.content.slice(0, 200) + (m.event.content.length > 200 ? "..." : ""),
|
|
4355
|
+
context: m.sessionContext
|
|
4356
|
+
})),
|
|
4357
|
+
meta: {
|
|
4358
|
+
totalMatches: result.memories.length,
|
|
4359
|
+
searchTime,
|
|
4360
|
+
confidence: result.matchResult.confidence,
|
|
4361
|
+
totalTokens: result.totalTokens
|
|
4362
|
+
}
|
|
4363
|
+
});
|
|
4364
|
+
} catch (error) {
|
|
4365
|
+
return c.json({ error: error.message }, 500);
|
|
4366
|
+
}
|
|
4367
|
+
});
|
|
4368
|
+
searchRouter.get("/", async (c) => {
|
|
4369
|
+
const query = c.req.query("q");
|
|
4370
|
+
if (!query) {
|
|
4371
|
+
return c.json({ error: 'Query parameter "q" is required' }, 400);
|
|
4372
|
+
}
|
|
4373
|
+
const topK = parseInt(c.req.query("topK") || "5", 10);
|
|
4374
|
+
try {
|
|
4375
|
+
const memoryService = getDefaultMemoryService();
|
|
4376
|
+
await memoryService.initialize();
|
|
4377
|
+
const result = await memoryService.retrieveMemories(query, { topK });
|
|
4378
|
+
return c.json({
|
|
4379
|
+
results: result.memories.map((m) => ({
|
|
4380
|
+
id: m.event.id,
|
|
4381
|
+
eventType: m.event.eventType,
|
|
4382
|
+
timestamp: m.event.timestamp,
|
|
4383
|
+
score: m.score,
|
|
4384
|
+
preview: m.event.content.slice(0, 200) + (m.event.content.length > 200 ? "..." : "")
|
|
4385
|
+
})),
|
|
4386
|
+
meta: {
|
|
4387
|
+
totalMatches: result.memories.length,
|
|
4388
|
+
confidence: result.matchResult.confidence
|
|
4389
|
+
}
|
|
4390
|
+
});
|
|
4391
|
+
} catch (error) {
|
|
4392
|
+
return c.json({ error: error.message }, 500);
|
|
4393
|
+
}
|
|
4394
|
+
});
|
|
4395
|
+
|
|
4396
|
+
// src/server/api/stats.ts
|
|
4397
|
+
import { Hono as Hono4 } from "hono";
|
|
4398
|
+
var statsRouter = new Hono4();
|
|
4399
|
+
statsRouter.get("/shared", async (c) => {
|
|
4400
|
+
try {
|
|
4401
|
+
const memoryService = getDefaultMemoryService();
|
|
4402
|
+
await memoryService.initialize();
|
|
4403
|
+
const sharedStats = await memoryService.getSharedStoreStats();
|
|
4404
|
+
return c.json({
|
|
4405
|
+
troubleshooting: sharedStats?.troubleshooting || 0,
|
|
4406
|
+
bestPractices: sharedStats?.bestPractices || 0,
|
|
4407
|
+
commonErrors: sharedStats?.commonErrors || 0,
|
|
4408
|
+
totalUsageCount: sharedStats?.totalUsageCount || 0,
|
|
4409
|
+
lastUpdated: sharedStats?.lastUpdated || null
|
|
4410
|
+
});
|
|
4411
|
+
} catch (error) {
|
|
4412
|
+
return c.json({
|
|
4413
|
+
troubleshooting: 0,
|
|
4414
|
+
bestPractices: 0,
|
|
4415
|
+
commonErrors: 0,
|
|
4416
|
+
totalUsageCount: 0,
|
|
4417
|
+
lastUpdated: null
|
|
4418
|
+
});
|
|
4419
|
+
}
|
|
4420
|
+
});
|
|
4421
|
+
statsRouter.get("/endless", async (c) => {
|
|
4422
|
+
try {
|
|
4423
|
+
const projectPath = c.req.query("project") || process.cwd();
|
|
4424
|
+
const memoryService = getMemoryServiceForProject(projectPath);
|
|
4425
|
+
await memoryService.initialize();
|
|
4426
|
+
const status = await memoryService.getEndlessModeStatus();
|
|
4427
|
+
return c.json({
|
|
4428
|
+
mode: status.mode,
|
|
4429
|
+
continuityScore: status.continuityScore,
|
|
4430
|
+
workingSetSize: status.workingSetSize,
|
|
4431
|
+
consolidatedCount: status.consolidatedCount,
|
|
4432
|
+
lastConsolidation: status.lastConsolidation?.toISOString() || null
|
|
4433
|
+
});
|
|
4434
|
+
} catch (error) {
|
|
4435
|
+
return c.json({
|
|
4436
|
+
mode: "session",
|
|
4437
|
+
continuityScore: 0,
|
|
4438
|
+
workingSetSize: 0,
|
|
4439
|
+
consolidatedCount: 0,
|
|
4440
|
+
lastConsolidation: null
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4443
|
+
});
|
|
4444
|
+
statsRouter.get("/", async (c) => {
|
|
4445
|
+
try {
|
|
4446
|
+
const memoryService = getDefaultMemoryService();
|
|
4447
|
+
await memoryService.initialize();
|
|
4448
|
+
const stats = await memoryService.getStats();
|
|
4449
|
+
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4450
|
+
const eventsByType = recentEvents.reduce((acc, e) => {
|
|
4451
|
+
acc[e.eventType] = (acc[e.eventType] || 0) + 1;
|
|
4452
|
+
return acc;
|
|
4453
|
+
}, {});
|
|
4454
|
+
const uniqueSessions = new Set(recentEvents.map((e) => e.sessionId));
|
|
4455
|
+
const now = /* @__PURE__ */ new Date();
|
|
4456
|
+
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
|
|
4457
|
+
const eventsByDay = recentEvents.filter((e) => e.timestamp >= sevenDaysAgo).reduce((acc, e) => {
|
|
4458
|
+
const day = e.timestamp.toISOString().split("T")[0];
|
|
4459
|
+
acc[day] = (acc[day] || 0) + 1;
|
|
4460
|
+
return acc;
|
|
4461
|
+
}, {});
|
|
4462
|
+
return c.json({
|
|
4463
|
+
storage: {
|
|
4464
|
+
eventCount: stats.totalEvents,
|
|
4465
|
+
vectorCount: stats.vectorCount
|
|
4466
|
+
},
|
|
4467
|
+
sessions: {
|
|
4468
|
+
total: uniqueSessions.size
|
|
4469
|
+
},
|
|
4470
|
+
eventsByType,
|
|
4471
|
+
activity: {
|
|
4472
|
+
daily: eventsByDay,
|
|
4473
|
+
total7Days: recentEvents.filter((e) => e.timestamp >= sevenDaysAgo).length
|
|
4474
|
+
},
|
|
4475
|
+
memory: {
|
|
4476
|
+
heapUsed: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
|
|
4477
|
+
heapTotal: Math.round(process.memoryUsage().heapTotal / 1024 / 1024)
|
|
4478
|
+
},
|
|
4479
|
+
levelStats: stats.levelStats
|
|
4480
|
+
});
|
|
4481
|
+
} catch (error) {
|
|
4482
|
+
return c.json({ error: error.message }, 500);
|
|
4483
|
+
}
|
|
4484
|
+
});
|
|
4485
|
+
statsRouter.get("/timeline", async (c) => {
|
|
4486
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
4487
|
+
try {
|
|
4488
|
+
const memoryService = getDefaultMemoryService();
|
|
4489
|
+
await memoryService.initialize();
|
|
4490
|
+
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4491
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
|
|
4492
|
+
const filteredEvents = recentEvents.filter((e) => e.timestamp >= cutoff);
|
|
4493
|
+
const daily = filteredEvents.reduce((acc, e) => {
|
|
4494
|
+
const day = e.timestamp.toISOString().split("T")[0];
|
|
4495
|
+
if (!acc[day]) {
|
|
4496
|
+
acc[day] = { date: day, total: 0, prompts: 0, responses: 0, tools: 0 };
|
|
4497
|
+
}
|
|
4498
|
+
acc[day].total++;
|
|
4499
|
+
if (e.eventType === "user_prompt")
|
|
4500
|
+
acc[day].prompts++;
|
|
4501
|
+
if (e.eventType === "agent_response")
|
|
4502
|
+
acc[day].responses++;
|
|
4503
|
+
if (e.eventType === "tool_observation")
|
|
4504
|
+
acc[day].tools++;
|
|
4505
|
+
return acc;
|
|
4506
|
+
}, {});
|
|
4507
|
+
return c.json({
|
|
4508
|
+
days,
|
|
4509
|
+
daily: Object.values(daily).sort((a, b) => a.date.localeCompare(b.date))
|
|
4510
|
+
});
|
|
4511
|
+
} catch (error) {
|
|
4512
|
+
return c.json({ error: error.message }, 500);
|
|
4513
|
+
}
|
|
4514
|
+
});
|
|
4515
|
+
|
|
4516
|
+
// src/server/api/citations.ts
|
|
4517
|
+
import { Hono as Hono5 } from "hono";
|
|
4518
|
+
|
|
4519
|
+
// src/core/citation-generator.ts
|
|
4520
|
+
import { createHash as createHash3 } from "crypto";
|
|
4521
|
+
var ID_LENGTH = 6;
|
|
4522
|
+
var CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
4523
|
+
function generateCitationId(eventId) {
|
|
4524
|
+
const hash = createHash3("sha256").update(eventId).digest();
|
|
4525
|
+
let id = "";
|
|
4526
|
+
for (let i = 0; i < ID_LENGTH; i++) {
|
|
4527
|
+
id += CHARSET[hash[i] % CHARSET.length];
|
|
4528
|
+
}
|
|
4529
|
+
return id;
|
|
4530
|
+
}
|
|
4531
|
+
function parseCitationId(formatted) {
|
|
4532
|
+
const match = formatted.match(/\[?mem:([A-Za-z0-9]{6})\]?/);
|
|
4533
|
+
return match ? match[1] : null;
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
// src/server/api/citations.ts
|
|
4537
|
+
var citationsRouter = new Hono5();
|
|
4538
|
+
citationsRouter.get("/:id", async (c) => {
|
|
4539
|
+
const { id } = c.req.param();
|
|
4540
|
+
const citationId = parseCitationId(id) || id;
|
|
4541
|
+
try {
|
|
4542
|
+
const memoryService = getDefaultMemoryService();
|
|
4543
|
+
await memoryService.initialize();
|
|
4544
|
+
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4545
|
+
const event = recentEvents.find((e) => {
|
|
4546
|
+
const eventCitationId = generateCitationId(e.id);
|
|
4547
|
+
return eventCitationId === citationId;
|
|
4548
|
+
});
|
|
4549
|
+
if (!event) {
|
|
4550
|
+
return c.json({ error: "Citation not found" }, 404);
|
|
4551
|
+
}
|
|
4552
|
+
return c.json({
|
|
4553
|
+
citation: {
|
|
4554
|
+
id: citationId,
|
|
4555
|
+
eventId: event.id
|
|
4556
|
+
},
|
|
4557
|
+
event: {
|
|
4558
|
+
id: event.id,
|
|
4559
|
+
eventType: event.eventType,
|
|
4560
|
+
timestamp: event.timestamp,
|
|
4561
|
+
sessionId: event.sessionId,
|
|
4562
|
+
content: event.content,
|
|
4563
|
+
metadata: event.metadata
|
|
4564
|
+
}
|
|
4565
|
+
});
|
|
4566
|
+
} catch (error) {
|
|
4567
|
+
return c.json({ error: error.message }, 500);
|
|
4568
|
+
}
|
|
4569
|
+
});
|
|
4570
|
+
citationsRouter.get("/:id/related", async (c) => {
|
|
4571
|
+
const { id } = c.req.param();
|
|
4572
|
+
const citationId = parseCitationId(id) || id;
|
|
4573
|
+
try {
|
|
4574
|
+
const memoryService = getDefaultMemoryService();
|
|
4575
|
+
await memoryService.initialize();
|
|
4576
|
+
const recentEvents = await memoryService.getRecentEvents(1e4);
|
|
4577
|
+
const event = recentEvents.find((e) => {
|
|
4578
|
+
const eventCitationId = generateCitationId(e.id);
|
|
4579
|
+
return eventCitationId === citationId;
|
|
4580
|
+
});
|
|
4581
|
+
if (!event) {
|
|
4582
|
+
return c.json({ error: "Citation not found" }, 404);
|
|
4583
|
+
}
|
|
4584
|
+
const sessionEvents = recentEvents.filter((e) => e.sessionId === event.sessionId).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
4585
|
+
const eventIndex = sessionEvents.findIndex((e) => e.id === event.id);
|
|
4586
|
+
const prev = eventIndex > 0 ? sessionEvents[eventIndex - 1] : null;
|
|
4587
|
+
const next = eventIndex < sessionEvents.length - 1 ? sessionEvents[eventIndex + 1] : null;
|
|
4588
|
+
return c.json({
|
|
4589
|
+
previous: prev ? {
|
|
4590
|
+
citationId: generateCitationId(prev.id),
|
|
4591
|
+
eventType: prev.eventType,
|
|
4592
|
+
timestamp: prev.timestamp,
|
|
4593
|
+
preview: prev.content.slice(0, 100) + (prev.content.length > 100 ? "..." : "")
|
|
4594
|
+
} : null,
|
|
4595
|
+
next: next ? {
|
|
4596
|
+
citationId: generateCitationId(next.id),
|
|
4597
|
+
eventType: next.eventType,
|
|
4598
|
+
timestamp: next.timestamp,
|
|
4599
|
+
preview: next.content.slice(0, 100) + (next.content.length > 100 ? "..." : "")
|
|
4600
|
+
} : null
|
|
4601
|
+
});
|
|
4602
|
+
} catch (error) {
|
|
4603
|
+
return c.json({ error: error.message }, 500);
|
|
4604
|
+
}
|
|
4605
|
+
});
|
|
4606
|
+
|
|
4607
|
+
// src/server/api/index.ts
|
|
4608
|
+
var apiRouter = new Hono6().route("/sessions", sessionsRouter).route("/events", eventsRouter).route("/search", searchRouter).route("/stats", statsRouter).route("/citations", citationsRouter);
|
|
4609
|
+
|
|
4610
|
+
// src/server/index.ts
|
|
4611
|
+
var app = new Hono7();
|
|
4612
|
+
app.use("/*", cors());
|
|
4613
|
+
app.use("/*", logger());
|
|
4614
|
+
app.route("/api", apiRouter);
|
|
4615
|
+
app.get("/health", (c) => c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
4616
|
+
var uiPath = path3.join(import.meta.dir, "../../dist/ui");
|
|
4617
|
+
if (fs3.existsSync(uiPath)) {
|
|
4618
|
+
app.use("/*", serveStatic({ root: uiPath }));
|
|
4619
|
+
}
|
|
4620
|
+
app.get("*", (c) => {
|
|
4621
|
+
const indexPath = path3.join(uiPath, "index.html");
|
|
4622
|
+
if (fs3.existsSync(indexPath)) {
|
|
4623
|
+
return c.html(fs3.readFileSync(indexPath, "utf-8"));
|
|
4624
|
+
}
|
|
4625
|
+
return c.text('UI not built. Run "npm run build:ui" first.', 404);
|
|
4626
|
+
});
|
|
4627
|
+
var serverInstance = null;
|
|
4628
|
+
function startServer(port = 37777) {
|
|
4629
|
+
if (serverInstance) {
|
|
4630
|
+
return serverInstance;
|
|
4631
|
+
}
|
|
4632
|
+
serverInstance = Bun.serve({
|
|
4633
|
+
hostname: "127.0.0.1",
|
|
4634
|
+
port,
|
|
4635
|
+
fetch: app.fetch
|
|
4636
|
+
});
|
|
4637
|
+
console.log(`\u{1F9E0} Code Memory viewer started at http://localhost:${port}`);
|
|
4638
|
+
return serverInstance;
|
|
4639
|
+
}
|
|
4640
|
+
function stopServer() {
|
|
4641
|
+
if (serverInstance) {
|
|
4642
|
+
serverInstance.stop();
|
|
4643
|
+
serverInstance = null;
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
async function isServerRunning(port = 37777) {
|
|
4647
|
+
try {
|
|
4648
|
+
const response = await fetch(`http://127.0.0.1:${port}/health`);
|
|
4649
|
+
return response.ok;
|
|
4650
|
+
} catch {
|
|
4651
|
+
return false;
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (import.meta.main) {
|
|
4655
|
+
const port = parseInt(process.env.PORT || "37777", 10);
|
|
4656
|
+
startServer(port);
|
|
4657
|
+
}
|
|
4658
|
+
|
|
4161
4659
|
// src/cli/index.ts
|
|
4162
4660
|
var program = new Command();
|
|
4163
|
-
program.name("
|
|
4661
|
+
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.0");
|
|
4164
4662
|
program.command("search <query>").description("Search memories using semantic search").option("-k, --top-k <number>", "Number of results", "5").option("-s, --min-score <number>", "Minimum similarity score", "0.7").option("--session <id>", "Filter by session ID").option("-p, --project <path>", "Project path (defaults to cwd)").action(async (query, options) => {
|
|
4165
4663
|
const projectPath = options.project || process.cwd();
|
|
4166
4664
|
const service = getMemoryServiceForProject(projectPath);
|
|
@@ -4384,7 +4882,7 @@ program.command("list").description("List available Claude Code sessions").optio
|
|
|
4384
4882
|
if (sessions.length > 20) {
|
|
4385
4883
|
console.log(`... and ${sessions.length - 20} more sessions`);
|
|
4386
4884
|
}
|
|
4387
|
-
console.log('\nUse "
|
|
4885
|
+
console.log('\nUse "claude-memory-layer import --session <path>" to import a specific session');
|
|
4388
4886
|
} catch (error) {
|
|
4389
4887
|
console.error("List failed:", error);
|
|
4390
4888
|
process.exit(1);
|
|
@@ -4404,7 +4902,7 @@ endlessCmd.command("enable").description("Enable Endless Mode").option("-p, --pr
|
|
|
4404
4902
|
console.log(" - Working Set: Recent context kept active");
|
|
4405
4903
|
console.log(" - Consolidation: Automatic memory integration");
|
|
4406
4904
|
console.log(" - Continuity: Seamless context transitions\n");
|
|
4407
|
-
console.log('Use "
|
|
4905
|
+
console.log('Use "claude-memory-layer endless status" to view current state');
|
|
4408
4906
|
await service.shutdown();
|
|
4409
4907
|
} catch (error) {
|
|
4410
4908
|
console.error("Enable failed:", error);
|
|
@@ -4452,7 +4950,7 @@ ${modeIcon} ${modeName}
|
|
|
4452
4950
|
}
|
|
4453
4951
|
} else {
|
|
4454
4952
|
console.log("Endless Mode is disabled.");
|
|
4455
|
-
console.log('Use "
|
|
4953
|
+
console.log('Use "claude-memory-layer endless enable" to activate.');
|
|
4456
4954
|
}
|
|
4457
4955
|
await service.shutdown();
|
|
4458
4956
|
} catch (error) {
|
|
@@ -4467,7 +4965,7 @@ endlessCmd.command("consolidate").description("Manually trigger memory consolida
|
|
|
4467
4965
|
await service.initialize();
|
|
4468
4966
|
if (!service.isEndlessModeActive()) {
|
|
4469
4967
|
console.log("\n\u26A0\uFE0F Endless Mode is not active");
|
|
4470
|
-
console.log('Use "
|
|
4968
|
+
console.log('Use "claude-memory-layer endless enable" first');
|
|
4471
4969
|
process.exit(1);
|
|
4472
4970
|
}
|
|
4473
4971
|
console.log("\n\u23F3 Running memory consolidation...");
|
|
@@ -4492,7 +4990,7 @@ endlessCmd.command("working-set").alias("ws").description("View current working
|
|
|
4492
4990
|
await service.initialize();
|
|
4493
4991
|
if (!service.isEndlessModeActive()) {
|
|
4494
4992
|
console.log("\n\u26A0\uFE0F Endless Mode is not active");
|
|
4495
|
-
console.log('Use "
|
|
4993
|
+
console.log('Use "claude-memory-layer endless enable" first');
|
|
4496
4994
|
process.exit(1);
|
|
4497
4995
|
}
|
|
4498
4996
|
const workingSet = await service.getWorkingSet();
|
|
@@ -4568,5 +5066,61 @@ endlessCmd.command("memories").description("View consolidated memories").option(
|
|
|
4568
5066
|
process.exit(1);
|
|
4569
5067
|
}
|
|
4570
5068
|
});
|
|
5069
|
+
program.command("dashboard").description("Open memory dashboard in browser").option("-p, --port <port>", "Server port", "37777").option("--no-open", "Do not auto-open browser").action(async (options) => {
|
|
5070
|
+
const port = parseInt(options.port, 10);
|
|
5071
|
+
try {
|
|
5072
|
+
const running = await isServerRunning(port);
|
|
5073
|
+
if (running) {
|
|
5074
|
+
console.log(`
|
|
5075
|
+
\u{1F9E0} Dashboard already running at http://localhost:${port}
|
|
5076
|
+
`);
|
|
5077
|
+
if (options.open) {
|
|
5078
|
+
openBrowser(`http://localhost:${port}`);
|
|
5079
|
+
}
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
console.log("\n\u{1F9E0} Starting Code Memory Dashboard...\n");
|
|
5083
|
+
startServer(port);
|
|
5084
|
+
if (options.open) {
|
|
5085
|
+
setTimeout(() => {
|
|
5086
|
+
openBrowser(`http://localhost:${port}`);
|
|
5087
|
+
}, 500);
|
|
5088
|
+
}
|
|
5089
|
+
console.log(`
|
|
5090
|
+
\u{1F4CA} Dashboard: http://localhost:${port}`);
|
|
5091
|
+
console.log("Press Ctrl+C to stop the server\n");
|
|
5092
|
+
const shutdown = () => {
|
|
5093
|
+
console.log("\n\n\u{1F44B} Shutting down dashboard...");
|
|
5094
|
+
stopServer();
|
|
5095
|
+
process.exit(0);
|
|
5096
|
+
};
|
|
5097
|
+
process.on("SIGINT", shutdown);
|
|
5098
|
+
process.on("SIGTERM", shutdown);
|
|
5099
|
+
await new Promise(() => {
|
|
5100
|
+
});
|
|
5101
|
+
} catch (error) {
|
|
5102
|
+
console.error("Dashboard failed:", error);
|
|
5103
|
+
process.exit(1);
|
|
5104
|
+
}
|
|
5105
|
+
});
|
|
5106
|
+
function openBrowser(url) {
|
|
5107
|
+
const platform = process.platform;
|
|
5108
|
+
let command;
|
|
5109
|
+
if (platform === "darwin") {
|
|
5110
|
+
command = `open "${url}"`;
|
|
5111
|
+
} else if (platform === "win32") {
|
|
5112
|
+
command = `start "" "${url}"`;
|
|
5113
|
+
} else {
|
|
5114
|
+
command = `xdg-open "${url}"`;
|
|
5115
|
+
}
|
|
5116
|
+
exec(command, (error) => {
|
|
5117
|
+
if (error) {
|
|
5118
|
+
console.log(`
|
|
5119
|
+
\u26A0\uFE0F Could not open browser automatically.`);
|
|
5120
|
+
console.log(` Please open ${url} manually.
|
|
5121
|
+
`);
|
|
5122
|
+
}
|
|
5123
|
+
});
|
|
5124
|
+
}
|
|
4571
5125
|
program.parse();
|
|
4572
5126
|
//# sourceMappingURL=index.js.map
|