@saulwade/swl-ses 2.2.4 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +48 -7
- package/README.md +3 -3
- package/agentes/accesibilidad-wcag-swl.md +690 -690
- package/agentes/arquitecto-swl.md +267 -267
- package/agentes/auto-evolucion-swl.md +908 -908
- package/agentes/backend-api-swl.md +472 -472
- package/agentes/backend-csharp-swl.md +420 -420
- package/agentes/backend-go-swl.md +390 -390
- package/agentes/backend-java-swl.md +281 -281
- package/agentes/backend-node-swl.md +479 -479
- package/agentes/backend-python-swl.md +605 -605
- package/agentes/backend-rust-swl.md +364 -364
- package/agentes/backend-workers-swl.md +482 -482
- package/agentes/cloud-infra-swl.md +509 -509
- package/agentes/consolidador-swl.md +541 -541
- package/agentes/datos-swl.md +605 -605
- package/agentes/depurador-swl.md +352 -352
- package/agentes/devops-ci-swl.md +400 -400
- package/agentes/disenador-ui-swl.md +569 -569
- package/agentes/documentador-swl.md +345 -345
- package/agentes/frontend-angular-swl.md +621 -621
- package/agentes/frontend-css-swl.md +716 -716
- package/agentes/frontend-react-swl.md +692 -692
- package/agentes/frontend-swl.md +496 -496
- package/agentes/frontend-tailwind-swl.md +826 -826
- package/agentes/gh-fix-ci-swl.md +2 -2
- package/agentes/implementador-swl.md +328 -328
- package/agentes/investigador-swl.md +432 -432
- package/agentes/investigador-ux-swl.md +505 -505
- package/agentes/llm-apps-swl.md +278 -278
- package/agentes/migrador-swl.md +442 -442
- package/agentes/mobile-android-swl.md +511 -511
- package/agentes/mobile-cross-swl.md +541 -541
- package/agentes/mobile-ios-swl.md +502 -502
- package/agentes/mobile-testing-swl.md +302 -302
- package/agentes/nemesis-auditor-swl.md +285 -285
- package/agentes/notificador-swl.md +918 -918
- package/agentes/observabilidad-swl.md +438 -438
- package/agentes/orquestador-swl.md +1026 -1026
- package/agentes/pagos-swl.md +310 -310
- package/agentes/perfilador-usuario-swl.md +321 -321
- package/agentes/planificador-swl.md +399 -399
- package/agentes/producto-prd-swl.md +589 -589
- package/agentes/red-team-swl.md +1 -1
- package/agentes/release-manager-swl.md +590 -590
- package/agentes/rendimiento-swl.md +713 -713
- package/agentes/resolutor-build-swl.md +245 -245
- package/agentes/revisor-angular-swl.md +278 -278
- package/agentes/revisor-codigo-swl.md +505 -505
- package/agentes/revisor-csharp-swl.md +264 -264
- package/agentes/revisor-go-swl.md +259 -259
- package/agentes/revisor-java-swl.md +257 -257
- package/agentes/revisor-kotlin-swl.md +273 -273
- package/agentes/revisor-nextjs-swl.md +281 -281
- package/agentes/revisor-php-swl.md +271 -271
- package/agentes/revisor-react-swl.md +278 -278
- package/agentes/revisor-rust-swl.md +346 -346
- package/agentes/revisor-seguridad-swl.md +399 -399
- package/agentes/revisor-swift-swl.md +268 -268
- package/agentes/revisor-typescript-swl.md +346 -346
- package/agentes/sre-swl.md +291 -291
- package/agentes/tdd-qa-swl.md +393 -393
- package/comandos/swl/aprobar-plan.md +146 -141
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/predecir.md +139 -139
- package/comandos/swl/release.md +30 -8
- package/comandos/swl/sesiones.md +1 -1
- package/comandos/swl/status.md +343 -333
- package/gateway/agent-executor.js +1 -1
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/agent-deep-links/SKILL.md +148 -148
- package/habilidades/agentes-como-servicio/SKILL.md +2 -2
- package/habilidades/angular-moderno/SKILL.md +20 -1
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -276
- package/habilidades/backend-async-postgres-testing/SKILL.md +215 -215
- package/habilidades/backend-error-design/SKILL.md +221 -221
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/calidad-anti-patrones-universales/SKILL.md +368 -368
- package/habilidades/calidad-contract-testing/SKILL.md +165 -165
- package/habilidades/calidad-mutation-testing/SKILL.md +170 -170
- package/habilidades/changelog-generator/scripts/parse-commits.js +367 -367
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
- package/habilidades/contenedores-docker/SKILL.md +3 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/drift-detection/SKILL.md +179 -179
- package/habilidades/ejecutar-fase/SKILL.md +564 -541
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +1 -1
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/extractor-de-aprendizajes/SKILL.md +3 -3
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/recursos/disciplina-harness-regla.md +252 -252
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/perfil-usuario/SKILL.md +200 -200
- package/habilidades/planear-fase/SKILL.md +15 -1
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +255 -255
- package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
- package/habilidades/proceso-debate-adversarial/SKILL.md +191 -164
- package/habilidades/proceso-debate-adversarial/recursos/personas.md +105 -105
- package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
- package/habilidades/proceso-dynamic-workflows/SKILL.md +138 -138
- package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
- package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
- package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
- package/habilidades/proceso-modular-split/SKILL.md +256 -256
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +2 -2
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/structured-outputs/SKILL.md +2 -2
- package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
- package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
- package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
- package/habilidades/swl-dashboard/SKILL.md +2 -2
- package/habilidades/tdd-workflow/SKILL.md +33 -1
- package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +1 -1
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +1 -1
- package/hooks/captura-acciones-post.js +151 -0
- package/hooks/captura-acciones-session.js +155 -0
- package/hooks/ciclo-evolucion-subagente.js +26 -26
- package/hooks/ciclo-evolucion.js +26 -26
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/claudemd-duplicacion-detector.js +170 -170
- package/hooks/contexto-iteracion.js +144 -144
- package/hooks/guardrail-modelo.js +1 -1
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/autonomia.js +208 -208
- package/hooks/lib/briefing.js +512 -474
- package/hooks/lib/captura-acciones.js +392 -0
- package/hooks/lib/ciclo-evolucion.js +47 -47
- package/hooks/lib/deep-links.js +185 -185
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/etapa-auto-evolucion.js +701 -701
- package/hooks/lib/etapa-metricas.js +388 -388
- package/hooks/lib/etapa-perfil-usuario.js +376 -376
- package/hooks/lib/loop-telemetry.js +321 -321
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/model-router.js +2 -2
- package/hooks/lib/propose-step.js +358 -358
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/token-estimator.js +1 -0
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/lib/usage-model.js +1 -1
- package/hooks/linea-estado.js +2 -0
- package/hooks/registro-turnos.js +209 -209
- package/hooks/session-briefing.js +98 -98
- package/hooks/spec-gate.js +211 -211
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/tdd-gate.js +241 -241
- package/hooks/telemetria-skill-routing.js +100 -100
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-intent-spec.js +242 -242
- package/hooks/validar-memoria-hook.js +218 -218
- package/hooks/validar-planning-paths.js +134 -134
- package/instintos/autonomia.yaml +27 -27
- package/instintos/prompt-appendices.yaml +57 -57
- package/llms.txt +2 -2
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +2291 -1637
- package/manifiestos/hooks-config.json +18 -0
- package/manifiestos/modulos.json +4 -0
- package/manifiestos/planning-paths.json +44 -44
- package/manifiestos/skills-lock.json +1282 -1282
- package/package.json +2 -2
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +2 -2
- package/reglas/accesibilidad.md +10 -10
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/api-diseno.md +9 -9
- package/reglas/arreglar-al-detectar.md +240 -240
- package/reglas/auditorias-documentales-estructurales.md +7 -7
- package/reglas/cloud-infra.md +8 -8
- package/reglas/consultar-vault-primero.md +195 -195
- package/reglas/debatir-antes-de-aceptar.md +158 -158
- package/reglas/fragmentos-compartidos.md +183 -183
- package/reglas/hooks.md +6 -6
- package/reglas/intent-engineering.md +218 -218
- package/reglas/markitdown.md +8 -8
- package/reglas/memoria-consolidada.md +41 -0
- package/reglas/monitor-ci.md +309 -309
- package/reglas/patrones.md +6 -6
- package/reglas/seguridad-agentes.md +66 -0
- package/reglas/sesiones-paralelas.md +180 -180
- package/reglas/sin-duplicacion-reglas-globales.md +182 -182
- package/reglas/skills-estandar.md +6 -6
- package/reglas/testing.md +7 -7
- package/reglas/tests-cleanup.md +224 -224
- package/reglas/usar-code-review-graph.md +155 -155
- package/reglas/usar-context7.md +226 -226
- package/reglas/verificar-citas-normativas.md +548 -548
- package/schemas/agent-frontmatter.schema.json +3 -3
- package/schemas/agent-message.schema.json +73 -73
- package/schemas/agent-output-implementacion.schema.json +114 -114
- package/schemas/agent-output-planificacion.schema.json +150 -150
- package/schemas/agent-output-review.schema.json +98 -98
- package/schemas/diary-entry.schema.json +112 -112
- package/schemas/hook-profiles.schema.json +54 -54
- package/schemas/hooks-config.schema.json +89 -89
- package/schemas/instinct.schema.json +161 -152
- package/schemas/modulos.schema.json +38 -38
- package/schemas/perfiles.schema.json +36 -36
- package/schemas/plugin.schema.json +77 -77
- package/schemas/skill-evals.schema.json +119 -119
- package/schemas/skill-frontmatter.schema.json +245 -245
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/bootstrap-instintos.js +25 -7
- package/scripts/cli/aprobar-plan.js +73 -73
- package/scripts/cli/briefing.js +23 -23
- package/scripts/cli/ciclo-evolucion.js +26 -26
- package/scripts/cli/configurar-ci.js +40 -40
- package/scripts/cli/derivar-feature-list.js +25 -25
- package/scripts/cli/detectar-host.js +27 -27
- package/scripts/cli/diary-entry.js +69 -69
- package/scripts/cli/execution-state.js +18 -18
- package/scripts/cli/gateway-notify.js +41 -41
- package/scripts/cli/liberar-fase.js +42 -42
- package/scripts/cli/loop-telemetry.js +125 -125
- package/scripts/cli/mark-evolved.js +56 -56
- package/scripts/cli/metricas-dora.js +26 -26
- package/scripts/cli/near-duplicate.js +55 -55
- package/scripts/cli/notificaciones.js +123 -123
- package/scripts/cli/propose-step.js +29 -29
- package/scripts/cli/schedule-parse.js +19 -19
- package/scripts/cli/sugerir-modelo.js +20 -20
- package/scripts/cli/verificar-plan.js +36 -36
- package/scripts/cli/verificar-trazabilidad.js +35 -35
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/auditar-invocaciones-comandos.js +104 -104
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/ci-reader.js +193 -193
- package/scripts/lib/claude-sessions.js +1 -0
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-host-swl.js +175 -175
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
- package/scripts/lib/detector-reglas-duplicadas.js +220 -220
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/evidencia-release.js +322 -322
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/gate-hooks-requires.js +249 -249
- package/scripts/lib/gate-licencias.js +212 -212
- package/scripts/lib/git-metricas.js +257 -257
- package/scripts/lib/gitignore-manifest.js +44 -11
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/metricas-dora.js +204 -204
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/plan-lock.js +275 -275
- package/scripts/lib/pr-analyzer.js +399 -399
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/reglas-globales-conocidas.json +180 -180
- package/scripts/lib/resolver-plan-fase.js +37 -37
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/schema-version.js +164 -164
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/skill-metrics.js +41 -1
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/tool-cost-analyzer.js +1 -0
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +200 -200
- package/scripts/publicar.js +497 -497
- package/scripts/run-eval.js +141 -141
- package/scripts/tui/componentes/selector-multi.js +189 -189
- package/scripts/tui/componentes/selector-unico.js +158 -158
- package/scripts/tui/ejecutores.js +375 -375
- package/scripts/tui/index.js +162 -162
- package/scripts/tui/lib/colores.js +129 -129
- package/scripts/tui/lib/render.js +264 -264
- package/scripts/tui/lib/teclas.js +113 -113
- package/scripts/tui/pantallas/inspect.js +173 -173
- package/scripts/tui/pantallas/install-wizard.js +334 -334
- package/scripts/tui/pantallas/menu-principal.js +52 -52
- package/scripts/tui/pantallas/progreso.js +274 -274
- package/scripts/tui/pantallas/resumen.js +132 -132
- package/scripts/tui/pantallas/uninstall-wizard.js +208 -208
- package/scripts/tui/pantallas/update-wizard.js +232 -232
- package/scripts/tui/pantallas/welcome.js +187 -187
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/verificar-trazabilidad.js +329 -298
|
@@ -1,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/evolution/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', 'evolution', '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/evolution/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', 'evolution', '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,170 +1,170 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Hook: claudemd-duplicacion-detector.js
|
|
6
|
-
* Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit)
|
|
7
|
-
*
|
|
8
|
-
* Detecta duplicación de reglas globales (`~/.claude/rules/`) inline en
|
|
9
|
-
* `CLAUDE.md` de proyecto recién modificados. Consume el detector
|
|
10
|
-
* `scripts/lib/detector-reglas-duplicadas.js` que lee el catálogo
|
|
11
|
-
* declarativo `scripts/lib/reglas-globales-conocidas.json`.
|
|
12
|
-
*
|
|
13
|
-
* Aplica la regla `reglas/sin-duplicacion-reglas-globales.md`:
|
|
14
|
-
* el `CLAUDE.md` del proyecto NO debe duplicar reglas que ya viven en
|
|
15
|
-
* `~/.claude/rules/`. La regla global es fuente de verdad; el proyecto
|
|
16
|
-
* referencia, no duplica.
|
|
17
|
-
*
|
|
18
|
-
* Opt-out: SWL_CLAUDEMD_DUPLICACION=0 desactiva completamente el hook.
|
|
19
|
-
*
|
|
20
|
-
* Comportamiento:
|
|
21
|
-
* - Nunca bloquea operaciones (exit code 0 siempre)
|
|
22
|
-
* - Solo emite nudge cuando hay duplicaciones detectadas
|
|
23
|
-
* - Solo se dispara con archivos cuyo basename sea exactamente CLAUDE.md
|
|
24
|
-
* - Respeta exclusiones: temp/, node_modules/, respositorios-git/
|
|
25
|
-
* - NO evalúa user-level (~/.claude/CLAUDE.md) — ahí sí pueden
|
|
26
|
-
* declararse preferencias personales
|
|
27
|
-
*
|
|
28
|
-
* Formato del nudge:
|
|
29
|
-
* {
|
|
30
|
-
* id: string,
|
|
31
|
-
* kind: "claudemd-duplicacion-reglas",
|
|
32
|
-
* target: "documentador-swl",
|
|
33
|
-
* source: "hooks/claudemd-duplicacion-detector.js",
|
|
34
|
-
* message: "...",
|
|
35
|
-
* data: { archivo, duplicaciones: [...] },
|
|
36
|
-
* ts: ISO,
|
|
37
|
-
* accionado: false
|
|
38
|
-
* }
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
const fs = require('fs');
|
|
42
|
-
const path = require('path');
|
|
43
|
-
const os = require('os');
|
|
44
|
-
const crypto = require('crypto');
|
|
45
|
-
|
|
46
|
-
// ─── Opt-out global ───────────────────────────────────────────────────────
|
|
47
|
-
if (process.env.SWL_CLAUDEMD_DUPLICACION === '0') {
|
|
48
|
-
process.exit(0);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let hookInput = '';
|
|
52
|
-
try {
|
|
53
|
-
hookInput = fs.readFileSync(0, 'utf-8');
|
|
54
|
-
} catch (_) {
|
|
55
|
-
process.exit(0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let evento;
|
|
59
|
-
try {
|
|
60
|
-
evento = JSON.parse(hookInput);
|
|
61
|
-
} catch (_) {
|
|
62
|
-
process.exit(0);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const toolName = evento?.tool_name;
|
|
66
|
-
const toolInput = evento?.tool_input;
|
|
67
|
-
|
|
68
|
-
if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
69
|
-
process.exit(0);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const filePath = toolInput?.file_path;
|
|
73
|
-
if (!filePath) {
|
|
74
|
-
process.exit(0);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Solo CLAUDE.md (basename exacto, case-sensitive)
|
|
78
|
-
const basename = path.basename(filePath);
|
|
79
|
-
if (basename !== 'CLAUDE.md') {
|
|
80
|
-
process.exit(0);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const pathNormalized = filePath.replace(/\\/g, '/');
|
|
84
|
-
const RUTAS_EXCLUIDAS = [
|
|
85
|
-
'/temp/',
|
|
86
|
-
'/node_modules/',
|
|
87
|
-
'/respositorios-git/',
|
|
88
|
-
'/.planning/',
|
|
89
|
-
];
|
|
90
|
-
if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
|
|
91
|
-
process.exit(0);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// User-level: no evaluar (puede declarar preferencias personales)
|
|
95
|
-
const homeClaudeDir = path.resolve(path.join(os.homedir(), '.claude'));
|
|
96
|
-
const rutaAbs = path.resolve(filePath);
|
|
97
|
-
const esUserLevel = rutaAbs.startsWith(homeClaudeDir + path.sep) || rutaAbs === path.join(homeClaudeDir, 'CLAUDE.md');
|
|
98
|
-
|
|
99
|
-
// El archivo debe existir
|
|
100
|
-
if (!fs.existsSync(filePath)) {
|
|
101
|
-
process.exit(0);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ─── Cargar detector ──────────────────────────────────────────────────────
|
|
105
|
-
const CWD = process.cwd();
|
|
106
|
-
const detectorPath = path.join(CWD, 'scripts', 'lib', 'detector-reglas-duplicadas.js');
|
|
107
|
-
if (!fs.existsSync(detectorPath)) {
|
|
108
|
-
// No hay detector instalado en este destino; salir silenciosamente
|
|
109
|
-
process.exit(0);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let resultado;
|
|
113
|
-
try {
|
|
114
|
-
const { detectarDuplicaciones } = require(detectorPath);
|
|
115
|
-
const contenido = fs.readFileSync(filePath, 'utf8');
|
|
116
|
-
resultado = detectarDuplicaciones(contenido, null, { esUserLevel });
|
|
117
|
-
} catch (_) {
|
|
118
|
-
// Cualquier error del detector: salir silenciosamente
|
|
119
|
-
process.exit(0);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Solo emitir nudge si hay duplicaciones detectadas
|
|
123
|
-
if (!resultado || !resultado.evaluado || resultado.duplicaciones.length === 0) {
|
|
124
|
-
process.exit(0);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ─── Construir nudge ──────────────────────────────────────────────────────
|
|
128
|
-
const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
|
|
129
|
-
const topDuplicaciones = resultado.duplicaciones
|
|
130
|
-
.slice(0, 5)
|
|
131
|
-
.map((d) => ` - [${d.severidad}] ${d.id} (regla global: ${d.regla_global}, línea ~${d.linea_aproximada})`)
|
|
132
|
-
.join('\n');
|
|
133
|
-
|
|
134
|
-
const nudge = {
|
|
135
|
-
id: crypto.randomBytes(8).toString('hex'),
|
|
136
|
-
kind: 'claudemd-duplicacion-reglas',
|
|
137
|
-
target: 'documentador-swl',
|
|
138
|
-
source: 'hooks/claudemd-duplicacion-detector.js',
|
|
139
|
-
message:
|
|
140
|
-
`[claudemd-dup] ${rutaRelativa} duplica ${resultado.duplicaciones.length} regla(s) global(es):\n` +
|
|
141
|
-
topDuplicaciones + '\n' +
|
|
142
|
-
` Ejecutar \`/swl:claudemd audit\` para detalle, ` +
|
|
143
|
-
`\`/swl:claudemd refactor\` para sugerencias de reemplazo.\n` +
|
|
144
|
-
` Ver regla: \`reglas/sin-duplicacion-reglas-globales.md\`.`,
|
|
145
|
-
data: {
|
|
146
|
-
archivo: rutaRelativa,
|
|
147
|
-
total_evaluadas: resultado.total_reglas_evaluadas,
|
|
148
|
-
detectadas: resultado.duplicaciones.length,
|
|
149
|
-
ids: resultado.duplicaciones.map((d) => d.id),
|
|
150
|
-
reglas_globales: [...new Set(resultado.duplicaciones.map((d) => d.regla_global))],
|
|
151
|
-
},
|
|
152
|
-
ts: new Date().toISOString(),
|
|
153
|
-
accionado: false,
|
|
154
|
-
accionado_ts: null,
|
|
155
|
-
accionado_por: null,
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// ─── Persistir a nudges.jsonl ─────────────────────────────────────────────
|
|
159
|
-
try {
|
|
160
|
-
const nudgesPath = path.join(CWD, '.planning', 'evolution', 'nudges.jsonl');
|
|
161
|
-
const nudgesDir = path.dirname(nudgesPath);
|
|
162
|
-
if (!fs.existsSync(nudgesDir)) {
|
|
163
|
-
fs.mkdirSync(nudgesDir, { recursive: true });
|
|
164
|
-
}
|
|
165
|
-
fs.appendFileSync(nudgesPath, JSON.stringify(nudge) + '\n', 'utf-8');
|
|
166
|
-
} catch (_) {
|
|
167
|
-
// No fallar el hook por error de escritura
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook: claudemd-duplicacion-detector.js
|
|
6
|
+
* Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit)
|
|
7
|
+
*
|
|
8
|
+
* Detecta duplicación de reglas globales (`~/.claude/rules/`) inline en
|
|
9
|
+
* `CLAUDE.md` de proyecto recién modificados. Consume el detector
|
|
10
|
+
* `scripts/lib/detector-reglas-duplicadas.js` que lee el catálogo
|
|
11
|
+
* declarativo `scripts/lib/reglas-globales-conocidas.json`.
|
|
12
|
+
*
|
|
13
|
+
* Aplica la regla `reglas/sin-duplicacion-reglas-globales.md`:
|
|
14
|
+
* el `CLAUDE.md` del proyecto NO debe duplicar reglas que ya viven en
|
|
15
|
+
* `~/.claude/rules/`. La regla global es fuente de verdad; el proyecto
|
|
16
|
+
* referencia, no duplica.
|
|
17
|
+
*
|
|
18
|
+
* Opt-out: SWL_CLAUDEMD_DUPLICACION=0 desactiva completamente el hook.
|
|
19
|
+
*
|
|
20
|
+
* Comportamiento:
|
|
21
|
+
* - Nunca bloquea operaciones (exit code 0 siempre)
|
|
22
|
+
* - Solo emite nudge cuando hay duplicaciones detectadas
|
|
23
|
+
* - Solo se dispara con archivos cuyo basename sea exactamente CLAUDE.md
|
|
24
|
+
* - Respeta exclusiones: temp/, node_modules/, respositorios-git/
|
|
25
|
+
* - NO evalúa user-level (~/.claude/CLAUDE.md) — ahí sí pueden
|
|
26
|
+
* declararse preferencias personales
|
|
27
|
+
*
|
|
28
|
+
* Formato del nudge:
|
|
29
|
+
* {
|
|
30
|
+
* id: string,
|
|
31
|
+
* kind: "claudemd-duplicacion-reglas",
|
|
32
|
+
* target: "documentador-swl",
|
|
33
|
+
* source: "hooks/claudemd-duplicacion-detector.js",
|
|
34
|
+
* message: "...",
|
|
35
|
+
* data: { archivo, duplicaciones: [...] },
|
|
36
|
+
* ts: ISO,
|
|
37
|
+
* accionado: false
|
|
38
|
+
* }
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const fs = require('fs');
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const os = require('os');
|
|
44
|
+
const crypto = require('crypto');
|
|
45
|
+
|
|
46
|
+
// ─── Opt-out global ───────────────────────────────────────────────────────
|
|
47
|
+
if (process.env.SWL_CLAUDEMD_DUPLICACION === '0') {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let hookInput = '';
|
|
52
|
+
try {
|
|
53
|
+
hookInput = fs.readFileSync(0, 'utf-8');
|
|
54
|
+
} catch (_) {
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let evento;
|
|
59
|
+
try {
|
|
60
|
+
evento = JSON.parse(hookInput);
|
|
61
|
+
} catch (_) {
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const toolName = evento?.tool_name;
|
|
66
|
+
const toolInput = evento?.tool_input;
|
|
67
|
+
|
|
68
|
+
if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const filePath = toolInput?.file_path;
|
|
73
|
+
if (!filePath) {
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Solo CLAUDE.md (basename exacto, case-sensitive)
|
|
78
|
+
const basename = path.basename(filePath);
|
|
79
|
+
if (basename !== 'CLAUDE.md') {
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const pathNormalized = filePath.replace(/\\/g, '/');
|
|
84
|
+
const RUTAS_EXCLUIDAS = [
|
|
85
|
+
'/temp/',
|
|
86
|
+
'/node_modules/',
|
|
87
|
+
'/respositorios-git/',
|
|
88
|
+
'/.planning/',
|
|
89
|
+
];
|
|
90
|
+
if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// User-level: no evaluar (puede declarar preferencias personales)
|
|
95
|
+
const homeClaudeDir = path.resolve(path.join(os.homedir(), '.claude'));
|
|
96
|
+
const rutaAbs = path.resolve(filePath);
|
|
97
|
+
const esUserLevel = rutaAbs.startsWith(homeClaudeDir + path.sep) || rutaAbs === path.join(homeClaudeDir, 'CLAUDE.md');
|
|
98
|
+
|
|
99
|
+
// El archivo debe existir
|
|
100
|
+
if (!fs.existsSync(filePath)) {
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── Cargar detector ──────────────────────────────────────────────────────
|
|
105
|
+
const CWD = process.cwd();
|
|
106
|
+
const detectorPath = path.join(CWD, 'scripts', 'lib', 'detector-reglas-duplicadas.js');
|
|
107
|
+
if (!fs.existsSync(detectorPath)) {
|
|
108
|
+
// No hay detector instalado en este destino; salir silenciosamente
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let resultado;
|
|
113
|
+
try {
|
|
114
|
+
const { detectarDuplicaciones } = require(detectorPath);
|
|
115
|
+
const contenido = fs.readFileSync(filePath, 'utf8');
|
|
116
|
+
resultado = detectarDuplicaciones(contenido, null, { esUserLevel });
|
|
117
|
+
} catch (_) {
|
|
118
|
+
// Cualquier error del detector: salir silenciosamente
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Solo emitir nudge si hay duplicaciones detectadas
|
|
123
|
+
if (!resultado || !resultado.evaluado || resultado.duplicaciones.length === 0) {
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Construir nudge ──────────────────────────────────────────────────────
|
|
128
|
+
const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
|
|
129
|
+
const topDuplicaciones = resultado.duplicaciones
|
|
130
|
+
.slice(0, 5)
|
|
131
|
+
.map((d) => ` - [${d.severidad}] ${d.id} (regla global: ${d.regla_global}, línea ~${d.linea_aproximada})`)
|
|
132
|
+
.join('\n');
|
|
133
|
+
|
|
134
|
+
const nudge = {
|
|
135
|
+
id: crypto.randomBytes(8).toString('hex'),
|
|
136
|
+
kind: 'claudemd-duplicacion-reglas',
|
|
137
|
+
target: 'documentador-swl',
|
|
138
|
+
source: 'hooks/claudemd-duplicacion-detector.js',
|
|
139
|
+
message:
|
|
140
|
+
`[claudemd-dup] ${rutaRelativa} duplica ${resultado.duplicaciones.length} regla(s) global(es):\n` +
|
|
141
|
+
topDuplicaciones + '\n' +
|
|
142
|
+
` Ejecutar \`/swl:claudemd audit\` para detalle, ` +
|
|
143
|
+
`\`/swl:claudemd refactor\` para sugerencias de reemplazo.\n` +
|
|
144
|
+
` Ver regla: \`reglas/sin-duplicacion-reglas-globales.md\`.`,
|
|
145
|
+
data: {
|
|
146
|
+
archivo: rutaRelativa,
|
|
147
|
+
total_evaluadas: resultado.total_reglas_evaluadas,
|
|
148
|
+
detectadas: resultado.duplicaciones.length,
|
|
149
|
+
ids: resultado.duplicaciones.map((d) => d.id),
|
|
150
|
+
reglas_globales: [...new Set(resultado.duplicaciones.map((d) => d.regla_global))],
|
|
151
|
+
},
|
|
152
|
+
ts: new Date().toISOString(),
|
|
153
|
+
accionado: false,
|
|
154
|
+
accionado_ts: null,
|
|
155
|
+
accionado_por: null,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// ─── Persistir a nudges.jsonl ─────────────────────────────────────────────
|
|
159
|
+
try {
|
|
160
|
+
const nudgesPath = path.join(CWD, '.planning', 'evolution', 'nudges.jsonl');
|
|
161
|
+
const nudgesDir = path.dirname(nudgesPath);
|
|
162
|
+
if (!fs.existsSync(nudgesDir)) {
|
|
163
|
+
fs.mkdirSync(nudgesDir, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
fs.appendFileSync(nudgesPath, JSON.stringify(nudge) + '\n', 'utf-8');
|
|
166
|
+
} catch (_) {
|
|
167
|
+
// No fallar el hook por error de escritura
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
process.exit(0);
|