@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.
- 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/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/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- 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 -1
- 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 +25 -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/tool-definitions.js +215 -1
- 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 +0 -102
- package/dist/src/utils/hook-installer.js +8 -0
- package/package.json +5 -5
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- 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/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) {
|
|
@@ -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 {
|
|
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(
|
|
43
|
-
|
|
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
|
-
{
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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/
|
|
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
|
|
180
|
-
|
|
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
|
-
|
|
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(
|
|
43
|
+
console.log(
|
|
44
|
+
chalk.gray("\nWhen ready, run: ") + chalk.cyan("stackmemory login")
|
|
45
|
+
);
|
|
44
46
|
}
|
|
45
47
|
});
|
|
46
48
|
}
|