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/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(path3) {
74
- return new duckdb.Database(path3);
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("code-memory").description("Claude Code Memory Plugin CLI").version("1.0.0");
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 "code-memory import --session <path>" to import a specific session');
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 "code-memory endless status" to view current state');
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 "code-memory endless enable" to activate.');
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 "code-memory endless enable" first');
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 "code-memory endless enable" first');
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