zugzbot 1.1.7 → 1.2.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.
@@ -49,6 +49,13 @@ 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
+ 2.5 **Detección de intención de catálogo Akash (CRÍTICO — bugfix sesión 1176)**: Analiza la petición. Si contiene ALGUNA keyword de marketing/landing/auth/composite o un nombre propio del catálogo:
53
+ - Keywords: `hero`, `landing`, `pricing`, `features`, `faq`, `testimonials`, `footer`, `cta`, `navbar`, `stats`, `logo cloud`, `login page`, `signup page`, `forgot password`, `chart`, `sidebar`, `dashboard section`, `comparison`, `use cases`, `how it works`, `changelog`, `cookie banner`, `newsletter`, `bento`, `marquee`, `gallery`, `integration`
54
+ - Nombres propios: `akash`, `akash3444`, `shadcnui-blocks`, `shadcn-ui-blocks`, `basecn`, `shadcn blocks`
55
+ - **Acción obligatoria**: cargar la skill `shadcn-templates` (que ahora referencia las tools MCP `sdd_akash_catalog_list_blocks` y `sdd_akash_catalog_get_block` — NUNCA uses `webfetch` directo a GitHub API, está cacheado).
56
+ - En modo HIL: llama `sdd_akash_catalog_list_blocks({category, limit: 5})` y presenta las 2-3 mejores opciones con `preview_url` para que el usuario elija.
57
+ - En modo autopiloto (`/loop`): llama `sdd_akash_catalog_get_block({name})` sobre 2-3 candidatos y elige el que mejor encaje.
58
+ - Esto evita el ciclo "ping-pong" de 3+ turnos que vimos en sesión 1176 donde el usuario tuvo que guiar al orquestador hacia el catálogo correcto.
52
59
  3. **Detección Fast-Track Dashboard (CRÍTICO — bugfix sesión 1186 y 1180)**: Analiza la petición del usuario. Si contiene ALGUNA de estas keywords: `dashboard`, `admin panel`, `panel de control`, `panel admin`, `crm`, `erp`, `panel`:
53
60
  - **NO preguntes NADA**. Asume todos los defaults: Stack = Next.js 16 + Shadcn UI + Tailwind v4, Persistencia = localStorage/mock, Diseño = `default` (zinc nativo de Shadcn, NO inyectar marcas de Oh-My-Design), Verificación = `console`.
54
61
  - 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`.
@@ -86,6 +93,11 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
86
93
  - Sección `brain_learnings` del brief
87
94
  - *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.
88
95
  3. **Micro-Fixes (Anti-Spec Spam)**: Si el usuario reporta un bug, un desajuste visual o pide una pequeña corrección sobre lo que acabas de entregar, **TIENES PROHIBIDO crear un nuevo spec en F1**. Debes mantener el estado actual en `activeContract`, transicionar/mantener a `F2_IMPLEMENTATION`, y delegar el arreglo al Coder. Solo crea nuevos specs en F1 si el usuario solicita una característica completamente nueva.
96
+ 3.5 **Plantilla de prompt para chart/visual fixes (bugfix sesión 1176)**: Cuando corrijas colores de gráficos Recharts:
97
+ 1. Cambia valores OKLCH en `globals.css` (`.dark` y `:root`).
98
+ 2. Verifica que el `chartConfig` del componente use `'var(--color-chart-N)'` DIRECTAMENTE, NUNCA `'hsl(var(--chart-N))'` — `hsl(oklch(...))` es CSS inválido.
99
+ 3. Si encuentras `'hsl(var(--chart-N))'` en cualquier chartConfig, reemplázalo por `'var(--color-chart-N)'` en el mismo turno.
100
+ 4. El linter `scripts/lint-charts.js` corre automáticamente en shift-left y bloqueará la transición a F3 si quedan ocurrencias inválidas.
89
101
  4. **Verificación de Servidor y HMR**:
90
102
  - **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.
91
103
  - **De lo contrario (Web Next/FastAPI):**
@@ -129,6 +141,11 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
129
141
  - Anúnciala en 1 línea y delega la siguiente iteración completa a un NUEVO hilo `sdd-orchestrator` con contexto de 0 tokens.
130
142
  - Si `loopCurrentIteration >= loopTargetIterations` o modo normal: Concluye con `sdd_set_phase({ phase: "F0_DETECT", loopMode: false })`.
131
143
  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.
144
+ 2.5 **Memory Post-Sesión para Catálogos (CRÍTICO — bugfix sesión 1176)**: Si la sesión tocó el catálogo Akash (shadcn-ui-blocks, basecn) o cualquier catálogo externo de UI, registra OBLIGATORIAMENTE en `brain.routing`:
145
+ - URL canónica del catálogo
146
+ - Comando `npx shadcn@latest add <URL>` listo para usar
147
+ - Nombre del autor/repo (para evitar redescubrimiento en próximas sesiones)
148
+ - Tool MCP del arnés a usar (`sdd_akash_catalog_list_blocks`, etc.)
132
149
  3. **Mensaje Final MÁXIMO 10 LÍNEAS**:
133
150
  ```
