UNPKG

woaru

Version:

Universal Project Setup Autopilot - Analyze and automatically configure development tools for ANY programming language

1,111 lines 55.8 kB
#!/usr/bin/env node // src/cli.ts - NEUE, SAUBERE IMPLEMENTIERUNG // Initialize Sentry first, before any other imports import * as Sentry from '@sentry/node'; // Initialize Sentry with environment variable Sentry.init({ dsn: process.env.SENTRY_DSN, tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, environment: process.env.NODE_ENV || 'development', enabled: !!process.env.SENTRY_DSN, // Only enable if DSN is provided beforeSend(event) { // Filter out non-critical errors in development if (process.env.NODE_ENV !== 'production' && event.level === 'warning') { return null; } return event; }, }); import { Command } from 'commander'; import { initializeI18n, t } from './config/i18n.js'; import { readFileSync } from 'fs'; import fs from 'fs-extra'; import * as path from 'path'; import chalk from 'chalk'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; // ES module compatibility const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // WIR WERDEN HIER NACH UND NACH DIE ANDEREN IMPORTS HINZUFÜGEN // Diese Funktion wird ALLE Befehle definieren, NACHDEM i18n bereit ist. function defineCommands(program) { // Dynamische Version aus package.json const packageJsonPath = path.join(__dirname, '../package.json'); const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); program.version(packageJson.version); // Init Command - Interactive Project Scaffolding program .command('init') .description('Initialize a new project with best practices') .option('-t, --template <type>', 'Project template (nextjs, python-fastapi)') .option('-d, --directory <path>', 'Target directory') .option('-f, --features <features>', 'Comma-separated list of features') .option('--skip-install', 'Skip dependency installation') .option('--non-interactive', 'Run in non-interactive mode') .option('--dry-run', 'Preview changes without creating files') .action(async (options) => { try { const { InitCommand } = await import('./init/InitCommand.js'); const initCmd = new InitCommand(); await initCmd.execute({ template: options.template, directory: options.directory, features: options.features, skipInstall: options.skipInstall, interactive: !options.nonInteractive, dryRun: options.dryRun }); } catch (error) { console.error(chalk.red('Failed to initialize project:'), error); process.exit(1); } }); // Version Command const versionCommand = program .command('version') .description(t('commands.version.description')) .action(() => { console.log(chalk.cyan(t('version.display', { version: packageJson.version }))); }); versionCommand .command('check') .description(t('commands.version.check.description')) .action(async () => { console.log(chalk.yellow(t('version.checking_updates'))); // TODO: Implement update check logic console.log(chalk.green(t('version.up_to_date', { version: packageJson.version }))); }); // Commands Command - Enhanced to show all commands, subcommands and options program .command('commands') .description(t('commands.commands.description')) .action(() => { // Ensure i18n is initialized for this action initializeI18n(); console.log(chalk.cyan.bold('📚 WOARU Command Reference')); console.log(chalk.gray('═'.repeat(60))); console.log(); // Helper function to display command details recursively const displayCommand = (cmd, level = 0) => { if (!cmd.name() || !cmd.description()) return; const indent = ' '.repeat(level); const nameColor = level === 0 ? chalk.blue.bold : chalk.green; const arrow = level === 0 ? '📌' : ' ├─'; console.log(`${indent}${arrow} ${nameColor(`woaru ${getFullCommandName(cmd)}`)}`); // Get the translated description, fallback to raw description if translation fails const description = cmd.description() || ''; const translatedDescription = description.startsWith('commands.') ? t(description.replace('commands.', 'cli.commands.')) : description; console.log(`${indent} ${chalk.gray(translatedDescription)}`); // Show command options if any const options = cmd.options || []; if (options.length > 0) { console.log(`${indent} ${chalk.yellow('Options:')}`); options.forEach((option) => { const flags = option.flags || ''; const description = option.description || ''; console.log(`${indent} ${chalk.cyan(flags)} - ${description}`); }); } // Show usage examples based on full command path const fullCommandName = getFullCommandName(cmd); // Remove "cli " prefix for example lookup const commandKey = (fullCommandName || cmd.name()).replace(/^cli /, ''); const examples = getCommandExamples(commandKey); if (examples.length > 0) { console.log(`${indent} ${chalk.yellow('Examples:')}`); examples.forEach((example) => { console.log(`${indent} ${chalk.gray(example)}`); }); } console.log(); // Recursively display subcommands if (cmd.commands && cmd.commands.length > 0) { cmd.commands.forEach((subCmd) => { displayCommand(subCmd, level + 1); }); } }; // Helper function to get full command path const getFullCommandName = (cmd) => { const names = []; let current = cmd; while (current && current.name && current.name()) { names.unshift(current.name()); current = current.parent; } // Remove the root program name to avoid "woaru woaru" if (names.length > 0 && names[0] === 'woaru') { names.shift(); } return names.join(' '); }; // Helper function to get command examples const getCommandExamples = (cmdName) => { const examples = { 'version': [ 'woaru version', 'woaru version check' ], 'version check': [ 'woaru version check' ], 'commands': [ 'woaru commands' ], 'wiki': [ 'woaru wiki' ], 'quick-analyze': [ 'woaru quick-analyze' ], 'setup': [ 'woaru setup', 'woaru setup --dry-run', 'woaru setup --interactive' ], 'ai': [ 'woaru ai', 'woaru ai setup', 'woaru ai status' ], 'ai setup': [ 'woaru ai setup' ], 'ai status': [ 'woaru ai status', 'woaru ai status --compact', 'woaru ai status --no-details' ], 'update-db': [ 'woaru update-db' ], 'watch': [ 'woaru watch', 'woaru watch --daemon' ], 'status': [ 'woaru status' ], 'update': [ 'woaru update' ], 'stop': [ 'woaru stop' ], 'logs': [ 'woaru logs' ], 'recommendations': [ 'woaru recommendations' ], 'helpers': [ 'woaru helpers' ], 'docu': [ 'woaru docu', 'woaru docu nopro src/utils/helpers.js', 'woaru docu pro --local', 'woaru docu forai --path-only src/services/' ], 'docu nopro': [ 'woaru docu nopro', 'woaru docu nopro --local', 'woaru docu nopro --git main', 'woaru docu nopro --path-only src/', 'woaru docu nopro --preview' ], 'docu pro': [ 'woaru docu pro', 'woaru docu pro --local', 'woaru docu pro --git develop', 'woaru docu pro --path-only lib/', 'woaru docu pro --force' ], 'docu forai': [ 'woaru docu forai', 'woaru docu forai --local', 'woaru docu forai --git main', 'woaru docu forai --path-only src/', 'woaru docu forai --preview --force' ], 'ignore': [ 'woaru ignore eslint', 'woaru ignore prettier' ], 'review': [ 'woaru review', 'woaru review --ai', 'woaru review --git', 'woaru review /path/to/code' ], 'analyze': [ 'woaru analyze', 'woaru analyze --path /project/path', 'woaru analyze ai', 'woaru analyze ai --comprehensive' ], 'analyze ai': [ 'woaru analyze ai', 'woaru analyze ai --comprehensive', 'woaru analyze ai --provider openai', 'woaru analyze ai --path /custom/path' ], 'rollback': [ 'woaru rollback eslint', 'woaru rollback prettier' ], 'message': [ 'woaru message --webhook https://hooks.slack.com/...', 'woaru message --latest --webhook https://discord.com/api/webhooks/...', 'woaru message --type review --webhook https://hooks.slack.com/...', 'woaru message --type analyze --latest --webhook https://hooks.slack.com/...' ], 'config': [ 'woaru config' ], 'language': [ 'woaru language' ] }; return examples[cmdName] || []; }; // Display all main commands program.commands.forEach(cmd => { displayCommand(cmd); }); console.log(chalk.yellow('💡 Pro Tips:')); console.log(chalk.gray(' • Use "woaru <command> --help" for detailed help on any command')); console.log(chalk.gray(' • Use "woaru <command> <subcommand> --help" for subcommand help')); console.log(chalk.gray(' • Run "woaru" without arguments to see the main interface')); console.log(); }); // Wiki Command program .command('wiki') .description(t('commands.wiki.description')) .action(async () => { try { console.log(chalk.cyan.bold(t('commands.wiki.title'))); console.log(); // Get current language for wiki files const currentLang = await getCurrentLanguage(); const wikiPath = path.join(__dirname, '../docs/wiki', currentLang); if (fs.existsSync(wikiPath)) { const files = fs.readdirSync(wikiPath).filter(f => f.endsWith('.md')); for (const file of files) { const filePath = path.join(wikiPath, file); const content = readFileSync(filePath, 'utf-8'); const title = file.replace('.md', ''); console.log(chalk.yellow(`## ${title.toUpperCase()}`)); console.log(content.substring(0, 500) + (content.length > 500 ? '...' : '')); console.log(); } } else { console.log(chalk.yellow(t('commands.wiki.no_docs_found', { lang: currentLang }))); } } catch (error) { console.error(chalk.red(t('commands.wiki.error')), error); } }); // Add missing commands that should be shown in 'woaru commands' // Quick Analyze Command program .command('quick-analyze') .description(t('commands.quick_analyze.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.quick_analyze'))); }); // Setup Command program .command('setup') .description(t('commands.setup.description')) .option('-d, --dry-run', 'Show what would be done without actually doing it') .option('-i, --interactive', 'Interactive mode with prompts', true) .action(async (options) => { try { console.log(chalk.cyan('Starting WOARU setup...')); const { WOARUEngine } = await import('./core/WOARUEngine.js'); const engine = new WOARUEngine(); // First analyze the project const analysisResult = await engine.analyzeProject(process.cwd()); if (analysisResult.setup_recommendations.length === 0) { console.log(chalk.green(t('woaru_engine.project_well_configured'))); return; } console.log(chalk.yellow(`Found ${analysisResult.setup_recommendations.length} recommendations`)); if (options.dryRun) { console.log(chalk.blue(t('woaru_engine.dry_run_mode'))); analysisResult.setup_recommendations.forEach((rec) => { console.log(chalk.gray(` • ${rec}`)); }); } else { await engine.setupProject(process.cwd(), { dryRun: false, interactive: options.interactive, }); console.log(chalk.green('Setup completed!')); } } catch (error) { console.error(chalk.red('Setup failed:'), error); } }); // AI Command - Interactive Control Center const aiCommand = program .command('ai') .description(t('commands.ai.description')) .action(async () => { try { const { showAiControlCenter } = await import('./utils/ai-control-center.js'); await showAiControlCenter(); } catch (error) { console.error(chalk.red('AI command failed:'), error); } }); // AI Setup Sub-command aiCommand .command('setup') .description(t('commands.ai_setup.description')) .action(async () => { try { const { showAiSetup } = await import('./utils/ai-control-center.js'); await showAiSetup(); } catch (error) { console.error(chalk.red('AI setup failed:'), error); } }); // AI Status Sub-command - Visual AI Status Check aiCommand .command('status') .description(t('commands.ai_status.description')) .option('--compact', 'Show compact status display') .option('--no-details', 'Hide detailed configuration information') .action(async (options) => { try { const { displayAiStatus } = await import('./utils/ai-helpers.js'); await displayAiStatus(options.compact || false); } catch (error) { console.error(chalk.red('AI status check failed:'), error); } }); // Update Database Command program .command('update-db') .description(t('commands.update_db.description')) .action(async () => { try { console.log(chalk.cyan(t('woaru_engine.updating_database'))); const { WOARUEngine } = await import('./core/WOARUEngine.js'); const engine = new WOARUEngine(); const success = await engine.updateDatabase(); if (success) { console.log(chalk.green(t('woaru_engine.database_updated'))); } else { console.log(chalk.red(t('woaru_engine.database_update_failed'))); } } catch (error) { console.error(chalk.red('Database update failed:'), error); } }); // Watch Command program .command('watch') .description(t('commands.watch.description')) .option('-d, --daemon', 'Run as daemon in background') .action(async (_options) => { try { console.log(chalk.cyan('Starting WOARU supervisor...')); const { WOARUSupervisor } = await import('./supervisor/WOARUSupervisor.js'); const supervisor = new WOARUSupervisor(process.cwd(), { autoFix: false, autoSetup: false, notifications: { terminal: true, desktop: false, }, ignoreTools: [], watchPatterns: ['**/*'], ignorePatterns: ['node_modules/**', '.git/**'], dashboard: true, }); await supervisor.start(); console.log(chalk.green('WOARU supervisor started successfully!')); console.log(chalk.gray('Monitoring project for changes...')); console.log(chalk.gray('Press Ctrl+C to stop')); // Keep process alive until interrupted process.on('SIGINT', async () => { console.log(chalk.yellow('\\nStopping WOARU supervisor...')); await supervisor.stop(); process.exit(0); }); } catch (error) { console.error(chalk.red('Failed to start supervisor:'), error); } }); // Status Command program .command('status') .description(t('commands.status.description')) .action(async () => { try { console.log(chalk.cyan.bold(t('status.title'))); console.log(); // Check if supervisor is running const { WOARUSupervisor } = await import('./supervisor/WOARUSupervisor.js'); const supervisor = new WOARUSupervisor(process.cwd()); // Get current recommendations const recommendations = await supervisor.getCurrentRecommendations(); console.log(chalk.blue('📊 Project Status:')); console.log(` • Recommendations: ${recommendations.length} found`); console.log(` • Project Path: ${process.cwd()}`); if (recommendations.length > 0) { console.log(chalk.yellow('\\n🔧 Current Recommendations:')); recommendations.slice(0, 5).forEach((rec, index) => { console.log(` ${index + 1}. ${rec.tool}: ${rec.reason}`); }); if (recommendations.length > 5) { console.log(chalk.gray(` ... and ${recommendations.length - 5} more`)); } } else { console.log(chalk.green('\\n✅ No recommendations - project looks good!')); } } catch (error) { console.error(chalk.red('Failed to get status:'), error); } }); // Update Command program .command('update') .description(t('commands.update.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.update'))); }); // Stop Command program .command('stop') .description(t('commands.stop.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.stop'))); }); // Logs Command program .command('logs') .description(t('commands.logs.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.logs'))); }); // Recommendations Command program .command('recommendations') .description(t('commands.recommendations.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.recommendations'))); }); // Helpers Command program .command('helpers') .description(t('commands.helpers.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.helpers'))); }); // Docu Command const docuCommand = program .command('docu') .description(t('cli.commands.docu.description')) .action(() => { // Show main documentation help when no subcommand is provided console.log(chalk.cyan.bold(t('cli.commands.docu.description'))); console.log(); console.log(chalk.blue('📚 Available Documentation Commands:')); console.log(); console.log(chalk.green(' • woaru docu nopro') + chalk.gray(' - ' + t('cli.commands.docu_nopro.description'))); console.log(chalk.green(' • woaru docu pro') + chalk.gray(' - ' + t('cli.commands.docu_pro.description'))); console.log(chalk.green(' • woaru docu forai') + chalk.gray(' - ' + t('cli.commands.docu_forai.description'))); console.log(); console.log(chalk.yellow('💡 Example usage:')); console.log(chalk.gray(' woaru docu nopro src/utils/helpers.js')); console.log(chalk.gray(' woaru docu pro --local')); console.log(chalk.gray(' woaru docu forai --path-only src/services/')); console.log(); console.log(chalk.gray('Use "woaru docu <command> --help" for detailed options')); }); // Docu NoPro Subcommand - Human-friendly documentation docuCommand .command('nopro') .description(t('cli.commands.docu_nopro.description')) .option('--local', 'Document uncommitted changes only') .option('--git <branch>', 'Document changes since specified branch') .option('--path-only <path>', 'Document specific files or directories') .option('--preview', 'Preview changes without applying them') .option('--force', 'Apply documentation without confirmation') .action(async (options) => { try { console.log(chalk.cyan('🔍 Generating human-friendly documentation...')); // Check if AI is configured with visual status const { isAiReady, displayAiStatus } = await import('./utils/ai-helpers.js'); const isConfigured = await isAiReady(); if (!isConfigured) { console.log(chalk.yellow('⚠️ AI provider required for documentation generation.')); await displayAiStatus(true); // Show compact status return; } await runDocumentationCommand('nopro', options); } catch (error) { console.error(chalk.red('Documentation generation failed:'), error); } }); // Docu Pro Subcommand - Technical documentation docuCommand .command('pro') .description(t('cli.commands.docu_pro.description')) .option('--local', 'Document uncommitted changes only') .option('--git <branch>', 'Document changes since specified branch') .option('--path-only <path>', 'Document specific files or directories') .option('--preview', 'Preview changes without applying them') .option('--force', 'Apply documentation without confirmation') .action(async (options) => { try { console.log(chalk.cyan('🔍 Generating technical documentation...')); // Check if AI is configured with visual status const { isAiReady, displayAiStatus } = await import('./utils/ai-helpers.js'); const isConfigured = await isAiReady(); if (!isConfigured) { console.log(chalk.yellow('⚠️ AI provider required for documentation generation.')); await displayAiStatus(true); // Show compact status return; } await runDocumentationCommand('pro', options); } catch (error) { console.error(chalk.red('Documentation generation failed:'), error); } }); // Docu ForAI Subcommand - Machine-readable documentation docuCommand .command('forai') .description(t('cli.commands.docu_forai.description')) .option('--local', 'Document uncommitted changes only') .option('--git <branch>', 'Document changes since specified branch') .option('--path-only <path>', 'Document specific files or directories') .option('--preview', 'Preview changes without applying them') .option('--force', 'Apply documentation without confirmation') .action(async (options) => { try { console.log(chalk.cyan('🔍 Generating ForAI-optimized documentation...')); // Check if AI is configured with visual status const { isAiReady, displayAiStatus } = await import('./utils/ai-helpers.js'); const isConfigured = await isAiReady(); if (!isConfigured) { console.log(chalk.yellow('⚠️ AI provider required for documentation generation.')); await displayAiStatus(true); // Show compact status return; } await runDocumentationCommand('forai', options); } catch (error) { console.error(chalk.red('Documentation generation failed:'), error); } }); // Ignore Command program .command('ignore') .description(t('commands.ignore.description')) .argument('<tool>', 'Tool to ignore') .action(tool => { console.log(chalk.yellow(`Ignore command not implemented yet. Would ignore: ${tool}`)); }); // Review Command program .command('review') .description(t('commands.review.description')) .argument('[path]', 'Path to review', process.cwd()) .option('--ai', 'Use AI-powered review') .option('--git', 'Review git changes') .action(async (path, options) => { try { console.log(chalk.cyan('Starting code review...')); if (options.ai) { // AI-powered review with visual status const { isAiReady, displayAiStatus } = await import('./utils/ai-helpers.js'); const isConfigured = await isAiReady(); if (!isConfigured) { console.log(chalk.yellow('AI not configured for review.')); await displayAiStatus(true); // Show compact status return; } // Load actual AI configuration from ConfigLoader const { ConfigLoader } = await import('./ai/ConfigLoader.js'); const configLoader = ConfigLoader.getInstance(); const aiConfig = await configLoader.loadConfig(path); if (!aiConfig || !aiConfig.providers || aiConfig.providers.length === 0) { console.error(chalk.red('❌ Fehler: Keine aktivierten AI-Provider für den Review gefunden.')); console.log(chalk.blue('💡 Bitte aktiviere mindestens einen Provider mit: woaru ai setup')); return; } // Filter enabled providers only const enabledProviders = aiConfig.providers.filter(p => p.enabled); if (enabledProviders.length === 0) { console.error(chalk.red('❌ Fehler: Keine aktivierten AI-Provider für den Review gefunden.')); console.log(chalk.blue('💡 Bitte aktiviere mindestens einen Provider mit: woaru ai setup')); return; } console.log(chalk.green(`🤖 Found ${enabledProviders.length} enabled AI provider(s): ${enabledProviders.map(p => p.id).join(', ')}`)); const { AIReviewAgent } = await import('./ai/AIReviewAgent.js'); const agent = new AIReviewAgent({ ...aiConfig, providers: enabledProviders, // Use only enabled providers }); const result = await agent.performMultiLLMReview('', { filePath: path, language: 'unknown', framework: 'unknown', totalLines: 0, }); console.log(chalk.green('AI review completed!')); console.log(JSON.stringify(result, null, 2)); } else { // Standard review using WOARUEngine const { WOARUEngine } = await import('./core/WOARUEngine.js'); const engine = new WOARUEngine(); const result = await engine.analyzeProject(path); console.log(chalk.green('\\n✅ Review completed!')); if (result.setup_recommendations.length > 0) { console.log(chalk.yellow('\\n🔧 Issues found:')); result.setup_recommendations.forEach((rec, index) => { console.log(` ${index + 1}. ${rec}`); }); } else { console.log(chalk.green('\\n✅ No issues found - code looks good!')); } } } catch (error) { console.error(chalk.red('Review failed:'), error); } }); // Analyze Command with AI subcommand const analyzeCmd = program .command('analyze') .description(t('commands.analyze.description')) .option('-p, --path <path>', 'Path to analyze', process.cwd()) .action(async (options) => { try { console.log(chalk.cyan(t('woaru_engine.analyzing_project'))); const { WOARUEngine } = await import('./core/WOARUEngine.js'); const engine = new WOARUEngine(); const result = await engine.analyzeProject(options.path); // Fix audit configuration for production readiness audit if (result && typeof result === 'object') { const auditConfig = { language: result.language || 'typescript', projectType: result.framework && result.framework.length > 0 ? 'fullstack' : 'library', frameworks: Array.isArray(result.framework) ? result.framework : (result.framework ? [result.framework] : []) }; // Store audit config for production auditor result.auditConfig = auditConfig; } // Display results console.log(chalk.green('\\n✅ Analysis completed!')); const recommendationCount = result.setup_recommendations.length + result.tool_suggestions.length + result.framework_specific_tools.length; console.log(chalk.blue(`\\n📊 Found ${recommendationCount} recommendations`)); if (recommendationCount > 0) { console.log(chalk.yellow('\\n🔧 Recommendations:')); if (result.setup_recommendations.length > 0) { console.log(chalk.cyan('\\nSetup Recommendations:')); result.setup_recommendations.forEach((rec, index) => { console.log(` ${index + 1}. ${rec}`); }); } if (result.tool_suggestions.length > 0) { console.log(chalk.cyan('\\nTool Suggestions:')); result.tool_suggestions.forEach((tool, index) => { console.log(` ${index + 1}. ${tool}`); }); } if (result.framework_specific_tools.length > 0) { console.log(chalk.cyan('\\nFramework-specific Tools:')); result.framework_specific_tools.forEach((tool, index) => { console.log(` ${index + 1}. ${tool}`); }); } } else { console.log(chalk.green(t('woaru_engine.project_well_configured'))); } } catch (error) { console.error(chalk.red('Analysis failed:'), error); } }); // Add AI subcommand to analyze analyzeCmd .command('ai') .description(t('commands.analyze_ai.description')) .option('-p, --path <path>', 'Path to analyze', process.cwd()) .option('--provider <provider>', 'AI provider to use') .option('--comprehensive', 'Run comprehensive AI analysis') .action(async (options) => { try { // Check AI configuration with visual status const { isAiReady, displayAiStatus } = await import('./utils/ai-helpers.js'); const isAiConfigured = await isAiReady(); if (!isAiConfigured) { console.log(chalk.yellow('AI is required for analysis.')); await displayAiStatus(true); // Show compact status console.log(chalk.yellow(t('ai_helpers.config_setup_hint'))); return; } console.log(chalk.cyan(t('woaru_engine.analyzing_project'))); console.log(chalk.blue('🤖 AI-powered comprehensive analysis enabled')); const { WOARUEngine } = await import('./core/WOARUEngine.js'); const engine = new WOARUEngine(); // Run analysis with AI enhancement const result = await engine.analyzeProject(options.path); // Run AI review if configured try { const { ConfigLoader } = await import('./ai/ConfigLoader.js'); const configLoader = ConfigLoader.getInstance(); const config = await configLoader.loadConfig(); if (config && config.providers && Object.keys(config.providers).length > 0) { const { AIReviewAgent } = await import('./ai/AIReviewAgent.js'); const aiAgent = new AIReviewAgent(config); // Use the correct method name from AIReviewAgent // AIReviewAgent doesn't have reviewCode method, use performMultiLLMReview instead console.log('🔍 Performing AI code review...'); // For now, just indicate AI analysis is enabled console.log('✅ AI analysis integration ready (detailed implementation pending)'); } else { console.log(chalk.yellow('\\n⚠️ No AI providers configured. Use "woaru ai setup" to configure AI analysis.')); } } catch (aiError) { console.warn(chalk.yellow('AI analysis failed, showing standard results:'), aiError); } // Display standard results console.log(chalk.green('\\n✅ Analysis completed!')); const recommendationCount = result.setup_recommendations.length + result.tool_suggestions.length + result.framework_specific_tools.length; console.log(chalk.blue(`\\n📊 Found ${recommendationCount} recommendations`)); if (recommendationCount > 0) { console.log(chalk.yellow('\\n🔧 Recommendations:')); if (result.setup_recommendations.length > 0) { console.log(chalk.cyan('\\nSetup Recommendations:')); result.setup_recommendations.forEach((rec, index) => { console.log(` ${index + 1}. ${rec}`); }); } if (result.tool_suggestions.length > 0) { console.log(chalk.cyan('\\nTool Suggestions:')); result.tool_suggestions.forEach((tool, index) => { console.log(` ${index + 1}. ${tool}`); }); } if (result.framework_specific_tools.length > 0) { console.log(chalk.cyan('\\nFramework-specific Tools:')); result.framework_specific_tools.forEach((tool, index) => { console.log(` ${index + 1}. ${tool}`); }); } } else { console.log(chalk.green(t('woaru_engine.project_well_configured'))); } } catch (error) { console.error(chalk.red('AI Analysis failed:'), error); } }); // Rollback Command program .command('rollback') .description(t('commands.rollback.description')) .argument('<tool>', 'Tool to rollback') .action(tool => { console.log(chalk.yellow(`Rollback command not implemented yet. Would rollback: ${tool}`)); }); // Message Command - Send WOARU reports via terminal or webhook program .command('message') .description(t('commands.message.description')) .option('--latest', 'Send latest report only') .option('--type <type>', 'Filter by report type (analyze|review|audit|llm-review)') .option('--webhook <url>', 'Send to webhook URL (Slack/Discord compatible)') .option('--verbose', 'Show detailed output and error information') .option('--format <format>', 'Output format: markdown or json', 'markdown') .action(async (options) => { try { console.log(chalk.cyan('📤 Starting WOARU Message Handler...')); // Input validation if (options.type && !['analyze', 'review', 'audit', 'llm-review', 'all'].includes(options.type)) { console.error(chalk.red('❌ Invalid type. Use: analyze, review, audit, llm-review, or all')); return; } if (options.format && !['markdown', 'json'].includes(options.format)) { console.error(chalk.red('❌ Invalid format. Use: markdown or json')); return; } // Import and execute MessageHandler const { MessageHandler } = await import('./message/MessageHandler.js'); await MessageHandler.send({ type: options.type || 'all', latest: options.latest || false, url: options.webhook, format: options.format || 'markdown', verbose: options.verbose || false }); console.log(chalk.green('✅ Message handler completed successfully')); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(chalk.red(`❌ Message command failed: ${errorMessage}`)); if (options.verbose && error instanceof Error && error.stack) { console.error(chalk.gray('Stack trace:'), error.stack); } process.exit(1); } }); // Config Command program .command('config') .description(t('commands.config.description')) .action(() => { console.log(chalk.yellow(t('command_not_implemented.config'))); }); // Language Command program .command('language') .description(t('commands.language.description')) .action(async () => { const { showLanguageStatus, promptLanguageSelection } = await import('./config/languageSetup.js'); // Show current language status await showLanguageStatus(); // Ask if user wants to change language const { default: inquirer } = await import('inquirer'); const { change } = await inquirer.prompt([ { type: 'confirm', name: 'change', message: t('language_command.select_new'), default: false, }, ]); if (change) { try { const newLanguage = await promptLanguageSelection(); const { setUserLanguage } = await import('./config/i18n.js'); await setUserLanguage(newLanguage); console.log(chalk.green(t('language_command.language_changed', { language: t(`language_selection.${newLanguage === 'en' ? 'english' : 'german'}`), }))); console.log(chalk.gray(t('language_command.next_usage_note'))); } catch (error) { console.error(chalk.red(t('language_command.error_changing')), error); } } else { console.log(chalk.blue(t('language_command.language_unchanged', { language: t(`language_selection.${(await getCurrentLanguage()) === 'en' ? 'english' : 'german'}`), }))); } }); // Setze die Hauptbeschreibung am Ende, wenn i18n garantiert vollständig geladen ist program.description(t('cli.main.description')); } // Helper function to get current language async function getCurrentLanguage() { const { getCurrentLanguage: getI18nLanguage } = await import('./config/i18n.js'); return getI18nLanguage(); } // Documentation command handler async function runDocumentationCommand(documentationType, options) { const projectPath = process.cwd(); try { // Import required modules const { ConfigLoader } = await import('./ai/ConfigLoader.js'); const { DocumentationAgent } = await import('./ai/DocumentationAgent.js'); const { PromptManager } = await import('./ai/PromptManager.js'); // Get AI configuration const configLoader = ConfigLoader.getInstance(); const aiConfig = await configLoader.loadConfig(); if (!aiConfig || aiConfig.providers.length === 0) { console.log(chalk.yellow('⚠️ No AI providers configured.')); console.log(chalk.gray('Run: woaru ai setup')); return; } // Load prompt templates using PromptManager const promptManager = PromptManager.getInstance(); await promptManager.initialize(); // Load documentation-specific prompts const promptTemplates = {}; try { promptTemplates[`docu_${documentationType}`] = await promptManager.loadPrompt('global', `docu_${documentationType}`); } catch (error) { console.log(chalk.yellow(`⚠️ Could not load documentation prompt template for ${documentationType}`)); console.log(chalk.gray('Using default AI review prompts')); } // Create documentation agent const docAgent = new DocumentationAgent(aiConfig, promptTemplates); // Determine which files to document let filesToDocument = []; if (options.pathOnly) { // Document specific files or directories const { glob } = await import('glob'); const pattern = options.pathOnly.endsWith('/') ? `${options.pathOnly}**/*.{js,ts,jsx,tsx,py,java,go,rs,php,rb}` : options.pathOnly; filesToDocument = await glob(pattern, { cwd: projectPath }); } else if (options.local) { // Document uncommitted changes const { exec } = await import('child_process'); const { promisify } = await import('util'); const execAsync = promisify(exec); try { const { stdout } = await execAsync('git diff --name-only', { cwd: projectPath }); filesToDocument = stdout.trim().split('\n').filter(f => f.length > 0); } catch (error) { console.log(chalk.yellow('⚠️ Git not available or not in a git repository')); console.log(chalk.gray('Using all supported files in current directory')); const { glob } = await import('glob'); filesToDocument = await glob('**/*.{js,ts,jsx,tsx,py,java,go,rs,php,rb}', { cwd: projectPath }); } } else if (options.git) { // Document changes since specified branch const { exec } = await import('child_process'); const { promisify } = await import('util'); const execAsync = promisify(exec); try { const { stdout } = await execAsync(`git diff --name-only ${options.git}`, { cwd: projectPath }); filesToDocument = stdout.trim().split('\n').filter(f => f.length > 0); } catch (error) { console.error(chalk.red(`Failed to get changes since branch ${options.git}:`), error); return; } } else { // Document all supported files const { glob } = await import('glob'); filesToDocument = await glob('**/*.{js,ts,jsx,tsx,py,java,go,rs,php,rb}', { cwd: projectPath, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**', 'coverage/**'] }); } if (filesToDocument.length === 0) { console.log(chalk.yellow('📂 No files found to document.')); return; } console.log(chalk.blue(`📝 Found ${filesToDocument.length} files to document`)); // Generate documentation const results = await docAgent.generateDocumentation(filesToDocument.map(f => path.resolve(projectPath, f)), projectPath, documentationType); if (results.length === 0) { console.log(chalk.yellow('📝 No documentation generated. Files may already be documented.')); return; } console.log(chalk.green(`✅ Generated documentation for ${results.length} items`)); // Preview mode - show what would be changed if (options.preview) { console.log(chalk.cyan('\n📋 Preview of documentation changes:')); for (const result of results.slice(0, 3)) { // Show first 3 as examples console.log(chalk.blue(`\n📄 ${path.relative(projectPath, result.filePath)}:`)); console.log(chalk.gray('─'.repeat(50))); console.log(result.generatedDoc); if (results.length > 3 && result === results[2]) { console.log(chalk.gray(`... and ${results.length - 3} more files`)); } } console.log(chalk.yellow('\n💡 Run without --preview to apply changes')); return; } // Apply documentation if not in preview mode if (!options.force) { const { default: inquirer } = await import('inquirer'); const { confirm } = await inquirer.prompt([ { type: 'confirm', name: 'confirm', message: `Apply documentation to ${results.length} items?`, default: true, }, ]); if (!confirm) { console.log(chalk.gray('Documentation cancelled.')); return; } } // Apply the documentation await docAgent.applyDocumentation(results); console.log(chalk.green(`\n🎉 Documentation applied successfully!`)); console.log(chalk.gray(`Generated ${documentationType} documentation for ${results.length} items`)); } catch (error) { console.error(chalk.red('❌ Documentation generation failed:'), error); throw error; } } // Dies ist der EINZIGE Startpunkt der Anwendung. async function main() { try { // SCHRITT 1: Initialisiere i18n synchron (keine Wartezeit mehr nötig) initializeI18n(); // SCHRITT 2: Prüfe ob keine Argumente übergeben wurden (für Splash Screen) if (process.argv.length === 2) { // Zeige Splash Screen displaySplashScreen(); return; } // SCHRITT 3: Erstelle das Programm und definiere die Befehle const program = new Command(); defineCommands(program); // SCHRITT 4: Führe das Programm aus await program.parseAsync(process.argv); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error(chalk.red('❌ A critical error occurred:'), errorMessage); // Report error to Sentry with context Sentry.withScope((scope) => { scope.setTag('component', 'cli-main'); scope.setLevel('fatal'); scope.setContext('process', { argv: process.argv, cwd: process.cwd(), version: getVersion(), }); Sentry.captureException(error); }); process.exit(1); } } // Helper function to center text in the ASCII box and ensure exact length function centerText(text, availableWidth) { // Use simple string length instead of visual width to avoid emoji issues const textLength = text.length; const padding = Math.max(0, Math.floor((availableWidth - textLength) / 2)); const rightPadding = availableWidth - textLength - padding; let result = ' '.repeat(padding) + text + ' '.repeat(Math.max