zugzbot 1.1.1 → 1.1.3

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.
@@ -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: 25
5
+ steps: 50
6
6
  model: deepseek/deepseek-v4-flash
7
7
  temperature: 0.35
8
8
  frequency_penalty: 0.5
@@ -99,6 +99,7 @@ Eres el Programador de Código (sdd-coder) del arnés SDD. Tu trabajo es codific
99
99
  - Usa siempre `router.replace("/")` en lugar de `push` para redirecciones de login.
100
100
  - Para evitar efectos concurrentes o solapados en Strict Mode, usa un `useRef(false)` en redirecciones asíncronas para dispararlas solo una vez.
101
101
  - En layouts protegidos por SSR, para evitar que parpadeen spinners o modales antes de hidratar la sesión en cliente, declara un estado `mounted` (`useEffect` asignando `setMounted(true)`) y retorna `null` si `!mounted`.
102
+ - **Shadcn Sidebar + Tailwind v4 Fix (Bug Crítico)**: Si instalas el componente `sidebar` o bloques como `dashboard-01` que lo incluyen, debes parchear OBLIGATORIAMENTE el archivo `src/components/ui/sidebar.tsx` transformando la sintaxis arbitraria a explícita: `w-[--sidebar-width]` → `w-[var(--sidebar-width)]`, `w-[--sidebar-width-icon]` → `w-[var(--sidebar-width-icon)]` y `max-w-[--skeleton-width]` → `max-w-[var(--skeleton-width)]`. De lo contrario, el layout colapsará.
102
103
  - **Iconos**: Importa desde `lucide-react`. Usa `data-icon="inline-start"` para espaciados automáticos.
103
104
  </coding_cheatsheet>
104
105
 
@@ -27,6 +27,12 @@ Eres el Desplegador de Software (sdd-deployer) del flujo SDD. Tu único trabajo
27
27
  2. `docker compose up -d --build --force-recreate` — construye e inicia
28
28
  3. `curl http://localhost:3000` (y opcionalmente `/dashboard`) — verifica
29
29
  - **NO cargar skill docker-templates** si el tool está disponible (ahorra 300+ tokens).
30
+ - **Timeout duro Docker daemon (CRÍTICO — bugfix sesión 1186)**: Tienes un límite MÁXIMO de 90 segundos para esperar a que Docker daemon responda. Después de eso, ABORTA inmediatamente con el reporte `❌ Docker daemon no disponible después de 90s` y NO reintentes más allá de ese límite. Pasos:
31
+ 1. `docker info > /dev/null 2>&1 && echo OK || echo FAIL` (1 intento)
32
+ 2. Si FAIL: `open -a Docker` en macOS, o mostrar instrucciones para Linux
33
+ 3. Loop de espera con `sleep 5 && docker info > /dev/null` hasta 90s acumulados
34
+ 4. Si después de 90s sigue FAIL: abortar y dar instrucciones al usuario
35
+ - Esto previene que el deployer quede atrapado esperando Docker daemon indefinidamente (como ocurrió en sesión 118f).
30
36
  </constraints>
31
37
 
32
38
  <deployment>
@@ -49,15 +49,18 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
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
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
+ 3. **Detección Fast-Track Dashboard (CRÍTICO — bugfix sesión 1186)**: Analiza la petición del usuario. Si contiene ALGUNA de estas keywords: `dashboard`, `admin panel`, `panel de control`, `panel admin`, `crm`, `erp`, `panel`:
53
+ - **NO preguntes NADA**. Asume todos los defaults: Stack = Next.js 16 + Shadcn UI + Tailwind v4, Persistencia = localStorage/mock, Diseño = primer recomendado (Vercel/Linear/Supabase), Verificación = `console`.
54
+ - Llama directamente a `sdd_set_phase({ phase: "F1_CONTRACT", spec_name: "dashboard-admin" })` con `coreStack: ["Next.js 16", "Shadcn UI", "Tailwind CSS v4"]` y `skip_lint_gate: true`.
55
+ - El spec-writer creará automáticamente el spec con `sdd_hints.fast_track: true` y `blocks_to_install: ["@shadcn/dashboard-01"]`.
56
+ 4. **Autopiloto (/loop)**:
53
57
  - Si el usuario especificó `/loop N` o `iteraciones=N`, autoselecciona Next.js 16, Console mode, y el primer diseño recomendado.
