@saulwade/swl-ses 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CLAUDE.md +3 -3
  2. package/README.md +561 -560
  3. package/agentes/nemesis-auditor-swl.md +161 -161
  4. package/bin/swl-mcp-server.js +49 -22
  5. package/bin/swl-ses.js +74 -0
  6. package/comandos/swl/.evolved.json +22 -22
  7. package/comandos/swl/contribuir.md +233 -233
  8. package/comandos/swl/ejecutar-fase.md +33 -4
  9. package/comandos/swl/metricas.md +72 -0
  10. package/comandos/swl/nemesis.md +122 -122
  11. package/gateway/lib/event-channel.js +191 -191
  12. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  13. package/habilidades/benchmark-memoria/SKILL.md +186 -186
  14. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  15. package/habilidades/discutir-fase/SKILL.md +50 -2
  16. package/habilidades/doubt-driven-review/SKILL.md +171 -171
  17. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  18. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -0
  19. package/habilidades/eval-framework/SKILL.md +212 -212
  20. package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
  21. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  22. package/habilidades/harness-claude-code/SKILL.md +299 -299
  23. package/habilidades/infra-github-actions/SKILL.md +166 -166
  24. package/habilidades/legacy-code-rescue/SKILL.md +267 -267
  25. package/habilidades/manejo-errores/.evolved.json +8 -8
  26. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  27. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
  28. package/habilidades/patrones-python/SKILL.md +229 -229
  29. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  30. package/habilidades/planear-fase/SKILL.md +319 -319
  31. package/habilidades/protocolo-revision-swl/SKILL.md +276 -0
  32. package/habilidades/release-semver/.evolved.json +8 -8
  33. package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
  34. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  35. package/habilidades/testing-python/SKILL.md +340 -340
  36. package/habilidades/verificar-trabajo/SKILL.md +49 -5
  37. package/habilidades/web-fetcher-routing/SKILL.md +75 -75
  38. package/hooks/claudemd-bloat-detector.js +161 -161
  39. package/hooks/lib/agent-routing.js +107 -107
  40. package/hooks/lib/auto-consolidator.js +335 -335
  41. package/hooks/lib/error-classifier.js +308 -308
  42. package/hooks/lib/merkle-audit.js +96 -96
  43. package/hooks/lib/provenance-tracker.js +191 -191
  44. package/hooks/lib/rate-limit-tracker.js +253 -253
  45. package/hooks/lib/resource-quota.js +122 -122
  46. package/hooks/lib/retry-jitter.js +165 -165
  47. package/hooks/lib/security-net.js +201 -201
  48. package/hooks/lib/skill-auditor.js +588 -588
  49. package/hooks/lib/sync-status.js +228 -228
  50. package/hooks/lib/taint-tracker.js +107 -107
  51. package/hooks/lib/text-similarity.js +241 -241
  52. package/hooks/lib/toon-compressor.js +245 -245
  53. package/hooks/registro-turnos.js +209 -209
  54. package/hooks/sugerir-regenerar-inventario.js +170 -170
  55. package/hooks/validar-formato-post-subagente.js +140 -140
  56. package/hooks/validar-memoria-hook.js +218 -218
  57. package/instintos/prompt-appendices.yaml +57 -57
  58. package/manifiestos/agent-output-schemas.json +57 -57
  59. package/manifiestos/modulos.json +1321 -1262
  60. package/manifiestos/perfiles.json +2 -1
  61. package/manifiestos/skills-lock.json +1114 -1114
  62. package/package.json +3 -3
  63. package/plantillas/auditor-veto-template.md +105 -105
  64. package/plantillas/github-workflows/README.md +47 -47
  65. package/plantillas/github-workflows/release-please.yml +44 -44
  66. package/plantillas/github-workflows/swl-ci.yml +107 -107
  67. package/plantillas/github-workflows/swl-security.yml +51 -51
  68. package/plugin.json +351 -343
  69. package/reglas/analisis-previo-tareas-grandes.md +172 -172
  70. package/reglas/arreglar-al-detectar.md +147 -147
  71. package/reglas/fragmentos-compartidos.md +152 -152
  72. package/reglas/harness-claude-code.md +213 -213
  73. package/reglas/usar-context7.md +226 -226
  74. package/schemas/diary-entry.schema.json +80 -80
  75. package/scripts/audit-tools/audit-history.js +330 -330
  76. package/scripts/audit-tools/bundle-tracker.js +290 -290
  77. package/scripts/audit-tools/canary-monitor.js +352 -352
  78. package/scripts/audit-tools/code-profiler.js +605 -605
  79. package/scripts/audit-tools/dep-doctor.js +320 -320
  80. package/scripts/audit-tools/env-validator.js +206 -206
  81. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  82. package/scripts/audit-tools/lib/output.js +23 -23
  83. package/scripts/audit-tools/migration-checker.js +392 -392
  84. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  85. package/scripts/benchmark-memoria.js +167 -167
  86. package/scripts/configurar-branch-protection.js +418 -418
  87. package/scripts/derivar-feature-list.js +489 -0
  88. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  89. package/scripts/doctor.js +31 -4
  90. package/scripts/field-report.js +199 -199
  91. package/scripts/generar-checklists-consolidados.js +273 -273
  92. package/scripts/generar-inventario.js +420 -420
  93. package/scripts/generar-matriz-lenguajes.js +271 -271
  94. package/scripts/instalador.js +56 -5
  95. package/scripts/lib/artefactos-python.js +43 -43
  96. package/scripts/lib/benchmark-metrics.js +160 -160
  97. package/scripts/lib/budget-enforcer.js +252 -252
  98. package/scripts/lib/configurar-ci.js +380 -380
  99. package/scripts/lib/contadores-inventario.js +217 -217
  100. package/scripts/lib/detectar-runtime.js +75 -9
  101. package/scripts/lib/detectar-stack-detallado.js +307 -307
  102. package/scripts/lib/diary-entry.js +234 -234
  103. package/scripts/lib/estado.js +13 -1
  104. package/scripts/lib/eval-metrics-store.js +218 -218
  105. package/scripts/lib/eval-quality.js +171 -171
  106. package/scripts/lib/eval-schemas.js +144 -144
  107. package/scripts/lib/eval-self-correct.js +106 -106
  108. package/scripts/lib/eval-validator.js +185 -185
  109. package/scripts/lib/expandir-targets.js +71 -0
  110. package/scripts/lib/jaccard-similarity.js +98 -98
  111. package/scripts/lib/longmemeval-runner.js +125 -125
  112. package/scripts/lib/manifiestos.js +42 -1
  113. package/scripts/lib/npm-version.js +261 -261
  114. package/scripts/lib/paquetes-conocidos.js +50 -50
  115. package/scripts/lib/parsear-opciones.js +3 -0
  116. package/scripts/lib/prompt-builder.js +264 -264
  117. package/scripts/lib/rrf-fusion.js +175 -175
  118. package/scripts/lib/scoring-instintos.js +277 -277
  119. package/scripts/lib/semantic-search.js +252 -252
  120. package/scripts/lib/toml-merge.js +204 -0
  121. package/scripts/lib/transformadores/base.js +43 -9
  122. package/scripts/lib/transformadores/codex.js +375 -115
  123. package/scripts/lib/transformadores/cursor.js +359 -0
  124. package/scripts/lib/transformadores/index.js +2 -0
  125. package/scripts/limpiar-artefactos-python.js +131 -131
  126. package/scripts/mcp-server/README.md +122 -80
  127. package/scripts/mcp-server/auth.js +105 -0
  128. package/scripts/mcp-server/cache.js +106 -0
  129. package/scripts/mcp-server/handlers.js +386 -206
  130. package/scripts/mcp-server/telemetry.js +78 -0
  131. package/scripts/migrar-csv-a-array.js +168 -168
  132. package/scripts/migrar-fase-dominio.js +201 -201
  133. package/scripts/publicar.js +511 -511
  134. package/scripts/run-eval.js +141 -141
  135. package/scripts/validar-manifest.js +231 -195
  136. package/scripts/validar-userland-vacio.js +110 -110
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: verificar-trabajo
3
- description: Verificación goal-backward del trabajo ejecutado en 4 niveles progresivos — EXISTE, SUSTANTIVO, CONECTADO, DATOS_FLUYEN. Detecta stubs, componentes huérfanos, integraciones rotas y flujos incompletos. Produce veredictos estructurados JSON con clasificación de riesgo (Low/Medium/High) y evidencia verificable. Soporta loop de reparación cuando el veredicto es Fail.
4
- version: "1.1.1"
3
+ description: Verificación goal-backward del trabajo ejecutado en 4 niveles progresivos — EXISTE, SUSTANTIVO, CONECTADO, DATOS_FLUYEN. Clasifica claims en 4 tipos (TASK, FIX, TEST_OR_BUILD, FEATURE_GO) con evidencia proporcional. Detecta stubs, componentes huérfanos, integraciones rotas y flujos incompletos. Produce veredictos estructurados JSON con clasificación de riesgo (Low/Medium/High) y evidencia verificable. Soporta loop de reparación cuando el veredicto es Fail.
4
+ version: "1.2.0"
5
5
  evolved: true
