zugzbot 1.1.7 → 1.3.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,14 @@ 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 externo (CRÍTICO — bugfix sesión 1176)**: Analiza la petición. Si contiene ALGUNA keyword de marketing/landing/auth/composite, animaciones/shaders/WebGL, AI components, o un nombre propio de cualquier catálogo:
53
+ - Keywords marketing/landing (shadcn): `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
+ - Keywords animaciones (reactbits): `shader`, `webgl`, `gsap`, `framer-motion`, `three.js`, `drei`, `animated background`, `animated text`, `gradient mesh`, `magnet`, `floating dock`, `cursor`, `particles`, `noise`, `orbit`, `aurora`, `dither`, `iridescence`, `blob`, `glare`, `staggered`, `magic bento`, `electric border`, `metallic paint`
55
+ - Nombres propios: `akash`, `akash3444`, `shadcnui-blocks`, `shadcn-ui-blocks`, `basecn`, `shadcn blocks`, `reactbits`, `react-bits`, `david haz`
56
+ - **Acción obligatoria**: cargar la skill `shadcn-templates` (que ahora referencia las tools MCP `sdd_catalog_list_blocks`, `sdd_catalog_get_block` y `sdd_catalog_warm_index` — NUNCA uses `webfetch` directo a GitHub/reactbits.dev; esta cacheado).
57
+ - En modo HIL: llama `sdd_catalog_list_blocks({category, limit: 5, registry: 'all'})` y presenta las 2-3 mejores opciones con `preview_url` para que el usuario elija. **Incluir siempre `preview_url` en la respuesta** — para reactbits el demo es animado y el usuario puede ver cómo se ve antes de instalar.
58
+ - En modo autopiloto (`/loop`): llama `sdd_catalog_get_block({name})` sobre 2-3 candidatos (uno de cada registry) y elige el que mejor encaje.
59
+ - 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
60
  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
61
  - **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
62
  - 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 +94,11 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
86
94
  - Sección `brain_learnings` del brief
87
95
  - *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
96
  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.
97
+ 3.5 **Plantilla de prompt para chart/visual fixes (bugfix sesión 1176)**: Cuando corrijas colores de gráficos Recharts:
98
+ 1. Cambia valores OKLCH en `globals.css` (`.dark` y `:root`).
99
+ 2. Verifica que el `chartConfig` del componente use `'var(--color-chart-N)'` DIRECTAMENTE, NUNCA `'hsl(var(--chart-N))'` — `hsl(oklch(...))` es CSS inválido.
100
+ 3. Si encuentras `'hsl(var(--chart-N))'` en cualquier chartConfig, reemplázalo por `'var(--color-chart-N)'` en el mismo turno.
101
+ 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
102
  4. **Verificación de Servidor y HMR**:
90
103
  - **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
104
  - **De lo contrario (Web Next/FastAPI):**
@@ -129,6 +142,11 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
129
142
  - 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
143
  - Si `loopCurrentIteration >= loopTargetIterations` o modo normal: Concluye con `sdd_set_phase({ phase: "F0_DETECT", loopMode: false })`.
131
144
  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.
145
+ 2.5 **Memory Post-Sesión para Catálogos (CRÍTICO — bugfix sesión 1176)**: Si la sesión tocó cualquier catálogo externo de UI (Akash shadcn-ui-blocks, basecn, reactbits.dev), registra OBLIGATORIAMENTE en `brain.routing`:
146
+ - URL canónica del catálogo
147
+ - Comando `npx shadcn@latest add <URL>` listo para usar
148
+ - Nombre del autor/repo (para evitar redescubrimiento en próximas sesiones)
149
+ - Tool MCP del arnés a usar (`sdd_catalog_list_blocks`, `sdd_catalog_get_block`, etc.)
132
150
  3. **Mensaje Final MÁXIMO 10 LÍNEAS**:
133
151
  ```
134
152
  ✅ Sesión SDD completada
