cdd-cli 3.2.2 → 4.0.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.
Files changed (74) hide show
  1. package/.~lock.ROADMAP.md# +1 -0
  2. package/CHANGELOG.md +51 -0
  3. package/README.es.md +198 -0
  4. package/README.md +81 -23
  5. package/ROADMAP.md +647 -0
  6. package/coverage/clover.xml +400 -519
  7. package/coverage/coverage-final.json +16 -20
  8. package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +400 -0
  9. package/coverage/lcov-report/ControlsHUD.jsx.html +256 -0
  10. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +31 -13
  11. package/coverage/lcov-report/components/PromptField.jsx.html +4 -4
  12. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +37 -13
  13. package/coverage/lcov-report/components/index.html +14 -14
  14. package/coverage/lcov-report/helpers/constants.js.html +87 -33
  15. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +40 -40
  16. package/coverage/lcov-report/helpers/dockerHubService.js.html +310 -0
  17. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +1 -1
  18. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +1 -1
  19. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +1 -1
  20. package/coverage/lcov-report/helpers/imageNameUtils.js.html +18 -18
  21. package/coverage/lcov-report/helpers/index.html +77 -62
  22. package/coverage/lcov-report/helpers/logger.js.html +60 -60
  23. package/coverage/lcov-report/helpers/safeCall.js.html +14 -14
  24. package/coverage/lcov-report/helpers/validationHelpers.js.html +52 -52
  25. package/coverage/lcov-report/hooks/creation/index.html +19 -19
  26. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +4 -4
  27. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +225 -48
  28. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +5 -5
  29. package/coverage/lcov-report/hooks/debug/index.html +1 -1
  30. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +7 -7
  31. package/coverage/lcov-report/hooks/index.html +15 -15
  32. package/coverage/lcov-report/hooks/navigation/index.html +1 -1
  33. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +6 -6
  34. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +13 -13
  35. package/coverage/lcov-report/hooks/useControls.js.html +82 -40
  36. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +5 -5
  37. package/coverage/lcov-report/hooks/useExitHandler.js.html +6 -6
  38. package/coverage/lcov-report/index.html +38 -68
  39. package/coverage/lcov.info +742 -981
  40. package/dist/App.js +7 -1
  41. package/dist/components/ContainerCreationPrompt.js +26 -10
  42. package/dist/components/ControlsHUD.js +106 -0
  43. package/dist/components/SuggestionPanel.js +16 -7
  44. package/dist/helpers/constants.js +60 -21
  45. package/dist/helpers/dockerHubService.js +111 -0
  46. package/dist/helpers/logger.js +1 -1
  47. package/dist/hooks/creation/useContainerActions.js +22 -5
  48. package/dist/hooks/creation/useContainerCreation.js +395 -107
  49. package/dist/hooks/useContainerCommandRouter.js +1 -1
  50. package/dist/hooks/useControls.js +23 -7
  51. package/dist/index.js +7 -0
  52. package/package.json +1 -1
  53. package/src/App.jsx +6 -0
  54. package/src/components/ContainerCreationPrompt.jsx +15 -5
  55. package/src/components/ControlsHUD.jsx +66 -0
  56. package/src/components/SuggestionPanel.jsx +10 -2
  57. package/src/helpers/constants.js +38 -20
  58. package/src/helpers/dockerHubService.js +75 -0
  59. package/src/helpers/logger.js +1 -2
  60. package/src/hooks/creation/useContainerActions.js +19 -6
  61. package/src/hooks/creation/useContainerCreation.js +224 -87
  62. package/src/hooks/useContainerCommandRouter.js +1 -1
  63. package/src/hooks/useControls.js +24 -8
  64. package/src/index.js +4 -0
  65. package/test/ContainerCreationPrompt.dom.test.js +245 -0
  66. package/test/ControlsHUD.dom.test.js +125 -0
  67. package/test/SuggestionPanel.dom.test.js +30 -0
  68. package/test/constants.test.js +7 -0
  69. package/test/dockerHubService.test.js +213 -0
  70. package/test/logger.test.js +58 -0
  71. package/test/resolveImageTag.test.js +26 -0
  72. package/test/useContainerCreation.dom.test.js +306 -3
  73. package/test/useContainerCreationHub.dom.test.js +184 -0
  74. package/test/useControls.dom.test.js +212 -6
