UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

221 lines (197 loc) • 6.99 kB
#!/usr/bin/env tsx /** * Initialize StackMemory in the current project */ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync, } from 'fs'; import { join } from 'path'; import { execSync } from 'child_process'; import chalk from 'chalk'; // Parse CLI args for --with-diffmem flag const enableDiffMem = process.argv.includes('--with-diffmem'); // Type-safe environment variable access function getEnv(key: string, defaultValue?: string): string { const value = process.env[key]; if (value === undefined) { if (defaultValue !== undefined) return defaultValue; throw new Error(`Environment variable ${key} is required`); } return value; } function getOptionalEnv(key: string): string | undefined { return process.env[key]; } const projectRoot = process.cwd(); console.log(chalk.blue.bold('\nšŸš€ Initializing StackMemory...\n')); // 1. Create .stackmemory directory const stackDir = join(projectRoot, '.stackmemory'); if (!existsSync(stackDir)) { mkdirSync(stackDir, { recursive: true }); console.log(chalk.green('āœ“') + ' Created .stackmemory directory'); } else { console.log(chalk.yellow('⚠') + ' .stackmemory directory already exists'); } // 2. Add to .gitignore const gitignorePath = join(projectRoot, '.gitignore'); const gitignoreEntry = '\n# StackMemory\n.stackmemory/*.db\n.stackmemory/*.db-*\n'; if (existsSync(gitignorePath)) { const gitignore = readFileSync(gitignorePath, 'utf-8'); if (!gitignore.includes('.stackmemory')) { appendFileSync(gitignorePath, gitignoreEntry); console.log(chalk.green('āœ“') + ' Added .stackmemory to .gitignore'); } } else { writeFileSync(gitignorePath, gitignoreEntry); console.log(chalk.green('āœ“') + ' Created .gitignore with .stackmemory'); } // 3. Create config file const configPath = join(stackDir, 'config.json'); if (!existsSync(configPath)) { const config = { projectId: projectRoot.split('/').pop(), userId: process.env['USER'] || 'default', teamId: 'local', initialized: new Date().toISOString(), }; writeFileSync(configPath, JSON.stringify(config, null, 2)); console.log(chalk.green('āœ“') + ' Created config file'); } // 4. Create initial JSONL file const jsonlPath = join(stackDir, 'frames.jsonl'); if (!existsSync(jsonlPath)) { const initialFrame = { id: 'init_' + Date.now(), type: 'system', content: 'StackMemory initialized', timestamp: Date.now(), }; writeFileSync(jsonlPath, JSON.stringify(initialFrame) + '\n'); console.log(chalk.green('āœ“') + ' Created frames.jsonl'); } // 5. Create MCP config for Claude Code const isMacOS = process.platform === 'darwin'; const mcpConfigPath = isMacOS ? join( process.env['HOME'] || '~', 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json' ) : join( process.env['HOME'] || '~', '.config', 'claude', 'claude_desktop_config.json' ); console.log(chalk.blue('\nšŸ“ MCP Configuration for Claude Code:\n')); const mcpConfig = { mcpServers: { stackmemory: { command: 'node', args: [join(projectRoot, 'dist', 'integrations', 'mcp', 'server.js')], env: { PROJECT_ROOT: projectRoot, }, }, }, }; console.log(chalk.gray('Add this to your Claude Code MCP configuration:')); console.log(chalk.gray('(' + mcpConfigPath + ')\n')); console.log(chalk.cyan(JSON.stringify(mcpConfig, null, 2))); // 6. Build the project console.log(chalk.blue('\nšŸ“¦ Building TypeScript files...\n')); try { execSync('npm run build', { stdio: 'inherit', cwd: projectRoot }); console.log(chalk.green('āœ“') + ' Build completed'); } catch { console.log(chalk.yellow('⚠') + ' Build failed - run npm run build manually'); } // 7. Optional: Initialize DiffMem for long-term user memory if (enableDiffMem) { console.log(chalk.blue('\n🧠 Setting up DiffMem integration...\n')); const diffmemDir = join(stackDir, 'diffmem'); const diffmemStorageDir = join(diffmemDir, 'storage'); const diffmemWorktreesDir = join(diffmemDir, 'worktrees'); // Create DiffMem directories mkdirSync(diffmemStorageDir, { recursive: true }); mkdirSync(diffmemWorktreesDir, { recursive: true }); // Initialize git repo for storage if (!existsSync(join(diffmemStorageDir, '.git'))) { try { execSync('git init', { cwd: diffmemStorageDir, stdio: 'pipe' }); execSync('git commit --allow-empty -m "Initialize DiffMem storage"', { cwd: diffmemStorageDir, stdio: 'pipe', }); console.log(chalk.green('āœ“') + ' Initialized DiffMem storage repository'); } catch { console.log( chalk.yellow('⚠') + ' Failed to initialize git repo for DiffMem' ); } } // Create DiffMem config const diffmemConfigPath = join(diffmemDir, 'config.json'); const diffmemConfig = { enabled: true, endpoint: 'http://localhost:8000', userId: process.env['USER'] || 'default', storagePath: diffmemStorageDir, worktreePath: diffmemWorktreesDir, }; writeFileSync(diffmemConfigPath, JSON.stringify(diffmemConfig, null, 2)); console.log(chalk.green('āœ“') + ' Created DiffMem configuration'); // Add DiffMem env vars to .env file const envPath = join(projectRoot, '.env'); const diffmemEnvVars = ` # DiffMem Configuration (Long-term User Memory) DIFFMEM_ENABLED=true DIFFMEM_ENDPOINT=http://localhost:8000 DIFFMEM_USER_ID=${process.env['USER'] || 'default'} DIFFMEM_STORAGE_PATH=${diffmemStorageDir} DIFFMEM_WORKTREE_PATH=${diffmemWorktreesDir} `; if (existsSync(envPath)) { const envContent = readFileSync(envPath, 'utf-8'); if (!envContent.includes('DIFFMEM_')) { appendFileSync(envPath, diffmemEnvVars); console.log(chalk.green('āœ“') + ' Added DiffMem configuration to .env'); } } else { writeFileSync(envPath, diffmemEnvVars.trim() + '\n'); console.log(chalk.green('āœ“') + ' Created .env with DiffMem configuration'); } console.log(chalk.green('āœ“') + ' DiffMem integration configured'); console.log( chalk.gray( ' Note: DiffMem server must be running for user memory features' ) ); } console.log(chalk.green.bold('\nāœ… StackMemory initialized successfully!\n')); console.log(chalk.gray('Next steps:')); console.log(chalk.gray('1. Add the MCP configuration above to Claude Code')); console.log(chalk.gray('2. Restart Claude Code')); console.log(chalk.gray('3. Start using context tracking!')); if (enableDiffMem) { console.log(chalk.gray('4. Start DiffMem server: npm run diffmem:start')); } console.log(chalk.gray('\nUseful commands:')); console.log( chalk.cyan(' npm run mcp:dev') + ' - Start MCP server in dev mode' ); console.log(chalk.cyan(' npm run status') + ' - Check StackMemory status'); console.log(chalk.cyan(' npm run analyze') + ' - Analyze context usage'); if (enableDiffMem) { console.log( chalk.cyan(' npm run diffmem:start') + ' - Start DiffMem server' ); } console.log();