54
58
  - Transiciona atómicamente a F1 con `sdd_set_phase({ phase: "F1_CONTRACT", spec_name: "<nombre-kebab>", loopMode: true, loopTargetIterations: N, loopCurrentIteration: 1 })`.
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).
59
+ 5. **Normal (HIL) — Pregunta Mínima SINGLE-QUESTION**: Si NO se detectó Fast-Track Dashboard, llama **UNA sola vez** a `question` con **una sola pregunta** (NO múltiples):
60
+ - **PROHIBIDO** hacer arrays de múltiples preguntas en `questions: [...]` causa errores de JSON parsing con caracteres especiales. SOLO una pregunta por llamada.
57
61
  - **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.
62
+ - Si llegas a preguntar, hazlo solo sobre `verificationMode` (Console vs Visual).
59
63
  - 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.
61
64
  </f0_detect>
62
65
 
63
66
  <f1_contract>
@@ -72,19 +75,23 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
72
75
  </f1_contract>
73
76
 
74
77
  <f2_implementation>
75
- 1. **Delega a `@sdd-coder`**: Envía un prompt conciso. NO re-envíes el contrato entero ni el package.json. Pasa solo:
78
+ 1. **Pre-división de sprints OBLIGATORIA (CRÍTICO bugfix sesión 1186)**: Si `files_affected.length > 5`, divide el trabajo en 2 sprints separados y delega a DOS instancias independientes del coder (NO reusar task_id):
79
+ - **Sprint 1 (Bootstrap + Block Install)**: Bootstrap del proyecto + instalar bloque(s) shadcn. NUNCA más de 5 archivos.
80
+ - **Sprint 2 (Personalización + Polish)**: Layout wrapper, ThemeToggle (si aplica), tokens de marca, theme provider. NUNCA más de 5 archivos.
81
+ - Cada sprint es una **nueva instancia** del coder (sin `task_id` previo).
82
+ - Esto previene "te quedaste sin pasos" y elimina esperas manuales del usuario.
83
+ 2. **Delega a `@sdd-coder`**: Envía un prompt conciso. NO re-envíes el contrato entero ni el package.json. Pasa solo:
76
84
  - Spec name + ruta del contract.json
77
- - Lista resumida de `files_affected`
78
- - Lista de sprints pre-divididos si `files_affected.length > 5`
85
+ - Lista resumida de `files_affected` para ESTE sprint (si dividiste)
79
86
  - Sección `brain_learnings` del brief
80
87
  - *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.
81
- 2. **Verificación de Servidor**:
88
+ 3. **Verificación de Servidor**:
82
89
  - **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.
83
90
  - **De lo contrario (Web Next/FastAPI):**
84
91
  - En autopiloto: Aprueba directo si el server corre sin errores fatales. Transiciona a F3.
85
92
  - En modo `console`: Dile al usuario que verifique localmente en `http://localhost:3000`. Prohibido usar Playwright.
86
93
  - En modo `visual`: Toma screenshot en `.openspec/ts-f2-hil.png` y preséntalo para aprobación.
87
- 3. Transiciona a `F3_VERIFICATION` llamando a `sdd_set_phase`.
94
+ 4. Transiciona a `F3_VERIFICATION` llamando a `sdd_set_phase`.
88
95
  </f2_implementation>
89
96
 
90
97
  <f3_verification>
@@ -92,6 +99,7 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
92
99
  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
100
  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
101
  - *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.
102
+ - **PROHIBIDO**: pasar al tester una descripción que lo invite a hacer `render()` sobre componentes complejos. En modo `console` solo se permiten smoke tests (`fs.existsSync` + verificación de export).
95
103
  4. **Transición**:
