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.

1,316 lines (1,068 loc) 47.2 kB
#!/usr/bin/env node const fs = require('fs-extra'); const path = require('path'); const inquirer = require('inquirer'); // Import chalk de manera que funcione con diferentes versiones let chalk; try { chalk = require('chalk'); } catch (error) { // Fallback si chalk no está disponible chalk = { blue: { bold: (text) => text }, gray: (text) => text, yellow: (text) => text, cyan: (text) => text, green: (text) => text, red: (text) => text }; } // Detectar flags de línea de comandos const args = process.argv.slice(2); const isAutoMode = args.includes('--auto'); const isQuickMode = args.includes('--quick'); if (!isAutoMode) { console.log(chalk.blue.bold('\n🚀 ACS Framework CLI')); console.log(chalk.gray('Configurando el Augmented Context Standard para tu proyecto...\n')); } class ACSFrameworkCLI { constructor() { this.targetDir = process.cwd(); this.templatesDir = path.join(__dirname, 'templates'); this.projectData = {}; this.isAutoMode = isAutoMode; this.isQuickMode = isQuickMode; } async run() { try { if (!this.isAutoMode) { console.log(chalk.yellow(`📂 Configurando ACS Framework en: ${this.targetDir}\n`)); } // En modo automático, saltamos análisis de complejidad largo if (!this.isQuickMode) { // Analizar complejidad del proyecto ANTES de continuar const LargeProjectHandler = require('./large-project-handler'); const complexityHandler = new LargeProjectHandler(this.targetDir); const complexity = await complexityHandler.analyzeProjectComplexity(); await complexityHandler.showComplexityWarnings(); } // Verificar si ya existe .context await this.checkExistingStructure(); // Recopilar información del proyecto (automático si está en modo auto) if (this.isAutoMode) { await this.autoGatherProjectInfo(); } else { await this.gatherProjectInfo(); } // Validar información crítica antes de continuar const validationResult = this.validateCriticalInfo(); if (!validationResult.valid) { console.log(chalk.red('\n❌ Información crítica incompleta:')); validationResult.errors.forEach(error => { console.log(chalk.red(` • ${error}`)); }); console.log(chalk.yellow('\n💡 Por favor completa la información faltante para continuar.')); return; } // Crear estructura de carpetas await this.createDirectoryStructure(); // Generar archivos desde templates (con validación) await this.generateFiles(); // Validar archivos generados antes de finalizar await this.validateGeneratedFiles(); // Mostrar resumen final await this.showSummary(); } catch (error) { console.error(chalk.red('❌ Error durante la configuración:'), error.message); process.exit(1); } } async checkExistingStructure() { const contextDir = path.join(this.targetDir, '.context'); const githubDir = path.join(this.targetDir, '.github'); if (await fs.pathExists(contextDir)) { const { overwrite } = await inquirer.prompt([{ type: 'confirm', name: 'overwrite', message: chalk.yellow('⚠️ Ya existe una carpeta .context. ¿Deseas sobrescribirla?'), default: false }]); if (!overwrite) { console.log(chalk.gray('🔄 Configuración cancelada.')); process.exit(0); } await fs.remove(contextDir); } } async autoDetectProjectInfo() { const ProjectFactsExtractor = require('./extract-facts'); const extractor = new ProjectFactsExtractor(this.targetDir); try { const facts = await extractor.extract(); return { suggestedName: facts.project.name || path.basename(this.targetDir), suggestedDescription: facts.project.description || `${facts.project.detectedType || 'Software'} project`, suggestedResponsible: this.detectGitUser() || '', suggestedStatus: this.inferProjectStatus(facts), suggestedTechs: this.mapDependenciesToChoices(facts.tech), additionalTech: this.extractAdditionalTech(facts.tech) }; } catch (error) { console.log(chalk.yellow('💡 No se pudo detectar información automáticamente')); return { suggestedName: path.basename(this.targetDir), suggestedDescription: '', suggestedResponsible: '', suggestedStatus: 'En desarrollo activo', suggestedTechs: [], additionalTech: [] }; } } detectGitUser() { try { const { execSync } = require('child_process'); const name = execSync('git config user.name', { encoding: 'utf8', stdio: 'pipe' }).trim(); const email = execSync('git config user.email', { encoding: 'utf8', stdio: 'pipe' }).trim(); return `${name} - ${email}`; } catch (error) { return ''; } } inferProjectStatus(facts) { if (facts.git && facts.git.recentTags && facts.git.recentTags.length > 0) { return 'En producción'; } if (facts.project.version && facts.project.version.startsWith('0.')) { return 'En desarrollo inicial'; } return 'En desarrollo activo'; } mapDependenciesToChoices(tech) { const deps = { ...tech.dependencies, ...tech.devDependencies }; const choices = []; const mapping = { 'react': 'React', 'vue': 'Vue.js', '@angular/core': 'Angular', 'next': 'Next.js', 'express': 'Express', 'laravel': 'Laravel', 'django': 'Django', 'flask': 'Flask', 'spring-boot': 'Spring Boot', 'flutter': 'Flutter', 'react-native': 'React Native', 'typescript': 'JavaScript/TypeScript', 'javascript': 'JavaScript/TypeScript', 'mysql': 'MySQL', 'postgresql': 'PostgreSQL', 'mongodb': 'MongoDB', 'redis': 'Redis', 'docker': 'Docker' }; Object.keys(deps).forEach(dep => { const choice = mapping[dep.toLowerCase()]; if (choice && !choices.includes(choice)) { choices.push(choice); } }); // Detectar Node.js si hay package.json if (tech.dependencies || tech.devDependencies) { choices.push('Node.js'); } return choices; } extractAdditionalTech(tech) { const deps = { ...tech.dependencies, ...tech.devDependencies }; const additional = []; // Frameworks específicos no en la lista principal Object.keys(deps).forEach(dep => { if (dep.includes('framework') || dep.includes('library')) { additional.push(dep); } }); return additional; } suggestComponents(selectedTechs, autoDetectedInfo) { const components = []; // Componentes basados en tecnologías seleccionadas if (selectedTechs.includes('React') || selectedTechs.includes('Vue.js') || selectedTechs.includes('Angular')) { components.push('Frontend SPA'); } if (selectedTechs.includes('Express') || selectedTechs.includes('Laravel') || selectedTechs.includes('Django')) { components.push('API REST'); } if (selectedTechs.includes('MySQL') || selectedTechs.includes('PostgreSQL') || selectedTechs.includes('MongoDB')) { components.push('Base de datos'); } if (selectedTechs.includes('Redis')) { components.push('Cache'); } if (selectedTechs.includes('Docker')) { components.push('Contenedores'); } // Si no hay componentes sugeridos, usar genéricos if (components.length === 0) { return 'API, Base de datos'; } return components.join(', '); } getProjectFileCount() { try { const { execSync } = require('child_process'); // Cuenta archivos de código (excluyendo node_modules, .git, etc.) const result = execSync( 'find . -type f \\( -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.php" -o -name "*.java" -o -name "*.go" -o -name "*.rs" -o -name "*.c" -o -name "*.cpp" \\) ! -path "./node_modules/*" ! -path "./.git/*" ! -path "./vendor/*" ! -path "./dist/*" ! -path "./build/*" | wc -l', { encoding: 'utf8', stdio: 'pipe', cwd: process.cwd() } ); return parseInt(result.trim()) || 0; } catch (error) { // Fallback: contar directorios como indicador de complejidad try { const fs = require('fs'); const files = fs.readdirSync(process.cwd()); const dirs = files.filter(file => { try { return fs.statSync(file).isDirectory() && !file.startsWith('.'); } catch { return false; } }); return dirs.length * 5; // Estimación: 5 archivos por directorio } catch { return 10; // Default para proyectos pequeños } } } validateCriticalInfo() { const errors = []; if (!this.projectData.projectName || this.projectData.projectName.trim().length < 3) { errors.push('Nombre del proyecto es muy corto o está vacío'); } if (!this.projectData.projectDescription || this.projectData.projectDescription.trim().length < 10) { errors.push('Descripción del proyecto es muy corta o está vacía'); } if (!this.projectData.responsible || !this.projectData.responsible.includes('@')) { errors.push('Responsable debe incluir email válido'); } if (!this.projectData.technologies || this.projectData.technologies.length === 0) { errors.push('Debe seleccionar al menos una tecnología principal'); } // Componentes pueden estar vacíos - IA los detectará automáticamente if (this.projectData.mainComponents && this.projectData.mainComponents.trim().length > 0 && this.projectData.mainComponents.trim().length < 5) { errors.push('Componentes principales deben ser más específicos (o déjalos vacíos para completar con IA)'); } return { valid: errors.length === 0, errors }; } async validateGeneratedFiles() { console.log(chalk.yellow('\n🔍 Validando archivos generados...')); const TemplateValidator = require('./template-validator'); const validator = new TemplateValidator(); const files = ['README.md', 'ARCHITECTURE.md', 'RULES.md', 'SYSTEM_PROMPT.md']; let hasErrors = false; for (const fileName of files) { const filePath = path.join(this.targetDir, '.context', fileName); if (await fs.pathExists(filePath)) { const content = await fs.readFile(filePath, 'utf8'); const validation = validator.validateAndSuggestFixes(content, fileName, this.projectData); if (!validation.isValid) { hasErrors = true; console.log(chalk.red(`❌ ${fileName}: ${validation.summary}`)); validation.issues.critical.forEach(issue => { console.log(chalk.red(` 🚫 ${issue.message}`)); }); if (validation.suggestions.length > 0) { console.log(chalk.yellow(' 💡 Sugerencias:')); validation.suggestions.forEach(suggestion => { console.log(chalk.yellow(` • ${suggestion}`)); }); } } else { console.log(chalk.green(`✅ ${fileName}: Válido`)); } } } if (hasErrors) { console.log(chalk.yellow('\n⚠️ Algunos archivos contienen placeholders o información incompleta.')); console.log(chalk.gray(' Esto es normal - puedes completarlos manualmente o usar acs-agent más tarde.')); } else { console.log(chalk.green('\n✅ Todos los archivos generados son válidos')); } } async gatherProjectInfo() { console.log(chalk.cyan('📋 Información básica del proyecto:')); // Primero intentar extraer información automáticamente const autoDetectedInfo = await this.autoDetectProjectInfo(); const answers = await inquirer.prompt([ { type: 'input', name: 'projectName', message: '🏷️ Nombre del proyecto:', default: autoDetectedInfo.suggestedName, validate: (input) => { const trimmed = input.trim(); if (trimmed === '') return 'El nombre es obligatorio'; if (trimmed.length < 3) return 'El nombre debe tener al menos 3 caracteres'; return true; } }, { type: 'input', name: 'projectDescription', message: '📄 Descripción breve (1-2 líneas):', default: autoDetectedInfo.suggestedDescription, validate: (input) => { const trimmed = input.trim(); if (trimmed === '') { console.log(chalk.yellow('\n💡 Tip: Una buena descripción ayuda mucho al contexto de IA')); console.log(chalk.gray(' Ejemplo: "API REST para gestión de inventario de e-commerce"')); return 'La descripción es obligatoria - describe qué hace tu proyecto'; } if (trimmed.length < 10) return 'La descripción debe ser más detallada (mínimo 10 caracteres)'; if (trimmed.toLowerCase().includes('proyecto') && trimmed.length < 30) { console.log(chalk.yellow('\n💡 Intenta ser más específico sobre QUÉ hace el proyecto')); return 'Describe la funcionalidad específica, no solo que "es un proyecto"'; } return true; } }, { type: 'input', name: 'responsible', message: '👤 Responsable del proyecto (nombre + email):', default: autoDetectedInfo.suggestedResponsible, validate: (input) => { const trimmed = input.trim(); if (trimmed === '') { console.log(chalk.yellow('\n💡 Tip: Esto ayuda a saber a quién preguntar sobre el proyecto')); return 'El responsable es obligatorio'; } if (!trimmed.includes('@')) { console.log(chalk.yellow('\n💡 Incluye el email para contacto (ej: Juan Pérez - juan@empresa.com)')); return 'Incluye el email del responsable'; } return true; } }, { type: 'list', name: 'projectStatus', message: '📊 Estado actual del proyecto:', choices: [ 'En desarrollo inicial', 'En desarrollo activo', 'En testing/staging', 'En producción', 'Mantenimiento' ], default: autoDetectedInfo.suggestedStatus }, { type: 'checkbox', name: 'technologies', message: '⚙️ Tecnologías principales (selecciona todas las que apliquen):', choices: [ 'React', 'Vue.js', 'Angular', 'Next.js', 'Node.js', 'Express', 'Laravel', 'Django', 'Flask', 'Spring Boot', 'Flutter', 'React Native', 'Python', 'JavaScript/TypeScript', 'PHP', 'Java', 'C#/.NET', 'Go', 'Rust', 'PostgreSQL', 'MySQL', 'MongoDB', 'Redis', 'Docker', 'Kubernetes', 'AWS', 'Google Cloud', 'Azure' ], default: autoDetectedInfo.suggestedTechs, validate: (input) => { if (input.length === 0) { console.log(chalk.yellow('\n💡 Selecciona al menos una tecnología principal')); console.log(chalk.gray(' Si no ves tu tech, podrás agregarla en el siguiente paso')); return 'Selecciona al menos una tecnología'; } return true; } }, { type: 'input', name: 'additionalTech', message: '🔧 Otras tecnologías no listadas (separadas por comas):', default: autoDetectedInfo.additionalTech.join(', '), filter: (input) => { // Limpiar y validar input adicional return input.split(',').map(t => t.trim()).filter(t => t.length > 0).join(', '); } } ]); // Preguntas sobre arquitectura básica console.log(chalk.cyan('\n🏗️ Información de arquitectura:')); // Mostrar sugerencias basadas en facts si están disponibles if (autoDetectedInfo.suggestedTechs.length > 0) { console.log(chalk.gray(`💡 Detecté: ${autoDetectedInfo.suggestedTechs.join(', ')}`)); } const archAnswers = await inquirer.prompt([ { type: 'list', name: 'projectType', message: '🎯 Tipo de proyecto:', choices: [ 'API REST/GraphQL', 'Aplicación web (frontend)', 'Aplicación móvil', 'Aplicación fullstack', 'Microservicio', 'CLI/Herramienta', 'Librería/Framework', 'Script/Automatización', 'Otro' ] }, { type: 'input', name: 'mainComponents', message: '🧩 Componentes principales (ej: API, Base de datos, Queue, etc.):', // Sin default - es mejor placeholder que información falsa validate: (input) => { const trimmed = input.trim(); if (trimmed === '') { console.log(chalk.yellow('\n💡 Tip: Puedes dejarlo vacío para completar después con IA')); console.log(chalk.gray(' Si prefieres especificar ahora: API, Base de datos, Frontend, Worker queues, Cache, etc.')); return true; // Permitir vacío } if (trimmed.length < 5) { return 'Describe los componentes de manera más específica'; } return true; } }, { type: 'list', name: 'criticalFlow', message: '🔄 ¿Quieres definir el flujo crítico principal ahora?', choices: [ { name: '✍️ Sí, lo describo ahora (recomendado para proyectos simples)', value: 'describe' }, { name: '⏭️ Completar después con IA (recomendado para proyectos complejos)', value: 'later' }, { name: '🤖 Que IA lo detecte automáticamente del código', value: 'auto' } ], default: () => { // Lógica inteligente basada en complejidad detectada const projectFiles = this.getProjectFileCount(); if (projectFiles > 50) { return 'later'; // Proyectos complejos = completar después } return 'describe'; // Proyectos simples = describir ahora } } ]); // Pregunta condicional para flujo crítico si eligió "describe" let criticalFlowDetail = ''; if (archAnswers.criticalFlow === 'describe') { const flowAnswer = await inquirer.prompt([{ type: 'input', name: 'flowDescription', message: '🔄 Describe el flujo crítico principal (ej: Usuario registra → valida email → accede):', validate: (input) => { const trimmed = input.trim(); if (trimmed === '') { console.log(chalk.yellow('\n💡 Tip: Describe el flujo más importante de tu aplicación')); console.log(chalk.gray(' Ejemplo: "Cliente hace pedido → pago → confirma → envía por email"')); return 'El flujo crítico ayuda mucho al contexto - descríbelo brevemente'; } if (trimmed.length < 10) { return 'Describe el flujo con más detalle'; } return true; } }]); criticalFlowDetail = flowAnswer.flowDescription; } // Preguntas sobre convenciones del equipo console.log(chalk.cyan('\n👥 Convenciones del equipo:')); const teamAnswers = await inquirer.prompt([ { type: 'list', name: 'commitStyle', message: '📝 Estilo de commits:', choices: [ 'Conventional Commits (feat:, fix:, docs:)', 'Descriptivo simple', 'Con prefijo de ticket/issue', 'Personalizado' ] }, { type: 'list', name: 'branchStrategy', message: '🌿 Estrategia de branches:', choices: [ 'feature/TICKET-123', 'feature/descripcion-corta', 'main + feature branches', 'git-flow (main, develop, feature, release)', 'Personalizada' ] }, { type: 'confirm', name: 'requirePRReview', message: '✅ ¿Requieren review las Pull Requests?', default: true }, { type: 'confirm', name: 'includeTests', message: '🧪 ¿El proyecto incluye tests automatizados?', default: true } ]); // Opcional: Historia de usuario inicial console.log(chalk.cyan('\n📖 Historias de usuario (opcional):')); // Consolidar toda la información this.projectData = { ...answers, ...archAnswers, ...teamAnswers, criticalFlow: this.resolveCriticalFlow(archAnswers.criticalFlow, criticalFlowDetail), criticalFlowType: archAnswers.criticalFlow, dateCreated: new Date().toLocaleDateString('es-ES'), technologies: [...answers.technologies, ...(answers.additionalTech ? answers.additionalTech.split(',').map(t => t.trim()) : [])].filter(Boolean) }; } async autoGatherProjectInfo() { if (!this.isAutoMode) return; console.log(chalk.yellow('⚡ Configuración automática...')); // Auto-detectar información básica const autoDetectedInfo = await this.autoDetectProjectInfo(); // Configuración automática con valores sensatos this.projectData = { projectName: autoDetectedInfo.suggestedName, projectDescription: autoDetectedInfo.suggestedDescription || `Proyecto ${autoDetectedInfo.suggestedName}`, responsible: autoDetectedInfo.suggestedResponsible || 'Pendiente de definir', projectStatus: autoDetectedInfo.suggestedStatus || 'En desarrollo activo', technologies: autoDetectedInfo.detectedTechnologies || [], developmentGuide: 'auto', // Configuración automática teamSize: 'small', criticalFlow: '*[IA detectará flujos automáticamente con acs-agent]*', criticalFlowType: 'auto', mainComponents: '', dateCreated: new Date().toLocaleDateString('es-ES') }; console.log(chalk.green(`✅ Configuración automática completada para ${this.projectData.projectName}`)); } resolveCriticalFlow(flowType, flowDetail) { switch (flowType) { case 'describe': return flowDetail; case 'later': return '*[Completar después - usar acs-master → Completar flujos críticos]*'; case 'auto': return '*[IA lo detectará automáticamente durante la próxima ejecución de acs-agent]*'; default: return '*[Flujo crítico pendiente de definir]*'; } } resolveMainComponents() { const components = this.projectData.mainComponents; if (!components || components.trim() === '') { return '*[Completar con acs-agent - IA detectará componentes del código]*'; } return components; } resolveMainComponentsList() { const components = this.projectData.mainComponents; if (!components || components.trim() === '') { return '- *[IA detectará componentes automáticamente - usar acs-agent]*'; } return components.split(',').map(comp => `- **${comp.trim()}:** [descripción pendiente]`).join('\n'); } resolveMainComponentsDetailed() { const components = this.projectData.mainComponents; if (!components || components.trim() === '') { return '- *[IA detectará componentes automáticamente - usar acs-agent]*'; } return components.split(',').map(comp => `- **${comp.trim()}:** [Descripción pendiente]`).join('\n'); } async createDirectoryStructure() { console.log(chalk.cyan('\n📁 Creando estructura de directorios...')); const contextDir = path.join(this.targetDir, '.context'); const githubDir = path.join(this.targetDir, '.github'); await fs.ensureDir(contextDir); await fs.ensureDir(githubDir); console.log(chalk.green('✅ Directorios creados')); } async generateFiles() { console.log(chalk.cyan('\n📝 Generando archivos...')); try { // Generar README.md console.log('Generando README.md...'); await this.generateReadme(); // Generar ARCHITECTURE.md console.log('Generando ARCHITECTURE.md...'); await this.generateArchitecture(); // Generar RULES.md console.log('Generando RULES.md...'); await this.generateRules(); // Generar SYSTEM_PROMPT.md console.log('Generando SYSTEM_PROMPT.md...'); await this.generateSystemPrompt(); // Generar FEATURES.md (siempre incluido) console.log('Generando FEATURES.md...'); await this.generateFeatures(); // Generar AGENT_PROMPT.md (para automatización con IA) console.log('Generando AGENT_PROMPT.md...'); await this.generateAgentPrompt(); // Generar CHANGELOG.md console.log('Generando CHANGELOG.md...'); await this.generateChangelog(); // Generar Pull Request template console.log('Generando PR template...'); await this.generatePRTemplate(); console.log(chalk.green('✅ Archivos generados exitosamente')); } catch (error) { console.error(chalk.red('❌ Error generando archivos:'), error.message); console.error('Stack trace:', error.stack); throw error; } } async generateReadme() { // Asegurar que technologies sea un array válido const technologies = this.projectData.technologies || []; const techList = technologies.length > 0 ? technologies : ['[Tecnologías por definir]']; const content = `# ${this.projectData.projectName} ${this.projectData.projectDescription} **Responsable:** ${this.projectData.responsible} **Stack:** ${techList.slice(0, 5).join(', ')}${techList.length > 5 ? ', ...' : ''} **Estado:** ${this.projectData.projectStatus} (Última actualización: ${this.projectData.dateCreated}) ## Para desarrollar local: 1. \`git clone [URL_DEL_REPO]\` 2. \`[comando_instalacion_dependencias]\` (ej: npm install, composer install, pip install -r requirements.txt) 3. \`cp .env.example .env\` (completar variables necesarias) 4. \`[comando_setup_bd]\` (ej: npm run migrate, php artisan migrate) 5. \`[comando_desarrollo]\` (ej: npm run dev, php artisan serve) **Usuario demo:** [usuario_prueba] / [password_prueba] ## Componentes principales: ${this.resolveMainComponentsList()} ## Flujos Críticos: ${this.projectData.criticalFlow} --- *Generado automáticamente por ACS Framework CLI - ${this.projectData.dateCreated}* `; await fs.writeFile(path.join(this.targetDir, '.context', 'README.md'), content); } async generateArchitecture() { // Asegurar que technologies sea un array válido const technologies = this.projectData.technologies || []; const techList = technologies.length > 0 ? technologies : ['[Tecnologías por definir]']; const content = `# Arquitectura - ${this.projectData.projectName} ## 1. Resumen **Problema que resuelve:** ${this.projectData.projectDescription} **Tipo de proyecto:** ${this.projectData.projectType} **Estado actual:** ${this.projectData.projectStatus} **Última actualización:** ${this.projectData.dateCreated} ## 2. Stack Tecnológico ${techList.map(tech => `- ${tech}`).join('\n')} ## 3. Componentes Principales ${this.resolveMainComponentsDetailed()} ## 4. Flujos Críticos ${this.projectData.criticalFlow} ### Instrucciones para completar (si está pendiente): 1. Primero, completa las historias de usuario en \`.context/FEATURES.md\` 2. Luego, usa IA para analizar las historias y generar los flujos críticos 3. Prompt sugerido: "Basándome en estas historias de usuario [pegar contenido], identifica y describe los 3-5 flujos críticos más importantes del sistema" ### Ejemplo de formato esperado: - **Flujo de Registro**: Usuario llena formulario → validación → envío email confirmación → activación cuenta - **Flujo de Compra**: Selección producto → carrito → pago → confirmación → entrega - **Flujo de Autenticación**: Login → validación credenciales → generación tokens → acceso dashboard ## 5. Ambientes | Ambiente | Propósito | URL/Acceso | |----------|-----------|------------| | Local | Desarrollo | localhost | | Staging | Testing | [Pendiente definir] | | Producción | Operación | [Pendiente definir] | ## 6. Decisiones Técnicas Importantes ### ADR-001: Stack Tecnológico Inicial **Fecha:** ${this.projectData.dateCreated} **Decisión:** Usar ${technologies.length > 0 ? technologies.slice(0, 3).join(', ') : '[Stack por definir]'} **Motivo:** Setup inicial del proyecto **Estado:** Aprobado ## 7. Próximas Decisiones Pendientes - [Definir arquitectura detallada según evolucione el proyecto] - [Documentar patrones de diseño específicos] - [Agregar diagramas cuando sea necesario] --- *Generado por ACS Framework CLI - ${this.projectData.dateCreated}* *Actualizar este archivo cuando se tomen decisiones arquitectónicas importantes* `; await fs.writeFile(path.join(this.targetDir, '.context', 'ARCHITECTURE.md'), content); } async generateRules() { // Asegurar que technologies sea un array válido const technologies = this.projectData.technologies || []; const techList = technologies.length > 0 ? technologies : ['[Tecnologías por definir]']; const branchExamples = { 'feature/TICKET-123': 'feature/ACS-001, fix/BUG-456', 'feature/descripcion-corta': 'feature/user-login, fix/email-validation', 'main + feature branches': 'feature/nueva-funcionalidad, hotfix/critical-bug', 'git-flow (main, develop, feature, release)': 'feature/login, release/v1.2.0, hotfix/security-patch' }; const commitExamples = { 'Conventional Commits (feat:, fix:, docs:)': `**Tipos principales:** - **feat:** nueva funcionalidad - \`feat: agregar login con Google\` - \`feat: filtro por fecha en reportes\` - **fix:** corrección de bug - \`fix: error 500 en endpoint de usuarios\` - \`fix: validación de email que fallaba\` - **docs:** cambios en documentación - \`docs: actualizar README con nuevas variables\` - \`docs: agregar ejemplos de uso de API\` - **chore:** mantenimiento, configuración - \`chore: actualizar dependencias\` - \`chore: configurar CI/CD pipeline\``, 'Descriptivo simple': 'Agregar funcionalidad de login\nCorregir error en validación de email\nActualizar documentación del API', 'Con prefijo de ticket/issue': 'ACS-001: Agregar sistema de autenticación\nBUG-456: Corregir error en cálculo de totales\nDOC-789: Actualizar guía de instalación' }; const content = `# Reglas del Proyecto - ${this.projectData.projectName} ## Stack Obligatorio **Tecnologías principales:** ${techList.map(tech => `- ${tech}`).join('\n')} **Versiones mínimas:** - [Definir versiones específicas según el stack] - [Ejemplo: Node.js 18+, PHP 8.1+, Python 3.9+] ## Convenciones de Naming **Archivos:** [Pendiente definir - ej: snake_case, camelCase, kebab-case] **Variables:** [Pendiente definir según lenguaje] **Branches:** ${this.projectData.branchStrategy} - Ejemplos: ${branchExamples[this.projectData.branchStrategy] || 'Definir ejemplos específicos'} ## Commits ${commitExamples[this.projectData.commitStyle] || `Estilo: ${this.projectData.commitStyle} - [Definir ejemplos específicos para este estilo]`} ## Pull Requests - ${this.projectData.requirePRReview ? 'Toda PR requiere al menos 1 review' : 'Reviews opcionales pero recomendadas'} - ${this.projectData.includeTests ? 'Incluir tests para funcionalidades nuevas' : 'Tests recomendados cuando sea posible'} - Usar el template de PR (incluye checklist ACS) - Actualizar documentación si la PR cambia algo importante ## Testing ${this.projectData.includeTests ? `**Estrategia de tests:** - [Definir tipos: unit, integration, e2e] - [Definir coverage mínimo esperado] - [Definir herramientas: Jest, PHPUnit, pytest, etc.] **Ejecutar tests:** \`\`\`bash [comando_para_ejecutar_tests] \`\`\`` : '**Tests:** Pendiente implementar estrategia de testing'} ## Secretos y Variables - Passwords y API keys van en .env (nunca en código) - Para staging: usar datos de prueba, no producción - Variables críticas deben estar documentadas en README.md ## Despliegue **Ambientes:** - **Local:** Desarrollo diario - **Staging:** Testing antes de producción - **Producción:** Ambiente final **Proceso:** - [Definir pipeline de CI/CD] - [Definir proceso de rollback] - [Definir ventanas de mantenimiento] ## Librerías y Dependencias **Aprobadas:** - [Lista de librerías permitidas] - [Proceso para aprobar nuevas dependencias] **Prohibidas:** - [Lista de librerías a evitar y por qué] - [Alternativas recomendadas] --- *Generado por ACS Framework CLI - ${this.projectData.dateCreated}* *Actualizar cuando se definan nuevas convenciones* `; await fs.writeFile(path.join(this.targetDir, '.context', 'RULES.md'), content); } async generateSystemPrompt() { // Asegurar que technologies sea un array válido const technologies = this.projectData.technologies || []; const techList = technologies.length > 0 ? technologies : ['[Tecnologías por definir]']; const priorityTech = technologies.length > 0 ? technologies.slice(0, 3).join(', ') : '[Stack por definir]'; const content = `# Instrucciones para IA - ${this.projectData.projectName} Eres un asistente especializado para el proyecto "${this.projectData.projectName}". ## ⚠️ REGLAS CRÍTICAS PARA IA **ANTES de generar cualquier código o documentación:** 1. **🔍 VERIFICAR CONTEXTO**: Lee todos los archivos .context/ antes de asumir algo 2. **⏱️ TÓMATE TU TIEMPO**: Calidad sobre velocidad. Usa todas tus herramientas disponibles 3. **🔬 SÉ PROLIJO**: Revisa package.json, verifica dependencias, confirma versiones 4. **🚫 CERO ALUCINACIONES**: No documentes funciones que no existen, no inventes APIs 5. **📋 AUDITORÍA FINAL**: Antes de entregar código, revisa que sea consistente con el proyecto real **FILOSOFÍA**: Este proyecto usa ACS Framework para eliminar inconsistencias. **NO generes código que no respete la arquitectura documentada.** ## Contexto del Proyecto **Descripción:** ${this.projectData.projectDescription} **Tipo:** ${this.projectData.projectType} **Estado:** ${this.projectData.projectStatus} **Responsable:** ${this.projectData.responsible} **Stack tecnológico:** ${techList.map(tech => `- ${tech}`).join('\n')} **Arquitectura básica:** - Componentes: ${this.resolveMainComponents()} - Flujos críticos: ${this.projectData.criticalFlow} ## Al generar código: **Tecnologías a usar:** - Priorizar: ${priorityTech} - Mantener compatibilidad con stack existente - Usar versiones modernas y mejores prácticas **Patrones requeridos:** - ${this.projectData.namingConvention || 'Seguir convenciones de naming del equipo'} - Incluir manejo de errores apropiado - Logging adecuado para debugging - Documentar decisiones técnicas importantes **Estilo de commits:** ${this.projectData.commitStyle} ## Reglas específicas del proyecto: **SIEMPRE hacer:** - Verificar que las dependencias existan en package.json antes de usarlas - Validar inputs del usuario con el patrón establecido - Incluir manejo de errores apropiado según la arquitectura - Agregar comments en español para lógica compleja - Seguir los principios de seguridad del proyecto - Mantener consistencia con los patterns existentes ${this.projectData.includeTests ? '- Incluir tests siguiendo los patterns del proyecto' : '- Considerar agregar tests cuando sea apropiado'} **NUNCA hacer:** - Hardcodear credenciales, URLs o configuraciones sensibles - Ignorar las validaciones de entrada establecidas - Escribir código sin considerar el performance del stack actual - Usar librerías que no estén aprobadas o sean deprecated - Cambiar arquitectura sin consultar .context/ARCHITECTURE.md - Generar código que rompa las convenciones establecidas en .context/RULES.md ## Validaciones OBLIGATORIAS antes de generar código: 1. **📁 Verificar estructura**: Revisar .context/ARCHITECTURE.md para entender la organización 2. **📋 Leer package.json**: Confirmar dependencias, scripts y versiones disponibles 3. **🔍 Revisar .context/RULES.md**: Confirmar convenciones de naming, patterns y restricciones 4. **📖 Entender el negocio**: Leer .context/FEATURES.md para entender el contexto de negocio 5. **🧪 Considerar tests**: Si existen, seguir los patterns de testing establecidos ## Contexto de negocio: **Funcionalidades principales:** - ${this.projectData.criticalFlow} - [Revisar .context/FEATURES.md para detalles completos] **Componentes del sistema:** - ${this.resolveMainComponents()} Para más detalles sobre historias de usuario específicas, consultar \`.context/FEATURES.md\` ## Estilo de comunicación: - Responde en español técnico, claro y directo - NUNCA asumas funcionalidades: pregunta o verifica en .context/ - Si no estás seguro de algo, revisa la documentación del proyecto - Explica decisiones técnicas cuando sean relevantes - Sugiere mejoras alineadas con la arquitectura existente - Prioriza mantener consistencia sobre agregar features nuevas ## Referencias: - Documentación del proyecto: \`.context/\` folder - Arquitectura detallada: \`.context/ARCHITECTURE.md\` - Reglas del equipo: \`.context/RULES.md\` - Funcionalidades: \`.context/FEATURES.md\` --- *Generado por ACS Framework CLI - ${this.projectData.dateCreated}* *Copia este contenido al chatear con IA para mejor contexto* `; await fs.writeFile(path.join(this.targetDir, '.context', 'SYSTEM_PROMPT.md'), content); } async generateFeatures() { const content = `# FEATURES.md — Contexto de Negocio > Historias de usuario que conectan el código con la intención de negocio. Este archivo se mantiene actualizado conforme se desarrollan nuevas funcionalidades. ## En Desarrollo Actual **HU-001: [Título de la historia en desarrollo]** - **Como** [rol del usuario] - **Quiero** [acción que desea realizar] - **Para** [beneficio u objetivo] **Criterios de aceptación:** - [ ] [Criterio específico 1] - [ ] [Criterio específico 2] - [ ] [Criterio específico 3] **Archivos relacionados:** [Agregar cuando se implemente] **Notas técnicas:** - [Consideraciones técnicas específicas] - [Dependencias o restricciones] --- ## Próximas Historias (Backlog) **HU-002: [Título de próxima funcionalidad]** - **Como** [rol] - **Quiero** [acción] - **Para** [beneficio] **Prioridad:** [Alta/Media/Baja] **Estimación:** [Puntos o tiempo estimado] --- ## Implementadas ✅ *[Las historias completadas aparecerán aquí conforme se desarrollen]* **Ejemplo:** **HU-000: Setup inicial del proyecto** ✅ - **Como** desarrollador - **Quiero** tener la estructura base del proyecto - **Para** comenzar a desarrollar funcionalidades **Estado:** Completado en setup inicial **Archivos:** Configuración base del proyecto --- **Notas para el equipo:** - Actualizar este archivo cuando se definan nuevas historias - Mover historias de "En Desarrollo" a "Implementadas" cuando se completen - Incluir enlaces a PRs y archivos modificados - Mantener criterios de aceptación específicos y medibles ## 🚀 Siguiente Paso: Generar Flujos Críticos **Una vez que completes las historias de usuario arriba, usa este prompt con IA:** \`\`\` Tengo estas historias de usuario para mi proyecto: [PEGAR AQUÍ LAS HISTORIAS DE USUARIO COMPLETAS] Necesito que identifiques y describes los 3-5 flujos críticos más importantes del sistema. Formato esperado: - **Nombre del Flujo**: Paso 1 → Paso 2 → Paso 3 → Resultado Después, actualiza el archivo .context/ARCHITECTURE.md en la sección "4. Flujos Críticos". \`\`\` --- *Generado por ACS Framework CLI - ${this.projectData.dateCreated}* *Completar las historias de usuario según se definan en el proceso de desarrollo* `; await fs.writeFile(path.join(this.targetDir, '.context', 'FEATURES.md'), content); } async generateAgentPrompt() { // Copiar el archivo AGENT_PROMPT.md al proyecto const sourcePath = path.join(__dirname, 'AGENT_PROMPT.md'); const targetPath = path.join(this.targetDir, '.context', 'AGENT_PROMPT.md'); if (await fs.pathExists(sourcePath)) { await fs.copy(sourcePath, targetPath); } else { // Fallback si no existe el archivo fuente const content = `# AGENT_PROMPT.md — Agente Documentador IA > Este archivo contiene instrucciones especializadas para que la IA actualice automáticamente la documentación ACS Framework. Ver documentación completa en: https://github.com/jiaas/acs-framework ## Uso rápido: 1. Ejecuta \`acs-validate\` para verificar completitud 2. Si hay issues, usa este prompt con tu IA favorita: \`\`\` Actúa como agente documentador según AGENT_PROMPT.md. Analiza mi proyecto y actualiza la documentación ACS Framework. [Pegar aquí estructura del proyecto + archivos .context/ actuales] \`\`\` 3. Copia las actualizaciones generadas 4. Valida nuevamente con \`acs-validate\` El resultado: documentación técnica precisa y actualizada automáticamente. `; await fs.writeFile(targetPath, content); } } async generateChangelog() { const content = `# Changelog Registro de cambios importantes del proyecto "${this.projectData.projectName}". ## [Unreleased] ### Added - Configuración inicial del ACS Framework - Estructura base de documentación ## [0.1.0] - ${this.projectData.dateCreated} ### Added - Setup inicial del proyecto - Configuración de stack: ${this.projectData.technologies.slice(0, 3).join(', ')} - Documentación base en carpeta .context/ ### Changed - N/A ### Deprecated - N/A ### Removed - N/A ### Fixed - N/A ### Security - N/A --- ## Formato Este changelog sigue [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), y el proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Tipos de cambios: - **Added** para funcionalidades nuevas - **Changed** para cambios en funcionalidades existentes - **Deprecated** para funcionalidades que se eliminarán pronto - **Removed** para funcionalidades eliminadas - **Fixed** para corrección de bugs - **Security** para vulnerabilidades `; await fs.writeFile(path.join(this.targetDir, '.context', 'CHANGELOG.md'), content); } async generatePRTemplate() { const content = `## Descripción Breve descripción de los cambios realizados. ## Tipo de cambio - [ ] 🐛 Bug fix (corrección que soluciona un problema) - [ ] ✨ Nueva funcionalidad (cambio que agrega funcionalidad) - [ ] 💥 Breaking change (corrección o funcionalidad que causa que funcionalidades existentes no trabajen como se esperaba) - [ ] 📚 Documentación (cambios solo en documentación) - [ ] 🔧 Configuración (cambios en configuración, scripts de build, etc.) - [ ] ♻️ Refactor (cambio de código que no corrige bugs ni agrega funcionalidades) - [ ] 🧪 Tests (agregar tests faltantes o corregir tests existentes) ## Checklist ### General - [ ] Mi código sigue las convenciones de estilo de este proyecto - [ ] He realizado una auto-revisión de mi código - [ ] He comentado mi código, particularmente en áreas difíciles de entender ${this.projectData.includeTests ? '- [ ] He agregado tests que prueban mi funcionalidad' : '- [ ] He considerado agregar tests (si aplica)'} - [ ] Los tests nuevos y existentes pasan localmente ### Checklist ACS Framework - [ ] Si agregué/cambié tecnologías → actualicé \`.context/README.md\` - [ ] Si modifiqué arquitectura/flujos → actualicé \`.context/ARCHITECTURE.md\` - [ ] Si definí nuevas reglas → actualicé \`.context/RULES.md\` - [ ] Si la IA necesita saber algo → actualicé \`.context/SYSTEM_PROMPT.md\` - [ ] Si implementé una historia de usuario → actualicé \`.context/FEATURES.md\` - [ ] Si hay cambios importantes → actualicé \`.context/CHANGELOG.md\` - [ ] ✅ N/A - Esta PR no afecta el contexto del proyecto ## Screenshots (si aplica) ## Notas adicionales Cualquier información adicional que sea importante para los revisores. `; await fs.writeFile(path.join(this.targetDir, '.github', 'pull_request_template.md'), content); } async showSummary() { console.log(chalk.green.bold('\n🎉 ¡ACS Framework configurado exitosamente!\n')); console.log(chalk.cyan('📁 Estructura creada:')); console.log(chalk.gray(' .context/')); console.log(chalk.gray(' ├── README.md (info básica del proyecto)')); console.log(chalk.gray(' ├── ARCHITECTURE.md (mapa del sistema)')); console.log(chalk.gray(' ├── RULES.md (convenciones del equipo)')); console.log(chalk.gray(' ├── SYSTEM_PROMPT.md (instrucciones para IA)')); console.log(chalk.gray(' ├── FEATURES.md (historias de usuario + instrucciones para flujos críticos)')); console.log(chalk.gray(' ├── AGENT_PROMPT.md (instrucciones para IA agente documentador)')); console.log(chalk.gray(' └── CHANGELOG.md (registro de cambios)')); console.log(chalk.gray(' .github/')); console.log(chalk.gray(' └── pull_request_template.md (template de PR)')); console.log(chalk.cyan('\n📋 Próximos pasos:')); console.log(chalk.yellow('1.'), 'Revisa y personaliza los archivos generados'); console.log(chalk.yellow('2.'), 'Completa las secciones marcadas como [Pendiente definir]'); console.log(chalk.yellow('3.'), 'Actualiza .context/README.md con comandos específicos de tu proyecto'); console.log(chalk.yellow('4.'), 'Haz tu primer commit usando las convenciones definidas'); console.log(chalk.yellow('5.'), 'Comparte .context/SYSTEM_PROMPT.md con tu IA favorita'); console.log(chalk.yellow('6.'), 'Usa `acs-validate` para verificar completitud de documentación'); console.log(chalk.yellow('7.'), 'Si hay pendientes, usa .context/AGENT_PROMPT.md para actualizaciones automáticas con IA'); console.log(chalk.cyan('\n💡 Tips:')); console.log(chalk.gray('• Los archivos crecerán gradualmente - no intentes completar todo de una vez')); console.log(chalk.gray('• Actualiza la documentación como parte del flujo de desarrollo')); console.log(chalk.gray('• Usa el checklist en las PRs para mantener todo sincronizado')); console.log(chalk.gray('• AGENT_PROMPT.md + IA = documentación actualizada automáticamente')); console.log(chalk.green('\n✨ ¡Ahora tu proyecto tiene superpoderes de contexto!\n')); } } // Ejecutar la CLI const cli = new ACSFrameworkCLI(); cli.run().catch(console.error); // Exportar la clase para uso en otros módulos module.exports = ACSFrameworkCLI;