134
151
  ✅ Sesión SDD completada
@@ -146,6 +163,7 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
146
163
  <mcp_guidelines>
147
164
  - **MCPs**: `shadcn` (UI), `context7` (APIs Next.js/FastAPI), `playwright` (Visual tests), `lucide-icons` (Iconos).
148
165
  - `next-devtools` deshabilitado por defecto. MCPs invocados exclusivamente por subagentes.
166
+ - **Catálogos externos (Tools internas)**: `sdd_akash_catalog_list_blocks`, `sdd_akash_catalog_get_block`, `sdd_akash_catalog_warm_index` — cachean el catálogo de Akash (shadcn-ui-blocks + basecn) en `.openspec/cache/` con TTL 7d. NUNCA uses `webfetch` directo a GitHub API para descubrir bloques Akash; usa siempre estas tools.
149
167
  </mcp_guidelines>
150
168
 
151
169
  <communication_rules>
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+ /**
4
+ * lint-charts.js — Linter bloqueante para configuraciones de Recharts inválidas.
5
+ *
6
+ * Bugfix sesión 1176: el coder dejó `color: "hsl(var(--chart-1))"` en
7
+ * `chart-area-interactive.tsx`. Como `--chart-1` está definido en OKLCH en
8
+ * `globals.css`, `hsl(oklch(...))` produce CSS inválido y los colores no
9
+ * cambian al actualizar el token.
10
+ *
11
+ * REGLA: en chartConfig, los `color:` DEBEN usar `var(--color-chart-N)`
12
+ * directamente, NUNCA `hsl(var(--chart-N))`.
13
+ *
14
+ * Uso:
15
+ * node .opencode/scripts/lint-charts.js [paths...]
16
+ *
17
+ * Por defecto escanea `src/` y `app/`. Sale con código 1 si encuentra
18
+ * violaciones (BLOQUEANTE en shift-left).
19
+ *
20
+ * Salida JSON para integración programática:
21
+ * node .opencode/scripts/lint-charts.js --json
22
+ */
23
+ "use strict"
24
+
25
+ const fs = require("fs")
26
+ const path = require("path")
27
+
28
+ // ============================================================================
29
+ // Config
30
+ // ============================================================================
31
+
32
+ const INCLUDE_EXT = new Set([".ts", ".tsx", ".js", ".jsx"])
33
+ const IGNORE_DIRS = new Set([
34
+ "node_modules",
35
+ ".next",
36
+ "dist",
37
+ "build",
38
+ "out",
39
+ "coverage",
40
+ ".openspec/cache",
41
+ ".openspec/__tests__",
42
+ ".opencode/templates",
43
+ ".opencode/oh-my-design"
44
+ ])
45
+
46
+ // Patrones prohibidos: cualquier chartConfig que envuelva una variable CSS
47
+ // --chart-N dentro de hsl(). Capturamos la ocurrencia exacta.
48
+ const FORBIDDEN_PATTERNS = [
49
+ {
50
+ name: "hsl(oklch) misuse",
51
+ regex: /hsl\(\s*var\(\s*--chart-\d+\s*\)\s*\)/g,
52
+ fix: "Usa 'var(--color-chart-N)' directamente en chartConfig.color"
53
+ },
54
+ {
55
+ name: "hsl(--chart) sin var()",
56
+ regex: /hsl\(\s*--chart-\d+\s*\)/g,
57
+ fix: "Envuelve en var(): 'hsl(var(--chart-N))' — pero si --chart-N es OKLCH, sigue siendo inválido. Usa 'var(--color-chart-N)'."
58
+ }
59
+ ]
60
+
61
+ // ============================================================================
62
+ // Walk
63
+ // ============================================================================
64
+
65
+ function walk(dir, acc = []) {
66
+ let entries
67
+ try {
68
+ entries = fs.readdirSync(dir, { withFileTypes: true })
69
+ } catch (e) {
70
+ return acc
71
+ }
72
+ for (const entry of entries) {
73
+ if (entry.name.startsWith(".") && entry.name !== ".opencode") continue
74
+ const full = path.join(dir, entry.name)
75
+ if (entry.isDirectory()) {
76
+ if (IGNORE_DIRS.has(entry.name)) continue
77
+ walk(full, acc)
78
+ } else if (entry.isFile()) {
79
+ const ext = path.extname(entry.name)
80
+ if (INCLUDE_EXT.has(ext)) acc.push(full)
81
+ }
82
+ }
83
+ return acc
84
+ }
85
+
86
+ function isInChartContext(filePath, lineContent) {
87
+ // Heurística: considerar chartConfig si el archivo contiene Recharts
88
+ // o si la línea está cerca de una mención a ChartConfig. Para mantener
89
+ // el linter simple y rápido, NO restringimos por contexto y reportamos
90
+ // cualquier coincidencia; el developer decide si aplica.
91
+ return true
92
+ }
93
+
94
+ // ============================================================================
95
+ // Lint
96
+ // ============================================================================
97
+
98
+ function lintFile(filePath) {
99
+ const content = fs.readFileSync(filePath, "utf8")
100
+ const lines = content.split("\n")
101
+ const violations = []
102
+
103
+ lines.forEach((line, idx) => {
104
+ for (const pat of FORBIDDEN_PATTERNS) {
105
+ pat.regex.lastIndex = 0
106
+ let m
107
+ while ((m = pat.regex.exec(line)) !== null) {
108
+ if (!isInChartContext(filePath, line)) continue
109
+ violations.push({
110
+ file: filePath,
111
+ line: idx + 1,
112
+ column: m.index + 1,
113
+ match: m[0],
114
+ pattern: pat.name,
115
+ fix: pat.fix,
116
+ snippet: line.trim()
117
+ })
118
+ }
119
+ }
120
+ })
121
+
122
+ return violations
123
+ }
124
+
125
+ // ============================================================================
126
+ // Main
127
+ // ============================================================================
128
+
129
+ function main() {
130
+ const argv = process.argv.slice(2)
131
+ const jsonOut = argv.includes("--json")
132
+ const explicitPaths = argv.filter((a) => !a.startsWith("--"))
133
+
134
+ const roots =
135
+ explicitPaths.length > 0
136
+ ? explicitPaths
137
+ : [path.join(process.cwd(), "src"), path.join(process.cwd(), "app")].filter((p) => {
138
+ try {
139
+ return fs.statSync(p).isDirectory()
140
+ } catch {
141
+ return false
142
+ }
143
+ })
144
+
145
+ if (roots.length === 0) {
146
+ const msg = "No se encontraron directorios src/ ni app/ para escanear."
147
+ if (jsonOut) {
148
+ console.log(JSON.stringify({ status: "SKIPPED", message: msg, violations: [] }, null, 2))
149
+ } else {
150
+ console.log(`[lint-charts] ${msg}`)
151
+ }
152
+ process.exit(0)
153
+ }
154
+
155
+ const files = roots.flatMap((r) => walk(r))
156
+ const allViolations = files.flatMap((f) => lintFile(f))
157
+
158
+ if (jsonOut) {
159
+ console.log(
160
+ JSON.stringify(
161
+ {
162
+ status: allViolations.length === 0 ? "PASS" : "FAIL",
163
+ filesScanned: files.length,
164
+ violations: allViolations,
165
+ summary: allViolations.length === 0
166
+ ? "No se encontraron chartConfig con hsl()+oklch misuse."
167
+ : `${allViolations.length} violacion(es) detectada(s).`
168
+ },
169
+ null,
170
+ 2
171
+ )
172
+ )
173
+ } else {
174
+ console.log(`[lint-charts] Escaneados ${files.length} archivos.`)
175
+ if (allViolations.length === 0) {
176
+ console.log("[lint-charts] PASS - sin violaciones.")
177
+ } else {
178
+ console.log(`[lint-charts] FAIL - ${allViolations.length} violacion(es):`)
179
+ for (const v of allViolations) {
180
+ const rel = path.relative(process.cwd(), v.file)
181
+ console.log(
182
+ ` ${rel}:${v.line}:${v.column} ${v.pattern}\n` +
183
+ ` match: ${v.match}\n` +
184
+ ` fix: ${v.fix}\n` +
185
+ ` line: ${v.snippet}`
186
+ )
187
+ }
188
+ }
189
+ }
190
+
191
+ process.exit(allViolations.length === 0 ? 0 : 1)
192
+ }
193
+
194
+ main()
@@ -148,6 +148,41 @@ Copia y adapta los IDs y descripciones que apliquen. Borra los que no apliquen.
148
148
  - `AUTH-02`: Login con credenciales inválidas muestra error genérico.
