|
1 | | -# AGENT_TASKS.md — Fase 1: Robustez Absoluta y Cobertura |
| 1 | +# Tareas de Agentes IA — Fase 2: Flexibilidad Interna y DX |
2 | 2 |
|
3 | | -> Guía de proceso para agentes de IA trabajando en la Fase 1 del roadmap (v0.4.x - v0.5.x). |
4 | | -> |
5 | | -> Este documento complementa al `AGENTS.md` del template y se enfoca exclusivamente en las tareas de robustez y cobertura del CLI scaffolder. |
| 3 | +Este archivo es el **Sprint Plan** para los agentes de IA (OpenClaw, Cursor, Claude). Define las reglas de trabajo y las tareas específicas para la **Fase 2**. |
6 | 4 |
|
7 | | ---- |
8 | | - |
9 | | -## 🎯 Objetivo de la Fase 1 |
10 | | - |
11 | | -**El CLI nunca debe fallar de manera inesperada ("crash") bajo ninguna condición local.** |
12 | | - |
13 | | -Esto implica: |
14 | | -1. Testing exhaustivo de todos los flujos del scaffolder |
15 | | -2. Soporte confiable para `npm`, `pnpm`, `yarn` y `bun` |
16 | | -3. Manejo amigable de edge cases del file system |
17 | | -4. Inicialización Git robusta con fallbacks limpios |
18 | | - |
19 | | ---- |
20 | | - |
21 | | -## 📋 Workflow Obligatorio por Tarea |
22 | | - |
23 | | -Antes de implementar CUALQUIER tarea de esta fase, el agente DEBE seguir este ciclo: |
24 | | - |
25 | | -1. **Leer contexto**: `docs/decisions.md`, `CHANGELOG.md`, `ROADMAP.md` |
26 | | -2. **Verificar estado actual**: Correr `npm run test:run` para confirmar que todo pasa antes de tocar código |
27 | | -3. **Implementar** siguiendo las convenciones del `AGENTS.md` del template |
28 | | -4. **Testear**: Escribir tests ANTES o JUNTO con el código (no después) |
29 | | -5. **Lint**: Correr `npm run lint:fix` tras cada modificación |
30 | | -6. **Typecheck**: Correr `npm run typecheck` para verificar tipos |
31 | | -7. **Documentar**: Actualizar `docs/decisions.md` (ADR) si se tomó una decisión técnica |
32 | | -8. **Changelog**: Registrar cambios en `CHANGELOG.md` bajo `[Unreleased]` |
33 | | - |
34 | | ---- |
35 | | - |
36 | | -## 📐 Decisiones de Diseño Aprobadas |
37 | | - |
38 | | -| # | Decisión | Resolución | |
39 | | -|---|----------|------------| |
40 | | -| D002 | Exportar funciones internas (`validateProjectName`, `execInDir`) | ✅ Aprobado — para testabilidad directa | |
41 | | -| D003 | Fallback Git sin `user.name`/`user.email` | ✅ Opción A — usar valores genéricos (`create-stack-next` / `noreply@create-stack-next`) con warning | |
42 | | -| D004 | Directorio destino existente vacío | ✅ Aceptar y continuar generación. Solo rechazar si NO está vacío | |
43 | | -| D005 | Tests multi-PM en CI | ⏸️ Pospuesto a fase posterior. Tests locales usan `skipIf` si el PM no está disponible | |
44 | | - |
45 | | ---- |
46 | | - |
47 | | -## 🛠️ Épica 1: Testing Exhaustivo del Scaffolder |
48 | | - |
49 | | -### Archivos involucrados |
50 | | -- `src/test/cli.test.ts` |
51 | | -- `src/test/copy-template.test.ts` |
52 | | -- `src/test/integration.test.ts` |
53 | | - |
54 | | -### Tareas |
55 | | - |
56 | | -#### 1.1 Ampliar `cli.test.ts` |
57 | | - |
58 | | -**Estado actual:** 13 tests que validan parsing de args y regex de nombres (sin usar `validateProjectName` directamente). |
59 | | - |
60 | | -**Tests nuevos requeridos:** |
61 | | -- [ ] Importar y testear `validateProjectName` directamente |
62 | | -- [ ] Nombre vacío (`""`) → `{ ok: false }` |
63 | | -- [ ] Nombre con caracteres especiales (`@scope/name`, `my-app!`, `foo/bar`) → `{ ok: false }` |
64 | | -- [ ] Directorio existente NO vacío → `{ ok: false, reason: "...ya existe" }` |
65 | | -- [ ] Directorio existente VACÍO → `{ ok: true }` |
66 | | -- [ ] Nombre que empieza con `-` → `{ ok: false }` |
67 | | -- [ ] `parseArgs` con múltiples flags combinadas (`--no-git --no-install --pm pnpm`) |
68 | | - |
69 | | -**Criterio de aceptación:** Cobertura completa de `validateProjectName` y `parseArgs`. |
70 | | - |
71 | | -#### 1.2 Ampliar `copy-template.test.ts` |
72 | | - |
73 | | -**Estado actual:** 2 tests (nombre en package.json + PM en README). |
74 | | - |
75 | | -**Tests nuevos requeridos:** |
76 | | -- [ ] Verificar que todos los archivos del template se copian (comparar `listFiles(templateDir)` vs `listFiles(targetDir)`) |
77 | | -- [ ] Verificar que dotfiles se copian (`.gitignore`, `.openclaw/`, `.agents/`, `.github/`, `.vscode/`) |
78 | | -- [ ] Verificar que archivos sin placeholders no se modifican (comparar contenido byte a byte) |
79 | | -- [ ] Verificar que paths anidados profundos existen (`docs/`, `src/app/`, `tests/e2e/`) |
80 | | - |
81 | | -**Criterio de aceptación:** La copia del template es verificada exhaustivamente. |
82 | | - |
83 | | -#### 1.3 Ampliar `integration.test.ts` |
84 | | - |
85 | | -**Estado actual:** 1 test que genera un proyecto con `npm` y corre lint/typecheck/test/build. |
86 | | - |
87 | | -**Tests nuevos requeridos (condicionales):** |
88 | | -- [ ] Generación con `pnpm` (skip si no está instalado) |
89 | | -- [ ] Generación con `yarn` (skip si no está instalado) |
90 | | -- [ ] Generación con `bun` (skip si no está instalado) |
91 | | - |
92 | | -> **Nota:** Estos tests multi-PM se posponen a una fase posterior según decisión del usuario. En esta fase solo se deja la estructura preparada con `it.skip`. |
93 | | -
|
94 | | -**Criterio de aceptación:** Test de `npm` robusto + placeholders para otros PMs. |
95 | | - |
96 | | ---- |
97 | | - |
98 | | -## 🛠️ Épica 2: Garantía Multi-Package Manager |
| 5 | +## 🛑 Workflow Obligatorio por Tarea |
99 | 6 |
|
100 | | -> ⏸️ **Pospuesto a fase posterior** según decisión del usuario. |
101 | | -> Solo se valida que el flag `--pm` parsea correctamente y que `runInstall` construye el comando correcto. |
102 | | -
|
103 | | -### Tareas mínimas para esta fase |
104 | | -- [ ] Tests unitarios de `parseArgs` ya cubren validación de `--pm` ✅ |
105 | | -- [ ] Agregar test que verifique que el output de "Próximos pasos" muestra el PM correcto |
106 | | - |
107 | | ---- |
108 | | - |
109 | | -## 🛠️ Épica 3: Edge Cases de File System |
110 | | - |
111 | | -### Archivos involucrados |
112 | | -- `src/cli.ts` (modificar `validateProjectName`) |
113 | | - |
114 | | -### Tareas |
115 | | - |
116 | | -#### 3.1 Directorio existente vacío → permitir |
117 | | - |
118 | | -**Cambio en `validateProjectName`:** |
119 | | -```typescript |
120 | | -// Antes: rechaza si existe |
121 | | -if (existsSync(name)) { |
122 | | - return { ok: false, reason: `El directorio "${name}" ya existe` }; |
123 | | -} |
124 | | - |
125 | | -// Después: rechaza solo si existe Y no está vacío |
126 | | -if (existsSync(name)) { |
127 | | - const entries = readdirSync(name); |
128 | | - // Permitir .DS_Store y similares |
129 | | - const meaningful = entries.filter(e => !e.startsWith('.DS_')); |
130 | | - if (meaningful.length > 0) { |
131 | | - return { ok: false, reason: `El directorio "${name}" ya existe y no está vacío` }; |
132 | | - } |
133 | | -} |
134 | | -``` |
135 | | - |
136 | | -#### 3.2 Validación de permisos de escritura |
137 | | - |
138 | | -**Nuevo chequeo antes de crear el directorio:** |
139 | | -```typescript |
140 | | -// Verificar que el directorio padre tiene permisos de escritura |
141 | | -try { |
142 | | - accessSync(dirname(resolve(name)), constants.W_OK); |
143 | | -} catch { |
144 | | - return { ok: false, reason: `Sin permisos de escritura en "${dirname(resolve(name))}"` }; |
145 | | -} |
146 | | -``` |
147 | | - |
148 | | -#### 3.3 Tests para edge cases de FS |
149 | | - |
150 | | -- [ ] Directorio existente no vacío → error con mensaje claro |
151 | | -- [ ] Directorio existente vacío → OK |
152 | | -- [ ] Directorio existente con solo `.DS_Store` → OK |
153 | | - |
154 | | ---- |
155 | | - |
156 | | -## 🛠️ Épica 4: Robustez en Inicialización Git |
157 | | - |
158 | | -### Archivos involucrados |
159 | | -- `src/cli.ts` (modificar `runGitInit`) |
160 | | -- `src/test/git.test.ts` (nuevo) |
161 | | - |
162 | | -### Tareas |
163 | | - |
164 | | -#### 4.1 Detectar si Git está instalado |
165 | | - |
166 | | -```typescript |
167 | | -async function isGitAvailable(): Promise<boolean> { |
168 | | - try { |
169 | | - await execInDir("git", ["--version"], process.cwd()); |
170 | | - return true; |
171 | | - } catch { |
172 | | - return false; |
173 | | - } |
174 | | -} |
175 | | -``` |
176 | | - |
177 | | -#### 4.2 Detectar y manejar `user.name` / `user.email` |
178 | | - |
179 | | -```typescript |
180 | | -async function getGitConfig(key: string): Promise<string | null> { |
181 | | - try { |
182 | | - // Capturar stdout en vez de heredar |
183 | | - const value = await execCapture("git", ["config", "--global", key]); |
184 | | - return value.trim() || null; |
185 | | - } catch { |
186 | | - return null; |
187 | | - } |
188 | | -} |
189 | | -``` |
190 | | - |
191 | | -#### 4.3 Fallback con valores genéricos |
192 | | - |
193 | | -```typescript |
194 | | -async function runGitInit(projectDir: string): Promise<void> { |
195 | | - // 1. Verificar que git está disponible |
196 | | - if (!(await isGitAvailable())) { |
197 | | - logStep("⚠️", "Git no encontrado. Saltando inicialización git."); |
198 | | - logStep("💡", `Instalá Git y corré ${pc.cyan("git init")} manualmente.`); |
199 | | - return; |
200 | | - } |
201 | | - |
202 | | - await execInDir("git", ["init", "-b", "main"], projectDir); |
203 | | - await execInDir("git", ["add", "."], projectDir); |
204 | | - |
205 | | - // 2. Verificar user.name/user.email |
206 | | - const userName = await getGitConfig("user.name"); |
207 | | - const userEmail = await getGitConfig("user.email"); |
208 | | - |
209 | | - if (!userName || !userEmail) { |
210 | | - logStep("⚠️", "Git user.name/user.email no configurados. Usando valores temporales."); |
211 | | - await execInDir( |
212 | | - "git", |
213 | | - [ |
214 | | - "-c", "user.name=create-stack-next", |
215 | | - "-c", "user.email=noreply@create-stack-next", |
216 | | - "commit", "-m", "chore: initial commit from create-stack-next", |
217 | | - ], |
218 | | - projectDir, |
219 | | - ); |
220 | | - logStep("💡", `Configurá tu Git: ${pc.cyan("git config --global user.name \"Tu Nombre\"")}`); |
221 | | - } else { |
222 | | - await execInDir( |
223 | | - "git", |
224 | | - ["commit", "-m", "chore: initial commit from create-stack-next"], |
225 | | - projectDir, |
226 | | - ); |
227 | | - } |
228 | | -} |
229 | | -``` |
230 | | - |
231 | | -#### 4.4 Tests de Git (`src/test/git.test.ts`) |
232 | | - |
233 | | -- [ ] Mock de `execInDir` para simular git no disponible → verifica warning |
234 | | -- [ ] Mock para simular `user.name`/`user.email` no configurados → verifica fallback |
235 | | -- [ ] Test de `--no-git` → verifica que `runGitInit` no se ejecuta |
236 | | -- [ ] Test de flujo exitoso completo |
| 7 | +Antes de escribir una sola línea de código, el agente DEBE: |
| 8 | +1. Leer el `ROADMAP.md` y este archivo para contexto. |
| 9 | +2. Si la tarea implica una decisión arquitectónica nueva o cambio de diseño, documentarla como ADR en `docs/decisions.md`. |
| 10 | +3. Planificar la implementación paso a paso (en la memoria o artifacts temporales). |
| 11 | +4. Ejecutar cambios atómicos. |
| 12 | +5. Correr linting (`npm run lint`), typecheck (`npm run typecheck`) y tests (`npm run test:run`). |
| 13 | +6. Actualizar `CHANGELOG.md` en la sección `[Unreleased]`. |
237 | 14 |
|
238 | 15 | --- |
239 | 16 |
|
240 | | -## 📚 Documentación Requerida |
241 | | - |
242 | | -Al completar CADA épica, actualizar: |
| 17 | +## 🎯 Épicas Activas (Fase 2) |
243 | 18 |
|
244 | | -1. **`docs/decisions.md`** — Nuevo ADR para cada decisión técnica tomada |
245 | | -2. **`CHANGELOG.md`** — Bajo `[Unreleased]`: |
246 | | - - `Added`: tests nuevos, `AGENT_TASKS.md` |
247 | | - - `Changed`: validaciones mejoradas, Git robusto |
248 | | - - `Fixed`: edge cases de FS |
249 | | -3. **`ROADMAP.md`** — Marcar tareas completadas con `[x]` |
| 19 | +### Épica 1: Soporte Oficial para Workspaces (Monorepos) |
| 20 | +El CLI debe funcionar de forma fluida si se lanza dentro de un monorepo (ej: carpeta `apps/`). |
| 21 | +- [ ] Implementar función para detectar si estamos en un workspace (buscar `pnpm-workspace.yaml`, `turbo.json`, `lerna.json` o campo `workspaces` en `package.json` hacia arriba). |
| 22 | +- [ ] Si es un workspace: saltar automáticamente el `git init` (o preguntar) para evitar repos anidados. |
| 23 | +- [ ] Escribir tests unitarios para la detección de workspaces. |
250 | 24 |
|
251 | | ---- |
252 | | - |
253 | | -## ✅ Criterios de "Done" para la Fase 1 |
| 25 | +### Épica 2: Pulido Visual Extremo del CLI |
| 26 | +La terminal debe sentirse "premium" (spinners, colores consistentes). |
| 27 | +- [ ] Instalar `@clack/prompts` como dependencia. |
| 28 | +- [ ] Refactorizar el flujo actual de logs para usar los componentes de Clack (intro, spinners para instalación de npm y git, outro). |
| 29 | +- [ ] Estandarizar el uso de `picocolors` para outputs intermedios o custom. |
254 | 30 |
|
255 | | -- [ ] `npm run test:run` pasa al 100% |
256 | | -- [ ] `npm run typecheck` sin errores |
257 | | -- [ ] `npm run lint` sin errores |
258 | | -- [ ] `validateProjectName` cubre: nombre vacío, caracteres inválidos, directorio existente vacío/no vacío, permisos |
259 | | -- [ ] `runGitInit` no crashea bajo ninguna condición (git no instalado, user no configurado) |
260 | | -- [ ] Todos los archivos del template se copian correctamente (verificado por test) |
261 | | -- [ ] ADRs documentados (D002, D003, D004) |
262 | | -- [ ] CHANGELOG actualizado |
263 | | -- [ ] ROADMAP Fase 1 checklist marcada como completada |
264 | | - |
265 | | ---- |
| 31 | +### Épica 3: Template API (`--template api`) |
| 32 | +Permitir generar un backend puro sin UI React. |
| 33 | +- [ ] Agregar el flag `--template` (con valores `app` | `api`, por defecto `app`) en `parse-args.ts`. |
| 34 | +- [ ] Crear la carpeta `template-api/` con la base (ej: un Next.js solo con Route Handlers o un setup agnóstico). |
| 35 | +- [ ] Ajustar la lógica de copia en `cli.ts` para usar la carpeta de template correspondiente. |
266 | 36 |
|
267 | | -**Última actualización:** 2026-06-30 |
268 | | -**Fase:** 1 de 3 (Robustez Absoluta y Cobertura) |
269 | | -**Versión target:** v0.4.0 - v0.5.0 |
| 37 | +### Épica 4: Garantía Multi-PM en CI (De Fase 1) |
| 38 | +- [ ] Configurar el GitHub Action (`.github/workflows/ci.yml`) para que pruebe la generación de proyecto con `npm`, `pnpm`, `yarn` y `bun`. |
0 commit comments