6
- evolved-from: "1.1.0"
7
- evolved-at: "2026-05-05"
6
+ evolved-from: "1.1.1"
7
+ evolved-at: "2026-05-15"
8
8
  evolved-by: "aprender"
9
- evolved-note: "Gotcha de la sesión SIGM 2026-05-05 (L7): tests + linter no detectan schema-seed drift; cuando el alcance toca BD, Nivel 4 obligatorio con docker compose down -v && up fresco"
9
+ evolved-note: "Taxonomía de claim types adoptada de cc-sdd kiro-verify-completion: cada tipo de claim (TASK/FIX/TEST_OR_BUILD/FEATURE_GO) requiere evidencia distinta. Previene declarar éxito broader que la evidencia disponible."
10
10
  herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
11
11
  exclusiones:
12
12
  - "No cargar durante la implementación activa de una tarea; la verificación es posterior a la implementación, no concurrente."
@@ -60,6 +60,50 @@ El skill `verificacion-evidencia` tiene el detalle completo de este protocolo.
60
60
 
61
61
  ---
62
62
 
63
+ ## Taxonomía de claims (clasifica ANTES de verificar)
64
+
65
+ El nivel de evidencia requerido depende del tipo de claim que se quiere validar.
66
+ Antes de aplicar los 4 niveles, identifica qué clase de claim estás verificando.
67
+
68
+ Inspirado en `kiro-verify-completion` (cc-sdd) — ver `temp/cc-sdd-main/tools/cc-sdd/templates/agents/claude-code-skills/skills/kiro-verify-completion/SKILL.md`.
69
+
70
+ ### Los 4 tipos de claim
71
+
72
+ | Claim type | Cuándo aplica | Evidencia mínima obligatoria |
73
+ |------------|---------------|-------------------------------|
74
+ | **TASK** | "Esta tarea está completa" — una sub-tarea atómica del PLAN.md | Niveles 1-3; tests locales de la tarea pasan; sin findings de revisor bloqueantes; evidencia alineada al boundary de la tarea |
75
+ | **FIX** | "Este bug está arreglado" — corrección de un defecto reportado | Síntoma original reproducido antes del fix + ya no se reproduce después; sin regresiones en el scope relevante |
76
+ | **TEST_OR_BUILD** | "Los tests pasan" / "el build pasa" — claim mecánico | Output literal del comando + exit code 0; conteo de pass/fail/skipped explícito; NO inferir desde checks no relacionados |
77
+ | **FEATURE_GO** | "Esta feature/fase está lista para producción" — claim de cierre mayor | Suite de tests completa verde + smoke test runtime (la app realmente arranca a estado usable) + cobertura de requisitos evaluada + alineación end-to-end con design + integración cross-task verificada |
78
+
79
+ ### Reglas duras
80
+
81
+ 1. **Identifica el claim type ANTES** de seleccionar evidencia. No reverse-engineer "qué tipo es" desde la evidencia que ya tienes.
82
+ 2. **Rechaza claims más amplios que la evidencia**. Si solo verificaste una tarea (TASK), no puedes emitir FEATURE_GO. Reporta el alcance real.
83
+ 3. **Para TEST_OR_BUILD**: la evidencia es el output del comando, no la inferencia. Si los tests pasan pero hay 47 skipped por preconditions falsas, NO es un Pass limpio — reporta los skipped como riesgo (ver regla `monitor-ci.md` § "verde con N skipped").
84
+ 4. **Para FEATURE_GO**: un test suite verde NO basta. Requiere también smoke runtime, cobertura de requisitos y alineación end-to-end.
85
+ 5. **Si la validación canónica no puede completarse** (sin BD disponible, sin red, sin runtime), retorna `MANUAL_VERIFY_REQUIRED` en lugar de inventar evidencia.
86
+
87
+ ### Mapeo a salidas del veredicto
88
+
89
+ El claim type se incluye en el veredicto estructurado JSON:
90
+
91
+ ```json
92
+ {
93
+ "claim_type": "TASK | FIX | TEST_OR_BUILD | FEATURE_GO",
94
+ "claim_text": "Tarea 3.2 — Crear endpoint POST /facturas",
95
+ "verdict": "VERIFIED | NOT_VERIFIED | MANUAL_VERIFY_REQUIRED",
96
+ "scope_evidence_match": "exact | broader_claim_than_evidence | narrower_claim_than_evidence",
97
+ ...
98
+ }
99
+ ```
100
+
101
+ Si `scope_evidence_match` es `broader_claim_than_evidence`, el verdict se
102
+ degrada automáticamente a `NOT_VERIFIED` con instrucción de re-emitir el
103
+ claim con scope acotado.
104
+
105
+ ---
106
+
63
107
  ## Los 4 niveles de verificación
