@stackmemoryai/stackmemory 1.2.2 → 1.2.4

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 (63) hide show
  1. package/dist/src/cli/codex-sm.js +6 -8
  2. package/dist/src/cli/commands/config.js +0 -81
  3. package/dist/src/cli/commands/context-rehydrate.js +133 -47
  4. package/dist/src/cli/commands/db.js +35 -8
  5. package/dist/src/cli/commands/handoff.js +1 -1
  6. package/dist/src/cli/commands/linear.js +9 -0
  7. package/dist/src/cli/commands/ralph.js +2 -2
  8. package/dist/src/cli/commands/setup.js +2 -2
  9. package/dist/src/cli/commands/signup.js +3 -1
  10. package/dist/src/cli/commands/storage-tier.js +26 -8
  11. package/dist/src/cli/index.js +1 -57
  12. package/dist/src/core/config/feature-flags.js +0 -4
  13. package/dist/src/core/context/dual-stack-manager.js +10 -3
  14. package/dist/src/core/context/frame-database.js +32 -0
  15. package/dist/src/core/context/frame-handoff-manager.js +2 -2
  16. package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
  17. package/dist/src/core/context/index.js +2 -2
  18. package/dist/src/core/database/sqlite-adapter.js +161 -1
  19. package/dist/src/core/digest/frame-digest-integration.js +1 -1
  20. package/dist/src/core/digest/index.js +1 -1
  21. package/dist/src/core/execution/parallel-executor.js +5 -1
  22. package/dist/src/core/projects/project-isolation.js +18 -4
  23. package/dist/src/core/security/index.js +2 -0
  24. package/dist/src/core/security/input-sanitizer.js +23 -0
  25. package/dist/src/core/utils/update-checker.js +10 -6
  26. package/dist/src/daemon/daemon-config.js +2 -1
  27. package/dist/src/daemon/services/auto-save-service.js +121 -0
  28. package/dist/src/daemon/services/maintenance-service.js +76 -1
  29. package/dist/src/features/sweep/prompt-builder.js +2 -2
  30. package/dist/src/integrations/linear/config.js +3 -1
  31. package/dist/src/integrations/linear/sync.js +18 -5
  32. package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
  33. package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
  34. package/dist/src/integrations/mcp/handlers/index.js +55 -1
  35. package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
  36. package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
  37. package/dist/src/integrations/mcp/handlers/trace-handlers.js +25 -9
  38. package/dist/src/integrations/mcp/index.js +2 -2
  39. package/dist/src/integrations/mcp/refactored-server.js +31 -10
  40. package/dist/src/integrations/mcp/tool-definitions.js +215 -1
  41. package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
  42. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
  43. package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
  44. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
  45. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
  46. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
  47. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
  48. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
  49. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
  50. package/dist/src/skills/claude-skills.js +0 -102
  51. package/dist/src/utils/hook-installer.js +8 -0
  52. package/package.json +5 -5
  53. package/scripts/install-claude-hooks-auto.js +8 -0
  54. package/templates/claude-hooks/cord-trace.js +225 -0
  55. package/dist/src/core/config/storage-config.js +0 -114
  56. package/dist/src/core/storage/chromadb-adapter.js +0 -379
  57. package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
  58. package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
  59. package/dist/src/skills/repo-ingestion-skill.js +0 -631
  60. package/templates/claude-hooks/chromadb-wrapper +0 -21
  61. /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
  62. /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
  63. /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
@@ -339,14 +339,12 @@ class CodexSM {
339
339
  action: "session_end",
340
340
  exitCode: code
341
341
  });
