@saulwade/swl-ses 2.3.0 → 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 +47 -6
- package/README.md +1 -1
- 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/briefing.md +119 -119
- package/comandos/swl/contribuir.md +233 -233
- 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 +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/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-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/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/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/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/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/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 +29 -29
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +2291 -1964
- package/manifiestos/planning-paths.json +44 -44
- package/manifiestos/skills-lock.json +1282 -1282
- package/package.json +1 -1
- 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 +1 -1
- 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 +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/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/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/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/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
|
@@ -1,242 +1,242 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Hook: validar-intent-spec.js
|
|
6
|
-
* Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit sobre agentes/*.md)
|
|
7
|
-
*
|
|
8
|
-
* Verifica que los agentes con `nivelRiesgo: ALTO` declaren las 6 partes
|
|
9
|
-
* del framework Intent Engineering exigidas por `reglas/intent-engineering.md`:
|
|
10
|
-
* - strategy (Parte 1)
|
|
11
|
-
* - healthMetrics (Parte 4)
|
|
12
|
-
* - steering (Parte 6 — prompt-level)
|
|
13
|
-
* - hardGuardrails (Parte 6 — orchestration-level)
|
|
14
|
-
* - fragmentos: [_intent-spec]
|
|
15
|
-
* - maxTurnos (Parte 8 — Stop Rules)
|
|
16
|
-
*
|
|
17
|
-
* Comportamiento (modo blocking desde ADR-0033):
|
|
18
|
-
* - BLOQUEA (exit 2) cuando un agente ALTO tiene frontmatter incompleto.
|
|
19
|
-
* El Write/Edit ya ocurrió (PostToolUse); el exit 2 surface el error a
|
|
20
|
-
* Claude para que complete el intent spec. Sigue emitiendo el nudge para
|
|
21
|
-
* trazabilidad antes de bloquear.
|
|
22
|
-
* - Sale 0 (sin ruido) cuando el agente ALTO está completo o no es ALTO.
|
|
23
|
-
* - Solo se dispara con archivos `agentes/*.md` (no fragmentos `_*.md`)
|
|
24
|
-
* - Respeta exclusiones: temp/, node_modules/, respositorios-git/, _userland/
|
|
25
|
-
*
|
|
26
|
-
* Opt-out: SWL_INTENT_SPEC=0 desactiva completamente el hook.
|
|
27
|
-
*
|
|
28
|
-
* Formato del nudge:
|
|
29
|
-
* {
|
|
30
|
-
* id: string,
|
|
31
|
-
* kind: "intent-spec-incomplete",
|
|
32
|
-
* target: "auto-evolucion-swl",
|
|
33
|
-
* source: "hooks/validar-intent-spec.js",
|
|
34
|
-
* message: "...",
|
|
35
|
-
* data: { archivo, agente, faltan: [...] },
|
|
36
|
-
* ts: ISO,
|
|
37
|
-
* accionado: false
|
|
38
|
-
* }
|
|
39
|
-
*
|
|
40
|
-
* Origen: ADR-0027 (warn-only) → ADR-0033 (promoción a blocking, 2026-06-11).
|
|
41
|
-
* Promoción justificada: 24 días estable en warn-only, 8/8 agentes ALTO con
|
|
42
|
-
* frontmatter completo al momento de la promoción (cero falsos positivos sobre
|
|
43
|
-
* el estado actual). Opt-out preservado: SWL_INTENT_SPEC=0.
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
const fs = require('fs');
|
|
47
|
-
const path = require('path');
|
|
48
|
-
const crypto = require('crypto');
|
|
49
|
-
|
|
50
|
-
// ─── Opt-out global ───────────────────────────────────────────────────────
|
|
51
|
-
if (process.env.SWL_INTENT_SPEC === '0') {
|
|
52
|
-
process.exit(0);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let hookInput = '';
|
|
56
|
-
try {
|
|
57
|
-
hookInput = fs.readFileSync(0, 'utf-8');
|
|
58
|
-
} catch (_) {
|
|
59
|
-
process.exit(0);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
let evento;
|
|
63
|
-
try {
|
|
64
|
-
evento = JSON.parse(hookInput);
|
|
65
|
-
} catch (_) {
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const toolName = evento?.tool_name;
|
|
70
|
-
const toolInput = evento?.tool_input;
|
|
71
|
-
|
|
72
|
-
if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
73
|
-
process.exit(0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const filePath = toolInput?.file_path;
|
|
77
|
-
if (!filePath) {
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const pathNormalized = filePath.replace(/\\/g, '/');
|
|
82
|
-
|
|
83
|
-
// Solo agentes/*.md (NO fragmentos _*.md)
|
|
84
|
-
if (!/\/agentes\/[^_][^/]*\.md$/.test(pathNormalized)) {
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const RUTAS_EXCLUIDAS = [
|
|
89
|
-
'/temp/',
|
|
90
|
-
'/node_modules/',
|
|
91
|
-
'/respositorios-git/',
|
|
92
|
-
'/_userland/',
|
|
93
|
-
'/.planning/',
|
|
94
|
-
];
|
|
95
|
-
if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
|
|
96
|
-
process.exit(0);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!fs.existsSync(filePath)) {
|
|
100
|
-
process.exit(0);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ─── Parsear frontmatter YAML ─────────────────────────────────────────────
|
|
104
|
-
let contenido;
|
|
105
|
-
try {
|
|
106
|
-
contenido = fs.readFileSync(filePath, 'utf-8');
|
|
107
|
-
} catch (_) {
|
|
108
|
-
process.exit(0);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Normalizar line endings (CRLF Windows → LF) antes del regex.
|
|
112
|
-
// Sin esto el hook nunca detectaba frontmatter en archivos guardados desde
|
|
113
|
-
// Git for Windows con autocrlf=true (bug reportado 2026-05-18).
|
|
114
|
-
contenido = contenido.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
115
|
-
|
|
116
|
-
const matchFM = contenido.match(/^---\n([\s\S]+?)\n---/);
|
|
117
|
-
if (!matchFM) {
|
|
118
|
-
// Sin frontmatter — no es un agente conforme. Otros validadores lo detectan.
|
|
119
|
-
process.exit(0);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const yamlBlob = matchFM[1];
|
|
123
|
-
|
|
124
|
-
// Parser mínimo de frontmatter (sin dependencias). Solo lee:
|
|
125
|
-
// - nivelRiesgo: VALOR
|
|
126
|
-
// - presencia de claves de primer nivel
|
|
127
|
-
const lineas = yamlBlob.split('\n');
|
|
128
|
-
const claves = new Set();
|
|
129
|
-
let nivelRiesgo = null;
|
|
130
|
-
let fragmentosTieneIntentSpec = false;
|
|
131
|
-
let dentroDeFragmentos = false;
|
|
132
|
-
|
|
133
|
-
// Strip inline YAML comments (' # ...' o '\t# ...') y comillas wrapping.
|
|
134
|
-
// Sin esto, `nivelRiesgo: ALTO # ejemplo` se parsearía como "ALTO # ejemplo"
|
|
135
|
-
// y la comparación con 'ALTO' fallaría silenciosamente (bug latente 2026-05-18).
|
|
136
|
-
function limpiarValorYaml(crudo) {
|
|
137
|
-
let v = crudo;
|
|
138
|
-
// Comentario inline: '#' precedido de whitespace (no '#' dentro de strings comillados,
|
|
139
|
-
// que en este parser mínimo no soportamos pero sí intentamos respetar).
|
|
140
|
-
const m = v.match(/^(.*?)(?:\s+#.*)?$/);
|
|
141
|
-
if (m) v = m[1];
|
|
142
|
-
v = v.trim();
|
|
143
|
-
// Strip comillas wrapping si las hay
|
|
144
|
-
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
|
|
145
|
-
v = v.slice(1, -1);
|
|
146
|
-
}
|
|
147
|
-
return v;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
for (const linea of lineas) {
|
|
151
|
-
// Detectar clave de primer nivel (sin indent)
|
|
152
|
-
const m = linea.match(/^([a-zA-Z_][a-zA-Z0-9_]*):/);
|
|
153
|
-
if (m) {
|
|
154
|
-
claves.add(m[1]);
|
|
155
|
-
if (m[1] === 'nivelRiesgo') {
|
|
156
|
-
const crudo = linea.split(':').slice(1).join(':').trim();
|
|
157
|
-
nivelRiesgo = limpiarValorYaml(crudo);
|
|
158
|
-
dentroDeFragmentos = false;
|
|
159
|
-
} else if (m[1] === 'fragmentos') {
|
|
160
|
-
dentroDeFragmentos = true;
|
|
161
|
-
// Caso fragmentos: [_intent-spec] inline
|
|
162
|
-
if (linea.includes('_intent-spec')) {
|
|
163
|
-
fragmentosTieneIntentSpec = true;
|
|
164
|
-
}
|
|
165
|
-
} else {
|
|
166
|
-
dentroDeFragmentos = false;
|
|
167
|
-
}
|
|
168
|
-
} else if (dentroDeFragmentos && linea.includes('_intent-spec')) {
|
|
169
|
-
fragmentosTieneIntentSpec = true;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Solo evaluar agentes ALTO
|
|
174
|
-
if (nivelRiesgo !== 'ALTO') {
|
|
175
|
-
process.exit(0);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Verificar las 6 partes obligatorias
|
|
179
|
-
const CAMPOS_OBLIGATORIOS = ['strategy', 'healthMetrics', 'steering', 'hardGuardrails', 'maxTurnos'];
|
|
180
|
-
const faltan = CAMPOS_OBLIGATORIOS.filter((c) => !claves.has(c));
|
|
181
|
-
|
|
182
|
-
if (!fragmentosTieneIntentSpec) {
|
|
183
|
-
faltan.push('fragmentos:[_intent-spec]');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (faltan.length === 0) {
|
|
187
|
-
process.exit(0);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// ─── Emitir nudge ─────────────────────────────────────────────────────────
|
|
191
|
-
const CWD = process.cwd();
|
|
192
|
-
const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
|
|
193
|
-
const nombreAgente = path.basename(filePath, '.md');
|
|
194
|
-
|
|
195
|
-
const nudge = {
|
|
196
|
-
id: crypto.randomBytes(8).toString('hex'),
|
|
197
|
-
kind: 'intent-spec-incomplete',
|
|
198
|
-
target: 'auto-evolucion-swl',
|
|
199
|
-
source: 'hooks/validar-intent-spec.js',
|
|
200
|
-
message:
|
|
201
|
-
`Agente ALTO riesgo ${nombreAgente} no declara intent spec completo. ` +
|
|
202
|
-
`Faltan: ${faltan.join(', ')}. Cargar Skill("proceso-intent-engineering") + ` +
|
|
203
|
-
`regla intent-engineering.md y completar frontmatter.`,
|
|
204
|
-
data: {
|
|
205
|
-
archivo: rutaRelativa,
|
|
206
|
-
agente: nombreAgente,
|
|
207
|
-
nivelRiesgo: 'ALTO',
|
|
208
|
-
faltan,
|
|
209
|
-
},
|
|
210
|
-
ts: new Date().toISOString(),
|
|
211
|
-
accionado: false,
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const nudgesDir = path.join(CWD, '.planning', 'evolution');
|
|
215
|
-
const nudgesFile = path.join(nudgesDir, 'nudges.jsonl');
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
if (!fs.existsSync(nudgesDir)) {
|
|
219
|
-
fs.mkdirSync(nudgesDir, { recursive: true });
|
|
220
|
-
}
|
|
221
|
-
fs.appendFileSync(nudgesFile, JSON.stringify(nudge) + '\n');
|
|
222
|
-
} catch (_) {
|
|
223
|
-
// Si falla persistir, no romper el flujo del bloqueo
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ─── Modo blocking (ADR-0033) ─────────────────────────────────────────────
|
|
227
|
-
// El agente ALTO tiene frontmatter incompleto: bloquear (exit 2) y surface el
|
|
228
|
-
// detalle a Claude por stderr para que complete el intent spec. Opt-out global
|
|
229
|
-
// SWL_INTENT_SPEC=0 ya cortó arriba; aquí el bloqueo es activo.
|
|
230
|
-
const razon = [
|
|
231
|
-
`Intent spec incompleto en agente ALTO riesgo: ${nombreAgente}`,
|
|
232
|
-
`Faltan campos obligatorios: ${faltan.join(', ')}.`,
|
|
233
|
-
``,
|
|
234
|
-
`Completa el frontmatter según reglas/intent-engineering.md:`,
|
|
235
|
-
` strategy, healthMetrics, steering, hardGuardrails, maxTurnos,`,
|
|
236
|
-
` y fragmentos: [_intent-spec].`,
|
|
237
|
-
`Carga Skill("proceso-intent-engineering") para la guía completa.`,
|
|
238
|
-
``,
|
|
239
|
-
`Para omitir esta verificación: SWL_INTENT_SPEC=0 (registra la excepción).`,
|
|
240
|
-
].join('\n');
|
|
241
|
-
process.stderr.write(razon);
|
|
242
|
-
process.exit(2);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook: validar-intent-spec.js
|
|
6
|
+
* Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit sobre agentes/*.md)
|
|
7
|
+
*
|
|
8
|
+
* Verifica que los agentes con `nivelRiesgo: ALTO` declaren las 6 partes
|
|
9
|
+
* del framework Intent Engineering exigidas por `reglas/intent-engineering.md`:
|
|
10
|
+
* - strategy (Parte 1)
|
|
11
|
+
* - healthMetrics (Parte 4)
|
|
12
|
+
* - steering (Parte 6 — prompt-level)
|
|
13
|
+
* - hardGuardrails (Parte 6 — orchestration-level)
|
|
14
|
+
* - fragmentos: [_intent-spec]
|
|
15
|
+
* - maxTurnos (Parte 8 — Stop Rules)
|
|
16
|
+
*
|
|
17
|
+
* Comportamiento (modo blocking desde ADR-0033):
|
|
18
|
+
* - BLOQUEA (exit 2) cuando un agente ALTO tiene frontmatter incompleto.
|
|
19
|
+
* El Write/Edit ya ocurrió (PostToolUse); el exit 2 surface el error a
|
|
20
|
+
* Claude para que complete el intent spec. Sigue emitiendo el nudge para
|
|
21
|
+
* trazabilidad antes de bloquear.
|
|
22
|
+
* - Sale 0 (sin ruido) cuando el agente ALTO está completo o no es ALTO.
|
|
23
|
+
* - Solo se dispara con archivos `agentes/*.md` (no fragmentos `_*.md`)
|
|
24
|
+
* - Respeta exclusiones: temp/, node_modules/, respositorios-git/, _userland/
|
|
25
|
+
*
|
|
26
|
+
* Opt-out: SWL_INTENT_SPEC=0 desactiva completamente el hook.
|
|
27
|
+
*
|
|
28
|
+
* Formato del nudge:
|
|
29
|
+
* {
|
|
30
|
+
* id: string,
|
|
31
|
+
* kind: "intent-spec-incomplete",
|
|
32
|
+
* target: "auto-evolucion-swl",
|
|
33
|
+
* source: "hooks/validar-intent-spec.js",
|
|
34
|
+
* message: "...",
|
|
35
|
+
* data: { archivo, agente, faltan: [...] },
|
|
36
|
+
* ts: ISO,
|
|
37
|
+
* accionado: false
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* Origen: ADR-0027 (warn-only) → ADR-0033 (promoción a blocking, 2026-06-11).
|
|
41
|
+
* Promoción justificada: 24 días estable en warn-only, 8/8 agentes ALTO con
|
|
42
|
+
* frontmatter completo al momento de la promoción (cero falsos positivos sobre
|
|
43
|
+
* el estado actual). Opt-out preservado: SWL_INTENT_SPEC=0.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
const fs = require('fs');
|
|
47
|
+
const path = require('path');
|
|
48
|
+
const crypto = require('crypto');
|
|
49
|
+
|
|
50
|
+
// ─── Opt-out global ───────────────────────────────────────────────────────
|
|
51
|
+
if (process.env.SWL_INTENT_SPEC === '0') {
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let hookInput = '';
|
|
56
|
+
try {
|
|
57
|
+
hookInput = fs.readFileSync(0, 'utf-8');
|
|
58
|
+
} catch (_) {
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let evento;
|
|
63
|
+
try {
|
|
64
|
+
evento = JSON.parse(hookInput);
|
|
65
|
+
} catch (_) {
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const toolName = evento?.tool_name;
|
|
70
|
+
const toolInput = evento?.tool_input;
|
|
71
|
+
|
|
72
|
+
if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const filePath = toolInput?.file_path;
|
|
77
|
+
if (!filePath) {
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const pathNormalized = filePath.replace(/\\/g, '/');
|
|
82
|
+
|
|
83
|
+
// Solo agentes/*.md (NO fragmentos _*.md)
|
|
84
|
+
if (!/\/agentes\/[^_][^/]*\.md$/.test(pathNormalized)) {
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const RUTAS_EXCLUIDAS = [
|
|
89
|
+
'/temp/',
|
|
90
|
+
'/node_modules/',
|
|
91
|
+
'/respositorios-git/',
|
|
92
|
+
'/_userland/',
|
|
93
|
+
'/.planning/',
|
|
94
|
+
];
|
|
95
|
+
if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!fs.existsSync(filePath)) {
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ─── Parsear frontmatter YAML ─────────────────────────────────────────────
|
|
104
|
+
let contenido;
|
|
105
|
+
try {
|
|
106
|
+
contenido = fs.readFileSync(filePath, 'utf-8');
|
|
107
|
+
} catch (_) {
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Normalizar line endings (CRLF Windows → LF) antes del regex.
|
|
112
|
+
// Sin esto el hook nunca detectaba frontmatter en archivos guardados desde
|
|
113
|
+
// Git for Windows con autocrlf=true (bug reportado 2026-05-18).
|
|
114
|
+
contenido = contenido.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
115
|
+
|
|
116
|
+
const matchFM = contenido.match(/^---\n([\s\S]+?)\n---/);
|
|
117
|
+
if (!matchFM) {
|
|
118
|
+
// Sin frontmatter — no es un agente conforme. Otros validadores lo detectan.
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const yamlBlob = matchFM[1];
|
|
123
|
+
|
|
124
|
+
// Parser mínimo de frontmatter (sin dependencias). Solo lee:
|
|
125
|
+
// - nivelRiesgo: VALOR
|
|
126
|
+
// - presencia de claves de primer nivel
|
|
127
|
+
const lineas = yamlBlob.split('\n');
|
|
128
|
+
const claves = new Set();
|
|
129
|
+
let nivelRiesgo = null;
|
|
130
|
+
let fragmentosTieneIntentSpec = false;
|
|
131
|
+
let dentroDeFragmentos = false;
|
|
132
|
+
|
|
133
|
+
// Strip inline YAML comments (' # ...' o '\t# ...') y comillas wrapping.
|
|
134
|
+
// Sin esto, `nivelRiesgo: ALTO # ejemplo` se parsearía como "ALTO # ejemplo"
|
|
135
|
+
// y la comparación con 'ALTO' fallaría silenciosamente (bug latente 2026-05-18).
|
|
136
|
+
function limpiarValorYaml(crudo) {
|
|
137
|
+
let v = crudo;
|
|
138
|
+
// Comentario inline: '#' precedido de whitespace (no '#' dentro de strings comillados,
|
|
139
|
+
// que en este parser mínimo no soportamos pero sí intentamos respetar).
|
|
140
|
+
const m = v.match(/^(.*?)(?:\s+#.*)?$/);
|
|
141
|
+
if (m) v = m[1];
|
|
142
|
+
v = v.trim();
|
|
143
|
+
// Strip comillas wrapping si las hay
|
|
144
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
|
|
145
|
+
v = v.slice(1, -1);
|
|
146
|
+
}
|
|
147
|
+
return v;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const linea of lineas) {
|
|
151
|
+
// Detectar clave de primer nivel (sin indent)
|
|
152
|
+
const m = linea.match(/^([a-zA-Z_][a-zA-Z0-9_]*):/);
|
|
153
|
+
if (m) {
|
|
154
|
+
claves.add(m[1]);
|
|
155
|
+
if (m[1] === 'nivelRiesgo') {
|
|
156
|
+
const crudo = linea.split(':').slice(1).join(':').trim();
|
|
157
|
+
nivelRiesgo = limpiarValorYaml(crudo);
|
|
158
|
+
dentroDeFragmentos = false;
|
|
159
|
+
} else if (m[1] === 'fragmentos') {
|
|
160
|
+
dentroDeFragmentos = true;
|
|
161
|
+
// Caso fragmentos: [_intent-spec] inline
|
|
162
|
+
if (linea.includes('_intent-spec')) {
|
|
163
|
+
fragmentosTieneIntentSpec = true;
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
dentroDeFragmentos = false;
|
|
167
|
+
}
|
|
168
|
+
} else if (dentroDeFragmentos && linea.includes('_intent-spec')) {
|
|
169
|
+
fragmentosTieneIntentSpec = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Solo evaluar agentes ALTO
|
|
174
|
+
if (nivelRiesgo !== 'ALTO') {
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Verificar las 6 partes obligatorias
|
|
179
|
+
const CAMPOS_OBLIGATORIOS = ['strategy', 'healthMetrics', 'steering', 'hardGuardrails', 'maxTurnos'];
|
|
180
|
+
const faltan = CAMPOS_OBLIGATORIOS.filter((c) => !claves.has(c));
|
|
181
|
+
|
|
182
|
+
if (!fragmentosTieneIntentSpec) {
|
|
183
|
+
faltan.push('fragmentos:[_intent-spec]');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (faltan.length === 0) {
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ─── Emitir nudge ─────────────────────────────────────────────────────────
|
|
191
|
+
const CWD = process.cwd();
|
|
192
|
+
const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
|
|
193
|
+
const nombreAgente = path.basename(filePath, '.md');
|
|
194
|
+
|
|
195
|
+
const nudge = {
|
|
196
|
+
id: crypto.randomBytes(8).toString('hex'),
|
|
197
|
+
kind: 'intent-spec-incomplete',
|
|
198
|
+
target: 'auto-evolucion-swl',
|
|
199
|
+
source: 'hooks/validar-intent-spec.js',
|
|
200
|
+
message:
|
|
201
|
+
`Agente ALTO riesgo ${nombreAgente} no declara intent spec completo. ` +
|
|
202
|
+
`Faltan: ${faltan.join(', ')}. Cargar Skill("proceso-intent-engineering") + ` +
|
|
203
|
+
`regla intent-engineering.md y completar frontmatter.`,
|
|
204
|
+
data: {
|
|
205
|
+
archivo: rutaRelativa,
|
|
206
|
+
agente: nombreAgente,
|
|
207
|
+
nivelRiesgo: 'ALTO',
|
|
208
|
+
faltan,
|
|
209
|
+
},
|
|
210
|
+
ts: new Date().toISOString(),
|
|
211
|
+
accionado: false,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const nudgesDir = path.join(CWD, '.planning', 'evolution');
|
|
215
|
+
const nudgesFile = path.join(nudgesDir, 'nudges.jsonl');
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
if (!fs.existsSync(nudgesDir)) {
|
|
219
|
+
fs.mkdirSync(nudgesDir, { recursive: true });
|
|
220
|
+
}
|
|
221
|
+
fs.appendFileSync(nudgesFile, JSON.stringify(nudge) + '\n');
|
|
222
|
+
} catch (_) {
|
|
223
|
+
// Si falla persistir, no romper el flujo del bloqueo
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ─── Modo blocking (ADR-0033) ─────────────────────────────────────────────
|
|
227
|
+
// El agente ALTO tiene frontmatter incompleto: bloquear (exit 2) y surface el
|
|
228
|
+
// detalle a Claude por stderr para que complete el intent spec. Opt-out global
|
|
229
|
+
// SWL_INTENT_SPEC=0 ya cortó arriba; aquí el bloqueo es activo.
|
|
230
|
+
const razon = [
|
|
231
|
+
`Intent spec incompleto en agente ALTO riesgo: ${nombreAgente}`,
|
|
232
|
+
`Faltan campos obligatorios: ${faltan.join(', ')}.`,
|
|
233
|
+
``,
|
|
234
|
+
`Completa el frontmatter según reglas/intent-engineering.md:`,
|
|
235
|
+
` strategy, healthMetrics, steering, hardGuardrails, maxTurnos,`,
|
|
236
|
+
` y fragmentos: [_intent-spec].`,
|
|
237
|
+
`Carga Skill("proceso-intent-engineering") para la guía completa.`,
|
|
238
|
+
``,
|
|
239
|
+
`Para omitir esta verificación: SWL_INTENT_SPEC=0 (registra la excepción).`,
|
|
240
|
+
].join('\n');
|
|
241
|
+
process.stderr.write(razon);
|
|
242
|
+
process.exit(2);
|