96
104
  - **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>`.
97
105
  - **De lo contrario (Web):**
@@ -46,6 +46,9 @@ Eres el Validador de Contratos (sdd-tester) del flujo SDD. Tu trabajo es ejecuta
46
46
  - `regressions` (lecciones del Brain sobre fallos históricos)
47
47
  - Resumen de los `test_scenarios` del contrato activo
48
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.
49
+ - **PROHIBIDO ABSOLUTO: `render()` en modo `console`** (CRÍTICO — bugfix sesión 1186): En modo `console`, tienes ESTRICTAMENTE PROHIBIDO usar `render()` de `@testing-library/react`, `userEvent`, `screen` ni cualquier API que monte componentes reales. Razón: happy-dom se cuelga 30+ segundos al cargar componentes de shadcn (DataTable, ChartAreaInteractive, AppSidebar) que internamente importan Radix, recharts y otros con cadenas de Context. Si necesitas validación visual, usa modo `visual` con Playwright.
50
+ - **Patrón obligatorio**: smoke tests. Validar que el archivo del componente EXISTE en disco (`fs.existsSync`), que CONTIENE el export esperado (`grep -E "export (default )?(function|const) Name"`), y que su firma coincide con el contrato.
51
+ - **Excepción**: PROHIBIDO incluso para ThemeToggle. Valida el contrato sin renderizar.
49
52
  - **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.
50
53
  - **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.
51
54
  - **Prohibición de Playwright**: Si `verificationMode === "console"` en contract.json, tienes STRICTAMENTE PROHIBIDO usar cualquier tool o navegador de Playwright.
@@ -51,6 +51,7 @@ Cualquier desarrollo de interfaz de usuario (Dashboards, Landings, Formularios,
51
51
  - Está estrictamente prohibido usar variables CSS puras directamente en `stroke` o `fill` de SVG si estas no son resueltas de forma dinámica. Si es necesario, utiliza una paleta de colores hexadecimales fija de alto contraste y compatible con ambos modos claro y oscuro (ej: `#0072f5` para líneas y barras, o una paleta de sectores sólida para PieCharts) que garantice legibilidad 100% en fondos claros y oscuros, manteniendo solo los ejes y rejillas de fondo (grids) referenciando variables semánticas.
52
52
  - **Evitar Solapamiento de Sidebar (Sidebar Overlap Prevention):**
53
53
  - Al implementar barras laterales colapsables de Shadcn (`Sidebar`), el Coder debe asegurarse de que el contenedor del contenido principal use de forma robusta `<SidebarInset>` o una estructura flexbox flexible (ej: `flex flex-1 flex-col`) que responda al estado del sidebar. Está estrictamente prohibido usar posicionamiento fijo o absoluto en el sidebar sin un espaciador o contenedor dinámico que evite que el sidebar se sobreponga al contenido en pantallas anchas.
54
+ - **Fix Crítico Tailwind v4 + Shadcn Sidebar**: La CLI de Shadcn inyecta clases arbitrarias con variables CSS directas (`w-[--sidebar-width]`, `w-[--sidebar-width-icon]`, `max-w-[--skeleton-width]`) que en Tailwind v4 generan CSS inválido (`width: --sidebar-width;` sin `var()`). El Coder **debe** parchear inmediatamente `src/components/ui/sidebar.tsx` transformando a sintaxis explícita: `w-[var(--sidebar-width)]`, `w-[var(--sidebar-width-icon)]`, `max-w-[var(--skeleton-width)]`. Sin este fix, el espaciador colapsa a 0px y el contenido principal se solapa bajo el sidebar fijo.
54
55
  - **Robustez HTML y Regla de No-Nesting en Triggers:**
55
56
  - Al utilizar componentes contenedores de Radix/Shadcn UI (como `TooltipTrigger`, `DropdownMenuTrigger`, `DialogTrigger`, `SheetTrigger`), está estrictamente PROHIBIDO anidar un elemento interactivo nativo (como `<button>` o `<a>`) directamente dentro de otro trigger que ya renderice un botón por defecto.
56
57
  - Para evitar la hidratación rota y HTML inválido, se debe usar siempre el prop `asChild` en el trigger de Radix o delegar los props correctamente usando las directivas del componente de base.
@@ -100,6 +100,16 @@ Para garantizar interfaces de usuario premium y visualmente idénticas a los dem
100
100
  **REGLA CRÍTICA DE RUTAS DUPLICADAS POR SHADCN CLI**:
