@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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Regla: Sin duplicación de reglas globales en CLAUDE.md de proyecto
|
|
2
|
+
|
|
3
|
+
Esta regla es **OBLIGATORIA** y aplica al diseño/auditoría de cualquier
|
|
4
|
+
`CLAUDE.md` de proyecto donde el sistema SWL esté instalado. Extiende el
|
|
5
|
+
contrato canónico de `swl-claudemd` con un sub-caso específico: la
|
|
6
|
+
duplicación inline de reglas que ya viven en `~/.claude/rules/`.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Principio
|
|
11
|
+
|
|
12
|
+
> Cuando una regla ya está documentada en `~/.claude/rules/` (carga global
|
|
13
|
+
> automática en todas las sesiones del usuario), **el `CLAUDE.md` del
|
|
14
|
+
> proyecto NO debe duplicarla inline**. La duplicación viola DRY a nivel
|
|
15
|
+
> de gobernanza, genera drift cuando la regla global se actualiza, y
|
|
16
|
+
> erosiona el contrato canónico de `CLAUDE.md` (≤200 LOC).
|
|
17
|
+
|
|
18
|
+
La regla global es **la fuente de verdad**. El `CLAUDE.md` del proyecto
|
|
19
|
+
puede referenciarla con `@~/.claude/rules/<archivo>.md` o agregar
|
|
20
|
+
**matices propios del proyecto**, pero NUNCA re-derivar el principio.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Cuándo aplicar
|
|
25
|
+
|
|
26
|
+
OBLIGATORIO al:
|
|
27
|
+
|
|
28
|
+
- Crear un `CLAUDE.md` de proyecto nuevo (`/swl:claudemd init-project`,
|
|
29
|
+
`/swl:nuevo-proyecto`, `/swl:adoptar-proyecto`).
|
|
30
|
+
- Modificar `CLAUDE.md` de proyecto inline (`/swl:aprender` Paso 6 Tipo A,
|
|
31
|
+
edición manual del usuario, `Edit`/`Write` programático).
|
|
32
|
+
- Revisar `CLAUDE.md` (`/swl:claudemd audit`, `/swl:claudemd refactor`,
|
|
33
|
+
hook `claudemd-bloat-detector`, hook `claudemd-duplicacion-detector`).
|
|
34
|
+
|
|
35
|
+
NO aplica al:
|
|
36
|
+
|
|
37
|
+
- `~/.claude/CLAUDE.md` (user-level) — ahí SÍ pueden declararse
|
|
38
|
+
preferencias personales que parafrasean o complementan reglas globales.
|
|
39
|
+
- Documentación dentro del proyecto que no es `CLAUDE.md` (READMEs,
|
|
40
|
+
ADRs, docs de feature) — ahí no aplica el contrato canónico.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Catálogo de reglas globales conocidas
|
|
45
|
+
|
|
46
|
+
Las siguientes reglas globales se cargan automáticamente cada sesión y
|
|
47
|
+
NO deben duplicarse en `CLAUDE.md` de proyecto:
|
|
48
|
+
|
|
49
|
+
| Regla global | Sección canónica | Referencia para citar |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `brevedad-output.md` | Idioma obligatorio: español de México | `@~/.claude/rules/brevedad-output.md § Idioma obligatorio` |
|
|
52
|
+
| `brevedad-output.md` | Brevedad y eficiencia de output | `@~/.claude/rules/brevedad-output.md § Brevedad` |
|
|
53
|
+
| `git-coauthor.md` | Sin co-autores en commits | `@~/.claude/rules/git-coauthor.md` |
|
|
54
|
+
| `arreglar-al-detectar.md` | Detectar → Informar → Arreglar | `@~/.claude/rules/arreglar-al-detectar.md` |
|
|
55
|
+
| `debatir-antes-de-aceptar.md` | Debatir decisiones que chocan con reglas | `@~/.claude/rules/debatir-antes-de-aceptar.md` |
|
|
56
|
+
| `usar-context7.md` | Consultar Context7 antes de dependencias | `@~/.claude/rules/usar-context7.md` |
|
|
57
|
+
|
|
58
|
+
Catálogo declarativo completo:
|
|
59
|
+
`scripts/lib/reglas-globales-conocidas.json`. El catálogo es la fuente
|
|
60
|
+
de verdad consumida por el auditor (`scripts/auditar-claudemd.js`) y el
|
|
61
|
+
hook (`hooks/claudemd-duplicacion-detector.js`).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Qué SÍ es legítimo en CLAUDE.md de proyecto
|
|
66
|
+
|
|
67
|
+
Lo siguiente NO se considera duplicación y es bienvenido:
|
|
68
|
+
|
|
69
|
+
1. **Matiz local específico**: "Convenciones locales: identificadores
|
|
70
|
+
técnicos en inglés (rutas, comandos), prosa en español." — el matiz
|
|
71
|
+
es del proyecto, no la regla del idioma.
|
|
72
|
+
2. **Override explícito documentado**: "Override de
|
|
73
|
+
`~/.claude/rules/brevedad-output.md` § Brevedad: este proyecto usa
|
|
74
|
+
docstrings extendidos por requerimiento legal." — el override es
|
|
75
|
+
explícito y nombra la regla global.
|
|
76
|
+
3. **Excepción acotada con justificación**: "Excepción a
|
|
77
|
+
`~/.claude/rules/arreglar-al-detectar.md`: bugs cosméticos en el
|
|
78
|
+
módulo legacy `X/` se difieren a Q3 por congelación." — la excepción
|
|
79
|
+
nombra la regla y justifica.
|
|
80
|
+
4. **Referencia `@`**: `@~/.claude/rules/<archivo>.md` — incluir la
|
|
81
|
+
regla por referencia, no inline.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Cómo detectarlo
|
|
86
|
+
|
|
87
|
+
### Manual
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
node scripts/auditar-claudemd.js
|
|
91
|
+
# Buscar en el output: "duplicacion-reglas-globales"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Automático
|
|
95
|
+
|
|
96
|
+
- **Auditor síncrono**: `scripts/auditar-claudemd.js` (dimensión 7,
|
|
97
|
+
severidad WARN, no bloquea).
|
|
98
|
+
- **Hook async**: `hooks/claudemd-duplicacion-detector.js` (PostToolUse,
|
|
99
|
+
emite nudge a `.planning/evolucion/nudges.jsonl`).
|
|
100
|
+
- **Comando**: `/swl:claudemd audit` reporta el conteo; `/swl:claudemd
|
|
101
|
+
refactor` propone el reemplazo concreto.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Cómo remediar
|
|
106
|
+
|
|
107
|
+
1. **Identificar el bloque** señalado por el auditor (línea aproximada).
|
|
108
|
+
2. **Decidir**:
|
|
109
|
+
- ¿El bloque es 100% paráfrasis de la regla global? → **eliminar**.
|
|
110
|
+
- ¿El bloque agrega matiz local? → **reescribir como matiz corto**
|
|
111
|
+
(≤3 líneas) que nombra la regla global ("Convenciones locales:
|
|
112
|
+
<matiz>. Ver `@~/.claude/rules/<archivo>.md`.").
|
|
113
|
+
3. **Verificar**: re-ejecutar `node scripts/auditar-claudemd.js` hasta
|
|
114
|
+
que la duplicación desaparezca.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Anti-patrones
|
|
119
|
+
|
|
120
|
+
- **Copiar contenido de reglas globales "para que esté visible"**: la
|
|
121
|
+
regla global YA está visible — se carga automáticamente. Copiarla en
|
|
122
|
+
cada proyecto es deuda silenciosa.
|
|
123
|
+
- **Re-derivar el principio con palabras propias**: si dices "todo
|
|
124
|
+
contenido generado debe ser en español de México con acentos
|
|
125
|
+
correctos…" estás re-derivando `brevedad-output.md § Idioma`.
|
|
126
|
+
- **Bloque "Reglas de máxima prioridad" que repite reglas globales**:
|
|
127
|
+
las reglas globales SON prioritarias por construcción — duplicarlas
|
|
128
|
+
no aumenta su prioridad.
|
|
129
|
+
- **Idioma "Language" en inglés que dice "español"**: paradójico y
|
|
130
|
+
duplica la regla.
|
|
131
|
+
- **Override implícito sin nombrar la regla global**: "este proyecto
|
|
132
|
+
usa docstrings extendidos" sin decir que es override de qué.
|
|
133
|
+
- **Ignorar el WARN del auditor argumentando "es para claridad"**: el
|
|
134
|
+
auditor lleva razón. La regla global es suficiente claridad.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Relación con otras reglas
|
|
139
|
+
|
|
140
|
+
- `~/.claude/rules/memoria-consolidada.md § Reglas de no-duplicación`:
|
|
141
|
+
principio general "un dato vive en exactamente un canal". Esta regla
|
|
142
|
+
lo aplica al canal CLAUDE.md.
|
|
143
|
+
- `~/.claude/rules/fragmentos-compartidos.md`: la analogía a nivel de
|
|
144
|
+
agentes (fragmentos `_*.md` se incrustan, no se duplican).
|
|
145
|
+
- `~/.claude/rules/skills-estandar.md`: la analogía a nivel de skills
|
|
146
|
+
(skill referencia otro skill, no duplica su contenido).
|
|
147
|
+
- `reglas/auditorias-documentales-estructurales.md § Para prosa
|
|
148
|
+
cuantificada en campos descriptivos`: hermana — verifica drift
|
|
149
|
+
cross-manifest. Esta regla verifica drift cross-fuente.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Origen de esta regla
|
|
154
|
+
|
|
155
|
+
Sesión 2026-05-22, swl-ses v1.7.0. El usuario pegó 4 fragmentos de
|
|
156
|
+
`CLAUDE.md` de distintos proyectos que repetían la misma regla de idioma
|
|
157
|
+
"español de México" en formas distintas (lista con bullets, H3 inline,
|
|
158
|
+
lista numerada, sección "Language" en inglés). El análisis reveló:
|
|
159
|
+
|
|
160
|
+
- La regla ya vivía en `~/.claude/rules/brevedad-output.md § Idioma
|
|
161
|
+
obligatorio: español de México` desde sesiones previas.
|
|
162
|
+
- Cada proyecto la había re-derivado por iteraciones de
|
|
163
|
+
`/swl:aprender` Tipo A sin que ningún check detectara la
|
|
164
|
+
duplicación.
|
|
165
|
+
- El bloat acumulado en cada `CLAUDE.md` consumía 7-15 líneas que
|
|
166
|
+
podían eliminarse sin perder semántica.
|
|
167
|
+
|
|
168
|
+
Resolución: crear el catálogo declarativo + detector + dimensión 7 del
|
|
169
|
+
auditor + hook PostToolUse + esta regla. Release v1.7.0 MINOR.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Checklist al modificar CLAUDE.md de proyecto
|
|
174
|
+
|
|
175
|
+
- [ ] ¿El bloque que voy a agregar parafrasea una regla global de
|
|
176
|
+
`~/.claude/rules/`?
|
|
177
|
+
- [ ] Si sí: ¿agrega matiz local genuino, o solo repite el principio?
|
|
178
|
+
- [ ] Si solo repite: NO agregar — la regla global ya aplica.
|
|
179
|
+
- [ ] Si agrega matiz: redactar en ≤3 líneas que **nombran la regla
|
|
180
|
+
global** (referencia con `@~/.claude/rules/<archivo>.md`).
|
|
181
|
+
- [ ] Tras escribir: ejecutar `node scripts/auditar-claudemd.js` y
|
|
182
|
+
confirmar 0 hallazgos `duplicacion-reglas-globales`.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Regla: Comentarios temporales en código NO son verificación
|
|
2
|
+
|
|
3
|
+
Esta regla es OBLIGATORIA para todo trabajo de Claude en swl-ses. Extiende la
|
|
4
|
+
regla global `~/.claude/rules/verificar-citas-normativas.md § Familia 2` con
|
|
5
|
+
el sub-caso específico de **comentarios temporales como `// Alineado con
|
|
6
|
+
commits X+Y` o `// Refactorizado a vN.M.0`**.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Principio
|
|
11
|
+
|
|
12
|
+
> Un comentario en código que afirma alineación con otro commit, versión o
|
|
13
|
+
> contrato (`// Alineado con commits X+Y`, `// Refactorizado a vN.M.0`,
|
|
14
|
+
> `# Sincronizado con backend`, `<!-- Coherente con schema X -->`) **NO es
|
|
15
|
+
> verificación** — es afirmación temporal sin prueba. Antes de actuar
|
|
16
|
+
> sobre el comentario como si fuera evidencia, verificar contra la fuente
|
|
17
|
+
> real con `grep` / `Read` / `git log`.
|
|
18
|
+
|
|
19
|
+
El comentario puede haberse desactualizado silenciosamente si el commit
|
|
20
|
+
referenciado se revirtió, nunca se aplicó, o si el refactor citado cambió
|
|
21
|
+
después de escribir el comentario.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Cuándo aplicar
|
|
26
|
+
|
|
27
|
+
OBLIGATORIO verificar antes de aceptar como evidencia accionable:
|
|
28
|
+
|
|
29
|
+
- Comentarios `// Alineado con commits <SHA>+<SHA>` en código
|
|
30
|
+
- Comentarios `// Refactorizado a v<N.M.0>` o `// Migrado a <framework>`
|
|
31
|
+
- Comentarios `# Sincronizado con backend` o `# Espejo del schema X`
|
|
32
|
+
- Comentarios `<!-- Tipos coherentes con API vN -->`
|
|
33
|
+
- Cualquier afirmación de cross-stack alignment en comentario inline
|
|
34
|
+
|
|
35
|
+
NO aplica cuando:
|
|
36
|
+
|
|
37
|
+
- El comentario describe la INTENCIÓN del código (no afirma alineación)
|
|
38
|
+
- El comentario es una nota pendiente (`TODO` / `FIXME`) con ticket asociado
|
|
39
|
+
- El comentario es una explicación del POR QUÉ (no del QUÉ histórico)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Cómo verificar
|
|
44
|
+
|
|
45
|
+
Cuando un comentario afirma "alineado con X":
|
|
46
|
+
|
|
47
|
+
1. Identificar **qué se afirma alineado**: archivo, commit, versión, schema.
|
|
48
|
+
2. Localizar la fuente real:
|
|
49
|
+
```bash
|
|
50
|
+
# Si afirma alineación con commit:
|
|
51
|
+
git show <SHA> -- <archivo-citado>
|
|
52
|
+
|
|
53
|
+
# Si afirma alineación con schema/types:
|
|
54
|
+
grep -rn "<campo-clave>" backend/ frontend/ schemas/
|
|
55
|
+
```
|
|
56
|
+
3. Comparar campo por campo. Si hay drift, NO confiar en el comentario.
|
|
57
|
+
4. Actualizar el comentario o eliminarlo (un comentario stale es peor que
|
|
58
|
+
ningún comentario).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Caso de origen — sesión SIGAF 2026-05-22
|
|
63
|
+
|
|
64
|
+
`frontend/.../contratos/models/contrato.models.ts` línea 2 decía:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Alineados con el backend refactorizado (commits 284df74 + 5869ac9)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Pero el backend nunca aplicó ese refactor. El frontend enviaba 5 campos
|
|
71
|
+
(`monto_total`, `fecha_inicio`, `fecha_termino`, `porcentaje_iva`,
|
|
72
|
+
`tipo_contrato_id`) que Pydantic con `extra=ignore` descartaba
|
|
73
|
+
silenciosamente. Los contratos se creaban sin monto ni vigencia durante
|
|
74
|
+
semanas, hasta que se detectó por triangulación cross-stack (modelo
|
|
75
|
+
SQLAlchemy + schema Pydantic + types TypeScript).
|
|
76
|
+
|
|
77
|
+
El comentario funcionó como **falso anclaje de confianza**: un reviewer
|
|
78
|
+
posterior lo leyó, asumió validez, y no verificó. La causa raíz era el
|
|
79
|
+
comentario stale, no el código.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Anti-patrones específicos
|
|
84
|
+
|
|
85
|
+
- **Confiar en el comentario en code review**: el reviewer ve
|
|
86
|
+
`// Alineado con commits X+Y` y asume válido sin hacer `git show`.
|
|
87
|
+
- **Propagar el comentario en refactors**: al copiar código, el comentario
|
|
88
|
+
viaja con él y eventualmente se vuelve falso sin que nadie lo note.
|
|
89
|
+
- **No actualizar el comentario al revertir el commit citado**: el `git
|
|
90
|
+
revert` no borra comentarios — el comentario sobrevive afirmando
|
|
91
|
+
alineación con un commit que ya no existe en la línea de desarrollo.
|
|
92
|
+
- **Usar el comentario como "prueba" en debates técnicos**: "ya está
|
|
93
|
+
alineado con X" cuando el comentario es la única evidencia es razonar
|
|
94
|
+
desde una afirmación no verificada.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Alternativas seguras a los comentarios temporales
|
|
99
|
+
|
|
100
|
+
Cuando se necesita marcar alineación cross-stack:
|
|
101
|
+
|
|
102
|
+
| En vez de | Usar |
|
|
103
|
+
|-----------|------|
|
|
104
|
+
| `// Alineado con commits X+Y` | Test de contrato que valide el shape real en CI |
|
|
105
|
+
| `// Refactorizado a v2.3.0` | Versión semántica en el archivo + lock |
|
|
106
|
+
| `# Sincronizado con backend` | Tipos generados (OpenAPI codegen, Prisma) |
|
|
107
|
+
| `<!-- Coherente con schema X -->` | Validador automatizado en pre-commit |
|
|
108
|
+
|
|
109
|
+
Si el comentario es la única opción (no hay test/codegen disponible),
|
|
110
|
+
incluir **fecha y SHA** explícitos para que la revisión periódica detecte
|
|
111
|
+
drift:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Alineado con backend commit 284df74 al 2026-05-22.
|
|
115
|
+
// Re-verificar si el endpoint POST /contratos cambia su contrato.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Relación con otras reglas
|
|
121
|
+
|
|
122
|
+
- `~/.claude/rules/verificar-citas-normativas.md § Familia 2` — regla
|
|
123
|
+
global hermana. Esta regla extiende el principio al sub-caso "comentarios
|
|
124
|
+
temporales en código".
|
|
125
|
+
- `reglas/arreglar-al-detectar.md` — un comentario stale detectado es
|
|
126
|
+
trabajo a resolver en mismo turno (actualizarlo o eliminarlo).
|
|
127
|
+
- `~/.claude/rules/usar-sistema-swl.md` — invocar `revisor-codigo-swl`
|
|
128
|
+
cuando se sospecha drift acumulado de comentarios temporales en un
|
|
129
|
+
módulo.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Checklist al detectar un comentario temporal
|
|
134
|
+
|
|
135
|
+
- [ ] ¿El comentario afirma alineación con algo verificable (commit, schema, versión)?
|
|
136
|
+
- [ ] ¿Verifiqué la fuente real con `grep`/`Read`/`git show` ANTES de actuar?
|
|
137
|
+
- [ ] Si hay drift detectado, ¿actualicé/eliminé el comentario en el mismo commit?
|
|
138
|
+
- [ ] Si voy a propagar el código (copy-paste, refactor), ¿el comentario sigue siendo válido?
|
|
139
|
+
- [ ] Si el comentario es la única opción, ¿incluye fecha y SHA explícitos?
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - Presencia de secciones canónicas (Stack, Comandos, Code style, Conventions, @references)
|
|
14
14
|
* - Uso de @references (al menos 1 si el archivo supera 80 líneas)
|
|
15
15
|
* - Placeholders sin reemplazar ([TBD], [TODO], [COMPLETAR])
|
|
16
|
+
* - Referencia Karpathy (CLAUDE.md project-level >50 LOC menciona los 4 principios o el skill)
|
|
16
17
|
*
|
|
17
18
|
* Uso:
|
|
18
19
|
* node scripts/auditar-claudemd.js [ruta] # Audita CLAUDE.md (default: ./)
|
|
@@ -26,11 +27,24 @@
|
|
|
26
27
|
|
|
27
28
|
const fs = require('fs');
|
|
28
29
|
const path = require('path');
|
|
30
|
+
const os = require('os');
|
|
31
|
+
|
|
32
|
+
// Lazy: el detector vive en scripts/lib/ y puede no estar presente en
|
|
33
|
+
// destinos legacy. Si falta, la auditoría sigue con las 7 dimensiones
|
|
34
|
+
// originales sin la dimensión 8.
|
|
35
|
+
function cargarDetectorReglasDuplicadas() {
|
|
36
|
+
try {
|
|
37
|
+
return require('./lib/detector-reglas-duplicadas').detectarDuplicaciones;
|
|
38
|
+
} catch (_) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
29
42
|
|
|
30
43
|
// ─── Config ───────────────────────────────────────────────────────────────
|
|
31
44
|
const MAX_LINES = parseInt(process.env.SWL_CLAUDEMD_MAX_LINES, 10) || 200;
|
|
32
45
|
const MAX_BULLET_CHARS =
|
|
33
46
|
parseInt(process.env.SWL_CLAUDEMD_MAX_BULLET_CHARS, 10) || 1000;
|
|
47
|
+
const KARPATHY_MIN_LINES = 50;
|
|
34
48
|
|
|
35
49
|
const SECCIONES_CANONICAS = [
|
|
36
50
|
{ nombre: 'Stack', regex: /^##\s+Stack/m },
|
|
@@ -39,6 +53,15 @@ const SECCIONES_CANONICAS = [
|
|
|
39
53
|
{ nombre: 'Conventions', regex: /^##\s+(Conventions|Convenciones)/im },
|
|
40
54
|
];
|
|
41
55
|
|
|
56
|
+
// Patrones que reconocen que el CLAUDE.md incorpora los 4 principios Karpathy
|
|
57
|
+
// o referencia explícita al skill prevencion-sobreingenieria.
|
|
58
|
+
const KARPATHY_PATTERNS = [
|
|
59
|
+
/karpathy/i,
|
|
60
|
+
/cuatro\s+principios/i,
|
|
61
|
+
/4\s+principios/i,
|
|
62
|
+
/prevencion-sobreingenieria/i,
|
|
63
|
+
];
|
|
64
|
+
|
|
42
65
|
const PLACEHOLDERS = /\[(TBD|TODO|COMPLETAR|PENDIENTE|XXX|FIXME)\]/g;
|
|
43
66
|
|
|
44
67
|
// ─── Auditoría ────────────────────────────────────────────────────────────
|
|
@@ -129,6 +152,42 @@ function auditar(rutaClaudeMd) {
|
|
|
129
152
|
});
|
|
130
153
|
}
|
|
131
154
|
|
|
155
|
+
// 6. Referencia a los 4 principios Karpathy o al skill prevencion-sobreingenieria.
|
|
156
|
+
// Solo aplica a CLAUDE.md project-level con tamaño no trivial.
|
|
157
|
+
const tieneReferenciaKarpathy = detectarReferenciaKarpathy(contenido);
|
|
158
|
+
const esProjectLevel = !esRutaUserLevel(rutaClaudeMd);
|
|
159
|
+
if (!tieneReferenciaKarpathy && lineas.length > KARPATHY_MIN_LINES && esProjectLevel) {
|
|
160
|
+
hallazgos.push({
|
|
161
|
+
severidad: 'WARN',
|
|
162
|
+
regla: 'karpathy-reference',
|
|
163
|
+
mensaje: 'CLAUDE.md no menciona los cuatro principios Karpathy ni el skill prevencion-sobreingenieria',
|
|
164
|
+
sugerencia: 'Agregar la sub-sección compacta (ver `/swl:claudemd init-project` § Bloque obligatorio Karpathy)',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 7. Duplicación de reglas globales (~/.claude/rules/) en CLAUDE.md de proyecto.
|
|
169
|
+
// Solo aplica a project-level (user-level puede declarar preferencias).
|
|
170
|
+
let resultadoDuplicaciones = { evaluado: false, duplicaciones: [], total_reglas_evaluadas: 0 };
|
|
171
|
+
const detectarDuplicaciones = cargarDetectorReglasDuplicadas();
|
|
172
|
+
if (detectarDuplicaciones) {
|
|
173
|
+
try {
|
|
174
|
+
resultadoDuplicaciones = detectarDuplicaciones(contenido, null, {
|
|
175
|
+
esUserLevel: !esProjectLevel,
|
|
176
|
+
});
|
|
177
|
+
for (const dup of resultadoDuplicaciones.duplicaciones) {
|
|
178
|
+
hallazgos.push({
|
|
179
|
+
severidad: dup.severidad || 'WARN',
|
|
180
|
+
regla: 'duplicacion-reglas-globales',
|
|
181
|
+
mensaje: `Bloque duplica regla global \`~/.claude/rules/${dup.regla_global}\` (línea ~${dup.linea_aproximada})`,
|
|
182
|
+
sugerencia: dup.remediacion,
|
|
183
|
+
referencia_canonica: dup.referencia_canonica,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
// Si el catálogo está corrupto o falla, no bloqueamos la auditoría.
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
132
191
|
// ─── Veredicto ───────────────────────────────────────────────────────────
|
|
133
192
|
const tieneError = hallazgos.some(h => h.severidad === 'ERROR');
|
|
134
193
|
const tieneWarn = hallazgos.some(h => h.severidad === 'WARN');
|
|
@@ -145,11 +204,39 @@ function auditar(rutaClaudeMd) {
|
|
|
145
204
|
secciones_presentes: SECCIONES_CANONICAS.filter(s => s.regex.test(contenido)).map(s => s.nombre),
|
|
146
205
|
secciones_ausentes: seccionesAusentes.map(s => s.nombre),
|
|
147
206
|
tiene_at_references: /@[a-zA-Z][a-zA-Z0-9_\-./]+\.md/.test(contenido),
|
|
207
|
+
tiene_referencia_karpathy: tieneReferenciaKarpathy,
|
|
208
|
+
es_project_level: esProjectLevel,
|
|
209
|
+
duplicaciones_reglas_globales: {
|
|
210
|
+
evaluado: resultadoDuplicaciones.evaluado,
|
|
211
|
+
total_reglas_evaluadas: resultadoDuplicaciones.total_reglas_evaluadas,
|
|
212
|
+
detectadas: resultadoDuplicaciones.duplicaciones.length,
|
|
213
|
+
ids: resultadoDuplicaciones.duplicaciones.map(d => d.id),
|
|
214
|
+
},
|
|
148
215
|
},
|
|
149
216
|
hallazgos,
|
|
150
217
|
};
|
|
151
218
|
}
|
|
152
219
|
|
|
220
|
+
/**
|
|
221
|
+
* True si el contenido menciona los 4 principios Karpathy o el skill
|
|
222
|
+
* prevencion-sobreingenieria (cualquier coincidencia case-insensitive).
|
|
223
|
+
*/
|
|
224
|
+
function detectarReferenciaKarpathy(contenido) {
|
|
225
|
+
return KARPATHY_PATTERNS.some(re => re.test(contenido));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* True si la ruta apunta a un CLAUDE.md user-level (~/.claude/CLAUDE.md).
|
|
230
|
+
* El check Karpathy NO aplica a user-level — esos siguen otro contrato
|
|
231
|
+
* (preferencias personales, no implementación).
|
|
232
|
+
*/
|
|
233
|
+
function esRutaUserLevel(rutaClaudeMd) {
|
|
234
|
+
if (!rutaClaudeMd) return false;
|
|
235
|
+
const homeClaudeDir = path.resolve(path.join(os.homedir(), '.claude'));
|
|
236
|
+
const rutaAbs = path.resolve(rutaClaudeMd);
|
|
237
|
+
return rutaAbs.startsWith(homeClaudeDir + path.sep) || rutaAbs === path.join(homeClaudeDir, 'CLAUDE.md');
|
|
238
|
+
}
|
|
239
|
+
|
|
153
240
|
/**
|
|
154
241
|
* Detecta bullets/párrafos cuyo contenido excede MAX_BULLET_CHARS.
|
|
155
242
|
* Un "bullet" es una línea que empieza con `-` o `*` (ignorando indentación).
|
|
@@ -249,6 +336,13 @@ function imprimirReporte(resultado) {
|
|
|
249
336
|
console.log(` - Secciones ausentes: ${m.secciones_ausentes.join(', ')}`);
|
|
250
337
|
}
|
|
251
338
|
console.log(` - @references: ${m.tiene_at_references ? 'sí' : 'no'}`);
|
|
339
|
+
if (m.es_project_level) {
|
|
340
|
+
console.log(` - Referencia Karpathy: ${m.tiene_referencia_karpathy ? 'sí' : 'no'}`);
|
|
341
|
+
}
|
|
342
|
+
if (m.duplicaciones_reglas_globales && m.duplicaciones_reglas_globales.evaluado) {
|
|
343
|
+
const d = m.duplicaciones_reglas_globales;
|
|
344
|
+
console.log(` - Duplicaciones de reglas globales: ${d.detectadas}/${d.total_reglas_evaluadas} reglas duplicadas${d.detectadas > 0 ? ' (' + d.ids.join(', ') + ')' : ''}`);
|
|
345
|
+
}
|
|
252
346
|
console.log('');
|
|
253
347
|
}
|
|
254
348
|
|
|
@@ -294,4 +388,16 @@ if (require.main === module) {
|
|
|
294
388
|
main();
|
|
295
389
|
}
|
|
296
390
|
|
|
297
|
-
module.exports = {
|
|
391
|
+
module.exports = {
|
|
392
|
+
auditar,
|
|
393
|
+
ubicarClaudeMd,
|
|
394
|
+
detectarBulletsGigantes,
|
|
395
|
+
detectarReferenciaKarpathy,
|
|
396
|
+
esRutaUserLevel,
|
|
397
|
+
main,
|
|
398
|
+
MAX_LINES,
|
|
399
|
+
MAX_BULLET_CHARS,
|
|
400
|
+
KARPATHY_MIN_LINES,
|
|
401
|
+
// Re-export lazy del detector (puede ser null si lib/ no está disponible):
|
|
402
|
+
cargarDetectorReglasDuplicadas,
|
|
403
|
+
};
|