#!/usr/bin/env node /** * Smart Install Script for claude-mem * * Ensures Bun runtime and uv (Python package manager) are installed * (auto-installs if missing) and handles dependency installation when needed. */ import { existsSync, readFileSync, writeFileSync } from 'fs'; import { execSync, spawnSync } from 'child_process'; import { join } from 'path'; import { homedir } from 'os'; const ROOT = join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack'); const MARKER = join(ROOT, '.install-version'); const IS_WINDOWS = process.platform === 'win32'; /** * Check if Bun is installed and accessible */ function isBunInstalled() { try { const result = spawnSync('bun', ['--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS }); return result.status === 0; } catch { return false; } } /** * Get Bun version if installed */ function getBunVersion() { try { const result = spawnSync('bun', ['--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS }); return result.status === 0 ? result.stdout.trim() : null; } catch { return null; } } /** * Check if uv is installed and accessible */ function isUvInstalled() { try { const result = spawnSync('uv', ['--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS }); return result.status === 0; } catch { return false; } } /** * Get uv version if installed */ function getUvVersion() { try { const result = spawnSync('uv', ['--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS }); return result.status === 0 ? result.stdout.trim() : null; } catch { return null; } } /** * Install Bun automatically based on platform */ function installBun() { console.error('🔧 Bun not found. Installing Bun runtime...'); try { if (IS_WINDOWS) { // Windows: Use PowerShell installer console.error(' Installing via PowerShell...'); execSync('powershell -c "irm bun.sh/install.ps1 | iex"', { stdio: 'inherit', shell: true }); } else { // Unix/macOS: Use curl installer console.error(' Installing via curl...'); execSync('curl -fsSL https://bun.sh/install | bash', { stdio: 'inherit', shell: true }); } // Verify installation if (isBunInstalled()) { const version = getBunVersion(); console.error(`✅ Bun ${version} installed successfully`); return true; } else { // Bun may be installed but not in PATH yet for this session // Try common installation paths const bunPaths = IS_WINDOWS ? [join(homedir(), '.bun', 'bin', 'bun.exe')] : [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun']; for (const bunPath of bunPaths) { if (existsSync(bunPath)) { console.error(`✅ Bun installed at ${bunPath}`); console.error('⚠️ Please restart your terminal or add Bun to PATH:'); if (IS_WINDOWS) { console.error(` $env:Path += ";${join(homedir(), '.bun', 'bin')}"`); } else { console.error(` export PATH="$HOME/.bun/bin:$PATH"`); } return true; } } throw new Error('Bun installation completed but binary not found'); } } catch (error) { console.error('❌ Failed to install Bun automatically'); console.error(' Please install manually:'); if (IS_WINDOWS) { console.error(' - winget install Oven-sh.Bun'); console.error(' - Or: powershell -c "irm bun.sh/install.ps1 | iex"'); } else { console.error(' - curl -fsSL https://bun.sh/install | bash'); console.error(' - Or: brew install oven-sh/bun/bun'); } console.error(' Then restart your terminal and try again.'); throw error; } } /** * Install uv automatically based on platform */ function installUv() { console.error('🐍 Installing uv for Python/Chroma support...'); try { if (IS_WINDOWS) { // Windows: Use PowerShell installer console.error(' Installing via PowerShell...'); execSync('powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"', { stdio: 'inherit', shell: true }); } else { // Unix/macOS: Use curl installer console.error(' Installing via curl...'); execSync('curl -LsSf https://astral.sh/uv/install.sh | sh', { stdio: 'inherit', shell: true }); } // Verify installation if (isUvInstalled()) { const version = getUvVersion(); console.error(`✅ uv ${version} installed successfully`); return true; } else { // uv may be installed but not in PATH yet for this session // Try common installation paths const uvPaths = IS_WINDOWS ? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')] : [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv']; for (const uvPath of uvPaths) { if (existsSync(uvPath)) { console.error(`✅ uv installed at ${uvPath}`); console.error('⚠️ Please restart your terminal or add uv to PATH:'); if (IS_WINDOWS) { console.error(` $env:Path += ";${join(homedir(), '.local', 'bin')}"`); } else { console.error(` export PATH="$HOME/.local/bin:$PATH"`); } return true; } } throw new Error('uv installation completed but binary not found'); } } catch (error) { console.error('❌ Failed to install uv automatically'); console.error(' Please install manually:'); if (IS_WINDOWS) { console.error(' - winget install astral-sh.uv'); console.error(' - Or: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"'); } else { console.error(' - curl -LsSf https://astral.sh/uv/install.sh | sh'); console.error(' - Or: brew install uv (macOS)'); } console.error(' Then restart your terminal and try again.'); throw error; } } /** * Check if dependencies need to be installed */ function needsInstall() { if (!existsSync(join(ROOT, 'node_modules'))) return true; try { const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8')); const marker = JSON.parse(readFileSync(MARKER, 'utf-8')); return pkg.version !== marker.version || getBunVersion() !== marker.bun; } catch { return true; } } /** * Install dependencies using Bun */ function installDeps() { console.error('📦 Installing dependencies with Bun...'); try { execSync('bun install', { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS }); } catch { // Retry with force flag execSync('bun install --force', { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS }); } // Write version marker const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8')); writeFileSync(MARKER, JSON.stringify({ version: pkg.version, bun: getBunVersion(), uv: getUvVersion(), installedAt: new Date().toISOString() })); } // Main execution try { // Step 1: Ensure Bun is installed (REQUIRED) if (!isBunInstalled()) { installBun(); // Re-check after installation if (!isBunInstalled()) { console.error('❌ Bun is required but not available in PATH'); console.error(' Please restart your terminal after installation'); process.exit(1); } } // Step 2: Ensure uv is installed (REQUIRED for vector search) if (!isUvInstalled()) { installUv(); // Re-check after installation if (!isUvInstalled()) { console.error('❌ uv is required but not available in PATH'); console.error(' Please restart your terminal after installation'); process.exit(1); } } // Step 3: Install dependencies if needed if (needsInstall()) { installDeps(); console.error('✅ Dependencies installed'); } } catch (e) { console.error('❌ Installation failed:', e.message); process.exit(1); }