101
101
  Al agregar un bloque interactivo complejo (como `dashboard-01`), el CLI de Shadcn puede inyectar automáticamente archivos en directorios fijos (como `src/app/dashboard/page.tsx`). El Coder **debe** verificar inmediatamente si existe una colisión con la estructura de rutas establecida por el contrato (`contract.json`) —por ejemplo, si el contrato establece un Route Group `src/app/(dashboard)/page.tsx` que sirve en la raíz `/`—. Si ocurre esto, el Coder tiene la obligación de **borrar, mover o refactorizar de inmediato el archivo inyectado automáticamente** para eliminar la duplicidad de rutas, evitando así bucles infinitos de redirección o errores fatales de compilación del router de Next.js.
102
102
 
103
+ **REGLA CRÍTICA DE COMPATIBILIDAD TAILWIND V4 + SHADCN UI (Sidebar Overlap Fix)**:
104
+ Al instalar componentes que usan `sidebar` (como `dashboard-01`, `sidebar-01` a `sidebar-16`, o el componente `sidebar` individual), la CLI de Shadcn inyecta código con sintaxis de variables CSS arbitrarias propias de Tailwind v3 (`w-[--sidebar-width]`, `w-[--sidebar-width-icon]`, `max-w-[--skeleton-width]`) que **rompen en Tailwind v4** generando CSS inválido (`width: --sidebar-width;` sin `var()`). Esto hace que el espaciador invisible del sidebar colapse a 0px, provocando que el contenido principal se solape bajo el sidebar fijo.
105
+
106
+ **Acción obligatoria inmediata tras instalar**: El Coder debe parchear `src/components/ui/sidebar.tsx` en el mismo paso de instalación transformando:
107
+ - `w-[--sidebar-width]` → `w-[var(--sidebar-width)]`
108
+ - `w-[--sidebar-width-icon]` → `w-[var(--sidebar-width-icon)]`
109
+ - `max-w-[--skeleton-width]` → `max-w-[var(--skeleton-width)]`
110
+
111
+ Sin este fix, el dashboard renderizará con el sidebar tapando el contenido. Verificar con `npx tsc --noEmit` y comprobación visual tras la instalación.
112
+
103
113
  ### 3.2 Form Layouts (FieldGroup + Field)
104
114
  Para estructurar campos de formularios, no uses `div`s arbitrarios con clases de espaciado complejas. Usa las composiciones semánticas oficiales de Shadcn:
105
115
  ```tsx
@@ -52,7 +52,7 @@ Para garantizar consistencia y escalabilidad en todos los proyectos, la estructu
52
52
  │ └── integration/ # Pruebas de integración
53
53
  ├── components.json # Configuración Shadcn (style, registries, aliases)
54
54
  ├── package.json # Dependencias frontend (incluye @radix-ui/*, class-variance-authority, clsx, tailwind-merge, lucide-react)
55
- └── tailwind.config.ts # Configuración de TailwindCSS v4
55
+ └── src/app/globals.css # Configuración TailwindCSS v4 vía @theme inline (no existe tailwind.config.ts)
56
56
  ```
57
57
 
58
58
  ## Convenciones de Shadcn UI y Next.js (OBLIGATORIAS)
@@ -4,7 +4,6 @@
4
4
  "rsc": true,
5
5
  "tsx": true,
