@saulwade/swl-ses 1.6.7 → 1.7.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.
- package/CLAUDE.md +196 -196
- package/README.md +578 -578
- package/agentes/orquestador-swl.md +37 -1
- package/comandos/swl/adoptar-proyecto.md +28 -0
- package/comandos/swl/aprender.md +142 -3
- package/comandos/swl/claudemd.md +81 -1
- package/comandos/swl/ejecutar-fase.md +14 -0
- package/comandos/swl/nuevo-proyecto.md +29 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +9 -5
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -0
- package/habilidades/swl-claudemd/SKILL.md +224 -2
- package/hooks/claudemd-duplicacion-detector.js +170 -0
- package/manifiestos/hooks-config.json +9 -0
- package/manifiestos/modulos.json +6 -1
- package/manifiestos/skills-lock.json +8 -8
- package/package.json +92 -92
- package/plugin.json +371 -371
- package/reglas/sin-duplicacion-reglas-globales.md +182 -0
- package/reglas/verificar-citas-temporales.md +139 -0
- package/scripts/auditar-claudemd.js +107 -1
- package/scripts/lib/detector-reglas-duplicadas.js +220 -0
- package/scripts/lib/reglas-globales-conocidas.json +112 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swl-claudemd
|
|
3
3
|
description: Conocimiento operacional para auditar y mantener archivos CLAUDE.md — contrato canónico de secciones (best practices Anthropic, ADR-0016), umbrales de bloat (líneas totales, bullets gigantes, placeholders, @references rotas), reglas de extracción a archivos referenciados con @, y plantillas de inicialización (init-user para ~/.claude/CLAUDE.md, init-project para CLAUDE.md raíz detectando stack). Provee las reglas; el comando /swl:claudemd ejecuta el flujo. Cargar desde ese comando o cuando el hook claudemd-bloat-detector sugiera intervención.
|
|
4
|
-
version: "1.0
|
|
4
|
+
version: "1.2.0"
|
|
5
5
|
herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
|
|
6
6
|
exclusiones:
|
|
7
7
|
- "No cargar para editar reglas globales en ~/.claude/rules/ — usar Edit directo."
|
|
@@ -9,6 +9,11 @@ exclusiones:
|
|
|
9
9
|
- "No cargar para validar otros archivos (.md de docs, READMEs) — solo CLAUDE.md tiene contrato canónico."
|
|
10
10
|
- "No cargar para generar el bloque del installer en CLAUDE.md de proyectos destino — eso lo hace scripts/lib/transformadores/claude.js."
|
|
11
11
|
evolvable: true
|
|
12
|
+
evolved: true
|
|
13
|
+
evolved-from: "1.1.0"
|
|
14
|
+
evolved-at: "2026-05-22"
|
|
15
|
+
evolved-by: "aprender"
|
|
16
|
+
evolved-note: "Dimensión 8 — detección de duplicación de reglas globales (catálogo declarativo en scripts/lib/reglas-globales-conocidas.json). Refactor propone reemplazo canónico. Hook claudemd-duplicacion-detector.js complementa la capa async. Aplica regla nueva reglas/sin-duplicacion-reglas-globales.md (v1.7.0)."
|
|
12
17
|
---
|
|
13
18
|
|
|
14
19
|
# Habilidad: Tratamiento profesional de CLAUDE.md
|
|
@@ -58,7 +63,7 @@ node scripts/auditar-claudemd.js --json # para parsing
|
|
|
58
63
|
node scripts/auditar-claudemd.js --strict # exit 1 si WARN
|
|
59
64
|
```
|
|
60
65
|
|
|
61
|
-
El auditor verifica
|
|
66
|
+
El auditor verifica ocho dimensiones:
|
|
62
67
|
|
|
63
68
|
| Dimensión | Regla | Severidad |
|
|
64
69
|
|---|---|---|
|
|
@@ -68,6 +73,8 @@ El auditor verifica seis dimensiones:
|
|
|
68
73
|
| Secciones canónicas | Stack, Comandos, Code style, Conventions presentes | WARN |
|
|
69
74
|
| @references | Archivos >80 líneas usan al menos un `@docs/...md` | WARN |
|
|
70
75
|
| Placeholders | `[TBD]`, `[TODO]`, `[COMPLETAR]` | ERROR |
|
|
76
|
+
| Karpathy reference | Project-level >50 LOC menciona "Karpathy", los 4 principios, o `prevencion-sobreingenieria` | WARN |
|
|
77
|
+
| Duplicación reglas globales | Bloque inline parafrasea regla de `~/.claude/rules/` ya cargada globalmente (catálogo en `scripts/lib/reglas-globales-conocidas.json`) | WARN |
|
|
71
78
|
|
|
72
79
|
Veredicto final: ERROR → WARN → OK (el más severo gana).
|
|
73
80
|
|
|
@@ -168,6 +175,9 @@ Genera `./CLAUDE.md` raíz del proyecto detectando stack actual.
|
|
|
168
175
|
2. Ejecutar `detectarStackDetallado(process.cwd())` (de
|
|
169
176
|
`scripts/lib/detectar-stack-detallado.js`).
|
|
170
177
|
3. Generar archivo con secciones pobladas:
|
|
178
|
+
- **Reglas obligatorias**: `@reglas/usar-sistema-swl.md`
|
|
179
|
+
- **Reglas de máxima prioridad** con sub-sección **Cuatro principios de
|
|
180
|
+
implementación (Karpathy)** — bloque exacto definido abajo
|
|
171
181
|
- **Stack**: lenguaje + framework + ORM + package manager detectados
|
|
172
182
|
- **Comandos**: npm scripts detectados o comandos típicos por lenguaje
|
|
173
183
|
- **Code style**: placeholders explícitos (`<!-- pendiente: definir convención de X -->`)
|
|
@@ -177,6 +187,218 @@ Genera `./CLAUDE.md` raíz del proyecto detectando stack actual.
|
|
|
177
187
|
4. Imprimir mensaje con próximo paso: "ejecuta `/swl:claudemd audit`
|
|
178
188
|
para verificar".
|
|
179
189
|
|
|
190
|
+
### Bloque obligatorio Karpathy en init-project
|
|
191
|
+
|
|
192
|
+
Toda generación nueva incluye esta sub-sección literal en "Reglas de
|
|
193
|
+
máxima prioridad":
|
|
194
|
+
|
|
195
|
+
```markdown
|
|
196
|
+
### Cuatro principios de implementación (Karpathy)
|
|
197
|
+
Antes de implementar, refactorizar o corregir bugs: (1) **pensar antes de codificar** (no asumir en silencio), (2) **simplicidad primero** (sin abstracciones especulativas), (3) **cambios quirúrgicos** (leer archivo completo antes de editar, no refactor de oportunidad), (4) **ejecución orientada a metas** (criterios verificables, test que reproduce bugs antes del fix). Detalle + 9 ejemplos MAL→BIEN: `Skill("prevencion-sobreingenieria")` + `recursos/EXAMPLES.md`.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Justificación: el skill `prevencion-sobreingenieria` y su `recursos/EXAMPLES.md`
|
|
201
|
+
se distribuyen con cada instalación SWL, así que la referencia siempre resuelve.
|
|
202
|
+
La sub-sección compacta (5 líneas) ataca los gaps cognitivos más frecuentes
|
|
203
|
+
de cualquier proyecto sin agregar opinión específica.
|
|
204
|
+
|
|
205
|
+
### Check Karpathy en audit
|
|
206
|
+
|
|
207
|
+
`scripts/auditar-claudemd.js` debe verificar (search regex case-insensitive):
|
|
208
|
+
|
|
209
|
+
- `/karpathy/i`
|
|
210
|
+
- `/cuatro principios/i` o `/4 principios/i`
|
|
211
|
+
- `/prevencion-sobreingenieria/`
|
|
212
|
+
- `/Skill\(["'`]prevencion-sobreingenieria["'`]\)/`
|
|
213
|
+
|
|
214
|
+
Si NO encuentra ninguna AND el archivo tiene >50 LOC AND es project-level
|
|
215
|
+
(no `~/.claude/CLAUDE.md`): emitir hallazgo WARN con mensaje:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
CLAUDE.md no menciona los cuatro principios Karpathy ni el skill
|
|
219
|
+
prevencion-sobreingenieria. Considerar agregar la sub-sección compacta
|
|
220
|
+
(ver /swl:claudemd init-project § Bloque obligatorio Karpathy).
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Esta dimensión es WARN, no ERROR — guía sin imponer. CLAUDE.md user-level
|
|
224
|
+
(`~/.claude/`) NO se evalúa contra este check.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Interacción con /swl:aprender (contrato cruzado)
|
|
229
|
+
|
|
230
|
+
`/swl:aprender` y `/swl:claudemd` operan sobre el mismo archivo desde
|
|
231
|
+
ángulos opuestos: aprender **muta**, claudemd **prescribe contrato**.
|
|
232
|
+
Sin coordinación explícita, una sesión de aprender puede romper el
|
|
233
|
+
contrato silenciosamente.
|
|
234
|
+
|
|
235
|
+
### El gap arquitectural que esta interacción cierra
|
|
236
|
+
|
|
237
|
+
`/swl:aprender` Paso 6 Tipo A agrega reglas a `CLAUDE.md` del proyecto.
|
|
238
|
+
El hook `claudemd-bloat-detector.js` (PostToolUse, async, no bloqueante)
|
|
239
|
+
emite nudge tras Write/Edit/MultiEdit sobre CLAUDE.md, pero **el nudge
|
|
240
|
+
llega después** del comando de aprender. Resultado: aprender termina,
|
|
241
|
+
reporta éxito al usuario, y solo después aparece el nudge — con el
|
|
242
|
+
commit ya hecho.
|
|
243
|
+
|
|
244
|
+
Origen detectado: sesión 2026-05-22 al evaluar el flujo SIGAF→swl-ses.
|
|
245
|
+
CLAUDE.md SIGAF recibió ~25 líneas inline de "Triangulación schema
|
|
246
|
+
cross-stack" sin validación post-mutación → archivo probablemente
|
|
247
|
+
superó el umbral de 200 LOC sin warning consciente.
|
|
248
|
+
|
|
249
|
+
### Contrato canónico de interacción
|
|
250
|
+
|
|
251
|
+
Para cualquier comando SWL que escriba a CLAUDE.md del proyecto
|
|
252
|
+
(`/swl:aprender`, `/swl:adoptar-proyecto`, `/swl:nuevo-proyecto`,
|
|
253
|
+
`/swl:claudemd init-project`, futuros comandos similares):
|
|
254
|
+
|
|
255
|
+
1. **Antes de escribir**: opcional, pero recomendado — leer el tamaño
|
|
256
|
+
actual del archivo y estimar el delta.
|
|
257
|
+
2. **Después de escribir**: OBLIGATORIO ejecutar el auditor síncrono:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
node scripts/auditar-claudemd.js --json
|
|
261
|
+
# o fallback:
|
|
262
|
+
npx -y @saulwade/swl-ses@latest audit-claudemd --json
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
3. **Evaluar veredicto**:
|
|
266
|
+
- `OK` → continuar
|
|
267
|
+
- `WARN tamano-total` → proponer condensar o extraer a `@docs/lessons-<tema>.md`
|
|
268
|
+
- `WARN bullet-gigante` → proponer condensar a tabla/lista jerárquica
|
|
269
|
+
- `WARN secciones-canonicas` / `WARN sin-at-references` / `WARN karpathy-reference` → reportar al usuario, permitir continuar
|
|
270
|
+
- `ERROR placeholders` → **DETENER** y revertir
|
|
271
|
+
|
|
272
|
+
4. **Re-ejecutar el auditor** tras la corrección hasta veredicto OK o
|
|
273
|
+
WARN aceptable.
|
|
274
|
+
|
|
275
|
+
### Doble red de seguridad
|
|
276
|
+
|
|
277
|
+
El sistema tiene dos capas de protección complementarias:
|
|
278
|
+
|
|
279
|
+
| Capa | Mecanismo | Cuándo actúa | Bloqueo |
|
|
280
|
+
|------|-----------|--------------|---------|
|
|
281
|
+
| Síncrona | Paso 6.5 de `/swl:aprender` (y equivalente en otros comandos) | Justo después del Write/Edit, antes del reporte final | Bloquea pasos posteriores hasta resolver |
|
|
282
|
+
| Asíncrona | Hook `claudemd-bloat-detector.js` (PostToolUse) | Tras cualquier Write/Edit/MultiEdit a CLAUDE.md (incluso fuera de comandos SWL) | No bloquea — emite nudge a `.planning/evolucion/nudges.jsonl` |
|
|
283
|
+
|
|
284
|
+
La capa síncrona es proactiva (detiene el comando antes de reportar
|
|
285
|
+
éxito). La asíncrona es retroactiva (cubre escrituras desde fuera de
|
|
286
|
+
comandos SWL, ej. edición manual).
|
|
287
|
+
|
|
288
|
+
### Por qué NO se duplica al hook
|
|
289
|
+
|
|
290
|
+
El hook async existente cubre el caso de escrituras desde **cualquier
|
|
291
|
+
fuente** (edición manual del usuario, otros comandos, scripts externos).
|
|
292
|
+
Es la red de seguridad universal. La validación síncrona en aprender es
|
|
293
|
+
específica para el flujo del comando — donde el agente puede actuar
|
|
294
|
+
sobre el WARN antes de continuar.
|
|
295
|
+
|
|
296
|
+
NO crear un hook nuevo dedicado a aprender — duplicaría el rol del
|
|
297
|
+
existente. La invocación síncrona en el Paso 6.5 es suficiente.
|
|
298
|
+
|
|
299
|
+
### Reglas duras
|
|
300
|
+
|
|
301
|
+
- `/swl:aprender` Paso 6.5 es OBLIGATORIO si se aplicó Tipo A.
|
|
302
|
+
- `/swl:adoptar-proyecto` Paso 8 debe ejecutar el auditor tras
|
|
303
|
+
generar/modificar CLAUDE.md.
|
|
304
|
+
- `/swl:nuevo-proyecto` Paso 6 debe ejecutar el auditor tras generar
|
|
305
|
+
CLAUDE.md inicial.
|
|
306
|
+
- `/swl:claudemd init-project` debe ejecutar el auditor sobre el
|
|
307
|
+
archivo recién generado.
|
|
308
|
+
|
|
309
|
+
Cualquier comando futuro que escriba a CLAUDE.md DEBE seguir este
|
|
310
|
+
contrato.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Detección de duplicación de reglas globales (dimensión 8)
|
|
317
|
+
|
|
318
|
+
### Por qué existe
|
|
319
|
+
|
|
320
|
+
Las reglas en `~/.claude/rules/` se cargan automáticamente en cada
|
|
321
|
+
sesión SWL. Si un proyecto duplica inline el contenido de esas reglas
|
|
322
|
+
en su `CLAUDE.md`, ocurren tres patologías:
|
|
323
|
+
|
|
324
|
+
1. **Bloat acumulativo**: 7-15 líneas por regla duplicada × N proyectos
|
|
325
|
+
= inflación silenciosa que rompe el umbral de 200 LOC.
|
|
326
|
+
2. **Drift al actualizar la global**: si la regla global cambia, las
|
|
327
|
+
copias en cada CLAUDE.md quedan obsoletas sin alerta.
|
|
328
|
+
3. **Erosión del contrato canónico**: cada CLAUDE.md re-deriva el
|
|
329
|
+
principio con palabras propias, generando 4 paráfrasis distintas
|
|
330
|
+
del mismo contenido.
|
|
331
|
+
|
|
332
|
+
La regla `reglas/sin-duplicacion-reglas-globales.md` (v1.7.0) prohíbe
|
|
333
|
+
esta duplicación. Esta dimensión del auditor la detecta.
|
|
334
|
+
|
|
335
|
+
### Catálogo declarativo
|
|
336
|
+
|
|
337
|
+
`scripts/lib/reglas-globales-conocidas.json` lista las reglas globales
|
|
338
|
+
conocidas con sus patrones de detección. Estructura por entrada:
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"id": "idioma-espanol-mexico",
|
|
343
|
+
"regla_global": "brevedad-output.md",
|
|
344
|
+
"seccion_canonica": "Idioma obligatorio: español de México",
|
|
345
|
+
"referencia_canonica": "@~/.claude/rules/brevedad-output.md § Idioma obligatorio",
|
|
346
|
+
"patrones": ["\\bespañol\\s+de\\s+M[ée]xico\\b", "..."],
|
|
347
|
+
"min_matches": 2,
|
|
348
|
+
"remediacion_sugerida": "Eliminar el bloque local..."
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Reglas incluidas en v1.7.0:
|
|
353
|
+
|
|
354
|
+
- `idioma-espanol-mexico` (brevedad-output.md § Idioma)
|
|
355
|
+
- `brevedad-sin-aiisms` (brevedad-output.md § Brevedad)
|
|
356
|
+
- `git-sin-coautores` (git-coauthor.md)
|
|
357
|
+
- `arreglar-al-detectar` (arreglar-al-detectar.md)
|
|
358
|
+
- `debatir-antes-de-aceptar` (debatir-antes-de-aceptar.md)
|
|
359
|
+
- `usar-context7` (usar-context7.md)
|
|
360
|
+
|
|
361
|
+
### Cuándo NO se evalúa
|
|
362
|
+
|
|
363
|
+
- **User-level** (`~/.claude/CLAUDE.md`): ahí sí pueden declararse
|
|
364
|
+
preferencias personales que parafrasean reglas globales. La opción
|
|
365
|
+
`esUserLevel: true` skipea la evaluación.
|
|
366
|
+
- **Archivos < 20 LOC**: CLAUDE.md mínimos no acumulan duplicaciones
|
|
367
|
+
con suficiente densidad para evaluar.
|
|
368
|
+
|
|
369
|
+
### Capa async — hook
|
|
370
|
+
|
|
371
|
+
`hooks/claudemd-duplicacion-detector.js` (PostToolUse, no bloquea)
|
|
372
|
+
ejecuta el detector tras cualquier Write/Edit a CLAUDE.md y emite
|
|
373
|
+
nudge a `.planning/evolucion/nudges.jsonl` con `kind:
|
|
374
|
+
claudemd-duplicacion-reglas`. Opt-out: `SWL_CLAUDEMD_DUPLICACION=0`.
|
|
375
|
+
|
|
376
|
+
### Cómo refactorizar duplicaciones detectadas
|
|
377
|
+
|
|
378
|
+
Tres opciones para cada hallazgo:
|
|
379
|
+
|
|
380
|
+
1. **Eliminar** (default recomendado): el bloque solo repite la regla
|
|
381
|
+
global, eliminarlo no pierde información.
|
|
382
|
+
2. **Convertir a matiz local** (≤3 líneas): si el bloque agrega valor
|
|
383
|
+
local genuino, reescribirlo corto referenciando la regla global:
|
|
384
|
+
```markdown
|
|
385
|
+
Convenciones locales: identificadores técnicos en inglés (rutas,
|
|
386
|
+
comandos). Ver @~/.claude/rules/brevedad-output.md.
|
|
387
|
+
```
|
|
388
|
+
3. **Documentar override explícito** (raro): si el proyecto contradice
|
|
389
|
+
la regla global por requerimiento documentable:
|
|
390
|
+
```markdown
|
|
391
|
+
Override de @~/.claude/rules/brevedad-output.md § Brevedad: este
|
|
392
|
+
proyecto exige docstrings extendidos por compliance regulatorio.
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Anti-patrón: "lo dejo por claridad"
|
|
396
|
+
|
|
397
|
+
La regla global YA es visible — se carga automáticamente. Duplicarla
|
|
398
|
+
no aumenta su prioridad ni su claridad; solo crea deuda. Si el WARN
|
|
399
|
+
del auditor incomoda, la respuesta correcta es **resolver la
|
|
400
|
+
duplicación**, no aceptar el WARN.
|
|
401
|
+
|
|
180
402
|
---
|
|
181
403
|
|
|
182
404
|
## Gotchas / Errores comunes no obvios
|
|
@@ -0,0 +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', 'evolucion', '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);
|
|
@@ -356,6 +356,15 @@
|
|
|
356
356
|
"maxConsecutiveFailures": 5,
|
|
357
357
|
"degradeOnFailure": "skip"
|
|
358
358
|
},
|
|
359
|
+
"claudemd-duplicacion-detector.js": {
|
|
360
|
+
"event": "PostToolUse",
|
|
361
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
362
|
+
"description": "Detecta duplicación de reglas globales (~/.claude/rules/) inline en CLAUDE.md de proyecto. Consume scripts/lib/detector-reglas-duplicadas.js + catálogo scripts/lib/reglas-globales-conocidas.json. Aplica regla reglas/sin-duplicacion-reglas-globales.md. Emite nudge a .planning/evolucion/nudges.jsonl si hay duplicaciones detectadas. No bloquea — kind: claudemd-duplicacion-reglas. Excluye temp/, node_modules/, respositorios-git/, .planning/, ~/.claude/ (user-level). Opt-out: SWL_CLAUDEMD_DUPLICACION=0.",
|
|
363
|
+
"blocking": false,
|
|
364
|
+
"async": true,
|
|
365
|
+
"maxConsecutiveFailures": 5,
|
|
366
|
+
"degradeOnFailure": "skip"
|
|
367
|
+
},
|
|
359
368
|
"validar-intent-spec.js": {
|
|
360
369
|
"event": "PostToolUse",
|
|
361
370
|
"matcher": "Write|Edit|MultiEdit",
|
package/manifiestos/modulos.json
CHANGED
|
@@ -905,7 +905,9 @@
|
|
|
905
905
|
"reglas/registro-componentes-nuevos.md",
|
|
906
906
|
"reglas/auditorias-documentales-estructurales.md",
|
|
907
907
|
"reglas/intent-engineering.md",
|
|
908
|
-
"reglas/tests-cleanup.md"
|
|
908
|
+
"reglas/tests-cleanup.md",
|
|
909
|
+
"reglas/verificar-citas-temporales.md",
|
|
910
|
+
"reglas/sin-duplicacion-reglas-globales.md"
|
|
909
911
|
],
|
|
910
912
|
"targets": [
|
|
911
913
|
"claude",
|
|
@@ -1024,6 +1026,7 @@
|
|
|
1024
1026
|
"hooks/inbox-aviso.js",
|
|
1025
1027
|
"hooks/aiisms-detector.js",
|
|
1026
1028
|
"hooks/claudemd-bloat-detector.js",
|
|
1029
|
+
"hooks/claudemd-duplicacion-detector.js",
|
|
1027
1030
|
"hooks/validar-intent-spec.js",
|
|
1028
1031
|
"hooks/sugerir-regenerar-inventario.js",
|
|
1029
1032
|
"hooks/sugerir-contribuir.js",
|
|
@@ -1219,6 +1222,8 @@
|
|
|
1219
1222
|
"tipo": "scripts",
|
|
1220
1223
|
"archivos": [
|
|
1221
1224
|
"scripts/auditar-claudemd.js",
|
|
1225
|
+
"scripts/lib/detector-reglas-duplicadas.js",
|
|
1226
|
+
"scripts/lib/reglas-globales-conocidas.json",
|
|
1222
1227
|
"docs/variables-entorno.md"
|
|
1223
1228
|
],
|
|
1224
1229
|
"targets": [
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"lockfileVersion": 1,
|
|
3
|
-
"generatedAt": "2026-05-
|
|
3
|
+
"generatedAt": "2026-05-22T23:28:47.439Z",
|
|
4
4
|
"skillsCount": 177,
|
|
5
|
-
"lockHash": "sha256:
|
|
5
|
+
"lockHash": "sha256:d0306679d924ecbbd0071f532df9f2cfb499817eb42b0bb5e6e5ebcdacb941f2",
|
|
6
6
|
"skills": [
|
|
7
7
|
{
|
|
8
8
|
"nombre": "accesibilidad-a11y",
|
|
@@ -889,9 +889,9 @@
|
|
|
889
889
|
{
|
|
890
890
|
"nombre": "prevencion-sobreingenieria",
|
|
891
891
|
"path": "habilidades/prevencion-sobreingenieria/SKILL.md",
|
|
892
|
-
"hash": "sha256:
|
|
893
|
-
"bytes":
|
|
894
|
-
"version": "\"1.
|
|
892
|
+
"hash": "sha256:9f6d0d4f5c54e29aff3e5fa66d89c42a0dabfd0a59eefcfbf6715ca085984f3e",
|
|
893
|
+
"bytes": 16812,
|
|
894
|
+
"version": "\"1.2.0\""
|
|
895
895
|
},
|
|
896
896
|
{
|
|
897
897
|
"nombre": "privacy-memoria",
|
|
@@ -1099,9 +1099,9 @@
|
|
|
1099
1099
|
{
|
|
1100
1100
|
"nombre": "swl-claudemd",
|
|
1101
1101
|
"path": "habilidades/swl-claudemd/SKILL.md",
|
|
1102
|
-
"hash": "sha256:
|
|
1103
|
-
"bytes":
|
|
1104
|
-
"version": "\"1.0
|
|
1102
|
+
"hash": "sha256:0f5eed29f3fedb3bca8036ae5680901712520b31190ad416700c732b2b5897e4",
|
|
1103
|
+
"bytes": 21518,
|
|
1104
|
+
"version": "\"1.2.0\""
|
|
1105
1105
|
},
|
|
1106
1106
|
{
|
|
1107
1107
|
"nombre": "swl-dashboard",
|