149
149
  - `AUTH-03`: Logout limpia el token y redirige a `/login`.
150
150
 
151
+ ### Charts / Recharts (CRÍTICO — bugfix sesión 1176)
152
+
153
+ Cuando el contrato declare un chart de Recharts (`chart-area-*`, `chart-bar-*`, `chart-pie-*`, etc.) o cualquier componente que lea `var(--chart-*)`, **debes incluir al menos UNO de los siguientes test_scenarios para forzar la validación de tema dark/light en F3**:
154
+
155
+ ```json
156
+ {
157
+ "id": "TS-CHART-01",
158
+ "name": "Chart visible con contraste WCAG AA en modo oscuro",
159
+ "type": "visual",
160
+ "feature_ref": "ChartAreaInteractive",
161
+ "given": "el dashboard se renderiza en modo oscuro",
162
+ "when": "el area chart muestra las series desktop y mobile",
163
+ "then": "las líneas y rellenos tienen contraste >= 4.5:1 con el fondo de la card",
164
+ "selector": "[data-testid='chart-area-interactive']",
165
+ "assertion": "color-contrast-pass AND not-invisible"
166
+ }
167
+ ```
168
+
169
+ ```json
170
+ {
171
+ "id": "TS-CHART-02",
172
+ "name": "ChartConfig usa variables CSS válidas (no hsl+oklch mix)",
173
+ "type": "unit",
174
+ "feature_ref": "ChartAreaInteractive",
175
+ "given": "el componente importa su chartConfig",
176
+ "when": "el linter `node .opencode/scripts/lint-charts.js` escanea el archivo",
177
+ "then": "ninguna entrada usa 'hsl(var(--chart-N))'; todas usan 'var(--color-chart-N)' directamente",
178
+ "assertion": "lint-charts --json status == PASS"
179
+ }
180
+ ```
181
+
182
+ **Por qué importan**: en sesión 1176, el coder dejó `color: "hsl(var(--chart-1))"` que es CSS inválido porque `--chart-1` se define en OKLCH. El usuario tuvo que reportar el bug manualmente. Estos test_scenarios **activan el linter BLOQUEANTE** `sdd_testing_lint_charts` durante shift-left y fuerzan al tester a validar contraste visual con Playwright.
183
+
184
+ Si el chart es solo decorativo (no necesita tema oscuro), puedes omitir `TS-CHART-01`. Pero `TS-CHART-02` debería incluirse **siempre** que el chart use tokens `--chart-*`.
185
+
151
186
  ---
