@saulwade/swl-ses 2.6.1 → 2.8.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.
- package/CLAUDE.md +14 -2
- package/README.md +65 -18
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/bin/swl-ses.js +10 -0
- package/comandos/swl/brainstorm.md +1 -0
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/mcp-status.md +1 -0
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/agent-deep-links/SKILL.md +148 -148
- package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
- package/habilidades/backend-error-design/SKILL.md +221 -221
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
- package/habilidades/calidad-contract-testing/SKILL.md +165 -165
- package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
- package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
- package/habilidades/css-moderno/SKILL.md +7 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
- package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +3 -2
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/perfil-usuario/SKILL.md +200 -200
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
- package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
- package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
- package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
- 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/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- 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/tdd-workflow/recursos/gherkin-bdd.md +111 -111
- package/hooks/calidad-pre-commit.js +159 -10
- package/hooks/ciclo-evolucion-subagente.js +26 -26
- package/hooks/ciclo-evolucion.js +26 -26
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/lib/auto-consolidator.js +335 -335
- 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/notificacion-formato.js +45 -11
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/raiz-proyecto.js +35 -4
- 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/toon-compressor.js +245 -245
- package/hooks/notificacion-telegram.js +5 -11
- package/hooks/session-briefing.js +12 -4
- package/instintos/autonomia.yaml +27 -27
- package/instintos/prompt-appendices.yaml +57 -57
- package/llms.txt +1 -1
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +662 -0
- package/manifiestos/harness-ir.json +47536 -0
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/policy-bundle.json +2065 -0
- package/manifiestos/policy-corpus-w2.json +3926 -0
- package/manifiestos/runtime-adapters-core3.json +208 -0
- package/manifiestos/runtime-conformance.json +139 -0
- package/manifiestos/skills-lock.json +43 -43
- package/package.json +2 -2
- package/plantillas/auditor-veto-template.md +105 -105
- 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 +2 -2
- package/reglas/accesibilidad.md +10 -10
- 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/git-workflow.md +1 -0
- package/reglas/hooks.md +6 -6
- package/reglas/intent-engineering.md +218 -218
- package/reglas/markitdown.md +8 -8
- package/reglas/monitor-ci.md +12 -0
- package/reglas/patrones.md +6 -6
- package/reglas/testing.md +7 -7
- package/reglas/tests-cleanup.md +224 -224
- 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/gate-state.schema.json +76 -0
- package/schemas/harness-ir.schema.json +369 -0
- package/schemas/hook-profiles.schema.json +54 -54
- package/schemas/hooks-config.schema.json +89 -89
- package/schemas/legacy-gates.schema.json +45 -0
- package/schemas/modulos.schema.json +38 -38
- package/schemas/perfiles.schema.json +36 -36
- package/schemas/plugin.schema.json +77 -77
- package/schemas/policy-bundle.schema.json +140 -0
- package/schemas/policy-enforcement.schema.json +117 -0
- package/schemas/policy-operation.schema.json +261 -0
- package/schemas/runtime-adapter.schema.json +176 -0
- package/schemas/runtime-build-attestation.schema.json +100 -0
- package/schemas/runtime-conformance.schema.json +239 -0
- package/schemas/runtime-diagnostic.schema.json +395 -0
- package/schemas/skill-evals.schema.json +119 -119
- package/schemas/skill-frontmatter.schema.json +245 -245
- package/schemas/w4-certification-request.schema.json +72 -0
- package/schemas/w4-certification-verdict.schema.json +224 -0
- package/schemas/w4-corpus.schema.json +172 -0
- package/schemas/w4-mutation-report.schema.json +116 -0
- package/schemas/w4-replay-result.schema.json +164 -0
- package/schemas/w4-scoring-report.schema.json +89 -0
- 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/bootstrap-instintos.js +3 -0
- 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/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/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/comandos/install-asistido.js +8 -7
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +61 -36
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-claims-runtime.js +1342 -0
- package/scripts/generar-harness-ir.js +257 -0
- package/scripts/generar-inventario.js +52 -54
- package/scripts/generar-policy-bundle.js +202 -0
- package/scripts/instalador.js +26 -7
- package/scripts/lib/approval-receipts.js +190 -0
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/certificacion-loop-state.js +421 -0
- package/scripts/lib/ci-reader.js +193 -193
- package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
- package/scripts/lib/clasificar-directorio.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-host-swl.js +175 -175
- package/scripts/lib/detectar-runtime.js +29 -20
- 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/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/evidence-verifier.js +192 -0
- package/scripts/lib/evidencia-release.js +322 -322
- package/scripts/lib/frontmatter-canonico.js +509 -0
- package/scripts/lib/gate-engine.js +871 -0
- package/scripts/lib/gate-hooks-requires.js +249 -249
- package/scripts/lib/gate-licencias.js +212 -212
- package/scripts/lib/git-config-preflight.js +48 -0
- package/scripts/lib/git-metricas.js +257 -257
- package/scripts/lib/harness-ir.js +778 -0
- package/scripts/lib/harness-source-snapshot.js +309 -0
- package/scripts/lib/integrity-ledger.js +1147 -0
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/legacy-gate-migration.js +324 -0
- package/scripts/lib/limpiar-basura-global.js +45 -2
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/metricas-dora.js +204 -204
- package/scripts/lib/notificaciones-telegram.js +1 -0
- package/scripts/lib/npm-version.js +1 -0
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/plan-lock.js +61 -13
- package/scripts/lib/policy-broker.js +338 -0
- package/scripts/lib/policy-bundle.js +342 -0
- package/scripts/lib/policy-context-provider.js +310 -0
- package/scripts/lib/policy-contract.js +479 -0
- package/scripts/lib/policy-verifier-utils.js +65 -0
- package/scripts/lib/pr-analyzer.js +399 -399
- package/scripts/lib/principal-verifier.js +178 -0
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/resolver-plan-fase.js +37 -37
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/runtime-adapter-contract.js +267 -0
- package/scripts/lib/runtime-artifact-verifier.js +426 -0
- package/scripts/lib/runtime-build-attestation.js +127 -0
- package/scripts/lib/runtime-bundle-installer.js +586 -0
- package/scripts/lib/runtime-compiler.js +327 -0
- package/scripts/lib/runtime-conformance.js +202 -0
- package/scripts/lib/runtime-doctor-core3.js +567 -0
- package/scripts/lib/runtime-doctor-input.js +59 -0
- package/scripts/lib/runtime-operation-adapter.js +267 -0
- package/scripts/lib/schema-version.js +164 -164
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/signed-envelope.js +545 -0
- package/scripts/lib/single-use-store.js +359 -0
- package/scripts/lib/skills-externas.js +31 -0
- package/scripts/lib/transformadores/codex.js +15 -8
- package/scripts/lib/transformadores/gemini.js +79 -5
- package/scripts/lib/w4-attestation-adapter.js +158 -0
- package/scripts/lib/w4-canario.js +337 -0
- package/scripts/lib/w4-claims.js +182 -0
- package/scripts/lib/w4-corpus-generador.js +542 -0
- package/scripts/lib/w4-gate-c5.js +115 -0
- package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
- package/scripts/lib/w4-matriz-combos.js +55 -0
- package/scripts/lib/w4-motor-mutacion.js +1348 -0
- package/scripts/lib/w4-motor-replay.js +735 -0
- package/scripts/lib/w4-pin-origen.js +54 -0
- package/scripts/lib/w4-publicar-request.js +132 -0
- package/scripts/lib/w4-revocacion.js +62 -0
- package/scripts/lib/w4-runtimes-core3.js +38 -0
- package/scripts/lib/w4-scorer-certificacion.js +692 -0
- package/scripts/lib/w4-superficie-candidato.js +49 -0
- package/scripts/lib/w4-veredicto.js +452 -0
- package/scripts/lib/w4-verificar-veredicto.js +302 -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/migrar-gates-legacy.js +108 -0
- package/scripts/publicar-certification-request.js +115 -0
- package/scripts/runtime-doctor.js +107 -0
- 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/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/install-wizard.js +12 -7
- 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/validar-userland-vacio.js +110 -110
- package/scripts/verificar-aislamiento-swl-eval.js +87 -0
- package/scripts/verificar-empaquetado-downstream.js +375 -0
- package/scripts/verificar-loop-constructor.js +215 -0
- package/scripts/verificar-trazabilidad.js +13 -6
- package/scripts/verificar-veredicto-real.js +84 -0
|
@@ -1,158 +1,158 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Selector único (single-select) para la TUI.
|
|
5
|
-
* Más rico que `preguntarOpcion` de scripts/lib/ui.js: navegación con flechas,
|
|
6
|
-
* descripciones largas con wrap, soporte para items con etiquetas (badges),
|
|
7
|
-
* pie de página con atajos, indicador de cursor visible.
|
|
8
|
-
*
|
|
9
|
-
* Diseño:
|
|
10
|
-
* - Cada item: { valor, etiqueta, descripcion?, badge?, deshabilitado? }
|
|
11
|
-
* - El cursor se mueve solo entre items habilitados.
|
|
12
|
-
* - Enter resuelve con `items[cursor].valor`.
|
|
13
|
-
* - Escape resuelve con `null` (señal de "cancelado").
|
|
14
|
-
*
|
|
15
|
-
* No-TTY: resuelve inmediatamente con el item por defecto sin esperar input.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const render = require('../lib/render');
|
|
19
|
-
const teclas = require('../lib/teclas');
|
|
20
|
-
const { colores, semantico, iconos } = require('../lib/colores');
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @param {object} opciones
|
|
24
|
-
* @param {string} opciones.titulo - Título mostrado arriba de la lista.
|
|
25
|
-
* @param {Array<{valor:string,etiqueta:string,descripcion?:string,badge?:string,deshabilitado?:boolean}>} opciones.items
|
|
26
|
-
* @param {number} [opciones.indiceDefault=0]
|
|
27
|
-
* @param {string} [opciones.hintAtajos] - Hint adicional en el pie (opcional).
|
|
28
|
-
* @returns {Promise<string|null>} - Valor seleccionado o null si se canceló.
|
|
29
|
-
*/
|
|
30
|
-
function selectorUnico(opciones) {
|
|
31
|
-
const items = opciones.items || [];
|
|
32
|
-
const titulo = opciones.titulo || 'Selecciona una opción:';
|
|
33
|
-
let cursor = opciones.indiceDefault || 0;
|
|
34
|
-
|
|
35
|
-
// Asegurar que el cursor inicial caiga en un item habilitado
|
|
36
|
-
while (cursor < items.length && items[cursor]?.deshabilitado) cursor++;
|
|
37
|
-
if (cursor >= items.length) cursor = 0;
|
|
38
|
-
|
|
39
|
-
return new Promise((resolve) => {
|
|
40
|
-
if (!render.ES_TTY || !process.stdin.isTTY) {
|
|
41
|
-
// No-TTY: devolver el item por defecto sin renderizar
|
|
42
|
-
const valor = items[cursor]?.valor ?? items[0]?.valor ?? null;
|
|
43
|
-
resolve(valor);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function _renderizar() {
|
|
48
|
-
render.limpiarPantalla();
|
|
49
|
-
const { cols, rows } = render.obtenerDimensiones();
|
|
50
|
-
|
|
51
|
-
// Título
|
|
52
|
-
render.escribirEn(2, 3, semantico.titulo(titulo));
|
|
53
|
-
|
|
54
|
-
// Items
|
|
55
|
-
const filaInicio = 4;
|
|
56
|
-
const maxVisible = Math.min(items.length, rows - 6);
|
|
57
|
-
let scrollOffset = 0;
|
|
58
|
-
if (cursor < scrollOffset) scrollOffset = cursor;
|
|
59
|
-
if (cursor >= scrollOffset + maxVisible) scrollOffset = cursor - maxVisible + 1;
|
|
60
|
-
|
|
61
|
-
for (let i = scrollOffset; i < Math.min(items.length, scrollOffset + maxVisible); i++) {
|
|
62
|
-
const item = items[i];
|
|
63
|
-
const esCursor = i === cursor;
|
|
64
|
-
const filaItem = filaInicio + (i - scrollOffset);
|
|
65
|
-
|
|
66
|
-
let prefijo;
|
|
67
|
-
if (item.deshabilitado) {
|
|
68
|
-
prefijo = colores.dim(' ');
|
|
69
|
-
} else if (esCursor) {
|
|
70
|
-
prefijo = semantico.cursor(iconos.cursor + ' ');
|
|
71
|
-
} else {
|
|
72
|
-
prefijo = ' ';
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Etiqueta principal
|
|
76
|
-
let etiqueta = item.etiqueta;
|
|
77
|
-
if (item.deshabilitado) {
|
|
78
|
-
etiqueta = colores.dim(etiqueta);
|
|
79
|
-
} else if (esCursor) {
|
|
80
|
-
etiqueta = colores.negrita(etiqueta);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Badge opcional
|
|
84
|
-
const badge = item.badge ? ' ' + semantico.badge(item.badge) : '';
|
|
85
|
-
|
|
86
|
-
render.escribirEn(filaItem, 3, prefijo + etiqueta + badge);
|
|
87
|
-
|
|
88
|
-
// Descripción solo del item bajo cursor (panel inferior)
|
|
89
|
-
if (esCursor && item.descripcion && !item.deshabilitado) {
|
|
90
|
-
const filaDesc = filaInicio + maxVisible + 1;
|
|
91
|
-
const maxAnchoDesc = cols - 6;
|
|
92
|
-
const desc = render.recortar(colores.dim(item.descripcion), maxAnchoDesc);
|
|
93
|
-
render.escribirEn(filaDesc, 3, desc);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Indicador de scroll si hay más items abajo
|
|
98
|
-
if (scrollOffset + maxVisible < items.length) {
|
|
99
|
-
render.escribirEn(filaInicio + maxVisible, 3,
|
|
100
|
-
colores.dim(`${iconos.flechaAbajo} ${items.length - scrollOffset - maxVisible} más`));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Pie con atajos
|
|
104
|
-
const atajos = [
|
|
105
|
-
['↑↓', 'navegar'],
|
|
106
|
-
['Enter', 'elegir'],
|
|
107
|
-
['Esc', 'cancelar'],
|
|
108
|
-
];
|
|
109
|
-
if (opciones.hintAtajos) atajos.push([opciones.hintAtajos, '']);
|
|
110
|
-
render.dibujarPiePagina(atajos);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
render.iniciarModoTui();
|
|
114
|
-
_renderizar();
|
|
115
|
-
|
|
116
|
-
const onResize = () => _renderizar();
|
|
117
|
-
process.stdout.on('resize', onResize);
|
|
118
|
-
|
|
119
|
-
const teclado = teclas.crearTeclado();
|
|
120
|
-
|
|
121
|
-
function _moverArriba() {
|
|
122
|
-
// Saltar items deshabilitados
|
|
123
|
-
let nuevo = cursor;
|
|
124
|
-
do {
|
|
125
|
-
nuevo = nuevo > 0 ? nuevo - 1 : items.length - 1;
|
|
126
|
-
} while (nuevo !== cursor && items[nuevo]?.deshabilitado);
|
|
127
|
-
cursor = nuevo;
|
|
128
|
-
_renderizar();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function _moverAbajo() {
|
|
132
|
-
let nuevo = cursor;
|
|
133
|
-
do {
|
|
134
|
-
nuevo = nuevo < items.length - 1 ? nuevo + 1 : 0;
|
|
135
|
-
} while (nuevo !== cursor && items[nuevo]?.deshabilitado);
|
|
136
|
-
cursor = nuevo;
|
|
137
|
-
_renderizar();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function _finalizar(valor) {
|
|
141
|
-
teclado.desactivar();
|
|
142
|
-
process.stdout.removeListener('resize', onResize);
|
|
143
|
-
render.salirModoTui();
|
|
144
|
-
resolve(valor);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
teclado.on('up', _moverArriba);
|
|
148
|
-
teclado.on('down', _moverAbajo);
|
|
149
|
-
teclado.on('return', () => {
|
|
150
|
-
const item = items[cursor];
|
|
151
|
-
if (item && !item.deshabilitado) _finalizar(item.valor);
|
|
152
|
-
});
|
|
153
|
-
teclado.on('escape', () => _finalizar(null));
|
|
154
|
-
teclado.activar();
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
module.exports = { selectorUnico };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Selector único (single-select) para la TUI.
|
|
5
|
+
* Más rico que `preguntarOpcion` de scripts/lib/ui.js: navegación con flechas,
|
|
6
|
+
* descripciones largas con wrap, soporte para items con etiquetas (badges),
|
|
7
|
+
* pie de página con atajos, indicador de cursor visible.
|
|
8
|
+
*
|
|
9
|
+
* Diseño:
|
|
10
|
+
* - Cada item: { valor, etiqueta, descripcion?, badge?, deshabilitado? }
|
|
11
|
+
* - El cursor se mueve solo entre items habilitados.
|
|
12
|
+
* - Enter resuelve con `items[cursor].valor`.
|
|
13
|
+
* - Escape resuelve con `null` (señal de "cancelado").
|
|
14
|
+
*
|
|
15
|
+
* No-TTY: resuelve inmediatamente con el item por defecto sin esperar input.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const render = require('../lib/render');
|
|
19
|
+
const teclas = require('../lib/teclas');
|
|
20
|
+
const { colores, semantico, iconos } = require('../lib/colores');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} opciones
|
|
24
|
+
* @param {string} opciones.titulo - Título mostrado arriba de la lista.
|
|
25
|
+
* @param {Array<{valor:string,etiqueta:string,descripcion?:string,badge?:string,deshabilitado?:boolean}>} opciones.items
|
|
26
|
+
* @param {number} [opciones.indiceDefault=0]
|
|
27
|
+
* @param {string} [opciones.hintAtajos] - Hint adicional en el pie (opcional).
|
|
28
|
+
* @returns {Promise<string|null>} - Valor seleccionado o null si se canceló.
|
|
29
|
+
*/
|
|
30
|
+
function selectorUnico(opciones) {
|
|
31
|
+
const items = opciones.items || [];
|
|
32
|
+
const titulo = opciones.titulo || 'Selecciona una opción:';
|
|
33
|
+
let cursor = opciones.indiceDefault || 0;
|
|
34
|
+
|
|
35
|
+
// Asegurar que el cursor inicial caiga en un item habilitado
|
|
36
|
+
while (cursor < items.length && items[cursor]?.deshabilitado) cursor++;
|
|
37
|
+
if (cursor >= items.length) cursor = 0;
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
if (!render.ES_TTY || !process.stdin.isTTY) {
|
|
41
|
+
// No-TTY: devolver el item por defecto sin renderizar
|
|
42
|
+
const valor = items[cursor]?.valor ?? items[0]?.valor ?? null;
|
|
43
|
+
resolve(valor);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _renderizar() {
|
|
48
|
+
render.limpiarPantalla();
|
|
49
|
+
const { cols, rows } = render.obtenerDimensiones();
|
|
50
|
+
|
|
51
|
+
// Título
|
|
52
|
+
render.escribirEn(2, 3, semantico.titulo(titulo));
|
|
53
|
+
|
|
54
|
+
// Items
|
|
55
|
+
const filaInicio = 4;
|
|
56
|
+
const maxVisible = Math.min(items.length, rows - 6);
|
|
57
|
+
let scrollOffset = 0;
|
|
58
|
+
if (cursor < scrollOffset) scrollOffset = cursor;
|
|
59
|
+
if (cursor >= scrollOffset + maxVisible) scrollOffset = cursor - maxVisible + 1;
|
|
60
|
+
|
|
61
|
+
for (let i = scrollOffset; i < Math.min(items.length, scrollOffset + maxVisible); i++) {
|
|
62
|
+
const item = items[i];
|
|
63
|
+
const esCursor = i === cursor;
|
|
64
|
+
const filaItem = filaInicio + (i - scrollOffset);
|
|
65
|
+
|
|
66
|
+
let prefijo;
|
|
67
|
+
if (item.deshabilitado) {
|
|
68
|
+
prefijo = colores.dim(' ');
|
|
69
|
+
} else if (esCursor) {
|
|
70
|
+
prefijo = semantico.cursor(iconos.cursor + ' ');
|
|
71
|
+
} else {
|
|
72
|
+
prefijo = ' ';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Etiqueta principal
|
|
76
|
+
let etiqueta = item.etiqueta;
|
|
77
|
+
if (item.deshabilitado) {
|
|
78
|
+
etiqueta = colores.dim(etiqueta);
|
|
79
|
+
} else if (esCursor) {
|
|
80
|
+
etiqueta = colores.negrita(etiqueta);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Badge opcional
|
|
84
|
+
const badge = item.badge ? ' ' + semantico.badge(item.badge) : '';
|
|
85
|
+
|
|
86
|
+
render.escribirEn(filaItem, 3, prefijo + etiqueta + badge);
|
|
87
|
+
|
|
88
|
+
// Descripción solo del item bajo cursor (panel inferior)
|
|
89
|
+
if (esCursor && item.descripcion && !item.deshabilitado) {
|
|
90
|
+
const filaDesc = filaInicio + maxVisible + 1;
|
|
91
|
+
const maxAnchoDesc = cols - 6;
|
|
92
|
+
const desc = render.recortar(colores.dim(item.descripcion), maxAnchoDesc);
|
|
93
|
+
render.escribirEn(filaDesc, 3, desc);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Indicador de scroll si hay más items abajo
|
|
98
|
+
if (scrollOffset + maxVisible < items.length) {
|
|
99
|
+
render.escribirEn(filaInicio + maxVisible, 3,
|
|
100
|
+
colores.dim(`${iconos.flechaAbajo} ${items.length - scrollOffset - maxVisible} más`));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Pie con atajos
|
|
104
|
+
const atajos = [
|
|
105
|
+
['↑↓', 'navegar'],
|
|
106
|
+
['Enter', 'elegir'],
|
|
107
|
+
['Esc', 'cancelar'],
|
|
108
|
+
];
|
|
109
|
+
if (opciones.hintAtajos) atajos.push([opciones.hintAtajos, '']);
|
|
110
|
+
render.dibujarPiePagina(atajos);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
render.iniciarModoTui();
|
|
114
|
+
_renderizar();
|
|
115
|
+
|
|
116
|
+
const onResize = () => _renderizar();
|
|
117
|
+
process.stdout.on('resize', onResize);
|
|
118
|
+
|
|
119
|
+
const teclado = teclas.crearTeclado();
|
|
120
|
+
|
|
121
|
+
function _moverArriba() {
|
|
122
|
+
// Saltar items deshabilitados
|
|
123
|
+
let nuevo = cursor;
|
|
124
|
+
do {
|
|
125
|
+
nuevo = nuevo > 0 ? nuevo - 1 : items.length - 1;
|
|
126
|
+
} while (nuevo !== cursor && items[nuevo]?.deshabilitado);
|
|
127
|
+
cursor = nuevo;
|
|
128
|
+
_renderizar();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function _moverAbajo() {
|
|
132
|
+
let nuevo = cursor;
|
|
133
|
+
do {
|
|
134
|
+
nuevo = nuevo < items.length - 1 ? nuevo + 1 : 0;
|
|
135
|
+
} while (nuevo !== cursor && items[nuevo]?.deshabilitado);
|
|
136
|
+
cursor = nuevo;
|
|
137
|
+
_renderizar();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function _finalizar(valor) {
|
|
141
|
+
teclado.desactivar();
|
|
142
|
+
process.stdout.removeListener('resize', onResize);
|
|
143
|
+
render.salirModoTui();
|
|
144
|
+
resolve(valor);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
teclado.on('up', _moverArriba);
|
|
148
|
+
teclado.on('down', _moverAbajo);
|
|
149
|
+
teclado.on('return', () => {
|
|
150
|
+
const item = items[cursor];
|
|
151
|
+
if (item && !item.deshabilitado) _finalizar(item.valor);
|
|
152
|
+
});
|
|
153
|
+
teclado.on('escape', () => _finalizar(null));
|
|
154
|
+
teclado.activar();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = { selectorUnico };
|