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
JavaScript
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);
});