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