342
- if (process.env["LINEAR_API_KEY"]) {
343
- try {
344
- execSync("stackmemory linear sync", {
345
- stdio: "ignore",
346
- timeout: 1e4
347
- });
348
- } catch {
349
- }
342
+ try {
343
+ execSync("stackmemory linear sync", {
344
+ stdio: "ignore",
345
+ timeout: 1e4
346
+ });
347
+ } catch {
350
348
  }
351
349
  if (this.config.tracingEnabled) {
352
350
  const summary = trace.getExecutionSummary();
@@ -13,13 +13,6 @@ import {
13
13
  DEFAULT_WEIGHTS,
14
14
  DEFAULT_TOOL_SCORES
15
15
  } from "../../core/config/types.js";
16
- import {
17
- loadStorageConfig,
18
- enableChromaDB,
19
- disableChromaDB,
20
- getStorageModeDescription
21
- } from "../../core/config/storage-config.js";
22
- import inquirer from "inquirer";
23
16
  function createConfigCommand() {
24
17
  const config = new Command("config").description(
25
18
  "Manage StackMemory configuration"
@@ -363,80 +356,6 @@ Profile: ${profile}`));
363
356
  console.log(" \u2022 Profile usage frequency");
364
357
  console.log(" \u2022 Score trends over time");
365
358
  });
366
- const storageCmd = config.command("storage").description(
367
- `Manage storage configuration
368
-
369
- Storage Modes:
370
- sqlite (default): Local storage only, fast, no external dependencies
371
- hybrid: SQLite + ChromaDB for semantic search and cloud backup`
372
- );
373
- storageCmd.command("show").description("Show current storage configuration").action(async () => {
374
- const storageConfig = loadStorageConfig();
375
- console.log(chalk.blue("\nStorage Configuration:"));
376
- console.log(` Mode: ${chalk.cyan(storageConfig.mode)}`);
377
- console.log(` Description: ${chalk.gray(getStorageModeDescription())}`);
378
- if (storageConfig.chromadb.enabled) {
379
- console.log(chalk.blue("\nChromaDB Settings:"));
380
- console.log(` Enabled: ${chalk.green("Yes")}`);
381
- console.log(
382
- ` API URL: ${chalk.gray(storageConfig.chromadb.apiUrl || "https://api.trychroma.com")}`
383
- );
384
- console.log(
385
- ` Tenant: ${chalk.gray(storageConfig.chromadb.tenant || "default_tenant")}`
386
- );
387
- console.log(
388
- ` Database: ${chalk.gray(storageConfig.chromadb.database || "default_database")}`
389
- );
390
- console.log(
391
- ` API Key: ${chalk.gray(storageConfig.chromadb.apiKey ? "[configured]" : "[not set]")}`
392
- );
393
- } else {
394
- console.log(chalk.blue("\nChromaDB Settings:"));
395
- console.log(` Enabled: ${chalk.yellow("No")}`);
396
- console.log(
397
- chalk.gray(
398
- " Enable with: stackmemory config storage enable-chromadb"
399
- )
400
- );
401
- }
402
- });
403
- storageCmd.command("enable-chromadb").description("Enable ChromaDB for semantic search and cloud backup").option("--api-key <key>", "ChromaDB API key").option("--api-url <url>", "ChromaDB API URL", "https://api.trychroma.com").action(async (options) => {
404
- let apiKey = options.apiKey;
405
- if (!apiKey && process.stdin.isTTY) {
406
- const answers = await inquirer.prompt([
407
- {
408
- type: "password",
409
- name: "apiKey",
410
- message: "Enter your ChromaDB API key:",
411
- validate: (input) => {
412
- if (!input || input.trim().length === 0) {
413
- return "API key is required for ChromaDB";
414
- }
415
- return true;
416
- }
417
- }
418
- ]);
419
- apiKey = answers.apiKey;
420
- }
421
- if (!apiKey) {
422
- console.log(chalk.red("[ERROR] ChromaDB API key is required."));
423
- console.log(
424
- chalk.gray("Provide via --api-key flag or run interactively.")
425
- );
426
- process.exit(1);
427
- }
428
- enableChromaDB({
429
- apiKey,
430
- apiUrl: options.apiUrl
431
- });
432
- console.log(chalk.green("[OK] ChromaDB enabled successfully."));
433
- console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));
434
- });
435
- storageCmd.command("disable-chromadb").description("Disable ChromaDB and use SQLite-only storage").action(async () => {
436
- disableChromaDB();
437
- console.log(chalk.green("[OK] ChromaDB disabled."));
438
- console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));
439
- });
440
359
  return config;
441
360
  }
442
361
  export {
@@ -17,12 +17,16 @@ async function handleContextRehydrate(options) {
17
17
  const projectRoot = process.cwd();
18
18
  const dbPath = join(projectRoot, ".stackmemory", "context.db");
19
19
  if (!existsSync(dbPath)) {
20
- console.log('\u274C StackMemory not initialized. Run "stackmemory init" first.');
20
+ console.log(
21
+ '\u274C StackMemory not initialized. Run "stackmemory init" first.'
22
+ );
21
23
  return;
22
24
  }
23
25
  try {
24
26
  console.log("\u{1F504} Enhanced Context Rehydration System");
25
- console.log("\u{1F4DA} This system preserves rich context across Claude compactions\n");
27
+ console.log(
28
+ "\u{1F4DA} This system preserves rich context across Claude compactions\n"
29
+ );
26
30
  if (options.list) {
27
31
  await listCheckpoints();
28
32
  return;
@@ -66,7 +70,10 @@ async function handleContextRehydrate(options) {
66
70
  }
67
71
  } catch (error) {
68
72
  logger.error("Context rehydration error:", error);
69
- console.error("\u274C Failed to rehydrate context:", error instanceof Error ? error.message : error);
73
+ console.error(
74
+ "\u274C Failed to rehydrate context:",
75
+ error instanceof Error ? error.message : error
76
+ );
70
77
  process.exit(1);
71
78
  }
72
79
  }
@@ -101,7 +108,10 @@ async function createRehydrationCheckpoint(withTraces = false) {
101
108
  const checkpointPath = join(checkpointDir, `${checkpointId}.json`);
102
109
  await fs.writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2));
103
110
  checkpoint.verification.files_captured = checkpoint.recent_files.length;
104
- checkpoint.verification.total_size = checkpoint.recent_files.reduce((sum, file) => sum + file.size, 0);
111
+ checkpoint.verification.total_size = checkpoint.recent_files.reduce(
112
+ (sum, file) => sum + file.size,
113
+ 0
114
+ );
105
115
  checkpoint.verification.integrity_hash = await calculateCheckpointHash(checkpoint);
106
116
  await fs.writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2));
107
117
  console.log(`\u2705 Created checkpoint: ${checkpointId}`);
@@ -109,7 +119,9 @@ async function createRehydrationCheckpoint(withTraces = false) {
109
119
  console.log(`\u{1F4CA} Captured ${checkpoint.recent_files.length} recent files`);
110
120
  if (withTraces) {
111
121
  console.log(`\u{1F41B} Captured ${checkpoint.stack_traces.length} stack traces`);
112
- console.log(`\u{1F50D} Detected ${checkpoint.error_patterns.length} error patterns`);
122
+ console.log(
123
+ `\u{1F50D} Detected ${checkpoint.error_patterns.length} error patterns`
124
+ );
113
125
  }
114
126
  } catch (error) {
115
127
  console.error("\u274C Failed to create checkpoint:", error);
@@ -134,7 +146,10 @@ async function performRehydration(checkpointId) {
134
146
  }
135
147
  checkpointFiles.sort((a, b) => b.localeCompare(a));
136
148
  const latestFile = checkpointFiles[0];
137
- const content = await fs.readFile(join(checkpointDir, latestFile), "utf8");
149
+ const content = await fs.readFile(
150
+ join(checkpointDir, latestFile),
151
+ "utf8"
152
+ );
138
153
  checkpoint = JSON.parse(content);
139
154
  }
140
155
  console.log(`\u{1F504} Rehydrating from checkpoint: ${checkpoint.id}`);
@@ -143,11 +158,15 @@ async function performRehydration(checkpointId) {
143
158
  console.log(`\u{1F4CB} Recent files: ${checkpoint.recent_files.length}`);
144
159
  console.log("\n\u{1F4CA} Context Summary:");
145
160
  checkpoint.recent_files.slice(0, 5).forEach((file, i) => {
146
- console.log(` ${i + 1}. ${file.path} (${file.size} bytes, modified ${new Date(file.mtime).toLocaleString()})`);
161
+ console.log(
162
+ ` ${i + 1}. ${file.path} (${file.size} bytes, modified ${new Date(file.mtime).toLocaleString()})`
163
+ );
147
164
  });
148
165
  if (checkpoint.project_context.key_files.length > 0) {
149
- console.log(`
150
- \u{1F511} Key project files: ${checkpoint.project_context.key_files.join(", ")}`);
166
+ console.log(
167
+ `
168
+ \u{1F511} Key project files: ${checkpoint.project_context.key_files.join(", ")}`
169
+ );
151
170
  }
152
171
  return true;
153
172
  } catch (error) {
@@ -187,7 +206,12 @@ async function analyzeProjectContext() {
187
206
  framework: "unknown"
188
207
  };
189
208
  try {
190
- const projectFiles = ["package.json", "tsconfig.json", "README.md", "docker-compose.yml"];
209
+ const projectFiles = [
210
+ "package.json",
211
+ "tsconfig.json",
212
+ "README.md",
213
+ "docker-compose.yml"
214
+ ];
191
215
  for (const file of projectFiles) {
192
216
  try {
193
217
  await fs.access(file);
@@ -224,11 +248,15 @@ async function listCheckpoints() {
224
248
  const checkpoints = files.filter((f) => f.endsWith(".json"));
225
249
  if (checkpoints.length === 0) {
226
250
  console.log("\u{1F4ED} No rehydration checkpoints found");
227
- console.log("\u{1F4A1} Create one with: stackmemory context rehydrate --create");
251
+ console.log(
252
+ "\u{1F4A1} Create one with: stackmemory context rehydrate --create"
253
+ );
228
254
  return;
229
255
  }
230
- console.log(`\u{1F4CB} Found ${checkpoints.length} rehydration checkpoint(s):
231
- `);
256
+ console.log(
257
+ `\u{1F4CB} Found ${checkpoints.length} rehydration checkpoint(s):
258
+ `
259
+ );
232
260
  for (const file of checkpoints) {
233
261
  const id = file.replace(".json", "");
234
262
  const stats = await fs.stat(`${checkpointDir}/${file}`);
@@ -238,12 +266,16 @@ async function listCheckpoints() {
238
266
  console.log(`\u{1F516} ${id}`);
239
267
  console.log(` Created: ${stats.birthtime.toISOString()}`);
240
268
  console.log(` Size: ${(stats.size / 1024).toFixed(1)} KB`);
241
- console.log(` Files: ${checkpoint.verification?.files_captured || checkpoint.recent_files?.length || 0}`);
269
+ console.log(
270
+ ` Files: ${checkpoint.verification?.files_captured || checkpoint.recent_files?.length || 0}`
271
+ );
242
272
  if (checkpoint.stack_traces?.length > 0) {
243
273
  console.log(` Stack traces: ${checkpoint.stack_traces.length}`);
244
274
  }
245
275
  if (checkpoint.error_patterns?.length > 0) {
246
- console.log(` Error patterns: ${checkpoint.error_patterns.length}`);
276
+ console.log(
277
+ ` Error patterns: ${checkpoint.error_patterns.length}`
278
+ );
247
279
  }
248
280
  console.log("");
249
281
  } catch {
@@ -254,10 +286,14 @@ async function listCheckpoints() {
254
286
  }
255
287
  }
256
288
  console.log("\u{1F4A1} Use: stackmemory context rehydrate -c <checkpoint-id>");
257
- console.log("\u{1F4A1} Verify: stackmemory context rehydrate --verify -c <checkpoint-id>");
289
+ console.log(
290
+ "\u{1F4A1} Verify: stackmemory context rehydrate --verify -c <checkpoint-id>"
291
+ );
258
292
  } catch (err) {
259
293
  console.log("\u{1F4ED} No rehydration checkpoints directory found");
260
- console.log("\u{1F4A1} Create first checkpoint with: stackmemory context rehydrate --create");
294
+ console.log(
295
+ "\u{1F4A1} Create first checkpoint with: stackmemory context rehydrate --create"
296
+ );
261
297
  }
262
298
  } catch (error) {
263
299
  console.error("\u274C Failed to list checkpoints:", error);
@@ -291,7 +327,12 @@ async function verifyCheckpoint(checkpointPath) {
291
327
  const checkpoint = JSON.parse(content);
292
328
  const fileName = checkpointPath.split("/").pop()?.replace(".json", "") || "unknown";
293
329
  console.log(`\u{1F50D} Verifying checkpoint: ${fileName}`);
294
- const requiredFields = ["id", "timestamp", "working_directory", "recent_files"];
330
+ const requiredFields = [
331
+ "id",
332
+ "timestamp",
333
+ "working_directory",
334
+ "recent_files"
335
+ ];
295
336
  const missingFields = requiredFields.filter((field) => !checkpoint[field]);
296
337
  if (missingFields.length > 0) {
297
338
  console.log(`\u274C Missing required fields: ${missingFields.join(", ")}`);
@@ -299,8 +340,12 @@ async function verifyCheckpoint(checkpointPath) {
299
340
  }
300
341
  console.log(`\u{1F4C1} Files captured: ${checkpoint.recent_files?.length || 0}`);
301
342
  if (checkpoint.verification) {
302
- console.log(`\u{1F4CA} Total size: ${(checkpoint.verification.total_size / 1024).toFixed(1)} KB`);
303
- console.log(`\u{1F512} Integrity hash: ${checkpoint.verification.integrity_hash.slice(0, 12)}...`);
343
+ console.log(
344
+ `\u{1F4CA} Total size: ${(checkpoint.verification.total_size / 1024).toFixed(1)} KB`
345
+ );
346
+ console.log(
347
+ `\u{1F512} Integrity hash: ${checkpoint.verification.integrity_hash.slice(0, 12)}...`
348
+ );
304
349
  const recalculatedHash = await calculateCheckpointHash(checkpoint);
305
350
  if (recalculatedHash === checkpoint.verification.integrity_hash) {
306
351
  console.log(`\u2705 Integrity check: PASSED`);
@@ -310,8 +355,12 @@ async function verifyCheckpoint(checkpointPath) {
310
355
  }
311
356
  if (checkpoint.stack_traces) {
312
357
  console.log(`\u{1F41B} Stack traces: ${checkpoint.stack_traces.length}`);
313
- const pendingTraces = checkpoint.stack_traces.filter((t) => t.resolution_status === "pending");
314
- const resolvedTraces = checkpoint.stack_traces.filter((t) => t.resolution_status === "resolved");
358
+ const pendingTraces = checkpoint.stack_traces.filter(
359
+ (t) => t.resolution_status === "pending"
360
+ );
361
+ const resolvedTraces = checkpoint.stack_traces.filter(
362
+ (t) => t.resolution_status === "resolved"
363
+ );
315
364
  if (pendingTraces.length > 0) {
316
365
  console.log(` \u23F3 Pending resolution: ${pendingTraces.length}`);
317
366
  }
@@ -320,7 +369,9 @@ async function verifyCheckpoint(checkpointPath) {
320
369
  }
321
370
  }
322
371
  if (checkpoint.error_patterns?.length > 0) {
323
- console.log(`\u{1F50D} Error patterns detected: ${checkpoint.error_patterns.join(", ")}`);
372
+ console.log(
373
+ `\u{1F50D} Error patterns detected: ${checkpoint.error_patterns.join(", ")}`
374
+ );
324
375
  }
325
376
  console.log(`\u2705 Checkpoint verification complete`);
326
377
  } catch (error) {
@@ -357,7 +408,12 @@ async function captureStackTraces() {
357
408
  "logs/error.log",
358
409
  "tmp/cache/error.log"
359
410
  ];
360
- const allLogFiles = [...stackMemoryLogs, ...nodeLogs, ...buildLogs, ...frameworkLogs];
411
+ const allLogFiles = [
412
+ ...stackMemoryLogs,
413
+ ...nodeLogs,
414
+ ...buildLogs,
415
+ ...frameworkLogs
416
+ ];
361
417
  for (const logFile of allLogFiles) {
362
418
  await extractTracesFromLogFile(logFile, traces, fs);
363
419
  }
@@ -590,26 +646,34 @@ async function showStackTraces() {
590
646
  const projectRoot = process.cwd();
591
647
  const dbPath = join(projectRoot, ".stackmemory", "context.db");
592
648
  if (!existsSync(dbPath)) {
593
- console.log('\u274C StackMemory not initialized. Run "stackmemory init" first.');
649
+ console.log(
650
+ '\u274C StackMemory not initialized. Run "stackmemory init" first.'
651
+ );
594
652
  return;
595
653
  }
596
654
  const Database = (await import("better-sqlite3")).default;
597
655
  const db = new Database(dbPath);
598
656
  try {
599
- const tableExists = db.prepare(`
657
+ const tableExists = db.prepare(
658
+ `
600
659
  SELECT name FROM sqlite_master
601
660
  WHERE type='table' AND name='stack_traces'
602
- `).get();
661
+ `
662
+ ).get();
603
663
  if (!tableExists) {
604
664
  console.log("\u{1F4ED} No stack traces found in database");
605
- console.log("\u{1F4A1} Stack traces are stored when using enhanced rehydration features");
665
+ console.log(
666
+ "\u{1F4A1} Stack traces are stored when using enhanced rehydration features"
667
+ );
606
668
  return;
607
669
  }
608
- const traces = db.prepare(`
670
+ const traces = db.prepare(
671
+ `
609
672
  SELECT * FROM stack_traces
610
673
  ORDER BY created_at DESC
611
674
  LIMIT 20
612
- `).all();
675
+ `
676
+ ).all();
613
677
  if (traces.length === 0) {
614
678
  console.log("\u{1F4ED} No stack traces found in database");
615
679
  return;
@@ -620,9 +684,13 @@ async function showStackTraces() {
620
684
  const createdAt = new Date(trace.created_at * 1e3).toLocaleString();
621
685
  const severity = trace.error_severity || "medium";
622
686
  const severityIcon = severity === "high" ? "\u{1F534}" : severity === "low" ? "\u{1F7E1}" : "\u{1F7E0}";
623
- console.log(`${severityIcon} ${trace.error_type || "Error"} - ${severity.toUpperCase()}`);
687
+ console.log(
688
+ `${severityIcon} ${trace.error_type || "Error"} - ${severity.toUpperCase()}`
689
+ );
624
690
  console.log(` Message: ${trace.error_message}`);
625
- console.log(` File: ${trace.file_path || "unknown"}${trace.line_number ? `:${trace.line_number}` : ""}`);
691
+ console.log(
692
+ ` File: ${trace.file_path || "unknown"}${trace.line_number ? `:${trace.line_number}` : ""}`
693
+ );
626
694
  console.log(` Function: ${trace.function_name || "unknown"}`);
627
695
  console.log(` Status: ${trace.resolution_status}`);
628
696
  console.log(` Created: ${createdAt}`);
@@ -649,16 +717,20 @@ async function showStackTraceStats() {
649
717
  const projectRoot = process.cwd();
650
718
  const dbPath = join(projectRoot, ".stackmemory", "context.db");
651
719
  if (!existsSync(dbPath)) {
652
- console.log('\u274C StackMemory not initialized. Run "stackmemory init" first.');
720
+ console.log(
721
+ '\u274C StackMemory not initialized. Run "stackmemory init" first.'
722
+ );
653
723
  return;
654
724
  }
655
725
  const Database = (await import("better-sqlite3")).default;
656
726
  const db = new Database(dbPath);
657
727
  try {
658
- const tableExists = db.prepare(`
728
+ const tableExists = db.prepare(
729
+ `
659
730
  SELECT name FROM sqlite_master
660
731
  WHERE type='table' AND name='stack_traces'
661
- `).get();
732
+ `
733
+ ).get();
662
734
  if (!tableExists) {
663
735
  console.log("\u{1F4ED} No stack trace data available");
664
736
  return;
@@ -666,30 +738,37 @@ async function showStackTraceStats() {
666
738
  console.log("\u{1F4CA} Stack Trace Statistics\n");
667
739
  const totalTraces = db.prepare("SELECT COUNT(*) as count FROM stack_traces").get().count;
668
740
  console.log(`Total traces: ${totalTraces}`);
669
- const statusStats = db.prepare(`
741
+ const statusStats = db.prepare(
742
+ `
670
743
  SELECT resolution_status, COUNT(*) as count
671
744
  FROM stack_traces
672
745
  GROUP BY resolution_status
673
746
  ORDER BY count DESC
674
- `).all();
747
+ `
748
+ ).all();
675
749
  console.log("\n\u{1F4C8} By Resolution Status:");
676
750
  for (const stat of statusStats) {
677
751
  const percentage = (stat.count / totalTraces * 100).toFixed(1);
678
- console.log(` ${stat.resolution_status}: ${stat.count} (${percentage}%)`);
752
+ console.log(
753
+ ` ${stat.resolution_status}: ${stat.count} (${percentage}%)`
754
+ );
679
755
  }
680
- const typeStats = db.prepare(`
756
+ const typeStats = db.prepare(
757
+ `
681
758
  SELECT error_type, COUNT(*) as count
682
759
  FROM stack_traces
683
760
  GROUP BY error_type
684
761
  ORDER BY count DESC
685
762
  LIMIT 10
686
- `).all();
763
+ `
764
+ ).all();
687
765
  console.log("\n\u{1F50D} Top Error Types:");
688
766
  for (const stat of typeStats) {
689
767
  const percentage = (stat.count / totalTraces * 100).toFixed(1);
690
768
  console.log(` ${stat.error_type}: ${stat.count} (${percentage}%)`);
691
769
  }
692
- const severityStats = db.prepare(`
770
+ const severityStats = db.prepare(
771
+ `
693
772
  SELECT error_severity, COUNT(*) as count
694
773
  FROM stack_traces
695
774
  GROUP BY error_severity
@@ -699,28 +778,35 @@ async function showStackTraceStats() {
699
778
  WHEN 'medium' THEN 2
700
779
  WHEN 'low' THEN 3
701
780
  END
702
- `).all();
781
+ `
782
+ ).all();
703
783
  console.log("\n\u26A0\uFE0F By Severity:");
704
784
  for (const stat of severityStats) {
705
785
  const percentage = (stat.count / totalTraces * 100).toFixed(1);
706
786
  const icon = stat.error_severity === "high" ? "\u{1F534}" : stat.error_severity === "low" ? "\u{1F7E1}" : "\u{1F7E0}";
707
- console.log(` ${icon} ${stat.error_severity}: ${stat.count} (${percentage}%)`);
787
+ console.log(
788
+ ` ${icon} ${stat.error_severity}: ${stat.count} (${percentage}%)`
789
+ );
708
790
  }
709
- const recentTraces = db.prepare(`
791
+ const recentTraces = db.prepare(
792
+ `
710
793
  SELECT COUNT(*) as count
711
794
  FROM stack_traces
712
795
  WHERE created_at > (unixepoch() - 86400)
713
- `).get().count;
796
+ `
797
+ ).get().count;
714
798
  console.log(`
715
799
  \u{1F4C5} Recent Activity (24 hours): ${recentTraces} traces`);
716
- const fileStats = db.prepare(`
800
+ const fileStats = db.prepare(
801
+ `
717
802
  SELECT file_path, COUNT(*) as count
718
803
  FROM stack_traces
719
804
  WHERE file_path IS NOT NULL
720
805
  GROUP BY file_path
721
806
  ORDER BY count DESC
722
807
  LIMIT 5
723
- `).all();
808
+ `
809
+ ).all();
724
810
  if (fileStats.length > 0) {
725
811
  console.log("\n\u{1F5C2}\uFE0F Most Problematic Files:");
726
812
  for (const stat of fileStats) {
@@ -6,7 +6,13 @@ import inquirer from "inquirer";
6
6
  import chalk from "chalk";
7
7
  import { homedir } from "os";
8
8
  import { join } from "path";
9
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
9
+ import {
10
+ existsSync,
11
+ mkdirSync,
12
+ readFileSync,
13
+ writeFileSync,
14
+ unlinkSync
15
+ } from "fs";
10
16
  import { Pool } from "pg";
11
17
  function loadConfig() {
12
18
  const cfgDir = join(homedir(), ".stackmemory");
@@ -39,8 +45,14 @@ function registerLogoutCommand(program) {
39
45
  if (existsSync(envPath)) unlinkSync(envPath);
40
46
  } catch {
41
47
  }
42
- console.log(chalk.green("\u2713 Switched to local storage and cleared hosted credentials."));
43
- console.log(chalk.gray("Start the server without DATABASE_URL to use local SQLite."));
48
+ console.log(
49
+ chalk.green(
50
+ "\u2713 Switched to local storage and cleared hosted credentials."
51
+ )
52
+ );
53
+ console.log(
54
+ chalk.gray("Start the server without DATABASE_URL to use local SQLite.")
55
+ );
44
56
  });
45
57
  }
46
58
  function registerDbCommands(program) {
@@ -75,7 +87,12 @@ function registerDbCommands(program) {
75
87
  return;
76
88
  }
77
89
  const { openSignup } = await inquirer.prompt([
78
- { type: "confirm", name: "openSignup", message: "Open hosted signup/login page?", default: false }
90
+ {
91
+ type: "confirm",
92
+ name: "openSignup",
93
+ message: "Open hosted signup/login page?",
94
+ default: false
95
+ }
79
96
  ]);
80
97
  if (openSignup) {
81
98
  try {
@@ -96,7 +113,9 @@ function registerDbCommands(program) {
96
113
  const ok = await testPostgres(url);
97
114
  if (!ok) {
98
115
  console.log(chalk.red("failed"));
99
- console.log(chalk.red("\u2717 Could not connect to Postgres with provided URL."));
116
+ console.log(
117
+ chalk.red("\u2717 Could not connect to Postgres with provided URL.")
118
+ );
100
119
  return;
101
120
  }
102
121
  console.log(chalk.green("ok"));
@@ -107,7 +126,9 @@ function registerDbCommands(program) {
107
126
  DATABASE_URL=${url}
108
127
  `);
109
128
  console.log(chalk.green("\u2713 Switched to hosted database."));
110
- console.log(chalk.gray("Tip: export DATABASE_URL before starting the server."));
129
+ console.log(
130
+ chalk.gray("Tip: export DATABASE_URL before starting the server.")
131
+ );
111
132
  });
112
133
  db.command("status").description("Show current database mode and connection status").action(async () => {
113
134
  const { cfgDir, cfg } = loadConfig();
@@ -116,7 +137,11 @@ DATABASE_URL=${url}
116
137
  if (mode === "hosted") {
117
138
  const url = process.env.DATABASE_URL || cfg.database?.url || "";
118
139
  if (!url) {
119
- console.log(chalk.yellow('DATABASE_URL not set and not found in config. Run "stackmemory login".'));
140
+ console.log(
141
+ chalk.yellow(
142
+ 'DATABASE_URL not set and not found in config. Run "stackmemory login".'
143
+ )
144
+ );
120
145
  return;
121
146
  }
122
147
  const masked = maskDsn(url);
@@ -126,7 +151,9 @@ DATABASE_URL=${url}
126
151
  } else {
127
152
  const sqlitePath = join(cfgDir, "railway.db");
128
153
  const exists = existsSync(sqlitePath);
129
- console.log(`Local SQLite path: ${sqlitePath} (${exists ? "exists" : "will be created at first run"})`);
154
+ console.log(
155
+ `Local SQLite path: ${sqlitePath} (${exists ? "exists" : "will be created at first run"})`
156
+ );
130
157
  }
131
158
  });
132
159
  }
