@stackmemoryai/stackmemory 1.2.2 → 1.2.6
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/README.md +10 -6
- package/dist/src/cli/claude-sm.js +33 -4
- package/dist/src/cli/codex-sm-danger.js +4 -1
- package/dist/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +108 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/daemon.js +0 -8
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +0 -16
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -9
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +0 -130
- package/dist/src/integrations/mcp/tool-definitions.js +196 -67
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +37 -103
- package/dist/src/skills/theory-skill.js +191 -0
- package/dist/src/utils/hook-installer.js +16 -0
- package/package.json +6 -6
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/templates/claude-hooks/theory-capture.js +100 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/hooks/graphiti-hooks.js +0 -253
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/graphiti/client.js +0 -115
- package/dist/src/integrations/graphiti/config.js +0 -17
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
- package/dist/src/integrations/graphiti/types.js +0 -4
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
package/README.md
CHANGED
|
@@ -10,14 +10,17 @@ Lossless, project-scoped memory for AI coding tools.
|
|
|
10
10
|
StackMemory is a **production-ready memory runtime** for AI coding tools that preserves full project context across sessions:
|
|
11
11
|
|
|
12
12
|
- **Zero-config setup** — `stackmemory init` just works
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
13
|
+
- **32 MCP tools** for Claude Code integration (context, tasks, Linear, traces, discovery, cord, team)
|
|
14
|
+
- **FTS5 full-text search** with BM25 scoring and hybrid retrieval
|
|
15
|
+
- **Full Linear integration** with bidirectional sync and OAuth/API key support
|
|
15
16
|
- **Context persistence** that survives `/clear` operations
|
|
16
17
|
- **Hierarchical frame organization** (nested call stack model)
|
|
18
|
+
- **Multi-wrapper support** — `claude-sm`, `codex-sm`, `opencode-sm` with auto context loading
|
|
17
19
|
- **Skills system** with `/spec` and `/linear-run` for Claude Code
|
|
18
20
|
- **Automatic hooks** for task tracking, Linear sync, and spec progress
|
|
19
21
|
- **Memory monitor daemon** with automatic capture/clear on RAM pressure
|
|
20
|
-
- **
|
|
22
|
+
- **Auto-save service** for periodic context persistence
|
|
23
|
+
- **Comprehensive test coverage** across all core modules
|
|
21
24
|
|
|
22
25
|
Instead of a linear chat log, StackMemory organizes memory as a **call stack** of scoped work (frames), with intelligent LLM-driven retrieval and team collaboration features.
|
|
23
26
|
|
|
@@ -50,13 +53,14 @@ Tools forget decisions and constraints between sessions. StackMemory makes conte
|
|
|
50
53
|
|
|
51
54
|
## Features
|
|
52
55
|
|
|
53
|
-
- **MCP tools** for Claude Code:
|
|
56
|
+
- **MCP tools** for Claude Code: 36 tools across context, tasks, Linear, traces, discovery, cord, and team
|
|
57
|
+
- **FTS5 search**: full-text search with BM25 scoring, hybrid retrieval, and smart thresholds
|
|
54
58
|
- **Skills**: `/spec` (iterative spec generation), `/linear-run` (task execution via RLM)
|
|
55
|
-
- **Hooks**: automatic context save, task tracking, Linear sync, PROMPT_PLAN updates
|
|
59
|
+
- **Hooks**: automatic context save, task tracking, Linear sync, PROMPT_PLAN updates, cord tracing
|
|
56
60
|
- **Prompt Forge**: watches CLAUDE.md and AGENTS.md for prompt optimization (GEPA)
|
|
57
61
|
- **Safe branches**: worktree isolation with `--worktree` or `-w`
|
|
58
62
|
- **Persistent context**: frames, anchors, decisions, retrieval
|
|
59
|
-
- **Integrations**: Linear, DiffMem, Browser MCP
|
|
63
|
+
- **Integrations**: Linear (API key + OAuth), DiffMem, Browser MCP, log-mcp (log analysis)
|
|
60
64
|
|
|
61
65
|
---
|
|
62
66
|
|
|
@@ -322,6 +322,28 @@ class ClaudeSM {
|
|
|
322
322
|
} catch {
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
getTheoryContent() {
|
|
326
|
+
try {
|
|
327
|
+
let root;
|
|
328
|
+
try {
|
|
329
|
+
root = execSync("git rev-parse --show-toplevel", {
|
|
330
|
+
encoding: "utf-8",
|
|
331
|
+
timeout: 5e3
|
|
332
|
+
}).trim();
|
|
333
|
+
} catch {
|
|
334
|
+
root = process.cwd();
|
|
335
|
+
}
|
|
336
|
+
const theoryPath = path.join(root, "THEORY.MD");
|
|
337
|
+
if (fs.existsSync(theoryPath)) {
|
|
338
|
+
const content = fs.readFileSync(theoryPath, "utf8").trim();
|
|
339
|
+
if (content.length > 0) {
|
|
340
|
+
return content.length > 4e3 ? content.substring(0, 4e3) + "\n\n[...truncated]" : content;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
325
347
|
getHandoffContent() {
|
|
326
348
|
if (!this.config.contextEnabled) return null;
|
|
327
349
|
try {
|
|
@@ -684,13 +706,20 @@ Session ended (exit ${exitCode ?? 0})`));
|
|
|
684
706
|
this.startGEPAWatcher();
|
|
685
707
|
}
|
|
686
708
|
let initialInput = "";
|
|
687
|
-
const
|
|
688
|
-
if (
|
|
689
|
-
const
|
|
690
|
-
if (
|
|
709
|
+
const hasResume = claudeArgs.includes("--continue") || claudeArgs.some((a) => a === "--resume");
|
|
710
|
+
if (!hasResume) {
|
|
711
|
+
const handoffContent = this.getHandoffContent();
|
|
712
|
+
if (handoffContent) {
|
|
691
713
|
initialInput = handoffContent;
|
|
692
714
|
console.log(chalk.gray(" Handoff context ready"));
|
|
693
715
|
}
|
|
716
|
+
const theoryContent = this.getTheoryContent();
|
|
717
|
+
if (theoryContent) {
|
|
718
|
+
initialInput += (initialInput ? "\n\n---\n\n" : "") + `## Operating Theory (THEORY.MD)
|
|
719
|
+
|
|
720
|
+
${theoryContent}`;
|
|
721
|
+
console.log(chalk.gray(" Theory context loaded"));
|
|
722
|
+
}
|
|
694
723
|
}
|
|
695
724
|
console.log();
|
|
696
725
|
if (this.config.useSweep) {
|
|
@@ -6,7 +6,10 @@ const __dirname = __pathDirname(__filename);
|
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
const codexSmPath = path.join(__dirname, "codex-sm.js");
|
|
9
|
-
const args = [
|
|
9
|
+
const args = [
|
|
10
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
11
|
+
...process.argv.slice(2)
|
|
12
|
+
];
|
|
10
13
|
const child = spawn("node", [codexSmPath, ...args], {
|
|
11
14
|
stdio: "inherit",
|
|
12
15
|
env: process.env
|
package/dist/src/cli/codex-sm.js
CHANGED
|
@@ -339,14 +339,12 @@ class CodexSM {
|
|
|
339
339
|
action: "session_end",
|
|
340
340
|
exitCode: code
|
|
341
341
|
});
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 = [
|
|
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(
|
|
251
|
+
console.log(
|
|
252
|
+
"\u{1F4A1} Create one with: stackmemory context rehydrate --create"
|
|
253
|
+
);
|
|
228
254
|
return;
|
|
229
255
|
}
|
|
230
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 = [
|
|
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(
|
|
303
|
-
|
|
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(
|
|
314
|
-
|
|
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(
|
|
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 = [
|
|
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(
|
|
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
|
-
`
|
|
661
|
+
`
|
|
662
|
+
).get();
|
|
603
663
|
if (!tableExists) {
|
|
604
664
|
console.log("\u{1F4ED} No stack traces found in database");
|
|
605
|
-
console.log(
|
|
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
|
-
`
|
|
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(
|
|
687
|
+
console.log(
|
|
688
|
+
`${severityIcon} ${trace.error_type || "Error"} - ${severity.toUpperCase()}`
|
|
689
|
+
);
|
|
624
690
|
console.log(` Message: ${trace.error_message}`);
|
|
625
|
-
console.log(
|
|
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(
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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(
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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(
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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) {
|