152
187
 
153
188
  ## 2.5. Plantilla MÍNIMA viable (Fast-Track Dashboard)
@@ -156,16 +156,31 @@ El `@sdd-coder` utiliza el template base `nextjs-shadcn`. Si el contrato no deta
156
156
 
157
157
  Además del catálogo nativo de Shadcn UI, dispones de acceso autónomo a más de 200 bloques de alta calidad del repositorio **Shadcn UI Blocks (por Akash)**. Si el usuario solicita una interfaz relacionada con landings, marketing, autenticación, footers o dashboards, **TIENES LA OBLIGACIÓN** de explorar este catálogo ANTES de proponer crear componentes desde cero.
158
158
 
159
+ ### 5.0 Herramientas MCP Nativas del Arnés (PREFERENTE — bugfix sesión 1176)
160
+
161
+ **PROHIBIDO** usar `webfetch` directo a la API de GitHub para descubrir bloques. El arnés expone herramientas MCP dedicadas que **cachean el índice localmente** con TTL de 7 días, evitando redescubrimiento y latencia:
162
+
163
+ - `sdd_akash_catalog_list_blocks({ category?, query?, registry?, limit?, force_refresh? })`
164
+ - Devuelve el listado completo del catálogo (shadcn-ui-blocks + basecn) filtrado por categoría o substring.
165
+ - `registry`: `"shadcn"` (Radix), `"basecn"` (Base UI) o `"both"` (default).
166
+ - `force_refresh: true` re-descarga aunque el cache sea fresco.
167
+ - `sdd_akash_catalog_get_block({ name, registry?, force_refresh? })`
168
+ - Devuelve el código fuente, archivos y dependencias de un bloque específico.
169
+ - Incluye `install_command` listo: `npx shadcn@latest add https://shadcnui-blocks.com/r/radix/<name>.json --yes`.
170
+ - `sdd_akash_catalog_warm_index({ registry? })`
171
+ - Pre-calienta el caché para evitar la latencia del primer `list_blocks`. Llamar al bootstrap del proyecto.
172
+
173
+ Cache local en `.openspec/cache/` (`.gitignore` lo excluye automáticamente). Si el caché tiene más de 7 días, se re-descarga silenciosamente en el próximo `list_blocks`.
174
+
159
175
  ### 5.1 Flujo de Descubrimiento (Fase F1 - Spec-Writer)