@@ -146,6 +164,7 @@ Eres el coordinador principal del arnés de desarrollo SDD (Spec-Driven Developm
146
164
  <mcp_guidelines>
147
165
  - **MCPs**: `shadcn` (UI), `context7` (APIs Next.js/FastAPI), `playwright` (Visual tests), `lucide-icons` (Iconos).
148
166
  - `next-devtools` deshabilitado por defecto. MCPs invocados exclusivamente por subagentes.
167
+ - **Catálogos externos (Tools internas)**: `sdd_catalog_list_blocks`, `sdd_catalog_get_block`, `sdd_catalog_warm_index` — cachean el catálogo unificado (Akash shadcn-ui-blocks + basecn + reactbits.dev) en `.openspec/cache/` con TTL 7d (Akash/Basecn) y 1d (reactbits). NUNCA uses `webfetch` directo a GitHub o reactbits.dev para descubrir bloques; usa siempre estas tools. Solo se soportan registries con catálogo JSON oficialmente discoverable.
149
168
  </mcp_guidelines>
150
169
 
151
170
  <communication_rules>
@@ -33,6 +33,14 @@ Para optimizar el tiempo de desarrollo, garantizar una experiencia visual premiu
33
33
  - **Preservación de Datos Dummy**: El archivo de datos del bloque (por ejemplo, `data.json` o constantes locales) debe mantenerse intacto en su ruta inyectada por la CLI en esta Fase A para garantizar que la vista se muestre de inmediato en el navegador exactamente idéntica al demo real.
34
34
  - **Sustitución Atómica de Datos en Fase B**: Una vez que el bloque compila e interactúa en el navegador de manera idéntica al demo con los datos de ejemplo, el Coder procederá de forma exclusiva a sustituir los datos mock por lógica y fetchings de datos dinámicos del backend, manteniendo intactos los contenedores estructurales del bloque (grids de diseño, paddings y contenedores flex) sin alteración alguna para evitar aberraciones visuales.
35
35
  - **Resolución Activa de Conflictos de Ruteo por Inyección de Bloques**: Si la CLI de Shadcn inyecta automáticamente una estructura de rutas (ej: `src/app/dashboard/page.tsx`) que compite con las rutas del contrato (como un route group `(dashboard)` o una ruta raíz), el Coder **debe** borrar o refactorizar de inmediato el código inyectado por defecto para evitar bucles de redirección infinitos o colisiones en el router.
36
+ - **Catálogo Unificado `sdd_catalog` (3 registries, todos discoverables)**: Antes de inventar cualquier layout/bloque desde cero, el Spec-Writer (F1) y el Coder (F2) **deben** consultar `sdd_catalog_list_blocks({ registry: "all", category: "<cat>" })`. El catálogo cubre 3 fuentes con catálogo JSON oficial en un solo tool:
37
+ 1. **`shadcn`** (akash3444/shadcn-ui-blocks, GitHub Contents API) — ~540 bloques Radix/Next.js curados. **Default** para marketing, landing, auth, dashboards, footers.
38
+ 2. **`basecn`** (akash3444/basecn, GitHub Contents API) — ~60 bloques fork Base UI. Usar solo si el proyecto está en Base UI.
39
+ 3. **`reactbits`** (reactbits.dev, `/r/registry.json` oficial shadcn-compatible) — 134 primitivas animadas × 4 variantes (JS-CSS/JS-TW/TS-CSS/TS-TW, default `TS-TW`). **Fuente preferente** para backgrounds animados, textos con efecto, cards interactivas, cursors custom, dock flotante. Tratar como third-party trust: revisar siempre el campo `dependencies` (suelen incluir `gsap`, `three`, `@react-three/fiber`, `motion`) y validar compat con el stack target antes de instalar.
40
+ - **Criterio de inclusion**: solo se soportan registries con catálogo JSON oficialmente discoverable. 21st.dev fue removido porque su catálogo es JS-rendered y rompe el principio de "siempre saber qué hay disponible antes de elegir".
41
+ - El comando install siempre viene en el campo `install_command` del resultado de `sdd_catalog_get_block`. **Prohibido** ejecutar `npx shadcn add` con una URL adivinada; usar siempre el `install_command` literal que devuelve la tool.
42
+ - El catálogo guarda caché local en `.openspec/cache/` con TTL diferenciado (7d shadcn/basecn, 1d reactbits) — **NUNCA** usar `webfetch` directo a GitHub o reactbits.dev, está cacheado.
43
+ - Todos los items exponen `preview_url`. Para reactbits el demo es animado vivo (`https://reactbits.dev/<BaseName>`) — el orchestrator debe **siempre** presentar el `preview_url` al usuario en modo HIL para que vea cómo se ve antes de elegir.
36
44
 
37
45
  ## 4. Soporte para Subdirectorios, Monorepos (targetDir) y Estándar Corporativo `src/`
38
46
  Si el proyecto o aplicación principal vive en un subdirectorio (ej. `next-app`, `frontend`, `backend`), se deben seguir estas directivas estrictas para mantener la consistencia y el "estándar corporativo con carpeta `src/`":
@@ -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)
@@ -152,35 +152,119 @@ El `@sdd-spec-writer` utiliza de manera proactiva el MCP `shadcn` (`shadcn_searc
152
152
  ### Fase F2 (Implementation)
153
153
  El `@sdd-coder` utiliza el template base `nextjs-shadcn`. Si el contrato no detalla todos los sub-componentes o el Coder necesita verificar el comportamiento de un bloque antes de acoplarlo con el backend, debe ejecutar `shadcn_get_item_examples_from_registries` para ver ejemplos funcionales del componente real. Tras ejecutar la instalación con `npx shadcn@latest add`, el Coder debe contrastar los archivos inyectados con el mapa del contrato y limpiar de inmediato cualquier archivo redundante (como una página de inicio duplicada) que no coincida con el router oficial.
154
154
 
155
- ## 5. Catálogo Externo Oficial: Shadcn UI Blocks (Akash)
155
+ ## 5. Catálogo Unificado de Bloques Externos (`sdd_catalog`)
156
156
 
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.
157
+ Además del catálogo nativo de Shadcn UI, el arnés expone un catálogo unificado de **3 registries** externos (todos con catálogo JSON oficial y discoverable programáticamente). Si el usuario pide cualquier interfaz (hero, footer, dashboard, login, AI chat, animated background, etc.), **TIENES LA OBLIGACIÓN** de explorar este catálogo ANTES de inventar desde cero.
158
158
 
159
- ### 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.
159
+ ### 5.0 Registries Soportados (todos discoverables)
164
160
 
165
- ### 5.2 Selección de Bloques (Interactiva o Autónoma)
166
- Cuando existan múltiples opciones para un mismo tipo de bloque (ej. 10 footers distintos):
167
- - **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.
161
+ | Registry | Fuente de catálogo | Contenido | TTL | Notas |
162
+ |---|---|---|---|---|
163
+ | `shadcn` | akash3444/shadcn-ui-blocks (GitHub Contents API) | ~540 bloques Radix | 7d | **Default** para marketing/landing/auth/dashboards |
164
+ | `basecn` | akash3444/basecn (GitHub Contents API) | ~60 bloques fork Base UI | 7d | Alternativa si el proyecto usa Base UI |
165
+ | `reactbits` | reactbits.dev/r/registry.json (JSON oficial shadcn) | 134 bases × 4 variantes = 536 items | 1d | Primitivas animadas (backgrounds, text, cards, cursors, etc.) |
169
166
 
170
- ### 5.3 Inyección e Instalación (Contrato y Coder)
171
- - **Spec-Writer (F1):** En el `contract.json`, dentro de `frontend.components`, anota el bloque utilizando la URL remota directa:
167
+ > **Criterio de inclusion**: solo se soportan registries con catálogo JSON oficialmente discoverable. 21st.dev fue removido porque su catálogo es JS-rendered y requiere scraping para descubrir items nuevos — eso rompe el principio de "siempre saber qué hay disponible antes de elegir".
168
+
169
+ ### 5.1 Herramientas MCP Nativas del Arnés (PREFERENTE — `sdd_catalog_*`)
170
+
171
+ **PROHIBIDO** usar `webfetch` directo a GitHub o reactbits.dev para descubrir bloques. El arnés expone 3 tools MCP dedicadas que cachean en `.openspec/cache/` y evitan redescubrimiento:
172
+
173
+ - `sdd_catalog_list_blocks({ category?, query?, registry?, limit?, force_refresh? })`
174
+ - `registry`: `"shadcn"` | `"basecn"` | `"reactbits"` | `"all"` (default).
175
+ - Filtra por categoria exacta (`hero`, `pricing`, `backgrounds`, `text`, `cards`, `navigation`, ...) o substring libre (sobre `name`, `description`, `tags`).
176
+ - Para `reactbits`: devuelve las **134 bases** (cada una con `variants[]` y `default_variant`), NO las 536 variantes. Esto evita duplicados y permite ver opciones de un vistazo.
177
+ - `sdd_catalog_get_block({ name, registry?, variant?, force_refresh? })`
178
+ - Formatos de `name`:
179
+ - shadcn/basecn: `"hero-06"`, `"sidebar-07"`
180
+ - reactbits: `"Dither"` (auto-resuelve a `Dither-TS-TW` por default) **o** `"Dither-JS-CSS"` (variante canónica completa)
181
+ - URL directa: `"https://reactbits.dev/r/Dither-TS-TW.json"` o `"https://shadcnui-blocks.com/r/radix/hero-06.json"`
182
+ - Namespace shadcn: `"@acme/button"`
183
+ - Para reactbits, parámetro opcional `variant`: `"JS-CSS" | "JS-TW" | "TS-CSS" | "TS-TW"` (default `"TS-TW"`). Aplicar solo cuando pasas el nombre base sin sufijo de variante.
184
+ - `registry` autodetecta por el formato; pasalo explícito solo si necesitas forzar.
185
+ - Devuelve `source_files[]`, `dependencies[]`, `preview_url`, `install_command` listo y metadatos `base_name` + `variant` (para reactbits).
186
+ - `sdd_catalog_warm_index({ registry? })`
187
+ - Pre-calienta caches. Llamar al bootstrap (F0) o inicio de F1.
188
+
189
+ ### 5.2 Flujo de Descubrimiento (Fase F1 - Spec-Writer)
190
+
191
+ Cuando el usuario pide una UI, sigue este orden de prioridad:
192
+
193
+ 1. **Primero** `sdd_catalog_list_blocks({ registry: "all", category: "<categoria>", limit: 10 })` — devuelve candidatos de los 3 registries simultáneamente.
194
+ 2. **Si el usuario pide animaciones/shaders/efectos visuales cinematograficos**, prioriza `registry: "reactbits"` (es donde estan las primitivas animadas reales: `Dither`, `Aurora`, `GlareHover`, `Magnet`, `StaggeredMenu`, `FloatingDock`, etc.).
195
+ 3. **Si el usuario pide paginas de marketing completas** (landing, pricing, features), prioriza `registry: "shadcn"` (bloques Radix mas completos con texto, CTAs, imagenes placeholder).
196
+ 4. **Si el usuario ya dio una URL** (`https://reactbits.dev/...` o `https://shadcnui-blocks.com/...`), ve directo a `sdd_catalog_get_block` con esa URL.
197
+ 5. **Presenta opciones con `preview_url`** para que el usuario vea cada una antes de elegir:
198
+ - **Asistido (HIL):** muestra `preview_url` (que el usuario puede abrir en navegador) + `install_command` para los 2-3 mejores candidatos y deja que elija. Si el usuario dice "ese", ejecuta el `install_command`.
199
+ - **Autopiloto (`/loop`):** inspecciona 2-3 candidatos con `sdd_catalog_get_block`, analiza dependencias y compat con el stack del contrato, elige autonomamente.
200
+
201
+ ### 5.3 Seleccion de Bloques (Interactiva o Autonoma)
202
+
203
+ Cuando hay multiples opciones para una categoria (ej. 10 heroes):
204
+ - **Modo Asistido (default):** pregunta al usuario con `question` o en chat: *"Tengo X opciones de [categoria]. ¿Quieres revisar alguna? Puedo darte el `preview_url` (ej. `https://shadcnui-blocks.com/blocks/hero-06` o `https://reactbits.dev/Dither`) para que elijas."*
205
+ - **Modo Autopiloto:** lee el codigo de 2-3 opciones con `sdd_catalog_get_block` y elige sin preguntar.
206
+
207
+ ### 5.4 Inyeccion e Instalacion (Spec-Writer + Coder)
208
+
209
+ - **Spec-Writer (F1):** En el `contract.json`, dentro de `frontend.components`, anota el bloque usando **el `install_url` o el `name` canonico** que devolvio `sdd_catalog_get_block`:
172
210
  ```json
173
211
  "frontend": {
174
212
  "components": [
175
- "https://shadcnui-blocks.com/r/radix/footer-04.json"
213
+ { "name": "hero-06", "registry": "shadcn", "source": "akash3444/shadcn-ui-blocks" },
214
+ { "name": "Dither", "registry": "reactbits", "variant": "TS-TW", "source": "reactbits.dev/Dither" }
176
215
  ]
177
216
  }
178
217
  ```
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:
218
+ - **Coder (F2):** Para instalar, usa el `install_command` literal que devuelve `sdd_catalog_get_block` (campo `block.install_command`). Ejemplos reales:
180
219
  ```bash
181
- npx shadcn@latest add https://shadcnui-blocks.com/r/radix/footer-04.json
220
+ # Akash Radix (shadcn CLI nativo)
221
+ npx shadcn@latest add https://shadcnui-blocks.com/r/radix/hero-06.json --yes
222
+ # Basecn
223
+ npx shadcn@latest add https://basecn.dev/r/footer-04.json --yes
224
+ # reactbits (variante TS-TW explicita)
225
+ npx shadcn@latest add https://reactbits.dev/r/Dither-TS-TW.json --yes
182
226
  ```
183
- La CLI se encargará automáticamente de descargar el código y resolver dependencias internas.
227
+ La CLI v3 de Shadcn negocia `Accept: application/json` automaticamente, descarga el codigo y resuelve las `registryDependencies`.
228
+
229
+ ### 5.5 Validacion Post-Instalacion (BLOQUEANTE)
230
+
231
+ Tras instalar cualquier bloque externo, ejecuta INMEDIATAMENTE:
232
+ 1. `npx tsc --noEmit` para detectar imports rotos o packages faltantes.
233
+ 2. Verifica que todos los archivos declarados en `source_files[]` (devuelto por `sdd_catalog_get_block`) fueron creados fisicamente.
234
+ 3. Si faltan archivos, reintenta con `--overwrite`.
235
+ 4. **Para bloques reactbits con dependencias pesadas** (`gsap`, `three`, `@react-three/fiber`, `motion`): confirma que el `package.json` del proyecto las incluye; si no, `npm install <deps>` antes de continuar. Revisa el campo `dependencies[]` del JSON retornado por `sdd_catalog_get_block`.
236
+ 5. **Solo entonces** reporta exito al orquestador.
237
+
238
+ **Bug conocido**: `Can't resolve 'shadcn/tailwind.css'` en Tailwind v4 requiere `npm install shadcn` antes de continuar.
239
+
240
+ ### 5.6 reactbits.dev - Detalles de Variantes
241
+
242
+ reactbits.dev es el registry mas "vivo" del catalogo unificado. Cada componente base se publica en **4 variantes** con sufijo `-<Lang>-<Style>`:
243
+
244
+ | Sufijo | Lenguaje | Styling |
245
+ |---|---|---|
246
+ | `JS-CSS` | JavaScript | CSS plano (.css) |
247
+ | `JS-TW` | JavaScript | Tailwind v4 (.tw) |
248
+ | `TS-CSS` | TypeScript | CSS plano |
249
+ | `TS-TW` | TypeScript | Tailwind v4 |
250
+
251
+ - **Default del arnés**: `TS-TW` (TypeScript + Tailwind v4, alineado al stack Next.js 16 + Shadcn + Tailwind v4 del harness).
252
+ - **Para usar otra variante**: `sdd_catalog_get_block({ name: "Dither", variant: "JS-CSS", registry: "reactbits" })`.
253
+ - **El preview_url es siempre por base**: `https://reactbits.dev/Dither` (muestra el demo vivo con animación real, ideal para que el usuario vea cómo se ve antes de instalar).
254
+ - **Las dependencias son reales**: `gsap@^3.13.0`, `three`, `@react-three/fiber@^9.3.0`, `postprocessing`, `motion`. **Third-party trust obligatorio**: validar compat con el stack target antes de instalar.
255
+ - **Mapa curado de categorias** (dentro de `sdd_catalog.ts`, constante `REACTBITS_POPULAR_BY_CATEGORY`): las ~85 bases más populares organizadas en `backgrounds`, `text`, `cards`, `navigation`, `buttons`, `animations`, `cursors`, `misc`. Las bases fuera del mapa caen en `misc` pero son buscables por `query`.
256
+
257
+ ### 5.7 Preview UX — Cómo se ve antes de instalar
258
+
259
+ **Todos los items del catálogo unificado exponen `preview_url`** que el orchestrator debe presentar al usuario en modo HIL para que vea la animación/componente real antes de decidir:
260
+
261
+ | Registry | Preview URL | Tipo de preview |
262
+ |---|---|---|
263
+ | shadcn | `https://shadcnui-blocks.com/blocks/<name>` | iframe del demo oficial |
264
+ | basecn | `https://basecn.dev/blocks/<name>` | iframe del demo oficial |
265
+ | reactbits | `https://reactbits.dev/<BaseName>` | demo vivo con animacion real + codigo a la derecha |
266
+
267
+ Cuando el orchestrator presente opciones al usuario, **siempre incluir el `preview_url`** en el chat. Para reactbits en particular, el demo es animado y se ve inmediatamente cómo se mueve la UI en el navegador del usuario — esto cierra el ciclo "imaginar como se ve → ver real → decidir instalar" sin hacer conjeturas.
184
268
 
185
269
  ---
186
270
 
@@ -16,5 +16,8 @@
16
16
  "lib": "@/lib",
17
17
  "hooks": "@/hooks"
18
18
  },
