@stackmemoryai/stackmemory 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli/commands/config.js +81 -0
  2. package/dist/cli/commands/config.js.map +2 -2
  3. package/dist/cli/commands/decision.js +262 -0
  4. package/dist/cli/commands/decision.js.map +7 -0
  5. package/dist/cli/commands/handoff.js +87 -24
  6. package/dist/cli/commands/handoff.js.map +3 -3
  7. package/dist/cli/commands/service.js +684 -0
  8. package/dist/cli/commands/service.js.map +7 -0
  9. package/dist/cli/commands/sweep.js +311 -0
  10. package/dist/cli/commands/sweep.js.map +7 -0
  11. package/dist/cli/index.js +98 -4
  12. package/dist/cli/index.js.map +2 -2
  13. package/dist/cli/streamlined-cli.js +144 -0
  14. package/dist/cli/streamlined-cli.js.map +7 -0
  15. package/dist/core/config/storage-config.js +111 -0
  16. package/dist/core/config/storage-config.js.map +7 -0
  17. package/dist/core/events/event-bus.js +110 -0
  18. package/dist/core/events/event-bus.js.map +7 -0
  19. package/dist/core/plugins/plugin-interface.js +87 -0
  20. package/dist/core/plugins/plugin-interface.js.map +7 -0
  21. package/dist/core/session/enhanced-handoff.js +654 -0
  22. package/dist/core/session/enhanced-handoff.js.map +7 -0
  23. package/dist/core/storage/simplified-storage.js +328 -0
  24. package/dist/core/storage/simplified-storage.js.map +7 -0
  25. package/dist/daemon/session-daemon.js +308 -0
  26. package/dist/daemon/session-daemon.js.map +7 -0
  27. package/dist/plugins/linear/index.js +166 -0
  28. package/dist/plugins/linear/index.js.map +7 -0
  29. package/dist/plugins/loader.js +57 -0
  30. package/dist/plugins/loader.js.map +7 -0
  31. package/dist/plugins/plugin-interface.js +67 -0
  32. package/dist/plugins/plugin-interface.js.map +7 -0
  33. package/dist/plugins/ralph/simple-ralph-plugin.js +305 -0
  34. package/dist/plugins/ralph/simple-ralph-plugin.js.map +7 -0
  35. package/dist/plugins/ralph/use-cases/code-generator.js +151 -0
  36. package/dist/plugins/ralph/use-cases/code-generator.js.map +7 -0
  37. package/dist/plugins/ralph/use-cases/test-generator.js +201 -0
  38. package/dist/plugins/ralph/use-cases/test-generator.js.map +7 -0
  39. package/dist/skills/repo-ingestion-skill.js +54 -10
  40. package/dist/skills/repo-ingestion-skill.js.map +2 -2
  41. package/package.json +4 -8
  42. package/scripts/archive/check-all-duplicates.ts +2 -2
  43. package/scripts/archive/merge-linear-duplicates.ts +6 -4
  44. package/scripts/install-claude-hooks-auto.js +72 -15
  45. package/scripts/measure-handoff-impact.mjs +395 -0
  46. package/scripts/measure-handoff-impact.ts +450 -0
  47. package/templates/claude-hooks/on-startup.js +200 -19
  48. package/templates/services/com.stackmemory.guardian.plist +59 -0
  49. package/templates/services/stackmemory-guardian.service +41 -0
  50. package/scripts/testing/results/real-performance-results.json +0 -90
  51. package/scripts/testing/test-tier-migration.js +0 -100
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/cli/commands/service.ts"],
4
+ "sourcesContent": ["/**\n * Service command for StackMemory\n * Manages OS-level service installation for the guardian daemon\n *\n * The guardian service monitors ~/.stackmemory/sessions/ for active sessions\n * and starts context sync when activity is detected.\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { spawn, execSync } from 'child_process';\nimport { existsSync, readFileSync } from 'fs';\n\ninterface ServiceConfig {\n platform: 'darwin' | 'linux' | 'unsupported';\n serviceDir: string;\n serviceName: string;\n serviceFile: string;\n logDir: string;\n}\n\nfunction getServiceConfig(): ServiceConfig {\n const home = process.env.HOME || '';\n const platform = process.platform;\n\n if (platform === 'darwin') {\n return {\n platform: 'darwin',\n serviceDir: path.join(home, 'Library', 'LaunchAgents'),\n serviceName: 'com.stackmemory.guardian',\n serviceFile: path.join(\n home,\n 'Library',\n 'LaunchAgents',\n 'com.stackmemory.guardian.plist'\n ),\n logDir: path.join(home, '.stackmemory', 'logs'),\n };\n } else if (platform === 'linux') {\n return {\n platform: 'linux',\n serviceDir: path.join(home, '.config', 'systemd', 'user'),\n serviceName: 'stackmemory-guardian',\n serviceFile: path.join(\n home,\n '.config',\n 'systemd',\n 'user',\n 'stackmemory-guardian.service'\n ),\n logDir: path.join(home, '.stackmemory', 'logs'),\n };\n }\n\n return {\n platform: 'unsupported',\n serviceDir: '',\n serviceName: '',\n serviceFile: '',\n logDir: path.join(home, '.stackmemory', 'logs'),\n };\n}\n\nfunction _getStackMemoryBinPath(): string {\n const localBin = path.join(process.cwd(), 'dist', 'cli', 'index.js');\n if (existsSync(localBin)) {\n return localBin;\n }\n const globalBin = path.join(\n process.env.HOME || '',\n '.stackmemory',\n 'bin',\n 'stackmemory'\n );\n if (existsSync(globalBin)) {\n return globalBin;\n }\n return 'npx stackmemory';\n}\nvoid _getStackMemoryBinPath;\n\nfunction getNodePath(): string {\n try {\n const nodePath = execSync('which node', { encoding: 'utf-8' }).trim();\n return nodePath;\n } catch {\n return '/usr/local/bin/node';\n }\n}\n\nfunction generateMacOSPlist(config: ServiceConfig): string {\n const home = process.env.HOME || '';\n const nodePath = getNodePath();\n const guardianScript = path.join(home, '.stackmemory', 'guardian.js');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${config.serviceName}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${nodePath}</string>\n <string>${guardianScript}</string>\n </array>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n\n <key>WorkingDirectory</key>\n <string>${home}/.stackmemory</string>\n\n <key>StandardOutPath</key>\n <string>${config.logDir}/guardian.log</string>\n\n <key>StandardErrorPath</key>\n <string>${config.logDir}/guardian.error.log</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n <key>HOME</key>\n <string>${home}</string>\n <key>PATH</key>\n <string>/usr/local/bin:/usr/bin:/bin</string>\n </dict>\n\n <key>ThrottleInterval</key>\n <integer>30</integer>\n</dict>\n</plist>`;\n}\n\nfunction generateLinuxSystemdService(config: ServiceConfig): string {\n const home = process.env.HOME || '';\n const nodePath = getNodePath();\n const guardianScript = path.join(home, '.stackmemory', 'guardian.js');\n\n return `[Unit]\nDescription=StackMemory Guardian Service\nDocumentation=https://github.com/stackmemoryai/stackmemory\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=${nodePath} ${guardianScript}\nRestart=on-failure\nRestartSec=30\nWorkingDirectory=${home}/.stackmemory\n\nEnvironment=HOME=${home}\nEnvironment=PATH=/usr/local/bin:/usr/bin:/bin\n\nStandardOutput=append:${config.logDir}/guardian.log\nStandardError=append:${config.logDir}/guardian.error.log\n\n[Install]\nWantedBy=default.target`;\n}\n\nfunction generateGuardianScript(): string {\n return `#!/usr/bin/env node\n/**\n * StackMemory Guardian Service\n * Monitors ~/.stackmemory/sessions/ for active sessions\n * and manages context sync accordingly.\n */\n\nconst fs = require('fs');\nconst path = require('path');\nconst { spawn } = require('child_process');\n\nconst HOME = process.env.HOME || '';\nconst SESSIONS_DIR = path.join(HOME, '.stackmemory', 'sessions');\nconst STATE_FILE = path.join(HOME, '.stackmemory', 'guardian.state');\nconst IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\nclass Guardian {\n constructor() {\n this.syncProcess = null;\n this.lastActivityTime = Date.now();\n this.activeSessions = new Set();\n this.checkInterval = null;\n }\n\n log(message, level = 'INFO') {\n const timestamp = new Date().toISOString();\n console.log('[' + timestamp + '] [' + level + '] ' + message);\n }\n\n async getActiveSessions() {\n const sessions = new Set();\n\n try {\n if (!fs.existsSync(SESSIONS_DIR)) {\n return sessions;\n }\n\n const files = fs.readdirSync(SESSIONS_DIR);\n\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n\n const filePath = path.join(SESSIONS_DIR, file);\n try {\n const content = fs.readFileSync(filePath, 'utf8');\n const session = JSON.parse(content);\n\n // Check if session is active (updated within last 5 minutes)\n const lastUpdate = new Date(session.lastActiveAt || session.startedAt).getTime();\n const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);\n\n if (session.state === 'active' && lastUpdate > fiveMinutesAgo) {\n sessions.add(session.sessionId);\n }\n } catch (err) {\n // Skip invalid session files\n }\n }\n } catch (err) {\n this.log('Error reading sessions: ' + err.message, 'ERROR');\n }\n\n return sessions;\n }\n\n startContextSync() {\n if (this.syncProcess) {\n this.log('Context sync already running');\n return;\n }\n\n this.log('Starting context sync...');\n\n // Find stackmemory binary\n const stackmemoryPaths = [\n path.join(HOME, '.stackmemory', 'bin', 'stackmemory'),\n 'npx'\n ];\n\n let binPath = null;\n for (const p of stackmemoryPaths) {\n if (p === 'npx' || fs.existsSync(p)) {\n binPath = p;\n break;\n }\n }\n\n if (!binPath) {\n this.log('Cannot find stackmemory binary', 'ERROR');\n return;\n }\n\n const args = binPath === 'npx'\n ? ['stackmemory', 'monitor', '--daemon']\n : ['monitor', '--daemon'];\n\n this.syncProcess = spawn(binPath, args, {\n detached: true,\n stdio: ['ignore', 'pipe', 'pipe']\n });\n\n this.syncProcess.stdout.on('data', (data) => {\n this.log('sync: ' + data.toString().trim());\n });\n\n this.syncProcess.stderr.on('data', (data) => {\n this.log('sync error: ' + data.toString().trim(), 'WARN');\n });\n\n this.syncProcess.on('exit', (code) => {\n this.log('Context sync exited with code: ' + code);\n this.syncProcess = null;\n });\n\n this.log('Context sync started');\n }\n\n stopContextSync() {\n if (!this.syncProcess) {\n return;\n }\n\n this.log('Stopping context sync...');\n\n try {\n this.syncProcess.kill('SIGTERM');\n this.syncProcess = null;\n this.log('Context sync stopped');\n } catch (err) {\n this.log('Error stopping sync: ' + err.message, 'ERROR');\n }\n }\n\n saveState() {\n const state = {\n lastCheck: new Date().toISOString(),\n activeSessions: Array.from(this.activeSessions),\n syncRunning: this.syncProcess !== null,\n lastActivity: new Date(this.lastActivityTime).toISOString()\n };\n\n try {\n fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));\n } catch (err) {\n this.log('Error saving state: ' + err.message, 'ERROR');\n }\n }\n\n async check() {\n const currentSessions = await this.getActiveSessions();\n const hadActivity = currentSessions.size > 0;\n\n if (hadActivity) {\n this.lastActivityTime = Date.now();\n }\n\n // Detect session changes\n const newSessions = [...currentSessions].filter(s => !this.activeSessions.has(s));\n const closedSessions = [...this.activeSessions].filter(s => !currentSessions.has(s));\n\n if (newSessions.length > 0) {\n this.log('New sessions detected: ' + newSessions.join(', '));\n if (!this.syncProcess) {\n this.startContextSync();\n }\n }\n\n if (closedSessions.length > 0) {\n this.log('Sessions closed: ' + closedSessions.join(', '));\n }\n\n this.activeSessions = currentSessions;\n\n // Check idle timeout\n const idleTime = Date.now() - this.lastActivityTime;\n if (this.syncProcess && currentSessions.size === 0 && idleTime > IDLE_TIMEOUT_MS) {\n this.log('No activity for 30 minutes, stopping sync');\n this.stopContextSync();\n }\n\n this.saveState();\n }\n\n async start() {\n this.log('StackMemory Guardian starting...');\n this.log('Monitoring: ' + SESSIONS_DIR);\n\n // Ensure directories exist\n const dirs = [\n SESSIONS_DIR,\n path.join(HOME, '.stackmemory', 'logs')\n ];\n\n for (const dir of dirs) {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n }\n\n // Initial check\n await this.check();\n\n // Start monitoring loop (every 30 seconds)\n this.checkInterval = setInterval(() => this.check(), 30 * 1000);\n\n this.log('Guardian started successfully');\n\n // Handle shutdown signals\n process.on('SIGTERM', () => this.stop());\n process.on('SIGINT', () => this.stop());\n }\n\n stop() {\n this.log('Guardian stopping...');\n\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n }\n\n this.stopContextSync();\n\n // Clean up state file\n try {\n if (fs.existsSync(STATE_FILE)) {\n fs.unlinkSync(STATE_FILE);\n }\n } catch (err) {\n // Ignore\n }\n\n this.log('Guardian stopped');\n process.exit(0);\n }\n}\n\nconst guardian = new Guardian();\nguardian.start().catch(err => {\n console.error('Guardian failed to start:', err);\n process.exit(1);\n});\n`;\n}\n\nasync function installService(\n config: ServiceConfig,\n spinner: ora.Ora\n): Promise<void> {\n const home = process.env.HOME || '';\n\n // Create directories\n await fs.mkdir(config.serviceDir, { recursive: true });\n await fs.mkdir(config.logDir, { recursive: true });\n\n // Write guardian script\n const guardianPath = path.join(home, '.stackmemory', 'guardian.js');\n await fs.writeFile(guardianPath, generateGuardianScript(), 'utf-8');\n await fs.chmod(guardianPath, 0o755);\n\n if (config.platform === 'darwin') {\n // Write launchd plist\n const plistContent = generateMacOSPlist(config);\n await fs.writeFile(config.serviceFile, plistContent, 'utf-8');\n\n spinner.text = 'Loading service...';\n\n // Load the service\n try {\n execSync(`launchctl load -w \"${config.serviceFile}\"`, { stdio: 'pipe' });\n } catch {\n // Service might already be loaded, try unload first\n try {\n execSync(`launchctl unload \"${config.serviceFile}\"`, { stdio: 'pipe' });\n execSync(`launchctl load -w \"${config.serviceFile}\"`, {\n stdio: 'pipe',\n });\n } catch {\n throw new Error('Failed to load launchd service');\n }\n }\n\n spinner.succeed(chalk.green('Guardian service installed and started'));\n console.log(chalk.gray(`Service file: ${config.serviceFile}`));\n console.log(chalk.gray(`Guardian script: ${guardianPath}`));\n console.log(chalk.gray(`Logs: ${config.logDir}/guardian.log`));\n } else if (config.platform === 'linux') {\n // Write systemd service\n const serviceContent = generateLinuxSystemdService(config);\n await fs.writeFile(config.serviceFile, serviceContent, 'utf-8');\n\n spinner.text = 'Enabling service...';\n\n // Reload systemd and enable service\n try {\n execSync('systemctl --user daemon-reload', { stdio: 'pipe' });\n execSync(`systemctl --user enable ${config.serviceName}`, {\n stdio: 'pipe',\n });\n execSync(`systemctl --user start ${config.serviceName}`, {\n stdio: 'pipe',\n });\n } catch {\n throw new Error(\n 'Failed to enable systemd service. Make sure systemd user session is available.'\n );\n }\n\n spinner.succeed(chalk.green('Guardian service installed and started'));\n console.log(chalk.gray(`Service file: ${config.serviceFile}`));\n console.log(chalk.gray(`Guardian script: ${guardianPath}`));\n console.log(chalk.gray(`Logs: ${config.logDir}/guardian.log`));\n }\n}\n\nasync function uninstallService(\n config: ServiceConfig,\n spinner: ora.Ora\n): Promise<void> {\n const home = process.env.HOME || '';\n const guardianPath = path.join(home, '.stackmemory', 'guardian.js');\n\n if (config.platform === 'darwin') {\n spinner.text = 'Unloading service...';\n\n try {\n execSync(`launchctl unload \"${config.serviceFile}\"`, { stdio: 'pipe' });\n } catch {\n // Service might not be loaded\n }\n\n // Remove plist file\n try {\n await fs.unlink(config.serviceFile);\n } catch {\n // File might not exist\n }\n\n // Remove guardian script\n try {\n await fs.unlink(guardianPath);\n } catch {\n // File might not exist\n }\n\n spinner.succeed(chalk.green('Guardian service uninstalled'));\n } else if (config.platform === 'linux') {\n spinner.text = 'Stopping service...';\n\n try {\n execSync(`systemctl --user stop ${config.serviceName}`, {\n stdio: 'pipe',\n });\n execSync(`systemctl --user disable ${config.serviceName}`, {\n stdio: 'pipe',\n });\n } catch {\n // Service might not be running\n }\n\n // Remove service file\n try {\n await fs.unlink(config.serviceFile);\n } catch {\n // File might not exist\n }\n\n // Remove guardian script\n try {\n await fs.unlink(guardianPath);\n } catch {\n // File might not exist\n }\n\n // Reload systemd\n try {\n execSync('systemctl --user daemon-reload', { stdio: 'pipe' });\n } catch {\n // Ignore\n }\n\n spinner.succeed(chalk.green('Guardian service uninstalled'));\n }\n}\n\nasync function showServiceStatus(config: ServiceConfig): Promise<void> {\n const home = process.env.HOME || '';\n const stateFile = path.join(home, '.stackmemory', 'guardian.state');\n\n console.log(chalk.bold('\\nStackMemory Guardian Service Status\\n'));\n\n if (config.platform === 'unsupported') {\n console.log(chalk.red('Platform not supported for service installation'));\n console.log(\n chalk.gray('Supported platforms: macOS (launchd), Linux (systemd)')\n );\n return;\n }\n\n // Check if service file exists\n if (!existsSync(config.serviceFile)) {\n console.log(chalk.yellow('Service not installed'));\n console.log(chalk.gray('Install with: stackmemory service install'));\n return;\n }\n\n let isRunning = false;\n let serviceOutput = '';\n\n if (config.platform === 'darwin') {\n try {\n serviceOutput = execSync(`launchctl list | grep ${config.serviceName}`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n isRunning = serviceOutput.includes(config.serviceName);\n } catch {\n isRunning = false;\n }\n } else if (config.platform === 'linux') {\n try {\n serviceOutput = execSync(\n `systemctl --user is-active ${config.serviceName}`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }\n ).trim();\n isRunning = serviceOutput === 'active';\n } catch {\n isRunning = false;\n }\n }\n\n if (isRunning) {\n console.log(chalk.green('Status: Running'));\n } else {\n console.log(chalk.yellow('Status: Stopped'));\n }\n\n console.log(chalk.gray(`Platform: ${config.platform}`));\n console.log(chalk.gray(`Service: ${config.serviceName}`));\n console.log(chalk.gray(`Config: ${config.serviceFile}`));\n\n // Try to read guardian state\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'));\n console.log(chalk.bold('\\nGuardian State:'));\n console.log(` Last check: ${state.lastCheck}`);\n console.log(` Active sessions: ${state.activeSessions?.length || 0}`);\n console.log(` Sync running: ${state.syncRunning ? 'Yes' : 'No'}`);\n console.log(` Last activity: ${state.lastActivity}`);\n } catch {\n // Invalid state file\n }\n }\n}\n\nasync function showServiceLogs(\n config: ServiceConfig,\n lines: number\n): Promise<void> {\n console.log(\n chalk.bold(`\\nStackMemory Guardian Logs (last ${lines} lines)\\n`)\n );\n\n const logFile = path.join(config.logDir, 'guardian.log');\n\n if (!existsSync(logFile)) {\n console.log(chalk.yellow('No logs found'));\n console.log(chalk.gray(`Expected at: ${logFile}`));\n return;\n }\n\n try {\n const content = readFileSync(logFile, 'utf-8');\n const logLines = content.split('\\n').filter(Boolean);\n const lastLines = logLines.slice(-lines);\n\n lastLines.forEach((line) => {\n if (line.includes('[ERROR]')) {\n console.log(chalk.red(line));\n } else if (line.includes('[WARN]')) {\n console.log(chalk.yellow(line));\n } else {\n console.log(chalk.gray(line));\n }\n });\n\n console.log(chalk.gray(`\\nFull log: ${logFile}`));\n } catch (err) {\n console.log(chalk.red(`Failed to read logs: ${(err as Error).message}`));\n }\n}\n\nexport function createServiceCommand(): Command {\n const cmd = new Command('service')\n .description('Manage StackMemory guardian OS service (auto-start on login)')\n .addHelpText(\n 'after',\n `\nExamples:\n stackmemory service install Install and start the guardian service\n stackmemory service uninstall Remove the guardian service\n stackmemory service status Show service status\n stackmemory service logs Show recent service logs\n stackmemory service logs -n 50 Show last 50 log lines\n\nThe guardian service:\n - Monitors ~/.stackmemory/sessions/ for active sessions\n - Starts context sync when an active session is detected\n - Stops gracefully after 30 minutes of inactivity\n - Runs automatically on system login (opt-in)\n`\n );\n\n cmd\n .command('install')\n .description('Install the guardian service (starts on login)')\n .action(async () => {\n const spinner = ora('Installing guardian service...').start();\n\n try {\n const config = getServiceConfig();\n\n if (config.platform === 'unsupported') {\n spinner.fail(chalk.red('Platform not supported'));\n console.log(\n chalk.gray('Supported: macOS (launchd), Linux (systemd)')\n );\n process.exit(1);\n }\n\n await installService(config, spinner);\n\n console.log(chalk.bold('\\nGuardian service will:'));\n console.log(' - Start automatically on login');\n console.log(' - Monitor for active StackMemory sessions');\n console.log(' - Manage context sync based on activity');\n console.log(' - Stop gracefully after 30 min idle');\n } catch (err) {\n spinner.fail(\n chalk.red(`Installation failed: ${(err as Error).message}`)\n );\n process.exit(1);\n }\n });\n\n cmd\n .command('uninstall')\n .description('Remove the guardian service')\n .action(async () => {\n const spinner = ora('Uninstalling guardian service...').start();\n\n try {\n const config = getServiceConfig();\n\n if (config.platform === 'unsupported') {\n spinner.fail(chalk.red('Platform not supported'));\n process.exit(1);\n }\n\n await uninstallService(config, spinner);\n } catch (err) {\n spinner.fail(\n chalk.red(`Uninstallation failed: ${(err as Error).message}`)\n );\n process.exit(1);\n }\n });\n\n cmd\n .command('status')\n .description('Show guardian service status')\n .action(async () => {\n try {\n const config = getServiceConfig();\n await showServiceStatus(config);\n } catch (err) {\n console.error(\n chalk.red(`Status check failed: ${(err as Error).message}`)\n );\n process.exit(1);\n }\n });\n\n cmd\n .command('logs')\n .description('Show recent guardian service logs')\n .option('-n, --lines <number>', 'Number of log lines to show', '20')\n .option('-f, --follow', 'Follow log output (tail -f style)')\n .action(async (options) => {\n try {\n const config = getServiceConfig();\n const lines = parseInt(options.lines) || 20;\n\n if (options.follow) {\n // Use tail -f for live following\n const logFile = path.join(config.logDir, 'guardian.log');\n console.log(chalk.bold(`Following ${logFile} (Ctrl+C to stop)\\n`));\n\n const tail = spawn('tail', ['-f', '-n', lines.toString(), logFile], {\n stdio: 'inherit',\n });\n\n process.on('SIGINT', () => {\n tail.kill();\n process.exit(0);\n });\n } else {\n await showServiceLogs(config, lines);\n }\n } catch (err) {\n console.error(\n chalk.red(`Failed to show logs: ${(err as Error).message}`)\n );\n process.exit(1);\n }\n });\n\n // Default action - show status\n cmd.action(async () => {\n try {\n const config = getServiceConfig();\n await showServiceStatus(config);\n } catch (err) {\n console.error(\n chalk.red(`Status check failed: ${(err as Error).message}`)\n );\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nexport default createServiceCommand();\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,311 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { existsSync, readFileSync } from "fs";
5
+ import { join } from "path";
6
+ import { spawn, execSync } from "child_process";
7
+ function findPythonScript() {
8
+ const locations = [
9
+ join(
10
+ process.cwd(),
11
+ "packages",
12
+ "sweep-addon",
13
+ "python",
14
+ "sweep_predict.py"
15
+ ),
16
+ join(
17
+ process.cwd(),
18
+ "node_modules",
19
+ "@stackmemory",
20
+ "sweep-addon",
21
+ "python",
22
+ "sweep_predict.py"
23
+ ),
24
+ join(process.env.HOME || "", ".stackmemory", "sweep", "sweep_predict.py")
25
+ ];
26
+ for (const loc of locations) {
27
+ if (existsSync(loc)) {
28
+ return loc;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ async function findPython() {
34
+ const candidates = ["python3", "python"];
35
+ for (const cmd of candidates) {
36
+ try {
37
+ execSync(`${cmd} --version`, { stdio: "pipe" });
38
+ return cmd;
39
+ } catch {
40
+ continue;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ async function checkSweepStatus() {
46
+ const pythonPath = await findPython();
47
+ if (!pythonPath) {
48
+ return {
49
+ installed: false,
50
+ model_downloaded: false,
51
+ error: "Python not found. Install Python 3.10+"
52
+ };
53
+ }
54
+ const scriptPath = findPythonScript();
55
+ if (!scriptPath) {
56
+ return {
57
+ installed: false,
58
+ model_downloaded: false,
59
+ python_path: pythonPath,
60
+ error: "Sweep addon not installed. Run: stackmemory sweep setup"
61
+ };
62
+ }
63
+ const homeDir = process.env.HOME || "";
64
+ const modelPath = join(
65
+ homeDir,
66
+ ".stackmemory",
67
+ "models",
68
+ "sweep",
69
+ "sweep-next-edit-1.5b.q8_0.v2.gguf"
70
+ );
71
+ const modelDownloaded = existsSync(modelPath);
72
+ return {
73
+ installed: true,
74
+ model_downloaded: modelDownloaded,
75
+ python_path: pythonPath,
76
+ model_path: modelDownloaded ? modelPath : void 0
77
+ };
78
+ }
79
+ async function runPrediction(filePath, pythonPath, scriptPath) {
80
+ if (!existsSync(filePath)) {
81
+ return {
82
+ success: false,
83
+ error: "file_not_found",
84
+ message: `File not found: ${filePath}`
85
+ };
86
+ }
87
+ const currentContent = readFileSync(filePath, "utf-8");
88
+ const input = {
89
+ file_path: filePath,
90
+ current_content: currentContent
91
+ };
92
+ return new Promise((resolve) => {
93
+ const proc = spawn(pythonPath, [scriptPath], {
94
+ stdio: ["pipe", "pipe", "pipe"]
95
+ });
96
+ let stdout = "";
97
+ let stderr = "";
98
+ proc.stdout.on("data", (data) => stdout += data);
99
+ proc.stderr.on("data", (data) => stderr += data);
100
+ proc.on("close", (code) => {
101
+ try {
102
+ if (stdout.trim()) {
103
+ const result = JSON.parse(stdout.trim());
104
+ resolve(result);
105
+ } else if (code !== 0) {
106
+ resolve({
107
+ success: false,
108
+ error: "process_error",
109
+ message: stderr || `Process exited with code ${code}`
110
+ });
111
+ } else {
112
+ resolve({
113
+ success: false,
114
+ error: "no_output",
115
+ message: "No output from prediction script"
116
+ });
117
+ }
118
+ } catch {
119
+ resolve({
120
+ success: false,
121
+ error: "parse_error",
122
+ message: `Failed to parse output: ${stdout}`
123
+ });
124
+ }
125
+ });
126
+ proc.on("error", (error) => {
127
+ resolve({
128
+ success: false,
129
+ error: "spawn_error",
130
+ message: error.message
131
+ });
132
+ });
133
+ proc.stdin.write(JSON.stringify(input));
134
+ proc.stdin.end();
135
+ });
136
+ }
137
+ function createSweepCommand() {
138
+ const cmd = new Command("sweep").description(
139
+ "Next-edit predictions using Sweep 1.5B model (optional addon)"
140
+ ).addHelpText(
141
+ "after",
142
+ `
143
+ Examples:
144
+ stackmemory sweep setup Install Python dependencies
145
+ stackmemory sweep setup --download Also download the model (1.5GB)
146
+ stackmemory sweep status Check addon status
147
+ stackmemory sweep predict src/app.ts Predict next edit for a file
148
+
149
+ Requirements:
150
+ - Python 3.10+
151
+ - pip packages: huggingface_hub, llama-cpp-python
152
+
153
+ The Sweep 1.5B model predicts what code changes you'll make next based on:
154
+ - Current file content
155
+ - Recent changes (diffs)
156
+ - Context from other files
157
+
158
+ Model is downloaded from HuggingFace on first prediction (~1.5GB).
159
+ `
160
+ );
161
+ cmd.command("setup").description("Install Python dependencies for Sweep addon").option("--download", "Also download the model now").action(async (options) => {
162
+ const spinner = ora("Checking Python...").start();
163
+ const pythonPath = await findPython();
164
+ if (!pythonPath) {
165
+ spinner.fail(chalk.red("Python not found"));
166
+ console.log(chalk.gray("Please install Python 3.10+"));
167
+ process.exit(1);
168
+ }
169
+ spinner.text = "Installing Python dependencies...";
170
+ try {
171
+ execSync(
172
+ `${pythonPath} -m pip install --quiet huggingface_hub llama-cpp-python`,
173
+ {
174
+ stdio: "pipe"
175
+ }
176
+ );
177
+ spinner.succeed(chalk.green("Python dependencies installed"));
178
+ } catch {
179
+ spinner.fail(chalk.red("Failed to install dependencies"));
180
+ console.log(
181
+ chalk.gray(
182
+ `Run: ${pythonPath} -m pip install huggingface_hub llama-cpp-python`
183
+ )
184
+ );
185
+ process.exit(1);
186
+ }
187
+ if (options.download) {
188
+ const downloadSpinner = ora("Downloading Sweep 1.5B model...").start();
189
+ downloadSpinner.text = "Downloading model from HuggingFace (~1.5GB)...";
190
+ try {
191
+ execSync(
192
+ `${pythonPath} -c "
193
+ from huggingface_hub import hf_hub_download
194
+ import os
195
+ model_dir = os.path.expanduser('~/.stackmemory/models/sweep')
196
+ os.makedirs(model_dir, exist_ok=True)
197
+ hf_hub_download(
198
+ repo_id='sweepai/sweep-next-edit-1.5B',
199
+ filename='sweep-next-edit-1.5b.q8_0.v2.gguf',
200
+ repo_type='model',
201
+ local_dir=model_dir,
202
+ local_dir_use_symlinks=False
203
+ )
204
+ "`,
205
+ { stdio: "pipe", timeout: 6e5 }
206
+ );
207
+ downloadSpinner.succeed(chalk.green("Model downloaded"));
208
+ } catch {
209
+ downloadSpinner.fail(chalk.red("Model download failed"));
210
+ console.log(chalk.gray("Model will be downloaded on first use"));
211
+ }
212
+ } else {
213
+ console.log(
214
+ chalk.gray("\nModel will be downloaded on first prediction (~1.5GB)")
215
+ );
216
+ console.log(chalk.gray("Or run: stackmemory sweep setup --download"));
217
+ }
218
+ console.log(chalk.bold("\nSetup complete!"));
219
+ });
220
+ cmd.command("status").description("Check Sweep addon status").action(async () => {
221
+ console.log(chalk.bold("\nSweep 1.5B Addon Status\n"));
222
+ const status = await checkSweepStatus();
223
+ if (status.error) {
224
+ console.log(chalk.red(`Error: ${status.error}`));
225
+ console.log("");
226
+ }
227
+ console.log(
228
+ `Python: ${status.python_path ? chalk.green(status.python_path) : chalk.red("Not found")}`
229
+ );
230
+ console.log(
231
+ `Addon installed: ${status.installed ? chalk.green("Yes") : chalk.yellow("No")}`
232
+ );
233
+ console.log(
234
+ `Model downloaded: ${status.model_downloaded ? chalk.green("Yes") : chalk.yellow("No (will download on first use)")}`
235
+ );
236
+ if (status.model_path) {
237
+ console.log(chalk.gray(`Model path: ${status.model_path}`));
238
+ }
239
+ if (!status.installed) {
240
+ console.log(chalk.bold("\nTo install:"));
241
+ console.log(" stackmemory sweep setup");
242
+ }
243
+ });
244
+ cmd.command("predict <file>").description("Predict next edit for a file").option("-o, --output <path>", "Write prediction to file instead of stdout").option("--json", "Output raw JSON result").action(async (file, options) => {
245
+ const status = await checkSweepStatus();
246
+ if (!status.installed) {
247
+ console.error(chalk.red("Sweep addon not installed"));
248
+ console.log(chalk.gray("Run: stackmemory sweep setup"));
249
+ process.exit(1);
250
+ }
251
+ const scriptPath = findPythonScript();
252
+ if (!scriptPath || !status.python_path) {
253
+ console.error(chalk.red("Could not find Sweep prediction script"));
254
+ process.exit(1);
255
+ }
256
+ const spinner = ora("Running prediction...").start();
257
+ if (!status.model_downloaded) {
258
+ spinner.text = "Downloading model (first time only, ~1.5GB)...";
259
+ }
260
+ const result = await runPrediction(file, status.python_path, scriptPath);
261
+ if (!result.success) {
262
+ spinner.fail(
263
+ chalk.red(`Prediction failed: ${result.message || result.error}`)
264
+ );
265
+ process.exit(1);
266
+ }
267
+ spinner.succeed(chalk.green("Prediction complete"));
268
+ if (options.json) {
269
+ console.log(JSON.stringify(result, null, 2));
270
+ return;
271
+ }
272
+ console.log(chalk.bold("\nPredicted content:"));
273
+ console.log(chalk.gray("\u2500".repeat(50)));
274
+ console.log(result.predicted_content);
275
+ console.log(chalk.gray("\u2500".repeat(50)));
276
+ if (result.latency_ms) {
277
+ console.log(chalk.gray(`Latency: ${result.latency_ms}ms`));
278
+ }
279
+ if (result.tokens_generated) {
280
+ console.log(chalk.gray(`Tokens: ${result.tokens_generated}`));
281
+ }
282
+ if (options.output) {
283
+ const { writeFileSync } = await import("fs");
284
+ writeFileSync(options.output, result.predicted_content || "");
285
+ console.log(chalk.green(`
286
+ Written to: ${options.output}`));
287
+ }
288
+ });
289
+ cmd.action(async () => {
290
+ const status = await checkSweepStatus();
291
+ console.log(chalk.bold("\nSweep 1.5B Addon Status\n"));
292
+ console.log(
293
+ `Installed: ${status.installed ? chalk.green("Yes") : chalk.yellow("No")}`
294
+ );
295
+ console.log(
296
+ `Model ready: ${status.model_downloaded ? chalk.green("Yes") : chalk.yellow("No")}`
297
+ );
298
+ if (!status.installed) {
299
+ console.log(chalk.bold("\nRun: stackmemory sweep setup"));
300
+ } else {
301
+ console.log(chalk.bold("\nUsage: stackmemory sweep predict <file>"));
302
+ }
303
+ });
304
+ return cmd;
305
+ }
306
+ var sweep_default = createSweepCommand();
307
+ export {
308
+ createSweepCommand,
309
+ sweep_default as default
310
+ };
311
+ //# sourceMappingURL=sweep.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/cli/commands/sweep.ts"],
4
+ "sourcesContent": ["/**\n * Sweep command for StackMemory\n * Provides next-edit predictions using the Sweep 1.5B model\n *\n * Usage:\n * stackmemory sweep setup Install dependencies and optionally download model\n * stackmemory sweep status Check if Sweep addon is properly configured\n * stackmemory sweep predict <file> Run prediction on a file\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { spawn, execSync } from 'child_process';\n\ninterface SweepStatus {\n installed: boolean;\n model_downloaded: boolean;\n python_path?: string;\n model_path?: string;\n error?: string;\n}\n\ninterface SweepPredictResult {\n success: boolean;\n predicted_content?: string;\n file_path?: string;\n latency_ms?: number;\n tokens_generated?: number;\n error?: string;\n message?: string;\n}\n\nfunction findPythonScript(): string | null {\n const locations = [\n join(\n process.cwd(),\n 'packages',\n 'sweep-addon',\n 'python',\n 'sweep_predict.py'\n ),\n join(\n process.cwd(),\n 'node_modules',\n '@stackmemory',\n 'sweep-addon',\n 'python',\n 'sweep_predict.py'\n ),\n join(process.env.HOME || '', '.stackmemory', 'sweep', 'sweep_predict.py'),\n ];\n\n for (const loc of locations) {\n if (existsSync(loc)) {\n return loc;\n }\n }\n return null;\n}\n\nasync function findPython(): Promise<string | null> {\n const candidates = ['python3', 'python'];\n\n for (const cmd of candidates) {\n try {\n execSync(`${cmd} --version`, { stdio: 'pipe' });\n return cmd;\n } catch {\n continue;\n }\n }\n return null;\n}\n\nasync function checkSweepStatus(): Promise<SweepStatus> {\n const pythonPath = await findPython();\n if (!pythonPath) {\n return {\n installed: false,\n model_downloaded: false,\n error: 'Python not found. Install Python 3.10+',\n };\n }\n\n const scriptPath = findPythonScript();\n if (!scriptPath) {\n return {\n installed: false,\n model_downloaded: false,\n python_path: pythonPath,\n error: 'Sweep addon not installed. Run: stackmemory sweep setup',\n };\n }\n\n const homeDir = process.env.HOME || '';\n const modelPath = join(\n homeDir,\n '.stackmemory',\n 'models',\n 'sweep',\n 'sweep-next-edit-1.5b.q8_0.v2.gguf'\n );\n const modelDownloaded = existsSync(modelPath);\n\n return {\n installed: true,\n model_downloaded: modelDownloaded,\n python_path: pythonPath,\n model_path: modelDownloaded ? modelPath : undefined,\n };\n}\n\nasync function runPrediction(\n filePath: string,\n pythonPath: string,\n scriptPath: string\n): Promise<SweepPredictResult> {\n if (!existsSync(filePath)) {\n return {\n success: false,\n error: 'file_not_found',\n message: `File not found: ${filePath}`,\n };\n }\n\n const currentContent = readFileSync(filePath, 'utf-8');\n\n const input = {\n file_path: filePath,\n current_content: currentContent,\n };\n\n return new Promise((resolve) => {\n const proc = spawn(pythonPath, [scriptPath], {\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout.on('data', (data) => (stdout += data));\n proc.stderr.on('data', (data) => (stderr += data));\n\n proc.on('close', (code) => {\n try {\n if (stdout.trim()) {\n const result = JSON.parse(stdout.trim());\n resolve(result);\n } else if (code !== 0) {\n resolve({\n success: false,\n error: 'process_error',\n message: stderr || `Process exited with code ${code}`,\n });\n } else {\n resolve({\n success: false,\n error: 'no_output',\n message: 'No output from prediction script',\n });\n }\n } catch {\n resolve({\n success: false,\n error: 'parse_error',\n message: `Failed to parse output: ${stdout}`,\n });\n }\n });\n\n proc.on('error', (error) => {\n resolve({\n success: false,\n error: 'spawn_error',\n message: error.message,\n });\n });\n\n proc.stdin.write(JSON.stringify(input));\n proc.stdin.end();\n });\n}\n\nexport function createSweepCommand(): Command {\n const cmd = new Command('sweep')\n .description(\n 'Next-edit predictions using Sweep 1.5B model (optional addon)'\n )\n .addHelpText(\n 'after',\n `\nExamples:\n stackmemory sweep setup Install Python dependencies\n stackmemory sweep setup --download Also download the model (1.5GB)\n stackmemory sweep status Check addon status\n stackmemory sweep predict src/app.ts Predict next edit for a file\n\nRequirements:\n - Python 3.10+\n - pip packages: huggingface_hub, llama-cpp-python\n\nThe Sweep 1.5B model predicts what code changes you'll make next based on:\n - Current file content\n - Recent changes (diffs)\n - Context from other files\n\nModel is downloaded from HuggingFace on first prediction (~1.5GB).\n`\n );\n\n cmd\n .command('setup')\n .description('Install Python dependencies for Sweep addon')\n .option('--download', 'Also download the model now')\n .action(async (options) => {\n const spinner = ora('Checking Python...').start();\n\n const pythonPath = await findPython();\n if (!pythonPath) {\n spinner.fail(chalk.red('Python not found'));\n console.log(chalk.gray('Please install Python 3.10+'));\n process.exit(1);\n }\n\n spinner.text = 'Installing Python dependencies...';\n\n try {\n execSync(\n `${pythonPath} -m pip install --quiet huggingface_hub llama-cpp-python`,\n {\n stdio: 'pipe',\n }\n );\n spinner.succeed(chalk.green('Python dependencies installed'));\n } catch {\n spinner.fail(chalk.red('Failed to install dependencies'));\n console.log(\n chalk.gray(\n `Run: ${pythonPath} -m pip install huggingface_hub llama-cpp-python`\n )\n );\n process.exit(1);\n }\n\n if (options.download) {\n const downloadSpinner = ora('Downloading Sweep 1.5B model...').start();\n downloadSpinner.text = 'Downloading model from HuggingFace (~1.5GB)...';\n\n try {\n execSync(\n `${pythonPath} -c \"\nfrom huggingface_hub import hf_hub_download\nimport os\nmodel_dir = os.path.expanduser('~/.stackmemory/models/sweep')\nos.makedirs(model_dir, exist_ok=True)\nhf_hub_download(\n repo_id='sweepai/sweep-next-edit-1.5B',\n filename='sweep-next-edit-1.5b.q8_0.v2.gguf',\n repo_type='model',\n local_dir=model_dir,\n local_dir_use_symlinks=False\n)\n\"`,\n { stdio: 'pipe', timeout: 600000 }\n );\n downloadSpinner.succeed(chalk.green('Model downloaded'));\n } catch {\n downloadSpinner.fail(chalk.red('Model download failed'));\n console.log(chalk.gray('Model will be downloaded on first use'));\n }\n } else {\n console.log(\n chalk.gray('\\nModel will be downloaded on first prediction (~1.5GB)')\n );\n console.log(chalk.gray('Or run: stackmemory sweep setup --download'));\n }\n\n console.log(chalk.bold('\\nSetup complete!'));\n });\n\n cmd\n .command('status')\n .description('Check Sweep addon status')\n .action(async () => {\n console.log(chalk.bold('\\nSweep 1.5B Addon Status\\n'));\n\n const status = await checkSweepStatus();\n\n if (status.error) {\n console.log(chalk.red(`Error: ${status.error}`));\n console.log('');\n }\n\n console.log(\n `Python: ${status.python_path ? chalk.green(status.python_path) : chalk.red('Not found')}`\n );\n console.log(\n `Addon installed: ${status.installed ? chalk.green('Yes') : chalk.yellow('No')}`\n );\n console.log(\n `Model downloaded: ${status.model_downloaded ? chalk.green('Yes') : chalk.yellow('No (will download on first use)')}`\n );\n\n if (status.model_path) {\n console.log(chalk.gray(`Model path: ${status.model_path}`));\n }\n\n if (!status.installed) {\n console.log(chalk.bold('\\nTo install:'));\n console.log(' stackmemory sweep setup');\n }\n });\n\n cmd\n .command('predict <file>')\n .description('Predict next edit for a file')\n .option('-o, --output <path>', 'Write prediction to file instead of stdout')\n .option('--json', 'Output raw JSON result')\n .action(async (file, options) => {\n const status = await checkSweepStatus();\n\n if (!status.installed) {\n console.error(chalk.red('Sweep addon not installed'));\n console.log(chalk.gray('Run: stackmemory sweep setup'));\n process.exit(1);\n }\n\n const scriptPath = findPythonScript();\n if (!scriptPath || !status.python_path) {\n console.error(chalk.red('Could not find Sweep prediction script'));\n process.exit(1);\n }\n\n const spinner = ora('Running prediction...').start();\n\n if (!status.model_downloaded) {\n spinner.text = 'Downloading model (first time only, ~1.5GB)...';\n }\n\n const result = await runPrediction(file, status.python_path, scriptPath);\n\n if (!result.success) {\n spinner.fail(\n chalk.red(`Prediction failed: ${result.message || result.error}`)\n );\n process.exit(1);\n }\n\n spinner.succeed(chalk.green('Prediction complete'));\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(chalk.bold('\\nPredicted content:'));\n console.log(chalk.gray('\u2500'.repeat(50)));\n console.log(result.predicted_content);\n console.log(chalk.gray('\u2500'.repeat(50)));\n\n if (result.latency_ms) {\n console.log(chalk.gray(`Latency: ${result.latency_ms}ms`));\n }\n if (result.tokens_generated) {\n console.log(chalk.gray(`Tokens: ${result.tokens_generated}`));\n }\n\n if (options.output) {\n const { writeFileSync } = await import('fs');\n writeFileSync(options.output, result.predicted_content || '');\n console.log(chalk.green(`\\nWritten to: ${options.output}`));\n }\n });\n\n cmd.action(async () => {\n const status = await checkSweepStatus();\n console.log(chalk.bold('\\nSweep 1.5B Addon Status\\n'));\n\n console.log(\n `Installed: ${status.installed ? chalk.green('Yes') : chalk.yellow('No')}`\n );\n console.log(\n `Model ready: ${status.model_downloaded ? chalk.green('Yes') : chalk.yellow('No')}`\n );\n\n if (!status.installed) {\n console.log(chalk.bold('\\nRun: stackmemory sweep setup'));\n } else {\n console.log(chalk.bold('\\nUsage: stackmemory sweep predict <file>'));\n }\n });\n\n return cmd;\n}\n\nexport default createSweepCommand();\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/cli/index.js CHANGED
@@ -21,6 +21,10 @@ import { createLogCommand } from "./commands/log.js";
21
21
  import { createContextCommands } from "./commands/context.js";
22
22
  import { createConfigCommand } from "./commands/config.js";
23
23
  import { createHandoffCommand } from "./commands/handoff.js";
24
+ import {
25
+ createDecisionCommand,
26
+ createMemoryCommand
27
+ } from "./commands/decision.js";
24
28
  import { createStorageCommand } from "./commands/storage.js";
25
29
  import { createSkillsCommand } from "./commands/skills.js";
26
30
  import { createTestCommand } from "./commands/test.js";
@@ -29,38 +33,124 @@ import createWorkflowCommand from "./commands/workflow.js";
29
33
  import monitorCommand from "./commands/monitor.js";
30
34
  import qualityCommand from "./commands/quality.js";
31
35
  import createRalphCommand from "./commands/ralph.js";
36
+ import serviceCommand from "./commands/service.js";
32
37
  import { registerLoginCommand } from "./commands/login.js";
33
38
  import { registerSignupCommand } from "./commands/signup.js";
34
39
  import { registerLogoutCommand, registerDbCommands } from "./commands/db.js";
40
+ import { createSweepCommand } from "./commands/sweep.js";
35
41
  import { ProjectManager } from "../core/projects/project-manager.js";
36
42
  import Database from "better-sqlite3";
37
43
  import { join } from "path";
38
44
  import { existsSync, mkdirSync } from "fs";
39
- const VERSION = "0.4.2";
45
+ import inquirer from "inquirer";
46
+ import chalk from "chalk";
47
+ import {
48
+ loadStorageConfig,
49
+ enableChromaDB,
50
+ getStorageModeDescription
51
+ } from "../core/config/storage-config.js";
52
+ const VERSION = "0.5.2";
40
53
  UpdateChecker.checkForUpdates(VERSION, true).catch(() => {
41
54
  });
42
55
  program.name("stackmemory").description(
43
56
  "Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention"
44
57
  ).version(VERSION);
45
- program.command("init").description("Initialize StackMemory in current project").action(async () => {
58
+ program.command("init").description(
59
+ `Initialize StackMemory in current project
60
+
61
+ Storage Modes:
62
+ SQLite (default): Local only, fast, no setup required
63
+ ChromaDB (hybrid): Adds semantic search and cloud backup, requires API key`
64
+ ).option("--sqlite", "Use SQLite-only storage (default, skip prompts)").option(
65
+ "--chromadb",
66
+ "Enable ChromaDB for semantic search (prompts for API key)"
67
+ ).option("--skip-storage-prompt", "Skip storage configuration prompt").action(async (options) => {
46
68
  try {
47
69
  const projectRoot = process.cwd();
48
70
  const dbDir = join(projectRoot, ".stackmemory");
49
71
  if (!existsSync(dbDir)) {
50
72
  mkdirSync(dbDir, { recursive: true });
51
73
  }
74
+ let storageConfig = loadStorageConfig();
75
+ const isFirstTimeSetup = !storageConfig.chromadb.enabled && storageConfig.mode === "sqlite";
76
+ if (options.sqlite || options.skipStoragePrompt) {
77
+ console.log(chalk.gray("Using SQLite-only storage mode."));
78
+ } else if (options.chromadb) {
79
+ await promptAndEnableChromaDB();
80
+ } else if (isFirstTimeSetup && process.stdin.isTTY) {
81
+ console.log(chalk.cyan("\nStorage Configuration"));
82
+ console.log(chalk.gray("StackMemory supports two storage modes:\n"));
83
+ console.log(chalk.white(" SQLite (default):"));
84
+ console.log(chalk.gray(" - Local storage only"));
85
+ console.log(chalk.gray(" - Fast and simple"));
86
+ console.log(chalk.gray(" - No external dependencies\n"));
87
+ console.log(chalk.white(" ChromaDB (hybrid):"));
88
+ console.log(chalk.gray(" - Semantic search across your context"));
89
+ console.log(chalk.gray(" - Cloud backup capability"));
90
+ console.log(chalk.gray(" - Requires ChromaDB API key\n"));
91
+ const { enableChroma } = await inquirer.prompt([
92
+ {
93
+ type: "confirm",
94
+ name: "enableChroma",
95
+ message: "Enable ChromaDB for semantic search? (requires API key)",
96
+ default: false
97
+ }
98
+ ]);
99
+ if (enableChroma) {
100
+ await promptAndEnableChromaDB();
101
+ } else {
102
+ console.log(chalk.gray("Using SQLite-only storage mode."));
103
+ }
104
+ }
52
105
  const dbPath = join(dbDir, "context.db");
53
106
  const db = new Database(dbPath);
54
107
  new FrameManager(db, "cli-project");
55
108
  logger.info("StackMemory initialized successfully", { projectRoot });
56
- console.log("\u2705 StackMemory initialized in", projectRoot);
109
+ console.log(
110
+ chalk.green("\n[OK] StackMemory initialized in"),
111
+ projectRoot
112
+ );
113
+ storageConfig = loadStorageConfig();
114
+ console.log(chalk.gray(`Storage mode: ${getStorageModeDescription()}`));
57
115
  db.close();
58
116
  } catch (error) {
59
117
  logger.error("Failed to initialize StackMemory", error);
60
- console.error("\u274C Initialization failed:", error.message);
118
+ console.error(
119
+ chalk.red("[ERROR] Initialization failed:"),
120
+ error.message
121
+ );
61
122
  process.exit(1);
62
123
  }
63
124
  });
125
+ async function promptAndEnableChromaDB() {
126
+ const answers = await inquirer.prompt([
127
+ {
128
+ type: "password",
129
+ name: "apiKey",
130
+ message: "Enter your ChromaDB API key:",
131
+ validate: (input) => {
132
+ if (!input || input.trim().length === 0) {
133
+ return "API key is required for ChromaDB";
134
+ }
135
+ return true;
136
+ }
137
+ },
138
+ {
139
+ type: "input",
140
+ name: "apiUrl",
141
+ message: "ChromaDB API URL (press Enter for default):",
142
+ default: "https://api.trychroma.com"
143
+ }
144
+ ]);
145
+ enableChromaDB({
146
+ apiKey: answers.apiKey,
147
+ apiUrl: answers.apiUrl
148
+ });
149
+ console.log(chalk.green("[OK] ChromaDB enabled for semantic search."));
150
+ console.log(
151
+ chalk.gray("API key saved to ~/.stackmemory/storage-config.json")
152
+ );
153
+ }
64
154
  program.command("status").description("Show current StackMemory status").option("--all", "Show all active frames across sessions").option("--project", "Show all active frames in current project").option("--session <id>", "Show frames for specific session").action(async (options) => {
65
155
  return trace.command("stackmemory-status", options, async () => {
66
156
  try {
@@ -323,6 +413,8 @@ program.addCommand(createLogCommand());
323
413
  program.addCommand(createContextCommands());
324
414
  program.addCommand(createConfigCommand());
325
415
  program.addCommand(createHandoffCommand());
416
+ program.addCommand(createDecisionCommand());
417
+ program.addCommand(createMemoryCommand());
326
418
  program.addCommand(createStorageCommand());
327
419
  program.addCommand(createSkillsCommand());
328
420
  program.addCommand(createTestCommand());
@@ -331,6 +423,8 @@ program.addCommand(createWorkflowCommand());
331
423
  program.addCommand(monitorCommand);
332
424
  program.addCommand(qualityCommand);
333
425
  program.addCommand(createRalphCommand());
426
+ program.addCommand(serviceCommand);
427
+ program.addCommand(createSweepCommand());
334
428
  program.command("dashboard").description("Display monitoring dashboard in terminal").option("-w, --watch", "Auto-refresh dashboard").option("-i, --interval <seconds>", "Refresh interval in seconds", "5").action(async (options) => {
335
429
  const { dashboardCommand } = await import("./commands/dashboard.js");
336
430
  await dashboardCommand.handler(options);