160
- Si el usuario solicita una interfaz (ej: "necesito un hero", "haz un footer"), no asumas que debes programarlo a mano. Primero consulta la lista maestra de bloques en tiempo real:
161
- - **Comando a usar:** Ejecuta la herramienta `webfetch` apuntando a la API de GitHub:
162
- `webfetch("https://api.github.com/repos/akash3444/shadcn-ui-blocks/contents/public/r/radix")`
163
- - **Análisis de resultados:** El JSON devuelto listará todos los bloques (`hero-01.json`, `footer-05.json`, `pricing-02.json`, etc.). Selecciona mentalmente los que coincidan con la necesidad.
176
+ Si el usuario solicita una interfaz (ej: "necesito un hero", "haz un footer"), **llama primero a `sdd_akash_catalog_list_blocks`**:
177
+ - **Asistido:** `sdd_akash_catalog_list_blocks({ category: "hero", limit: 5 })` muestra las 5 mejores opciones con su `preview_url` (`https://shadcnui-blocks.com/blocks/<name>`).
178
+ - **Autopiloto:** `sdd_akash_catalog_get_block({ name: "hero-06" })` sobre 2-3 candidatos y elige el que mejor encaje.
164
179
 
165
180
  ### 5.2 Selección de Bloques (Interactiva o Autónoma)
