UNPKG

@michaelnkomo/cli

Version:

BroCode CLI - AI coding assistant with @ file tagging and multi-language support

202 lines (201 loc) 7.45 kB
#!/usr/bin/env node /** * BroCode CLI Entry Point - Native Node.js version */ import readline from 'readline'; import { Agent, ConfigManager, Logger, AuthService } from '@michaelnkomo/core'; import { printBanner, printError, printSuccess, formatResponse, createLoadingSpinner, stopLoadingSpinner, printCommandHelp, } from './utils/ui.js'; // ============================================ // MAIN CLI // ============================================ async function main() { try { // Show banner printBanner(); // Initialize backend authentication const backendUrl = process.env.BROCODE_BACKEND_URL || 'https://brocode-backend.vercel.app'; const auth = new AuthService({ backendUrl }); // Check authentication if (!await auth.isAuthenticated()) { console.log(' Authentication required...\n'); console.log('BroCode now requires authentication to use the service.'); console.log('This keeps your API key secure and enables usage tracking.\n'); try { await auth.login(); } catch (error) { printError(`Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`); process.exit(1); } } // Get session const session = await auth.getSession(); if (!session) { printError('Failed to get authentication session'); process.exit(1); } // Show user info console.log(`\n Logged in as: ${session.user.email}`); console.log(` Tier: ${session.user.tier.toUpperCase()}`); if (session.user.apiCallsLimit === -1) { console.log(` API Calls: Unlimited\n`); } else { console.log(` API Calls: ${session.user.apiCallsUsed}/${session.user.apiCallsLimit}\n`); } // Load configuration const configManager = await ConfigManager.load(); const config = configManager.getAll(); // Create logger const logger = new Logger({ verbose: config.verbose, debug: config.debug, logToFile: true, component: 'cli', }); // Create agent config - use backend proxy for secure API access const agentConfig = { apiKey: '', // Not needed when using backend proxy apiUrl: '', // Not needed when using backend proxy model: config.model, temperature: config.temperature, top_p: config.top_p, max_tokens: config.max_tokens, // Backend proxy configuration useBackendProxy: true, backendUrl: backendUrl, authToken: session.token, }; // Initialize agent const agent = new Agent(agentConfig, logger); printSuccess('BroCode is ready! Type your request or /help for commands.'); // Create readline interface const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: '\n\x1b[32mYou:\x1b[0m ', }); rl.prompt(); rl.on('line', async (input) => { const userInput = input.trim(); // Handle empty input if (!userInput) { rl.prompt(); return; } // Handle commands if (userInput.startsWith('/')) { await handleCommand(userInput, agent, rl); return; } // Process with AI const spinner = createLoadingSpinner('BroCode is thinking'); try { const response = await agent.chat(userInput); stopLoadingSpinner(spinner); // Format and print response console.log('\n\x1b[36mBroCode:\x1b[0m'); console.log(formatResponse(response)); } catch (error) { stopLoadingSpinner(spinner); printError(`Error: ${error.message}`); } rl.prompt(); }); rl.on('close', () => { console.log('\n\nGoodbye! 👋\n'); process.exit(0); }); // Handle Ctrl+C gracefully process.on('SIGINT', () => { rl.close(); }); } catch (error) { printError(`Failed to initialize BroCode: ${error.message}`); process.exit(1); } } // ============================================ // COMMAND HANDLER // ============================================ async function handleCommand(command, agent, rl) { const cmd = command.toLowerCase(); switch (cmd) { case '/help': case '/h': printCommandHelp([ { command: '/help', description: 'Show this help message', aliases: ['/h'], }, { command: '/clear', description: 'Clear conversation history', aliases: ['/c'], }, { command: '/history', description: 'Show conversation history', aliases: ['/hist'], }, { command: '/exit', description: 'Exit BroCode', aliases: ['/quit', '/q'], }, { command: '/stats', description: 'Show conversation statistics', }, ]); break; case '/clear': case '/c': agent.clearHistory(); printSuccess('Conversation history cleared!'); break; case '/history': case '/hist': const history = agent.getHistory(); if (history.length === 0) { console.log('\n No conversation history yet.\n'); } else { console.log(`\n Conversation History (${history.length} messages):\n`); history.forEach((msg, idx) => { const role = msg.role === 'user' ? '\x1b[32mYou\x1b[0m' : '\x1b[36mBroCode\x1b[0m'; const preview = msg.content.substring(0, 80) + (msg.content.length > 80 ? '...' : ''); console.log(` ${idx + 1}. ${role}: ${preview}`); }); console.log(); } break; case '/stats': const messageCount = agent.getMessageCount(); console.log(`\n Conversation Statistics:\n`); console.log(` • Total messages: ${messageCount}`); console.log(` • User messages: ${Math.ceil(messageCount / 2)}`); console.log(` • AI responses: ${Math.floor(messageCount / 2)}`); console.log(); break; case '/exit': case '/quit': case '/q': rl.close(); break; default: printError(`Unknown command: ${command}\n\nType /help to see available commands.`); break; } rl.prompt(); } // ============================================ // RUN // ============================================ main().catch((error) => { printError(`Fatal error: ${error.message}`); process.exit(1); });