UNPKG

woaru

Version:

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

792 lines 34.7 kB
import { ProjectAnalyzer } from '../analyzer/ProjectAnalyzer.js'; import { CodeAnalyzer, } from '../analyzer/CodeAnalyzer.js'; import { DatabaseManager } from '../database/DatabaseManager.js'; import { PluginManager } from '../plugins/PluginManager.js'; import { ActionManager } from '../actions/ActionManager.js'; import { ProductionReadinessAuditor } from '../auditor/ProductionReadinessAuditor.js'; import { QualityRunner } from '../quality/QualityRunner.js'; import { NotificationManager } from '../supervisor/NotificationManager.js'; import chalk from 'chalk'; import * as path from 'path'; import { exec } from 'child_process'; import { promisify } from 'util'; import { t } from '../config/i18n.js'; const execAsync = promisify(exec); /** * WOARUEngine - Core orchestration engine for the WOARU project analysis and setup system * * This class serves as the central coordinator for all WOARU operations, managing * project analysis, code quality checks, security audits, and automated setup processes. * It orchestrates multiple specialized analyzers and managers to provide comprehensive * development environment recommendations. * * @example * ```typescript * const engine = new WOARUEngine(); * const result = await engine.analyzeProject('./my-project'); * console.log(result.setup_recommendations); * ``` * * @class WOARUEngine * @since 1.0.0 */ export class WOARUEngine { /** Handles project structure and framework detection */ projectAnalyzer; /** Performs static code analysis and quality checks */ codeAnalyzer; /** Manages tool configurations and recommendations database */ databaseManager; /** Coordinates language-specific plugin functionality */ pluginManager; /** Executes recommended actions and installations */ actionManager; /** Runs comprehensive code quality checks */ qualityRunner; /** Manages user notifications across different channels */ notificationManager; /** * Initializes a new instance of the WOARU Engine with all required components * * Sets up all internal analyzers and managers with default configurations. * The engine is ready to analyze projects immediately after construction. */ constructor() { this.projectAnalyzer = new ProjectAnalyzer(); this.codeAnalyzer = new CodeAnalyzer(); this.databaseManager = new DatabaseManager(); this.pluginManager = new PluginManager(); this.actionManager = new ActionManager(); this.notificationManager = new NotificationManager({ terminal: false, desktop: false, }); this.qualityRunner = new QualityRunner(this.notificationManager); } /** * Performs comprehensive analysis of a project directory * * This method orchestrates multiple analysis phases: * 1. Project structure and framework detection * 2. Code quality and pattern analysis * 3. Security vulnerability scanning * 4. Infrastructure configuration audit * 5. Production readiness assessment * * @param {string} projectPath - Absolute or relative path to the project directory * @returns {Promise<AnalysisResult>} Complete analysis results including recommendations, * security findings, and improvement suggestions * @throws {Error} If the project path is invalid or analysis fails * * @example * ```typescript * const results = await engine.analyzeProject('/path/to/project'); * if (results.security_summary.critical > 0) { * console.error('Critical security issues found!'); * } * ``` */ async analyzeProject(projectPath) { try { console.log(chalk.blue(t('woaru_engine.analyzing_project'))); const analysis = await this.projectAnalyzer.analyzeProject(projectPath); const metadata = await this.projectAnalyzer.getProjectMetadata(projectPath); console.log(chalk.gray(t('woaru_engine.project_info', { name: metadata.name, version: metadata.version, }))); console.log(chalk.gray(t('woaru_engine.language_info', { language: analysis.language }))); console.log(chalk.gray(t('woaru_engine.frameworks_info', { frameworks: analysis.framework.join(', ') || t('woaru_engine.none_detected'), }))); // Analyze code for specific insights console.log(chalk.blue(t('woaru_engine.analyzing_codebase'))); const codeInsights = await this.codeAnalyzer.analyzeCodebase(projectPath, analysis.language); // Get recommendations from plugins with code insights const recommendations = this.pluginManager.getAllRecommendations(analysis); // Enhance recommendations with code insights this.enhanceRecommendationsWithInsights(recommendations, codeInsights); const refactorSuggestions = this.pluginManager.getAllRefactorSuggestions(analysis); const frameworkSpecificPackages = this.pluginManager.getAllSpecificPackages(analysis); // Detect already installed tools const installedTools = await this.detectInstalledTools(analysis); // Run comprehensive security analysis console.log(chalk.blue(t('woaru_engine.security_analysis'))); const securityResults = await this.runComprehensiveSecurityAnalysis(projectPath); // Run infrastructure security check console.log(chalk.blue(t('woaru_engine.infrastructure_audit'))); const infrastructureResults = await this.runInfrastructureSecurityCheck(projectPath); // Run production-readiness audit (including security) console.log(chalk.blue(t('woaru_engine.production_audit'))); const productionAuditor = new ProductionReadinessAuditor(projectPath); const auditConfig = { language: analysis.language, frameworks: analysis.framework, projectType: this.determineProjectType(analysis), }; const productionAudits = await productionAuditor.auditProject(auditConfig); // Combine all security findings const allSecurityFindings = this.combineSecurityFindings(securityResults, infrastructureResults); // Group production audits by category and priority const securityAudits = productionAudits.filter(audit => audit.category === 'security'); const criticalSecurityAudits = securityAudits.filter(audit => audit.priority === 'critical'); const highSecurityAudits = securityAudits.filter(audit => audit.priority === 'high'); // Calculate comprehensive security metrics const totalCritical = allSecurityFindings.critical + criticalSecurityAudits.length; const totalHigh = allSecurityFindings.high + highSecurityAudits.length; // Log critical security issues immediately if (totalCritical > 0) { console.log(chalk.red(t('woaru_engine.critical_security_issues', { count: totalCritical }))); } if (totalHigh > 0) { console.log(chalk.yellow(t('woaru_engine.high_security_issues', { count: totalHigh }))); } // Generate Claude automation suggestions const claudeAutomations = this.generateClaudeAutomations(analysis); return { setup_recommendations: recommendations.map(r => `Install ${r.tool} for ${r.category}: ${r.reason}`), tool_suggestions: recommendations.map(r => r.tool), framework_specific_tools: frameworkSpecificPackages, refactor_suggestions: refactorSuggestions, installed_tools_detected: installedTools, claude_automations: claudeAutomations, code_insights: Array.from(codeInsights.entries()).map(([tool, insight]) => ({ tool, reason: insight.reason, evidence: insight.evidence, severity: insight.severity, })), // Add production audit results production_audits: productionAudits.map(audit => ({ category: audit.category, check: audit.check, priority: audit.priority, message: audit.message, recommendation: audit.recommendation, packages: audit.packages || [], files: audit.files || [], })), security_summary: { total_issues: allSecurityFindings.total + securityAudits.length, critical: totalCritical, high: totalHigh, medium: allSecurityFindings.medium + securityAudits.filter(audit => audit.priority === 'medium').length, low: allSecurityFindings.low + securityAudits.filter(audit => audit.priority === 'low').length, health_score: this.calculateComprehensiveSecurityScore(allSecurityFindings, securityAudits, infrastructureResults), // Note: Extended metrics stored in detailed_security instead recommendations: this.generateSecurityRecommendations(allSecurityFindings, securityAudits), }, // Add detailed security results (extended data) detailed_security: { dependency_vulnerabilities: securityResults.flatMap(r => r.findings.map(f => ({ id: f.cve || `${f.tool}-${f.file || f.package}`, title: f.title, severity: (f.severity === 'info' ? 'low' : f.severity === 'critical' || f.severity === 'high' || f.severity === 'medium' || f.severity === 'low' ? f.severity : 'low'), description: f.description || '', packageName: f.package || 'unknown', vulnerableVersions: f.version || 'unknown', patchedVersions: f.fixedIn, recommendation: f.recommendation || '', }))), infrastructure_security: infrastructureResults || undefined, configuration_audits: securityAudits, }, }; } catch (error) { throw new Error(t('woaru_engine.analysis_failed', { error: error instanceof Error ? error.message : 'Unknown error', })); } } /** * Automatically sets up recommended tools and configurations for a project * * Based on the analysis results, this method can: * - Install recommended development tools * - Configure linters and formatters * - Set up pre-commit hooks * - Initialize testing frameworks * * @param {string} projectPath - Path to the project directory to set up * @param {SetupOptions} options - Configuration options for the setup process * @param {boolean} options.dryRun - If true, only simulates actions without making changes * @param {boolean} options.force - If true, overwrites existing configurations * @param {string[]} options.skip - Array of tool names to skip during setup * @returns {Promise<boolean>} True if setup completed successfully, false otherwise * @throws {Error} If setup fails due to permissions or other issues * * @example * ```typescript * // Perform a dry run first * await engine.setupProject('./my-project', { dryRun: true }); * // Then execute actual setup * const success = await engine.setupProject('./my-project'); * ``` */ async setupProject(projectPath, options = {}) { try { const analysis = await this.projectAnalyzer.analyzeProject(projectPath); const recommendations = this.pluginManager.getAllRecommendations(analysis); if (recommendations.length === 0) { console.log(chalk.green(t('woaru_engine.project_well_configured'))); return true; } console.log(chalk.blue(t('woaru_engine.recommendations_found', { count: recommendations.length, }))); if (options.dryRun) { console.log(chalk.yellow(t('woaru_engine.dry_run_mode'))); recommendations.forEach(rec => { console.log(chalk.gray(t('woaru_engine.dry_run_item', { tool: rec.tool, reason: rec.reason, }))); }); return true; } const result = await this.actionManager.executeRecommendations(projectPath, recommendations, options); return result.success; } catch (error) { console.error(chalk.red(t('woaru_engine.setup_failed', { error: error instanceof Error ? error.message : 'Unknown error', }))); return false; } } async updateDatabase() { console.log(chalk.blue(t('woaru_engine.updating_database'))); const success = await this.databaseManager.updateDatabase(); if (success) { console.log(chalk.green(t('woaru_engine.database_updated'))); } else { console.log(chalk.red(t('woaru_engine.database_update_failed'))); } return success; } async detectInstalledTools(analysis) { try { const installedTools = []; const allDeps = [...analysis.dependencies, ...analysis.devDependencies]; const toolChecks = this.getToolChecks(); const projectPath = this.getValidatedProjectPath(analysis.projectPath); for (const [tool, check] of Object.entries(toolChecks)) { const hasPackage = this.checkPackageDependency(check.packages, allDeps); const hasConfig = await this.checkConfigFiles(check.configs || [], projectPath); if (hasPackage || hasConfig) { installedTools.push(tool); } } return installedTools; } catch (error) { console.warn(t('woaru_engine.tool_detection_warning', { error: error instanceof Error ? error.message : 'Unknown error', })); return []; } } getToolChecks() { return { eslint: { packages: ['eslint'], configs: [ '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml', '.eslintrc.yaml', '.eslintrc', 'eslint.config.js', ], }, prettier: { packages: ['prettier'], configs: [ '.prettierrc', '.prettierrc.json', '.prettierrc.yml', '.prettierrc.yaml', '.prettierrc.js', 'prettier.config.js', ], }, husky: { packages: ['husky'], configs: ['.husky', '.git/hooks'], }, jest: { packages: ['jest'], configs: ['jest.config.js', 'jest.config.ts', 'jest.config.json'], }, typescript: { packages: ['typescript'], configs: ['tsconfig.json'], }, tailwindcss: { packages: ['tailwindcss'], configs: ['tailwind.config.js', 'tailwind.config.ts'], }, postcss: { packages: ['postcss'], configs: ['postcss.config.js', 'postcss.config.ts'], }, commitlint: { packages: ['@commitlint/cli', '@commitlint/config-conventional'], configs: [ 'commitlint.config.js', '.commitlintrc.js', '.commitlintrc.json', ], }, webpack: { packages: ['webpack'], configs: ['webpack.config.js', 'webpack.config.ts'], }, vite: { packages: ['vite'], configs: ['vite.config.js', 'vite.config.ts'], }, rollup: { packages: ['rollup'], configs: ['rollup.config.js', 'rollup.config.ts'], }, babel: { packages: ['@babel/core', 'babel-core'], configs: [ '.babelrc', '.babelrc.js', '.babelrc.json', 'babel.config.js', ], }, }; } getValidatedProjectPath(projectPath) { const validPath = projectPath || process.cwd(); try { return path.resolve(validPath); } catch { throw new Error(t('woaru_engine.invalid_project_path', { path: validPath })); } } checkPackageDependency(packages, allDeps) { return packages.some(pkg => allDeps.includes(pkg)); } async checkConfigFiles(configFiles, projectPath) { const fs = await import('fs-extra'); for (const configFile of configFiles) { try { const configPath = path.join(projectPath, configFile); if (await fs.pathExists(configPath)) { return true; } } catch { // Continue checking other config files continue; } } return false; } generateClaudeAutomations(analysis) { const automations = []; // Framework-specific automations if (analysis.framework.includes('nextjs')) { automations.push(t('woaru_engine.automations.nextjs_api_routes')); automations.push(t('woaru_engine.automations.nextjs_components')); automations.push(t('woaru_engine.automations.nextjs_middleware')); } if (analysis.framework.includes('react')) { automations.push(t('woaru_engine.automations.react_functional_components')); automations.push(t('woaru_engine.automations.react_custom_hooks')); automations.push(t('woaru_engine.automations.react_storybook')); } // Language-specific automations if (analysis.language === 'TypeScript') { automations.push(t('woaru_engine.automations.typescript_interfaces')); automations.push(t('woaru_engine.automations.typescript_strict')); automations.push(t('woaru_engine.automations.typescript_utility_types')); } // Testing automations if (!analysis.devDependencies.includes('jest') && !analysis.devDependencies.includes('vitest')) { automations.push(t('woaru_engine.automations.setup_testing')); automations.push(t('woaru_engine.automations.generate_unit_tests')); } // Documentation automations automations.push(t('woaru_engine.automations.generate_readme')); automations.push(t('woaru_engine.automations.generate_contributing')); automations.push(t('woaru_engine.automations.generate_api_docs')); return automations; } enhanceRecommendationsWithInsights(recommendations, codeInsights) { recommendations.forEach((rec) => { const insight = codeInsights.get(rec.tool); if (insight) { rec.reason = insight.reason; rec.evidence = insight.evidence.join('; '); rec.priority = insight.severity === 'critical' ? 'high' : insight.severity === 'high' ? 'high' : insight.severity === 'medium' ? 'medium' : 'low'; } }); } determineProjectType(analysis) { // Check for frontend frameworks const frontendFrameworks = ['react', 'vue', 'angular', 'svelte', 'next']; const hasFrontend = analysis.framework.some(f => frontendFrameworks.includes(f)); // Check for backend frameworks const backendFrameworks = ['express', 'fastify', 'koa', 'nest']; const hasBackend = analysis.framework.some(f => backendFrameworks.includes(f)); // Check dependencies for more clues const allDeps = [...analysis.dependencies, ...analysis.devDependencies]; const hasServerDeps = allDeps.some(dep => ['express', 'fastify', 'koa', '@nestjs/core', 'http-server'].includes(dep)); const hasFrontendDeps = allDeps.some(dep => ['react', 'vue', '@angular/core', 'svelte'].includes(dep)); // Check for CLI tools const hasCliDeps = allDeps.some(dep => ['commander', 'yargs', 'inquirer', '@oclif/core'].includes(dep)); // Determine type based on evidence if (hasCliDeps) return 'cli'; if (hasFrontend && (hasBackend || hasServerDeps)) return 'fullstack'; if (hasFrontend || hasFrontendDeps) return 'frontend'; if (hasBackend || hasServerDeps) return 'backend'; // Default to library if no clear indicators return 'library'; } calculateSecurityHealthScore(securityAudits) { if (securityAudits.length === 0) return 100; let score = 100; securityAudits.forEach(audit => { switch (audit.priority) { case 'critical': score -= 25; break; case 'high': score -= 15; break; case 'medium': score -= 8; break; case 'low': score -= 3; break; } }); return Math.max(0, score); } /** * Run comprehensive security analysis using multiple tools */ async runComprehensiveSecurityAnalysis(_projectPath) { console.log(chalk.gray(t('woaru_engine.security_scan.running_snyk_gitleaks'))); try { // Get all project files for security scanning const allFiles = []; // For comprehensive analysis, we can scan the entire project const securityResults = await this.qualityRunner.runSecurityChecksForReview(allFiles); // Log summary of findings let criticalFindings = 0; securityResults.forEach(result => { criticalFindings += result.summary.critical; if (result.error) { console.log(chalk.red(t('woaru_engine.security_scan.tool_error', { tool: result.tool, error: result.error, }))); } else if (result.summary.total > 0) { console.log(chalk.yellow(t('woaru_engine.security_scan.issues_found', { tool: result.tool, count: result.summary.total, }))); } else { console.log(chalk.green(t('woaru_engine.security_scan.no_issues', { tool: result.tool }))); } }); if (criticalFindings > 0) { console.log(chalk.red(t('woaru_engine.security_scan.critical_vulnerabilities', { count: criticalFindings, }))); } return securityResults; } catch (error) { console.log(chalk.red(t('woaru_engine.security_scan.analysis_failed', { error: error }))); return []; } } /** * Run infrastructure security check using Trivy */ async runInfrastructureSecurityCheck(projectPath) { console.log(chalk.gray(t('woaru_engine.infrastructure_scan.scanning_containers'))); try { // Check if Trivy is installed await execAsync('which trivy'); const findings = []; const scanTargets = []; // Look for Docker files const dockerFile = path.join(projectPath, 'Dockerfile'); if (await this.fileExists(dockerFile)) { scanTargets.push({ type: 'dockerfile', path: dockerFile }); } // Look for docker-compose files const composeFiles = [ 'docker-compose.yml', 'docker-compose.yaml', 'compose.yml', ]; for (const file of composeFiles) { const composePath = path.join(projectPath, file); if (await this.fileExists(composePath)) { scanTargets.push({ type: 'compose', path: composePath }); } } // Look for Kubernetes manifests const k8sDir = path.join(projectPath, 'k8s'); if (await this.fileExists(k8sDir)) { scanTargets.push({ type: 'kubernetes', path: k8sDir }); } // Scan each target for (const target of scanTargets) { try { const { stdout } = await execAsync(`trivy config --format json "${target.path}"`, { cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }); if (stdout) { const results = JSON.parse(stdout); if (results.Results) { results.Results.forEach((result) => { const resultWithMisconfigs = result; if (resultWithMisconfigs.Misconfigurations) { resultWithMisconfigs.Misconfigurations.forEach((misc) => { findings.push({ tool: 'trivy', type: 'misconfiguration', severity: (misc.Severity?.toLowerCase() || 'medium'), title: misc.Title, description: misc.Description, file: target.path, recommendation: misc.Resolution, references: misc.References, }); }); } }); } } console.log(chalk.green(t('woaru_engine.infrastructure_scan.trivy_scanned', { type: target.type, path: target.path, }))); } catch { console.log(chalk.yellow(t('woaru_engine.infrastructure_scan.trivy_scan_failed', { path: target.path, }))); } } if (scanTargets.length === 0) { console.log(chalk.gray(t('woaru_engine.infrastructure_scan.no_infrastructure_files'))); return null; } const summary = { total: findings.length, critical: findings.filter(f => f.severity === 'critical').length, high: findings.filter(f => f.severity === 'high').length, medium: findings.filter(f => f.severity === 'medium').length, low: findings.filter(f => f.severity === 'low').length, info: findings.filter(f => f.severity === 'info').length, }; if (findings.length > 0) { console.log(chalk.yellow(t('woaru_engine.infrastructure_scan.issues_found', { count: findings.length, }))); } else { console.log(chalk.green(t('woaru_engine.infrastructure_scan.no_issues'))); } return { tool: 'trivy', scanTime: new Date(), findings, summary, targets_scanned: scanTargets.length, }; } catch (error) { if (error instanceof Error && error.message.includes('command not found')) { console.log(chalk.gray(t('woaru_engine.infrastructure_scan.trivy_not_installed'))); console.log(chalk.gray(t('woaru_engine.infrastructure_scan.trivy_install_hint'))); } else { console.log(chalk.red(t('woaru_engine.infrastructure_scan.scan_failed', { error: error }))); } return null; } } /** * Combine security findings from different tools */ combineSecurityFindings(securityResults, infrastructureResults) { let total = 0; let critical = 0; let high = 0; let medium = 0; let low = 0; let secrets = 0; let vulnerabilities = 0; // Count findings from security tools (Snyk, Gitleaks) securityResults.forEach(result => { total += result.summary.total; critical += result.summary.critical; high += result.summary.high; medium += result.summary.medium; low += result.summary.low; if (result.tool === 'gitleaks') { secrets += result.findings.length; } else if (result.tool === 'snyk') { vulnerabilities += result.findings.length; } }); // Add infrastructure findings if (infrastructureResults) { total += infrastructureResults.summary.total; critical += infrastructureResults.summary.critical; high += infrastructureResults.summary.high; medium += infrastructureResults.summary.medium; low += infrastructureResults.summary.low; } return { total, critical, high, medium, low, secrets, vulnerabilities, }; } /** * Calculate comprehensive security score including all security aspects */ calculateComprehensiveSecurityScore(allFindings, securityAudits, infrastructureResults) { let score = 100; // Penalize based on security findings score -= allFindings.critical * 30; // Critical vulnerabilities are severe score -= allFindings.high * 20; score -= allFindings.medium * 10; score -= allFindings.low * 3; score -= allFindings.secrets * 25; // Exposed secrets are very serious // Penalize based on production audit findings securityAudits.forEach(audit => { switch (audit.priority) { case 'critical': score -= 25; break; case 'high': score -= 15; break; case 'medium': score -= 8; break; case 'low': score -= 3; break; } }); // Penalize for infrastructure issues if (infrastructureResults) { score -= infrastructureResults.summary.critical * 20; score -= infrastructureResults.summary.high * 12; score -= infrastructureResults.summary.medium * 6; } return Math.max(0, Math.min(100, score)); } /** * Get list of security tools that were used in the analysis */ getSecurityToolsUsed(securityResults, infrastructureResults) { const tools = new Set(); securityResults.forEach(result => { if (!result.error) { tools.add(result.tool); } }); if (infrastructureResults && !('error' in infrastructureResults)) { tools.add('trivy'); } return Array.from(tools); } /** * Generate actionable security recommendations */ generateSecurityRecommendations(allFindings, securityAudits) { const recommendations = []; if (allFindings.critical > 0) { recommendations.push(t('woaru_engine.security_recommendations.critical_urgent', { count: allFindings.critical, })); } if (allFindings.secrets > 0) { recommendations.push(t('woaru_engine.security_recommendations.secrets_urgent', { count: allFindings.secrets, })); } if (allFindings.vulnerabilities > 0) { recommendations.push(t('woaru_engine.security_recommendations.update_dependencies', { count: allFindings.vulnerabilities, })); } if (allFindings.high > 0) { recommendations.push(t('woaru_engine.security_recommendations.high_severity', { count: allFindings.high, })); } const criticalAudits = securityAudits.filter(a => a.priority === 'critical'); if (criticalAudits.length > 0) { recommendations.push(t('woaru_engine.security_recommendations.configure_tools', { tools: criticalAudits.map(a => a.check).join(', '), })); } if (recommendations.length === 0) { recommendations.push(t('woaru_engine.security_recommendations.no_critical_issues')); } return recommendations; } /** * Helper to check if file exists */ async fileExists(filePath) { try { const fs = await import('fs-extra'); return await fs.pathExists(filePath); } catch { return false; } } } //# sourceMappingURL=WOARUEngine.js.map