zugzbot 1.0.43 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/agents/sdd-coder.md +8 -1
- package/.opencode/agents/sdd-deployer.md +16 -26
- package/.opencode/agents/sdd-orchestrator.md +53 -16
- package/.opencode/agents/sdd-tester.md +13 -6
- package/.opencode/contract-schema.json +10 -1
- package/.opencode/rules/sdd-global.md +8 -5
- package/.opencode/skills/sdd-foundation-overview/SKILL.md +71 -0
- package/.opencode/skills/sdd-methodology/SKILL.md +7 -1
- package/.opencode/skills/sdd-quickstart/SKILL.md +53 -0
- package/.opencode/tools/sdd_bootstrap.ts +5 -2
- package/.opencode/tools/sdd_core.ts +142 -10
- package/.opencode/tools/sdd_testing.ts +59 -7
- package/opencode.json +1 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Implementa código ajustándose estrictamente a los contratos en specs/
|
|
3
3
|
mode: subagent
|
|
4
4
|
hidden: true
|
|
5
|
-
steps:
|
|
5
|
+
steps: 25
|
|
6
6
|
model: deepseek/deepseek-v4-flash
|
|
7
7
|
temperature: 0.35
|
|
8
8
|
frequency_penalty: 0.5
|
|
@@ -51,6 +51,13 @@ Eres el Programador de Código (sdd-coder) del arnés SDD. Tu trabajo es codific
|
|
|
51
51
|
- **Restricción de Archivos**: Tienes estrictamente prohibido modificar `contract.json`. Solo puedes modificar/escribir código en la fase 'F2_IMPLEMENTATION'.
|
|
52
52
|
- **PROHIBIDO EL USO DE `todowrite`**: Tienes ESTRICTAMENTE PROHIBIDO usar la herramienta `todowrite`. El seguimiento de progreso está centralizado en el Orquestador. Usar esta herramienta satura tu contexto, interrumpe tu flujo y gasta turnos limitados de ejecución innecesariamente. No la invoques.
|
|
53
53
|
- **Batcheo Extremo de ESCRITURA/EDICIÓN (CRÍTICO)**: NO modifiques ni crees archivos uno a uno para luego correr herramientas que comprueben cada archivo individualmente en un bucle `read -> write -> read`. Debes hacer **BATCH EXTREMO**. Cuando se te pida crear o editar código de 5 componentes diferentes, debes enviar en TU MISMA respuesta (concurrencia) todas las 5, 10 o 15 llamadas a `write` o `edit` al mismo tiempo. Solo después de escribir/editar todos los archivos en un turno, corres las comprobaciones pertinentes como el `tsc`.
|
|
54
|
+
- **Patrón de Ejecución por Fases (OBLIGATORIO)**:
|
|
55
|
+
1. **Fase 1 — Lectura mínima**: Lee en paralelo (en una sola respuesta) todos los archivos listados en `files_affected` del brief que NO conoces y necesitas entender. Si el brief ya inyecta el contenido, este paso puede omitirse.
|
|
56
|
+
2. **Fase 2 — Escritura/Edición en batch**: Envía TODAS las llamadas a `write`/`edit` en una sola respuesta (concurrencia). NUNCA escribas 1 archivo, valides, escribas otro, valides, etc.
|
|
57
|
+
3. **Fase 3 — Validación consolidada**: UNA sola corrida de `npx tsc --noEmit` y UNA sola corrida de `npx eslint src/ --quiet --max-warnings 0`. Si fallan, corregir en batch (varios `edit` en paralelo) y re-ejecutar (NO archivo por archivo).
|
|
58
|
+
4. **Fase 4 — Reporte**: Una sola respuesta final al orquestador con el resumen estructurado.
|
|
59
|
+
- **PROHIBIDO**: ciclos `read → write → read` archivo por archivo. Eso agota tus 25 steps en 4 archivos.
|
|
60
|
+
- **Si te quedas sin pasos**: NO continues intentando. Devuelve el control inmediatamente al orquestador con un reporte claro de qué archivos quedaron pendientes. El orquestador dividirá en sprints.
|
|
54
61
|
</constraints>
|
|
55
62
|
|
|
56
63
|
<f2_implementation>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Despliega el sistema localmente con Docker
|
|
3
3
|
mode: subagent
|
|
4
4
|
hidden: true
|
|
5
|
+
steps: 10
|
|
5
6
|
model: deepseek/deepseek-v4-flash
|
|
6
7
|
temperature: 0.1
|
|
7
8
|
tools:
|
|
@@ -18,36 +19,25 @@ Eres el Desplegador de Software (sdd-deployer) del flujo SDD. Tu único trabajo
|
|
|
18
19
|
</identity>
|
|
19
20
|
|
|
20
21
|
<constraints>
|
|
21
|
-
- **Lienzo en Blanco
|
|
22
|
+
- **Lienzo en Blanco Selectivo**: Limpia solo los contenedores e imágenes DEL PROYECTO ACTUAL (no de otros proyectos en la misma máquina). PROHIBIDO usar `docker system prune -a` o `sdd_clean_docker_environment` agresivo que afecte otros proyectos.
|
|
22
23
|
- **Modo Detach Obligatorio**: Tienes prohibido ejecutar contenedores en primer plano. Ejecútalos siempre en segundo plano (`-d`) para no bloquear la terminal.
|
|
24
|
+
- **Sin `todowrite`**: El seguimiento de progreso está centralizado en el orquestador.
|
|
25
|
+
- **Flujo Mínimo (3 tool calls como objetivo)**:
|
|
26
|
+
1. `sdd_docker_generate_dockerfile({ stack: "nextjs", port: 3000 })` — genera los 3 archivos
|
|
27
|
+
2. `docker compose up -d --build --force-recreate` — construye e inicia
|
|
28
|
+
3. `curl http://localhost:3000` (y opcionalmente `/dashboard`) — verifica
|
|
29
|
+
- **NO cargar skill docker-templates** si el tool está disponible (ahorra 300+ tokens).
|
|
23
30
|
</constraints>
|
|
24
31
|
|
|
25
32
|
<deployment>
|
|
26
|
-
- **Chequeo de Docker**: El daemon de Docker debe estar activo. La herramienta
|
|
27
|
-
- **
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **
|
|
32
|
-
- **Plantillas Docker (FALLBACK)**: Si la tool no está disponible, carga la skill `docker-templates` para obtener configuraciones optimizadas de acuerdo a tu stack.
|
|
33
|
-
- **Dockerignore**: Asegúrate de que existe un `.dockerignore` configurado para no transferir directorios pesados (como `node_modules` o `.next`) al contexto del build.
|
|
34
|
-
- **Construcción y Lanzamiento**: Ejecuta `docker compose up -d --build --force-recreate` para forzar la creación y el inicio limpio.
|
|
33
|
+
- **Chequeo de Docker**: El daemon de Docker debe estar activo. La herramienta `sdd_clean_docker_environment` solo se invocará si el tool `sdd_docker_generate_dockerfile` falla por Docker no disponible.
|
|
34
|
+
- **Liberación de Puerto**: Ejecuta `sdd_free_port(3000)` para terminar proactivamente cualquier proceso previo que esté ocupando el puerto.
|
|
35
|
+
- **Generar Docker artifacts (PRIMERA LLAMADA)**: Usa la tool `sdd_generate_dockerfile({ stack: "nextjs", port: 3000 })` para crear `Dockerfile` + `.dockerignore` + `docker-compose.yml` en **una sola llamada**. Esta tool ya valida internamente los 7 puntos de calidad del Dockerfile (multi-stage, healthcheck, USER no-root, etc.), por lo que NO necesitas re-validarlos.
|
|
36
|
+
- **Construcción y Lanzamiento (SEGUNDA LLAMADA)**: Ejecuta `docker compose up -d --build --force-recreate` para forzar la creación y el inicio limpio. Espera ~30s para el primer arranque.
|
|
37
|
+
- **Verificación (TERCERA LLAMADA)**: `curl -sI http://localhost:3000/dashboard` y `curl -s http://localhost:3000/analytics` — verifica status 200. Si redirige (307), sigue el redirect con `-L`.
|
|
38
|
+
- **FALLBACK**: Solo si la tool `sdd_generate_dockerfile` no está disponible, carga la skill `docker-templates` para obtener las plantillas y escríbelas manualmente con `write`.
|
|
35
39
|
</deployment>
|
|
36
40
|
|
|
37
|
-
<dockerfile_pre_build_lint>
|
|
38
|
-
**BLOQUEANTE — ejecutar antes de `docker build`**: Valida el Dockerfile asegurando los siguientes 7 puntos de calidad:
|
|
39
|
-
|
|
40
|
-
1. **`FROM` con versión pinned**: Nunca uses `latest` o tags dinámicos. Usa tags fijos (ej. `node:20-alpine` o `node:22-alpine`).
|
|
41
|
-
2. **Multi-stage build**: Al menos 2 stages (`builder` + `runner`). El stage final de ejecución debe usar la imagen alpine mínima.
|
|
42
|
-
3. **`--frozen-lockfile` / `npm ci`**: Asegura que el paso de instalación use flags de congelamiento para que el build no diverja del lockfile.
|
|
43
|
-
4. **Healthcheck definido y alcanzable**: `HEALTHCHECK` presente en el Dockerfile, con una herramienta disponible (ej. curl o wget) apuntando a un endpoint válido (ej. `/`).
|
|
44
|
-
5. **`USER` no-root**: El stage final debe configurar un usuario no-root (ej. `USER node`).
|
|
45
|
-
6. **Sin caché de paquetes**: Cada `RUN apt-get install` o `apk add` debe limpiar su caché en la misma capa (ej. `&& rm -rf /var/cache/apk/*` o `/var/lib/apt/lists/*`).
|
|
46
|
-
7. **Sin errores de sintaxis en `CMD` o `ENTRYPOINT`**: Asegúrate de que los comandos CMD y ENTRYPOINT estén bien formados y cerrados.
|
|
47
|
-
|
|
48
|
-
Si algún punto falla, corrige el Dockerfile antes de construir. NO continúes con `docker build` hasta que pase este check.
|
|
49
|
-
</dockerfile_pre_build_lint>
|
|
50
|
-
|
|
51
41
|
<report>
|
|
52
|
-
Al finalizar, no te comuniques directamente con el usuario. Genera un reporte técnico estructurado para `@sdd-orchestrator` que incluya: las URLs de acceso, estado de los contenedores (`docker compose ps`), y logs iniciales de arranque (
|
|
42
|
+
Al finalizar, no te comuniques directamente con el usuario. Genera un reporte técnico estructurado para `@sdd-orchestrator` que incluya: las URLs de acceso (con sus status codes), estado de los contenedores (`docker compose ps` — solo del proyecto, filtrado por nombre), y logs iniciales de arranque (últimas 10 líneas de `docker compose logs --tail=10`).
|
|
53
43
|
</report>
|
|
@@ -48,24 +48,36 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
|
|
|
48
48
|
<workflow>
|
|
49
49
|
<f0_detect>
|
|
50
50
|
1. **Inicialización Atómica**: Llama obligatoriamente a `sdd_get_initial_session_data` para obtener estado, memorias e Oh-My-Design en un solo turno.
|
|
51
|
-
2. **
|
|
51
|
+
2. **Brain Pre-carga (OBLIGATORIO)**: Inmediatamente después, invoca `brain_read_memory({ category: "learnings" })` y `brain_read_memory({ category: "errors" })` para extraer errores y lecciones históricas que prevengan repetir problemas conocidos. Guarda el resultado en una variable interna para inyectarlo en cada brief de subagente.
|
|
52
|
+
3. **Autopiloto (/loop)**:
|
|
52
53
|
- Si el usuario especificó `/loop N` o `iteraciones=N`, autoselecciona Next.js 16, Console mode, y el primer diseño recomendado.
|
|
53
54
|
- Transiciona atómicamente a F1 con `sdd_set_phase({ phase: "F1_CONTRACT", spec_name: "<nombre-kebab>", loopMode: true, loopTargetIterations: N, loopCurrentIteration: 1 })`.
|
|
54
|
-
|
|
55
|
+
4. **Normal (HIL) — Pregunta Mínima**: Llama **UNA sola vez** a `question` con **una sola decisión clave**:
|
|
56
|
+
- Modo de verificación: `Console` (rápido, sin Playwright) o `Visual` (con Playwright, +30% tiempo).
|
|
57
|
+
- **Defaults razonables automáticos** (NO preguntes): Stack = Next.js 16 + Shadcn UI, Persistencia = localStorage/mock, Diseño = primer recomendado.
|
|
58
|
+
- Si el usuario pide un dashboard/panel/landing explícitamente, **recomienda Console** y usa `dashboard-01` como bloque base sin preguntar nada más.
|
|
59
|
+
- Luego, transiciona con `sdd_set_phase` a `F1_CONTRACT`.
|
|
60
|
+
5. **Fast-Track Dashboard (Auto-detect)**: Si la petición del usuario contiene keywords (`dashboard`, `admin panel`, `panel de control`, `panel admin`, `crm`, `erp`), y NO existe un proyecto en `src/`, **recomienda automáticamente** usar `@shadcn/dashboard-01` como bloque base y crea el spec con `sdd_hints.block_strategy = "npx shadcn@latest add @shadcn/dashboard-01"` pre-rellenado. En este modo, F1 se reduce a validación de tokens de diseño únicamente.
|
|
55
61
|
</f0_detect>
|
|
56
62
|
|
|
57
63
|
<f1_contract>
|
|
58
|
-
1. Delega a `@sdd-spec-writer` indicando solo el `activeContract
|
|
59
|
-
2. Presenta el contrato resumido al usuario (máximo
|
|
64
|
+
1. Delega a `@sdd-spec-writer` indicando solo el `activeContract`, el modo de verificación, y el bloque shadcn pre-seleccionado si es Fast-Track Dashboard.
|
|
65
|
+
2. Presenta el contrato resumido al usuario (máximo 20 líneas, en formato tabla markdown).
|
|
60
66
|
3. Asegura que el spec-writer llene `sdd_hints` con components, icons y bootstrap_template.
|
|
61
67
|
4. **Aprobación**: En autopiloto, transiciona directo. En modo normal, usa `question` para aprobación humana, luego llama a `sdd_set_phase({ phase: "F2_IMPLEMENTATION" })`.
|
|
62
|
-
5. **Preparar Brief**: Lee `contract.json`, extrae la lista de componentes/endpoints, estado de bootstrap previo, y lecciones
|
|
68
|
+
5. **Preparar Brief**: Lee `contract.json`, extrae la lista de componentes/endpoints, estado de bootstrap previo, y las lecciones de Brain pre-cargadas en F0. Genera un brief estructurado e inyéctalo en `.openspec/active-brief.md` usando `sdd_save_active_brief`.
|
|
69
|
+
- **Sección obligatoria para el Coder**: `brain_learnings` (resultado de `brain_read_memory({ category: "design" })`) — inyecta lecciones de diseño previas.
|
|
70
|
+
- **Sección obligatoria para el Tester**: `regressions` (resultado de `brain_read_memory({ category: "errors" })`) — inyecta regresiones históricas.
|
|
63
71
|
- *Asignación Dinámica de Skills (OBLIGATORIO):* Indica en el brief qué habilidades específicas tiene permitido cargar el subagente para ahorrar tokens de carga (ej: si el stack es Next.js, instruye explícitamente habilitar las skills `shadcn` y `shadcn-templates` y prohíbe estrictamente `docker-templates`; si el stack es FastAPI, prohíbe `shadcn` y `shadcn-templates` y autoriza `docker-templates`). Esto previene el desperdicio de tokens de carga de habilidades innecesarias.
|
|
64
72
|
</f1_contract>
|
|
65
73
|
|
|
66
74
|
<f2_implementation>
|
|
67
|
-
1. **Delega a `@sdd-coder`**: Envía un prompt conciso. NO re-envíes el contrato entero ni el package.json.
|
|
68
|
-
-
|
|
75
|
+
1. **Delega a `@sdd-coder`**: Envía un prompt conciso. NO re-envíes el contrato entero ni el package.json. Pasa solo:
|
|
76
|
+
- Spec name + ruta del contract.json
|
|
77
|
+
- Lista resumida de `files_affected`
|
|
78
|
+
- Lista de sprints pre-divididos si `files_affected.length > 5`
|
|
79
|
+
- Sección `brain_learnings` del brief
|
|
80
|
+
- *Instrucción clave:* Indica si requiere bootstrap. Prohíbe formalmente el uso de `brain_read_memory` (ya está inyectado). Si el subagente se había quedado sin pasos y debes re-invocarlo con su `task_id`, el prompt DEBE SER EXTREMADAMENTE BREVE (ej. "Te quedaste sin pasos. Tu contexto y tareas ya están en tu historial. Revisa lo que falta y continúa."). NO incluyas código ni listas largas para no contaminar el contexto.
|
|
69
81
|
2. **Verificación de Servidor**:
|
|
70
82
|
- **Si la categoría es 'script' o 'tooling' (Track Agnóstico):** No hay un dev server web que correr. Salta directamente este paso y transiciona de forma inmediata a F3.
|
|
71
83
|
- **De lo contrario (Web Next/FastAPI):**
|
|
@@ -77,9 +89,10 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
|
|
|
77
89
|
|
|
78
90
|
<f3_verification>
|
|
79
91
|
1. **Shift-Left**: Llama obligatoriamente a `sdd_shift_left_verify`. Si reporta errores de ESLint o TypeScript, haz rollback de estructura al Coder. No continúes si hay errores de compilación críticos.
|
|
80
|
-
2.
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
2. **Pre-inyecta el brief al Tester (BLOQUEANTE)**: Antes de delegar al tester, invoca `brain_read_memory({ category: "errors" })` (idempotente, ya lo hiciste en F0) e inyecta el resultado como sección `regressions` en `.openspec/active-brief.md`. Esto previene que el tester repita errores históricos.
|
|
93
|
+
3. Delega a `@sdd-tester` para ejecutar las pruebas unitarias o de integración de la suite. (Si es un App Script o Bash, el Tester verificará la estructura del archivo y sintaxis básica).
|
|
94
|
+
- *Instrucción clave al delegar:* Pasa solo la ruta del spec, la lista de test files a ejecutar, los mockPatterns pre-armados y la sección `regressions`. NO incluyas las listas de test_scenarios completas. PROHIBIDO usar `glob` o `read` masivos: el brief ya tiene todo pre-inyectado. Al igual que en F2, si el subagente se quedó sin pasos y usas su `task_id`, el prompt DEBE SER EXTREMADAMENTE BREVE.
|
|
95
|
+
4. **Transición**:
|
|
83
96
|
- **Si la categoría es 'script' o 'tooling' (Track Agnóstico):** Omitir la fase F4_DEPLOYMENT por completo (los scripts no requieren Docker en su ciclo estándar). Transiciona directamente a `<completion>`.
|
|
84
97
|
- **De lo contrario (Web):**
|
|
85
98
|
- Autopiloto e iteración intermedia (current < target): Omite la fase F4 para ahorrar tiempo y transiciona directo a `<completion>`.
|
|
@@ -95,17 +108,27 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
|
|
|
95
108
|
|
|
96
109
|
<rollbacks>
|
|
97
110
|
1. Si se reportan fallos, regresa a la fase correspondiente usando `sdd_set_phase` y delega la corrección al subagente experto.
|
|
111
|
+
2. Si una misma fase falla más de 2 veces consecutivas, presenta el error al usuario con el contexto completo en vez de seguir intentando.
|
|
98
112
|
</rollbacks>
|
|
99
113
|
|
|
100
114
|
<completion>
|
|
101
115
|
1. **Flujo de Iteraciones Autónomas (/loop)**:
|
|
102
116
|
- Si `loopCurrentIteration < loopTargetIterations`:
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
117
|
+
- Define **una (1) mejora autónoma de alto impacto** (ej. keyboard shortcuts, toast notifications, empty states).
|
|
118
|
+
- Anúnciala en 1 línea y delega la siguiente iteración completa a un NUEVO hilo `sdd-orchestrator` con contexto de 0 tokens.
|
|
119
|
+
- Si `loopCurrentIteration >= loopTargetIterations` o modo normal: Concluye con `sdd_set_phase({ phase: "F0_DETECT", loopMode: false })`.
|
|
120
|
+
2. **Brain save OBLIGATORIO**: Sintetiza 1-3 lecciones y regístralas con `brain_save_memory` (categoría: `learnings`, `errors`, `design` o `routing`). BLOQUEANTE — no omitir.
|
|
121
|
+
3. **Mensaje Final MÁXIMO 10 LÍNEAS**:
|
|
122
|
+
```
|
|
123
|
+
✅ Sesión SDD completada
|
|
124
|
+
- Spec: <nombre>
|
|
125
|
+
- Stack: <tecnologías>
|
|
126
|
+
- Tests: <X>/<X> passing
|
|
127
|
+
- Deploy: <URL>
|
|
128
|
+
- Lecciones guardadas en Brain: <categoría>
|
|
129
|
+
```
|
|
130
|
+
NO uses tablas largas ni recapitules cada fase. El usuario ya vio el progreso en tiempo real.
|
|
131
|
+
4. Actualiza todos los TODOs de la sesión como completados llamando a `todowrite` en bloque (1 sola vez).
|
|
109
132
|
</completion>
|
|
110
133
|
</workflow>
|
|
111
134
|
|
|
@@ -114,6 +137,20 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
|
|
|
114
137
|
- `next-devtools` deshabilitado por defecto. MCPs invocados exclusivamente por subagentes.
|
|
115
138
|
</mcp_guidelines>
|
|
116
139
|
|
|
140
|
+
<communication_rules>
|
|
141
|
+
- **Mensajes Concisos (OBLIGATORIO)**: Cada mensaje tuyo debe ser ≤ 10 líneas de prosa. Tablas con hasta 5 columnas OK. PROHIBIDO:
|
|
142
|
+
- Resúmenes largos con múltiples tablas decorativas
|
|
143
|
+
- Listas de emojis innecesarios
|
|
144
|
+
- Repetir lo que el subagente ya reportó
|
|
145
|
+
- "Mensaje final" con 30+ líneas recapitulando todo
|
|
146
|
+
- **Patrón de Reporte por Fase**:
|
|
147
|
+
```
|
|
148
|
+
✅ <Fase> completada — <1 línea con el outcome>
|
|
149
|
+
Siguiente: <Fase siguiente> → <acción concreta>
|
|
150
|
+
```
|
|
151
|
+
- **Errores**: Reportar 1 línea con qué falló + el siguiente paso concreto. NO pegar stack traces crudos (los subagentes ya los tienen).
|
|
152
|
+
</communication_rules>
|
|
153
|
+
|
|
117
154
|
<knowledge_base_design_html>
|
|
118
155
|
Marcas con layouts exactos interactivos de Oh My Design (SaaS/DevTools: supabase, linear.app, vercel, raycast, posthog; Fintech: stripe, revolut, wise, toss; Consumer/Productivity: airbnb, apple, nike, shopify, spotify, figma, notion).
|
|
119
156
|
</knowledge_base_design_html>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Diseña y corre pruebas unitarias, integración y visuales (Playwright)
|
|
3
3
|
mode: subagent
|
|
4
4
|
hidden: true
|
|
5
|
-
steps:
|
|
5
|
+
steps: 20
|
|
6
6
|
model: deepseek/deepseek-v4-flash
|
|
7
7
|
temperature: 0.35
|
|
8
8
|
frequency_penalty: 0.5
|
|
@@ -39,12 +39,18 @@ Eres el Validador de Contratos (sdd-tester) del flujo SDD. Tu trabajo es ejecuta
|
|
|
39
39
|
</identity>
|
|
40
40
|
|
|
41
41
|
<constraints>
|
|
42
|
-
- **
|
|
42
|
+
- **Brief Pre-inyectado (BLOQUEANTE)**: Al comenzar, NO explores el código a ciegas. El orquestador inyecta directamente en `.openspec/active-brief.md` (o `.opencode/active-brief.md`) la siguiente información que debes consumir de inmediato sin búsquedas adicionales:
|
|
43
|
+
- Lista de archivos de test a ejecutar (con su ruta absoluta)
|
|
44
|
+
- Lista de archivos de producción a testear (para entender qué mockear, ya leídos por el spec-writer)
|
|
45
|
+
- `mockPatterns` pre-armados (Proxy lucide-react, mock dinámico next-themes, mock next/navigation, polyfills crypto.randomUUID)
|
|
46
|
+
- `regressions` (lecciones del Brain sobre fallos históricos)
|
|
47
|
+
- Resumen de los `test_scenarios` del contrato activo
|
|
48
|
+
- Lee solo el `.openspec/active-brief.md` UNA vez. Si necesitas más contexto de un archivo específico, léelo UNA vez por archivo, NUNCA de forma masiva.
|
|
43
49
|
- **Prohibición de Duplicar Lógica**: Tienes estrictamente prohibido simular o recrear la lógica de negocio dentro de las pruebas para forzar que pasen. Debes importar y verificar los componentes y funciones reales desde `src/`. Moca dependencias externas o corrige configuraciones en `setup.ts` si es necesario.
|
|
44
50
|
- **Ruta de Capturas de Pantalla**: Cualquier screenshot con `playwright_browser_take_screenshot` debe guardarse obligatoriamente con el prefijo `.openspec/ts-` (ej. `.openspec/ts-home-page.png`). Nunca los guardes en la raíz.
|
|
45
51
|
- **Prohibición de Playwright**: Si `verificationMode === "console"` en contract.json, tienes STRICTAMENTE PROHIBIDO usar cualquier tool o navegador de Playwright.
|
|
46
|
-
- **
|
|
47
|
-
- **Memoria de Errores**: Tienes PROHIBIDO llamar a `brain_read_memory`.
|
|
52
|
+
- **Prohibición de Exploración Ciega**: Tienes ESTRICTAMENTE PROHIBIDO usar `glob` o `grep` repetidos para descubrir archivos. Todo lo que necesitas ya está en el brief. Solo `read` los archivos exactos listados en el brief.
|
|
53
|
+
- **Memoria de Errores**: Tienes PROHIBIDO llamar a `brain_read_memory`. El orquestador inyecta las regresiones históricas relevantes directamente en tu sección del brief.
|
|
48
54
|
- **Restricción de Archivos**: Solo se te permite modificar/escribir archivos en la fase 'F3_VERIFICATION', y únicamente archivos que contengan 'test', 'spec', 'tests/' o '.openspec/' en su ruta. Tienes estrictamente prohibido modificar el código fuente de producción.
|
|
49
55
|
- **Extensión JSX en Tests**: Todo archivo de pruebas que contenga JSX, monte componentes o simule iconos (mocks de Lucide con `<svg>`) **DEBE** tener la extensión `.tsx` obligatoriamente (nunca `.ts` ya que esbuild/Vitest fallarán en la compilación).
|
|
50
56
|
- **Selectores de Formularios Robustos**: Evita usar `getByLabelText(/password/i)` si hay botones con aria-label que coincidan de forma ambigua. Prefiere placeholders como `getByPlaceholderText()` o expresiones regulares con anclajes estrictos como `/^Password$/` para aislar el input deseado.
|
|
@@ -56,9 +62,10 @@ Eres el Validador de Contratos (sdd-tester) del flujo SDD. Tu trabajo es ejecuta
|
|
|
56
62
|
|
|
57
63
|
<pre_deploy>
|
|
58
64
|
- **Ámbito**: Validar pruebas unitarias y de integración antes del despliegue.
|
|
59
|
-
- **Auto-generación de Plantillas (OBLIGATORIO)**: Al comenzar, ejecuta obligatoriamente `sdd_generate_tests` para autogenerar las plantillas a partir de los `test_scenarios` del contrato.
|
|
65
|
+
- **Auto-generación de Plantillas (OBLIGATORIO)**: Al comenzar, ejecuta obligatoriamente `sdd_generate_tests` para autogenerar las plantillas a partir de los `test_scenarios` del contrato. Las plantillas ya incluyen imports reales, mocks estándar (Proxy lucide-react, mock reactivo next-themes), y assertions significativas basadas en el `then` de cada escenario. NO las reescribas desde cero.
|
|
66
|
+
- **NO explorar el código**: Tu brief ya contiene los archivos de producción a testear y los patrones de mock pre-armados. NO hagas `read` masivos de componentes, NO hagas `glob` ciegos. Solo lee el archivo de test específico que estés arreglando.
|
|
60
67
|
- **Preparación de Puerto**: Llama proactivamente a `sdd_free_port` para liberar el puerto de pruebas.
|
|
61
|
-
- **Ejecución Incremental/Dirigida**:
|
|
68
|
+
- **Ejecución Incremental/Dirigida**: Ejecuta la suite de pruebas de forma focalizada apuntando únicamente a los archivos de test específicos relacionados con la funcionalidad modificada (ej. `npx vitest run src/__tests__/<test_name>.test.tsx` o `pytest tests/unit/<test_name>.py` en lugar de correr toda la base de pruebas), acelerando exponencialmente los tiempos de espera.
|
|
62
69
|
- **Linter Focalizado**: Ejecuta `npx eslint` apuntando específicamente a los archivos de producción modificados listados en `files_affected` del brief y a tus archivos de test (ej. `npx eslint src/components/blocks/MyBlock.tsx src/__tests__/MyBlock.test.tsx`), optimizando el análisis estático.
|
|
63
70
|
- **Resolución Proactiva de Fallas**: Si los tests fallan debido a problemas menores de mocking, importaciones incorrectas, llaves duplicadas en React o configuraciones de test, **tienes autorización total para editarlos y repararlos tú mismo** dentro de `tests/` o `src/__tests__/`. Solo si el error es un fallo lógico en el código de producción de la aplicación (el cual tienes prohibido modificar), debes reportar un rollback a `F2_IMPLEMENTATION`.
|
|
64
71
|
- **Transición**:
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
},
|
|
181
181
|
"external_api_verification": {
|
|
182
182
|
"type": "object",
|
|
183
|
-
"description": "
|
|
183
|
+
"description": "OPCIONAL. Solo incluir cuando se valida una API no trivial con un MCP externo (ej: API de HeroUI con props compuestos). NO duplicar props de lucide-react, recharts o shadcn — esas ya están documentadas en sus skills. Para un dashboard típico, omitir este campo completo.",
|
|
184
184
|
"properties": {
|
|
185
185
|
"verified_at": {
|
|
186
186
|
"type": "string",
|
|
@@ -224,6 +224,15 @@
|
|
|
224
224
|
"type": "string",
|
|
225
225
|
"enum": ["nextjs-shadcn", "fastapi-sdd", "agnostic-fast"],
|
|
226
226
|
"description": "Plantilla preferida para bootstrap. nextjs-shadcn para web Next, fastapi-sdd para Python, next-fast o agnostic-fast si es un script ligero de un solo archivo."
|
|
227
|
+
},
|
|
228
|
+
"blocks_to_install": {
|
|
229
|
+
"type": "array",
|
|
230
|
+
"items": { "type": "string" },
|
|
231
|
+
"description": "Bloques shadcn pre-hechos para instalar (ej: ['@shadcn/dashboard-01']). El coder los instala con npx shadcn@latest add."
|
|
232
|
+
},
|
|
233
|
+
"fast_track": {
|
|
234
|
+
"type": "boolean",
|
|
235
|
+
"description": "Si true, indica que el spec es fast-track y debe usar bloques pre-hechos en lugar de diseñar desde cero."
|
|
227
236
|
}
|
|
228
237
|
}
|
|
229
238
|
}
|
|
@@ -5,14 +5,16 @@ Estas reglas son mandatorias para todos los agentes de la sesión (Orchestrator,
|
|
|
5
5
|
## 1. Sistema de Memoria Compartida (Brain)
|
|
6
6
|
El proyecto utiliza un sistema de memoria centralizado en `.openspec/brain.md` gestionado por las herramientas `brain_save_memory` y `brain_read_memory`.
|
|
7
7
|
|
|
8
|
-
- **Orquestador (
|
|
9
|
-
-
|
|
10
|
-
-
|
|
8
|
+
- **Orquestador (OBLIGATORIO en cada sesión)**:
|
|
9
|
+
- **F0**: Invocar `brain_read_memory({ category: "learnings" })` y `brain_read_memory({ category: "errors" })` para extraer errores históricos conocidos y prevenir repetirlos.
|
|
10
|
+
- **F2 inicio (antes de delegar al coder)**: Invocar `brain_read_memory({ category: "design" })` para inyectar lecciones de diseño previas en el brief.
|
|
11
|
+
- **F3 inicio (antes de delegar al tester)**: Invocar `brain_read_memory({ category: "errors" })` para inyectar regresiones en el brief.
|
|
12
|
+
- **Finalización (siempre, sin excepción)**: Sintetizar 1-3 lecciones valiosas de la sesión y registrarlas con `brain_save_memory` en la categoría adecuada (`learnings`, `errors`, `design`, `routing`).
|
|
11
13
|
- **Coder (F2)**:
|
|
12
|
-
- Antes de empezar a codificar, debe consultar la categoría `design` y `learnings` en el Brain.
|
|
14
|
+
- Antes de empezar a codificar, debe consultar la categoría `design` y `learnings` en el Brain. El orquestador inyecta estos datos en `.openspec/active-brief.md` antes de delegar.
|
|
13
15
|
- Si soluciona un bug complejo, debe reportarlo registrándolo en la categoría `errors` o `learnings`.
|
|
14
16
|
- **Tester (F3)**:
|
|
15
|
-
- Debe consultar la categoría `errors` para asegurar que los nuevos casos de prueba cubran regresiones históricas.
|
|
17
|
+
- Debe consultar la categoría `errors` para asegurar que los nuevos casos de prueba cubran regresiones históricas. El orquestador inyecta estos datos en `.openspec/active-brief.md` antes de delegar.
|
|
16
18
|
|
|
17
19
|
## 2. Modo Autopiloto (/loop)
|
|
18
20
|
Cuando el estado del sistema tiene `loopMode === true` (activado mediante el comando `/loop` o llamando a `sdd_set_phase`):
|
|
@@ -80,3 +82,4 @@ Para evitar que los subagentes se queden sin pasos (step/token exhaustion) o acu
|
|
|
80
82
|
- **Regla de Micro-Specs:** Si una funcionalidad requiere implementar múltiples vistas complejas o formularios densos, el Spec-Writer **debe** dividirla obligatoriamente en specs incrementales secuenciales de máximo 3 componentes principales por contrato. No intentes implementar todo un panel de administración en una sola iteración de contrato.
|
|
81
83
|
- **Compilación Atómica:** El Coder **debe** implementar y compilar exitosamente cada archivo de forma individual antes de escribir el siguiente. Está estrictamente prohibido escribir todos los componentes de golpe sin ejecutar validaciones intermedias de TypeScript (`tsc --noEmit`).
|
|
82
84
|
- **Verificación Temprana de Dependencias:** El Coder **debe** comprobar en el primer paso de F2 que dependencias de pruebas críticas como `@testing-library/dom` estén correctamente instaladas en proyectos React 19 para evitar fallos intermitentes de tipado en Vitest.
|
|
85
|
+
- **PROHIBICIÓN ESTRICTA DE `todowrite` en Coder/Tester/Deployer/Spec-Writer:** Estas herramientas **no deben invocar** `todowrite` en ningún momento de su sesión. El seguimiento de fases está centralizado en el Orquestador. Inyectar TODOs en subagentes satura su contexto y agota pasos de LLM innecesariamente. El Orquestador solo debe usar `todowrite` **2 veces máximo por sesión**: una al inicio (lista de fases) y otra al final (marcar todas completadas).
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sdd-foundation-overview
|
|
3
|
+
description: Resumen ejecutivo del flujo SDD para el orquestador. Cargado SOLO por el orquestador principal (~50 líneas). Para detalles profundos, los subagentes cargan `sdd-methodology` directamente.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: opencode
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Qué hace esta habilidad
|
|
9
|
+
|
|
10
|
+
Resumen ultra-compacto del ciclo SDD. El orquestador NO debe cargar `sdd-methodology` (600 líneas). Solo esta skill + delegar.
|
|
11
|
+
|
|
12
|
+
## El ciclo en 60 segundos
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
F0 → detectar stack + auto-bootstrap (si dashboard/admin/panel) + diseñar 1 pregunta
|
|
16
|
+
F1 → spec-writer redacta contract.json desde plantilla (sdd-quickstart)
|
|
17
|
+
F2 → coder implementa con block-first (@shadcn/dashboard-01 si es dashboard)
|
|
18
|
+
F3 → tester corre vitest/pytest (tests YA tienen assertions reales)
|
|
19
|
+
F4 → deployer genera Dockerfile + build + verify
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Defaults razonables (NO preguntar)
|
|
23
|
+
|
|
24
|
+
- **Stack**: Next.js 16 + Shadcn UI + Tailwind v4 (frontend), FastAPI + Pydantic (backend)
|
|
25
|
+
- **Verificación**: `console` (5x más rápido que `visual`)
|
|
26
|
+
- **Persistencia**: localStorage (mock frontend), SQLite (backend simple)
|
|
27
|
+
- **Diseño**: primer recomendado por Oh My Design para la categoría del usuario
|
|
28
|
+
|
|
29
|
+
## Atajos críticos
|
|
30
|
+
|
|
31
|
+
| Atajo | Ahorro |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `sdd_list_design_recommendations({ use_case: "all" })` en F0 | 4 calls → 1 |
|
|
34
|
+
| `sdd_set_phase({ phase: "F1_CONTRACT", spec_name: "..." })` atómico | Crea carpeta sin race |
|
|
35
|
+
| `sdd_select_design({ brandId: "..." })` | Copia DESIGN.md |
|
|
36
|
+
| `brain_read_memory({ category: ... })` en F0 y F3 inicio | Previene errores históricos |
|
|
37
|
+
| Skill `sdd-quickstart` en F1 | Plantilla pre-rellenada (~1 min vs 3+ min) |
|
|
38
|
+
| `sdd_apply_brand_tokens({ tokens: ... })` en F2 | Inyecta tokens sin romper shadcn |
|
|
39
|
+
| `sdd_generate_dockerfile({ stack: "nextjs", port: 3000 })` en F4 | Genera 3 archivos en 1 call |
|
|
40
|
+
|
|
41
|
+
## Reglas duras
|
|
42
|
+
|
|
43
|
+
1. **Coordinación pura**: el orquestador NO escribe/edita código. Solo delega vía `task`.
|
|
44
|
+
2. **Una pregunta por sesión**: solo Console vs Visual en F0. Todo lo demás por default.
|
|
45
|
+
3. **Fast-Track Dashboard**: si el usuario pide dashboard/admin/panel → usar `@shadcn/dashboard-01` directamente sin preguntar más.
|
|
46
|
+
4. **Brain obligatorio**: save 1-3 lecciones al cerrar cada sesión, read learnings+errors en F0.
|
|
47
|
+
5. **Tests con assertions reales**: nunca stubs con `expect(true).toBe(true)`.
|
|
48
|
+
|
|
49
|
+
## Métricas objetivo de una sesión
|
|
50
|
+
|
|
51
|
+
- F0: 1 ronda de `question` con 1 sola decisión
|
|
52
|
+
- F1: contract.json 80-120 líneas, 3-5 test_scenarios
|
|
53
|
+
- F2: self-audit limpio en 1 intento
|
|
54
|
+
- F3: lint + tests verdes en 1 intento
|
|
55
|
+
- F4: build OK al 1er intento, healthcheck `healthy` en <60s
|
|
56
|
+
|
|
57
|
+
## Anti-patrones (PROHIBIDO)
|
|
58
|
+
|
|
59
|
+
- Cargar `sdd-methodology` (600 líneas) en el orquestador
|
|
60
|
+
- Preguntar stack/diseño/persistencia si el usuario dijo "dashboard"
|
|
61
|
+
- Usar `todowrite` más de 2 veces por sesión
|
|
62
|
+
- Ejecutar `glob`/`grep` ciegos en el orquestador
|
|
63
|
+
- Pedirle al orquestador que compile/testee código
|
|
64
|
+
|
|
65
|
+
## Inyectar en briefs de subagentes
|
|
66
|
+
|
|
67
|
+
Cada vez que delegues a un subagente, pasa en el brief:
|
|
68
|
+
- `brain_learnings`: resultado de `brain_read_memory({ category: "design" })` para coder
|
|
69
|
+
- `regressions`: resultado de `brain_read_memory({ category: "errors" })` para tester
|
|
70
|
+
- `mockPatterns`: pre-armados para tester (Proxy lucide-react, mock next-themes)
|
|
71
|
+
- `files_affected`: lista exacta, NO paths inferidos
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sdd-methodology
|
|
3
|
-
description: Guía
|
|
3
|
+
description: Guía DETALLADA del flujo SDD con trampas conocidas. SOLO para subagentes (spec-writer, coder, tester, deployer). El orquestador debe cargar `sdd-foundation-overview` en su lugar.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: opencode
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## A quién está dirigida esta habilidad
|
|
9
|
+
|
|
10
|
+
Esta skill es la versión **detallada y profunda** del flujo SDD. Está optimizada para subagentes que necesitan el contexto completo de las 5 fases, las trampas conocidas y los atajos avanzados.
|
|
11
|
+
|
|
12
|
+
**El orquestador NO debe cargar esta skill.** Carga `sdd-foundation-overview` (~50 líneas) en su lugar.
|
|
13
|
+
|
|
8
14
|
## Qué hace esta habilidad
|
|
9
15
|
|
|
10
16
|
Esta habilidad instruye a los agentes del arnés SDD a través del ciclo de 5 fases (F0-F4) **usando los atajos del harness optimizado**. NO es solo una guía genérica — incluye las **trampas conocidas** que han costado tiempo en sesiones reales y los atajos que las evitan.
|
|
@@ -150,6 +150,59 @@ Copia y adapta los IDs y descripciones que apliquen. Borra los que no apliquen.
|
|
|
150
150
|
|
|
151
151
|
---
|
|
152
152
|
|
|
153
|
+
## 2.5. Plantilla MÍNIMA viable (Fast-Track Dashboard)
|
|
154
|
+
|
|
155
|
+
Para un dashboard o panel estándar, usa esta plantilla **reducida** de **~80 líneas** en lugar de la completa. Omite `external_api_verification`, `database` y detalles innecesarios.
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"$schema": "../../../.opencode/contract-schema.json",
|
|
160
|
+
"contractName": "{{NOMBRE_PASCAL}}",
|
|
161
|
+
"description": "{{UNA_LINEA}}",
|
|
162
|
+
"category": "frontend",
|
|
163
|
+
"settings": {
|
|
164
|
+
"verificationMode": "console",
|
|
165
|
+
"language": "TypeScript",
|
|
166
|
+
"testFramework": "Vitest"
|
|
167
|
+
},
|
|
168
|
+
"stack": {
|
|
169
|
+
"core": ["Next.js 16", "Shadcn UI", "Tailwind CSS v4"],
|
|
170
|
+
"databases": []
|
|
171
|
+
},
|
|
172
|
+
"files_affected": [
|
|
173
|
+
"src/app/page.tsx",
|
|
174
|
+
"src/app/layout.tsx",
|
|
175
|
+
"{{ARCHIVOS_RELEVANTES}}"
|
|
176
|
+
],
|
|
177
|
+
"frontend": {
|
|
178
|
+
"components": [
|
|
179
|
+
{ "name": "{{COMP_1}}", "path": "@/components/blocks/{{COMP_1_KEBAB}}" },
|
|
180
|
+
{ "name": "{{COMP_2}}", "path": "@/components/blocks/{{COMP_2_KEBAB}}" }
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
"test_scenarios": [
|
|
184
|
+
{
|
|
185
|
+
"id": "TS-01",
|
|
186
|
+
"name": "{{NOMBRE_TEST}}",
|
|
187
|
+
"type": "unit",
|
|
188
|
+
"feature_ref": "{{COMP_1}}",
|
|
189
|
+
"given": "{{GIVEN}}",
|
|
190
|
+
"when": "{{WHEN}}",
|
|
191
|
+
"then": "{{THEN_OBSERVABLE}}"
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
"sdd_hints": {
|
|
195
|
+
"bootstrap_template": "nextjs-shadcn",
|
|
196
|
+
"fast_track": true,
|
|
197
|
+
"blocks_to_install": ["@shadcn/dashboard-01"]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Tiempo objetivo F1 con Fast-Track**: 30-45 segundos.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
153
206
|
## 3. Anti-patrones a evitar en el contrato
|
|
154
207
|
|
|
155
208
|
- **Inconsistencia de nombres de componentes (CRÍTICO)**: Los nombres declarados en `frontend.components[].name` del contrato son **un compromiso inmutable**. El spec-writer DEBE usar exactamente estos mismos nombres al crear las pruebas unitarias/integración, y el coder DEBE usarlos para crear los componentes de producción. **PROHIBIDO renombrar o cambiar de PascalCase a kebab-case en archivos de test**. Si el contrato dice `CalculatorPanel`, el archivo de test se llamará `CalculatorPanel.test.tsx` y no se debe cambiar a `CalculatorPage.test.tsx` (esto causó fallas de compilación con imports rotos en la sesión 1374).
|
|
@@ -132,11 +132,14 @@ const DEFAULT_PACKAGE_JSON = {
|
|
|
132
132
|
"postcss": "^8.4.49",
|
|
133
133
|
"eslint": "^9.16.0",
|
|
134
134
|
"eslint-config-next": "^15.1.0",
|
|
135
|
+
"typescript-eslint": "^8.18.0",
|
|
135
136
|
"vitest": "^2.1.8",
|
|
136
137
|
"@vitejs/plugin-react": "^4.3.4",
|
|
137
|
-
"
|
|
138
|
+
"happy-dom": "^15.11.7",
|
|
138
139
|
"@testing-library/react": "^16.1.0",
|
|
139
|
-
"@testing-library/dom": "^10.4.0"
|
|
140
|
+
"@testing-library/dom": "^10.4.0",
|
|
141
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
142
|
+
"@testing-library/user-event": "^14.5.2"
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
|
|
@@ -409,6 +409,28 @@ export const set_phase = tool({
|
|
|
409
409
|
} catch (e) {
|
|
410
410
|
// ignore
|
|
411
411
|
}
|
|
412
|
+
|
|
413
|
+
// Clean up stale screenshots from previous sessions (.openspec/ts-*.png)
|
|
414
|
+
try {
|
|
415
|
+
const openspecDir = path.resolve(root, ".openspec")
|
|
416
|
+
if (fs.existsSync(openspecDir)) {
|
|
417
|
+
const entries = fs.readdirSync(openspecDir)
|
|
418
|
+
let cleanedCount = 0
|
|
419
|
+
for (const entry of entries) {
|
|
420
|
+
if (/^ts-.*\.(png|jpe?g|webp)$/i.test(entry)) {
|
|
421
|
+
try {
|
|
422
|
+
fs.unlinkSync(path.join(openspecDir, entry))
|
|
423
|
+
cleanedCount++
|
|
424
|
+
} catch (e) { /* ignore individual file errors */ }
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (cleanedCount > 0) {
|
|
428
|
+
;(currentState as any).cleanedScreenshots = cleanedCount
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
} catch (e) {
|
|
432
|
+
// ignore
|
|
433
|
+
}
|
|
412
434
|
}
|
|
413
435
|
|
|
414
436
|
writeState(filePath, currentState)
|
|
@@ -448,7 +470,30 @@ export const get_initial_session_data = tool({
|
|
|
448
470
|
const root = getRoot(context)
|
|
449
471
|
const statePath = getStateFilePath(context)
|
|
450
472
|
const currentState = readState(statePath)
|
|
451
|
-
|
|
473
|
+
|
|
474
|
+
// Limpieza silenciosa de carpetas/screenshots transitorios al iniciar F0
|
|
475
|
+
let cleanedCount = 0
|
|
476
|
+
try {
|
|
477
|
+
const playwrightTmpDir = path.resolve(root, ".openspec/.playwright")
|
|
478
|
+
if (fs.existsSync(playwrightTmpDir)) {
|
|
479
|
+
fs.rmSync(playwrightTmpDir, { recursive: true, force: true })
|
|
480
|
+
cleanedCount++
|
|
481
|
+
}
|
|
482
|
+
const openspecDir = path.resolve(root, ".openspec")
|
|
483
|
+
if (fs.existsSync(openspecDir)) {
|
|
484
|
+
for (const entry of fs.readdirSync(openspecDir)) {
|
|
485
|
+
if (/^ts-.*\.(png|jpe?g|webp)$/i.test(entry)) {
|
|
486
|
+
try {
|
|
487
|
+
fs.unlinkSync(path.join(openspecDir, entry))
|
|
488
|
+
cleanedCount++
|
|
489
|
+
} catch (e) { /* ignore */ }
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} catch (e) {
|
|
494
|
+
// ignore
|
|
495
|
+
}
|
|
496
|
+
|
|
452
497
|
// Read brain memory
|
|
453
498
|
let brainMemory: any = { found: false }
|
|
454
499
|
const brainFilePath = path.resolve(root, ".openspec/brain.md")
|
|
@@ -467,7 +512,7 @@ export const get_initial_session_data = tool({
|
|
|
467
512
|
sections[currentHeader].push(line)
|
|
468
513
|
}
|
|
469
514
|
}
|
|
470
|
-
|
|
515
|
+
|
|
471
516
|
let prunedContent = ""
|
|
472
517
|
for (const [header, bodyLines] of Object.entries(sections)) {
|
|
473
518
|
const limit = 10
|
|
@@ -486,13 +531,18 @@ export const get_initial_session_data = tool({
|
|
|
486
531
|
// Get design recommendations
|
|
487
532
|
const recommendations = RECOMMENDED_BRANDS
|
|
488
533
|
|
|
489
|
-
|
|
534
|
+
const response: any = {
|
|
490
535
|
status: "SUCCESS",
|
|
491
536
|
state: currentState,
|
|
492
537
|
brainMemory,
|
|
493
538
|
designRecommendations: recommendations,
|
|
494
539
|
note: "Inicialización completada. No es necesario que llames a sdd_get_state, brain_read_memory o sdd_list_design_recommendations por separado."
|
|
495
|
-
}
|
|
540
|
+
}
|
|
541
|
+
if (cleanedCount > 0) {
|
|
542
|
+
response.cleanedArtifacts = cleanedCount
|
|
543
|
+
response.note += ` Se limpiaron ${cleanedCount} artefactos transitorios (.playwright, screenshots).`
|
|
544
|
+
}
|
|
545
|
+
return JSON.stringify(response, null, 2)
|
|
496
546
|
}
|
|
497
547
|
})
|
|
498
548
|
|
|
@@ -511,6 +561,7 @@ export const save_active_brief = tool({
|
|
|
511
561
|
|
|
512
562
|
// Attempt to read the active contract path from sdd_state.json and extract files_affected
|
|
513
563
|
let filesAffected: string[] = []
|
|
564
|
+
let contract: any = null
|
|
514
565
|
try {
|
|
515
566
|
const statePath = path.resolve(openspecDir, "sdd_state.json")
|
|
516
567
|
if (fs.existsSync(statePath)) {
|
|
@@ -518,7 +569,7 @@ export const save_active_brief = tool({
|
|
|
518
569
|
if (state.activeContract) {
|
|
519
570
|
const contractPath = path.resolve(root, state.activeContract)
|
|
520
571
|
if (fs.existsSync(contractPath)) {
|
|
521
|
-
|
|
572
|
+
contract = JSON.parse(fs.readFileSync(contractPath, "utf8"))
|
|
522
573
|
if (contract && Array.isArray(contract.files_affected)) {
|
|
523
574
|
filesAffected = contract.files_affected
|
|
524
575
|
}
|
|
@@ -536,14 +587,90 @@ export const save_active_brief = tool({
|
|
|
536
587
|
enrichedBrief += `\n\n## [CRITICAL] POLÍTICA DE ZERO-SEARCH Y PRUEBAS INCREMENTALES\n`
|
|
537
588
|
enrichedBrief += `La lista de archivos exactos creados o modificados en este contrato es:\n`
|
|
538
589
|
enrichedBrief += filesAffected.map(f => `- \`${f}\``).join("\n") + "\n\n"
|
|
539
|
-
|
|
590
|
+
|
|
540
591
|
enrichedBrief += `### CODER_CONTEXT (F2)\n`
|
|
541
592
|
enrichedBrief += `- Debes editar, crear y leer EXCLUSIVAMENTE estos archivos de producción: ${filesAffected.map(f => `\`${f}\``).join(", ")}.\n`
|
|
542
|
-
enrichedBrief += `- PROHIBIDO utilizar glob o grep de manera exploratoria ciega.\n
|
|
543
|
-
|
|
593
|
+
enrichedBrief += `- PROHIBIDO utilizar glob o grep de manera exploratoria ciega.\n`
|
|
594
|
+
enrichedBrief += `- **Batch extremo**: escribe/edita TODOS los archivos en una sola respuesta antes de validar.\n\n`
|
|
595
|
+
|
|
544
596
|
enrichedBrief += `### TESTER_CONTEXT (F3)\n`
|
|
545
|
-
enrichedBrief += `-
|
|
597
|
+
enrichedBrief += `- Archivos de test asociados: src/__tests__/<feature_ref>.test.tsx\n`
|
|
546
598
|
enrichedBrief += `- Ejecuta el linter de forma dirigida: \`npx eslint ${filesAffected.join(" ")}\`.\n`
|
|
599
|
+
enrichedBrief += `- **NO explores** el código: todo lo que necesitas ya está inyectado en este brief.\n`
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Inyectar automáticamente Brain learnings + errors (regresiones)
|
|
603
|
+
try {
|
|
604
|
+
const brainPath = path.resolve(openspecDir, "brain.md")
|
|
605
|
+
if (fs.existsSync(brainPath)) {
|
|
606
|
+
const brainContent = fs.readFileSync(brainPath, "utf8") || ""
|
|
607
|
+
const sections: Record<string, string[]> = {}
|
|
608
|
+
let currentHeader = "general"
|
|
609
|
+
for (const line of brainContent.split(/\r?\n/)) {
|
|
610
|
+
if (line.startsWith("# ")) {
|
|
611
|
+
currentHeader = line.substring(2).trim().toLowerCase()
|
|
612
|
+
sections[currentHeader] = []
|
|
613
|
+
} else if (line.trim().length > 0) {
|
|
614
|
+
if (!sections[currentHeader]) sections[currentHeader] = []
|
|
615
|
+
sections[currentHeader].push(line)
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const last = (cat: string, n: number) =>
|
|
619
|
+
(sections[cat] || []).slice(-n).join("\n")
|
|
620
|
+
|
|
621
|
+
const learnings = last("learnings", 5)
|
|
622
|
+
const errors = last("errors", 5)
|
|
623
|
+
const design = last("design", 3)
|
|
624
|
+
|
|
625
|
+
if (learnings || errors || design) {
|
|
626
|
+
enrichedBrief += `\n\n## [AUTO-INJECTED] Brain Memory (lecciones de sesiones previas)\n`
|
|
627
|
+
if (design) {
|
|
628
|
+
enrichedBrief += `\n### Design (aplicar en estilo):\n${design}\n`
|
|
629
|
+
}
|
|
630
|
+
if (learnings) {
|
|
631
|
+
enrichedBrief += `\n### Learnings (patrones validados):\n${learnings}\n`
|
|
632
|
+
}
|
|
633
|
+
if (errors) {
|
|
634
|
+
enrichedBrief += `\n### Errors / Regresiones (evitar repetir):\n${errors}\n`
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
} catch (e) {
|
|
639
|
+
// ignore
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Inyectar mockPatterns estándar si el stack es Next.js con React
|
|
643
|
+
try {
|
|
644
|
+
const isNext = contract?.stack?.core?.some((c: string) => /next\.?js/i.test(c))
|
|
645
|
+
if (isNext) {
|
|
646
|
+
enrichedBrief += `\n\n## [AUTO-INJECTED] Mock Patterns (Vitest + Testing Library)\n`
|
|
647
|
+
enrichedBrief += `\`\`\`typescript\n`
|
|
648
|
+
enrichedBrief += `// Mock dinámico de lucide-react (cualquier icono)\n`
|
|
649
|
+
enrichedBrief += `vi.mock("lucide-react", () => new Proxy({}, {\n`
|
|
650
|
+
enrichedBrief += ` get: (_t, prop) => {\n`
|
|
651
|
+
enrichedBrief += ` const Icon = (props: any) => null;\n`
|
|
652
|
+
enrichedBrief += ` Icon.displayName = String(prop);\n`
|
|
653
|
+
enrichedBrief += ` return Icon;\n`
|
|
654
|
+
enrichedBrief += ` },\n`
|
|
655
|
+
enrichedBrief += `}));\n\n`
|
|
656
|
+
enrichedBrief += `// Mock reactivo de next-themes para ThemeToggle\n`
|
|
657
|
+
enrichedBrief += `let theme = "light";\n`
|
|
658
|
+
enrichedBrief += `vi.mock("next-themes", () => ({\n`
|
|
659
|
+
enrichedBrief += ` useTheme: () => ({\n`
|
|
660
|
+
enrichedBrief += ` get theme() { return theme; },\n`
|
|
661
|
+
enrichedBrief += ` setTheme: (t: string) => { theme = t; },\n`
|
|
662
|
+
enrichedBrief += ` get resolvedTheme() { return theme; },\n`
|
|
663
|
+
enrichedBrief += ` }),\n`
|
|
664
|
+
enrichedBrief += `}));\n\n`
|
|
665
|
+
enrichedBrief += `// Mock de next/navigation\n`
|
|
666
|
+
enrichedBrief += `vi.mock("next/navigation", () => ({\n`
|
|
667
|
+
enrichedBrief += ` useRouter: () => ({ push: vi.fn(), replace: vi.fn(), prefetch: vi.fn() }),\n`
|
|
668
|
+
enrichedBrief += ` usePathname: () => "/",\n`
|
|
669
|
+
enrichedBrief += `}));\n`
|
|
670
|
+
enrichedBrief += `\`\`\`\n`
|
|
671
|
+
}
|
|
672
|
+
} catch (e) {
|
|
673
|
+
// ignore
|
|
547
674
|
}
|
|
548
675
|
|
|
549
676
|
const briefPath = path.resolve(openspecDir, "active-brief.md")
|
|
@@ -551,7 +678,12 @@ export const save_active_brief = tool({
|
|
|
551
678
|
return JSON.stringify({
|
|
552
679
|
status: "SUCCESS",
|
|
553
680
|
message: `Brief de contexto de sistema guardado exitosamente en .openspec/active-brief.md`,
|
|
554
|
-
filePath: briefPath
|
|
681
|
+
filePath: briefPath,
|
|
682
|
+
injectedSections: {
|
|
683
|
+
brainMemory: enrichedBrief.includes("Brain Memory"),
|
|
684
|
+
mockPatterns: enrichedBrief.includes("Mock Patterns"),
|
|
685
|
+
filesAffected: filesAffected.length
|
|
686
|
+
}
|
|
555
687
|
}, null, 2)
|
|
556
688
|
}
|
|
557
689
|
})
|
|
@@ -402,26 +402,78 @@ export const generate_tests = tool({
|
|
|
402
402
|
lines.push("")
|
|
403
403
|
}
|
|
404
404
|
} else {
|
|
405
|
-
|
|
405
|
+
const componentImport = `@/components/blocks/${feature}`
|
|
406
|
+
lines.push('import { describe, it, expect, vi, beforeEach } from "vitest";')
|
|
407
|
+
|
|
406
408
|
if (hasReact) {
|
|
407
|
-
lines.push('import { render, screen } from "@testing-library/react";')
|
|
409
|
+
lines.push('import { render, screen, cleanup } from "@testing-library/react";')
|
|
408
410
|
lines.push('import userEvent from "@testing-library/user-event";')
|
|
409
|
-
lines.push(
|
|
411
|
+
lines.push('')
|
|
412
|
+
lines.push('// Mock lucide-react con Proxy (cualquier icono se mockea automáticamente)')
|
|
413
|
+
lines.push('vi.mock("lucide-react", () => new Proxy({}, {')
|
|
414
|
+
lines.push(' get: (_t, prop) => {')
|
|
415
|
+
lines.push(' const Icon = (props: any) => null;')
|
|
416
|
+
lines.push(' Icon.displayName = String(prop);')
|
|
417
|
+
lines.push(' return Icon;')
|
|
418
|
+
lines.push(' },')
|
|
419
|
+
lines.push('}));')
|
|
420
|
+
lines.push('')
|
|
421
|
+
lines.push(`import { ${feature} } from "${componentImport}";`)
|
|
410
422
|
}
|
|
411
423
|
lines.push("")
|
|
412
424
|
lines.push(`describe("${feature} Tests (Contract Scenarios)", () => {`)
|
|
425
|
+
if (hasReact) {
|
|
426
|
+
lines.push(' afterEach(() => cleanup());')
|
|
427
|
+
lines.push('')
|
|
428
|
+
}
|
|
413
429
|
|
|
414
430
|
for (const s of scenarios) {
|
|
415
431
|
const tid = s.id || "TS-XX"
|
|
416
432
|
const name = s.name || "Test case"
|
|
433
|
+
const isIntegration = s.type === "integration"
|
|
434
|
+
const thenText = (s.then || "").toLowerCase()
|
|
435
|
+
|
|
417
436
|
lines.push(` // ${tid}: ${name}`)
|
|
418
437
|
lines.push(` // Given: ${s.given || ""}`)
|
|
419
438
|
lines.push(` // When: ${s.when || ""}`)
|
|
420
439
|
lines.push(` // Then: ${s.then || ""}`)
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
440
|
+
|
|
441
|
+
if (isIntegration && thenText.includes("dark")) {
|
|
442
|
+
lines.push(` it("${tid}: ${name}", async () => {`)
|
|
443
|
+
lines.push(` // Mock reactivo de next-themes para toggle de tema`)
|
|
444
|
+
lines.push(` let theme = "light";`)
|
|
445
|
+
lines.push(` vi.mock("next-themes", () => ({`)
|
|
446
|
+
lines.push(` useTheme: () => ({ get theme() { return theme; }, setTheme: (t: string) => { theme = t; }, get resolvedTheme() { return theme; } }),`)
|
|
447
|
+
lines.push(` }));`)
|
|
448
|
+
lines.push(` document.documentElement.classList.add("light");`)
|
|
449
|
+
lines.push(` render(<${feature} />);`)
|
|
450
|
+
lines.push(` const button = screen.getByRole("button");`)
|
|
451
|
+
lines.push(` await userEvent.click(button);`)
|
|
452
|
+
lines.push(` expect(document.documentElement.classList.contains("dark")).toBe(true);`)
|
|
453
|
+
lines.push(` });`)
|
|
454
|
+
} else if (thenText.includes("kpi") || thenText.includes("tarjeta") || thenText.includes("valor")) {
|
|
455
|
+
lines.push(` it("${tid}: ${name}", () => {`)
|
|
456
|
+
lines.push(` render(<${feature} />);`)
|
|
457
|
+
lines.push(` expect(screen.getByText(/revenue/i)).toBeInTheDocument();`)
|
|
458
|
+
lines.push(` expect(screen.getByText(/\\$/)).toBeInTheDocument();`)
|
|
459
|
+
lines.push(` });`)
|
|
460
|
+
} else if (thenText.includes("sidebar") || thenText.includes("navega")) {
|
|
461
|
+
lines.push(` it("${tid}: ${name}", () => {`)
|
|
462
|
+
lines.push(` render(<${feature} />);`)
|
|
463
|
+
lines.push(` expect(screen.getByText(/dashboard/i)).toBeInTheDocument();`)
|
|
464
|
+
lines.push(` expect(screen.getByText(/analytics/i)).toBeInTheDocument();`)
|
|
465
|
+
lines.push(` });`)
|
|
466
|
+
} else if (thenText.includes("svg") || thenText.includes("chart") || thenText.includes("recharts") || thenText.includes("gráfico") || thenText.includes("grafico")) {
|
|
467
|
+
lines.push(` it("${tid}: ${name}", () => {`)
|
|
468
|
+
lines.push(` const { container } = render(<${feature} />);`)
|
|
469
|
+
lines.push(` expect(container.querySelector("svg")).toBeTruthy();`)
|
|
470
|
+
lines.push(` });`)
|
|
471
|
+
} else {
|
|
472
|
+
lines.push(` it("${tid}: ${name}", () => {`)
|
|
473
|
+
lines.push(` render(<${feature} />);`)
|
|
474
|
+
lines.push(` expect(screen.getByText(/${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').slice(0, 30)}/i)).toBeTruthy();`)
|
|
475
|
+
lines.push(` });`)
|
|
476
|
+
}
|
|
425
477
|
lines.push("")
|
|
426
478
|
}
|
|
427
479
|
lines.push("});")
|
package/opencode.json
CHANGED