@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
package/hooks/spec-gate.js
CHANGED
|
@@ -1,211 +1,212 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Hook: spec-gate.js (Gate G0 — SDD)
|
|
6
|
-
* Tipo: PreToolUse (matcher: Write|Edit|MultiEdit)
|
|
7
|
-
*
|
|
8
|
-
* Detecta escrituras de CÓDIGO FUENTE sin fase activa con PLAN aprobado.
|
|
9
|
-
* Implementa el gate G0 del flujo SPEC→PLAN→TEST→CODE→VERIFY (Revisión
|
|
10
|
-
* Evolutiva 03 §5.2): "ningún Write/Edit a código fuente sin fase activa
|
|
11
|
-
* con PLAN aprobado".
|
|
12
|
-
*
|
|
13
|
-
* Modo actual: WARN-ONLY (ADR-0034, decisión D-07 de 10-CONTEXTO.md).
|
|
14
|
-
* - Emite nudge `kind: spec-gate` a .planning/evolution/nudges.jsonl.
|
|
15
|
-
* - NUNCA exit 2. La promoción a blocking se decide vía ADR tras ~2
|
|
16
|
-
* semanas de calibración sin falsos positivos (patrón ADR-0027→0033).
|
|
17
|
-
*
|
|
18
|
-
* Concepto "fase activa": `.planning/locks/fase-activa.json`, escrito por
|
|
19
|
-
* /swl:aprobar-plan al firmar un plan y eliminado por /swl:ejecutar-fase al
|
|
20
|
-
* cerrar la fase. Formato:
|
|
21
|
-
* { numero, planPath, sha256, aprobadoEn, aprobadoPor }
|
|
22
|
-
* El hook re-computa el SHA256 del plan referenciado y lo compara con el del
|
|
23
|
-
* archivo de fase activa — verificación autocontenida (sin dependencia de
|
|
24
|
-
* scripts/lib/, que puede no estar distribuido en proyectos destino).
|
|
25
|
-
*
|
|
26
|
-
* Zero-config: en proyectos sin `.planning/` el hook sale 0 sin ruido.
|
|
27
|
-
* Opt-out: SWL_SPEC_GATE=0 desactiva completamente el hook.
|
|
28
|
-
*
|
|
29
|
-
* Modelo de amenaza (heredado de plan-lock.js): detecta omisión accidental
|
|
30
|
-
* del ciclo GSD y mutación post-firma; NO resiste a un adversario con
|
|
31
|
-
* escritura al repo (puede regenerar fase-activa.json).
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
const fs = require('fs');
|
|
35
|
-
const path = require('path');
|
|
36
|
-
const crypto = require('crypto');
|
|
37
|
-
|
|
38
|
-
const RUTAS_EXCLUIDAS = [
|
|
39
|
-
'/temp/',
|
|
40
|
-
'/node_modules/',
|
|
41
|
-
'/respositorios-git/',
|
|
42
|
-
'/_userland/',
|
|
43
|
-
'/.planning/',
|
|
44
|
-
'/.claude/',
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Clasificador de código fuente. Reutiliza esArchivoFuente() de
|
|
49
|
-
* calidad-pre-commit.js (misma definición que el gate G3) con fallback
|
|
50
|
-
* defensivo si el módulo no está disponible en el destino.
|
|
51
|
-
*/
|
|
52
|
-
function cargarClasificador() {
|
|
53
|
-
try {
|
|
54
|
-
const calidad = require(path.join(__dirname, 'calidad-pre-commit.js'));
|
|
55
|
-
if (typeof calidad.esArchivoFuente === 'function') return calidad.esArchivoFuente;
|
|
56
|
-
} catch (_) {
|
|
57
|
-
// Fallback mínimo: extensiones de código de los lenguajes soportados,
|
|
58
|
-
// excluyendo tests por sufijo/directorio.
|
|
59
|
-
}
|
|
60
|
-
return function esArchivoFuenteFallback(ruta) {
|
|
61
|
-
const normal = ruta.replace(/\\/g, '/');
|
|
62
|
-
if (/\.(test|spec)\.[jt]sx?$/.test(normal)) return false;
|
|
63
|
-
if (/(^|\/)(tests?|__tests__|spec)\//.test(normal)) return false;
|
|
64
|
-
if (/\.(md|json|ya?ml|toml|ini|cfg|conf|txt|svg|lock)$/i.test(normal)) return false;
|
|
65
|
-
return /\.(jsx?|tsx?|py|go|rs|java|kt|cs|rb|php|swift|c|cc|cpp|h|hpp)$/i.test(normal);
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Evalúa el estado de la fase activa del proyecto en `cwd`.
|
|
71
|
-
* @returns {{ ok: boolean, motivo: string|null, numero?: number }}
|
|
72
|
-
* ok=true → hay fase activa con plan íntegro (no advertir)
|
|
73
|
-
* ok=false → motivo describe por qué no hay cobertura de spec
|
|
74
|
-
*/
|
|
75
|
-
function evaluarFaseActiva(cwd) {
|
|
76
|
-
const rutaFaseActiva = path.join(cwd, '.planning', 'locks', 'fase-activa.json');
|
|
77
|
-
if (!fs.existsSync(rutaFaseActiva)) {
|
|
78
|
-
return { ok: false, motivo: 'sin fase activa (no existe .planning/locks/fase-activa.json)' };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let faseActiva;
|
|
82
|
-
try {
|
|
83
|
-
faseActiva = JSON.parse(fs.readFileSync(rutaFaseActiva, 'utf-8'));
|
|
84
|
-
} catch (_) {
|
|
85
|
-
return { ok: false, motivo: 'fase-activa.json corrupto (JSON inválido)' };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (!faseActiva || typeof faseActiva.planPath !== 'string' || typeof faseActiva.sha256 !== 'string') {
|
|
89
|
-
return { ok: false, motivo: 'fase-activa.json sin planPath/sha256' };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const planAbs = path.isAbsolute(faseActiva.planPath)
|
|
93
|
-
? faseActiva.planPath
|
|
94
|
-
: path.join(cwd, faseActiva.planPath);
|
|
95
|
-
|
|
96
|
-
if (!fs.existsSync(planAbs)) {
|
|
97
|
-
return { ok: false, motivo: `el plan de la fase activa no existe (${faseActiva.planPath})` };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
let hashActual;
|
|
101
|
-
try {
|
|
102
|
-
hashActual = crypto.createHash('sha256').update(fs.readFileSync(planAbs)).digest('hex');
|
|
103
|
-
} catch (_) {
|
|
104
|
-
return { ok: false, motivo: 'no se pudo leer el plan de la fase activa' };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (hashActual !== faseActiva.sha256) {
|
|
108
|
-
return {
|
|
109
|
-
ok: false,
|
|
110
|
-
motivo: `el plan de la fase activa fue mutado tras la firma (${faseActiva.planPath})`,
|
|
111
|
-
numero: faseActiva.numero,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return { ok: true, motivo: null, numero: faseActiva.numero };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Decide si la escritura amerita advertencia G0.
|
|
120
|
-
* @returns {null | { motivo: string }} null = silencio
|
|
121
|
-
*/
|
|
122
|
-
function evaluarEscritura({ cwd, toolName, filePath, esArchivoFuente }) {
|
|
123
|
-
if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) return null;
|
|
124
|
-
if (!filePath) return null;
|
|
125
|
-
|
|
126
|
-
// Zero-config: sin .planning/ no hay ciclo GSD que vigilar.
|
|
127
|
-
if (!fs.existsSync(path.join(cwd, '.planning'))) return null;
|
|
128
|
-
|
|
129
|
-
const normal = filePath.replace(/\\/g, '/');
|
|
130
|
-
if (RUTAS_EXCLUIDAS.some((ex) => normal.includes(ex))) return null;
|
|
131
|
-
if (!esArchivoFuente(normal)) return null;
|
|
132
|
-
|
|
133
|
-
const fase = evaluarFaseActiva(cwd);
|
|
134
|
-
if (fase.ok) return null;
|
|
135
|
-
|
|
136
|
-
return { motivo: fase.motivo };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function emitirNudge({ cwd, filePath, motivo }) {
|
|
140
|
-
const rutaRelativa = path.relative(cwd, filePath).replace(/\\/g, '/');
|
|
141
|
-
let emit;
|
|
142
|
-
try {
|
|
143
|
-
({ emit } = require(path.join(__dirname, 'lib', 'nudge-tracker.js')));
|
|
144
|
-
} catch (_) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
try {
|
|
148
|
-
emit({
|
|
149
|
-
kind: 'spec-gate',
|
|
150
|
-
target: rutaRelativa,
|
|
151
|
-
source: 'hooks/spec-gate.js',
|
|
152
|
-
message:
|
|
153
|
-
`Gate G0 (warn): escritura de código fuente ${rutaRelativa} ${motivo}. ` +
|
|
154
|
-
`El flujo SDD espera una fase activa con PLAN aprobado (/swl:aprobar-plan N) ` +
|
|
155
|
-
`antes de escribir código. Si es un fix trivial o exploración, ignora este aviso ` +
|
|
156
|
-
`— se usa para calibrar la promoción a blocking (ADR-0034). Opt-out: SWL_SPEC_GATE=0.`,
|
|
157
|
-
data: { archivo: rutaRelativa, motivo, gate: 'G0' },
|
|
158
|
-
mutation_category: 'optimize',
|
|
159
|
-
risk_level: 'low',
|
|
160
|
-
});
|
|
161
|
-
} catch (_) {
|
|
162
|
-
// La observabilidad nunca bloquea el trabajo productivo.
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function main() {
|
|
167
|
-
if (process.env.SWL_SPEC_GATE === '0') process.exit(0);
|
|
168
|
-
|
|
169
|
-
let hookInput = '';
|
|
170
|
-
try {
|
|
171
|
-
hookInput = fs.readFileSync(0, 'utf-8');
|
|
172
|
-
} catch (_) {
|
|
173
|
-
process.exit(0);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let evento;
|
|
177
|
-
try {
|
|
178
|
-
evento = JSON.parse(hookInput);
|
|
179
|
-
} catch (_) {
|
|
180
|
-
process.exit(0);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const cwd = process.cwd();
|
|
184
|
-
const toolName = evento?.tool_name;
|
|
185
|
-
const filePath = evento?.tool_input?.file_path;
|
|
186
|
-
|
|
187
|
-
let resultado = null;
|
|
188
|
-
try {
|
|
189
|
-
resultado = evaluarEscritura({
|
|
190
|
-
cwd,
|
|
191
|
-
toolName,
|
|
192
|
-
filePath,
|
|
193
|
-
esArchivoFuente: cargarClasificador(),
|
|
194
|
-
});
|
|
195
|
-
} catch (_) {
|
|
196
|
-
process.exit(0);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (resultado) {
|
|
200
|
-
emitirNudge({ cwd, filePath, motivo: resultado.motivo });
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// WARN-ONLY (ADR-0034): jamás exit 2 en este modo.
|
|
204
|
-
process.exit(0);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook: spec-gate.js (Gate G0 — SDD)
|
|
6
|
+
* Tipo: PreToolUse (matcher: Write|Edit|MultiEdit)
|
|
7
|
+
*
|
|
8
|
+
* Detecta escrituras de CÓDIGO FUENTE sin fase activa con PLAN aprobado.
|
|
9
|
+
* Implementa el gate G0 del flujo SPEC→PLAN→TEST→CODE→VERIFY (Revisión
|
|
10
|
+
* Evolutiva 03 §5.2): "ningún Write/Edit a código fuente sin fase activa
|
|
11
|
+
* con PLAN aprobado".
|
|
12
|
+
*
|
|
13
|
+
* Modo actual: WARN-ONLY (ADR-0034, decisión D-07 de 10-CONTEXTO.md).
|
|
14
|
+
* - Emite nudge `kind: spec-gate` a .planning/evolution/nudges.jsonl.
|
|
15
|
+
* - NUNCA exit 2. La promoción a blocking se decide vía ADR tras ~2
|
|
16
|
+
* semanas de calibración sin falsos positivos (patrón ADR-0027→0033).
|
|
17
|
+
*
|
|
18
|
+
* Concepto "fase activa": `.planning/locks/fase-activa.json`, escrito por
|
|
19
|
+
* /swl:aprobar-plan al firmar un plan y eliminado por /swl:ejecutar-fase al
|
|
20
|
+
* cerrar la fase. Formato:
|
|
21
|
+
* { numero, planPath, sha256, aprobadoEn, aprobadoPor }
|
|
22
|
+
* El hook re-computa el SHA256 del plan referenciado y lo compara con el del
|
|
23
|
+
* archivo de fase activa — verificación autocontenida (sin dependencia de
|
|
24
|
+
* scripts/lib/, que puede no estar distribuido en proyectos destino).
|
|
25
|
+
*
|
|
26
|
+
* Zero-config: en proyectos sin `.planning/` el hook sale 0 sin ruido.
|
|
27
|
+
* Opt-out: SWL_SPEC_GATE=0 desactiva completamente el hook.
|
|
28
|
+
*
|
|
29
|
+
* Modelo de amenaza (heredado de plan-lock.js): detecta omisión accidental
|
|
30
|
+
* del ciclo GSD y mutación post-firma; NO resiste a un adversario con
|
|
31
|
+
* escritura al repo (puede regenerar fase-activa.json).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const path = require('path');
|
|
36
|
+
const crypto = require('crypto');
|
|
37
|
+
|
|
38
|
+
const RUTAS_EXCLUIDAS = [
|
|
39
|
+
'/temp/',
|
|
40
|
+
'/node_modules/',
|
|
41
|
+
'/respositorios-git/',
|
|
42
|
+
'/_userland/',
|
|
43
|
+
'/.planning/',
|
|
44
|
+
'/.claude/',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Clasificador de código fuente. Reutiliza esArchivoFuente() de
|
|
49
|
+
* calidad-pre-commit.js (misma definición que el gate G3) con fallback
|
|
50
|
+
* defensivo si el módulo no está disponible en el destino.
|
|
51
|
+
*/
|
|
52
|
+
function cargarClasificador() {
|
|
53
|
+
try {
|
|
54
|
+
const calidad = require(path.join(__dirname, 'calidad-pre-commit.js'));
|
|
55
|
+
if (typeof calidad.esArchivoFuente === 'function') return calidad.esArchivoFuente;
|
|
56
|
+
} catch (_) {
|
|
57
|
+
// Fallback mínimo: extensiones de código de los lenguajes soportados,
|
|
58
|
+
// excluyendo tests por sufijo/directorio.
|
|
59
|
+
}
|
|
60
|
+
return function esArchivoFuenteFallback(ruta) {
|
|
61
|
+
const normal = ruta.replace(/\\/g, '/');
|
|
62
|
+
if (/\.(test|spec)\.[jt]sx?$/.test(normal)) return false;
|
|
63
|
+
if (/(^|\/)(tests?|__tests__|spec)\//.test(normal)) return false;
|
|
64
|
+
if (/\.(md|json|ya?ml|toml|ini|cfg|conf|txt|svg|lock)$/i.test(normal)) return false;
|
|
65
|
+
return /\.(jsx?|tsx?|py|go|rs|java|kt|cs|rb|php|swift|c|cc|cpp|h|hpp)$/i.test(normal);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Evalúa el estado de la fase activa del proyecto en `cwd`.
|
|
71
|
+
* @returns {{ ok: boolean, motivo: string|null, numero?: number }}
|
|
72
|
+
* ok=true → hay fase activa con plan íntegro (no advertir)
|
|
73
|
+
* ok=false → motivo describe por qué no hay cobertura de spec
|
|
74
|
+
*/
|
|
75
|
+
function evaluarFaseActiva(cwd) {
|
|
76
|
+
const rutaFaseActiva = path.join(cwd, '.planning', 'locks', 'fase-activa.json');
|
|
77
|
+
if (!fs.existsSync(rutaFaseActiva)) {
|
|
78
|
+
return { ok: false, motivo: 'sin fase activa (no existe .planning/locks/fase-activa.json)' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let faseActiva;
|
|
82
|
+
try {
|
|
83
|
+
faseActiva = JSON.parse(fs.readFileSync(rutaFaseActiva, 'utf-8'));
|
|
84
|
+
} catch (_) {
|
|
85
|
+
return { ok: false, motivo: 'fase-activa.json corrupto (JSON inválido)' };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!faseActiva || typeof faseActiva.planPath !== 'string' || typeof faseActiva.sha256 !== 'string') {
|
|
89
|
+
return { ok: false, motivo: 'fase-activa.json sin planPath/sha256' };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const planAbs = path.isAbsolute(faseActiva.planPath)
|
|
93
|
+
? faseActiva.planPath
|
|
94
|
+
: path.join(cwd, faseActiva.planPath);
|
|
95
|
+
|
|
96
|
+
if (!fs.existsSync(planAbs)) {
|
|
97
|
+
return { ok: false, motivo: `el plan de la fase activa no existe (${faseActiva.planPath})` };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let hashActual;
|
|
101
|
+
try {
|
|
102
|
+
hashActual = crypto.createHash('sha256').update(fs.readFileSync(planAbs)).digest('hex');
|
|
103
|
+
} catch (_) {
|
|
104
|
+
return { ok: false, motivo: 'no se pudo leer el plan de la fase activa' };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (hashActual !== faseActiva.sha256) {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
motivo: `el plan de la fase activa fue mutado tras la firma (${faseActiva.planPath})`,
|
|
111
|
+
numero: faseActiva.numero,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { ok: true, motivo: null, numero: faseActiva.numero };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Decide si la escritura amerita advertencia G0.
|
|
120
|
+
* @returns {null | { motivo: string }} null = silencio
|
|
121
|
+
*/
|
|
122
|
+
function evaluarEscritura({ cwd, toolName, filePath, esArchivoFuente }) {
|
|
123
|
+
if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) return null;
|
|
124
|
+
if (!filePath) return null;
|
|
125
|
+
|
|
126
|
+
// Zero-config: sin .planning/ no hay ciclo GSD que vigilar.
|
|
127
|
+
if (!fs.existsSync(path.join(cwd, '.planning'))) return null;
|
|
128
|
+
|
|
129
|
+
const normal = filePath.replace(/\\/g, '/');
|
|
130
|
+
if (RUTAS_EXCLUIDAS.some((ex) => normal.includes(ex))) return null;
|
|
131
|
+
if (!esArchivoFuente(normal)) return null;
|
|
132
|
+
|
|
133
|
+
const fase = evaluarFaseActiva(cwd);
|
|
134
|
+
if (fase.ok) return null;
|
|
135
|
+
|
|
136
|
+
return { motivo: fase.motivo };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function emitirNudge({ cwd, filePath, motivo }) {
|
|
140
|
+
const rutaRelativa = path.relative(cwd, filePath).replace(/\\/g, '/');
|
|
141
|
+
let emit;
|
|
142
|
+
try {
|
|
143
|
+
({ emit } = require(path.join(__dirname, 'lib', 'nudge-tracker.js')));
|
|
144
|
+
} catch (_) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
emit({
|
|
149
|
+
kind: 'spec-gate',
|
|
150
|
+
target: rutaRelativa,
|
|
151
|
+
source: 'hooks/spec-gate.js',
|
|
152
|
+
message:
|
|
153
|
+
`Gate G0 (warn): escritura de código fuente ${rutaRelativa} ${motivo}. ` +
|
|
154
|
+
`El flujo SDD espera una fase activa con PLAN aprobado (/swl:aprobar-plan N) ` +
|
|
155
|
+
`antes de escribir código. Si es un fix trivial o exploración, ignora este aviso ` +
|
|
156
|
+
`— se usa para calibrar la promoción a blocking (ADR-0034). Opt-out: SWL_SPEC_GATE=0.`,
|
|
157
|
+
data: { archivo: rutaRelativa, motivo, gate: 'G0' },
|
|
158
|
+
mutation_category: 'optimize',
|
|
159
|
+
risk_level: 'low',
|
|
160
|
+
});
|
|
161
|
+
} catch (_) {
|
|
162
|
+
// La observabilidad nunca bloquea el trabajo productivo.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function main() {
|
|
167
|
+
if (process.env.SWL_SPEC_GATE === '0') process.exit(0);
|
|
168
|
+
|
|
169
|
+
let hookInput = '';
|
|
170
|
+
try {
|
|
171
|
+
hookInput = fs.readFileSync(0, 'utf-8');
|
|
172
|
+
} catch (_) {
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let evento;
|
|
177
|
+
try {
|
|
178
|
+
evento = JSON.parse(hookInput);
|
|
179
|
+
} catch (_) {
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const cwd = process.cwd();
|
|
184
|
+
const toolName = evento?.tool_name;
|
|
185
|
+
const filePath = evento?.tool_input?.file_path;
|
|
186
|
+
|
|
187
|
+
let resultado = null;
|
|
188
|
+
try {
|
|
189
|
+
resultado = evaluarEscritura({
|
|
190
|
+
cwd,
|
|
191
|
+
toolName,
|
|
192
|
+
filePath,
|
|
193
|
+
esArchivoFuente: cargarClasificador(),
|
|
194
|
+
});
|
|
195
|
+
} catch (_) {
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (resultado) {
|
|
200
|
+
emitirNudge({ cwd, filePath, motivo: resultado.motivo });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// WARN-ONLY (ADR-0034): jamás exit 2 en este modo.
|
|
204
|
+
process.exit(0);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Entrypoint directo o wrapper `node -e "require(...)"` (require.main undefined).
|
|
208
|
+
if (!require.main || require.main === module) {
|
|
209
|
+
main();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = { evaluarFaseActiva, evaluarEscritura, RUTAS_EXCLUIDAS };
|
|
@@ -210,7 +210,8 @@ function ejecutarComoHook() {
|
|
|
210
210
|
});
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
// Entrypoint directo o wrapper `node -e "require(...)"` (require.main undefined).
|
|
214
|
+
if (!require.main || require.main === module) {
|
|
214
215
|
ejecutarComoHook();
|
|
215
216
|
}
|
|
216
217
|
|