19
- "iconLibrary": "lucide"
19
+ "iconLibrary": "lucide",
20
+ "registries": {
21
+ "@react-bits": "https://reactbits.dev/r/{name}.json"
22
+ }
20
23
  }
@@ -328,6 +328,27 @@ export const bootstrap_nextjs_shadcn = tool({
328
328
  ]
329
329
  for (const f of standardFiles) copyFileSafe(f)
330
330
 
331
+ // Post-copy: garantizar que components.json del target tenga los registries
332
+ // externos preconfigurados (idempotente: si ya existen, no los duplica).
333
+ const targetComponentsJson = path.resolve(targetPath, "components.json")
334
+ if (fs.existsSync(targetComponentsJson)) {
335
+ try {
336
+ const cfg = JSON.parse(fs.readFileSync(targetComponentsJson, "utf8"))
337
+ if (!cfg.registries) cfg.registries = {}
338
+ if (!cfg.registries["@react-bits"]) {
339
+ cfg.registries["@react-bits"] = "https://reactbits.dev/r/{name}.json"
340
+ fs.writeFileSync(
341
+ targetComponentsJson,
342
+ JSON.stringify(cfg, null, 2),
343
+ "utf8"
344
+ )
345
+ filesCopied.push("components.json (registries.@react-bits inyectado)")
346
+ }
347
+ } catch (e) {
348
+ // best-effort: si falla el parse, no bloqueamos el bootstrap
349
+ }
350
+ }
351
+
331
352
  if (targetDir !== ".") {
332
353
  const configFiles = [
333
354
  "tsconfig.json",