6
6
  "tailwind": {
7
- "config": "tailwind.config.ts",
8
7
  "css": "src/app/globals.css",
9
8
  "baseColor": "zinc",
10
9
  "cssVariables": true,
@@ -161,15 +161,20 @@ CMD ["python", "src/main.py"]
161
161
 
162
162
  const p = "npm"
163
163
  let pm = "npm"
164
- let installCmd = "npm ci --frozen-lockfile"
164
+ // v3.0 Usar `npm install` adaptativo en vez de `npm ci --frozen-lockfile`.
165
+ // Razón: el bootstrap del harness a menudo agrega packages (shadcn, tailwindcss v4,
166
+ // etc.) que desincronizan el lockfile con package.json, causando `npm ci`
167
+ // a fallar con "lock file does not satisfy". `npm install --no-audit --no-fund`
168
+ // resuelve adaptativamente sin sacrificar la velocidad.
169
+ let installCmd = "npm install --no-audit --no-fund"
165
170
  let buildCmd = "npm run build"
166
171
  if (fs.existsSync(path.resolve(targetPath, "pnpm-lock.yaml"))) {
167
172
  pm = "pnpm"
168
- installCmd = "pnpm install --frozen-lockfile"
173
+ installCmd = "pnpm install --no-frozen-lockfile"
169
174
  buildCmd = "pnpm build"
170
175
  } else if (fs.existsSync(path.resolve(targetPath, "yarn.lock"))) {
171
176
  pm = "yarn"
172
- installCmd = "yarn install --frozen-lockfile"
177
+ installCmd = "yarn install"
173
178
  buildCmd = "yarn build"
174
179
  }
175
180
 
@@ -403,68 +403,40 @@ export const generate_tests = tool({
403
403
  }
404
404
  } else {
405
405
  const componentImport = `@/components/blocks/${feature}`
406
+ // v3.0 — ESTRATEGIA SMOKE TEST PURO. NO importamos el componente
407
+ // para evitar cascadas de imports que cuelgan happy-dom (Radix, recharts,
408
+ // shadcn Sidebar, ThemeProvider, etc.). En su lugar validamos que el
409
+ // archivo del componente EXISTE en disco, tiene el export esperado
410
+ // y su firma (function type) coincide con el contrato.
406
411
  lines.push('import { describe, it, expect } from "vitest";')
412
+ lines.push('import * as fs from "fs";')
413
+ lines.push('import * as path from "path";')
414
+ lines.push("")
407
415
 
408
- if (hasReact) {
409
- lines.push('')
410
- lines.push(`import { ${feature} } from "${componentImport}";`)
411
- lines.push('')
412
- lines.push('// NOTA: NO importamos render/screen/userEvent aquí a propósito.')
413
- lines.push('// Hacer `render(<Component />)` sobre componentes que dependen de Context')
414
- lines.push('// (SidebarProvider, ThemeProvider, etc.) cuelga happy-dom indefinidamente.')
415
- lines.push('// Estrategia: smoke tests que verifican imports y signatures sin renderizar.')
416
- lines.push('// Para tests con interacción real (clicks, eventos), crea un archivo .test.tsx')
417
- lines.push('// dedicado que envuelva el componente con sus Providers necesarios.')
418
- }
416
+ lines.push(`describe("${feature} Tests (Contract Scenarios — smoke)", () => {`)
417
+ lines.push(` const componentPath = path.resolve(__dirname, "../components/blocks/${feature}.tsx");`)
418
+ lines.push(` const componentPathTs = path.resolve(__dirname, "../components/blocks/${feature}.ts");`)
419
+ lines.push(` const altPath = path.resolve(__dirname, "../components/${feature}.tsx");`)
420
+ lines.push(` const altPathTs = path.resolve(__dirname, "../components/${feature}.ts");`)
419
421
  lines.push("")
420
- lines.push(`describe("${feature} Tests (Contract Scenarios)", () => {`)
421
422
 
422
423
  for (const s of scenarios) {
423
424
  const tid = s.id || "TS-XX"
424
425
  const name = s.name || "Test case"
425
- const isIntegration = s.type === "integration"
426
- const thenText = (s.then || "").toLowerCase()
427
-
428
426
  lines.push(` // ${tid}: ${name}`)
429
427
  lines.push(` // Given: ${s.given || ""}`)
430
428
  lines.push(` // When: ${s.when || ""}`)
431
429
  lines.push(` // Then: ${s.then || ""}`)
432
-
433
- // PATRÓN SMOKE TEST (default): NO renderizamos componentes.
434
- // Solo validamos que el módulo se importa, exporta lo esperado,
435
- // y cumple con la firma del contrato. Esto evita el cuelgue de
436
- // happy-dom con árboles de componentes que requieren Providers.
437
- if (isIntegration && thenText.includes("dark")) {
438
- lines.push(` it("${tid}: ${name} (smoke: importable + toggle contract)", () => {`)
439
- lines.push(` // Verifica que el módulo importa sin errores`)
440
- lines.push(` expect(typeof ${feature}).toBe("function");`)
441
- lines.push(` // Verifica firma del contrato: ThemeToggle expone props esperadas`)
442
- lines.push(` expect(${feature}.length).toBeGreaterThanOrEqual(0); // acepta 0 props`)
443
- lines.push(` });`)
444
- } else if (thenText.includes("kpi") || thenText.includes("tarjeta") || thenText.includes("valor")) {
445
- lines.push(` it("${tid}: ${name} (smoke: import + render-safe module shape)", () => {`)
446
- lines.push(` expect(typeof ${feature}).toBe("function");`)
447
- lines.push(` // Verifica que el módulo tiene export default o named export consistente`)
448
- lines.push(` expect(${feature}).toBeDefined();`)
449
- lines.push(` });`)
450
- } else if (thenText.includes("sidebar") || thenText.includes("navega")) {
451
- lines.push(` it("${tid}: ${name} (smoke: sidebar module loads)", () => {`)
452
- lines.push(` expect(typeof ${feature}).toBe("function");`)
453
- lines.push(` // El sidebar requiere SidebarProvider; verificar solo import evita cuelgue`)
454
- lines.push(` expect(${feature}).toBeDefined();`)
455
- lines.push(` });`)
456
- } else if (thenText.includes("svg") || thenText.includes("chart") || thenText.includes("recharts") || thenText.includes("gráfico") || thenText.includes("grafico")) {
457
- lines.push(` it("${tid}: ${name} (smoke: chart module loads without crashing)", () => {`)
458
- lines.push(` expect(typeof ${feature}).toBe("function");`)
459
- lines.push(` // Los charts requieren Recharts ResponsiveContainer; verificamos solo el módulo`)
460
- lines.push(` expect(${feature}).toBeDefined();`)
461
- lines.push(` });`)
462
- } else {
463
- lines.push(` it("${tid}: ${name} (smoke: module + contract signature)", () => {`)
464
- lines.push(` expect(typeof ${feature}).toBe("function");`)
465
- lines.push(` expect(${feature}).toBeDefined();`)
466
- lines.push(` });`)
467
- }
430
+ lines.push(` it("${tid}: ${name}", () => {`)
431
+ lines.push(` // 1. El archivo del componente debe existir en disco`)
432
+ lines.push(` const exists = fs.existsSync(componentPath) || fs.existsSync(componentPathTs) || fs.existsSync(altPath) || fs.existsSync(altPathTs);`)
433
+ lines.push(` expect(exists, "Component file should exist").toBe(true);`)
434
+ lines.push(` // 2. El archivo debe contener el export del componente`)
435
+ lines.push(` const candidates = [componentPath, componentPathTs, altPath, altPathTs];`)
436
+ lines.push(` const found = candidates.find((p) => fs.existsSync(p));`)
437
+ lines.push(` const content = found ? fs.readFileSync(found, "utf8") : "";`)
438
+ lines.push(` expect(content).toMatch(new RegExp("(export\\\\s+(default\\\\s+)?(function|const)\\\\s+${feature}|export\\\\s+\\\\{[^}]*\\\\b${feature}\\\\b)"));`)
439
+ lines.push(" });")
468
440
  lines.push("")
469
441
  }
470
442
  lines.push("});")
@@ -9,7 +9,7 @@ DB_PATH = "/Users/wavesbyte/.local/share/opencode/opencode.db"
9
9
 
10
10
  # Escribe aquí el ID de la sesión que deseas exportar (ejemplo: "ses_1234...")
11
11
  # Si se deja vacío, el script requerirá el ID como argumento al ejecutarlo
12
- TARGET_SESSION_ID = "ses_119a"
12
+ TARGET_SESSION_ID = "ses_1183"
13
13
 
14
14
 
15
15
  def format_timestamp(ts):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zugzbot",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Fácil instalador del arnés SDD de Zugzbot para proyectos OpenCode",
5
5
  "type": "module",
6
6
  "bin": {