@@ -19,7 +19,7 @@ import { z } from "zod";
19
19
  import { FrameManager } from "../../core/context/index.js";
20
20
  import { LinearTaskManager } from "../../features/tasks/linear-task-manager.js";
21
21
  import { logger } from "../../core/monitoring/logger.js";
22
- import { EnhancedHandoffGenerator } from "../../core/session/enhanced-handoff.js";
22
+ import { EnhancedHandoffGenerator } from "../../core/session/handoff.js";
23
23
  const countTokens = (text) => Math.ceil(text.length / 3.5);
24
24
  const MAX_HANDOFF_VERSIONS = 10;
25
25
  function saveVersionedHandoff(projectRoot, branch, content) {
@@ -289,6 +289,15 @@ ${displayTasks.length} shown, ${tasks.length} total tasks`
289
289
  authManager,
290
290
  config
291
291
  );
292
+ if (!syncEngine.isConfigured) {
293
+ console.log(
294
+ chalk.gray(
295
+ '\u2139 Linear API key not configured \u2014 skipping sync. Set LINEAR_API_KEY or run "stackmemory linear setup".'
296
+ )
297
+ );
298
+ db.close();
299
+ return;
300
+ }
292
301
  console.log(chalk.yellow("\u{1F504} Syncing with Linear..."));
293
302
  if (options.dryRun) {
294
303
  console.log(chalk.gray("(Dry run - no changes will be made)"));
@@ -176,8 +176,8 @@ ${contextResponse.context}`;
176
176
  return trace.command("ralph-clean", options, async () => {
177
177
  try {
178
178
  if (!options.keepHistory && existsSync(".ralph/history")) {
179
- const { execSync } = await import("child_process");
180
- execSync("rm -rf .ralph/history");
179
+ const fs = await import("fs");
180
+ fs.rmSync(".ralph/history", { recursive: true, force: true });
181
181
  }
182
182
  if (existsSync(".ralph/work-complete.txt")) {
183
183
  const fs = await import("fs");
@@ -4,7 +4,7 @@ const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { Command } from "commander";
6
6
  import chalk from "chalk";
7
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync } from "fs";
8
8
  import { join } from "path";
9
9
  import { homedir } from "os";
10
10
  import { execSync } from "child_process";
@@ -366,7 +366,7 @@ function createSetupPluginsCommand() {
366
366
  if (existsSync(targetPath)) {
367
367
  if (options.force) {
368
368
  try {
369
- execSync(`rm -rf "${targetPath}"`, { encoding: "utf-8" });
369
+ rmSync(targetPath, { recursive: true, force: true });
370
370
  } catch {
371
371
  console.log(
372
372
  chalk.red(` [ERROR] ${plugin} - could not remove existing`)
@@ -40,7 +40,9 @@ function registerSignupCommand(program) {
40
40
  console.log(chalk.cyan("\n\u{1F510} Proceeding to login...\n"));
41
41
  await loginCmd.parseAsync(["node", "stackmemory", "login"]);
42
42
  } else {
43
- console.log(chalk.gray("\nWhen ready, run: ") + chalk.cyan("stackmemory login"));
43
+ console.log(
44
+ chalk.gray("\nWhen ready, run: ") + chalk.cyan("stackmemory login")
45
+ );
44
46
  }
45
47
  });
46
48
  }