blue-beatle
Version:
š¤ AI-Powered Development Assistant - Intelligent code analysis, problem solving, and terminal automation using Gemini API
435 lines (361 loc) ⢠15 kB
JavaScript
#!/usr/bin/env node
/**
* š Blue Beatle Setup Script
* Handles initial installation and configuration
*/
const fs = require('fs-extra');
const path = require('path');
const { execSync } = require('child_process');
const chalk = require('chalk');
const inquirer = require('inquirer');
const ora = require('ora');
class BlueBeatleSetup {
constructor() {
this.isWindows = process.platform === 'win32';
this.isMacOS = process.platform === 'darwin';
this.isLinux = process.platform === 'linux';
this.requirements = {
node: '16.0.0',
npm: '8.0.0'
};
}
async run() {
try {
console.clear();
this.showWelcome();
await this.checkSystemRequirements();
await this.installDependencies();
await this.setupConfiguration();
await this.createShortcuts();
await this.finalizeInstallation();
this.showSuccess();
} catch (error) {
console.error(chalk.red('ā Setup failed:'), error.message);
process.exit(1);
}
}
showWelcome() {
const figlet = require('figlet');
const gradient = require('gradient-string');
const title = figlet.textSync('Blue Beatle', {
font: 'ANSI Shadow',
horizontalLayout: 'default'
});
console.log(gradient(['#00f5ff', '#0080ff'])(title));
console.log(chalk.cyan.bold('š¤ AI-Powered Development Assistant'));
console.log(chalk.gray('Setting up your intelligent coding companion...\n'));
}
async checkSystemRequirements() {
const spinner = ora('š Checking system requirements...').start();
try {
// Check Node.js
const nodeVersion = process.version.slice(1);
if (!this.compareVersions(nodeVersion, this.requirements.node)) {
throw new Error(`Node.js ${this.requirements.node} or higher required. Found: ${nodeVersion}`);
}
// Check npm
const npmVersion = execSync('npm --version', { encoding: 'utf8' }).trim();
if (!this.compareVersions(npmVersion, this.requirements.npm)) {
throw new Error(`npm ${this.requirements.npm} or higher required. Found: ${npmVersion}`);
}
// Check internet connection
await this.checkInternetConnection();
spinner.succeed('ā
System requirements met');
} catch (error) {
spinner.fail('ā System requirements check failed');
throw error;
}
}
compareVersions(current, required) {
const currentParts = current.split('.').map(Number);
const requiredParts = required.split('.').map(Number);
for (let i = 0; i < Math.max(currentParts.length, requiredParts.length); i++) {
const currentPart = currentParts[i] || 0;
const requiredPart = requiredParts[i] || 0;
if (currentPart > requiredPart) return true;
if (currentPart < requiredPart) return false;
}
return true;
}
async checkInternetConnection() {
try {
const https = require('https');
return new Promise((resolve, reject) => {
const req = https.request('https://www.google.com', { timeout: 5000 }, (res) => {
resolve(true);
});
req.on('error', reject);
req.on('timeout', () => reject(new Error('Internet connection timeout')));
req.end();
});
} catch (error) {
throw new Error('Internet connection required for installation');
}
}
async installDependencies() {
const spinner = ora('š¦ Installing dependencies...').start();
try {
// Install production dependencies
execSync('npm install --production', {
stdio: 'pipe',
cwd: process.cwd()
});
spinner.succeed('ā
Dependencies installed');
} catch (error) {
spinner.fail('ā Failed to install dependencies');
throw new Error(`Dependency installation failed: ${error.message}`);
}
}
async setupConfiguration() {
console.log(chalk.cyan('\nāļø Configuration Setup\n'));
const { setupType } = await inquirer.prompt([{
type: 'list',
name: 'setupType',
message: 'Choose setup type:',
choices: [
{ name: 'š Quick Setup (recommended)', value: 'quick' },
{ name: 'š§ Custom Setup', value: 'custom' },
{ name: 'āļø Skip Configuration', value: 'skip' }
]
}]);
if (setupType === 'skip') {
console.log(chalk.yellow('āļø Configuration skipped. You can run "blue-beatle setup" later.'));
return;
}
if (setupType === 'quick') {
await this.quickSetup();
} else {
await this.customSetup();
}
}
async quickSetup() {
console.log(chalk.blue('š Quick Setup - Using recommended settings\n'));
const { apiKey } = await inquirer.prompt([{
type: 'confirm',
name: 'apiKey',
message: 'Do you have a Gemini API key to setup now?',
default: false
}]);
if (apiKey) {
const { key } = await inquirer.prompt([{
type: 'password',
name: 'key',
message: 'Enter your Gemini API key:',
mask: '*'
}]);
// Save API key securely
await this.saveApiKey(key);
}
// Create default configuration
await this.createDefaultConfig();
}
async customSetup() {
console.log(chalk.blue('š§ Custom Setup\n'));
const config = await inquirer.prompt([
{
type: 'input',
name: 'userName',
message: 'Your name:',
default: require('os').userInfo().username
},
{
type: 'input',
name: 'userEmail',
message: 'Your email (optional):'
},
{
type: 'confirm',
name: 'safeMode',
message: 'Enable safe mode for terminal commands?',
default: true
},
{
type: 'confirm',
name: 'autoUpdate',
message: 'Enable automatic updates?',
default: true
},
{
type: 'list',
name: 'defaultTemplate',
message: 'Default project template:',
choices: ['react', 'node', 'vue', 'python', 'rust', 'go'],
default: 'node'
}
]);
await this.createCustomConfig(config);
}
async saveApiKey(apiKey) {
const ConfigManager = require('../src/config-manager');
const configManager = new ConfigManager();
await configManager.initialize();
await configManager.setSecret('geminiApiKey', apiKey);
}
async createDefaultConfig() {
const ConfigManager = require('../src/config-manager');
const configManager = new ConfigManager();
await configManager.initialize();
// Default config is already created in ConfigManager
}
async createCustomConfig(config) {
const ConfigManager = require('../src/config-manager');
const configManager = new ConfigManager();
await configManager.initialize();
await configManager.set('user.name', config.userName);
if (config.userEmail) {
await configManager.set('user.email', config.userEmail);
}
await configManager.set('terminal.safeMode', config.safeMode);
await configManager.set('user.preferences.autoUpdate', config.autoUpdate);
await configManager.set('project.defaultTemplate', config.defaultTemplate);
}
async createShortcuts() {
const { createShortcuts } = await inquirer.prompt([{
type: 'confirm',
name: 'createShortcuts',
message: 'Create desktop shortcuts?',
default: true
}]);
if (!createShortcuts) return;
const spinner = ora('š Creating shortcuts...').start();
try {
if (this.isWindows) {
await this.createWindowsShortcuts();
} else if (this.isMacOS) {
await this.createMacOSShortcuts();
} else if (this.isLinux) {
await this.createLinuxShortcuts();
}
spinner.succeed('ā
Shortcuts created');
} catch (error) {
spinner.warn('ā ļø Could not create shortcuts');
console.log(chalk.yellow(`Warning: ${error.message}`));
}
}
async createWindowsShortcuts() {
const os = require('os');
const desktopPath = path.join(os.homedir(), 'Desktop');
// Create PowerShell shortcut
const shortcutContent = `$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("${path.join(desktopPath, 'Blue Beatle.lnk')}")
$Shortcut.TargetPath = "powershell.exe"
$Shortcut.Arguments = "-NoExit -Command \\"blue-beatle\\""
$Shortcut.WorkingDirectory = "${process.cwd()}"
$Shortcut.IconLocation = "powershell.exe,0"
$Shortcut.Description = "Blue Beatle AI Development Assistant"
$Shortcut.Save()`;
const scriptPath = path.join(os.tmpdir(), 'create-shortcut.ps1');
await fs.writeFile(scriptPath, shortcutContent);
try {
execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}"`, { stdio: 'ignore' });
} finally {
await fs.remove(scriptPath);
}
}
async createMacOSShortcuts() {
const os = require('os');
const applicationsPath = path.join(os.homedir(), 'Applications');
await fs.ensureDir(applicationsPath);
const appPath = path.join(applicationsPath, 'MacAdida.app');
const contentsPath = path.join(appPath, 'Contents');
const macOSPath = path.join(contentsPath, 'MacOS');
await fs.ensureDir(macOSPath);
// Create Info.plist
const infoPlist = `xml version="1.0" encoding="UTF-8"
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>blue-beatle</string>
<key>CFBundleIdentifier</key>
<string>com.engineermarcus.blue-beatle</string>
<key>CFBundleName</key>
<string>Blue Beatle</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
</dict>
</plist>`;
await fs.writeFile(path.join(contentsPath, 'Info.plist'), infoPlist);
// Create executable script
const executable = `#!/bin/bash
cd "${process.cwd()}"
open -a Terminal.app "${process.cwd()}/bin/blue-beatle.js"`;
await fs.writeFile(path.join(macOSPath, 'blue-beatle'), executable);
await fs.chmod(path.join(macOSPath, 'blue-beatle'), '755');
}
async createLinuxShortcuts() {
const os = require('os');
const desktopPath = path.join(os.homedir(), 'Desktop');
const desktopEntry = `[Desktop Entry]
Version=1.0
Type=Application
Name=Blue Beatle
Comment=AI-Powered Development Assistant
Exec=gnome-terminal -- bash -c "cd '${process.cwd()}' && node bin/blue-beatle.js; exec bash"
Icon=terminal
Terminal=false
Categories=Development;`;
await fs.writeFile(path.join(desktopPath, 'blue-beatle.desktop'), desktopEntry);
await fs.chmod(path.join(desktopPath, 'blue-beatle.desktop'), '755');
}
async finalizeInstallation() {
const spinner = ora('šÆ Finalizing installation...').start();
try {
// Make binary executable
await fs.chmod(path.join(process.cwd(), 'bin', 'blue-beatle.js'), '755');
// Create global link if possible
try {
execSync('npm link', { stdio: 'ignore' });
spinner.text = 'Global command "blue-beatle" is now available';
} catch (error) {
// Global link failed, that's okay
}
// Test installation
const { stdout } = require('child_process').spawnSync('node', [
path.join(process.cwd(), 'bin', 'blue-beatle.js'),
'--version'
], { encoding: 'utf8' });
if (!stdout) {
throw new Error('Installation test failed');
}
spinner.succeed('ā
Installation finalized');
} catch (error) {
spinner.fail('ā Finalization failed');
throw error;
}
}
showSuccess() {
const boxen = require('boxen');
const successMessage = `š Blue Beatle Installation Complete!
š Quick Start:
blue-beatle setup - Run setup wizard
blue-beatle ai "help" - Ask AI for help
blue-beatle interactive - Start chat mode
blue-beatle analyze - Analyze your code
š Documentation:
https://github.com/engineermarcus/marcus-alias
š” Need help? Run: blue-beatle --help`;
const box = boxen(successMessage, {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'green',
backgroundColor: 'black'
});
console.log(box);
console.log(chalk.cyan('\nšÆ Next Steps:'));
console.log(chalk.gray('1. Setup your Gemini API key: blue-beatle config --setup-api'));
console.log(chalk.gray('2. Try the interactive mode: blue-beatle interactive'));
console.log(chalk.gray('3. Analyze your first project: blue-beatle analyze'));
console.log(chalk.green('\n⨠Happy coding with Blue Beatle!'));
}
}
// Run setup if called directly
if (require.main === module) {
const setup = new BlueBeatleSetup();
setup.run().catch(error => {
console.error(chalk.red('ā Setup failed:'), error.message);
process.exit(1);
});
}
module.exports = BlueBeatleSetup;