@saulwade/swl-ses 2.3.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +241 -199
- package/README.md +597 -597
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/agentes/accesibilidad-wcag-swl.md +2 -2
- package/agentes/arquitecto-swl.md +2 -2
- package/agentes/auto-evolucion-swl.md +2 -2
- package/agentes/backend-api-swl.md +2 -2
- package/agentes/backend-csharp-swl.md +2 -2
- package/agentes/backend-go-swl.md +2 -2
- package/agentes/backend-java-swl.md +2 -2
- package/agentes/backend-node-swl.md +2 -2
- package/agentes/backend-python-swl.md +2 -2
- package/agentes/backend-rust-swl.md +2 -2
- package/agentes/backend-workers-swl.md +2 -2
- package/agentes/cloud-infra-swl.md +2 -2
- package/agentes/consolidador-swl.md +2 -2
- package/agentes/datos-swl.md +2 -2
- package/agentes/depurador-swl.md +2 -2
- package/agentes/devops-ci-swl.md +2 -2
- package/agentes/disenador-ui-swl.md +2 -2
- package/agentes/documentador-swl.md +2 -2
- package/agentes/frontend-angular-swl.md +2 -2
- package/agentes/frontend-css-swl.md +2 -2
- package/agentes/frontend-react-swl.md +2 -2
- package/agentes/frontend-swl.md +2 -2
- package/agentes/frontend-tailwind-swl.md +2 -2
- package/agentes/gh-fix-ci-swl.md +275 -275
- package/agentes/implementador-swl.md +2 -2
- package/agentes/investigador-swl.md +2 -2
- package/agentes/investigador-ux-swl.md +2 -2
- package/agentes/llm-apps-swl.md +2 -2
- package/agentes/migrador-swl.md +2 -2
- package/agentes/mobile-android-swl.md +2 -2
- package/agentes/mobile-cross-swl.md +2 -2
- package/agentes/mobile-ios-swl.md +2 -2
- package/agentes/mobile-testing-swl.md +2 -2
- package/agentes/nemesis-auditor-swl.md +1 -1
- package/agentes/notificador-swl.md +2 -2
- package/agentes/observabilidad-swl.md +2 -2
- package/agentes/orquestador-swl.md +41 -14
- package/agentes/pagos-swl.md +2 -2
- package/agentes/perfilador-usuario-swl.md +2 -2
- package/agentes/planificador-swl.md +2 -2
- package/agentes/producto-prd-swl.md +2 -2
- package/agentes/red-team-swl.md +218 -218
- package/agentes/release-manager-swl.md +2 -2
- package/agentes/rendimiento-swl.md +2 -2
- package/agentes/resolutor-build-swl.md +2 -2
- package/agentes/revisor-angular-swl.md +2 -2
- package/agentes/revisor-codigo-swl.md +36 -3
- package/agentes/revisor-csharp-swl.md +2 -2
- package/agentes/revisor-go-swl.md +2 -2
- package/agentes/revisor-java-swl.md +2 -2
- package/agentes/revisor-kotlin-swl.md +2 -2
- package/agentes/revisor-nextjs-swl.md +2 -2
- package/agentes/revisor-php-swl.md +2 -2
- package/agentes/revisor-react-swl.md +2 -2
- package/agentes/revisor-rust-swl.md +2 -2
- package/agentes/revisor-seguridad-swl.md +2 -2
- package/agentes/revisor-swift-swl.md +2 -2
- package/agentes/revisor-typescript-swl.md +2 -2
- package/agentes/sre-swl.md +2 -2
- package/agentes/tdd-qa-swl.md +2 -2
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/contexto.md +0 -2
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/deuda-codigo.md +97 -0
- package/comandos/swl/predecir.md +139 -139
- package/comandos/swl/sesiones.md +1 -1
- package/gateway/agent-executor.js +1 -1
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/agent-deep-links/SKILL.md +148 -148
- package/habilidades/agentes-como-servicio/SKILL.md +2 -2
- package/habilidades/angular-moderno/SKILL.md +24 -1
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -276
- package/habilidades/backend-async-postgres-testing/SKILL.md +215 -215
- package/habilidades/backend-error-design/SKILL.md +221 -221
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/calidad-anti-patrones-universales/SKILL.md +368 -368
- package/habilidades/calidad-contract-testing/SKILL.md +165 -165
- package/habilidades/calidad-mutation-testing/SKILL.md +170 -170
- package/habilidades/changelog-generator/scripts/parse-commits.js +367 -367
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
- package/habilidades/contenedores-docker/SKILL.md +3 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/drift-detection/SKILL.md +179 -179
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +1 -1
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +4 -4
- package/habilidades/harness-claude-code/recursos/disciplina-harness-regla.md +253 -252
- package/habilidades/legacy-code-rescue/SKILL.md +268 -267
- package/habilidades/meta-skills-estandar/SKILL.md +1 -1
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +2 -2
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/perfil-usuario/SKILL.md +200 -200
- package/habilidades/postgresql-experto/SKILL.md +3 -1
- package/habilidades/prevencion-sobreingenieria/SKILL.md +70 -92
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -0
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -0
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +255 -255
- package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
- package/habilidades/proceso-debate-adversarial/recursos/personas.md +105 -105
- package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
- package/habilidades/proceso-dynamic-workflows/SKILL.md +138 -138
- package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
- package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
- package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
- package/habilidades/proceso-modular-split/SKILL.md +256 -256
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +2 -2
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/structured-outputs/SKILL.md +2 -2
- package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
- package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
- package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
- package/habilidades/swl-dashboard/SKILL.md +2 -2
- package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +1 -1
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +1 -1
- package/hooks/audit-trail.js +4 -4
- package/hooks/calidad-pre-commit.js +15 -11
- package/hooks/captura-acciones-post.js +3 -2
- package/hooks/captura-acciones-session.js +3 -2
- package/hooks/ciclo-evolucion-subagente.js +26 -26
- package/hooks/ciclo-evolucion.js +26 -26
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/claudemd-duplicacion-detector.js +170 -170
- package/hooks/contexto-iteracion.js +145 -144
- package/hooks/contexto-subagente.js +68 -0
- package/hooks/guardrail-modelo.js +14 -4
- package/hooks/inyeccion-contexto.js +12 -12
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/autonomia.js +208 -208
- package/hooks/lib/ciclo-evolucion.js +47 -47
- package/hooks/lib/deep-links.js +185 -185
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/etapa-auto-evolucion.js +701 -701
- package/hooks/lib/etapa-metricas.js +388 -388
- package/hooks/lib/etapa-perfil-usuario.js +376 -376
- package/hooks/lib/loop-telemetry.js +321 -321
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/model-router.js +2 -2
- package/hooks/lib/propose-step.js +358 -358
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/token-estimator.js +1 -0
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/lib/usage-model.js +1 -1
- package/hooks/registro-turnos.js +210 -209
- package/hooks/session-briefing.js +98 -98
- package/hooks/spec-gate.js +212 -211
- package/hooks/sugerir-contribuir.js +2 -1
- package/hooks/sugerir-regenerar-inventario.js +171 -170
- package/hooks/tdd-gate.js +242 -241
- package/hooks/telemetria-skill-routing.js +100 -100
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-intent-spec.js +242 -242
- package/hooks/validar-memoria-hook.js +218 -218
- package/hooks/validar-planning-paths.js +134 -134
- package/instintos/autonomia.yaml +27 -27
- package/instintos/prompt-appendices.yaml +57 -57
- package/llms.txt +29 -29
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +2948 -1964
- package/manifiestos/hook-profiles.json +0 -2
- package/manifiestos/hooks-config.json +469 -477
- package/manifiestos/invariantes-criticos.json +30 -0
- package/manifiestos/modulos.json +1390 -1390
- package/manifiestos/planning-paths.json +44 -44
- package/manifiestos/skills-lock.json +1282 -1282
- package/package.json +93 -93
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +369 -371
- package/reglas/accesibilidad.md +10 -10
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/api-diseno.md +9 -9
- package/reglas/arreglar-al-detectar.md +240 -240
- package/reglas/auditorias-documentales-estructurales.md +7 -7
- package/reglas/cloud-infra.md +8 -8
- package/reglas/consultar-vault-primero.md +195 -195
- package/reglas/debatir-antes-de-aceptar.md +158 -158
- package/reglas/fragmentos-compartidos.md +183 -183
- package/reglas/hooks.md +6 -6
- package/reglas/intent-engineering.md +218 -218
- package/reglas/markitdown.md +8 -8
- package/reglas/monitor-ci.md +309 -309
- package/reglas/patrones.md +6 -6
- package/reglas/sesiones-paralelas.md +180 -180
- package/reglas/sin-duplicacion-reglas-globales.md +182 -182
- package/reglas/skills-estandar.md +6 -6
- package/reglas/testing.md +7 -7
- package/reglas/tests-cleanup.md +224 -224
- package/reglas/usar-code-review-graph.md +155 -155
- package/reglas/usar-context7.md +226 -226
- package/reglas/verificar-citas-normativas.md +548 -548
- package/schemas/agent-frontmatter.schema.json +5 -3
- package/schemas/agent-message.schema.json +73 -73
- package/schemas/agent-output-implementacion.schema.json +114 -114
- package/schemas/agent-output-planificacion.schema.json +150 -150
- package/schemas/agent-output-review.schema.json +98 -98
- package/schemas/diary-entry.schema.json +112 -112
- package/schemas/hook-profiles.schema.json +54 -54
- package/schemas/hooks-config.schema.json +89 -89
- package/schemas/modulos.schema.json +38 -38
- package/schemas/perfiles.schema.json +36 -36
- package/schemas/plugin.schema.json +77 -77
- package/schemas/skill-evals.schema.json +119 -119
- package/schemas/skill-frontmatter.schema.json +245 -245
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/cli/aprobar-plan.js +73 -73
- package/scripts/cli/briefing.js +23 -23
- package/scripts/cli/ciclo-evolucion.js +26 -26
- package/scripts/cli/configurar-ci.js +40 -40
- package/scripts/cli/derivar-feature-list.js +25 -25
- package/scripts/cli/detectar-host.js +27 -27
- package/scripts/cli/diary-entry.js +69 -69
- package/scripts/cli/execution-state.js +18 -18
- package/scripts/cli/gateway-notify.js +41 -41
- package/scripts/cli/liberar-fase.js +42 -42
- package/scripts/cli/loop-telemetry.js +125 -125
- package/scripts/cli/mark-evolved.js +56 -56
- package/scripts/cli/metricas-dora.js +26 -26
- package/scripts/cli/near-duplicate.js +55 -55
- package/scripts/cli/notificaciones.js +123 -123
- package/scripts/cli/propose-step.js +29 -29
- package/scripts/cli/schedule-parse.js +19 -19
- package/scripts/cli/sugerir-modelo.js +20 -20
- package/scripts/cli/verificar-plan.js +36 -36
- package/scripts/cli/verificar-trazabilidad.js +35 -35
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/instalador.js +4 -1
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/auditar-invocaciones-comandos.js +104 -104
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/ci-reader.js +193 -193
- package/scripts/lib/claude-sessions.js +1 -0
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-host-swl.js +175 -175
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
- package/scripts/lib/detector-reglas-duplicadas.js +220 -220
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/evidencia-release.js +322 -322
- package/scripts/lib/gate-hooks-requires.js +249 -249
- package/scripts/lib/gate-licencias.js +212 -212
- package/scripts/lib/git-metricas.js +257 -257
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/metricas-dora.js +204 -204
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/plan-lock.js +275 -275
- package/scripts/lib/pr-analyzer.js +399 -399
- package/scripts/lib/preservar-usuario.js +39 -5
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/reglas-globales-conocidas.json +180 -180
- package/scripts/lib/resolver-plan-fase.js +37 -37
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/schema-version.js +164 -164
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/tool-cost-analyzer.js +1 -0
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +200 -200
- package/scripts/publicar.js +497 -497
- package/scripts/run-eval.js +141 -141
- package/scripts/tui/componentes/selector-multi.js +189 -189
- package/scripts/tui/componentes/selector-unico.js +158 -158
- package/scripts/tui/ejecutores.js +375 -375
- package/scripts/tui/index.js +162 -162
- package/scripts/tui/lib/colores.js +129 -129
- package/scripts/tui/lib/render.js +264 -264
- package/scripts/tui/lib/teclas.js +113 -113
- package/scripts/tui/pantallas/inspect.js +173 -173
- package/scripts/tui/pantallas/install-wizard.js +334 -334
- package/scripts/tui/pantallas/menu-principal.js +52 -52
- package/scripts/tui/pantallas/progreso.js +274 -274
- package/scripts/tui/pantallas/resumen.js +132 -132
- package/scripts/tui/pantallas/uninstall-wizard.js +208 -208
- package/scripts/tui/pantallas/update-wizard.js +232 -232
- package/scripts/tui/pantallas/welcome.js +187 -187
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/validar.js +1 -1
- package/scripts/verificar-release.js +51 -0
- package/hooks/lib/context-compressor.js +0 -657
- package/hooks/linea-estado.js +0 -326
- package/hooks/monitor-contexto.js +0 -373
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Menú principal de la TUI de swl-ses.
|
|
5
|
-
* 4 operaciones disponibles: Install / Update / Inspect / Uninstall.
|
|
6
|
-
*
|
|
7
|
-
* Devuelve uno de: 'install' | 'update' | 'inspect' | 'uninstall' | null
|
|
8
|
-
* (null = el usuario canceló con Escape)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { selectorUnico } = require('../componentes/selector-unico');
|
|
12
|
-
|
|
13
|
-
const OPCIONES = [
|
|
14
|
-
{
|
|
15
|
-
valor: 'install',
|
|
16
|
-
etiqueta: 'Install — Instalar SWL en un runtime nuevo',
|
|
17
|
-
descripcion:
|
|
18
|
-
'Asistente paso a paso: runtime + perfil + scope + flags. ' +
|
|
19
|
-
'Detecta instalaciones existentes y avisa si hay conflictos de scope.',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
valor: 'update',
|
|
23
|
-
etiqueta: 'Update — Actualizar instalaciones existentes',
|
|
24
|
-
descripcion:
|
|
25
|
-
'Multi-select de runtimes a actualizar + componentes a refrescar ' +
|
|
26
|
-
'(skills, hooks, agentes, reglas, comandos). Muestra diff antes de aplicar.',
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
valor: 'inspect',
|
|
30
|
-
etiqueta: 'Inspect — Explorar componentes instalados',
|
|
31
|
-
descripcion:
|
|
32
|
-
'Navega por runtime → perfil → componentes sin modificar nada. ' +
|
|
33
|
-
'Lee de los manifiestos y archivos de estado.',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
valor: 'uninstall',
|
|
37
|
-
etiqueta: 'Uninstall — Desinstalar SWL de un runtime',
|
|
38
|
-
descripcion:
|
|
39
|
-
'Elige runtime y scope a desinstalar. Preserva _userland/ y APRENDIZAJES.md ' +
|
|
40
|
-
'por default. Pide confirmación explícita antes de borrar.',
|
|
41
|
-
},
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
function mostrarMenuPrincipal() {
|
|
45
|
-
return selectorUnico({
|
|
46
|
-
titulo: '¿Qué operación quieres realizar?',
|
|
47
|
-
items: OPCIONES,
|
|
48
|
-
indiceDefault: 0,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = { mostrarMenuPrincipal, OPCIONES };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Menú principal de la TUI de swl-ses.
|
|
5
|
+
* 4 operaciones disponibles: Install / Update / Inspect / Uninstall.
|
|
6
|
+
*
|
|
7
|
+
* Devuelve uno de: 'install' | 'update' | 'inspect' | 'uninstall' | null
|
|
8
|
+
* (null = el usuario canceló con Escape)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { selectorUnico } = require('../componentes/selector-unico');
|
|
12
|
+
|
|
13
|
+
const OPCIONES = [
|
|
14
|
+
{
|
|
15
|
+
valor: 'install',
|
|
16
|
+
etiqueta: 'Install — Instalar SWL en un runtime nuevo',
|
|
17
|
+
descripcion:
|
|
18
|
+
'Asistente paso a paso: runtime + perfil + scope + flags. ' +
|
|
19
|
+
'Detecta instalaciones existentes y avisa si hay conflictos de scope.',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
valor: 'update',
|
|
23
|
+
etiqueta: 'Update — Actualizar instalaciones existentes',
|
|
24
|
+
descripcion:
|
|
25
|
+
'Multi-select de runtimes a actualizar + componentes a refrescar ' +
|
|
26
|
+
'(skills, hooks, agentes, reglas, comandos). Muestra diff antes de aplicar.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
valor: 'inspect',
|
|
30
|
+
etiqueta: 'Inspect — Explorar componentes instalados',
|
|
31
|
+
descripcion:
|
|
32
|
+
'Navega por runtime → perfil → componentes sin modificar nada. ' +
|
|
33
|
+
'Lee de los manifiestos y archivos de estado.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
valor: 'uninstall',
|
|
37
|
+
etiqueta: 'Uninstall — Desinstalar SWL de un runtime',
|
|
38
|
+
descripcion:
|
|
39
|
+
'Elige runtime y scope a desinstalar. Preserva _userland/ y APRENDIZAJES.md ' +
|
|
40
|
+
'por default. Pide confirmación explícita antes de borrar.',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function mostrarMenuPrincipal() {
|
|
45
|
+
return selectorUnico({
|
|
46
|
+
titulo: '¿Qué operación quieres realizar?',
|
|
47
|
+
items: OPCIONES,
|
|
48
|
+
indiceDefault: 0,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { mostrarMenuPrincipal, OPCIONES };
|
|
@@ -1,274 +1,274 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Pantalla de progreso de la TUI.
|
|
5
|
-
* Diseño:
|
|
6
|
-
* - Cabecera con título de la operación (Install / Update)
|
|
7
|
-
* - Barra global de progreso con porcentaje
|
|
8
|
-
* - Tabla de contadores por categoría (skills, hooks, agentes, reglas, etc.)
|
|
9
|
-
* - Panel inferior con log rotativo (últimas N líneas)
|
|
10
|
-
*
|
|
11
|
-
* Modelo de uso:
|
|
12
|
-
*
|
|
13
|
-
* const ctrl = crearProgreso({ titulo, categorias });
|
|
14
|
-
* ctrl.iniciar();
|
|
15
|
-
*
|
|
16
|
-
* // El instalador emite eventos:
|
|
17
|
-
* ctrl.onEvento({ tipo: 'archivo-copiado', componente: 'skills', archivo: '...' });
|
|
18
|
-
* ctrl.onEvento({ tipo: 'componente-completado', componente: 'skills' });
|
|
19
|
-
* ctrl.onEvento({ tipo: 'log', linea: 'Backup creado: ...' });
|
|
20
|
-
*
|
|
21
|
-
* // Al terminar:
|
|
22
|
-
* ctrl.finalizar({ ok: true, mensaje: 'Instalación completa' });
|
|
23
|
-
*
|
|
24
|
-
* En no-TTY: imprime líneas simples a stdout sin esperar.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
const render = require('../lib/render');
|
|
28
|
-
const teclas = require('../lib/teclas');
|
|
29
|
-
const { colores, semantico, iconos } = require('../lib/colores');
|
|
30
|
-
|
|
31
|
-
const MAX_LOG_LINEAS_DEFAULT = 8;
|
|
32
|
-
const MAX_LOG_LINEAS_VERBOSE = 24;
|
|
33
|
-
|
|
34
|
-
// Compat: el contador histórico exportado seguía siendo MAX_LOG_LINEAS.
|
|
35
|
-
// Lo mantenemos para no romper consumidores externos.
|
|
36
|
-
const MAX_LOG_LINEAS = MAX_LOG_LINEAS_DEFAULT;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @param {object} opciones
|
|
40
|
-
* @param {string} opciones.titulo - Encabezado (ej: "Instalando SWL en Claude Code...")
|
|
41
|
-
* @param {Array<{nombre:string, etiqueta:string, total:number}>} opciones.categorias
|
|
42
|
-
* Categorías a contar. Ej: [{ nombre:'skills', etiqueta:'Skills', total:162 }, ...]
|
|
43
|
-
* @param {boolean} [opciones.verbose=false] - Expande el panel de log para mostrar
|
|
44
|
-
* más líneas (24 vs 8) y deshabilita el truncado del log por archivo.
|
|
45
|
-
* Util cuando se quiere depurar qué hace exactamente el instalador.
|
|
46
|
-
*/
|
|
47
|
-
function crearProgreso(opciones) {
|
|
48
|
-
const titulo = opciones.titulo || 'Procesando...';
|
|
49
|
-
const categorias = opciones.categorias || [];
|
|
50
|
-
const verbose = !!opciones.verbose;
|
|
51
|
-
const maxLogLineas = verbose ? MAX_LOG_LINEAS_VERBOSE : MAX_LOG_LINEAS_DEFAULT;
|
|
52
|
-
|
|
53
|
-
// Estado interno
|
|
54
|
-
const conteos = new Map(); // nombre → contador actual
|
|
55
|
-
for (const c of categorias) conteos.set(c.nombre, 0);
|
|
56
|
-
const logBuffer = []; // últimas N líneas
|
|
57
|
-
let finalizado = false;
|
|
58
|
-
let mensajeFinal = '';
|
|
59
|
-
let okFinal = true;
|
|
60
|
-
let onResize = null;
|
|
61
|
-
let teclado = null;
|
|
62
|
-
let resolverEspera = null;
|
|
63
|
-
|
|
64
|
-
function _porcentajeGlobal() {
|
|
65
|
-
let hecho = 0, total = 0;
|
|
66
|
-
for (const c of categorias) {
|
|
67
|
-
hecho += Math.min(conteos.get(c.nombre) || 0, c.total);
|
|
68
|
-
total += c.total;
|
|
69
|
-
}
|
|
70
|
-
return total > 0 ? hecho / total : 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function _renderizar() {
|
|
74
|
-
if (!render.ES_TTY) return;
|
|
75
|
-
render.limpiarPantalla();
|
|
76
|
-
const { cols, rows } = render.obtenerDimensiones();
|
|
77
|
-
|
|
78
|
-
// Cabecera
|
|
79
|
-
render.escribirEn(2, 3, semantico.titulo(titulo));
|
|
80
|
-
|
|
81
|
-
// Barra global
|
|
82
|
-
const filaBarra = 4;
|
|
83
|
-
render.escribirEn(filaBarra, 3, colores.dim('Progreso global'));
|
|
84
|
-
render.dibujarBarra(filaBarra + 1, 3, Math.min(cols - 6, 60), _porcentajeGlobal());
|
|
85
|
-
|
|
86
|
-
// Contadores por categoría
|
|
87
|
-
const filaCat = filaBarra + 4;
|
|
88
|
-
render.escribirEn(filaCat - 1, 3, colores.dim('Por categoría:'));
|
|
89
|
-
categorias.forEach((c, i) => {
|
|
90
|
-
const actual = conteos.get(c.nombre) || 0;
|
|
91
|
-
const pct = c.total > 0 ? actual / c.total : 0;
|
|
92
|
-
const completo = actual >= c.total && c.total > 0;
|
|
93
|
-
const icono = completo ? semantico.exito(iconos.check) : (actual > 0 ? semantico.info(iconos.arrow) : colores.dim(iconos.punto));
|
|
94
|
-
const linea =
|
|
95
|
-
`${icono} ` +
|
|
96
|
-
render.rellenarDer(colores.negrita(c.etiqueta), 14) +
|
|
97
|
-
render.rellenarDer(`${actual}/${c.total}`, 12);
|
|
98
|
-
render.escribirEn(filaCat + i, 5, linea);
|
|
99
|
-
// Mini-barra al lado
|
|
100
|
-
const anchoMini = Math.min(20, cols - 50);
|
|
101
|
-
if (anchoMini > 8) {
|
|
102
|
-
render.dibujarBarra(filaCat + i, 32, anchoMini, pct, {
|
|
103
|
-
colorLleno: completo ? colores.verde : colores.cyan,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Panel inferior con log rotativo
|
|
109
|
-
const filaLog = filaCat + categorias.length + 2;
|
|
110
|
-
const altoLog = Math.min(maxLogLineas + 2, rows - filaLog - 2);
|
|
111
|
-
if (altoLog >= 3) {
|
|
112
|
-
const tituloLog = verbose
|
|
113
|
-
? `Log detallado (verbose, ${maxLogLineas} líneas)`
|
|
114
|
-
: 'Log (últimas líneas)';
|
|
115
|
-
render.dibujarCaja(filaLog, 3, cols - 5, altoLog, {
|
|
116
|
-
titulo: tituloLog,
|
|
117
|
-
color: colores.dim,
|
|
118
|
-
});
|
|
119
|
-
const lineasMostradas = logBuffer.slice(-Math.max(1, altoLog - 2));
|
|
120
|
-
lineasMostradas.forEach((l, i) => {
|
|
121
|
-
render.escribirEn(filaLog + 1 + i, 5,
|
|
122
|
-
render.recortar(colores.dim(l), cols - 9));
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Pie
|
|
127
|
-
if (finalizado) {
|
|
128
|
-
const colorPie = okFinal ? semantico.exito : semantico.error;
|
|
129
|
-
const ico = okFinal ? iconos.check : iconos.cross;
|
|
130
|
-
render.escribirEn(rows - 2, 3,
|
|
131
|
-
colorPie(`${ico} ${mensajeFinal} ${colores.dim('— Enter para continuar')}`));
|
|
132
|
-
} else {
|
|
133
|
-
render.dibujarPiePagina([
|
|
134
|
-
['Esc', 'cancelar'],
|
|
135
|
-
]);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ── API pública ───────────────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
function iniciar() {
|
|
142
|
-
if (!render.ES_TTY) {
|
|
143
|
-
console.log('');
|
|
144
|
-
console.log(` ${titulo}`);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
render.iniciarModoTui();
|
|
148
|
-
_renderizar();
|
|
149
|
-
onResize = () => _renderizar();
|
|
150
|
-
process.stdout.on('resize', onResize);
|
|
151
|
-
teclado = teclas.crearTeclado();
|
|
152
|
-
teclado.on('escape', () => {
|
|
153
|
-
// No interrumpimos directamente: el caller decide cómo manejar.
|
|
154
|
-
// Aquí solo registramos en el log.
|
|
155
|
-
onEvento({ tipo: 'log', linea: '[usuario presionó Esc — ignorado durante ejecución]' });
|
|
156
|
-
});
|
|
157
|
-
teclado.activar();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Recibe eventos del instalador.
|
|
162
|
-
* tipos soportados:
|
|
163
|
-
* - 'archivo-copiado' { componente, archivo? }
|
|
164
|
-
* - 'componente-completado' { componente }
|
|
165
|
-
* - 'log' { linea }
|
|
166
|
-
* - 'set-total' { componente, total } — actualizar total dinámico
|
|
167
|
-
*/
|
|
168
|
-
function onEvento(evento) {
|
|
169
|
-
if (!evento || !evento.tipo) return;
|
|
170
|
-
|
|
171
|
-
switch (evento.tipo) {
|
|
172
|
-
case 'archivo-copiado':
|
|
173
|
-
if (evento.componente) {
|
|
174
|
-
const actual = conteos.get(evento.componente) || 0;
|
|
175
|
-
conteos.set(evento.componente, actual + 1);
|
|
176
|
-
}
|
|
177
|
-
if (evento.archivo) {
|
|
178
|
-
logBuffer.push(`${semantico.exito('+')} ${evento.componente}: ${evento.archivo}`);
|
|
179
|
-
if (logBuffer.length > maxLogLineas * 2) logBuffer.splice(0, logBuffer.length - maxLogLineas * 2);
|
|
180
|
-
}
|
|
181
|
-
break;
|
|
182
|
-
case 'componente-completado':
|
|
183
|
-
if (evento.componente) {
|
|
184
|
-
const cat = categorias.find(c => c.nombre === evento.componente);
|
|
185
|
-
if (cat) conteos.set(evento.componente, cat.total);
|
|
186
|
-
}
|
|
187
|
-
break;
|
|
188
|
-
case 'log':
|
|
189
|
-
if (evento.linea) {
|
|
190
|
-
logBuffer.push(evento.linea);
|
|
191
|
-
if (logBuffer.length > maxLogLineas * 2) logBuffer.splice(0, logBuffer.length - maxLogLineas * 2);
|
|
192
|
-
}
|
|
193
|
-
break;
|
|
194
|
-
case 'set-total':
|
|
195
|
-
if (evento.componente && typeof evento.total === 'number') {
|
|
196
|
-
const cat = categorias.find(c => c.nombre === evento.componente);
|
|
197
|
-
if (cat) cat.total = evento.total;
|
|
198
|
-
}
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
_renderizar();
|
|
203
|
-
if (!render.ES_TTY && evento.linea) {
|
|
204
|
-
// Modo no-TTY: imprimir log lineal
|
|
205
|
-
console.log(` ${evento.linea}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Marca la pantalla como finalizada. El usuario debe presionar Enter para
|
|
211
|
-
* cerrar. Resuelve la promise devuelta por esperarSalida().
|
|
212
|
-
*/
|
|
213
|
-
function finalizar(opts = {}) {
|
|
214
|
-
finalizado = true;
|
|
215
|
-
okFinal = opts.ok !== false;
|
|
216
|
-
mensajeFinal = opts.mensaje || (okFinal ? 'Completado' : 'Falló');
|
|
217
|
-
if (!render.ES_TTY) {
|
|
218
|
-
console.log('');
|
|
219
|
-
console.log(` ${okFinal ? '[OK]' : '[ERROR]'} ${mensajeFinal}`);
|
|
220
|
-
if (resolverEspera) resolverEspera();
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
_renderizar();
|
|
224
|
-
|
|
225
|
-
if (teclado) {
|
|
226
|
-
teclado.on('return', () => {
|
|
227
|
-
cerrar();
|
|
228
|
-
if (resolverEspera) {
|
|
229
|
-
const r = resolverEspera;
|
|
230
|
-
resolverEspera = null;
|
|
231
|
-
r();
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function cerrar() {
|
|
238
|
-
if (teclado) teclado.desactivar();
|
|
239
|
-
if (onResize) process.stdout.removeListener('resize', onResize);
|
|
240
|
-
render.salirModoTui();
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Devuelve una promise que se resuelve cuando el usuario presiona Enter
|
|
245
|
-
* después de finalizar(). En no-TTY se resuelve inmediatamente al finalizar.
|
|
246
|
-
*/
|
|
247
|
-
function esperarSalida() {
|
|
248
|
-
return new Promise((resolve) => {
|
|
249
|
-
if (finalizado && !render.ES_TTY) {
|
|
250
|
-
resolve();
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
resolverEspera = resolve;
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
iniciar,
|
|
259
|
-
onEvento,
|
|
260
|
-
finalizar,
|
|
261
|
-
cerrar,
|
|
262
|
-
esperarSalida,
|
|
263
|
-
// exports auxiliares para tests
|
|
264
|
-
_getEstado: () => ({
|
|
265
|
-
conteos: Object.fromEntries(conteos),
|
|
266
|
-
logBuffer: [...logBuffer],
|
|
267
|
-
finalizado,
|
|
268
|
-
mensajeFinal,
|
|
269
|
-
okFinal,
|
|
270
|
-
}),
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
module.exports = { crearProgreso, MAX_LOG_LINEAS, MAX_LOG_LINEAS_DEFAULT, MAX_LOG_LINEAS_VERBOSE };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pantalla de progreso de la TUI.
|
|
5
|
+
* Diseño:
|
|
6
|
+
* - Cabecera con título de la operación (Install / Update)
|
|
7
|
+
* - Barra global de progreso con porcentaje
|
|
8
|
+
* - Tabla de contadores por categoría (skills, hooks, agentes, reglas, etc.)
|
|
9
|
+
* - Panel inferior con log rotativo (últimas N líneas)
|
|
10
|
+
*
|
|
11
|
+
* Modelo de uso:
|
|
12
|
+
*
|
|
13
|
+
* const ctrl = crearProgreso({ titulo, categorias });
|
|
14
|
+
* ctrl.iniciar();
|
|
15
|
+
*
|
|
16
|
+
* // El instalador emite eventos:
|
|
17
|
+
* ctrl.onEvento({ tipo: 'archivo-copiado', componente: 'skills', archivo: '...' });
|
|
18
|
+
* ctrl.onEvento({ tipo: 'componente-completado', componente: 'skills' });
|
|
19
|
+
* ctrl.onEvento({ tipo: 'log', linea: 'Backup creado: ...' });
|
|
20
|
+
*
|
|
21
|
+
* // Al terminar:
|
|
22
|
+
* ctrl.finalizar({ ok: true, mensaje: 'Instalación completa' });
|
|
23
|
+
*
|
|
24
|
+
* En no-TTY: imprime líneas simples a stdout sin esperar.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const render = require('../lib/render');
|
|
28
|
+
const teclas = require('../lib/teclas');
|
|
29
|
+
const { colores, semantico, iconos } = require('../lib/colores');
|
|
30
|
+
|
|
31
|
+
const MAX_LOG_LINEAS_DEFAULT = 8;
|
|
32
|
+
const MAX_LOG_LINEAS_VERBOSE = 24;
|
|
33
|
+
|
|
34
|
+
// Compat: el contador histórico exportado seguía siendo MAX_LOG_LINEAS.
|
|
35
|
+
// Lo mantenemos para no romper consumidores externos.
|
|
36
|
+
const MAX_LOG_LINEAS = MAX_LOG_LINEAS_DEFAULT;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {object} opciones
|
|
40
|
+
* @param {string} opciones.titulo - Encabezado (ej: "Instalando SWL en Claude Code...")
|
|
41
|
+
* @param {Array<{nombre:string, etiqueta:string, total:number}>} opciones.categorias
|
|
42
|
+
* Categorías a contar. Ej: [{ nombre:'skills', etiqueta:'Skills', total:162 }, ...]
|
|
43
|
+
* @param {boolean} [opciones.verbose=false] - Expande el panel de log para mostrar
|
|
44
|
+
* más líneas (24 vs 8) y deshabilita el truncado del log por archivo.
|
|
45
|
+
* Util cuando se quiere depurar qué hace exactamente el instalador.
|
|
46
|
+
*/
|
|
47
|
+
function crearProgreso(opciones) {
|
|
48
|
+
const titulo = opciones.titulo || 'Procesando...';
|
|
49
|
+
const categorias = opciones.categorias || [];
|
|
50
|
+
const verbose = !!opciones.verbose;
|
|
51
|
+
const maxLogLineas = verbose ? MAX_LOG_LINEAS_VERBOSE : MAX_LOG_LINEAS_DEFAULT;
|
|
52
|
+
|
|
53
|
+
// Estado interno
|
|
54
|
+
const conteos = new Map(); // nombre → contador actual
|
|
55
|
+
for (const c of categorias) conteos.set(c.nombre, 0);
|
|
56
|
+
const logBuffer = []; // últimas N líneas
|
|
57
|
+
let finalizado = false;
|
|
58
|
+
let mensajeFinal = '';
|
|
59
|
+
let okFinal = true;
|
|
60
|
+
let onResize = null;
|
|
61
|
+
let teclado = null;
|
|
62
|
+
let resolverEspera = null;
|
|
63
|
+
|
|
64
|
+
function _porcentajeGlobal() {
|
|
65
|
+
let hecho = 0, total = 0;
|
|
66
|
+
for (const c of categorias) {
|
|
67
|
+
hecho += Math.min(conteos.get(c.nombre) || 0, c.total);
|
|
68
|
+
total += c.total;
|
|
69
|
+
}
|
|
70
|
+
return total > 0 ? hecho / total : 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _renderizar() {
|
|
74
|
+
if (!render.ES_TTY) return;
|
|
75
|
+
render.limpiarPantalla();
|
|
76
|
+
const { cols, rows } = render.obtenerDimensiones();
|
|
77
|
+
|
|
78
|
+
// Cabecera
|
|
79
|
+
render.escribirEn(2, 3, semantico.titulo(titulo));
|
|
80
|
+
|
|
81
|
+
// Barra global
|
|
82
|
+
const filaBarra = 4;
|
|
83
|
+
render.escribirEn(filaBarra, 3, colores.dim('Progreso global'));
|
|
84
|
+
render.dibujarBarra(filaBarra + 1, 3, Math.min(cols - 6, 60), _porcentajeGlobal());
|
|
85
|
+
|
|
86
|
+
// Contadores por categoría
|
|
87
|
+
const filaCat = filaBarra + 4;
|
|
88
|
+
render.escribirEn(filaCat - 1, 3, colores.dim('Por categoría:'));
|
|
89
|
+
categorias.forEach((c, i) => {
|
|
90
|
+
const actual = conteos.get(c.nombre) || 0;
|
|
91
|
+
const pct = c.total > 0 ? actual / c.total : 0;
|
|
92
|
+
const completo = actual >= c.total && c.total > 0;
|
|
93
|
+
const icono = completo ? semantico.exito(iconos.check) : (actual > 0 ? semantico.info(iconos.arrow) : colores.dim(iconos.punto));
|
|
94
|
+
const linea =
|
|
95
|
+
`${icono} ` +
|
|
96
|
+
render.rellenarDer(colores.negrita(c.etiqueta), 14) +
|
|
97
|
+
render.rellenarDer(`${actual}/${c.total}`, 12);
|
|
98
|
+
render.escribirEn(filaCat + i, 5, linea);
|
|
99
|
+
// Mini-barra al lado
|
|
100
|
+
const anchoMini = Math.min(20, cols - 50);
|
|
101
|
+
if (anchoMini > 8) {
|
|
102
|
+
render.dibujarBarra(filaCat + i, 32, anchoMini, pct, {
|
|
103
|
+
colorLleno: completo ? colores.verde : colores.cyan,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Panel inferior con log rotativo
|
|
109
|
+
const filaLog = filaCat + categorias.length + 2;
|
|
110
|
+
const altoLog = Math.min(maxLogLineas + 2, rows - filaLog - 2);
|
|
111
|
+
if (altoLog >= 3) {
|
|
112
|
+
const tituloLog = verbose
|
|
113
|
+
? `Log detallado (verbose, ${maxLogLineas} líneas)`
|
|
114
|
+
: 'Log (últimas líneas)';
|
|
115
|
+
render.dibujarCaja(filaLog, 3, cols - 5, altoLog, {
|
|
116
|
+
titulo: tituloLog,
|
|
117
|
+
color: colores.dim,
|
|
118
|
+
});
|
|
119
|
+
const lineasMostradas = logBuffer.slice(-Math.max(1, altoLog - 2));
|
|
120
|
+
lineasMostradas.forEach((l, i) => {
|
|
121
|
+
render.escribirEn(filaLog + 1 + i, 5,
|
|
122
|
+
render.recortar(colores.dim(l), cols - 9));
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Pie
|
|
127
|
+
if (finalizado) {
|
|
128
|
+
const colorPie = okFinal ? semantico.exito : semantico.error;
|
|
129
|
+
const ico = okFinal ? iconos.check : iconos.cross;
|
|
130
|
+
render.escribirEn(rows - 2, 3,
|
|
131
|
+
colorPie(`${ico} ${mensajeFinal} ${colores.dim('— Enter para continuar')}`));
|
|
132
|
+
} else {
|
|
133
|
+
render.dibujarPiePagina([
|
|
134
|
+
['Esc', 'cancelar'],
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── API pública ───────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
function iniciar() {
|
|
142
|
+
if (!render.ES_TTY) {
|
|
143
|
+
console.log('');
|
|
144
|
+
console.log(` ${titulo}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
render.iniciarModoTui();
|
|
148
|
+
_renderizar();
|
|
149
|
+
onResize = () => _renderizar();
|
|
150
|
+
process.stdout.on('resize', onResize);
|
|
151
|
+
teclado = teclas.crearTeclado();
|
|
152
|
+
teclado.on('escape', () => {
|
|
153
|
+
// No interrumpimos directamente: el caller decide cómo manejar.
|
|
154
|
+
// Aquí solo registramos en el log.
|
|
155
|
+
onEvento({ tipo: 'log', linea: '[usuario presionó Esc — ignorado durante ejecución]' });
|
|
156
|
+
});
|
|
157
|
+
teclado.activar();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Recibe eventos del instalador.
|
|
162
|
+
* tipos soportados:
|
|
163
|
+
* - 'archivo-copiado' { componente, archivo? }
|
|
164
|
+
* - 'componente-completado' { componente }
|
|
165
|
+
* - 'log' { linea }
|
|
166
|
+
* - 'set-total' { componente, total } — actualizar total dinámico
|
|
167
|
+
*/
|
|
168
|
+
function onEvento(evento) {
|
|
169
|
+
if (!evento || !evento.tipo) return;
|
|
170
|
+
|
|
171
|
+
switch (evento.tipo) {
|
|
172
|
+
case 'archivo-copiado':
|
|
173
|
+
if (evento.componente) {
|
|
174
|
+
const actual = conteos.get(evento.componente) || 0;
|
|
175
|
+
conteos.set(evento.componente, actual + 1);
|
|
176
|
+
}
|
|
177
|
+
if (evento.archivo) {
|
|
178
|
+
logBuffer.push(`${semantico.exito('+')} ${evento.componente}: ${evento.archivo}`);
|
|
179
|
+
if (logBuffer.length > maxLogLineas * 2) logBuffer.splice(0, logBuffer.length - maxLogLineas * 2);
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case 'componente-completado':
|
|
183
|
+
if (evento.componente) {
|
|
184
|
+
const cat = categorias.find(c => c.nombre === evento.componente);
|
|
185
|
+
if (cat) conteos.set(evento.componente, cat.total);
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
case 'log':
|
|
189
|
+
if (evento.linea) {
|
|
190
|
+
logBuffer.push(evento.linea);
|
|
191
|
+
if (logBuffer.length > maxLogLineas * 2) logBuffer.splice(0, logBuffer.length - maxLogLineas * 2);
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
case 'set-total':
|
|
195
|
+
if (evento.componente && typeof evento.total === 'number') {
|
|
196
|
+
const cat = categorias.find(c => c.nombre === evento.componente);
|
|
197
|
+
if (cat) cat.total = evento.total;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
_renderizar();
|
|
203
|
+
if (!render.ES_TTY && evento.linea) {
|
|
204
|
+
// Modo no-TTY: imprimir log lineal
|
|
205
|
+
console.log(` ${evento.linea}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Marca la pantalla como finalizada. El usuario debe presionar Enter para
|
|
211
|
+
* cerrar. Resuelve la promise devuelta por esperarSalida().
|
|
212
|
+
*/
|
|
213
|
+
function finalizar(opts = {}) {
|
|
214
|
+
finalizado = true;
|
|
215
|
+
okFinal = opts.ok !== false;
|
|
216
|
+
mensajeFinal = opts.mensaje || (okFinal ? 'Completado' : 'Falló');
|
|
217
|
+
if (!render.ES_TTY) {
|
|
218
|
+
console.log('');
|
|
219
|
+
console.log(` ${okFinal ? '[OK]' : '[ERROR]'} ${mensajeFinal}`);
|
|
220
|
+
if (resolverEspera) resolverEspera();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
_renderizar();
|
|
224
|
+
|
|
225
|
+
if (teclado) {
|
|
226
|
+
teclado.on('return', () => {
|
|
227
|
+
cerrar();
|
|
228
|
+
if (resolverEspera) {
|
|
229
|
+
const r = resolverEspera;
|
|
230
|
+
resolverEspera = null;
|
|
231
|
+
r();
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function cerrar() {
|
|
238
|
+
if (teclado) teclado.desactivar();
|
|
239
|
+
if (onResize) process.stdout.removeListener('resize', onResize);
|
|
240
|
+
render.salirModoTui();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Devuelve una promise que se resuelve cuando el usuario presiona Enter
|
|
245
|
+
* después de finalizar(). En no-TTY se resuelve inmediatamente al finalizar.
|
|
246
|
+
*/
|
|
247
|
+
function esperarSalida() {
|
|
248
|
+
return new Promise((resolve) => {
|
|
249
|
+
if (finalizado && !render.ES_TTY) {
|
|
250
|
+
resolve();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
resolverEspera = resolve;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
iniciar,
|
|
259
|
+
onEvento,
|
|
260
|
+
finalizar,
|
|
261
|
+
cerrar,
|
|
262
|
+
esperarSalida,
|
|
263
|
+
// exports auxiliares para tests
|
|
264
|
+
_getEstado: () => ({
|
|
265
|
+
conteos: Object.fromEntries(conteos),
|
|
266
|
+
logBuffer: [...logBuffer],
|
|
267
|
+
finalizado,
|
|
268
|
+
mensajeFinal,
|
|
269
|
+
okFinal,
|
|
270
|
+
}),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = { crearProgreso, MAX_LOG_LINEAS, MAX_LOG_LINEAS_DEFAULT, MAX_LOG_LINEAS_VERBOSE };
|