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