UNPKG

@clyde-code-ai/clyde-code

Version:

A CLI AI code assistant.

214 lines (184 loc) • 5.98 kB
#!/usr/bin/env node const { spawn, exec } = require('child_process'); const fs = require('fs'); const path = require('path'); const os = require('os'); const readline = require('readline'); function runCommand(command, args, options = {}) { return new Promise((resolve, reject) => { const child = spawn(command, args, { stdio: 'pipe', ...options }); let stdout = ''; let stderr = ''; child.stdout?.on('data', (data) => stdout += data); child.stderr?.on('data', (data) => stderr += data); child.on('close', (code) => { if (code === 0) { resolve({ stdout, stderr }); } else { reject(new Error(`Command failed with code ${code}: ${stderr}`)); } }); }); } function promptUser(question) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(question, (answer) => { rl.close(); resolve(answer.toLowerCase().trim()); }); }); } async function checkPythonVersion() { try { const { stdout } = await runCommand('python3', ['--version']); const version = stdout.trim().match(/Python (\d+)\.(\d+)/); if (version) { const major = parseInt(version[1]); const minor = parseInt(version[2]); return major > 3 || (major === 3 && minor >= 12); } return false; } catch { return false; } } async function checkUv() { try { await runCommand('uv', ['--version']); return true; } catch { return false; } } async function checkPipx() { try { await runCommand('pipx', ['--version']); return true; } catch { return false; } } async function installUv() { console.log('šŸ“¦ Installing uv...'); try { await runCommand('sh', ['-c', 'curl -LsSf https://astral.sh/uv/install.sh | sh']); console.log('āœ… Successfully installed uv'); console.log('ā„¹ļø You may need to restart your terminal or run: source ~/.bashrc'); return true; } catch (error) { console.warn('āš ļø uv installation failed:', error.message); return false; } } async function installWithUv() { console.log('šŸ“¦ Installing clyde-code using uv (recommended)...'); console.log(' This may take a moment while dependencies are resolved...'); try { await runCommand('uv', ['tool', 'install', '.'], { cwd: __dirname + '/..' }); console.log('āœ… Successfully installed clyde-code with uv'); return true; } catch (error) { console.warn('āš ļø uv installation failed:', error.message); return false; } } async function installWithPipx() { console.log('šŸ“¦ Installing clyde-code using pipx...'); console.log(' This may take a moment while dependencies are resolved...'); try { await runCommand('pipx', ['install', '.'], { cwd: __dirname + '/..' }); console.log('āœ… Successfully installed clyde-code with pipx'); return true; } catch (error) { console.warn('āš ļø pipx installation failed:', error.message); return false; } } async function createConfigFile() { const configDir = path.join(os.homedir(), '.clyde-code'); const configPath = path.join(configDir, 'config.json'); // Check if config already exists if (fs.existsSync(configPath)) { console.log('ā„¹ļø Config file already exists at ~/.clyde-code/config.json'); return; } // Create config directory if it doesn't exist if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } // Create config file with placeholder values const config = { llm: { model_name: "your-model-here", api_key: "your-api-key-here", base_url: "your-optional-url-here" }, agent: { thread_id: "main" } }; try { fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); console.log(`āœ… Created config file at ~/.clyde-code/config.json`); console.log('šŸ“ Please update the config file with your API key and model before using clyde-code'); } catch (error) { console.warn('āš ļø Failed to create config file:', error.message); console.log('You can create it manually at ~/.clyde-code/config.json'); } } async function main() { console.log('Installing clyde-code Python package...'); // Check Python version const pythonOk = await checkPythonVersion(); if (!pythonOk) { console.error('āŒ Python 3.12+ is required but not found.'); console.error('Please install Python 3.12 or later.'); process.exit(1); } // Try installation methods in order of preference (uv first, then pipx) console.log('šŸ” Checking for Python package managers...'); let hasUv = await checkUv(); const hasPipx = await checkPipx(); if (hasUv) { console.log(' Found uv āœ“'); } if (hasPipx) { console.log(' Found pipx āœ“'); } // If uv is not found, ask user if they want to install it if (!hasUv) { console.log(' uv not found'); const response = await promptUser('Would you like to install uv? (recommended) [y/N]: '); if (response === 'y' || response === 'yes') { const uvInstalled = await installUv(); if (uvInstalled) { hasUv = await checkUv(); } } } let success = false; if (hasUv) { success = await installWithUv(); } if (!success && hasPipx) { success = await installWithPipx(); } if (!success) { console.error('āŒ Installation failed. Please install uv or pipx first:'); console.error(' pip install uv # then: uv tool install clyde-code'); console.error(' or: pip install pipx # then: pipx install clyde-code'); console.error(' or: brew install uv # on macOS'); process.exit(1); } // Create config file await createConfigFile(); console.log('\nšŸŽ‰ Installation complete! You can now use: clyde-code'); } main().catch((error) => { console.error('Installation failed:', error.message); process.exit(1); });