@stackmemoryai/stackmemory 0.5.35 → 0.5.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +13 -5
- package/dist/cli/index.js.map +2 -2
- package/dist/core/config/feature-flags.js +58 -0
- package/dist/core/config/feature-flags.js.map +7 -0
- package/dist/core/context/frame-recovery.js +302 -0
- package/dist/core/context/frame-recovery.js.map +7 -0
- package/dist/core/context/index.js +7 -1
- package/dist/core/context/index.js.map +2 -2
- package/dist/core/context/refactored-frame-manager.js +37 -1
- package/dist/core/context/refactored-frame-manager.js.map +2 -2
- package/dist/core/retrieval/llm-provider.js +24 -1
- package/dist/core/retrieval/llm-provider.js.map +2 -2
- package/dist/integrations/mcp/server.js +34 -14
- package/dist/integrations/mcp/server.js.map +3 -3
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -15,8 +15,8 @@ import { sharedContextLayer } from "../core/context/shared-context-layer.js";
|
|
|
15
15
|
import { UpdateChecker } from "../core/utils/update-checker.js";
|
|
16
16
|
import { ProgressTracker } from "../core/monitoring/progress-tracker.js";
|
|
17
17
|
import { registerProjectCommands } from "./commands/projects.js";
|
|
18
|
-
import { registerLinearCommands } from "./commands/linear.js";
|
|
19
18
|
import { createSessionCommands } from "./commands/session.js";
|
|
19
|
+
import { isFeatureEnabled, isLocalOnly } from "../core/config/feature-flags.js";
|
|
20
20
|
import { registerWorktreeCommands } from "./commands/worktree.js";
|
|
21
21
|
import { registerOnboardingCommand } from "./commands/onboard.js";
|
|
22
22
|
import { createTaskCommands } from "./commands/tasks.js";
|
|
@@ -46,7 +46,6 @@ import { createShellCommand } from "./commands/shell.js";
|
|
|
46
46
|
import { createAPICommand } from "./commands/api.js";
|
|
47
47
|
import { createCleanupProcessesCommand } from "./commands/cleanup-processes.js";
|
|
48
48
|
import { createAutoBackgroundCommand } from "./commands/auto-background.js";
|
|
49
|
-
import { createSMSNotifyCommand } from "./commands/sms-notify.js";
|
|
50
49
|
import { createSettingsCommand } from "./commands/settings.js";
|
|
51
50
|
import { createRetrievalCommands } from "./commands/retrieval.js";
|
|
52
51
|
import { createDiscoveryCommands } from "./commands/discovery.js";
|
|
@@ -62,7 +61,6 @@ import {
|
|
|
62
61
|
enableChromaDB,
|
|
63
62
|
getStorageModeDescription
|
|
64
63
|
} from "../core/config/storage-config.js";
|
|
65
|
-
import { loadSMSConfig } from "../hooks/sms-notify.js";
|
|
66
64
|
import { spawn } from "child_process";
|
|
67
65
|
import { homedir } from "os";
|
|
68
66
|
import { createRequire } from "module";
|
|
@@ -72,7 +70,9 @@ const VERSION = pkg.version;
|
|
|
72
70
|
UpdateChecker.checkForUpdates(VERSION, true).catch(() => {
|
|
73
71
|
});
|
|
74
72
|
async function startNotificationServices() {
|
|
73
|
+
if (isLocalOnly() || !isFeatureEnabled("whatsapp")) return;
|
|
75
74
|
try {
|
|
75
|
+
const { loadSMSConfig } = await import("../hooks/sms-notify.js");
|
|
76
76
|
const config = loadSMSConfig();
|
|
77
77
|
if (!config.enabled) return;
|
|
78
78
|
const WEBHOOK_PORT = 3456;
|
|
@@ -480,7 +480,10 @@ registerLogoutCommand(program);
|
|
|
480
480
|
registerDbCommands(program);
|
|
481
481
|
registerProjectCommands(program);
|
|
482
482
|
registerWorktreeCommands(program);
|
|
483
|
-
|
|
483
|
+
if (isFeatureEnabled("linear")) {
|
|
484
|
+
import("./commands/linear.js").then(({ registerLinearCommands }) => registerLinearCommands(program)).catch(() => {
|
|
485
|
+
});
|
|
486
|
+
}
|
|
484
487
|
program.addCommand(createSessionCommands());
|
|
485
488
|
program.addCommand(createTaskCommands());
|
|
486
489
|
program.addCommand(createSearchCommand());
|
|
@@ -504,8 +507,13 @@ program.addCommand(createShellCommand());
|
|
|
504
507
|
program.addCommand(createAPICommand());
|
|
505
508
|
program.addCommand(createCleanupProcessesCommand());
|
|
506
509
|
program.addCommand(createAutoBackgroundCommand());
|
|
507
|
-
program.addCommand(createSMSNotifyCommand());
|
|
508
510
|
program.addCommand(createSettingsCommand());
|
|
511
|
+
if (isFeatureEnabled("whatsapp")) {
|
|
512
|
+
import("./commands/sms-notify.js").then(
|
|
513
|
+
({ createSMSNotifyCommand }) => program.addCommand(createSMSNotifyCommand())
|
|
514
|
+
).catch(() => {
|
|
515
|
+
});
|
|
516
|
+
}
|
|
509
517
|
program.addCommand(createRetrievalCommands());
|
|
510
518
|
program.addCommand(createDiscoveryCommands());
|
|
511
519
|
program.addCommand(createModelCommand());
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/cli/index.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n/**\n * StackMemory CLI\n * Command-line interface for StackMemory operations\n */\n\n// Set environment flag for CLI usage to skip async context bridge\nprocess.env['STACKMEMORY_CLI'] = 'true';\n\n// Load environment variables\nimport 'dotenv/config';\n\n// Initialize tracing system early\nimport { initializeTracing, trace } from '../core/trace/index.js';\ninitializeTracing();\n\nimport { program } from 'commander';\nimport { logger } from '../core/monitoring/logger.js';\nimport { FrameManager } from '../core/context/index.js';\nimport { sessionManager, FrameQueryMode } from '../core/session/index.js';\nimport { sharedContextLayer } from '../core/context/shared-context-layer.js';\nimport { UpdateChecker } from '../core/utils/update-checker.js';\nimport { ProgressTracker } from '../core/monitoring/progress-tracker.js';\nimport { registerProjectCommands } from './commands/projects.js';\nimport { registerLinearCommands } from './commands/linear.js';\nimport { createSessionCommands } from './commands/session.js';\nimport { registerWorktreeCommands } from './commands/worktree.js';\nimport { registerOnboardingCommand } from './commands/onboard.js';\nimport { createTaskCommands } from './commands/tasks.js';\nimport { createSearchCommand } from './commands/search.js';\nimport { createLogCommand } from './commands/log.js';\nimport { createContextCommands } from './commands/context.js';\nimport { createConfigCommand } from './commands/config.js';\nimport { createHandoffCommand } from './commands/handoff.js';\nimport {\n createDecisionCommand,\n createMemoryCommand,\n} from './commands/decision.js';\nimport { createSkillsCommand } from './commands/skills.js';\nimport { createTestCommand } from './commands/test.js';\nimport clearCommand from './commands/clear.js';\nimport createWorkflowCommand from './commands/workflow.js';\nimport monitorCommand from './commands/monitor.js';\nimport qualityCommand from './commands/quality.js';\nimport createRalphCommand from './commands/ralph.js';\nimport serviceCommand from './commands/service.js';\nimport { registerLoginCommand } from './commands/login.js';\nimport { registerSignupCommand } from './commands/signup.js';\nimport { registerLogoutCommand, registerDbCommands } from './commands/db.js';\nimport { createSweepCommand } from './commands/sweep.js';\nimport { createHooksCommand } from './commands/hooks.js';\nimport { createShellCommand } from './commands/shell.js';\nimport { createAPICommand } from './commands/api.js';\nimport { createCleanupProcessesCommand } from './commands/cleanup-processes.js';\nimport { createAutoBackgroundCommand } from './commands/auto-background.js';\nimport { createSMSNotifyCommand } from './commands/sms-notify.js';\nimport { createSettingsCommand } from './commands/settings.js';\nimport { createRetrievalCommands } from './commands/retrieval.js';\nimport { createDiscoveryCommands } from './commands/discovery.js';\nimport { createModelCommand } from './commands/model.js';\nimport { ProjectManager } from '../core/projects/project-manager.js';\nimport Database from 'better-sqlite3';\nimport { join } from 'path';\nimport { existsSync, mkdirSync } from 'fs';\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport {\n loadStorageConfig,\n enableChromaDB,\n getStorageModeDescription,\n} from '../core/config/storage-config.js';\nimport { loadSMSConfig } from '../hooks/sms-notify.js';\nimport { spawn } from 'child_process';\nimport { homedir } from 'os';\n\n// Read version from package.json\nimport { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../../package.json');\nconst VERSION = pkg.version;\n\n// Check for updates on CLI startup\nUpdateChecker.checkForUpdates(VERSION, true).catch(() => {\n // Silently ignore errors\n});\n\n// Auto-start webhook and ngrok if notifications are enabled\nasync function startNotificationServices(): Promise<void> {\n try {\n const config = loadSMSConfig();\n if (!config.enabled) return;\n\n const WEBHOOK_PORT = 3456;\n let webhookStarted = false;\n let ngrokStarted = false;\n\n // Check if webhook is already running\n const webhookRunning = await fetch(\n `http://localhost:${WEBHOOK_PORT}/health`\n )\n .then((r) => r.ok)\n .catch(() => false);\n\n if (!webhookRunning) {\n // Start webhook in background using the dist path\n const webhookPath = join(__dirname, '../hooks/sms-webhook.js');\n const webhookProcess = spawn('node', [webhookPath], {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, SMS_WEBHOOK_PORT: String(WEBHOOK_PORT) },\n });\n webhookProcess.unref();\n webhookStarted = true;\n }\n\n // Check if ngrok is running\n const ngrokRunning = await fetch('http://localhost:4040/api/tunnels')\n .then((r) => r.ok)\n .catch(() => false);\n\n if (!ngrokRunning) {\n // Start ngrok in background\n const ngrokProcess = spawn('ngrok', ['http', String(WEBHOOK_PORT)], {\n detached: true,\n stdio: 'ignore',\n });\n ngrokProcess.unref();\n ngrokStarted = true;\n }\n\n // Save ngrok URL after startup\n if (webhookStarted || ngrokStarted) {\n setTimeout(async () => {\n try {\n const tunnels = await fetch('http://localhost:4040/api/tunnels').then(\n (r) =>\n r.json() as Promise<{ tunnels: Array<{ public_url: string }> }>\n );\n const publicUrl = tunnels?.tunnels?.[0]?.public_url;\n if (publicUrl) {\n const configDir = join(homedir(), '.stackmemory');\n const configPath = join(configDir, 'ngrok-url.txt');\n const { writeFileSync, mkdirSync, existsSync } = await import('fs');\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n writeFileSync(configPath, publicUrl);\n console.log(\n chalk.gray(`[notify] Webhook: ${publicUrl}/sms/incoming`)\n );\n }\n } catch {\n // Ignore errors\n }\n }, 4000);\n }\n } catch {\n // Silently ignore - notifications are optional\n }\n}\n\nstartNotificationServices();\n\nprogram\n .name('stackmemory')\n .description(\n 'Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention'\n )\n .version(VERSION);\n\nprogram\n .command('init')\n .description(\n `Initialize StackMemory in current project\n\nStorage Modes:\n SQLite (default): Local only, fast, no setup required\n ChromaDB (hybrid): Adds semantic search and cloud backup, requires API key`\n )\n .option('--sqlite', 'Use SQLite-only storage (default, skip prompts)')\n .option(\n '--chromadb',\n 'Enable ChromaDB for semantic search (prompts for API key)'\n )\n .option('--skip-storage-prompt', 'Skip storage configuration prompt')\n .action(async (options) => {\n try {\n const projectRoot = process.cwd();\n const dbDir = join(projectRoot, '.stackmemory');\n\n if (!existsSync(dbDir)) {\n mkdirSync(dbDir, { recursive: true });\n }\n\n // Handle storage configuration\n let storageConfig = loadStorageConfig();\n const isFirstTimeSetup =\n !storageConfig.chromadb.enabled && storageConfig.mode === 'sqlite';\n\n // Skip prompts if --sqlite flag or --skip-storage-prompt\n if (options.sqlite || options.skipStoragePrompt) {\n // Use SQLite-only (default)\n console.log(chalk.gray('Using SQLite-only storage mode.'));\n } else if (options.chromadb) {\n // User explicitly requested ChromaDB, prompt for API key\n await promptAndEnableChromaDB();\n } else if (isFirstTimeSetup && process.stdin.isTTY) {\n // Interactive mode - ask user about ChromaDB\n console.log(chalk.cyan('\\nStorage Configuration'));\n console.log(chalk.gray('StackMemory supports two storage modes:\\n'));\n console.log(chalk.white(' SQLite (default):'));\n console.log(chalk.gray(' - Local storage only'));\n console.log(chalk.gray(' - Fast and simple'));\n console.log(chalk.gray(' - No external dependencies\\n'));\n console.log(chalk.white(' ChromaDB (hybrid):'));\n console.log(chalk.gray(' - Semantic search across your context'));\n console.log(chalk.gray(' - Cloud backup capability'));\n console.log(chalk.gray(' - Requires ChromaDB API key\\n'));\n\n const { enableChroma } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'enableChroma',\n message: 'Enable ChromaDB for semantic search? (requires API key)',\n default: false,\n },\n ]);\n\n if (enableChroma) {\n await promptAndEnableChromaDB();\n } else {\n console.log(chalk.gray('Using SQLite-only storage mode.'));\n }\n }\n\n // Initialize SQLite database\n const dbPath = join(dbDir, 'context.db');\n const db = new Database(dbPath);\n new FrameManager(db, 'cli-project');\n\n logger.info('StackMemory initialized successfully', { projectRoot });\n console.log(\n chalk.green('\\n[OK] StackMemory initialized in'),\n projectRoot\n );\n\n // Show current storage mode\n storageConfig = loadStorageConfig();\n console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));\n\n db.close();\n } catch (error: unknown) {\n logger.error('Failed to initialize StackMemory', error as Error);\n console.error(\n chalk.red('[ERROR] Initialization failed:'),\n (error as Error).message\n );\n process.exit(1);\n }\n });\n\n/**\n * Prompt user for ChromaDB configuration and enable it\n */\nasync function promptAndEnableChromaDB(): Promise<void> {\n const answers = await inquirer.prompt([\n {\n type: 'password',\n name: 'apiKey',\n message: 'Enter your ChromaDB API key:',\n validate: (input: string) => {\n if (!input || input.trim().length === 0) {\n return 'API key is required for ChromaDB';\n }\n return true;\n },\n },\n {\n type: 'input',\n name: 'apiUrl',\n message: 'ChromaDB API URL (press Enter for default):',\n default: 'https://api.trychroma.com',\n },\n ]);\n\n enableChromaDB({\n apiKey: answers.apiKey,\n apiUrl: answers.apiUrl,\n });\n\n console.log(chalk.green('[OK] ChromaDB enabled for semantic search.'));\n console.log(\n chalk.gray('API key saved to ~/.stackmemory/storage-config.json')\n );\n}\n\nprogram\n .command('status')\n .description('Show current StackMemory status')\n .option('--all', 'Show all active frames across sessions')\n .option('--project', 'Show all active frames in current project')\n .option('--session <id>', 'Show frames for specific session')\n .action(async (options) => {\n return trace.command('stackmemory-status', options, async () => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n if (!existsSync(dbPath)) {\n console.log(\n '\u274C StackMemory not initialized. Run \"stackmemory init\" first.'\n );\n return;\n }\n\n // Check for updates and display if available\n await UpdateChecker.checkForUpdates(VERSION);\n\n // Initialize session manager and shared context\n await sessionManager.initialize();\n await sharedContextLayer.initialize();\n\n const session = await sessionManager.getOrCreateSession({\n projectPath: projectRoot,\n sessionId: options.session,\n });\n\n // Auto-discover shared context on startup\n const contextDiscovery = await sharedContextLayer.autoDiscoverContext();\n\n // Show context hints if available\n if (\n contextDiscovery.hasSharedContext &&\n contextDiscovery.sessionCount > 1\n ) {\n console.log(`\\n\uD83D\uDCA1 Shared Context Available:`);\n console.log(\n ` ${contextDiscovery.sessionCount} sessions with shared context`\n );\n\n if (contextDiscovery.recentPatterns.length > 0) {\n console.log(` Recent patterns:`);\n contextDiscovery.recentPatterns.slice(0, 3).forEach((p) => {\n console.log(\n ` \u2022 ${p.type}: ${p.pattern.slice(0, 50)} (${p.frequency}x)`\n );\n });\n }\n\n if (contextDiscovery.lastDecisions.length > 0) {\n console.log(\n ` Last decision: ${contextDiscovery.lastDecisions[0].decision.slice(0, 60)}`\n );\n }\n }\n\n const db = new Database(dbPath);\n const frameManager = new FrameManager(db, session.projectId);\n\n // Set query mode based on options\n if (options.all) {\n frameManager.setQueryMode(FrameQueryMode.ALL_ACTIVE);\n } else if (options.project) {\n frameManager.setQueryMode(FrameQueryMode.PROJECT_ACTIVE);\n }\n\n const activeFrames = frameManager.getActiveFramePath();\n const stackDepth = frameManager.getStackDepth();\n\n // Always get total counts across all sessions\n const totalStats = db\n .prepare(\n `\n SELECT \n COUNT(*) as total_frames,\n SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) as active_frames,\n SUM(CASE WHEN state = 'closed' THEN 1 ELSE 0 END) as closed_frames,\n COUNT(DISTINCT run_id) as total_sessions\n FROM frames\n WHERE project_id = ?\n `\n )\n .get(session.projectId) as {\n total_frames: number;\n active_frames: number;\n closed_frames: number;\n total_sessions: number;\n };\n\n const contextCount = db\n .prepare(\n `\n SELECT COUNT(*) as count FROM contexts\n `\n )\n .get() as { count: number };\n\n const eventCount = db\n .prepare(\n `\n SELECT COUNT(*) as count FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE f.project_id = ?\n `\n )\n .get(session.projectId) as { count: number };\n\n console.log('\uD83D\uDCCA StackMemory Status:');\n console.log(\n ` Session: ${session.sessionId.slice(0, 8)} (${session.state}, ${Math.round((Date.now() - session.startedAt) / 1000 / 60)}min old)`\n );\n console.log(` Project: ${session.projectId}`);\n if (session.branch) {\n console.log(` Branch: ${session.branch}`);\n }\n\n // Show total database statistics\n console.log(`\\n Database Statistics (this project):`);\n console.log(\n ` Frames: ${totalStats.total_frames || 0} (${totalStats.active_frames || 0} active, ${totalStats.closed_frames || 0} closed)`\n );\n console.log(` Events: ${eventCount.count || 0}`);\n console.log(` Sessions: ${totalStats.total_sessions || 0}`);\n console.log(\n ` Cached contexts: ${contextCount.count || 0} (global)`\n );\n\n // Show recent activity\n const recentFrames = db\n .prepare(\n `\n SELECT name, type, state, datetime(created_at, 'unixepoch') as created\n FROM frames\n WHERE project_id = ?\n ORDER BY created_at DESC\n LIMIT 3\n `\n )\n .all(session.projectId) as Array<{\n name: string;\n type: string;\n state: string;\n created: string;\n }>;\n\n if (recentFrames.length > 0) {\n console.log(`\\n Recent Activity:`);\n recentFrames.forEach((f) => {\n const stateIcon = f.state === 'active' ? '\uD83D\uDFE2' : '\u26AB';\n console.log(\n ` ${stateIcon} ${f.name} [${f.type}] - ${f.created}`\n );\n });\n }\n\n console.log(`\\n Current Session:`);\n console.log(` Stack depth: ${stackDepth}`);\n console.log(` Active frames: ${activeFrames.length}`);\n\n if (activeFrames.length > 0) {\n activeFrames.forEach((frame, i) => {\n const indent = ' ' + ' '.repeat(frame.depth || i);\n const prefix = i === 0 ? '\u2514\u2500' : ' \u2514\u2500';\n console.log(`${indent}${prefix} ${frame.name} [${frame.type}]`);\n });\n }\n\n // Show other sessions if in default mode\n if (!options.all && !options.project) {\n const otherSessions = await sessionManager.listSessions({\n projectId: session.projectId,\n state: 'active',\n });\n\n const otherActive = otherSessions.filter(\n (s) => s.sessionId !== session.sessionId\n );\n if (otherActive.length > 0) {\n console.log(`\\n Other Active Sessions (same project):`);\n otherActive.forEach((s) => {\n const age = Math.round(\n (Date.now() - s.lastActiveAt) / 1000 / 60 / 60\n );\n console.log(\n ` - ${s.sessionId.slice(0, 8)}: ${s.branch || 'main'}, ${age}h old`\n );\n });\n console.log(`\\n Tip: Use --all to see frames across sessions`);\n }\n }\n\n db.close();\n } catch (error: unknown) {\n logger.error('Failed to get status', error as Error);\n console.error('\u274C Status check failed:', (error as Error).message);\n process.exit(1);\n }\n });\n });\n\nprogram\n .command('update-check')\n .description('Check for StackMemory updates')\n .action(async () => {\n try {\n console.log('\uD83D\uDD0D Checking for updates...');\n await UpdateChecker.forceCheck(VERSION);\n } catch (error: unknown) {\n logger.error('Update check failed', error as Error);\n console.error('\u274C Update check failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\nprogram\n .command('progress')\n .description('Show current progress and recent changes')\n .action(async () => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n if (!existsSync(dbPath)) {\n console.log(\n '\u274C StackMemory not initialized. Run \"stackmemory init\" first.'\n );\n return;\n }\n\n const progress = new ProgressTracker(projectRoot);\n console.log(progress.getSummary());\n } catch (error: unknown) {\n logger.error('Failed to show progress', error as Error);\n console.error('\u274C Failed to show progress:', (error as Error).message);\n process.exit(1);\n }\n });\n\nprogram\n .command('mcp-server')\n .description('Start StackMemory MCP server for Claude Desktop')\n .option('-p, --project <path>', 'Project root directory', process.cwd())\n .action(async (options) => {\n try {\n const { runMCPServer } = await import('../integrations/mcp/server.js');\n\n // Set project root\n process.env['PROJECT_ROOT'] = options.project;\n\n console.log('\uD83D\uDE80 Starting StackMemory MCP Server...');\n console.log(` Project: ${options.project}`);\n console.log(` Version: ${VERSION}`);\n\n // Check for updates silently\n UpdateChecker.checkForUpdates(VERSION, true).catch(() => {});\n\n // Start the MCP server\n await runMCPServer();\n } catch (error: unknown) {\n logger.error('Failed to start MCP server', error as Error);\n console.error('\u274C MCP server failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n// Add test context command\nprogram\n .command('context:test')\n .description('Test context persistence by creating sample frames')\n .action(async () => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n if (!existsSync(dbPath)) {\n console.log(\n '\u274C StackMemory not initialized. Run \"stackmemory init\" first.'\n );\n return;\n }\n\n const db = new Database(dbPath);\n const frameManager = new FrameManager(db, 'cli-project');\n\n // Create test frames\n console.log('\uD83D\uDCDD Creating test context frames...');\n\n const rootFrame = frameManager.createFrame({\n type: 'task',\n name: 'Test Session',\n inputs: { test: true, timestamp: new Date().toISOString() },\n });\n\n const taskFrame = frameManager.createFrame({\n type: 'subtask',\n name: 'Sample Task',\n inputs: { description: 'Testing context persistence' },\n parentFrameId: rootFrame,\n });\n\n const commandFrame = frameManager.createFrame({\n type: 'tool_scope',\n name: 'test-command',\n inputs: { args: ['--test'] },\n parentFrameId: taskFrame,\n });\n\n // Add some events\n frameManager.addEvent(\n 'observation',\n {\n message: 'Test event recorded',\n },\n commandFrame\n );\n\n console.log('\u2705 Test frames created!');\n console.log(`\uD83D\uDCCA Stack depth: ${frameManager.getStackDepth()}`);\n console.log(\n `\uD83D\uDD04 Active frames: ${frameManager.getActiveFramePath().length}`\n );\n\n // Close one frame to test state changes\n frameManager.closeFrame(commandFrame);\n console.log(\n `\uD83D\uDCCA After closing command frame: depth = ${frameManager.getStackDepth()}`\n );\n\n db.close();\n } catch (error: unknown) {\n logger.error('Test context failed', error as Error);\n console.error('\u274C Test failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n// Register project management commands\n// Register command modules\nregisterOnboardingCommand(program);\nregisterSignupCommand(program);\nregisterLoginCommand(program);\nregisterLogoutCommand(program);\nregisterDbCommands(program);\nregisterProjectCommands(program);\nregisterWorktreeCommands(program);\n\n// Register Linear integration commands\nregisterLinearCommands(program);\n\n// Register session management commands\nprogram.addCommand(createSessionCommands());\n\n// Register enhanced CLI commands\nprogram.addCommand(createTaskCommands());\nprogram.addCommand(createSearchCommand());\nprogram.addCommand(createLogCommand());\nprogram.addCommand(createContextCommands());\nprogram.addCommand(createConfigCommand());\nprogram.addCommand(createHandoffCommand());\nprogram.addCommand(createDecisionCommand());\nprogram.addCommand(createMemoryCommand());\nprogram.addCommand(createSkillsCommand());\nprogram.addCommand(createTestCommand());\nprogram.addCommand(clearCommand);\nprogram.addCommand(createWorkflowCommand());\nprogram.addCommand(monitorCommand);\nprogram.addCommand(qualityCommand);\nprogram.addCommand(createRalphCommand());\nprogram.addCommand(serviceCommand);\nprogram.addCommand(createSweepCommand());\nprogram.addCommand(createHooksCommand());\nprogram.addCommand(createShellCommand());\nprogram.addCommand(createAPICommand());\nprogram.addCommand(createCleanupProcessesCommand());\nprogram.addCommand(createAutoBackgroundCommand());\nprogram.addCommand(createSMSNotifyCommand());\nprogram.addCommand(createSettingsCommand());\nprogram.addCommand(createRetrievalCommands());\nprogram.addCommand(createDiscoveryCommands());\nprogram.addCommand(createModelCommand());\n\n// Register dashboard command\nprogram\n .command('dashboard')\n .description('Display monitoring dashboard in terminal')\n .option('-w, --watch', 'Auto-refresh dashboard')\n .option('-i, --interval <seconds>', 'Refresh interval in seconds', '5')\n .action(async (options) => {\n const { dashboardCommand } = await import('./commands/dashboard.js');\n await dashboardCommand.handler(options);\n });\n\n// Auto-detect current project on startup\nif (process.argv.length > 2) {\n const manager = ProjectManager.getInstance();\n manager.detectProject().catch(() => {\n // Silently fail if not in a project directory\n });\n}\n\n// Only parse when running as main module (not when imported for testing)\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith('/stackmemory') ||\n process.argv[1]?.endsWith('index.ts') ||\n process.argv[1]?.includes('tsx');\n\nif (isMainModule) {\n program.parse();\n}\n\nexport { program };\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * StackMemory CLI\n * Command-line interface for StackMemory operations\n */\n\n// Set environment flag for CLI usage to skip async context bridge\nprocess.env['STACKMEMORY_CLI'] = 'true';\n\n// Load environment variables\nimport 'dotenv/config';\n\n// Initialize tracing system early\nimport { initializeTracing, trace } from '../core/trace/index.js';\ninitializeTracing();\n\nimport { program } from 'commander';\nimport { logger } from '../core/monitoring/logger.js';\nimport { FrameManager } from '../core/context/index.js';\nimport { sessionManager, FrameQueryMode } from '../core/session/index.js';\nimport { sharedContextLayer } from '../core/context/shared-context-layer.js';\nimport { UpdateChecker } from '../core/utils/update-checker.js';\nimport { ProgressTracker } from '../core/monitoring/progress-tracker.js';\nimport { registerProjectCommands } from './commands/projects.js';\nimport { createSessionCommands } from './commands/session.js';\nimport { isFeatureEnabled, isLocalOnly } from '../core/config/feature-flags.js';\nimport { registerWorktreeCommands } from './commands/worktree.js';\nimport { registerOnboardingCommand } from './commands/onboard.js';\nimport { createTaskCommands } from './commands/tasks.js';\nimport { createSearchCommand } from './commands/search.js';\nimport { createLogCommand } from './commands/log.js';\nimport { createContextCommands } from './commands/context.js';\nimport { createConfigCommand } from './commands/config.js';\nimport { createHandoffCommand } from './commands/handoff.js';\nimport {\n createDecisionCommand,\n createMemoryCommand,\n} from './commands/decision.js';\nimport { createSkillsCommand } from './commands/skills.js';\nimport { createTestCommand } from './commands/test.js';\nimport clearCommand from './commands/clear.js';\nimport createWorkflowCommand from './commands/workflow.js';\nimport monitorCommand from './commands/monitor.js';\nimport qualityCommand from './commands/quality.js';\nimport createRalphCommand from './commands/ralph.js';\nimport serviceCommand from './commands/service.js';\nimport { registerLoginCommand } from './commands/login.js';\nimport { registerSignupCommand } from './commands/signup.js';\nimport { registerLogoutCommand, registerDbCommands } from './commands/db.js';\nimport { createSweepCommand } from './commands/sweep.js';\nimport { createHooksCommand } from './commands/hooks.js';\nimport { createShellCommand } from './commands/shell.js';\nimport { createAPICommand } from './commands/api.js';\nimport { createCleanupProcessesCommand } from './commands/cleanup-processes.js';\nimport { createAutoBackgroundCommand } from './commands/auto-background.js';\nimport { createSettingsCommand } from './commands/settings.js';\nimport { createRetrievalCommands } from './commands/retrieval.js';\nimport { createDiscoveryCommands } from './commands/discovery.js';\nimport { createModelCommand } from './commands/model.js';\nimport { ProjectManager } from '../core/projects/project-manager.js';\nimport Database from 'better-sqlite3';\nimport { join } from 'path';\nimport { existsSync, mkdirSync } from 'fs';\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport {\n loadStorageConfig,\n enableChromaDB,\n getStorageModeDescription,\n} from '../core/config/storage-config.js';\nimport { spawn } from 'child_process';\nimport { homedir } from 'os';\n\n// Read version from package.json\nimport { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../../package.json');\nconst VERSION = pkg.version;\n\n// Check for updates on CLI startup\nUpdateChecker.checkForUpdates(VERSION, true).catch(() => {\n // Silently ignore errors\n});\n\n// Auto-start webhook and ngrok if notifications are enabled\nasync function startNotificationServices(): Promise<void> {\n // Skip in local-only mode\n if (isLocalOnly() || !isFeatureEnabled('whatsapp')) return;\n\n try {\n const { loadSMSConfig } = await import('../hooks/sms-notify.js');\n const config = loadSMSConfig();\n if (!config.enabled) return;\n\n const WEBHOOK_PORT = 3456;\n let webhookStarted = false;\n let ngrokStarted = false;\n\n // Check if webhook is already running\n const webhookRunning = await fetch(\n `http://localhost:${WEBHOOK_PORT}/health`\n )\n .then((r) => r.ok)\n .catch(() => false);\n\n if (!webhookRunning) {\n // Start webhook in background using the dist path\n const webhookPath = join(__dirname, '../hooks/sms-webhook.js');\n const webhookProcess = spawn('node', [webhookPath], {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, SMS_WEBHOOK_PORT: String(WEBHOOK_PORT) },\n });\n webhookProcess.unref();\n webhookStarted = true;\n }\n\n // Check if ngrok is running\n const ngrokRunning = await fetch('http://localhost:4040/api/tunnels')\n .then((r) => r.ok)\n .catch(() => false);\n\n if (!ngrokRunning) {\n // Start ngrok in background\n const ngrokProcess = spawn('ngrok', ['http', String(WEBHOOK_PORT)], {\n detached: true,\n stdio: 'ignore',\n });\n ngrokProcess.unref();\n ngrokStarted = true;\n }\n\n // Save ngrok URL after startup\n if (webhookStarted || ngrokStarted) {\n setTimeout(async () => {\n try {\n const tunnels = await fetch('http://localhost:4040/api/tunnels').then(\n (r) =>\n r.json() as Promise<{ tunnels: Array<{ public_url: string }> }>\n );\n const publicUrl = tunnels?.tunnels?.[0]?.public_url;\n if (publicUrl) {\n const configDir = join(homedir(), '.stackmemory');\n const configPath = join(configDir, 'ngrok-url.txt');\n const { writeFileSync, mkdirSync, existsSync } = await import('fs');\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n writeFileSync(configPath, publicUrl);\n console.log(\n chalk.gray(`[notify] Webhook: ${publicUrl}/sms/incoming`)\n );\n }\n } catch {\n // Ignore errors\n }\n }, 4000);\n }\n } catch {\n // Silently ignore - notifications are optional\n }\n}\n\nstartNotificationServices();\n\nprogram\n .name('stackmemory')\n .description(\n 'Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention'\n )\n .version(VERSION);\n\nprogram\n .command('init')\n .description(\n `Initialize StackMemory in current project\n\nStorage Modes:\n SQLite (default): Local only, fast, no setup required\n ChromaDB (hybrid): Adds semantic search and cloud backup, requires API key`\n )\n .option('--sqlite', 'Use SQLite-only storage (default, skip prompts)')\n .option(\n '--chromadb',\n 'Enable ChromaDB for semantic search (prompts for API key)'\n )\n .option('--skip-storage-prompt', 'Skip storage configuration prompt')\n .action(async (options) => {\n try {\n const projectRoot = process.cwd();\n const dbDir = join(projectRoot, '.stackmemory');\n\n if (!existsSync(dbDir)) {\n mkdirSync(dbDir, { recursive: true });\n }\n\n // Handle storage configuration\n let storageConfig = loadStorageConfig();\n const isFirstTimeSetup =\n !storageConfig.chromadb.enabled && storageConfig.mode === 'sqlite';\n\n // Skip prompts if --sqlite flag or --skip-storage-prompt\n if (options.sqlite || options.skipStoragePrompt) {\n // Use SQLite-only (default)\n console.log(chalk.gray('Using SQLite-only storage mode.'));\n } else if (options.chromadb) {\n // User explicitly requested ChromaDB, prompt for API key\n await promptAndEnableChromaDB();\n } else if (isFirstTimeSetup && process.stdin.isTTY) {\n // Interactive mode - ask user about ChromaDB\n console.log(chalk.cyan('\\nStorage Configuration'));\n console.log(chalk.gray('StackMemory supports two storage modes:\\n'));\n console.log(chalk.white(' SQLite (default):'));\n console.log(chalk.gray(' - Local storage only'));\n console.log(chalk.gray(' - Fast and simple'));\n console.log(chalk.gray(' - No external dependencies\\n'));\n console.log(chalk.white(' ChromaDB (hybrid):'));\n console.log(chalk.gray(' - Semantic search across your context'));\n console.log(chalk.gray(' - Cloud backup capability'));\n console.log(chalk.gray(' - Requires ChromaDB API key\\n'));\n\n const { enableChroma } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'enableChroma',\n message: 'Enable ChromaDB for semantic search? (requires API key)',\n default: false,\n },\n ]);\n\n if (enableChroma) {\n await promptAndEnableChromaDB();\n } else {\n console.log(chalk.gray('Using SQLite-only storage mode.'));\n }\n }\n\n // Initialize SQLite database\n const dbPath = join(dbDir, 'context.db');\n const db = new Database(dbPath);\n new FrameManager(db, 'cli-project');\n\n logger.info('StackMemory initialized successfully', { projectRoot });\n console.log(\n chalk.green('\\n[OK] StackMemory initialized in'),\n projectRoot\n );\n\n // Show current storage mode\n storageConfig = loadStorageConfig();\n console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));\n\n db.close();\n } catch (error: unknown) {\n logger.error('Failed to initialize StackMemory', error as Error);\n console.error(\n chalk.red('[ERROR] Initialization failed:'),\n (error as Error).message\n );\n process.exit(1);\n }\n });\n\n/**\n * Prompt user for ChromaDB configuration and enable it\n */\nasync function promptAndEnableChromaDB(): Promise<void> {\n const answers = await inquirer.prompt([\n {\n type: 'password',\n name: 'apiKey',\n message: 'Enter your ChromaDB API key:',\n validate: (input: string) => {\n if (!input || input.trim().length === 0) {\n return 'API key is required for ChromaDB';\n }\n return true;\n },\n },\n {\n type: 'input',\n name: 'apiUrl',\n message: 'ChromaDB API URL (press Enter for default):',\n default: 'https://api.trychroma.com',\n },\n ]);\n\n enableChromaDB({\n apiKey: answers.apiKey,\n apiUrl: answers.apiUrl,\n });\n\n console.log(chalk.green('[OK] ChromaDB enabled for semantic search.'));\n console.log(\n chalk.gray('API key saved to ~/.stackmemory/storage-config.json')\n );\n}\n\nprogram\n .command('status')\n .description('Show current StackMemory status')\n .option('--all', 'Show all active frames across sessions')\n .option('--project', 'Show all active frames in current project')\n .option('--session <id>', 'Show frames for specific session')\n .action(async (options) => {\n return trace.command('stackmemory-status', options, async () => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n if (!existsSync(dbPath)) {\n console.log(\n '\u274C StackMemory not initialized. Run \"stackmemory init\" first.'\n );\n return;\n }\n\n // Check for updates and display if available\n await UpdateChecker.checkForUpdates(VERSION);\n\n // Initialize session manager and shared context\n await sessionManager.initialize();\n await sharedContextLayer.initialize();\n\n const session = await sessionManager.getOrCreateSession({\n projectPath: projectRoot,\n sessionId: options.session,\n });\n\n // Auto-discover shared context on startup\n const contextDiscovery = await sharedContextLayer.autoDiscoverContext();\n\n // Show context hints if available\n if (\n contextDiscovery.hasSharedContext &&\n contextDiscovery.sessionCount > 1\n ) {\n console.log(`\\n\uD83D\uDCA1 Shared Context Available:`);\n console.log(\n ` ${contextDiscovery.sessionCount} sessions with shared context`\n );\n\n if (contextDiscovery.recentPatterns.length > 0) {\n console.log(` Recent patterns:`);\n contextDiscovery.recentPatterns.slice(0, 3).forEach((p) => {\n console.log(\n ` \u2022 ${p.type}: ${p.pattern.slice(0, 50)} (${p.frequency}x)`\n );\n });\n }\n\n if (contextDiscovery.lastDecisions.length > 0) {\n console.log(\n ` Last decision: ${contextDiscovery.lastDecisions[0].decision.slice(0, 60)}`\n );\n }\n }\n\n const db = new Database(dbPath);\n const frameManager = new FrameManager(db, session.projectId);\n\n // Set query mode based on options\n if (options.all) {\n frameManager.setQueryMode(FrameQueryMode.ALL_ACTIVE);\n } else if (options.project) {\n frameManager.setQueryMode(FrameQueryMode.PROJECT_ACTIVE);\n }\n\n const activeFrames = frameManager.getActiveFramePath();\n const stackDepth = frameManager.getStackDepth();\n\n // Always get total counts across all sessions\n const totalStats = db\n .prepare(\n `\n SELECT \n COUNT(*) as total_frames,\n SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) as active_frames,\n SUM(CASE WHEN state = 'closed' THEN 1 ELSE 0 END) as closed_frames,\n COUNT(DISTINCT run_id) as total_sessions\n FROM frames\n WHERE project_id = ?\n `\n )\n .get(session.projectId) as {\n total_frames: number;\n active_frames: number;\n closed_frames: number;\n total_sessions: number;\n };\n\n const contextCount = db\n .prepare(\n `\n SELECT COUNT(*) as count FROM contexts\n `\n )\n .get() as { count: number };\n\n const eventCount = db\n .prepare(\n `\n SELECT COUNT(*) as count FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE f.project_id = ?\n `\n )\n .get(session.projectId) as { count: number };\n\n console.log('\uD83D\uDCCA StackMemory Status:');\n console.log(\n ` Session: ${session.sessionId.slice(0, 8)} (${session.state}, ${Math.round((Date.now() - session.startedAt) / 1000 / 60)}min old)`\n );\n console.log(` Project: ${session.projectId}`);\n if (session.branch) {\n console.log(` Branch: ${session.branch}`);\n }\n\n // Show total database statistics\n console.log(`\\n Database Statistics (this project):`);\n console.log(\n ` Frames: ${totalStats.total_frames || 0} (${totalStats.active_frames || 0} active, ${totalStats.closed_frames || 0} closed)`\n );\n console.log(` Events: ${eventCount.count || 0}`);\n console.log(` Sessions: ${totalStats.total_sessions || 0}`);\n console.log(\n ` Cached contexts: ${contextCount.count || 0} (global)`\n );\n\n // Show recent activity\n const recentFrames = db\n .prepare(\n `\n SELECT name, type, state, datetime(created_at, 'unixepoch') as created\n FROM frames\n WHERE project_id = ?\n ORDER BY created_at DESC\n LIMIT 3\n `\n )\n .all(session.projectId) as Array<{\n name: string;\n type: string;\n state: string;\n created: string;\n }>;\n\n if (recentFrames.length > 0) {\n console.log(`\\n Recent Activity:`);\n recentFrames.forEach((f) => {\n const stateIcon = f.state === 'active' ? '\uD83D\uDFE2' : '\u26AB';\n console.log(\n ` ${stateIcon} ${f.name} [${f.type}] - ${f.created}`\n );\n });\n }\n\n console.log(`\\n Current Session:`);\n console.log(` Stack depth: ${stackDepth}`);\n console.log(` Active frames: ${activeFrames.length}`);\n\n if (activeFrames.length > 0) {\n activeFrames.forEach((frame, i) => {\n const indent = ' ' + ' '.repeat(frame.depth || i);\n const prefix = i === 0 ? '\u2514\u2500' : ' \u2514\u2500';\n console.log(`${indent}${prefix} ${frame.name} [${frame.type}]`);\n });\n }\n\n // Show other sessions if in default mode\n if (!options.all && !options.project) {\n const otherSessions = await sessionManager.listSessions({\n projectId: session.projectId,\n state: 'active',\n });\n\n const otherActive = otherSessions.filter(\n (s) => s.sessionId !== session.sessionId\n );\n if (otherActive.length > 0) {\n console.log(`\\n Other Active Sessions (same project):`);\n otherActive.forEach((s) => {\n const age = Math.round(\n (Date.now() - s.lastActiveAt) / 1000 / 60 / 60\n );\n console.log(\n ` - ${s.sessionId.slice(0, 8)}: ${s.branch || 'main'}, ${age}h old`\n );\n });\n console.log(`\\n Tip: Use --all to see frames across sessions`);\n }\n }\n\n db.close();\n } catch (error: unknown) {\n logger.error('Failed to get status', error as Error);\n console.error('\u274C Status check failed:', (error as Error).message);\n process.exit(1);\n }\n });\n });\n\nprogram\n .command('update-check')\n .description('Check for StackMemory updates')\n .action(async () => {\n try {\n console.log('\uD83D\uDD0D Checking for updates...');\n await UpdateChecker.forceCheck(VERSION);\n } catch (error: unknown) {\n logger.error('Update check failed', error as Error);\n console.error('\u274C Update check failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\nprogram\n .command('progress')\n .description('Show current progress and recent changes')\n .action(async () => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n if (!existsSync(dbPath)) {\n console.log(\n '\u274C StackMemory not initialized. Run \"stackmemory init\" first.'\n );\n return;\n }\n\n const progress = new ProgressTracker(projectRoot);\n console.log(progress.getSummary());\n } catch (error: unknown) {\n logger.error('Failed to show progress', error as Error);\n console.error('\u274C Failed to show progress:', (error as Error).message);\n process.exit(1);\n }\n });\n\nprogram\n .command('mcp-server')\n .description('Start StackMemory MCP server for Claude Desktop')\n .option('-p, --project <path>', 'Project root directory', process.cwd())\n .action(async (options) => {\n try {\n const { runMCPServer } = await import('../integrations/mcp/server.js');\n\n // Set project root\n process.env['PROJECT_ROOT'] = options.project;\n\n console.log('\uD83D\uDE80 Starting StackMemory MCP Server...');\n console.log(` Project: ${options.project}`);\n console.log(` Version: ${VERSION}`);\n\n // Check for updates silently\n UpdateChecker.checkForUpdates(VERSION, true).catch(() => {});\n\n // Start the MCP server\n await runMCPServer();\n } catch (error: unknown) {\n logger.error('Failed to start MCP server', error as Error);\n console.error('\u274C MCP server failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n// Add test context command\nprogram\n .command('context:test')\n .description('Test context persistence by creating sample frames')\n .action(async () => {\n try {\n const projectRoot = process.cwd();\n const dbPath = join(projectRoot, '.stackmemory', 'context.db');\n\n if (!existsSync(dbPath)) {\n console.log(\n '\u274C StackMemory not initialized. Run \"stackmemory init\" first.'\n );\n return;\n }\n\n const db = new Database(dbPath);\n const frameManager = new FrameManager(db, 'cli-project');\n\n // Create test frames\n console.log('\uD83D\uDCDD Creating test context frames...');\n\n const rootFrame = frameManager.createFrame({\n type: 'task',\n name: 'Test Session',\n inputs: { test: true, timestamp: new Date().toISOString() },\n });\n\n const taskFrame = frameManager.createFrame({\n type: 'subtask',\n name: 'Sample Task',\n inputs: { description: 'Testing context persistence' },\n parentFrameId: rootFrame,\n });\n\n const commandFrame = frameManager.createFrame({\n type: 'tool_scope',\n name: 'test-command',\n inputs: { args: ['--test'] },\n parentFrameId: taskFrame,\n });\n\n // Add some events\n frameManager.addEvent(\n 'observation',\n {\n message: 'Test event recorded',\n },\n commandFrame\n );\n\n console.log('\u2705 Test frames created!');\n console.log(`\uD83D\uDCCA Stack depth: ${frameManager.getStackDepth()}`);\n console.log(\n `\uD83D\uDD04 Active frames: ${frameManager.getActiveFramePath().length}`\n );\n\n // Close one frame to test state changes\n frameManager.closeFrame(commandFrame);\n console.log(\n `\uD83D\uDCCA After closing command frame: depth = ${frameManager.getStackDepth()}`\n );\n\n db.close();\n } catch (error: unknown) {\n logger.error('Test context failed', error as Error);\n console.error('\u274C Test failed:', (error as Error).message);\n process.exit(1);\n }\n });\n\n// Register project management commands\n// Register command modules\nregisterOnboardingCommand(program);\nregisterSignupCommand(program);\nregisterLoginCommand(program);\nregisterLogoutCommand(program);\nregisterDbCommands(program);\nregisterProjectCommands(program);\nregisterWorktreeCommands(program);\n\n// Register Linear integration commands (lazy-loaded, optional)\nif (isFeatureEnabled('linear')) {\n import('./commands/linear.js')\n .then(({ registerLinearCommands }) => registerLinearCommands(program))\n .catch(() => {\n // Linear integration not available - silently skip\n });\n}\n\n// Register session management commands\nprogram.addCommand(createSessionCommands());\n\n// Register enhanced CLI commands\nprogram.addCommand(createTaskCommands());\nprogram.addCommand(createSearchCommand());\nprogram.addCommand(createLogCommand());\nprogram.addCommand(createContextCommands());\nprogram.addCommand(createConfigCommand());\nprogram.addCommand(createHandoffCommand());\nprogram.addCommand(createDecisionCommand());\nprogram.addCommand(createMemoryCommand());\nprogram.addCommand(createSkillsCommand());\nprogram.addCommand(createTestCommand());\nprogram.addCommand(clearCommand);\nprogram.addCommand(createWorkflowCommand());\nprogram.addCommand(monitorCommand);\nprogram.addCommand(qualityCommand);\nprogram.addCommand(createRalphCommand());\nprogram.addCommand(serviceCommand);\nprogram.addCommand(createSweepCommand());\nprogram.addCommand(createHooksCommand());\nprogram.addCommand(createShellCommand());\nprogram.addCommand(createAPICommand());\nprogram.addCommand(createCleanupProcessesCommand());\nprogram.addCommand(createAutoBackgroundCommand());\nprogram.addCommand(createSettingsCommand());\n\n// Register WhatsApp/SMS commands (lazy-loaded, optional)\nif (isFeatureEnabled('whatsapp')) {\n import('./commands/sms-notify.js')\n .then(({ createSMSNotifyCommand }) =>\n program.addCommand(createSMSNotifyCommand())\n )\n .catch(() => {\n // WhatsApp integration not available - silently skip\n });\n}\nprogram.addCommand(createRetrievalCommands());\nprogram.addCommand(createDiscoveryCommands());\nprogram.addCommand(createModelCommand());\n\n// Register dashboard command\nprogram\n .command('dashboard')\n .description('Display monitoring dashboard in terminal')\n .option('-w, --watch', 'Auto-refresh dashboard')\n .option('-i, --interval <seconds>', 'Refresh interval in seconds', '5')\n .action(async (options) => {\n const { dashboardCommand } = await import('./commands/dashboard.js');\n await dashboardCommand.handler(options);\n });\n\n// Auto-detect current project on startup\nif (process.argv.length > 2) {\n const manager = ProjectManager.getInstance();\n manager.detectProject().catch(() => {\n // Silently fail if not in a project directory\n });\n}\n\n// Only parse when running as main module (not when imported for testing)\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith('/stackmemory') ||\n process.argv[1]?.endsWith('index.ts') ||\n process.argv[1]?.includes('tsx');\n\nif (isMainModule) {\n program.parse();\n}\n\nexport { program };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["require", "mkdirSync", "existsSync"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
function isLocalOnly() {
|
|
6
|
+
return process.env["STACKMEMORY_LOCAL"] === "true" || process.env["STACKMEMORY_LOCAL"] === "1" || process.env["LOCAL_ONLY"] === "true";
|
|
7
|
+
}
|
|
8
|
+
function isFeatureEnabled(feature) {
|
|
9
|
+
if (feature === "core") return true;
|
|
10
|
+
if (isLocalOnly()) return false;
|
|
11
|
+
switch (feature) {
|
|
12
|
+
case "linear":
|
|
13
|
+
return process.env["STACKMEMORY_LINEAR"] !== "false" && (!!process.env["LINEAR_API_KEY"] || !!process.env["LINEAR_OAUTH_TOKEN"]);
|
|
14
|
+
case "whatsapp":
|
|
15
|
+
return process.env["STACKMEMORY_WHATSAPP"] !== "false" && !!process.env["TWILIO_ACCOUNT_SID"];
|
|
16
|
+
case "chromadb":
|
|
17
|
+
return process.env["STACKMEMORY_CHROMADB"] === "true";
|
|
18
|
+
case "aiSummaries":
|
|
19
|
+
return process.env["STACKMEMORY_AI"] !== "false" && (!!process.env["ANTHROPIC_API_KEY"] || !!process.env["OPENAI_API_KEY"]);
|
|
20
|
+
default:
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function getFeatureFlags() {
|
|
25
|
+
return {
|
|
26
|
+
core: true,
|
|
27
|
+
linear: isFeatureEnabled("linear"),
|
|
28
|
+
whatsapp: isFeatureEnabled("whatsapp"),
|
|
29
|
+
chromadb: isFeatureEnabled("chromadb"),
|
|
30
|
+
aiSummaries: isFeatureEnabled("aiSummaries")
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function logFeatureStatus() {
|
|
34
|
+
const flags = getFeatureFlags();
|
|
35
|
+
const local = isLocalOnly();
|
|
36
|
+
console.log(
|
|
37
|
+
`StackMemory Mode: ${local ? "LOCAL (no external services)" : "FULL"}`
|
|
38
|
+
);
|
|
39
|
+
if (!local) {
|
|
40
|
+
console.log(
|
|
41
|
+
` Linear: ${flags.linear ? "enabled" : "disabled (no API key)"}`
|
|
42
|
+
);
|
|
43
|
+
console.log(
|
|
44
|
+
` WhatsApp: ${flags.whatsapp ? "enabled" : "disabled (no Twilio)"}`
|
|
45
|
+
);
|
|
46
|
+
console.log(` ChromaDB: ${flags.chromadb ? "enabled" : "disabled"}`);
|
|
47
|
+
console.log(
|
|
48
|
+
` AI Summaries: ${flags.aiSummaries ? "enabled" : "disabled (no API key)"}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
getFeatureFlags,
|
|
54
|
+
isFeatureEnabled,
|
|
55
|
+
isLocalOnly,
|
|
56
|
+
logFeatureStatus
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=feature-flags.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/core/config/feature-flags.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Feature Flags Configuration\n * Controls which external integrations are enabled\n *\n * Set STACKMEMORY_LOCAL=true to run without any external services\n */\n\nexport interface FeatureFlags {\n // Core features (always available)\n core: true;\n\n // External integrations (can be disabled)\n linear: boolean;\n whatsapp: boolean;\n chromadb: boolean;\n aiSummaries: boolean;\n}\n\n/**\n * Check if running in local-only mode\n * When true, all external service integrations are disabled\n */\nexport function isLocalOnly(): boolean {\n return (\n process.env['STACKMEMORY_LOCAL'] === 'true' ||\n process.env['STACKMEMORY_LOCAL'] === '1' ||\n process.env['LOCAL_ONLY'] === 'true'\n );\n}\n\n/**\n * Check if a specific feature is enabled\n */\nexport function isFeatureEnabled(feature: keyof FeatureFlags): boolean {\n if (feature === 'core') return true;\n\n // In local-only mode, external integrations are disabled\n if (isLocalOnly()) return false;\n\n // Check feature-specific env vars\n switch (feature) {\n case 'linear':\n return (\n process.env['STACKMEMORY_LINEAR'] !== 'false' &&\n (!!process.env['LINEAR_API_KEY'] || !!process.env['LINEAR_OAUTH_TOKEN'])\n );\n case 'whatsapp':\n return (\n process.env['STACKMEMORY_WHATSAPP'] !== 'false' &&\n !!process.env['TWILIO_ACCOUNT_SID']\n );\n case 'chromadb':\n return process.env['STACKMEMORY_CHROMADB'] === 'true';\n case 'aiSummaries':\n return (\n process.env['STACKMEMORY_AI'] !== 'false' &&\n (!!process.env['ANTHROPIC_API_KEY'] || !!process.env['OPENAI_API_KEY'])\n );\n default:\n return false;\n }\n}\n\n/**\n * Get all feature flags\n */\nexport function getFeatureFlags(): FeatureFlags {\n return {\n core: true,\n linear: isFeatureEnabled('linear'),\n whatsapp: isFeatureEnabled('whatsapp'),\n chromadb: isFeatureEnabled('chromadb'),\n aiSummaries: isFeatureEnabled('aiSummaries'),\n };\n}\n\n/**\n * Log feature flags status (for debugging)\n */\nexport function logFeatureStatus(): void {\n const flags = getFeatureFlags();\n const local = isLocalOnly();\n\n console.log(\n `StackMemory Mode: ${local ? 'LOCAL (no external services)' : 'FULL'}`\n );\n if (!local) {\n console.log(\n ` Linear: ${flags.linear ? 'enabled' : 'disabled (no API key)'}`\n );\n console.log(\n ` WhatsApp: ${flags.whatsapp ? 'enabled' : 'disabled (no Twilio)'}`\n );\n console.log(` ChromaDB: ${flags.chromadb ? 'enabled' : 'disabled'}`);\n console.log(\n ` AI Summaries: ${flags.aiSummaries ? 'enabled' : 'disabled (no API key)'}`\n );\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;AAsBO,SAAS,cAAuB;AACrC,SACE,QAAQ,IAAI,mBAAmB,MAAM,UACrC,QAAQ,IAAI,mBAAmB,MAAM,OACrC,QAAQ,IAAI,YAAY,MAAM;AAElC;AAKO,SAAS,iBAAiB,SAAsC;AACrE,MAAI,YAAY,OAAQ,QAAO;AAG/B,MAAI,YAAY,EAAG,QAAO;AAG1B,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aACE,QAAQ,IAAI,oBAAoB,MAAM,YACrC,CAAC,CAAC,QAAQ,IAAI,gBAAgB,KAAK,CAAC,CAAC,QAAQ,IAAI,oBAAoB;AAAA,IAE1E,KAAK;AACH,aACE,QAAQ,IAAI,sBAAsB,MAAM,WACxC,CAAC,CAAC,QAAQ,IAAI,oBAAoB;AAAA,IAEtC,KAAK;AACH,aAAO,QAAQ,IAAI,sBAAsB,MAAM;AAAA,IACjD,KAAK;AACH,aACE,QAAQ,IAAI,gBAAgB,MAAM,YACjC,CAAC,CAAC,QAAQ,IAAI,mBAAmB,KAAK,CAAC,CAAC,QAAQ,IAAI,gBAAgB;AAAA,IAEzE;AACE,aAAO;AAAA,EACX;AACF;AAKO,SAAS,kBAAgC;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,iBAAiB,QAAQ;AAAA,IACjC,UAAU,iBAAiB,UAAU;AAAA,IACrC,UAAU,iBAAiB,UAAU;AAAA,IACrC,aAAa,iBAAiB,aAAa;AAAA,EAC7C;AACF;AAKO,SAAS,mBAAyB;AACvC,QAAM,QAAQ,gBAAgB;AAC9B,QAAM,QAAQ,YAAY;AAE1B,UAAQ;AAAA,IACN,qBAAqB,QAAQ,iCAAiC,MAAM;AAAA,EACtE;AACA,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,aAAa,MAAM,SAAS,YAAY,uBAAuB;AAAA,IACjE;AACA,YAAQ;AAAA,MACN,eAAe,MAAM,WAAW,YAAY,sBAAsB;AAAA,IACpE;AACA,YAAQ,IAAI,eAAe,MAAM,WAAW,YAAY,UAAU,EAAE;AACpE,YAAQ;AAAA,MACN,mBAAmB,MAAM,cAAc,YAAY,uBAAuB;AAAA,IAC5E;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { logger } from "../monitoring/logger.js";
|
|
6
|
+
class FrameRecovery {
|
|
7
|
+
constructor(db, options = {}) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
this.orphanThresholdMs = (options.orphanThresholdHours ?? 24) * 60 * 60 * 1e3;
|
|
10
|
+
}
|
|
11
|
+
// Sessions older than this are considered orphaned (default: 24 hours)
|
|
12
|
+
orphanThresholdMs;
|
|
13
|
+
// Current session/run ID to exclude from orphan detection
|
|
14
|
+
currentRunId = null;
|
|
15
|
+
/**
|
|
16
|
+
* Set the current run ID to exclude from orphan detection
|
|
17
|
+
*/
|
|
18
|
+
setCurrentRunId(runId) {
|
|
19
|
+
this.currentRunId = runId;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Main recovery entry point - call on startup
|
|
23
|
+
*/
|
|
24
|
+
async recoverOnStartup() {
|
|
25
|
+
const errors = [];
|
|
26
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
27
|
+
logger.info("Starting crash recovery check");
|
|
28
|
+
const walStatus = this.checkWalStatus();
|
|
29
|
+
if (walStatus.checkpointNeeded) {
|
|
30
|
+
try {
|
|
31
|
+
this.checkpointWal();
|
|
32
|
+
logger.info("WAL checkpoint completed");
|
|
33
|
+
} catch (err) {
|
|
34
|
+
const msg = `WAL checkpoint failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
35
|
+
errors.push(msg);
|
|
36
|
+
logger.warn(msg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const integrityCheck = this.runIntegrityCheck();
|
|
40
|
+
if (!integrityCheck.passed) {
|
|
41
|
+
logger.error("Database integrity check failed", {
|
|
42
|
+
violations: integrityCheck.foreignKeyViolations,
|
|
43
|
+
corrupted: integrityCheck.corruptedRows
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const orphanedFrames = this.recoverOrphanedFrames();
|
|
47
|
+
if (orphanedFrames.detected > 0) {
|
|
48
|
+
logger.info("Orphaned frames processed", {
|
|
49
|
+
detected: orphanedFrames.detected,
|
|
50
|
+
recovered: orphanedFrames.recovered,
|
|
51
|
+
closed: orphanedFrames.closed
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const report = {
|
|
55
|
+
timestamp,
|
|
56
|
+
integrityCheck,
|
|
57
|
+
orphanedFrames,
|
|
58
|
+
walStatus,
|
|
59
|
+
recovered: integrityCheck.passed && orphanedFrames.detected === orphanedFrames.closed,
|
|
60
|
+
errors
|
|
61
|
+
};
|
|
62
|
+
logger.info("Crash recovery completed", {
|
|
63
|
+
recovered: report.recovered,
|
|
64
|
+
orphansFound: orphanedFrames.detected,
|
|
65
|
+
integrityPassed: integrityCheck.passed
|
|
66
|
+
});
|
|
67
|
+
return report;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check WAL mode status
|
|
71
|
+
*/
|
|
72
|
+
checkWalStatus() {
|
|
73
|
+
try {
|
|
74
|
+
const journalMode = this.db.pragma("journal_mode");
|
|
75
|
+
const isWal = journalMode[0]?.journal_mode === "wal";
|
|
76
|
+
if (!isWal) {
|
|
77
|
+
return { enabled: false, checkpointNeeded: false, walSize: 0 };
|
|
78
|
+
}
|
|
79
|
+
const walInfo = this.db.pragma("wal_checkpoint(PASSIVE)");
|
|
80
|
+
const walSize = walInfo[0]?.log ?? 0;
|
|
81
|
+
const checkpointed = walInfo[0]?.checkpointed ?? 0;
|
|
82
|
+
return {
|
|
83
|
+
enabled: true,
|
|
84
|
+
checkpointNeeded: walSize > 1e3,
|
|
85
|
+
// Checkpoint if WAL has > 1000 pages
|
|
86
|
+
walSize: walSize - checkpointed
|
|
87
|
+
};
|
|
88
|
+
} catch (err) {
|
|
89
|
+
logger.warn("Failed to check WAL status", { error: err });
|
|
90
|
+
return { enabled: false, checkpointNeeded: false, walSize: 0 };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Force WAL checkpoint
|
|
95
|
+
*/
|
|
96
|
+
checkpointWal() {
|
|
97
|
+
this.db.pragma("wal_checkpoint(TRUNCATE)");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Run database integrity checks
|
|
101
|
+
*/
|
|
102
|
+
runIntegrityCheck() {
|
|
103
|
+
const errors = [];
|
|
104
|
+
let foreignKeyViolations = 0;
|
|
105
|
+
let corruptedRows = 0;
|
|
106
|
+
try {
|
|
107
|
+
const fkViolations = this.db.pragma(
|
|
108
|
+
"foreign_key_check"
|
|
109
|
+
);
|
|
110
|
+
foreignKeyViolations = fkViolations.length;
|
|
111
|
+
if (foreignKeyViolations > 0) {
|
|
112
|
+
errors.push(`Found ${foreignKeyViolations} foreign key violations`);
|
|
113
|
+
logger.warn("Foreign key violations detected", {
|
|
114
|
+
count: foreignKeyViolations,
|
|
115
|
+
samples: fkViolations.slice(0, 5)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const integrity = this.db.pragma("integrity_check");
|
|
119
|
+
const integrityResult = integrity[0]?.integrity_check;
|
|
120
|
+
if (integrityResult !== "ok") {
|
|
121
|
+
corruptedRows = integrity.length;
|
|
122
|
+
errors.push(`Integrity check failed: ${integrityResult}`);
|
|
123
|
+
logger.error("Database corruption detected", {
|
|
124
|
+
result: integrityResult
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
errors.push(
|
|
129
|
+
`Integrity check error: ${err instanceof Error ? err.message : String(err)}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
passed: foreignKeyViolations === 0 && corruptedRows === 0,
|
|
134
|
+
foreignKeyViolations,
|
|
135
|
+
corruptedRows,
|
|
136
|
+
errors
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Detect and recover orphaned frames
|
|
141
|
+
* Orphaned = active frames from sessions that are no longer running
|
|
142
|
+
*/
|
|
143
|
+
recoverOrphanedFrames() {
|
|
144
|
+
const orphanThreshold = Date.now() - this.orphanThresholdMs;
|
|
145
|
+
const thresholdUnix = Math.floor(orphanThreshold / 1e3);
|
|
146
|
+
const query = `
|
|
147
|
+
SELECT frame_id, run_id, project_id, name, type, created_at, depth
|
|
148
|
+
FROM frames
|
|
149
|
+
WHERE state = 'active'
|
|
150
|
+
AND created_at < ?
|
|
151
|
+
${this.currentRunId ? "AND run_id != ?" : ""}
|
|
152
|
+
ORDER BY created_at ASC
|
|
153
|
+
`;
|
|
154
|
+
const params = this.currentRunId ? [thresholdUnix, this.currentRunId] : [thresholdUnix];
|
|
155
|
+
const orphaned = this.db.prepare(query).all(...params);
|
|
156
|
+
if (orphaned.length === 0) {
|
|
157
|
+
return { detected: 0, recovered: 0, closed: 0, frameIds: [] };
|
|
158
|
+
}
|
|
159
|
+
const frameIds = orphaned.map((f) => f.frame_id);
|
|
160
|
+
let closed = 0;
|
|
161
|
+
const closeStmt = this.db.prepare(`
|
|
162
|
+
UPDATE frames
|
|
163
|
+
SET state = 'closed',
|
|
164
|
+
closed_at = unixepoch(),
|
|
165
|
+
outputs = json_set(COALESCE(outputs, '{}'), '$.recovered', true, '$.recoveryReason', 'orphan_cleanup')
|
|
166
|
+
WHERE frame_id = ?
|
|
167
|
+
`);
|
|
168
|
+
const transaction = this.db.transaction(() => {
|
|
169
|
+
for (const frame of orphaned) {
|
|
170
|
+
try {
|
|
171
|
+
closeStmt.run(frame.frame_id);
|
|
172
|
+
closed++;
|
|
173
|
+
logger.debug("Closed orphaned frame", {
|
|
174
|
+
frameId: frame.frame_id,
|
|
175
|
+
runId: frame.run_id,
|
|
176
|
+
name: frame.name,
|
|
177
|
+
age: Math.round((Date.now() / 1e3 - frame.created_at) / 3600) + "h"
|
|
178
|
+
});
|
|
179
|
+
} catch (err) {
|
|
180
|
+
logger.warn("Failed to close orphaned frame", {
|
|
181
|
+
frameId: frame.frame_id,
|
|
182
|
+
error: err
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
transaction();
|
|
188
|
+
return {
|
|
189
|
+
detected: orphaned.length,
|
|
190
|
+
recovered: 0,
|
|
191
|
+
// Future: could attempt to resume some frames
|
|
192
|
+
closed,
|
|
193
|
+
frameIds
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate data integrity for a specific project
|
|
198
|
+
*/
|
|
199
|
+
validateProjectIntegrity(projectId) {
|
|
200
|
+
const issues = [];
|
|
201
|
+
const invalidParents = this.db.prepare(
|
|
202
|
+
`
|
|
203
|
+
SELECT f1.frame_id, f1.parent_frame_id
|
|
204
|
+
FROM frames f1
|
|
205
|
+
LEFT JOIN frames f2 ON f1.parent_frame_id = f2.frame_id
|
|
206
|
+
WHERE f1.project_id = ?
|
|
207
|
+
AND f1.parent_frame_id IS NOT NULL
|
|
208
|
+
AND f2.frame_id IS NULL
|
|
209
|
+
`
|
|
210
|
+
).all(projectId);
|
|
211
|
+
if (invalidParents.length > 0) {
|
|
212
|
+
issues.push(
|
|
213
|
+
`${invalidParents.length} frames with invalid parent references`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const depthIssues = this.db.prepare(
|
|
217
|
+
`
|
|
218
|
+
SELECT f1.frame_id, f1.depth as child_depth, f2.depth as parent_depth
|
|
219
|
+
FROM frames f1
|
|
220
|
+
JOIN frames f2 ON f1.parent_frame_id = f2.frame_id
|
|
221
|
+
WHERE f1.project_id = ?
|
|
222
|
+
AND f1.depth != f2.depth + 1
|
|
223
|
+
`
|
|
224
|
+
).all(projectId);
|
|
225
|
+
if (depthIssues.length > 0) {
|
|
226
|
+
issues.push(`${depthIssues.length} frames with incorrect depth values`);
|
|
227
|
+
}
|
|
228
|
+
const orphanEvents = this.db.prepare(
|
|
229
|
+
`
|
|
230
|
+
SELECT COUNT(*) as count
|
|
231
|
+
FROM events e
|
|
232
|
+
LEFT JOIN frames f ON e.frame_id = f.frame_id
|
|
233
|
+
WHERE f.frame_id IS NULL
|
|
234
|
+
`
|
|
235
|
+
).get();
|
|
236
|
+
if (orphanEvents.count > 0) {
|
|
237
|
+
issues.push(`${orphanEvents.count} orphaned events without valid frames`);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
valid: issues.length === 0,
|
|
241
|
+
issues
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Clean up orphaned events (events without valid frames)
|
|
246
|
+
*/
|
|
247
|
+
cleanupOrphanedEvents() {
|
|
248
|
+
const result = this.db.prepare(
|
|
249
|
+
`
|
|
250
|
+
DELETE FROM events
|
|
251
|
+
WHERE frame_id NOT IN (SELECT frame_id FROM frames)
|
|
252
|
+
`
|
|
253
|
+
).run();
|
|
254
|
+
if (result.changes > 0) {
|
|
255
|
+
logger.info("Cleaned up orphaned events", { count: result.changes });
|
|
256
|
+
}
|
|
257
|
+
return result.changes;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get recovery statistics
|
|
261
|
+
*/
|
|
262
|
+
getRecoveryStats() {
|
|
263
|
+
const stats = this.db.prepare(
|
|
264
|
+
`
|
|
265
|
+
SELECT
|
|
266
|
+
COUNT(*) as total,
|
|
267
|
+
SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) as active,
|
|
268
|
+
SUM(CASE WHEN state = 'closed' THEN 1 ELSE 0 END) as closed,
|
|
269
|
+
SUM(CASE WHEN json_extract(outputs, '$.recovered') = true THEN 1 ELSE 0 END) as recovered
|
|
270
|
+
FROM frames
|
|
271
|
+
`
|
|
272
|
+
).get();
|
|
273
|
+
const oldest = this.db.prepare(
|
|
274
|
+
`
|
|
275
|
+
SELECT datetime(created_at, 'unixepoch') as created
|
|
276
|
+
FROM frames
|
|
277
|
+
WHERE state = 'active'
|
|
278
|
+
ORDER BY created_at ASC
|
|
279
|
+
LIMIT 1
|
|
280
|
+
`
|
|
281
|
+
).get();
|
|
282
|
+
return {
|
|
283
|
+
totalFrames: stats.total,
|
|
284
|
+
activeFrames: stats.active,
|
|
285
|
+
closedFrames: stats.closed,
|
|
286
|
+
recoveredFrames: stats.recovered,
|
|
287
|
+
oldestActiveFrame: oldest?.created ?? null
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function recoverDatabase(db, currentRunId) {
|
|
292
|
+
const recovery = new FrameRecovery(db);
|
|
293
|
+
if (currentRunId) {
|
|
294
|
+
recovery.setCurrentRunId(currentRunId);
|
|
295
|
+
}
|
|
296
|
+
return recovery.recoverOnStartup();
|
|
297
|
+
}
|
|
298
|
+
export {
|
|
299
|
+
FrameRecovery,
|
|
300
|
+
recoverDatabase
|
|
301
|
+
};
|
|
302
|
+
//# sourceMappingURL=frame-recovery.js.map
|