166
181
  Cuando existan múltiples opciones para un mismo tipo de bloque (ej. 10 footers distintos):
167
182
  - **Modo Asistido (Por defecto):** Utiliza la herramienta `question` para preguntarle al usuario o dile explícitamente en el chat: *"Tengo acceso a la galería de Shadcn UI Blocks. He encontrado X opciones de [categoria]. ¿Quieres que revise algunas en particular o te dejo el enlace visual (`https://www.shadcnui-blocks.com/blocks/categories/[categoria]`) para que elijas la que más te guste?"*.
168
- - **Modo Autopiloto (`/loop`):** Si estás actuando de forma 100% autónoma, lee el código fuente de 2 o 3 opciones al azar utilizando `webfetch("https://raw.githubusercontent.com/akash3444/shadcn-ui-blocks/main/public/r/radix/[bloque].json")` para analizar qué código encaja mejor con la petición y elígelo autónomamente sin preguntar.
183
+ - **Modo Autopiloto (`/loop`):** Si estás actuando de forma 100% autónoma, lee el código fuente de 2 o 3 opciones con `sdd_akash_catalog_get_block` y elígelo autónomamente sin preguntar.
169
184
 
170
185
  ### 5.3 Inyección e Instalación (Contrato y Coder)
171
186
  - **Spec-Writer (F1):** En el `contract.json`, dentro de `frontend.components`, anota el bloque utilizando la URL remota directa:
@@ -176,11 +191,19 @@ Cuando existan múltiples opciones para un mismo tipo de bloque (ej. 10 footers
176
191
  ]
177
192
  }
178
193
  ```
179
- - **Coder (F2):** Para instalar estos bloques externos, utiliza la capacidad nativa de la CLI v3 de Shadcn. Ejecuta directamente el comando apuntando a la URL:
194
+ - **Coder (F2):** Para instalar estos bloques externos, **obtén el `install_command` listo** vía `sdd_akash_catalog_get_block({ name })` (campo `install_command` del resultado) y ejecútalo. La CLI v3 de Shadcn se encarga automáticamente de descargar el código y resolver dependencias internas:
180
195
  ```bash
181
- npx shadcn@latest add https://shadcnui-blocks.com/r/radix/footer-04.json
196
+ npx shadcn@latest add https://shadcnui-blocks.com/r/radix/footer-04.json --yes
182
197
  ```
183
- La CLI se encargará automáticamente de descargar el código y resolver dependencias internas.
198
+
199
+ ### 5.4 Validación Post-Instalación (BLOQUEANTE — bugfix sesión 1176)
200
+ Tras instalar cualquier bloque Akash (`npx shadcn add [URL]`), ejecuta INMEDIATAMENTE:
201
+ 1. `npx tsc --noEmit` para detectar imports rotos o packages faltantes.
202
+ 2. Verifica que todos los archivos declarados en el JSON del bloque fueron creados (compara con el campo `source_files` devuelto por `sdd_akash_catalog_get_block`).
203
+ 3. Si faltan archivos, reintenta la instalación con `--overwrite`.
204
+ 4. Solo entonces reporta éxito al orquestador.
205
+
206
+ **Bug conocido**: `Can't resolve 'shadcn/tailwind.css'` en Tailwind v4 requiere `npm install shadcn` antes de continuar.
184
207
 
185
208
  ---
186
209