64
108
 
65
109
  ### Nivel 1 — EXISTE
@@ -1,75 +1,75 @@
1
- ---
2
- name: web-fetcher-routing
3
- description: >
4
- Routing inteligente para fetching de URLs según dominio y formato:
5
- GitHub raw, PDF, sitios JS-heavy y default. Reduce tokens consumidos
6
- evitando WebFetch cuando hay alternativa más eficiente. Cargar antes
7
- de hacer WebFetch a URLs externas, especialmente repositorios GitHub,
8
- PDFs públicos, o sitios con paywall/Cloudflare/JS heavy.
9
- ---
10
-
11
- # web-fetcher-routing
12
-
13
- Selecciona la herramienta correcta para cada URL antes de intentar el fetch.
14
- Un WebFetch al HTML de GitHub UI consume 10-30× más tokens que leer el raw.
15
- Un PDF vía Read devuelve basura binaria. La elección equivocada cuesta tokens
16
- reales; este skill la automatiza.
17
-
18
- ## Cuándo cargar
19
-
20
- - Antes de hacer `WebFetch` a cualquier URL externa no trivial.
21
- - Cuando la URL apunta a GitHub (repositorio, archivo o blob).
22
- - Cuando la URL termina en `.pdf` o el contexto indica documento PDF.
23
- - Cuando el fetch previo falló con 402, página de login o contenido vacío
24
- (señal de JS-heavy o Cloudflare).
25
-
26
- ## Cuándo NO cargar
27
-
28
- - URLs internas del proyecto o localhost — usar `Read` directamente.
29
- - Contenido ya cargado en el contexto de esta sesión — no re-fetchar.
30
- - Cuando el usuario dictó explícitamente la herramienta a usar
31
- ("usa WebFetch", "lee con curl").
32
-
33
- ## Tabla de routing
34
-
35
- | Patrón de URL | Herramienta | Razón |
36
- |---|---|---|
37
- | `github.com/[user]/[repo]/blob/[branch]/[path]` | Reescribir a `raw.githubusercontent.com/[user]/[repo]/[branch]/[path]` y hacer `WebFetch` con la URL reescrita | Evita el HTML de la UI de GitHub; 10-30× menos tokens |
38
- | `raw.githubusercontent.com/...` | `WebFetch` directo | Ya es raw content |
39
- | URL termina en `.pdf` | Invocar `Skill("swl-markitdown")` para conversión con `pdftotext` o `markitdown` | `Read` no soporta PDF; `WebFetch` devuelve binario o HTML de visor |
40
- | `x.com`, `twitter.com`, cualquier host con CAPTCHA o Cloudflare detectado | Invocar `Skill("agent-browser")` con headless Chrome | `WebFetch` devuelve 402 o página de login; `agent-browser` usa accessibility tree (~82% menos tokens que screenshots) |
41
- | `mp.weixin.qq.com`, `feishu.cn`, `larksuite.com` | Invocar `Skill("agent-browser")` | Plataformas chinas con autenticación o JS requerido |
42
- | Todo lo demás | `WebFetch` directo | El caso común; probar primero antes de escalar |
43
-
44
- ## Algoritmo de decisión
45
-
46
- 1. Parsear la URL: extraer esquema, dominio y extensión del path.
47
- 2. Comparar con los patrones de la tabla, en orden de arriba hacia abajo.
48
- 3. Si el dominio es `github.com` con segmento `/blob/` en el path:
49
- reescribir la URL antes de hacer el fetch.
50
- 4. Invocar la herramienta asignada al patrón que coincide.
51
- 5. Si el resultado tiene señales de paywall o error (primeras 10 líneas
52
- contienen "Subscribe", "Sign in", "403", página vacía): escalar al
53
- patrón JS-heavy con `Skill("agent-browser")`.
54
- 6. Reportar qué método se usó y por qué, en una línea antes del contenido.
55
-
56
- ## Ejemplo de reescritura GitHub
57
-
58
- ```
59
- # URL original
60
- https://github.com/tw93/Waza/blob/main/skills/read/SKILL.md
61
-
62
- # URL reescrita para raw
63
- https://raw.githubusercontent.com/tw93/Waza/main/skills/read/SKILL.md
64
- ```
65
-
66
- ## Señales de fallo que activan escalado
67
-
68
- | Señal en la respuesta | Acción |
69
- |---|---|
70
- | HTTP 402 | Escalar a `agent-browser` |
71
- | Primeras líneas con "Sign in" o "Subscribe" | Detener, avisar al usuario |
72
- | Contenido HTML con menos de 200 caracteres | Reintentar con método alternativo |
73
- | Binario o caracteres ilegibles | Verificar si es PDF; si sí, usar `swl-markitdown` |
74
-
75
- <!-- Adaptado de Waza/skills/read bajo MIT License (tw93/Waza) -->
1
+ ---
2
+ name: web-fetcher-routing
3
+ description: >
4
+ Routing inteligente para fetching de URLs según dominio y formato:
5
+ GitHub raw, PDF, sitios JS-heavy y default. Reduce tokens consumidos
6
+ evitando WebFetch cuando hay alternativa más eficiente. Cargar antes
7
+ de hacer WebFetch a URLs externas, especialmente repositorios GitHub,
8
+ PDFs públicos, o sitios con paywall/Cloudflare/JS heavy.
9
+ ---
10
+
11
+ # web-fetcher-routing
12
+
13
+ Selecciona la herramienta correcta para cada URL antes de intentar el fetch.
14
+ Un WebFetch al HTML de GitHub UI consume 10-30× más tokens que leer el raw.
15
+ Un PDF vía Read devuelve basura binaria. La elección equivocada cuesta tokens
16
+ reales; este skill la automatiza.
17
+
18
+ ## Cuándo cargar
19
+
20
+ - Antes de hacer `WebFetch` a cualquier URL externa no trivial.
21
+ - Cuando la URL apunta a GitHub (repositorio, archivo o blob).
22
+ - Cuando la URL termina en `.pdf` o el contexto indica documento PDF.
23
+ - Cuando el fetch previo falló con 402, página de login o contenido vacío
24
+ (señal de JS-heavy o Cloudflare).
25
+
26
+ ## Cuándo NO cargar
27
+
28
+ - URLs internas del proyecto o localhost — usar `Read` directamente.
29
+ - Contenido ya cargado en el contexto de esta sesión — no re-fetchar.
30
+ - Cuando el usuario dictó explícitamente la herramienta a usar
31
+ ("usa WebFetch", "lee con curl").
32
+
33
+ ## Tabla de routing
34
+
35
+ | Patrón de URL | Herramienta | Razón |
36
+ |---|---|---|
37
+ | `github.com/[user]/[repo]/blob/[branch]/[path]` | Reescribir a `raw.githubusercontent.com/[user]/[repo]/[branch]/[path]` y hacer `WebFetch` con la URL reescrita | Evita el HTML de la UI de GitHub; 10-30× menos tokens |
38
+ | `raw.githubusercontent.com/...` | `WebFetch` directo | Ya es raw content |
39
+ | URL termina en `.pdf` | Invocar `Skill("swl-markitdown")` para conversión con `pdftotext` o `markitdown` | `Read` no soporta PDF; `WebFetch` devuelve binario o HTML de visor |
40
+ | `x.com`, `twitter.com`, cualquier host con CAPTCHA o Cloudflare detectado | Invocar `Skill("agent-browser")` con headless Chrome | `WebFetch` devuelve 402 o página de login; `agent-browser` usa accessibility tree (~82% menos tokens que screenshots) |
41
+ | `mp.weixin.qq.com`, `feishu.cn`, `larksuite.com` | Invocar `Skill("agent-browser")` | Plataformas chinas con autenticación o JS requerido |
42
+ | Todo lo demás | `WebFetch` directo | El caso común; probar primero antes de escalar |
43
+
44
+ ## Algoritmo de decisión
45
+
46
+ 1. Parsear la URL: extraer esquema, dominio y extensión del path.
47
+ 2. Comparar con los patrones de la tabla, en orden de arriba hacia abajo.
48
+ 3. Si el dominio es `github.com` con segmento `/blob/` en el path:
49
+ reescribir la URL antes de hacer el fetch.
50
+ 4. Invocar la herramienta asignada al patrón que coincide.
51
+ 5. Si el resultado tiene señales de paywall o error (primeras 10 líneas
52
+ contienen "Subscribe", "Sign in", "403", página vacía): escalar al
53
+ patrón JS-heavy con `Skill("agent-browser")`.
54
+ 6. Reportar qué método se usó y por qué, en una línea antes del contenido.
55
+
56
+ ## Ejemplo de reescritura GitHub
57
+
58
+ ```
59
+ # URL original
60
+ https://github.com/tw93/Waza/blob/main/skills/read/SKILL.md
61
+
62
+ # URL reescrita para raw
63
+ https://raw.githubusercontent.com/tw93/Waza/main/skills/read/SKILL.md
64
+ ```
65
+
66
+ ## Señales de fallo que activan escalado
67
+
68
+ | Señal en la respuesta | Acción |
69
+ |---|---|
70
+ | HTTP 402 | Escalar a `agent-browser` |
71
+ | Primeras líneas con "Sign in" o "Subscribe" | Detener, avisar al usuario |
72
+ | Contenido HTML con menos de 200 caracteres | Reintentar con método alternativo |
73
+ | Binario o caracteres ilegibles | Verificar si es PDF; si sí, usar `swl-markitdown` |
74
+
75
+ <!-- Adaptado de Waza/skills/read bajo MIT License (tw93/Waza) -->
@@ -1,161 +1,161 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * Hook: claudemd-bloat-detector.js
6
- * Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit)
7
- *
8
- * Ejecuta `scripts/auditar-claudemd.js` contra archivos `CLAUDE.md`
9
- * recién modificados y emite un nudge a `.planning/evolucion/nudges.jsonl`
10
- * si el veredicto es WARN o ERROR.
11
- *
12
- * Aplica ADR-0016 (best practices Anthropic "The CLAUDE.md file"):
13
- * detecta inflación (líneas excesivas, bullets monolíticos, secciones
14
- * canónicas ausentes, ausencia de @references) y sugiere intervención
15
- * con `/swl:claudemd refactor`.
16
- *
17
- * Opt-out: SWL_CLAUDEMD_BLOAT=0 desactiva completamente el hook.
18
- *
19
- * Comportamiento:
20
- * - Nunca bloquea operaciones (exit code 0 siempre)
21
- * - Solo emite nudge cuando veredicto != OK — ruido mínimo
22
- * - Solo se dispara con archivos cuyo basename sea exactamente CLAUDE.md
23
- * - Respeta exclusiones: temp/, node_modules/, respositorios-git/
24
- *
25
- * Formato del nudge:
26
- * {
27
- * id: string,
28
- * kind: "claudemd-bloat",
29
- * target: "documentador-swl",
30
- * source: "hooks/claudemd-bloat-detector.js",
31
- * message: "...",
32
- * data: { archivo, veredicto, lineas, hallazgos_count },
33
- * ts: ISO,
34
- * accionado: false
35
- * }
36
- */
37
-
38
- const fs = require('fs');
39
- const path = require('path');
40
- const crypto = require('crypto');
41
-
42
- // ─── Opt-out global ───────────────────────────────────────────────────────
43
- if (process.env.SWL_CLAUDEMD_BLOAT === '0') {
44
- process.exit(0);
45
- }
46
-
47
- let hookInput = '';
48
- try {
49
- hookInput = fs.readFileSync(0, 'utf-8');
50
- } catch (_) {
51
- process.exit(0);
52
- }
53
-
54
- let evento;
55
- try {
56
- evento = JSON.parse(hookInput);
57
- } catch (_) {
58
- process.exit(0);
59
- }
60
-
61
- const toolName = evento?.tool_name;
62
- const toolInput = evento?.tool_input;
63
-
64
- if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
65
- process.exit(0);
66
- }
67
-
68
- const filePath = toolInput?.file_path;
69
- if (!filePath) {
70
- process.exit(0);
71
- }
72
-
73
- // Solo CLAUDE.md (basename exacto, case-sensitive)
74
- const basename = path.basename(filePath);
75
- if (basename !== 'CLAUDE.md') {
76
- process.exit(0);
77
- }
78
-
79
- const pathNormalized = filePath.replace(/\\/g, '/');
80
- const RUTAS_EXCLUIDAS = [
81
- '/temp/',
82
- '/node_modules/',
83
- '/respositorios-git/',
84
- '/.planning/',
85
- ];
86
- if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
87
- process.exit(0);
88
- }
89
-
90
- // El archivo debe existir
91
- if (!fs.existsSync(filePath)) {
92
- process.exit(0);
93
- }
94
-
95
- // ─── Ejecutar auditor (módulo, no subproceso) ─────────────────────────────
96
- const CWD = process.cwd();
97
- const auditorPath = path.join(CWD, 'scripts', 'auditar-claudemd.js');
98
- if (!fs.existsSync(auditorPath)) {
99
- // No hay auditor instalado en este destino; salir silenciosamente
100
- process.exit(0);
101
- }
102
-
103
- let resultado;
104
- try {
105
- const { auditar } = require(auditorPath);
106
- resultado = auditar(filePath);
107
- } catch (_) {
108
- // Cualquier error del auditor: salir silenciosamente, no romper el hook
109
- process.exit(0);
110
- }
111
-
112
- // Solo emitir nudge si veredicto != OK
113
- if (!resultado || resultado.veredicto === 'OK') {
114
- process.exit(0);
115
- }
116
-
117
- // ─── Construir nudge ──────────────────────────────────────────────────────
118
- const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
119
- const topHallazgos = (resultado.hallazgos || [])
120
- .slice(0, 3)
121
- .map((h) => ` - [${h.severidad}] ${h.mensaje}`)
122
- .join('\n');
123
-
124
- const nudge = {
125
- id: crypto.randomBytes(8).toString('hex'),
126
- kind: 'claudemd-bloat',
127
- target: 'documentador-swl',
128
- source: 'hooks/claudemd-bloat-detector.js',
129
- message:
130
- `[claudemd] ${rutaRelativa} veredicto: ${resultado.veredicto} ` +
131
- `(${resultado.hallazgos.length} hallazgos)\n` +
132
- topHallazgos + '\n' +
133
- ` Ejecutar \`/swl:claudemd audit\` para detalle, ` +
134
- `\`/swl:claudemd refactor\` para sugerencias de extracción.`,
135
- data: {
136
- archivo: rutaRelativa,
137
- veredicto: resultado.veredicto,
138
- lineas: resultado.metricas?.lineas,
139
- secciones_ausentes: resultado.metricas?.secciones_ausentes || [],
140
- tiene_at_references: resultado.metricas?.tiene_at_references,
141
- hallazgos_count: resultado.hallazgos.length,
142
- },
143
- ts: new Date().toISOString(),
144
- accionado: false,
145
- accionado_ts: null,
146
- accionado_por: null,
147
- };
148
-
149
- // ─── Persistir a nudges.jsonl ─────────────────────────────────────────────
150
- try {
151
- const nudgesPath = path.join(CWD, '.planning', 'evolucion', 'nudges.jsonl');
152
- const nudgesDir = path.dirname(nudgesPath);
153
- if (!fs.existsSync(nudgesDir)) {
154
- fs.mkdirSync(nudgesDir, { recursive: true });
155
- }
156
- fs.appendFileSync(nudgesPath, JSON.stringify(nudge) + '\n', 'utf-8');
157
- } catch (_) {
158
- // No fallar el hook por error de escritura
159
- }
160
-
161
- process.exit(0);
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Hook: claudemd-bloat-detector.js
6
+ * Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit)
7
+ *
8
+ * Ejecuta `scripts/auditar-claudemd.js` contra archivos `CLAUDE.md`
9
+ * recién modificados y emite un nudge a `.planning/evolucion/nudges.jsonl`
10
+ * si el veredicto es WARN o ERROR.
11
+ *
12
+ * Aplica ADR-0016 (best practices Anthropic "The CLAUDE.md file"):
13
+ * detecta inflación (líneas excesivas, bullets monolíticos, secciones
14
+ * canónicas ausentes, ausencia de @references) y sugiere intervención
15
+ * con `/swl:claudemd refactor`.
16
+ *
17
+ * Opt-out: SWL_CLAUDEMD_BLOAT=0 desactiva completamente el hook.
18
+ *
19
+ * Comportamiento:
20
+ * - Nunca bloquea operaciones (exit code 0 siempre)
21
+ * - Solo emite nudge cuando veredicto != OK — ruido mínimo
22
+ * - Solo se dispara con archivos cuyo basename sea exactamente CLAUDE.md
23
+ * - Respeta exclusiones: temp/, node_modules/, respositorios-git/
24
+ *
25
+ * Formato del nudge:
26
+ * {
27
+ * id: string,
28
+ * kind: "claudemd-bloat",
29
+ * target: "documentador-swl",
30
+ * source: "hooks/claudemd-bloat-detector.js",
31
+ * message: "...",
32
+ * data: { archivo, veredicto, lineas, hallazgos_count },
33
+ * ts: ISO,
34
+ * accionado: false
35
+ * }
36
+ */
37
+
38
+ const fs = require('fs');
39
+ const path = require('path');
40
+ const crypto = require('crypto');
41
+
42
+ // ─── Opt-out global ───────────────────────────────────────────────────────
43
+ if (process.env.SWL_CLAUDEMD_BLOAT === '0') {
44
+ process.exit(0);
45
+ }
46
+
47
+ let hookInput = '';
48
+ try {
49
+ hookInput = fs.readFileSync(0, 'utf-8');
50
+ } catch (_) {
51
+ process.exit(0);
52
+ }
53
+
54
+ let evento;
55
+ try {
56
+ evento = JSON.parse(hookInput);
57
+ } catch (_) {
58
+ process.exit(0);
59
+ }
60
+
61
+ const toolName = evento?.tool_name;
62
+ const toolInput = evento?.tool_input;
63
+
64
+ if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
65
+ process.exit(0);
66
+ }
67
+
68
+ const filePath = toolInput?.file_path;
69
+ if (!filePath) {
70
+ process.exit(0);
71
+ }
72
+
73
+ // Solo CLAUDE.md (basename exacto, case-sensitive)
74
+ const basename = path.basename(filePath);
75
+ if (basename !== 'CLAUDE.md') {
76
+ process.exit(0);
77
+ }
78
+
79
+ const pathNormalized = filePath.replace(/\\/g, '/');
80
+ const RUTAS_EXCLUIDAS = [
81
+ '/temp/',
82
+ '/node_modules/',
83
+ '/respositorios-git/',
84
+ '/.planning/',
85
+ ];
86
+ if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
87
+ process.exit(0);
88
+ }
89
+
90
+ // El archivo debe existir
91
+ if (!fs.existsSync(filePath)) {
92
+ process.exit(0);
93
+ }
94
+
95
+ // ─── Ejecutar auditor (módulo, no subproceso) ─────────────────────────────
96
+ const CWD = process.cwd();
97
+ const auditorPath = path.join(CWD, 'scripts', 'auditar-claudemd.js');
98
+ if (!fs.existsSync(auditorPath)) {
99
+ // No hay auditor instalado en este destino; salir silenciosamente
100
+ process.exit(0);
101
+ }
102
+
103
+ let resultado;
104
+ try {
105
+ const { auditar } = require(auditorPath);
106
+ resultado = auditar(filePath);
107
+ } catch (_) {
108
+ // Cualquier error del auditor: salir silenciosamente, no romper el hook
109
+ process.exit(0);
110
+ }
111
+
112
+ // Solo emitir nudge si veredicto != OK
113
+ if (!resultado || resultado.veredicto === 'OK') {
114
+ process.exit(0);
115
+ }
116
+
117
+ // ─── Construir nudge ──────────────────────────────────────────────────────
118
+ const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
119
+ const topHallazgos = (resultado.hallazgos || [])
120
+ .slice(0, 3)
121
+ .map((h) => ` - [${h.severidad}] ${h.mensaje}`)
122
+ .join('\n');
123
+
124
+ const nudge = {
125
+ id: crypto.randomBytes(8).toString('hex'),
126
+ kind: 'claudemd-bloat',
127
+ target: 'documentador-swl',
128
+ source: 'hooks/claudemd-bloat-detector.js',
129
+ message:
130
+ `[claudemd] ${rutaRelativa} veredicto: ${resultado.veredicto} ` +
131
+ `(${resultado.hallazgos.length} hallazgos)\n` +
132
+ topHallazgos + '\n' +
133
+ ` Ejecutar \`/swl:claudemd audit\` para detalle, ` +
134
+ `\`/swl:claudemd refactor\` para sugerencias de extracción.`,
135
+ data: {
136
+ archivo: rutaRelativa,
137
+ veredicto: resultado.veredicto,
138
+ lineas: resultado.metricas?.lineas,
139
+ secciones_ausentes: resultado.metricas?.secciones_ausentes || [],
140
+ tiene_at_references: resultado.metricas?.tiene_at_references,
141
+ hallazgos_count: resultado.hallazgos.length,
142
+ },
143
+ ts: new Date().toISOString(),
144
+ accionado: false,
145
+ accionado_ts: null,
146
+ accionado_por: null,
147
+ };
148
+
149
+ // ─── Persistir a nudges.jsonl ─────────────────────────────────────────────
150
+ try {
151
+ const nudgesPath = path.join(CWD, '.planning', 'evolucion', 'nudges.jsonl');
152
+ const nudgesDir = path.dirname(nudgesPath);
153
+ if (!fs.existsSync(nudgesDir)) {
154
+ fs.mkdirSync(nudgesDir, { recursive: true });
155
+ }
156
+ fs.appendFileSync(nudgesPath, JSON.stringify(nudge) + '\n', 'utf-8');
157
+ } catch (_) {
158
+ // No fallar el hook por error de escritura
159
+ }
160
+
161
+ process.exit(0);