@saulwade/swl-ses 2.2.4 → 2.3.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 +48 -7
- package/README.md +3 -3
- package/agentes/accesibilidad-wcag-swl.md +690 -690
- package/agentes/arquitecto-swl.md +267 -267
- package/agentes/auto-evolucion-swl.md +908 -908
- package/agentes/backend-api-swl.md +472 -472
- package/agentes/backend-csharp-swl.md +420 -420
- package/agentes/backend-go-swl.md +390 -390
- package/agentes/backend-java-swl.md +281 -281
- package/agentes/backend-node-swl.md +479 -479
- package/agentes/backend-python-swl.md +605 -605
- package/agentes/backend-rust-swl.md +364 -364
- package/agentes/backend-workers-swl.md +482 -482
- package/agentes/cloud-infra-swl.md +509 -509
- package/agentes/consolidador-swl.md +541 -541
- package/agentes/datos-swl.md +605 -605
- package/agentes/depurador-swl.md +352 -352
- package/agentes/devops-ci-swl.md +400 -400
- package/agentes/disenador-ui-swl.md +569 -569
- package/agentes/documentador-swl.md +345 -345
- package/agentes/frontend-angular-swl.md +621 -621
- package/agentes/frontend-css-swl.md +716 -716
- package/agentes/frontend-react-swl.md +692 -692
- package/agentes/frontend-swl.md +496 -496
- package/agentes/frontend-tailwind-swl.md +826 -826
- package/agentes/gh-fix-ci-swl.md +2 -2
- package/agentes/implementador-swl.md +328 -328
- package/agentes/investigador-swl.md +432 -432
- package/agentes/investigador-ux-swl.md +505 -505
- package/agentes/llm-apps-swl.md +278 -278
- package/agentes/migrador-swl.md +442 -442
- package/agentes/mobile-android-swl.md +511 -511
- package/agentes/mobile-cross-swl.md +541 -541
- package/agentes/mobile-ios-swl.md +502 -502
- package/agentes/mobile-testing-swl.md +302 -302
- package/agentes/nemesis-auditor-swl.md +285 -285
- package/agentes/notificador-swl.md +918 -918
- package/agentes/observabilidad-swl.md +438 -438
- package/agentes/orquestador-swl.md +1026 -1026
- package/agentes/pagos-swl.md +310 -310
- package/agentes/perfilador-usuario-swl.md +321 -321
- package/agentes/planificador-swl.md +399 -399
- package/agentes/producto-prd-swl.md +589 -589
- package/agentes/red-team-swl.md +1 -1
- package/agentes/release-manager-swl.md +590 -590
- package/agentes/rendimiento-swl.md +713 -713
- package/agentes/resolutor-build-swl.md +245 -245
- package/agentes/revisor-angular-swl.md +278 -278
- package/agentes/revisor-codigo-swl.md +505 -505
- package/agentes/revisor-csharp-swl.md +264 -264
- package/agentes/revisor-go-swl.md +259 -259
- package/agentes/revisor-java-swl.md +257 -257
- package/agentes/revisor-kotlin-swl.md +273 -273
- package/agentes/revisor-nextjs-swl.md +281 -281
- package/agentes/revisor-php-swl.md +271 -271
- package/agentes/revisor-react-swl.md +278 -278
- package/agentes/revisor-rust-swl.md +346 -346
- package/agentes/revisor-seguridad-swl.md +399 -399
- package/agentes/revisor-swift-swl.md +268 -268
- package/agentes/revisor-typescript-swl.md +346 -346
- package/agentes/sre-swl.md +291 -291
- package/agentes/tdd-qa-swl.md +393 -393
- package/comandos/swl/aprobar-plan.md +146 -141
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/predecir.md +139 -139
- package/comandos/swl/release.md +30 -8
- package/comandos/swl/sesiones.md +1 -1
- package/comandos/swl/status.md +343 -333
- 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 +20 -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/ejecutar-fase/SKILL.md +564 -541
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- 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/extractor-de-aprendizajes/SKILL.md +3 -3
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/recursos/disciplina-harness-regla.md +252 -252
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- 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/planear-fase/SKILL.md +15 -1
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +255 -255
- package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
- package/habilidades/proceso-debate-adversarial/SKILL.md +191 -164
- 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/SKILL.md +33 -1
- 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/captura-acciones-post.js +151 -0
- package/hooks/captura-acciones-session.js +155 -0
- 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 +144 -144
- package/hooks/guardrail-modelo.js +1 -1
- 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/briefing.js +512 -474
- package/hooks/lib/captura-acciones.js +392 -0
- 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/linea-estado.js +2 -0
- package/hooks/registro-turnos.js +209 -209
- package/hooks/session-briefing.js +98 -98
- package/hooks/spec-gate.js +211 -211
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/tdd-gate.js +241 -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 +2 -2
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +2291 -1637
- package/manifiestos/hooks-config.json +18 -0
- package/manifiestos/modulos.json +4 -0
- package/manifiestos/planning-paths.json +44 -44
- package/manifiestos/skills-lock.json +1282 -1282
- package/package.json +2 -2
- 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 +2 -2
- 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/memoria-consolidada.md +41 -0
- package/reglas/monitor-ci.md +309 -309
- package/reglas/patrones.md +6 -6
- package/reglas/seguridad-agentes.md +66 -0
- 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 +3 -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/instinct.schema.json +161 -152
- 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/bootstrap-instintos.js +25 -7
- 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/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/expandir-targets.js +71 -71
- 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/gitignore-manifest.js +44 -11
- 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/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/skill-metrics.js +41 -1
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/tool-cost-analyzer.js +1 -0
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- 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/verificar-trazabilidad.js +329 -298
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Expansión de --target (CSV) y --all-runtimes para multi-target install.
|
|
5
|
-
*
|
|
6
|
-
* ADR-0019 Sub-fase 2.5.
|
|
7
|
-
*
|
|
8
|
-
* Extraído de bin/swl-ses.js para permitir tests unitarios. NUNCA debe tener
|
|
9
|
-
* side effects fuera de logging — solo manipula la lista de strings.
|
|
10
|
-
*
|
|
11
|
-
* @module scripts/lib/expandir-targets
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { listarRuntimesInstalables, RUNTIMES } = require('./detectar-runtime');
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Expande las opciones del CLI a un array de target IDs.
|
|
18
|
-
*
|
|
19
|
-
* Reglas:
|
|
20
|
-
* - `all_runtimes` tiene prioridad sobre `target`. Si ambos vienen, se loggea
|
|
21
|
-
* aviso (vía `logger.warn`) y se usa `all_runtimes`.
|
|
22
|
-
* - `target='a,b,c'` → ['a','b','c'].
|
|
23
|
-
* - `target='claude'` → ['claude'].
|
|
24
|
-
* - Sin nada → ['claude'] (default histórico, backward-compat).
|
|
25
|
-
* - Duplicados se deduplican preservando el primer orden.
|
|
26
|
-
* - Targets desconocidos se omiten con aviso (logger.warn).
|
|
27
|
-
*
|
|
28
|
-
* @param {object} opciones - Objeto con `target` (string|undefined) y `all_runtimes` (bool).
|
|
29
|
-
* @param {object} [logger] - Sustituible para tests. Default console.
|
|
30
|
-
* @returns {{ targets: string[], omitidos: string[], errores: string[] }}
|
|
31
|
-
*/
|
|
32
|
-
function expandirTargets(opciones, logger) {
|
|
33
|
-
const log = logger || console;
|
|
34
|
-
const omitidos = [];
|
|
35
|
-
const errores = [];
|
|
36
|
-
|
|
37
|
-
let candidatos;
|
|
38
|
-
if (opciones.all_runtimes) {
|
|
39
|
-
if (opciones.target && typeof opciones.target === 'string') {
|
|
40
|
-
log.warn && log.warn('[expandir-targets] --all-runtimes tiene prioridad sobre --target; se ignora --target.');
|
|
41
|
-
}
|
|
42
|
-
candidatos = listarRuntimesInstalables();
|
|
43
|
-
} else if (typeof opciones.target === 'string' && opciones.target.includes(',')) {
|
|
44
|
-
candidatos = opciones.target.split(',').map(s => s.trim()).filter(Boolean);
|
|
45
|
-
} else if (typeof opciones.target === 'string' && opciones.target.length > 0) {
|
|
46
|
-
candidatos = [opciones.target.trim()];
|
|
47
|
-
} else {
|
|
48
|
-
candidatos = ['claude']; // backward-compat
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const visto = new Set();
|
|
52
|
-
const targets = [];
|
|
53
|
-
for (const t of candidatos) {
|
|
54
|
-
if (visto.has(t)) continue;
|
|
55
|
-
visto.add(t);
|
|
56
|
-
if (!RUNTIMES[t]) {
|
|
57
|
-
log.warn && log.warn(`[expandir-targets] Target desconocido omitido: "${t}". Disponibles: ${Object.keys(RUNTIMES).join(', ')}`);
|
|
58
|
-
omitidos.push(t);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
targets.push(t);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (targets.length === 0) {
|
|
65
|
-
errores.push('Ningún target válido tras expansión.');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return { targets, omitidos, errores };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
module.exports = { expandirTargets };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Expansión de --target (CSV) y --all-runtimes para multi-target install.
|
|
5
|
+
*
|
|
6
|
+
* ADR-0019 Sub-fase 2.5.
|
|
7
|
+
*
|
|
8
|
+
* Extraído de bin/swl-ses.js para permitir tests unitarios. NUNCA debe tener
|
|
9
|
+
* side effects fuera de logging — solo manipula la lista de strings.
|
|
10
|
+
*
|
|
11
|
+
* @module scripts/lib/expandir-targets
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { listarRuntimesInstalables, RUNTIMES } = require('./detectar-runtime');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Expande las opciones del CLI a un array de target IDs.
|
|
18
|
+
*
|
|
19
|
+
* Reglas:
|
|
20
|
+
* - `all_runtimes` tiene prioridad sobre `target`. Si ambos vienen, se loggea
|
|
21
|
+
* aviso (vía `logger.warn`) y se usa `all_runtimes`.
|
|
22
|
+
* - `target='a,b,c'` → ['a','b','c'].
|
|
23
|
+
* - `target='claude'` → ['claude'].
|
|
24
|
+
* - Sin nada → ['claude'] (default histórico, backward-compat).
|
|
25
|
+
* - Duplicados se deduplican preservando el primer orden.
|
|
26
|
+
* - Targets desconocidos se omiten con aviso (logger.warn).
|
|
27
|
+
*
|
|
28
|
+
* @param {object} opciones - Objeto con `target` (string|undefined) y `all_runtimes` (bool).
|
|
29
|
+
* @param {object} [logger] - Sustituible para tests. Default console.
|
|
30
|
+
* @returns {{ targets: string[], omitidos: string[], errores: string[] }}
|
|
31
|
+
*/
|
|
32
|
+
function expandirTargets(opciones, logger) {
|
|
33
|
+
const log = logger || console;
|
|
34
|
+
const omitidos = [];
|
|
35
|
+
const errores = [];
|
|
36
|
+
|
|
37
|
+
let candidatos;
|
|
38
|
+
if (opciones.all_runtimes) {
|
|
39
|
+
if (opciones.target && typeof opciones.target === 'string') {
|
|
40
|
+
log.warn && log.warn('[expandir-targets] --all-runtimes tiene prioridad sobre --target; se ignora --target.');
|
|
41
|
+
}
|
|
42
|
+
candidatos = listarRuntimesInstalables();
|
|
43
|
+
} else if (typeof opciones.target === 'string' && opciones.target.includes(',')) {
|
|
44
|
+
candidatos = opciones.target.split(',').map(s => s.trim()).filter(Boolean);
|
|
45
|
+
} else if (typeof opciones.target === 'string' && opciones.target.length > 0) {
|
|
46
|
+
candidatos = [opciones.target.trim()];
|
|
47
|
+
} else {
|
|
48
|
+
candidatos = ['claude']; // backward-compat
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const visto = new Set();
|
|
52
|
+
const targets = [];
|
|
53
|
+
for (const t of candidatos) {
|
|
54
|
+
if (visto.has(t)) continue;
|
|
55
|
+
visto.add(t);
|
|
56
|
+
if (!RUNTIMES[t]) {
|
|
57
|
+
log.warn && log.warn(`[expandir-targets] Target desconocido omitido: "${t}". Disponibles: ${Object.keys(RUNTIMES).join(', ')}`);
|
|
58
|
+
omitidos.push(t);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
targets.push(t);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (targets.length === 0) {
|
|
65
|
+
errores.push('Ningún target válido tras expansión.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { targets, omitidos, errores };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { expandirTargets };
|
|
@@ -1,249 +1,249 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* gate-hooks-requires.js
|
|
5
|
-
*
|
|
6
|
-
* Gate de release: cruza los `require()` relativos de los hooks DISTRIBUIDOS
|
|
7
|
-
* (`hooks/**` registrados en `manifiestos/modulos.json`) que apuntan a
|
|
8
|
-
* `scripts/**`, y verifica que cada lib objetivo — incluidas sus dependencias
|
|
9
|
-
* TRANSITIVAS — esté registrada en `modulos.json` y cubierta en todos los
|
|
10
|
-
* perfiles que instalan el hook.
|
|
11
|
-
*
|
|
12
|
-
* Cierra la clase de bug del caso check-update (2026-06-12): el hook requería
|
|
13
|
-
* `scripts/lib/npm-version.js` (que a su vez requería `paquetes-conocidos.js`),
|
|
14
|
-
* ninguna estaba en módulo alguno, y el wrapper de settings.json traga
|
|
15
|
-
* MODULE_NOT_FOUND → el hook murió en silencio en TODA instalación destino
|
|
16
|
-
* desde su creación. Misma familia que `ejecutarGateBinImports` (bin/ vs
|
|
17
|
-
* `package.json#files`), en el eje instalador→destino.
|
|
18
|
-
*
|
|
19
|
-
* Hallazgos (todos bloqueantes — un hook distribuido con dependencia rota es
|
|
20
|
-
* un artefacto roto para el usuario del paquete):
|
|
21
|
-
* - `lib-inexistente`: el require apunta a un archivo que no existe en repo.
|
|
22
|
-
* - `no-registrada`: la lib existe pero no está en ningún módulo.
|
|
23
|
-
* - `sin-cobertura`: la lib está en módulo(s), pero algún perfil instala
|
|
24
|
-
* el módulo del hook sin ningún módulo de la lib.
|
|
25
|
-
*
|
|
26
|
-
* Los hooks NO registrados en modulos.json se ignoran: no se distribuyen, su
|
|
27
|
-
* require solo corre en el repo madre donde scripts/ existe completo.
|
|
28
|
-
*
|
|
29
|
-
* Funciones puras zero-deps + entrypoint CLI `--json`.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
const fs = require('node:fs');
|
|
33
|
-
const path = require('node:path');
|
|
34
|
-
|
|
35
|
-
const RE_REQUIRE_RELATIVO = /require\(\s*['"](\.\.?\/[^'"]+)['"]\s*\)/g;
|
|
36
|
-
const MAX_PROFUNDIDAD_TRANSITIVA = 10;
|
|
37
|
-
|
|
38
|
-
/** Extrae las rutas de los require() relativos de un código fuente. */
|
|
39
|
-
function extraerRequiresRelativos(codigo) {
|
|
40
|
-
const out = [];
|
|
41
|
-
let m;
|
|
42
|
-
RE_REQUIRE_RELATIVO.lastIndex = 0;
|
|
43
|
-
while ((m = RE_REQUIRE_RELATIVO.exec(codigo)) !== null) out.push(m[1]);
|
|
44
|
-
return out;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Resuelve un require relativo desde un archivo (ruta repo-relativa posix) a
|
|
49
|
-
* una ruta repo-relativa del objetivo, restringida a `scripts/`.
|
|
50
|
-
* @returns {string|null} 'scripts/lib/x.js' o null si no cae en scripts/.
|
|
51
|
-
*/
|
|
52
|
-
function resolverObjetivoScripts(archivoRel, requireRel, baseDir) {
|
|
53
|
-
const dir = path.posix.dirname(archivoRel.split(path.sep).join('/'));
|
|
54
|
-
let objetivo = path.posix.normalize(path.posix.join(dir, requireRel));
|
|
55
|
-
if (!objetivo.startsWith('scripts/')) return null;
|
|
56
|
-
// Resolución estilo Node: exacto → +.js → /index.js
|
|
57
|
-
const candidatos = objetivo.endsWith('.js')
|
|
58
|
-
? [objetivo]
|
|
59
|
-
: [objetivo, objetivo + '.js', objetivo + '/index.js'];
|
|
60
|
-
for (const c of candidatos) {
|
|
61
|
-
if (fs.existsSync(path.join(baseDir, c))) return c;
|
|
62
|
-
}
|
|
63
|
-
// No existe: devolver el candidato .js canónico para reportar lib-inexistente.
|
|
64
|
-
return objetivo.endsWith('.js') ? objetivo : objetivo + '.js';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Expande una lib de scripts/ a su cierre transitivo de requires relativos
|
|
69
|
-
* que permanezcan dentro de scripts/.
|
|
70
|
-
* @returns {string[]} rutas repo-relativas (incluye la lib raíz).
|
|
71
|
-
*/
|
|
72
|
-
function expandirTransitivas(baseDir, libRel, _vistos, _prof = 0) {
|
|
73
|
-
const vistos = _vistos || new Set();
|
|
74
|
-
if (vistos.has(libRel) || _prof > MAX_PROFUNDIDAD_TRANSITIVA) return [...vistos];
|
|
75
|
-
vistos.add(libRel);
|
|
76
|
-
let codigo = null;
|
|
77
|
-
try {
|
|
78
|
-
codigo = fs.readFileSync(path.join(baseDir, libRel), 'utf8');
|
|
79
|
-
} catch {
|
|
80
|
-
return [...vistos]; // inexistente: se reporta como hallazgo aguas arriba
|
|
81
|
-
}
|
|
82
|
-
for (const req of extraerRequiresRelativos(codigo)) {
|
|
83
|
-
const objetivo = resolverObjetivoScripts(libRel, req, baseDir);
|
|
84
|
-
if (objetivo) expandirTransitivas(baseDir, objetivo, vistos, _prof + 1);
|
|
85
|
-
}
|
|
86
|
-
return [...vistos];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Mapa archivo repo-relativo → array de nombres de módulo que lo declaran. */
|
|
90
|
-
function _mapaArchivoModulos(modulos) {
|
|
91
|
-
const mapa = new Map();
|
|
92
|
-
for (const [nombre, mod] of Object.entries(modulos)) {
|
|
93
|
-
for (const archivo of mod.archivos || []) {
|
|
94
|
-
if (!mapa.has(archivo)) mapa.set(archivo, []);
|
|
95
|
-
mapa.get(archivo).push(nombre);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return mapa;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Lista recursiva de archivos .js bajo hooks/ (repo-relativos posix). */
|
|
102
|
-
function _listarHooks(baseDir) {
|
|
103
|
-
const out = [];
|
|
104
|
-
const raiz = path.join(baseDir, 'hooks');
|
|
105
|
-
function walk(dir, rel) {
|
|
106
|
-
let entradas;
|
|
107
|
-
try {
|
|
108
|
-
entradas = fs.readdirSync(dir, { withFileTypes: true });
|
|
109
|
-
} catch {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
for (const e of entradas) {
|
|
113
|
-
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
114
|
-
if (e.isDirectory()) walk(path.join(dir, e.name), r);
|
|
115
|
-
else if (e.isFile() && e.name.endsWith('.js')) out.push(`hooks/${r}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
walk(raiz, '');
|
|
119
|
-
return out;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Evalúa los requires hooks→scripts contra modulos.json + perfiles.json.
|
|
124
|
-
* @returns {{disponible:boolean, hooksDistribuidos?:number, dependencias?:Array,
|
|
125
|
-
* hallazgos?:Array, error?:string}}
|
|
126
|
-
*/
|
|
127
|
-
function evaluarHooksRequires(baseDir = process.cwd(), opts = {}) {
|
|
128
|
-
let modulos, perfiles;
|
|
129
|
-
try {
|
|
130
|
-
modulos = (opts.modulos ||
|
|
131
|
-
JSON.parse(fs.readFileSync(path.join(baseDir, 'manifiestos', 'modulos.json'), 'utf8')).modulos);
|
|
132
|
-
const p = opts.perfiles ||
|
|
133
|
-
JSON.parse(fs.readFileSync(path.join(baseDir, 'manifiestos', 'perfiles.json'), 'utf8'));
|
|
134
|
-
perfiles = p.perfiles || p;
|
|
135
|
-
} catch (err) {
|
|
136
|
-
return { disponible: false, error: 'manifiestos ilegibles: ' + err.message };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const mapa = _mapaArchivoModulos(modulos);
|
|
140
|
-
const hooks = _listarHooks(baseDir).filter((h) => mapa.has(h)); // solo distribuidos
|
|
141
|
-
|
|
142
|
-
const dependencias = [];
|
|
143
|
-
const hallazgos = [];
|
|
144
|
-
|
|
145
|
-
for (const hook of hooks) {
|
|
146
|
-
let codigo;
|
|
147
|
-
try {
|
|
148
|
-
codigo = fs.readFileSync(path.join(baseDir, hook), 'utf8');
|
|
149
|
-
} catch {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
const modulosHook = mapa.get(hook);
|
|
153
|
-
|
|
154
|
-
const objetivosDirectos = new Set();
|
|
155
|
-
for (const req of extraerRequiresRelativos(codigo)) {
|
|
156
|
-
const objetivo = resolverObjetivoScripts(hook, req, baseDir);
|
|
157
|
-
if (objetivo) objetivosDirectos.add(objetivo);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Cierre transitivo de cada objetivo directo.
|
|
161
|
-
const objetivos = new Set();
|
|
162
|
-
for (const o of objetivosDirectos) {
|
|
163
|
-
for (const t of expandirTransitivas(baseDir, o)) objetivos.add(t);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
for (const lib of [...objetivos].sort()) {
|
|
167
|
-
const existe = fs.existsSync(path.join(baseDir, lib));
|
|
168
|
-
const modulosLib = mapa.get(lib) || [];
|
|
169
|
-
const dep = { hook, lib, modulosHook, modulosLib, ok: true };
|
|
170
|
-
|
|
171
|
-
if (!existe) {
|
|
172
|
-
dep.ok = false;
|
|
173
|
-
hallazgos.push({
|
|
174
|
-
tipo: 'lib-inexistente',
|
|
175
|
-
hook,
|
|
176
|
-
lib,
|
|
177
|
-
detalle: `${hook} requiere ${lib} pero el archivo no existe en el repo`,
|
|
178
|
-
});
|
|
179
|
-
} else if (modulosLib.length === 0) {
|
|
180
|
-
dep.ok = false;
|
|
181
|
-
hallazgos.push({
|
|
182
|
-
tipo: 'no-registrada',
|
|
183
|
-
hook,
|
|
184
|
-
lib,
|
|
185
|
-
detalle:
|
|
186
|
-
`${hook} (módulo ${modulosHook.join(',')}) requiere ${lib} que NO está ` +
|
|
187
|
-
`en ningún módulo de modulos.json — el hook morirá en silencio en el destino`,
|
|
188
|
-
});
|
|
189
|
-
} else {
|
|
190
|
-
// Cobertura: todo perfil que instala el hook debe instalar la lib.
|
|
191
|
-
const perfilesSinLib = [];
|
|
192
|
-
for (const [nombrePerfil, perfil] of Object.entries(perfiles)) {
|
|
193
|
-
const mods = perfil.modulos || [];
|
|
194
|
-
const instalaHook = modulosHook.some((m) => mods.includes(m));
|
|
195
|
-
const instalaLib = modulosLib.some((m) => mods.includes(m));
|
|
196
|
-
if (instalaHook && !instalaLib) perfilesSinLib.push(nombrePerfil);
|
|
197
|
-
}
|
|
198
|
-
if (perfilesSinLib.length > 0) {
|
|
199
|
-
dep.ok = false;
|
|
200
|
-
dep.perfilesSinLib = perfilesSinLib;
|
|
201
|
-
hallazgos.push({
|
|
202
|
-
tipo: 'sin-cobertura',
|
|
203
|
-
hook,
|
|
204
|
-
lib,
|
|
205
|
-
detalle:
|
|
206
|
-
`${lib} (módulo ${modulosLib.join(',')}) no llega a los perfiles que ` +
|
|
207
|
-
`instalan ${hook}: ${perfilesSinLib.join(', ')}`,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
dependencias.push(dep);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
disponible: true,
|
|
217
|
-
hooksDistribuidos: hooks.length,
|
|
218
|
-
dependencias,
|
|
219
|
-
hallazgos,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
module.exports = {
|
|
224
|
-
evaluarHooksRequires,
|
|
225
|
-
extraerRequiresRelativos,
|
|
226
|
-
resolverObjetivoScripts,
|
|
227
|
-
expandirTransitivas,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// Entrypoint CLI: node scripts/lib/gate-hooks-requires.js [--json] [baseDir]
|
|
231
|
-
if (!require.main || require.main === module) {
|
|
232
|
-
const args = process.argv.slice(2);
|
|
233
|
-
const json = args.includes('--json');
|
|
234
|
-
const baseDir = args.find((a) => !a.startsWith('--')) || process.cwd();
|
|
235
|
-
const r = evaluarHooksRequires(baseDir);
|
|
236
|
-
|
|
237
|
-
if (json) {
|
|
238
|
-
process.stdout.write(JSON.stringify(r, null, 2) + '\n');
|
|
239
|
-
} else if (!r.disponible) {
|
|
240
|
-
process.stdout.write(`Gate hooks-requires no disponible: ${r.error}\n`);
|
|
241
|
-
} else {
|
|
242
|
-
process.stdout.write(
|
|
243
|
-
`Hooks distribuidos analizados: ${r.hooksDistribuidos} | ` +
|
|
244
|
-
`dependencias hooks→scripts: ${r.dependencias.length} | hallazgos: ${r.hallazgos.length}\n`
|
|
245
|
-
);
|
|
246
|
-
for (const h of r.hallazgos) process.stdout.write(` [${h.tipo}] ${h.detalle}\n`);
|
|
247
|
-
}
|
|
248
|
-
process.exit(r.disponible && r.hallazgos && r.hallazgos.length > 0 ? 1 : 0);
|
|
249
|
-
}
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* gate-hooks-requires.js
|
|
5
|
+
*
|
|
6
|
+
* Gate de release: cruza los `require()` relativos de los hooks DISTRIBUIDOS
|
|
7
|
+
* (`hooks/**` registrados en `manifiestos/modulos.json`) que apuntan a
|
|
8
|
+
* `scripts/**`, y verifica que cada lib objetivo — incluidas sus dependencias
|
|
9
|
+
* TRANSITIVAS — esté registrada en `modulos.json` y cubierta en todos los
|
|
10
|
+
* perfiles que instalan el hook.
|
|
11
|
+
*
|
|
12
|
+
* Cierra la clase de bug del caso check-update (2026-06-12): el hook requería
|
|
13
|
+
* `scripts/lib/npm-version.js` (que a su vez requería `paquetes-conocidos.js`),
|
|
14
|
+
* ninguna estaba en módulo alguno, y el wrapper de settings.json traga
|
|
15
|
+
* MODULE_NOT_FOUND → el hook murió en silencio en TODA instalación destino
|
|
16
|
+
* desde su creación. Misma familia que `ejecutarGateBinImports` (bin/ vs
|
|
17
|
+
* `package.json#files`), en el eje instalador→destino.
|
|
18
|
+
*
|
|
19
|
+
* Hallazgos (todos bloqueantes — un hook distribuido con dependencia rota es
|
|
20
|
+
* un artefacto roto para el usuario del paquete):
|
|
21
|
+
* - `lib-inexistente`: el require apunta a un archivo que no existe en repo.
|
|
22
|
+
* - `no-registrada`: la lib existe pero no está en ningún módulo.
|
|
23
|
+
* - `sin-cobertura`: la lib está en módulo(s), pero algún perfil instala
|
|
24
|
+
* el módulo del hook sin ningún módulo de la lib.
|
|
25
|
+
*
|
|
26
|
+
* Los hooks NO registrados en modulos.json se ignoran: no se distribuyen, su
|
|
27
|
+
* require solo corre en el repo madre donde scripts/ existe completo.
|
|
28
|
+
*
|
|
29
|
+
* Funciones puras zero-deps + entrypoint CLI `--json`.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const fs = require('node:fs');
|
|
33
|
+
const path = require('node:path');
|
|
34
|
+
|
|
35
|
+
const RE_REQUIRE_RELATIVO = /require\(\s*['"](\.\.?\/[^'"]+)['"]\s*\)/g;
|
|
36
|
+
const MAX_PROFUNDIDAD_TRANSITIVA = 10;
|
|
37
|
+
|
|
38
|
+
/** Extrae las rutas de los require() relativos de un código fuente. */
|
|
39
|
+
function extraerRequiresRelativos(codigo) {
|
|
40
|
+
const out = [];
|
|
41
|
+
let m;
|
|
42
|
+
RE_REQUIRE_RELATIVO.lastIndex = 0;
|
|
43
|
+
while ((m = RE_REQUIRE_RELATIVO.exec(codigo)) !== null) out.push(m[1]);
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resuelve un require relativo desde un archivo (ruta repo-relativa posix) a
|
|
49
|
+
* una ruta repo-relativa del objetivo, restringida a `scripts/`.
|
|
50
|
+
* @returns {string|null} 'scripts/lib/x.js' o null si no cae en scripts/.
|
|
51
|
+
*/
|
|
52
|
+
function resolverObjetivoScripts(archivoRel, requireRel, baseDir) {
|
|
53
|
+
const dir = path.posix.dirname(archivoRel.split(path.sep).join('/'));
|
|
54
|
+
let objetivo = path.posix.normalize(path.posix.join(dir, requireRel));
|
|
55
|
+
if (!objetivo.startsWith('scripts/')) return null;
|
|
56
|
+
// Resolución estilo Node: exacto → +.js → /index.js
|
|
57
|
+
const candidatos = objetivo.endsWith('.js')
|
|
58
|
+
? [objetivo]
|
|
59
|
+
: [objetivo, objetivo + '.js', objetivo + '/index.js'];
|
|
60
|
+
for (const c of candidatos) {
|
|
61
|
+
if (fs.existsSync(path.join(baseDir, c))) return c;
|
|
62
|
+
}
|
|
63
|
+
// No existe: devolver el candidato .js canónico para reportar lib-inexistente.
|
|
64
|
+
return objetivo.endsWith('.js') ? objetivo : objetivo + '.js';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Expande una lib de scripts/ a su cierre transitivo de requires relativos
|
|
69
|
+
* que permanezcan dentro de scripts/.
|
|
70
|
+
* @returns {string[]} rutas repo-relativas (incluye la lib raíz).
|
|
71
|
+
*/
|
|
72
|
+
function expandirTransitivas(baseDir, libRel, _vistos, _prof = 0) {
|
|
73
|
+
const vistos = _vistos || new Set();
|
|
74
|
+
if (vistos.has(libRel) || _prof > MAX_PROFUNDIDAD_TRANSITIVA) return [...vistos];
|
|
75
|
+
vistos.add(libRel);
|
|
76
|
+
let codigo = null;
|
|
77
|
+
try {
|
|
78
|
+
codigo = fs.readFileSync(path.join(baseDir, libRel), 'utf8');
|
|
79
|
+
} catch {
|
|
80
|
+
return [...vistos]; // inexistente: se reporta como hallazgo aguas arriba
|
|
81
|
+
}
|
|
82
|
+
for (const req of extraerRequiresRelativos(codigo)) {
|
|
83
|
+
const objetivo = resolverObjetivoScripts(libRel, req, baseDir);
|
|
84
|
+
if (objetivo) expandirTransitivas(baseDir, objetivo, vistos, _prof + 1);
|
|
85
|
+
}
|
|
86
|
+
return [...vistos];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Mapa archivo repo-relativo → array de nombres de módulo que lo declaran. */
|
|
90
|
+
function _mapaArchivoModulos(modulos) {
|
|
91
|
+
const mapa = new Map();
|
|
92
|
+
for (const [nombre, mod] of Object.entries(modulos)) {
|
|
93
|
+
for (const archivo of mod.archivos || []) {
|
|
94
|
+
if (!mapa.has(archivo)) mapa.set(archivo, []);
|
|
95
|
+
mapa.get(archivo).push(nombre);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return mapa;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Lista recursiva de archivos .js bajo hooks/ (repo-relativos posix). */
|
|
102
|
+
function _listarHooks(baseDir) {
|
|
103
|
+
const out = [];
|
|
104
|
+
const raiz = path.join(baseDir, 'hooks');
|
|
105
|
+
function walk(dir, rel) {
|
|
106
|
+
let entradas;
|
|
107
|
+
try {
|
|
108
|
+
entradas = fs.readdirSync(dir, { withFileTypes: true });
|
|
109
|
+
} catch {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
for (const e of entradas) {
|
|
113
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
114
|
+
if (e.isDirectory()) walk(path.join(dir, e.name), r);
|
|
115
|
+
else if (e.isFile() && e.name.endsWith('.js')) out.push(`hooks/${r}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
walk(raiz, '');
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Evalúa los requires hooks→scripts contra modulos.json + perfiles.json.
|
|
124
|
+
* @returns {{disponible:boolean, hooksDistribuidos?:number, dependencias?:Array,
|
|
125
|
+
* hallazgos?:Array, error?:string}}
|
|
126
|
+
*/
|
|
127
|
+
function evaluarHooksRequires(baseDir = process.cwd(), opts = {}) {
|
|
128
|
+
let modulos, perfiles;
|
|
129
|
+
try {
|
|
130
|
+
modulos = (opts.modulos ||
|
|
131
|
+
JSON.parse(fs.readFileSync(path.join(baseDir, 'manifiestos', 'modulos.json'), 'utf8')).modulos);
|
|
132
|
+
const p = opts.perfiles ||
|
|
133
|
+
JSON.parse(fs.readFileSync(path.join(baseDir, 'manifiestos', 'perfiles.json'), 'utf8'));
|
|
134
|
+
perfiles = p.perfiles || p;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
return { disponible: false, error: 'manifiestos ilegibles: ' + err.message };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const mapa = _mapaArchivoModulos(modulos);
|
|
140
|
+
const hooks = _listarHooks(baseDir).filter((h) => mapa.has(h)); // solo distribuidos
|
|
141
|
+
|
|
142
|
+
const dependencias = [];
|
|
143
|
+
const hallazgos = [];
|
|
144
|
+
|
|
145
|
+
for (const hook of hooks) {
|
|
146
|
+
let codigo;
|
|
147
|
+
try {
|
|
148
|
+
codigo = fs.readFileSync(path.join(baseDir, hook), 'utf8');
|
|
149
|
+
} catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const modulosHook = mapa.get(hook);
|
|
153
|
+
|
|
154
|
+
const objetivosDirectos = new Set();
|
|
155
|
+
for (const req of extraerRequiresRelativos(codigo)) {
|
|
156
|
+
const objetivo = resolverObjetivoScripts(hook, req, baseDir);
|
|
157
|
+
if (objetivo) objetivosDirectos.add(objetivo);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Cierre transitivo de cada objetivo directo.
|
|
161
|
+
const objetivos = new Set();
|
|
162
|
+
for (const o of objetivosDirectos) {
|
|
163
|
+
for (const t of expandirTransitivas(baseDir, o)) objetivos.add(t);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const lib of [...objetivos].sort()) {
|
|
167
|
+
const existe = fs.existsSync(path.join(baseDir, lib));
|
|
168
|
+
const modulosLib = mapa.get(lib) || [];
|
|
169
|
+
const dep = { hook, lib, modulosHook, modulosLib, ok: true };
|
|
170
|
+
|
|
171
|
+
if (!existe) {
|
|
172
|
+
dep.ok = false;
|
|
173
|
+
hallazgos.push({
|
|
174
|
+
tipo: 'lib-inexistente',
|
|
175
|
+
hook,
|
|
176
|
+
lib,
|
|
177
|
+
detalle: `${hook} requiere ${lib} pero el archivo no existe en el repo`,
|
|
178
|
+
});
|
|
179
|
+
} else if (modulosLib.length === 0) {
|
|
180
|
+
dep.ok = false;
|
|
181
|
+
hallazgos.push({
|
|
182
|
+
tipo: 'no-registrada',
|
|
183
|
+
hook,
|
|
184
|
+
lib,
|
|
185
|
+
detalle:
|
|
186
|
+
`${hook} (módulo ${modulosHook.join(',')}) requiere ${lib} que NO está ` +
|
|
187
|
+
`en ningún módulo de modulos.json — el hook morirá en silencio en el destino`,
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
// Cobertura: todo perfil que instala el hook debe instalar la lib.
|
|
191
|
+
const perfilesSinLib = [];
|
|
192
|
+
for (const [nombrePerfil, perfil] of Object.entries(perfiles)) {
|
|
193
|
+
const mods = perfil.modulos || [];
|
|
194
|
+
const instalaHook = modulosHook.some((m) => mods.includes(m));
|
|
195
|
+
const instalaLib = modulosLib.some((m) => mods.includes(m));
|
|
196
|
+
if (instalaHook && !instalaLib) perfilesSinLib.push(nombrePerfil);
|
|
197
|
+
}
|
|
198
|
+
if (perfilesSinLib.length > 0) {
|
|
199
|
+
dep.ok = false;
|
|
200
|
+
dep.perfilesSinLib = perfilesSinLib;
|
|
201
|
+
hallazgos.push({
|
|
202
|
+
tipo: 'sin-cobertura',
|
|
203
|
+
hook,
|
|
204
|
+
lib,
|
|
205
|
+
detalle:
|
|
206
|
+
`${lib} (módulo ${modulosLib.join(',')}) no llega a los perfiles que ` +
|
|
207
|
+
`instalan ${hook}: ${perfilesSinLib.join(', ')}`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
dependencias.push(dep);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
disponible: true,
|
|
217
|
+
hooksDistribuidos: hooks.length,
|
|
218
|
+
dependencias,
|
|
219
|
+
hallazgos,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = {
|
|
224
|
+
evaluarHooksRequires,
|
|
225
|
+
extraerRequiresRelativos,
|
|
226
|
+
resolverObjetivoScripts,
|
|
227
|
+
expandirTransitivas,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Entrypoint CLI: node scripts/lib/gate-hooks-requires.js [--json] [baseDir]
|
|
231
|
+
if (!require.main || require.main === module) {
|
|
232
|
+
const args = process.argv.slice(2);
|
|
233
|
+
const json = args.includes('--json');
|
|
234
|
+
const baseDir = args.find((a) => !a.startsWith('--')) || process.cwd();
|
|
235
|
+
const r = evaluarHooksRequires(baseDir);
|
|
236
|
+
|
|
237
|
+
if (json) {
|
|
238
|
+
process.stdout.write(JSON.stringify(r, null, 2) + '\n');
|
|
239
|
+
} else if (!r.disponible) {
|
|
240
|
+
process.stdout.write(`Gate hooks-requires no disponible: ${r.error}\n`);
|
|
241
|
+
} else {
|
|
242
|
+
process.stdout.write(
|
|
243
|
+
`Hooks distribuidos analizados: ${r.hooksDistribuidos} | ` +
|
|
244
|
+
`dependencias hooks→scripts: ${r.dependencias.length} | hallazgos: ${r.hallazgos.length}\n`
|
|
245
|
+
);
|
|
246
|
+
for (const h of r.hallazgos) process.stdout.write(` [${h.tipo}] ${h.detalle}\n`);
|
|
247
|
+
}
|
|
248
|
+
process.exit(r.disponible && r.hallazgos && r.hallazgos.length > 0 ? 1 : 0);
|
|
249
|
+
}
|