UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

294 lines (286 loc) 12.5 kB
#!/usr/bin/env node import { executeTool } from '../services/routing/toolRegistry.js'; import { EnhancedCLIUtils } from './utils/cli-formatter.js'; import { parseCliArgs, extractRequestArgs, generateSessionId, validateEnvironment } from './utils/config-loader.js'; import { UnifiedCommandGateway } from './gateway/unified-command-gateway.js'; import { appInitializer } from './core/app-initializer.js'; import { detectCLIMode } from './utils/mode-detector.js'; import ora from 'ora'; import chalk from 'chalk'; import logger from '../logger.js'; async function gracefulExit(code = 0) { try { if (logger && typeof logger.flush === 'function') { await new Promise((resolve) => { logger.flush(() => resolve()); }); } await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { console.error('Error during graceful exit:', error); } finally { process.exit(code); } } async function main() { const args = process.argv.slice(2); const mode = detectCLIMode(args); switch (mode) { case 'help': displayHelp(); await gracefulExit(0); return; case 'interactive': await startInteractiveMode(); return; case 'oneshot': await processOneShot(args); return; } } async function startInteractiveMode() { try { const openRouterConfig = await appInitializer.initializeCoreServices(); const { VibeInteractiveREPL } = await import('./interactive/repl.js'); const repl = new VibeInteractiveREPL(); await repl.start(openRouterConfig); } catch (error) { logger.error({ err: error }, 'Failed to start interactive mode'); console.error(chalk.red('Failed to start interactive mode:'), error instanceof Error ? error.message : 'Unknown error'); await gracefulExit(1); } } async function processOneShot(args) { const cliConfig = parseCliArgs(args); const requestArgs = extractRequestArgs(args); if (requestArgs.length === 0) { console.log(chalk.cyan('💡 Tip: Run `vibe` without arguments to start interactive mode')); console.log(); displayUsageExample(); await gracefulExit(0); return; } const environmentValidation = await validateEnvironment(); if (!environmentValidation.valid) { EnhancedCLIUtils.formatError('Environment validation failed:'); environmentValidation.errors.forEach(error => { console.error(` • ${error}`); }); await gracefulExit(1); return; } const request = requestArgs.join(' '); let spinner = null; try { if (!cliConfig.quiet) { spinner = ora({ text: 'Processing your request...', color: cliConfig.color ? 'cyan' : undefined }).start(); } const openRouterConfig = await appInitializer.initializeCoreServices(); const unifiedGateway = UnifiedCommandGateway.getInstance(openRouterConfig); const sessionId = generateSessionId(); const unifiedContext = { sessionId, userId: undefined, currentProject: undefined, currentTask: undefined, conversationHistory: [], userPreferences: {}, activeWorkflow: undefined, workflowStack: [], toolHistory: [], preferredTools: {} }; const processingResult = await unifiedGateway.processUnifiedCommand(request, unifiedContext); let result; if (processingResult.success && !processingResult.metadata?.requiresConfirmation) { const executionResult = await unifiedGateway.executeUnifiedCommand(request, unifiedContext); result = executionResult.result; } else if (processingResult.success && processingResult.metadata?.requiresConfirmation) { result = { content: [{ type: 'text', text: `Command: "${request}"\nSelected Tool: ${processingResult.selectedTool}\nConfidence: ${(processingResult.intent?.confidence || 0 * 100).toFixed(1)}%\n\nValidation:\n${processingResult.validationErrors.join('\n')}\n\nSuggestions:\n${processingResult.suggestions.join('\n')}\n\nUse --force to execute without confirmation.` }] }; } else { const context = { sessionId, transportType: 'cli', metadata: { startTime: Date.now(), cliVersion: '1.0.0', cliConfig: cliConfig, fallbackReason: 'Unified gateway processing failed' } }; result = await executeTool('process-request', { request }, openRouterConfig, context); } if (spinner) { spinner.succeed('Request processed successfully!'); } await formatAndDisplayResult(result, cliConfig); await gracefulExit(0); } catch (error) { if (spinner) { spinner.fail('Request failed'); } await handleCliError(error, cliConfig); await gracefulExit(1); } } async function formatAndDisplayResult(result, config) { try { if (config.outputFormat === 'json') { console.log(JSON.stringify(result, null, 2)); return; } if (config.outputFormat === 'yaml') { console.log('result:'); console.log(' content:'); result.content.forEach((item, _index) => { console.log(` - type: ${item.type}`); if (item.text) { console.log(` text: |`); item.text.split('\n').forEach(line => { console.log(` ${line}`); }); } if (item.data) { console.log(` data: ${item.data}`); } if (item.mimeType) { console.log(` mimeType: ${item.mimeType}`); } }); return; } const content = result.content[0]; if (content && content.type === 'text' && content.text) { EnhancedCLIUtils.formatBox(content.text, '🤖 Vibe Coder Result'); } else if (content) { console.log(`Content type: ${content.type}`); if (content.data) { console.log('Data received (use --json for full output)'); } } else { console.log('No content returned from request'); } } catch (error) { console.error('Error formatting result:', error instanceof Error ? error.message : 'Unknown error'); console.log(JSON.stringify(result, null, 2)); } } async function handleCliError(error, config) { if (error instanceof Error) { EnhancedCLIUtils.formatError(error.message); if (config.verbose && error.stack) { console.log(); console.log(chalk.gray('Stack trace:')); console.log(chalk.gray(error.stack)); } } else { EnhancedCLIUtils.formatError('An unexpected error occurred'); if (config.verbose) { console.log(); console.log(chalk.gray('Error details:')); console.log(chalk.gray(String(error))); } } if (!config.quiet) { console.log(); EnhancedCLIUtils.formatInfo('Try running with --verbose for more details'); } } function displayHelp() { const helpText = ` ${chalk.cyan.bold('🤖 Vibe CLI - Natural Language Development Assistant')} ${chalk.yellow('DESCRIPTION:')} Unified command-line interface for all Vibe Coder MCP tools. Process natural language requests and route them to the appropriate tool. ${chalk.yellow('USAGE:')} ${chalk.green('vibe')} ${chalk.gray('Start interactive mode (REPL)')} ${chalk.green('vibe')} ${chalk.blue('<request>')} ${chalk.gray('[options]')} ${chalk.gray('Process one-shot request')} ${chalk.yellow('OPTIONS:')} ${chalk.green('-v, --verbose')} Show detailed output and error traces ${chalk.green('-q, --quiet')} Suppress non-error output ${chalk.green('--format <type>')} Output format: text, json, yaml (default: text) ${chalk.green('--json')} Shorthand for --format json ${chalk.green('--yaml')} Shorthand for --format yaml ${chalk.green('--no-color')} Disable colored output ${chalk.green('-i, --interactive')} Force interactive mode ${chalk.green('-h, --help')} Show this help message ${chalk.yellow('EXAMPLES:')}`; const examples = [ ['vibe "research best practices for React hooks"', 'Research technical topics and best practices'], ['vibe "create a PRD for an e-commerce platform"', 'Generate Product Requirements Documents'], ['vibe "generate user stories for authentication"', 'Create user stories with acceptance criteria'], ['vibe "create task list from user stories"', 'Break down user stories into development tasks'], ['vibe "map the codebase structure"', 'Analyze and visualize code architecture'], ['vibe "create coding standards for TypeScript"', 'Generate development rules and guidelines'], ['vibe "create a React app with Node backend"', 'Generate full-stack project templates'], ['vibe "create context for implementing auth"', 'Curate AI-optimized context packages'], ['vibe "create project MyApp" --verbose', 'Task management with detailed output'], ['vibe "check job status 12345" --json', 'Get job results in JSON format'] ]; examples.forEach(([command, description]) => { EnhancedCLIUtils.formatExample(command, description); }); const toolsText = `${chalk.yellow('AVAILABLE TOOLS:')} ${chalk.cyan('•')} Research Manager - Technical research and analysis ${chalk.cyan('•')} PRD Generator - Product requirements documents ${chalk.cyan('•')} User Stories Generator - Agile user stories ${chalk.cyan('•')} Task List Generator - Development task lists ${chalk.cyan('•')} Rules Generator - Coding standards and guidelines ${chalk.cyan('•')} Starter Kit Generator - Full-stack project templates ${chalk.cyan('•')} Code Map Generator - Codebase analysis and mapping ${chalk.cyan('•')} Context Curator - AI-optimized context packages ${chalk.cyan('•')} Vibe Task Manager - Task management and tracking ${chalk.cyan('•')} Workflow Runner - Multi-step workflow execution ${chalk.cyan('•')} Agent Coordination - Multi-agent task distribution ${chalk.yellow('CONFIGURATION:')} Configuration is loaded from centralized systems: ${chalk.cyan('•')} OpenRouter API settings from environment variables ${chalk.cyan('•')} Security boundaries from unified security config ${chalk.cyan('•')} LLM model mappings from llm_config.json ${chalk.yellow('ENVIRONMENT VARIABLES:')} ${chalk.green('OPENROUTER_API_KEY')} Required: Your OpenRouter API key ${chalk.green('OPENROUTER_BASE_URL')} Optional: OpenRouter API base URL ${chalk.green('GEMINI_MODEL')} Optional: Default Gemini model ${chalk.green('PERPLEXITY_MODEL')} Optional: Default Perplexity model ${chalk.gray('For more information, visit: https://github.com/freshtechbro/Vibe-Coder-MCP')} `; console.log(toolsText); console.log(helpText); } function displayUsageExample() { console.log(`${chalk.yellow('Usage:')} ${chalk.green('vibe')} ${chalk.blue('"your natural language request"')}`); console.log(`${chalk.yellow('Example:')} ${chalk.green('vibe')} ${chalk.blue('"research best practices for React"')}`); console.log(`${chalk.gray('Run')} ${chalk.cyan('vibe --help')} ${chalk.gray('for more information')}`); } main().catch(async (error) => { console.error(chalk.red('🚨 Fatal error:')); if (error instanceof Error) { console.error(chalk.red(error.message)); if (error.stack) { console.error(chalk.gray(error.stack)); } } else { console.error(chalk.red('Unknown error occurred')); console.error(chalk.gray(String(error))); } await gracefulExit(1); });