¡Gracias por tu interés en contribuir a Patto CLI! 🎉
Esta guía te ayudará a empezar con el desarrollo y a entender nuestro proceso de contribución.
- Código de Conducta
- Cómo Puedo Contribuir
- Setup del Entorno de Desarrollo
- Estructura del Proyecto
- Guidelines de Desarrollo
- Proceso de Pull Request
- Estándares de Código
- Testing
- Documentación
- Comunicación
Este proyecto y todos los que participen en él están regidos por el Código de Conducta de Patto CLI. Al participar, se espera que mantengas este código. Por favor reporta comportamiento inaceptable a hormigadev7@gmail.com.
Si encuentras un bug, por favor crea un issue con:
Título: Descripción breve y clara del problema
Descripción del Bug:
- ¿Qué pasó?
- ¿Qué esperabas que pasara?
- ¿Cómo puedo reproducir el problema?
Información del Entorno:
- OS: [ej. Ubuntu 22.04, Windows 11, macOS 14]
- Node.js: [ej. v20.10.0]
- Patto CLI: [ej. v0.0.1]
Pasos para Reproducir:
- Ejecuta
patto ... - Observa el error en ...
- Verifica que ...
Comportamiento Esperado: Una descripción clara de lo que esperabas que sucediera.
Screenshots (si aplica): Agrega screenshots para ayudar a explicar el problema.
Contexto Adicional: Cualquier otra información sobre el problema.
Las sugerencias de mejoras son bienvenidas. Por favor crea un issue con:
Título: Descripción breve de la mejora
¿Es tu solicitud de funcionalidad relacionada a un problema? Ej: "Siempre me frustra cuando [...]"
Describe la solución que te gustaría Una descripción clara de lo que quieres que suceda.
Describe alternativas que hayas considerado Una descripción de cualquier solución o funcionalidad alternativa.
Contexto adicional Cualquier otra información o screenshots sobre la solicitud.
¿No estás seguro por dónde empezar? Busca issues etiquetados con:
good first issue: Issues buenos para principianteshelp wanted: Issues que necesitan ayudabug: Correcciones de bugsenhancement: Nuevas funcionalidades
- Node.js: v18.0.0 o superior
- npm: v9.0.0 o superior
- Git: Cualquier versión reciente
- Editor: VS Code recomendado (con extensión ESLint si aplica)
-
Fork el repositorio
# Visita https://github.com/HormigaDev/patto-cli # Haz clic en "Fork" en la esquina superior derecha
-
Clona tu fork
git clone https://github.com/TU-USUARIO/patto-cli.git cd patto-cli -
Agrega el repositorio upstream
git remote add upstream https://github.com/HormigaDev/patto-cli.git
-
Instala las dependencias
npm install
-
Compila el proyecto
npm run build
-
Enlaza el CLI localmente
npm link
Ahora puedes usar
pattoen tu terminal para probar tus cambios. -
Verifica la instalación
patto --version # Debería mostrar: 0.0.1
Para trabajar con recarga automática:
# En una terminal, compila en modo watch
npm run build -- --watch
# En otra terminal, prueba tus cambios
patto g command testpatto-cli/
├── src/
│ ├── commands/
│ │ ├── generate/
│ │ │ ├── command.ts # Generador de comandos
│ │ │ ├── subcommand.ts # Generador de subcomandos
│ │ │ ├── subcommand-group.ts # Generador de grupos
│ │ │ ├── plugin.ts # Generador de plugins
│ │ │ └── README.md # Docs de los generadores
│ │ ├── generate.ts # Orquestador principal
│ │ └── init.ts # Comando init
│ ├── index.ts # Entry point del CLI
│ ├── loader.ts # Loader para comandos
│ ├── types.ts # Definiciones de tipos
│ └── utils.ts # Funciones de utilidad
├── tests/
│ └── generate/
│ ├── command.test.ts # Tests de comandos
│ ├── subcommand.test.ts # Tests de subcomandos
│ ├── subcommand-group.test.ts # Tests de grupos
│ ├── plugin.test.ts # Tests de plugins
│ └── README.md # Docs de los tests
├── dist/ # Código compilado (gitignored)
├── package.json # Dependencias y scripts
├── tsconfig.json # Configuración de TypeScript
├── README.md # Documentación principal
├── CHANGELOG.md # Registro de cambios
├── CONTRIBUTING.md # Este archivo
├── CODE_OF_CONDUCT.md # Código de conducta
└── SECURITY.md # Política de seguridad
Contiene todos los generadores:
-
command.ts: Genera comandos slash de Discord
- Funciones principales:
handleGenerateCommand(),createUnifiedFile(),createSplitFiles() - Validación:
validateKebabCase(),sanitizePath() - Parsing:
parseCommandName()
- Funciones principales:
-
subcommand.ts: Genera subcomandos
- Funciones principales:
handleGenerateSubcommand(),parseSubcommandName() - Validación de existencia del comando padre
- Funciones principales:
-
subcommand-group.ts: Genera grupos de subcomandos
- Funciones principales:
handleGenerateGroup(),parseGroupName() - Validación de padre y subcomando
- Funciones principales:
-
plugin.ts: Genera plugins con auto-registro
- Funciones principales:
handleGeneratePlugin() - Auto-registro en
plugins.config.ts
- Funciones principales:
Entry point del CLI que:
- Configura Commander.js
- Carga comandos dinámicamente
- Maneja errores globales
Funciones de utilidad compartidas:
sanitizePath(): Previene path traversalvalidateKebabCase(): Valida nombres- Otras helpers comunes
- Simplicidad: Mantén el código simple y legible
- Modularidad: Una función, una responsabilidad
- Seguridad: Valida todas las entradas del usuario
- Testing: Todo el código nuevo debe tener tests
- Documentación: Documenta funciones públicas y lógica compleja
-
Usa tipos explícitos siempre que sea posible
// ✅ Correcto function calculateSum(a: number, b: number): number { return a + b; } // ❌ Incorrecto function calculateSum(a, b) { return a + b; }
-
Usa interfaces para objetos complejos
interface GenerateOptions { name: string; description?: string; split?: boolean; }
-
Evita
any, usaunknownsi no conoces el tipo// ✅ Correcto function processData(data: unknown): void { if (typeof data === 'string') { // procesar } } // ❌ Incorrecto function processData(data: any): void { // ... }
-
Variables y funciones: camelCase
const userName = 'John'; function getUserName(): string {}
-
Clases e interfaces: PascalCase
class CommandGenerator {} interface CommandOptions {}
-
Constantes: UPPER_SNAKE_CASE
const MAX_RETRY_ATTEMPTS = 3; const DEFAULT_TIMEOUT = 5000;
-
Archivos: kebab-case
command-generator.ts user-utils.ts
-
Funciones pequeñas y enfocadas
// ✅ Correcto: Función con una responsabilidad function validateName(name: string): boolean { return /^[a-z0-9-]+$/.test(name); } // ❌ Incorrecto: Función que hace demasiado function processCommand(name, options, files, config) { // validar, crear archivos, actualizar config... }
-
Retorna temprano
// ✅ Correcto function processUser(user: User | null): void { if (!user) return; if (!user.isActive) return; // procesar usuario activo } // ❌ Incorrecto function processUser(user: User | null): void { if (user) { if (user.isActive) { // procesar usuario activo } } }
-
Usa errores descriptivos
// ✅ Correcto if (!fs.existsSync(parentPath)) { console.error(chalk.red(`❌ El comando padre '${parent}' no existe.`)); console.log( chalk.yellow(`💡 Primero crea el comando padre con: patto g command ${parent}`), ); process.exit(1); } // ❌ Incorrecto if (!fs.existsSync(parentPath)) { throw new Error('File not found'); }
Todas las entradas del usuario deben ser validadas:
// Valida formato kebab-case
function validateKebabCase(value: string): boolean {
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(value);
}
// Sanitiza rutas para prevenir path traversal
function sanitizePath(inputPath: string): string {
const parts = inputPath
.split('/')
.filter((part) => part !== '..' && part !== '.' && part !== '');
return parts.join('/');
}// ✅ Correcto: Sanitiza antes de usar
const safePath = sanitizePath(userInput);
const fullPath = path.join(baseDir, safePath);
// ❌ Incorrecto: Usa entrada directamente
const fullPath = path.join(baseDir, userInput); // ¡Peligroso!Usamos Vitest para testing. Todos los tests están en tests/.
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import fs from 'fs';
import path from 'path';
describe('Command Generator', () => {
const testDir = path.join(process.cwd(), 'test-commands-unique');
beforeEach(() => {
// Setup: Crea directorio de prueba
if (!fs.existsSync(testDir)) {
fs.mkdirSync(testDir, { recursive: true });
}
});
afterEach(() => {
// Cleanup: Elimina directorio de prueba
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true });
}
});
it('should create a command file', () => {
const commandName = 'test';
const filePath = path.join(testDir, `${commandName}.command.ts`);
// Genera el comando
// Tu código aquí
// Verifica que el archivo fue creado
expect(fs.existsSync(filePath)).toBe(true);
});
it('should include correct class name', () => {
const commandName = 'test-command';
const filePath = path.join(testDir, `${commandName}.command.ts`);
// Genera el comando
// Tu código aquí
// Lee el contenido
const content = fs.readFileSync(filePath, 'utf-8');
// Verifica el nombre de la clase
expect(content).toContain('class TestCommandCommand');
});
});-
Aislamiento: Cada test debe ser independiente
-
Cleanup: Siempre limpia después de los tests
-
Nombres descriptivos: Los nombres de tests deben ser claros
-
Arrange-Act-Assert: Sigue este patrón
it('should validate kebab-case', () => { // Arrange const validName = 'my-command'; const invalidName = 'MyCommand'; // Act const isValid = validateKebabCase(validName); const isInvalid = validateKebabCase(invalidName); // Assert expect(isValid).toBe(true); expect(isInvalid).toBe(false); });
# Ejecutar todos los tests
npm test
# Ejecutar con watch mode
npm run test:watch
# Ejecutar con cobertura
npm run test:coverage
# Ejecutar tests específicos
npm test -- command.test.tsNuestro objetivo es mantener >80% de cobertura:
npm run test:coverageEsto generará un reporte en coverage/.
# Actualiza tu fork
git checkout master
git pull upstream master
# Crea una rama para tu funcionalidad
git checkout -b feature/mi-nueva-funcionalidad
# O para una corrección de bug
git checkout -b fix/corregir-bug-xyz- Escribe código limpio y bien documentado
- Agrega tests para tus cambios
- Actualiza la documentación si es necesario
- Sigue las convenciones de código
Usamos Conventional Commits:
# Formato: <tipo>(<alcance>): <descripción> @issue/<número>
# Tipos:
# - feat: Nueva funcionalidad @issue/<número>
# - fix: Corrección de bug @issue/<número>
# - docs: Cambios en documentación @issue/<número>
# - style: Cambios de formato (sin cambios de código) @issue/<número>
# - refactor: Refactorización (sin cambios de funcionalidad) @issue/<número>
# - test: Agregar o modificar tests @issue/<número>
# - chore: Cambios en build o herramientas @issue/<número>
# Ejemplos:
git commit -m "feat(generate): agregar soporte para plugins @issue/42"
git commit -m "fix(command): corregir validación de nombres @issue/56"
git commit -m "docs(readme): actualizar ejemplos de uso @issue/78"
git commit -m "test(plugin): agregar tests para auto-registro @issue/90"git push origin feature/mi-nueva-funcionalidad- Ve a https://github.com/HormigaDev/patto-cli
- Haz clic en "Pull Request"
- Selecciona tu rama
- Completa la plantilla de PR:
## Descripción
Breve descripción de tus cambios
## Tipo de cambio
- [ ] Bug fix (cambio que corrige un issue)
- [ ] Nueva funcionalidad (cambio que agrega funcionalidad)
- [ ] Breaking change (corrección o funcionalidad que causaría que funcionalidad existente no funcione como se esperaba)
- [ ] Documentación
- [ ] Refactoring
- [ ] Tests
## ¿Cómo ha sido probado?
Describe los tests que ejecutaste
## Checklist
- [ ] Mi código sigue las convenciones de este proyecto
- [ ] He revisado mi propio código
- [ ] He comentado mi código, especialmente en áreas difíciles
- [ ] He actualizado la documentación
- [ ] Mis cambios no generan nuevas advertencias
- [ ] He agregado tests que prueban que mi corrección es efectiva o que mi funcionalidad funciona
- [ ] Los tests unitarios nuevos y existentes pasan localmente
- [ ] He actualizado CHANGELOG.md- Responde a los comentarios de revisión
- Haz cambios solicitados
- Mantén la discusión profesional y constructiva
Una vez aprobado, un mantenedor hará merge de tu PR. ¡Felicidades! 🎉
Configuraremos ESLint próximamente. Mientras tanto:
- Usa TypeScript strict mode
- Sigue las convenciones de este documento
- Revisa manualmente tu código
Prettier está configurado en el archivo .prettierrc. Usa los siguientes ajustes:
- Usa 4 espacios para indentación (como está configurado en tsconfig.json)
- No uses tabs
- Usa comillas simples para strings
- Agrega punto y coma al final de las declaraciones
/**
* Valida que un nombre esté en formato kebab-case.
*
* @param value - El string a validar
* @returns true si el valor es kebab-case válido, false de lo contrario
*
* @example
* ```typescript
* validateKebabCase('my-command') // true
* validateKebabCase('MyCommand') // false
* ```
*/
function validateKebabCase(value: string): boolean {
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(value);
}Si agregas una nueva funcionalidad, actualiza:
- README.md: Agrega ejemplos de uso
- CHANGELOG.md: Documenta el cambio bajo "No Publicado"
// ✅ Correcto: Explica el "por qué", no el "qué"
// Sanitizamos la ruta para prevenir ataques de path traversal
const safePath = sanitizePath(userInput);
// ❌ Incorrecto: Describe lo obvio
// Llama a la función sanitizePath con userInput
const safePath = sanitizePath(userInput);- Issues: Para bugs y solicitudes de funcionalidades
- Pull Requests: Para discutir código
- Email: hormigadev7@gmail.com para temas sensibles
- Sé respetuoso: Trata a otros como quieres ser tratado
- Sé claro: Comunica tus ideas claramente
- Sé paciente: Todos estamos aprendiendo
- Sé constructivo: Enfócate en soluciones, no en problemas
¿Atascado? ¡No hay problema!
- Revisa la documentación: README.md, código, tests
- Busca en issues: Puede que alguien ya haya preguntado
- Crea un issue: Describe tu pregunta claramente
- Envía email: hormigadev7@gmail.com para ayuda directa
Todos los contribuyentes son reconocidos en:
- Notas de lanzamiento
- CHANGELOG.md
- GitHub contributors page
Al contribuir, aceptas que tus contribuciones serán licenciadas bajo la Licencia MIT.
¡Gracias por contribuir a Patto CLI! 🚀
Tu tiempo y esfuerzo ayudan a hacer esta herramienta mejor para todos. 💙
Última Actualización: 14 de Noviembre de 2025
Mantenedor: HormigaDev (hormigadev7@gmail.com)