UNPKG

acs-framework-cli

Version:

🚀 CLI inteligente para configurar automáticamente el Augmented Context Standard (ACS) Framework. Context-as-Code que convierte tu conocimiento en un activo versionado.

446 lines (373 loc) 15.3 kB
const fs = require('fs-extra'); const path = require('path'); const chalk = require('chalk'); /** * NUEVO SISTEMA DE DETECCIÓN DE COMPLEJIDAD * Reemplaza el primitivo conteo de directorios por análisis real * * Filosofía ACS: Contexto preciso, no ruido estadístico */ class SmartComplexityAnalyzer { constructor(targetDir) { this.targetDir = targetDir; this.analysis = { score: 0, factors: [], recommendations: [], estimatedSetupTime: '2-5 min', riskLevel: 'low' }; } async analyze() { console.log(chalk.yellow('🔍 Analizando complejidad real del proyecto...')); await this.analyzeStructuralComplexity(); await this.analyzeVolumeComplexity(); await this.analyzeTechnologicalComplexity(); await this.analyzeArchitecturalComplexity(); await this.analyzeDependencyComplexity(); this.calculateFinalComplexity(); return this.analysis; } async analyzeStructuralComplexity() { try { // Profundidad de anidamiento (indicador real de complejidad) const maxDepth = await this.calculateMaxDepth(this.targetDir, 0); if (maxDepth > 6) { this.addComplexityFactor('structure', 4, `Estructura muy profunda (${maxDepth} niveles)`); } else if (maxDepth > 4) { this.addComplexityFactor('structure', 2, `Estructura moderadamente profunda (${maxDepth} niveles)`); } // Dispersión de archivos importantes const configFiles = await this.findConfigurationFiles(); if (configFiles.length > 8) { this.addComplexityFactor('config', 3, `Múltiples configuraciones (${configFiles.length})`); } // Detección de monorepo const isMonorepo = await this.detectMonorepoPattern(); if (isMonorepo.detected) { this.addComplexityFactor('monorepo', 5, `Monorepo detectado: ${isMonorepo.type}`); this.analysis.recommendations.push('Considerar configuración modular por package'); } } catch (error) { console.log(chalk.gray(`⚠️ Error en análisis estructural: ${error.message}`)); } } async analyzeVolumeComplexity() { try { // Conteo inteligente de archivos (excluyendo ruido) const meaningfulFiles = await this.countMeaningfulFiles(); if (meaningfulFiles > 2000) { this.addComplexityFactor('volume', 6, `Proyecto masivo (${meaningfulFiles} archivos relevantes)`); this.analysis.estimatedSetupTime = '10-15 min'; } else if (meaningfulFiles > 500) { this.addComplexityFactor('volume', 3, `Proyecto grande (${meaningfulFiles} archivos)`); this.analysis.estimatedSetupTime = '5-8 min'; } else if (meaningfulFiles > 100) { this.addComplexityFactor('volume', 1, `Proyecto mediano (${meaningfulFiles} archivos)`); } } catch (error) { console.log(chalk.gray(`⚠️ Error en análisis de volumen: ${error.message}`)); } } async analyzeTechnologicalComplexity() { try { const technologies = await this.detectTechnologies(); if (technologies.length > 4) { this.addComplexityFactor('tech', 4, `Stack híbrido: ${technologies.slice(0, 3).join(', ')}...`); } else if (technologies.length > 2) { this.addComplexityFactor('tech', 2, `Múltiples tecnologías: ${technologies.join(', ')}`); } // Tecnologías que añaden complejidad inherente const complexTechs = technologies.filter(tech => ['kubernetes', 'docker-compose', 'microservices', 'graphql', 'webpack', 'babel'].includes(tech.toLowerCase()) ); if (complexTechs.length > 0) { this.addComplexityFactor('complex-tech', 3, `Tecnologías complejas: ${complexTechs.join(', ')}`); } } catch (error) { console.log(chalk.gray(`⚠️ Error en análisis tecnológico: ${error.message}`)); } } async analyzeArchitecturalComplexity() { try { // Patrones arquitectónicos complejos const architecturalPatterns = await this.detectArchitecturalPatterns(); if (architecturalPatterns.microservices) { this.addComplexityFactor('architecture', 5, 'Arquitectura de microservicios detectada'); } if (architecturalPatterns.layered && architecturalPatterns.layerCount > 3) { this.addComplexityFactor('architecture', 2, `Arquitectura en capas (${architecturalPatterns.layerCount})`); } } catch (error) { console.log(chalk.gray(`⚠️ Error en análisis arquitectónico: ${error.message}`)); } } async analyzeDependencyComplexity() { try { const deps = await this.analyzeDependencies(); if (deps.total > 100) { this.addComplexityFactor('deps', 3, `Muchas dependencias (${deps.total})`); } if (deps.outdated > 20) { this.addComplexityFactor('deps-outdated', 2, `Dependencias desactualizadas (${deps.outdated})`); this.analysis.recommendations.push('Actualizar dependencias antes del setup ACS'); } } catch (error) { console.log(chalk.gray(`⚠️ Error en análisis de dependencias: ${error.message}`)); } } // === MÉTODOS AUXILIARES === async calculateMaxDepth(dir, currentDepth, maxDepth = 0) { if (currentDepth > 10) return maxDepth; // Evitar recursión infinita try { const items = await fs.readdir(dir); let localMaxDepth = Math.max(maxDepth, currentDepth); for (const item of items) { if (item.startsWith('.') || item === 'node_modules') continue; const itemPath = path.join(dir, item); const stat = await fs.stat(itemPath); if (stat.isDirectory()) { const subDepth = await this.calculateMaxDepth(itemPath, currentDepth + 1, localMaxDepth); localMaxDepth = Math.max(localMaxDepth, subDepth); } } return localMaxDepth; } catch (error) { return maxDepth; } } async countMeaningfulFiles() { const ignoredPatterns = [ /node_modules/, /\.git/, /dist/, /build/, /coverage/, /\.next/, /\.nuxt/, /vendor/, /target/, /\.idea/, /\.vscode/, /\.DS_Store/, /\.log$/, /\.lock$/ ]; let count = 0; const countRecursive = async (dir, depth = 0) => { if (depth > 8) return; // Evitar análisis demasiado profundo try { const items = await fs.readdir(dir); for (const item of items) { const itemPath = path.join(dir, item); const relativePath = path.relative(this.targetDir, itemPath); // Filtrar ruido if (ignoredPatterns.some(pattern => pattern.test(relativePath))) { continue; } const stat = await fs.stat(itemPath); if (stat.isFile()) { count++; } else if (stat.isDirectory()) { await countRecursive(itemPath, depth + 1); } } } catch (error) { // Silencioso para evitar spam de errores } }; await countRecursive(this.targetDir); return count; } async findConfigurationFiles() { const configPatterns = [ 'package.json', 'composer.json', 'requirements.txt', 'go.mod', 'Cargo.toml', 'pom.xml', 'build.gradle', 'CMakeLists.txt', 'Makefile', 'docker-compose.yml', 'Dockerfile', 'docker-compose.yaml', 'tsconfig.json', 'babel.config.js', 'webpack.config.js', 'vite.config.js', 'next.config.js', 'nuxt.config.js', 'angular.json', 'vue.config.js', 'pubspec.yaml', 'Package.swift', 'project.clj', '.env', '.env.example', 'jest.config.js', 'vitest.config.js', 'cypress.config.js', 'eslint.config.js', '.eslintrc.js', 'prettier.config.js', 'tailwind.config.js', 'postcss.config.js' ]; const found = []; for (const pattern of configPatterns) { const configPath = path.join(this.targetDir, pattern); if (await fs.pathExists(configPath)) { found.push(pattern); } } return found; } async detectMonorepoPattern() { const monorepoIndicators = [ { pattern: 'lerna.json', type: 'Lerna' }, { pattern: 'nx.json', type: 'Nx' }, { pattern: 'rush.json', type: 'Rush' }, { pattern: 'pnpm-workspace.yaml', type: 'PNPM Workspace' }, { pattern: 'yarn.lock', type: 'Yarn Workspace (posible)' } ]; for (const indicator of monorepoIndicators) { if (await fs.pathExists(path.join(this.targetDir, indicator.pattern))) { return { detected: true, type: indicator.type }; } } // Detectar estructura típica de monorepo const commonDirs = ['packages', 'apps', 'libs', 'modules', 'services']; for (const dir of commonDirs) { const dirPath = path.join(this.targetDir, dir); if (await fs.pathExists(dirPath)) { const subdirs = await fs.readdir(dirPath); if (subdirs.length > 2) { return { detected: true, type: `Estructura ${dir}/` }; } } } return { detected: false }; } async detectTechnologies() { const techs = []; const detectors = { 'package.json': 'JavaScript/Node.js', 'composer.json': 'PHP', 'requirements.txt': 'Python', 'go.mod': 'Go', 'Cargo.toml': 'Rust', 'pom.xml': 'Java/Maven', 'build.gradle': 'Java/Gradle', 'pubspec.yaml': 'Flutter/Dart', 'Package.swift': 'Swift', 'project.clj': 'Clojure' }; for (const [file, tech] of Object.entries(detectors)) { if (await fs.pathExists(path.join(this.targetDir, file))) { techs.push(tech); } } // Detectar frameworks específicos const packageJsonPath = path.join(this.targetDir, 'package.json'); if (await fs.pathExists(packageJsonPath)) { try { const pkg = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); const deps = { ...pkg.dependencies, ...pkg.devDependencies }; if (deps.react || deps['@types/react']) techs.push('React'); if (deps.vue || deps['@vue/cli']) techs.push('Vue.js'); if (deps.angular || deps['@angular/core']) techs.push('Angular'); if (deps.next || deps['next']) techs.push('Next.js'); if (deps.nuxt || deps['nuxt']) techs.push('Nuxt.js'); if (deps.express) techs.push('Express'); if (deps.fastify) techs.push('Fastify'); if (deps.nestjs || deps['@nestjs/core']) techs.push('NestJS'); } catch (error) { // Ignorar errores de parsing } } return [...new Set(techs)]; // Eliminar duplicados } async detectArchitecturalPatterns() { const patterns = { microservices: false, layered: false, layerCount: 0 }; // Detectar microservicios if (await fs.pathExists(path.join(this.targetDir, 'docker-compose.yml')) || await fs.pathExists(path.join(this.targetDir, 'k8s')) || await fs.pathExists(path.join(this.targetDir, 'kubernetes'))) { patterns.microservices = true; } // Detectar arquitectura en capas const layerDirs = ['controller', 'service', 'repository', 'model', 'dto', 'entity']; let foundLayers = 0; for (const layer of layerDirs) { if (await fs.pathExists(path.join(this.targetDir, 'src', layer)) || await fs.pathExists(path.join(this.targetDir, layer))) { foundLayers++; } } if (foundLayers >= 3) { patterns.layered = true; patterns.layerCount = foundLayers; } return patterns; } async analyzeDependencies() { const result = { total: 0, outdated: 0 }; try { const packageJsonPath = path.join(this.targetDir, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const pkg = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); result.total = Object.keys(pkg.dependencies || {}).length + Object.keys(pkg.devDependencies || {}).length; // Heurística simple para detectar dependencias desactualizadas // (En una implementación real, usaríamos npm outdated) const deps = { ...pkg.dependencies, ...pkg.devDependencies }; for (const [name, version] of Object.entries(deps)) { if (typeof version === 'string' && version.includes('^') && version.match(/\^[0-9]+\.[0-9]+\.[0-9]+/)) { const majorVersion = parseInt(version.split('.')[0].replace('^', '')); if (majorVersion < 1) result.outdated++; } } } } catch (error) { // Ignorar errores } return result; } addComplexityFactor(type, weight, description) { this.analysis.score += weight; this.analysis.factors.push({ type, weight, description }); } calculateFinalComplexity() { if (this.analysis.score >= 15) { this.analysis.level = 'complex'; this.analysis.icon = '🔥'; this.analysis.description = 'Proyecto complejo'; this.analysis.riskLevel = 'high'; this.analysis.estimatedSetupTime = '10-20 min'; this.analysis.recommendations.push('Setup guiado recomendado'); this.analysis.recommendations.push('Considerar configuración por módulos'); } else if (this.analysis.score >= 8) { this.analysis.level = 'moderate'; this.analysis.icon = '⚖️'; this.analysis.description = 'Proyecto moderado'; this.analysis.riskLevel = 'medium'; this.analysis.estimatedSetupTime = '5-10 min'; this.analysis.recommendations.push('Setup estándar apropiado'); } else { this.analysis.level = 'simple'; this.analysis.icon = '🟢'; this.analysis.description = 'Proyecto simple'; this.analysis.riskLevel = 'low'; this.analysis.estimatedSetupTime = '2-5 min'; this.analysis.recommendations.push('Setup automático recomendado'); } console.log(chalk.green(`✅ Análisis completado: ${this.analysis.description} (Score: ${this.analysis.score})`)); } getDetailedReport() { console.log(chalk.cyan('\n📊 REPORTE DE COMPLEJIDAD DETALLADO')); console.log(chalk.gray('═══════════════════════════════════════')); console.log(`${this.analysis.icon} Nivel: ${chalk.yellow(this.analysis.description)}`); console.log(`⏱️ Tiempo estimado: ${chalk.cyan(this.analysis.estimatedSetupTime)}`); console.log(`🎯 Riesgo: ${chalk[this.analysis.riskLevel === 'high' ? 'red' : this.analysis.riskLevel === 'medium' ? 'yellow' : 'green'](this.analysis.riskLevel)}`); if (this.analysis.factors.length > 0) { console.log(chalk.cyan('\n🔍 Factores detectados:')); this.analysis.factors.forEach(factor => { console.log(chalk.gray(` • ${factor.description} (+${factor.weight})`)); }); } if (this.analysis.recommendations.length > 0) { console.log(chalk.cyan('\n💡 Recomendaciones:')); this.analysis.recommendations.forEach(rec => { console.log(chalk.blue(` → ${rec}`)); }); } } } module.exports = SmartComplexityAnalyzer;