package/ROADMAP.md ADDED
@@ -0,0 +1,647 @@
1
+ # 🗺️ CDD — CLI Docker Dashboard: Roadmap de Desarrollo
2
+
3
+ > **Versión actual:** v4.0.0
4
+ > **Fecha:** 2026-04-28
5
+ > **Proyecto:** CDD es un dashboard TUI (Terminal UI) para gestionar contenedores Docker desde la línea de comandos. Construido con React/Ink, permite inspeccionar, iniciar, detener, crear y monitorear contenedores sin salir de la terminal.
6
+
7
+ ---
8
+
9
+ ## 📋 Resumen del Roadmap
10
+
11
+ | Fase | Versión | Foco Principal | Features incluidas |
12
+ |------|---------|---------------|-------------------|
13
+ | **Fase 0** | — | Deuda Técnica (Tests) | 8A, 8B, 8C, 8D |
14
+ | **Fase 1** | v4.1.0 | Navegación y UX crítica | 3A, 3B, 3D |
15
+ | **Fase 2** | v4.2.0 | Observabilidad y salud | 2B, 2C, 2D, 3C |
16
+ | **Fase 3** | v4.3.0 | Wizard de Creación mejorado | 1A, 1B, 1C |
17
+ | **Fase 4** | v5.0.0 | Gestión de Imágenes + Conectividad | 4A, 4B, 4C, 5B |
18
+ | **Backlog** | — | Ideas de baja prioridad | 2A, 5A, 6.x, 1D, 4D |
19
+
20
+ ---
21
+
22
+ ## 🚨 Fase 0 — Deuda Técnica (Prerrequisito bloqueante)
23
+
24
+ > **Objetivo:** Cubrir con tests los módulos críticos antes de agregar cualquier feature nueva. Sin esta base, cada iteración rompe cosas silenciosamente.
25
+
26
+ Esta fase no tiene versión de release asociada — es trabajo de infraestructura de calidad que debe completarse antes de la v4.1.0.
27
+
28
+ ---
29
+
30
+ ### 🔴 8A — Tests: `containerStats.js`
31
+
32
+ **Prioridad:** 🟠 Muy Alta
33
+
34
+ **Por qué es crítico:** Este módulo consume la API de stats de Docker en tiempo real (streaming). Sin tests, cualquier cambio en el formato de datos del daemon o en la lógica de parsing puede romper silenciosamente las métricas mostradas al usuario.
35
+
36
+ **Sub-tareas técnicas:**
37
+ - [ ] Identificar todas las funciones exportadas por `containerStats.js`
38
+ - [ ] Mockear el cliente Docker (`dockerode` o equivalente) para respuestas de stream
39
+ - [ ] Escribir test: parseo correcto de CPU % desde datos crudos del daemon
40
+ - [ ] Escribir test: parseo correcto de memoria usada / límite
41
+ - [ ] Escribir test: manejo de contenedor detenido (stats vacíos o null)
42
+ - [ ] Escribir test: manejo de error en stream (container no encontrado, daemon caído)
43
+ - [ ] Verificar cobertura >= 80% del módulo
44
+
45
+ **Criterio de aceptación:**
46
+ - Todos los tests pasan en CI
47
+ - Los casos de error (container inexistente, stream interrumpido) están cubiertos
48
+ - No hay lógica de transformación de datos sin test
49
+
50
+ ---
51
+
52
+ ### 🔴 8B — Tests: `containerLogs.js`
53
+
54
+ **Prioridad:** 🟠 Muy Alta
55
+
56
+ **Por qué es crítico:** Los logs son uno de los flujos más usados del TUI. Este módulo maneja streaming de logs, buffering y posiblemente filtrado. Un bug aquí afecta directamente la experiencia de depuración del usuario.
57
+
58
+ **Sub-tareas técnicas:**
59
+ - [ ] Identificar funciones exportadas: fetch de logs, streaming, parsing de timestamps
60
+ - [ ] Mockear stream de logs de Docker con datos sintéticos (stdout/stderr multiplex)
61
+ - [ ] Escribir test: lectura de logs con formato TTY
62
+ - [ ] Escribir test: lectura de logs sin TTY (stream multiplexado con header)
63
+ - [ ] Escribir test: logs vacíos (contenedor nuevo sin salida)
64
+ - [ ] Escribir test: truncado/límite de líneas si existe lógica de límite
65
+ - [ ] Escribir test: error al acceder a logs de contenedor inexistente
66
+
67
+ **Criterio de aceptación:**
68
+ - Los dos modos de stream (TTY y multiplexado) están testeados
69
+ - El módulo se puede importar e invocar sin un daemon real corriendo
70
+ - Cobertura >= 80%
71
+
72
+ ---
73
+
74
+ ### 🔴 8C — Tests: `imageUtils.js`
75
+
76
+ **Prioridad:** 🟠 Muy Alta
77
+
78
+ **Por qué es crítico:** Las utilidades de imagen son reutilizadas en múltiples vistas (lista de contenedores, wizard de creación, gestión de imágenes). Un bug en formateo o filtrado afecta múltiples partes de la UI.
79
+
80
+ **Sub-tareas técnicas:**
81
+ - [ ] Mapear todas las funciones utilitarias del módulo
82
+ - [ ] Escribir test: formateo de nombres de imagen (tag, digest, sin tag → "latest")
83
+ - [ ] Escribir test: cálculo/formateo de tamaño de imagen (bytes → MB/GB)
84
+ - [ ] Escribir test: filtrado o búsqueda de imágenes por nombre
85
+ - [ ] Escribir test: manejo de imágenes sin nombre (`<none>:<none>`)
86
+ - [ ] Escribir test: parseo de fecha de creación
87
+
88
+ **Criterio de aceptación:**
89
+ - Todas las funciones exportadas tienen al menos un test positivo y uno negativo
90
+ - Los edge cases (`<none>` tags, tamaños cero, fechas inválidas) están cubiertos
91
+
92
+ ---
93
+
94
+ ### 🔴 8D — Tests: `ContainerRow.jsx`
95
+
96
+ **Prioridad:** 🟠 Muy Alta
97
+
98
+ **Por qué es crítico:** `ContainerRow` es el componente más renderizado del TUI — aparece por cada contenedor en la lista. Cualquier regresión visual o de estado en este componente es inmediatamente visible para el usuario.
99
+
100
+ **Sub-tareas técnicas:**
101
+ - [ ] Configurar testing de componentes Ink/React (ink-testing-library o equivalente)
102
+ - [ ] Escribir test: render de contenedor en estado `running`
103
+ - [ ] Escribir test: render de contenedor en estado `exited` / `stopped`
104
+ - [ ] Escribir test: render de contenedor seleccionado (highlight / foco activo)
105
+ - [ ] Escribir test: truncado de nombre largo de contenedor
106
+ - [ ] Escribir test: muestra correcta de imagen y puerto(s)
107
+ - [ ] Escribir snapshot test para detectar regresiones de layout
108
+
109
+ **Criterio de aceptación:**
110
+ - El componente puede renderizarse en tests sin un terminal real
111
+ - Los tres estados principales (running, exited, selected) tienen cobertura
112
+ - Snapshot actualizado y en control de versiones
113
+
114
+ ---
115
+
116
+ ## 🧭 Fase 1 — v4.1.0: Navegación y UX crítica
117
+
118
+ > **Objetivo:** Hacer que encontrar y manejar contenedores sea rápido y fluido, independientemente de cuántos haya corriendo.
119
+
120
+ ---
121
+
122
+ ### 🔴 3A — Filtro de contenedores
123
+
124
+ **Prioridad:** 🔴 Suprema
125
+
126
+ **Descripción:** El usuario debe poder filtrar la lista de contenedores en tiempo real escribiendo texto. El filtro debe aplicarse sobre nombre del contenedor, imagen y estado. Es la feature de mayor impacto en usabilidad para usuarios con muchos contenedores.
127
+
128
+ **Sub-tareas técnicas:**
129
+ - [ ] Agregar estado `filterQuery` en el componente de lista de contenedores
130
+ - [ ] Implementar keybinding para activar el modo filtro (ej. `/` o `f`)
131
+ - [ ] Renderizar un input de texto en el footer/header cuando el modo filtro está activo
132
+ - [ ] Filtrar la lista derivada en tiempo real mientras el usuario escribe (case-insensitive)
133
+ - [ ] Aplicar filtro sobre: nombre del contenedor, nombre de imagen, estado
134
+ - [ ] Permitir salir del modo filtro con `Esc` y limpiar el query
135
+ - [ ] Mantener la selección actual si el contenedor seleccionado sigue visible tras filtrar
136
+ - [ ] Mostrar contador de resultados (ej. `3 / 12 contenedores`)
137
+
138
+ **Criterio de aceptación:**
139
+ - Escribir `/web` en la lista muestra solo contenedores cuyo nombre o imagen contiene "web"
140
+ - `Esc` limpia el filtro y restaura la lista completa
141
+ - El contador de resultados es preciso
142
+ - Con lista vacía tras filtrar, se muestra mensaje "Sin resultados para '{query}'"
143
+
144
+ **Testing:**
145
+ - Test: filtrar por nombre exacto devuelve solo ese contenedor
146
+ - Test: filtrar por imagen devuelve contenedores que usan esa imagen
147
+ - Test: filtro vacío devuelve todos los contenedores
148
+ - Test: `Esc` limpia el estado de filtro
149
+
150
+ ---
151
+
152
+ ### 🟡 3B — Scroll real en logs (todos los logs disponibles)
153
+
154
+ **Prioridad:** 🟡 Alta
155
+
156
+ **Descripción:** El panel de logs debe mostrar TODOS los logs disponibles del contenedor (no solo los últimos N), con scroll real que permita navegar hacia arriba y abajo. El usuario quiere poder ir al inicio del log de un contenedor y leer la historia completa, como hace `docker logs` sin `--tail`.
157
+
158
+ **Sub-tareas técnicas:**
159
+ - [ ] Cambiar la petición de logs para usar `tail: 'all'` en lugar de un límite fijo
160
+ - [ ] Implementar buffer de líneas en el estado del componente de logs
161
+ - [ ] Agregar índice de scroll (línea superior visible) en el estado
162
+ - [ ] Implementar scroll con teclas `↑` / `↓` (de línea en línea)
163
+ - [ ] Implementar scroll con `PgUp` / `PgDn` (saltos de página)
164
+ - [ ] Implementar `Home` / `End` para ir al inicio/fin del buffer
165
+ - [ ] Calcular dinámicamente cuántas líneas caben en el viewport actual
166
+ - [ ] Agregar indicador visual de posición (ej. `línea 120 / 3042` en el footer)
167
+ - [ ] Para logs en streaming (follow): auto-scroll al final a menos que el usuario haya scrolleado hacia arriba
168
+
169
+ **Criterio de aceptación:**
170
+ - `docker logs` de un contenedor con 5000 líneas muestra todas las 5000 líneas navegables
171
+ - El usuario puede llegar al inicio con `Home` y al final con `End`
172
+ - Al activar "follow" desde el inicio, el scroll sigue bajando automáticamente
173
+ - Si el usuario scrollea hacia arriba en modo follow, el auto-scroll se pausa
174
+
175
+ **Testing:**
176
+ - Test: buffer contiene todas las líneas recibidas del stream
177
+ - Test: scroll respeta los límites (no pasa de 0 ni del máximo)
178
+ - Test: auto-scroll se desactiva cuando el usuario scrollea manualmente
179
+
180
+ ---
181
+
182
+ ### 🟠 3D — Ordenamiento de la lista de contenedores
183
+
184
+ **Prioridad:** 🟠 Muy Alta
185
+
186
+ **Descripción:** El usuario debe poder ordenar la lista de contenedores por diferentes criterios: nombre, estado, imagen, y tiempo de creación/uptime. El orden debe ser configurable con atajos de teclado y debe persistir durante la sesión.
187
+
188
+ **Sub-tareas técnicas:**
189
+ - [ ] Definir los criterios de ordenamiento soportados: `name`, `status`, `image`, `created`
190
+ - [ ] Agregar estado `sortBy` y `sortDirection` (asc/desc) en el componente de lista
191
+ - [ ] Implementar función de comparación para cada criterio
192
+ - [ ] Agregar keybindings para ciclar entre criterios (ej. `s` para ciclar sort)
193
+ - [ ] Mostrar el criterio activo y la dirección en el header de la lista (ej. `↑ nombre`)
194
+ - [ ] Toggle de dirección al presionar el mismo criterio dos veces
195
+ - [ ] El sort debe ser compatible con el filtro activo (3A): ordenar el resultado filtrado
196
+
197
+ **Criterio de aceptación:**
198
+ - Presionar el keybinding ordena la lista visualmente de forma inmediata
199
+ - Presionar el mismo criterio invierte el orden (asc → desc)
200
+ - El indicador de sort activo es visible en la UI
201
+ - Filtro y sort funcionan juntos sin conflicto
202
+
203
+ **Testing:**
204
+ - Test: ordenar por nombre produce lista en orden alfabético
205
+ - Test: ordenar por estado agrupa running antes de exited
206
+ - Test: toggle de dirección invierte el orden correctamente
207
+ - Test: sort se aplica sobre la lista ya filtrada
208
+
209
+ ---
210
+
211
+ ### 📐 Testing — Fase 1
212
+
213
+ Todos los features de esta fase tocan lógica de estado y transformación de datos. Priorizar:
214
+ - Tests de la función de filtrado (pura, sin UI)
215
+ - Tests de la función de sort (pura, sin UI)
216
+ - Tests del buffer de scroll de logs
217
+ - Actualizar snapshots de `ContainerRow` si el indicador de sort afecta el header
218
+
219
+ ---
220
+
221
+ ## 🔬 Fase 2 — v4.2.0: Observabilidad y Salud de Contenedores
222
+
223
+ > **Objetivo:** Dar al usuario visibilidad real del estado interno de sus contenedores: métricas expandidas, health checks y alertas de cambio de estado.
224
+
225
+ ---
226
+
227
+ ### 🟡 2B — Stats expandidos (Disk I/O, memoria en MB, tasa de red)
228
+
229
+ **Prioridad:** 🟡 Alta
230
+
231
+ **Descripción:** El panel de estadísticas actual muestra métricas básicas. Debe expandirse para mostrar Disk I/O (read/write en bytes/s), uso de memoria en MB y GB (no solo porcentaje), y tasa de red (inbound/outbound en KB/s o MB/s). Los datos provienen de la misma API de stats de Docker.
232
+
233
+ **Sub-tareas técnicas:**
234
+ - [ ] Revisar el payload completo de `/containers/{id}/stats` y mapear campos disponibles
235
+ - [ ] Extraer `blkio_stats` para Disk I/O (read/write acumulados → calcular delta entre muestras)
236
+ - [ ] Extraer `memory_stats.usage` y `memory_stats.limit` y formatear en MB/GB
237
+ - [ ] Extraer `networks` para calcular rx_bytes/tx_bytes delta entre muestras
238
+ - [ ] Implementar función de cálculo de tasa (delta / intervalo de muestreo)
239
+ - [ ] Diseñar layout del panel de stats con las nuevas métricas
240
+ - [ ] Formatear automáticamente (KB, MB, GB según magnitud)
241
+
242
+ **Criterio de aceptación:**
243
+ - El panel muestra: CPU%, Memoria (usado/límite en MB), Disk Read, Disk Write, Net In, Net Out
244
+ - Los valores de tasa (Disk/Net) son calculados correctamente como deltas entre dos muestras
245
+ - El formateo cambia dinámicamente (ej. "1.2 MB/s" no "1234567 B/s")
246
+
247
+ **Testing:**
248
+ - Test: cálculo de delta de bytes entre dos muestras consecutivas
249
+ - Test: formateo de bytes a unidad legible
250
+ - Test: manejo de primera muestra (no hay delta previo → mostrar 0 o "—")
251
+
252
+ ---
253
+
254
+ ### 🟡 2C — Health Check visible
255
+
256
+ **Prioridad:** 🟡 Alta
257
+
258
+ **Descripción:** Si un contenedor tiene un health check configurado, el TUI debe mostrar su estado (`healthy`, `unhealthy`, `starting`) de forma destacada. También debe mostrar el resultado del último chequeo (exit code y output). Muchos usuarios no saben que sus contenedores están `unhealthy` hasta que algo falla.
259
+
260
+ **Sub-tareas técnicas:**
261
+ - [ ] Leer `State.Health` del endpoint `GET /containers/{id}/json`
262
+ - [ ] Mostrar badge de estado de health en `ContainerRow` cuando existe health check
263
+ - [ ] En el panel de detalle, mostrar: estado, último chequeo (timestamp), output del último test
264
+ - [ ] Diferenciar visualmente: sin health check / healthy / starting / unhealthy
265
+ - [ ] Para `unhealthy`, mostrar el output del log del health check para diagnóstico
266
+
267
+ **Criterio de aceptación:**
268
+ - Un contenedor `unhealthy` muestra un indicador visual distinto en la lista
269
+ - El panel de detalle muestra el output del último health check fallido
270
+ - Un contenedor sin health check configurado no muestra el badge
271
+
272
+ **Testing:**
273
+ - Test: componente recibe `Health: null` → no renderiza badge
274
+ - Test: componente recibe `Health.Status: "unhealthy"` → renderiza badge de error
275
+ - Test: output del último log se muestra correctamente truncado
276
+
277
+ ---
278
+
279
+ ### 🟡 2D — Notificaciones de estado
280
+
281
+ **Prioridad:** 🟡 Alta
282
+
283
+ **Descripción:** Cuando un contenedor cambia de estado (ej. pasa de `running` a `exited`, o un health check pasa a `unhealthy`), el TUI debe mostrar una notificación temporal no bloqueante (tipo toast) en la esquina de la pantalla. Esto permite al usuario seguir trabajando y ser alertado de cambios sin que el UI quede bloqueado.
284
+
285
+ **Sub-tareas técnicas:**
286
+ - [ ] Implementar sistema de polling de estados de contenedores (o usar Docker events API `GET /events`)
287
+ - [ ] Preferir Docker events API para reactividad real vs polling
288
+ - [ ] Crear componente `Notification` / `Toast` con texto, tipo (info/warn/error) y auto-dismiss
289
+ - [ ] Implementar cola de notificaciones con tiempo de vida configurable (ej. 5 segundos)
290
+ - [ ] Disparar notificación cuando: `running → exited`, `healthy → unhealthy`, contenedor creado/eliminado externamente
291
+ - [ ] Posicionar el toast en esquina inferior derecha sin bloquear la lista
292
+
293
+ **Criterio de aceptación:**
294
+ - Un contenedor que se detiene externamente (`docker stop` en otra terminal) genera un toast en el TUI en menos de 2 segundos
295
+ - El toast desaparece automáticamente tras 5 segundos
296
+ - Múltiples notificaciones simultáneas se apilan sin solaparse
297
+ - Las notificaciones no interrumpen el flujo de teclado del usuario
298
+
299
+ **Testing:**
300
+ - Test: la cola de notificaciones agrega y remueve correctamente
301
+ - Test: el componente Toast se renderiza con los tipos correctos (info/warn/error)
302
+ - Test: dos notificaciones se apilan sin reemplazarse
303
+
304
+ ---
305
+
306
+ ### 🟢 3C — Filtro de texto en logs
307
+
308
+ **Prioridad:** 🟢 Media
309
+
310
+ **Descripción:** Dentro del panel de logs (implementado en 3B), el usuario puede activar un filtro de texto para mostrar solo las líneas que contienen el patrón buscado. Compatible con el scroll completo de 3B — filtra el buffer completo, no solo las líneas visibles.
311
+
312
+ **Sub-tareas técnicas:**
313
+ - [ ] Agregar keybinding para activar filtro de logs (ej. `f` dentro del panel de logs)
314
+ - [ ] Renderizar input de texto para el patrón de búsqueda
315
+ - [ ] Filtrar el buffer completo de líneas contra el patrón (case-insensitive por defecto)
316
+ - [ ] Resaltar en cada línea visible el texto que coincide con el patrón
317
+ - [ ] Mostrar contador de líneas coincidentes (ej. `47 / 3042 líneas`)
318
+ - [ ] `Esc` limpia el filtro y restaura el buffer completo
319
+ - [ ] Si el filtro está activo, el scroll opera sobre el buffer filtrado
320
+
321
+ **Criterio de aceptación:**
322
+ - Escribir "ERROR" muestra solo líneas con "ERROR", resaltadas
323
+ - El contador refleja las líneas coincidentes sobre el total
324
+ - `Esc` restaura todas las líneas y el scroll vuelve a la posición anterior
325
+ - El filtro opera sobre el buffer completo, no solo las líneas visibles
326
+
327
+ **Testing:**
328
+ - Test: filtro vacío devuelve el buffer completo
329
+ - Test: filtro "ERROR" sobre buffer con 3 líneas ERROR devuelve exactamente 3
330
+ - Test: el resaltado de texto funciona correctamente con el patrón
331
+
332
+ ---
333
+
334
+ ### 📐 Testing — Fase 2
335
+
336
+ - Tests para la lógica de cálculo de deltas de stats (2B) — puramente funcional
337
+ - Tests del parser de health check status (2C)
338
+ - Tests de la cola de notificaciones (2D) — insert, auto-dismiss, límite de cola
339
+ - Tests del filtro de logs sobre buffer (3C)
340
+
341
+ ---
342
+
343
+ ## 🧙 Fase 3 — v4.3.0: Wizard de Creación Mejorado
344
+
345
+ > **Objetivo:** Hacer que crear contenedores desde el TUI sea tan completo y claro como usar `docker run` con sus flags más importantes.
346
+
347
+ ---
348
+
349
+ ### 🟡 1A — Volúmenes en el Wizard
350
+
351
+ **Prioridad:** 🟡 Alta
352
+
353
+ **Descripción:** El wizard de creación de contenedores debe ofrecer al usuario elegir explícitamente la estrategia de persistencia de datos: volumen nombrado (datos persisten entre reinicios), bind mount (carpeta local), `tmpfs` (en memoria, datos volátiles) o ninguno. Esto evita el error común de crear contenedores sin entender qué pasa con sus datos al detenerlos.
354
+
355
+ **Sub-tareas técnicas:**
356
+ - [ ] Agregar paso "Almacenamiento" al wizard con selector de tipo: `none`, `named volume`, `bind mount`, `tmpfs`
357
+ - [ ] Para `named volume`: input para nombre del volumen (o auto-generar basado en el nombre del contenedor) y ruta del mountpoint dentro del contenedor
358
+ - [ ] Para `bind mount`: input para ruta local (host) y ruta destino en el contenedor; validar que la ruta local existe
359
+ - [ ] Para `tmpfs`: input para ruta del mountpoint en el contenedor; input opcional para tamaño máximo
360
+ - [ ] Traducir la selección a los flags correctos en el payload de creación (`Volumes`, `HostConfig.Binds`, `HostConfig.Mounts`)
361
+ - [ ] Mostrar resumen de la configuración elegida en el paso final del wizard
362
+
363
+ **Criterio de aceptación:**
364
+ - Crear un contenedor con volumen nombrado genera un volumen persistente verificable con `docker volume ls`
365
+ - Crear con `tmpfs` no genera volumen persistente
366
+ - El resumen final del wizard muestra la configuración de almacenamiento seleccionada
367
+ - Un bind mount a ruta inexistente muestra error de validación antes de continuar
368
+
369
+ **Testing:**
370
+ - Test: traducción de selección `named volume` al payload de API correcto
371
+ - Test: traducción de `tmpfs` al payload correcto
372
+ - Test: validación de ruta local en bind mount (path no existe → error)
373
+ - Test: `none` no incluye campos de volumen en el payload
374
+
375
+ ---
376
+
377
+ ### 🟡 1B — Redes en el Wizard
378
+
379
+ **Prioridad:** 🟡 Alta
380
+
381
+ **Descripción:** El wizard debe permitir al usuario elegir a qué red(es) conectar el contenedor al crearlo. Debe listar las redes disponibles en el daemon, permitir seleccionar una o crear una nueva red, y permitir configurar aliases dentro de la red. Esto es clave para que los contenedores se comuniquen entre sí.
382
+
383
+ **Sub-tareas técnicas:**
384
+ - [ ] Agregar paso "Red" al wizard
385
+ - [ ] Listar redes existentes con `GET /networks` y presentarlas en un selector
386
+ - [ ] Mostrar tipo de red junto al nombre (bridge, host, none, overlay)
387
+ - [ ] Opción para no conectar a ninguna red (`--network none`)
388
+ - [ ] Opción para crear una nueva red bridge con nombre personalizado (invocar `POST /networks/create` antes de crear el contenedor)
389
+ - [ ] Input opcional para alias dentro de la red
390
+ - [ ] Traducir selección al campo `NetworkingConfig` del payload de creación
391
+
392
+ **Criterio de aceptación:**
393
+ - El contenedor creado aparece en la red seleccionada (verificable con `docker network inspect`)
394
+ - Si se crea una red nueva, esta existe antes de que el contenedor sea creado
395
+ - Seleccionar `none` crea el contenedor sin interfaces de red
396
+
397
+ **Testing:**
398
+ - Test: el listado de redes consume correctamente el endpoint de Docker
399
+ - Test: traducción de selección al campo `NetworkingConfig`
400
+ - Test: flujo de creación de red nueva → asociación al contenedor
401
+
402
+ ---
403
+
404
+ ### 🟢 1C — Restart Policy en el Wizard
405
+
406
+ **Prioridad:** 🟢 Media
407
+
408
+ **Descripción:** El wizard debe incluir un selector de política de reinicio del contenedor: `no` (nunca reiniciar), `always` (siempre), `on-failure` (solo si sale con error, con límite de reintentos opcional), `unless-stopped`. Este campo es simple pero evita confusión a usuarios que esperan que sus contenedores sobrevivan a reinicios del daemon.
409
+
410
+ **Sub-tareas técnicas:**
411
+ - [ ] Agregar campo "Restart Policy" al wizard (selector entre las 4 opciones)
412
+ - [ ] Para `on-failure`: mostrar input adicional para `MaximumRetryCount` (0 = sin límite)
413
+ - [ ] Mostrar descripción breve de cada opción junto al nombre
414
+ - [ ] Traducir a `HostConfig.RestartPolicy` en el payload
415
+
416
+ **Criterio de aceptación:**
417
+ - Seleccionar `always` crea el contenedor con `RestartPolicy.Name: "always"`
418
+ - Seleccionar `on-failure` con 3 reintentos crea el contenedor con `MaximumRetryCount: 3`
419
+ - La descripción de cada opción es visible en el UI durante la selección
420
+
421
+ **Testing:**
422
+ - Test: traducción de cada opción al campo del payload correcto
423
+ - Test: `on-failure` incluye `MaximumRetryCount` en el payload
424
+ - Test: valor por defecto es `no`
425
+
426
+ ---
427
+
428
+ ### 📐 Testing — Fase 3
429
+
430
+ - Tests de cada paso del wizard por separado (sin necesidad de correr el wizard completo)
431
+ - Tests de la función que construye el payload final de creación con todas las opciones nuevas
432
+ - Test de integración del wizard end-to-end con un mock del cliente Docker
433
+
434
+ ---
435
+
436
+ ## 🖼️ Fase 4 — v5.0.0: Gestión de Imágenes y Conectividad
437
+
438
+ > **Objetivo:** Dar al usuario control sobre imágenes locales y abrir el TUI a entornos Docker remotos.
439
+
440
+ ---
441
+
442
+ ### 🟢 4A — Vista de imágenes locales
443
+
444
+ **Prioridad:** 🟢 Media
445
+
446
+ **Descripción:** Agregar una vista dedicada que liste todas las imágenes Docker locales con su nombre, tag, tamaño y fecha de creación. Debe permitir eliminar imágenes no usadas y ver qué contenedores usan cada imagen.
447
+
448
+ **Sub-tareas técnicas:**
449
+ - [ ] Agregar tab / vista "Imágenes" navegable desde el menú principal (keybinding `i` o tab)
450
+ - [ ] Consumir `GET /images/json` para listar imágenes
451
+ - [ ] Renderizar tabla: nombre:tag, ID (short), tamaño, fecha
452
+ - [ ] Acción de eliminar imagen seleccionada (`DELETE /images/{id}`) con confirmación
453
+ - [ ] Manejar error de imagen en uso (Docker retorna 409 → mostrar mensaje claro)
454
+ - [ ] Mostrar imágenes sin tag (`<none>:<none>`) con label "dangling"
455
+
456
+ **Criterio de aceptación:**
457
+ - La vista lista todas las imágenes locales incluyendo las dangling
458
+ - Eliminar una imagen en uso muestra un mensaje de error explicativo, no un crash
459
+ - Eliminar una imagen no usada la remueve y refresca la lista
460
+
461
+ **Testing:**
462
+ - Test: render de imagen sin tag (dangling)
463
+ - Test: error 409 al borrar imagen en uso → mensaje claro, no excepción
464
+
465
+ ---
466
+
467
+ ### 🟢 4B — Pull de imagen sin crear contenedor
468
+
469
+ **Prioridad:** 🟢 Media
470
+
471
+ **Descripción:** Desde la vista de imágenes (4A), el usuario puede hacer pull de cualquier imagen de Docker Hub o registry privado sin necesidad de crear un contenedor. Debe mostrar el progreso del pull en tiempo real.
472
+
473
+ **Sub-tareas técnicas:**
474
+ - [ ] Agregar acción "Pull imagen" en la vista de imágenes
475
+ - [ ] Modal/panel con input para el nombre de la imagen (ej. `nginx:latest`)
476
+ - [ ] Consumir `POST /images/create` con stream de respuesta para mostrar progreso
477
+ - [ ] Parsear las capas del progreso del pull y mostrar barra o lista de estado por capa
478
+ - [ ] Al finalizar, agregar la imagen a la lista sin necesidad de refetch completo
479
+ - [ ] Manejar error de imagen no encontrada o autenticación fallida
480
+
481
+ **Criterio de aceptación:**
482
+ - El usuario puede escribir `postgres:16` y ver el pull en tiempo real
483
+ - Al finalizar el pull, la imagen aparece en la lista
484
+ - Si el nombre de imagen no existe, se muestra error "imagen no encontrada"
485
+
486
+ **Testing:**
487
+ - Test: parseo del stream de progreso de pull (por capas)
488
+ - Test: manejo de error 404 (imagen no encontrada en registry)
489
+
490
+ ---
491
+
492
+ ### 🟢 4C — Export `docker run` / compose
493
+
494
+ **Prioridad:** 🟢 Media
495
+
496
+ **Descripción:** Para cualquier contenedor existente, el usuario puede generar el comando `docker run` equivalente o un fragmento de `docker-compose.yml` que recree ese contenedor con la misma configuración. Útil para documentación y reproducibilidad.
497
+
498
+ **Sub-tareas técnicas:**
499
+ - [ ] Leer la configuración completa del contenedor con `GET /containers/{id}/json`
500
+ - [ ] Implementar función `toDockerRun(containerInfo)` que genere el string del comando
501
+ - [ ] Implementar función `toComposeService(containerInfo)` que genere el YAML del servicio
502
+ - [ ] Incluir: imagen, ports, volumes, env vars, network, restart policy, nombre
503
+ - [ ] Modal con el output generado y opción de copiar al portapapeles (si el entorno lo soporta)
504
+ - [ ] Advertir si algún campo no es representable en el formato exportado
505
+
506
+ **Criterio de aceptación:**
507
+ - El `docker run` generado, al ejecutarse, crea un contenedor funcionalmente equivalente
508
+ - El fragmento de compose es YAML válido
509
+ - Variables de entorno sensibles no son ocultadas pero sí se advierte al usuario
510
+
511
+ **Testing:**
512
+ - Test: `toDockerRun` con un contenedor con ports, volumes y env genera el comando correcto
513
+ - Test: `toComposeService` genera YAML válido y parseable
514
+ - Test: contenedor sin puertos no incluye flag `-p` en el output
515
+
516
+ ---
517
+
518
+ ### 🟢 5B — Multi-contexto Docker
519
+
520
+ **Prioridad:** 🟢 Media
521
+
522
+ **Descripción:** El TUI debe leer los contextos de Docker (`~/.docker/contexts/`) y permitir al usuario cambiar entre ellos desde una interfaz en el header. Al cambiar de contexto, todos los datos de contenedores, imágenes y redes se recargan desde el nuevo daemon.
523
+
524
+ **Sub-tareas técnicas:**
525
+ - [ ] Leer contextos disponibles desde `~/.docker/contexts/meta/` o via `docker context ls --format json`
526
+ - [ ] Mostrar el contexto activo en el header del TUI
527
+ - [ ] Acción para cambiar de contexto (modal con selector)
528
+ - [ ] Al cambiar, reinicializar el cliente Docker con el socket/host del nuevo contexto
529
+ - [ ] Recargar todos los datos desde el nuevo daemon
530
+ - [ ] Manejar error si el contexto seleccionado no está disponible (daemon apagado)
531
+
532
+ **Criterio de aceptación:**
533
+ - El contexto activo es visible en el header
534
+ - Cambiar a un contexto con un daemon remoto muestra los contenedores de ese daemon
535
+ - Si el daemon del contexto no responde, se muestra error sin crashear el TUI
536
+
537
+ **Testing:**
538
+ - Test: parsing de los archivos de contexto de Docker
539
+ - Test: error de conexión al cambiar a contexto no disponible → mensaje claro
540
+
541
+ ---
542
+
543
+ ### 📐 Testing — Fase 4
544
+
545
+ - Tests de `toDockerRun` y `toComposeService` (funciones puras, fáciles de testear)
546
+ - Tests del parser de contextos de Docker
547
+ - Tests del flujo de pull con mock del stream de progreso
548
+
549
+ ---
550
+
551
+ ## 📦 Backlog — Features de Baja Prioridad
552
+
553
+ Estas features no tienen fase asignada. Entran en el backlog para ser consideradas en futuras iteraciones según capacidad y demanda.
554
+
555
+ | ID | Feature | Prioridad | Notas |
556
+ |----|---------|-----------|-------|
557
+ | 2A | Panel Inspect | 🔵 Baja | Vista de `docker inspect` completa formateada como JSON |
558
+ | 5A | Soporte `DOCKER_HOST` | 🔵 Baja | Leer variable de entorno `DOCKER_HOST` para conexiones remotas simples |
559
+ | 6.x | Persistencia y Configuración | 🔵 Muy Baja | Preferencias de usuario, keybindings personalizables, temas |
560
+ | 1D | Recetas guardables | 🔵 Baja | Ver sección de Ideas Exploratorias |
561
+
562
+ ---
563
+
564
+ ## 🔭 Ideas Exploratorias
565
+
566
+ > Estas ideas no tienen versión asignada. Requieren investigación adicional antes de comprometerse a implementarlas.
567
+
568
+ ---
569
+
570
+ ### 💡 4D — Build de imagen custom desde el TUI
571
+
572
+ **Idea:** El usuario puede seleccionar un directorio con un `Dockerfile` (o incluso definir uno básico en el TUI) y hacer build de una imagen directamente desde la interfaz, sin salir a la terminal.
573
+
574
+ **Viabilidad técnica:**
575
+ La API de Docker soporta `POST /build` con un tar del contexto de build. La parte técnica de enviar el build es viable. El desafío real está en la UX: el usuario necesita poder seleccionar un directorio del filesystem (requiere un file picker en TUI), y el output del build (stream de texto con cada paso del Dockerfile) necesita una vista dedicada de progreso.
576
+
577
+ **Complejidad adicional:**
578
+ - File picker en TUI es complejo de implementar con buena UX (Ink no tiene uno nativo)
579
+ - Empaquetar el contexto de build como tar en memoria para mandarlo a la API
580
+ - El stream de build output tiene formato JSON por línea que debe parsearse y mostrarse limpiamente
581
+
582
+ **Riesgos:**
583
+ - Contextos de build grandes (proyectos con `node_modules` sin `.dockerignore`) pueden ser muy lentos o consumir mucha memoria
584
+ - La UX de file picker puede quedar inferior a simplemente usar `docker build` en terminal
585
+ - Requiere que el TUI tenga permisos de lectura al filesystem del usuario
586
+
587
+ **Recomendación:** Explorar primero si existe una librería de file picker para Ink. Si no hay una mantenida, esta feature tiene un costo de UX muy alto. Alternativa: el usuario provee la ruta como string (sin picker visual) — reduce la complejidad significativamente.
588
+
589
+ ---
590
+
591
+ ### 💡 1D — Recetas de contenedores guardables
592
+
593
+ **Idea:** El usuario puede guardar una configuración de contenedor como "receta" (nombre, imagen, volúmenes, redes, env vars) y reutilizarla con un nombre para recrear el mismo contenedor rápidamente.
594
+
595
+ **Viabilidad técnica:**
596
+ Las recetas serían JSON serializado en un archivo de configuración local (ej. `~/.config/cdd/recipes.json`). La lectura y escritura es trivial. El wizard de creación necesitaría un paso de "cargar receta" al inicio y un botón de "guardar como receta" al final.
597
+
598
+ **Riesgos:**
599
+ - Las recetas con variables de entorno pueden contener credenciales → necesidad de advertencias y posiblemente cifrado opcional
600
+ - La migración de recetas entre versiones del schema JSON puede romper recetas antiguas
601
+ - Los usuarios power users probablemente prefieren Docker Compose para esto — la propuesta de valor debe ser clara
602
+
603
+ **Recomendación:** Solo implementar si el wizard de creación (Fase 3) ha sido bien recibido y los usuarios piden explícitamente esta funcionalidad. No es prioritario ahora.
604
+
605
+ ---
606
+
607
+ ### 💡 7A — Docker exec interactivo
608
+
609
+ **Idea:** El usuario puede abrir una shell interactiva dentro de un contenedor corriendo, como `docker exec -it {container} sh`, directamente desde el TUI.
610
+
611
+ **Viabilidad técnica:**
612
+ La API de Docker soporta `POST /containers/{id}/exec` seguido de `POST /exec/{id}/start`. El problema es que esto requiere una terminal interactiva con PTY real, que debe ser manejada por el TUI. Ink/React está diseñado para UIs reactivas, no para hacer passthrough de un PTY arbitrario. Probablemente requiera spawnear un proceso hijo con `child_process.spawn` apuntando al terminal del usuario y "salir" del TUI temporalmente.
613
+
614
+ **Riesgos:**
615
+ - Integrar un PTY interactivo dentro de Ink puede ser estructuralmente incompatible con el modelo de renderizado de React/Ink
616
+ - La alternativa de suspender el TUI y lanzar el exec en el terminal nativo es viable pero requiere gestión de ciclo de vida cuidadosa (restaurar el TUI al salir)
617
+ - Posibles problemas con tamaños de terminal (SIGWINCH) cuando el usuario redimensiona la ventana dentro del exec
618
+
619
+ **Recomendación:** Investigar si `ink` tiene soporte para suspender el renderizado y restaurarlo. Si lo tiene, la implementación es viable. Si no, esta feature requeriría cambios arquitecturales significativos.
620
+
621
+ ---
622
+
623
+ ### 💡 7B — Soporte Docker Compose
624
+
625
+ **Idea:** Detectar archivos `docker-compose.yml` en el directorio actual y ofrecer acciones de compose (up, down, restart por servicio) desde el TUI.
626
+
627
+ **Viabilidad técnica:**
628
+ Docker Compose no tiene una API REST propia — `docker compose` es un plugin de CLI que invoca la API de Docker en secuencia. La opción más pragmática es invocar `docker compose` como proceso hijo. Alternativamente, leer el `docker-compose.yml`, parsear los servicios y orquestar las llamadas a la API de Docker manualmente — esto duplica lógica que ya tiene Compose.
629
+
630
+ **Riesgos:**
631
+ - Depender del CLI de `docker compose` como proceso hijo crea una dependencia externa que puede no estar instalada
632
+ - Parsear y ejecutar compose manualmente es una reimplementación parcial de Compose, con todos los bugs propios
633
+ - El scope de "soporte compose" puede expandirse indefinidamente (redes, profiles, overrides, etc.)
634
+
635
+ **Recomendación:** Si se implementa, usar el CLI de `docker compose` como proceso hijo para las acciones, y parsear el YAML solo para mostrar información (nombres de servicios, imágenes). No reimplementar la lógica de Compose.
636
+
637
+ ---
638
+
639
+ ## 📌 Notas de Versionado
640
+
641
+ - **v4.x** — Mejoras incrementales sobre la base existente. Sin cambios de arquitectura.
642
+ - **v5.0.0** — Contiene cambios que afectan múltiples capas (multi-contexto, vistas nuevas). Justifica bump de versión mayor.
643
+ - Las ideas exploratorias (4D, 7A, 7B, 1D) no tienen versión asignada intencionalmente — su complejidad real se conoce solo después de investigación dedicada.
644
+
645
+ ---
646
+
647
+ *Documento generado el 2026-04-28 — Roadmap v1.0 para CDD v4.0.0*