@imolko/ultra-generator
v2.1.19
Published
Generador para proyectos relacionados con @imolko/ultra-ddd
Readme
Ultra-Generator
Generador de código para proyectos basados en @imolko/ultra-ddd. Produce artefactos de dominio (value-objects, entidades, aggregate-roots, servicios), features (comandos, queries) y casos de uso, siguiendo los principios de Clean Architecture.
El código generado arranca en estado verde: compila sin errores y todos los tests pasan. Esto permite iniciar inmediatamente el ciclo red-green-refactor de TDD.
Instalación
npm install @imolko/ultra-generatorConfiguración inicial
Ejecuta el comando de configuración para preparar tu proyecto:
npx ultra-generator setupEsto crea el archivo ultra-generator.config.json en la raíz del proyecto:
{
"domainRoot": "src/domain",
"featuresRoot": "src/features",
"useCasesRoot": "src/domain/use-cases"
}Puedes modificar estas rutas según la estructura de tu proyecto.
Uso
Ultra-generator ofrece dos modos de uso: CLI interactivo y API programática. Ambos producen el mismo resultado.
Modo interactivo (CLI)
Para desarrolladores que trabajan directamente en la terminal:
npx ultra-generator generateEsto inicia un asistente interactivo que te guiará paso a paso para seleccionar el tipo de generador y proporcionar los datos necesarios.
Modo JSON (CLI)
Para agentes y automatizaciones que necesitan enviar datos estructurados sin interacción:
# Con JSON en línea
npx ultra-generator generate --json '{"generator":"add-domain","data":{"domainName":"user-email","domainType":"value-object","conditions":[{"name":"value","type":"string","category":"basic"}]}}'
# Con archivo JSON
npx ultra-generator generate --json-file ./input.json
# Vista previa sin escribir a disco
npx ultra-generator generate --json-file ./input.json --dry-run
# Directorio del proyecto (por defecto: directorio actual)
npx ultra-generator generate --json-file ./input.json --project-root /ruta/al/proyectoFormato del input JSON
{
"generator": "add-domain",
"data": {
"domainName": "user-email",
"domainType": "value-object",
"contextName": "core",
"boundedContext": "guarura",
"language": "es",
"conditions": [
{
"name": "value",
"type": "string",
"category": "basic",
"validation": { "minLength": 5, "maxLength": 100 }
}
],
"testCases": ["email vacío", "email sin arroba"]
}
}Salida JSON
{
"success": true,
"generator": "add-domain",
"changes": [
{ "path": "src/domain/user-email/value-object.ts", "type": "CREATE", "content": "..." },
{ "path": "src/domain/user-email/value-object.spec.ts", "type": "CREATE", "content": "..." }
],
"dryRun": false,
"instructions": {
"canonical": "@imolko/ultra-generator/skills/ultra-generator/references/value-object.md",
"stubPath": "src/domain/user-email/.instructions.md"
}
}En modo --dry-run, los archivos no se escriben a disco — solo se devuelve el contenido que se generaría.
API programática (JavaScript/TypeScript)
Para integraciones directas en código:
import { run, addDomain, addFeature, addUseCase } from '@imolko/ultra-generator';
// ── Usando el dispatcher ──
const result = await run(
{
generator: 'add-domain',
data: {
domainName: 'user-email',
domainType: 'value-object',
contextName: 'core',
conditions: [{ name: 'value', type: 'string', category: 'basic' }],
},
},
{ projectRoot: '/ruta/al/proyecto', dryRun: true }
);
if (result.success) {
console.log('Archivos a generar:', result.changes.map(c => c.path));
} else {
console.error('Error:', result.error.message);
}
// ── Usando las funciones nombradas ──
await addDomain({
domainName: 'user-profile',
domainType: 'entity',
contextName: 'core',
conditions: [{ name: 'displayName', type: 'string', category: 'basic' }],
});
await addFeature({
featureName: 'create-contact',
featureType: 'command',
});
await addUseCase({
useCaseName: 'Registrar Usuario',
useCaseType: 'core',
});API disponible
| Función | Descripción |
|---------|-------------|
| run(input, options?) | Dispatcher genérico. Valida con Zod y ejecuta el generador correspondiente. |
| addDomain(data) | Genera un objeto de dominio (value-object, entity, aggregate-root, service). |
| addFeature(data) | Genera un feature (command o query) con use-case, adaptadores y drivers. |
| addAction(data) | Agrega una acción a un feature existente. |
| addErrors(data) | Agrega errores de dominio o de feature. |
| addUseCase(data) | Genera un caso de uso (definición + spec). |
Generadores disponibles
add-domain
Genera objetos de dominio con todas sus piezas:
- value-object:
value-object.ts,value-object.spec.ts,definition.ts,props.ts,primitive.ts,payload.ts,conditions.ts,errors/ - entity:
entity.ts,entity.spec.ts,definition.ts,props.ts,primitive.ts,payload.ts,conditions.ts,errors/ - aggregate-root:
aggregate.ts,aggregate.spec.ts,definition.ts,props.ts,primitive.ts,snapshot.ts,payload.ts,conditions.ts,errors/,security-token.ts - service:
service.ts,service.spec.ts,definition.ts,service-input.ts,service-output.ts
// Schema Zod
{
description: string; // descripción del artefacto (requerido)
domainName: string; // nombre en kebab-case
domainType: 'value-object' | 'entity' | 'aggregate-root' | 'service';
contextName?: string; // contexto bounded context (default: '')
boundedContext?: string; // bounded context para documentación (fallback: contextName)
language?: 'en' | 'es'; // idioma para README generado (default: 'en')
conditions?: Array<{ // condiciones de validación
name: string;
type: string;
category: 'basic' | 'value-object' | 'entity' | 'aggregate-root';
description?: string; // descripción de la propiedad
validation?: { // validaciones nativas de Zod (opcional)
minLength?: number; // solo strings — .min(n)
maxLength?: number; // solo strings — .max(n)
pattern?: string; // solo strings — .regex(new RegExp(pattern))
min?: number; // solo numbers — .min(n)
max?: number; // solo numbers — .max(n)
customErrors?: string[]; // errores de dominio complejos (superRefine)
};
}>;
props?: Array<{ name: string; type: string; category: string; description?: string }>;
conditionsErrorsName?: string[];
othersErrorsName?: string[];
testCases?: string[]; // casos de borde a probar
tags?: string;
}Validaciones nativas de Zod
Cuando una condition incluye el campo validation con constraints nativos (minLength, maxLength, pattern, min, max), el generador emite primitivas de Zod en lugar de superRefine:
// Entrada
conditions: [{
name: 'email',
type: 'string',
category: 'basic',
validation: { minLength: 5, maxLength: 100, pattern: '^[a-z]+@[a-z]+\\.com$' }
}]
// Salida generada en conditions.ts
export const conditions = z.string()
.min(5)
.max(100)
.regex(new RegExp("^[a-z]+@[a-z]+\\.com$"))
.transform(value => ({value: value}));Si validation.customErrors tiene elementos, se genera superRefine a nivel de objeto y se invoca el generador add-errors automáticamente. Las validaciones nativas y customErrors pueden coexistir en la misma property.
Documentación multi-idioma
El README generado respeta el campo language:
"en"(default): Purpose, Rules, Usage Examples, Relationships"es": Propósito, Reglas, Ejemplos de uso, Relaciones
El README no incluye título H1 y agrega un diagrama Mermaid placeholder en la sección Relaciones. Si se provee boundedContext, se incluye en el frontmatter YAML.
Tests con JSDoc
Los stubs de test generados incluyen anotaciones JSDoc @name y @scenario antes de cada it():
/**
* @name crear con input válido
* @scenario email vacío
*/
it('crear con input válido', () => { ... });Cuando no hay conditions con valores por defecto, se usa un cast as Payload con un comentario TODO para que el stub compile:
// TODO: Provide a valid payload for this test case
const payload: Payload = {} as Payload;add-feature
Genera un feature completo con arquitectura limpia:
feature/
├── index.ts
├── use-case/
│ ├── use-case.ts
│ ├── use-case.spec.ts
│ ├── input.dto.ts
│ ├── presenter.port.ts
│ ├── index.ts
│ └── errors/
│ ├── errors.ts
│ ├── template.error.ts
│ └── index.ts
├── adapters/
│ ├── presenter/
│ │ ├── adapter.ts
│ │ ├── driver.port.ts
│ │ └── index.ts
│ └── controller/
│ ├── adapter.ts
│ ├── input.dto.ts
│ └── index.ts
└── drivers/
├── index.ts
└── graphql/
├── driver.ts
├── item.dto.ts
├── output.dto.ts
└── index.ts// Schema Zod
{
featureName: string; // nombre en kebab-case
featureType: 'command' | 'query';
testCases?: string[];
tags?: string;
}add-use-case
Genera la definición de un caso de uso:
// Schema Zod
{
useCaseName: string; // nombre en formato título
useCaseType: 'core' | 'support' | 'configuration';
contextName?: string;
testCases?: string[];
tags?: string;
}Ciclo red-green-refactor
El código generado por ultra-generator está diseñado para arrancar en estado verde:
- Compila con TypeScript en modo estricto (
strict: true) - Todos los tests pasan — los tests generados usan
it.todo()que Jest cuenta como pendientes, no como fallos
Esto permite al desarrollador o agente comenzar inmediatamente el ciclo TDD:
- Verde inicial: ejecuta
tsc --noEmit && jest— todo limpio - Rojo: escribe un test que falle
- Verde: implementa el código mínimo para pasar el test
- Refactor: mejora el código manteniendo los tests en verde
- Repite desde 2
Tests generados
| Tipo | Test generado |
|------|---------------|
| Value-object | it('crear con input válido', ...) — con JSDoc @name/@scenario, compila y pasa |
| Entity | it('crear X valida', ...) — con JSDoc @name/@scenario, compila y pasa |
| Aggregate-root | it('crear X valida', ...) + it('restaurar snapshot', ...) — compilan y pasan |
| Service | it.todo('template test') |
| Feature | it.todo('execute use case successfully') |
| Use-case | it.todo('template test') |
Los tests con domain-objects en sus condiciones se envuelven automáticamente en it.todo con un comentario guía, ya que el generador no puede conocer las instancias válidas de objetos de dominio arbitrarios.
Cuando no hay conditions, el payload usa {} as Payload con un comentario // TODO para que el stub compile desde el primer momento.
Verificación de plantillas (E2E)
El proyecto incluye un fixture y un test E2E para verificar que el código generado siempre arranca en verde:
npm run test:e2eEsto genera uno de cada tipo de artefacto en src/e2e/fixture/ y verifica:
tsc --noEmit→ cero erroresjest --passWithNoTests→ cero fallos
Lee src/e2e/README.md para más detalles sobre cómo usar el fixture para desarrollo de plantillas.
Agentes y Skills
Ultra-generator incluye un Skill compatible con la especificación agentskills.io para agentes de código (pi, gemini-cli, copilot-cli, etc.).
Skill del paquete
El skill se encuentra en node_modules/@imolko/ultra-generator/dist-node/skills/ultra-generator/SKILL.md. Sigue la estructura estándar:
skills/ultra-generator/
├── SKILL.md # Skill principal
└── references/ # Instrucciones canónicas por artefacto
├── value-object.md
├── entity.md
├── aggregate-root.md
└── service.mdCómo usan los agentes este skill
- Activar el skill: El agente carga
SKILL.mdy sigue la guía de recolección de datos - Invocar el generador: Usando el modo CLI con
--jsono la API programática - Interpretar la respuesta: El
GeneratorResultincluye una referenciainstructions:{ "instructions": { "canonical": "@imolko/ultra-generator/skills/ultra-generator/references/value-object.md", "stubPath": "src/domain/user-email/.instructions.md" } } - Leer el stub: El archivo
.instructions.mdgenerado junto al artefacto contiene:- Los parámetros usados en la generación
- Un checklist post-generación
- La lista de archivos a editar y los que no deben tocarse
- Un enlace a las instrucciones canónicas en el paquete
- Seguir el checklist: El agente implementa las personalizaciones necesarias siguiendo la guía de referencias
Flujo completo para agentes
Agente recibe solicitud del usuario
→ Consulta SKILL.md para entender qué datos recolectar
→ Construye JSON con { generator, data }
→ Ejecuta: npx ultra-generator generate --json-file /tmp/input.json --dry-run
→ Revisa cambios y presenta resumen al usuario (recomendado)
→ Ejecuta sin --dry-run para generar los archivos
→ Lee .instructions.md en el directorio del artefacto
→ Sigue las instrucciones canónicas en references/
→ Implementa personalizaciones con TDD red-green-refactorGeneración por lotes
El skill documenta cómo crear múltiples artefactos en lote resolviendo dependencias:
El usuario proporciona un CSV o JSON con múltiples artefactos
→ El agente construye un grafo de dependencias desde conditions[].type y props[].type
→ Ordena topológicamente (hojas primero, dependientes después)
→ Genera los artefactos en orden
→ Implementa de forma coherente (ej: todos los artefactos de un bounded context)