refacil-sdd-ai 2.2.1 → 2.6.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/README.md +150 -2
- package/bin/cli.js +204 -33
- package/lib/compact/bash.js +50 -0
- package/lib/compact/rules.js +190 -0
- package/lib/compact/telemetry.js +93 -0
- package/lib/compact-guidance.js +77 -0
- package/package.json +2 -1
- package/skills/apply/SKILL.md +2 -6
- package/skills/archive/SKILL.md +1 -4
- package/skills/bug/SKILL.md +9 -42
- package/skills/explore/SKILL.md +1 -3
- package/skills/prereqs/SKILL.md +16 -25
- package/skills/propose/SKILL.md +1 -3
- package/skills/review/SKILL.md +9 -47
- package/skills/setup/SKILL.md +6 -0
- package/skills/test/SKILL.md +10 -23
- package/skills/up-code/SKILL.md +5 -10
- package/skills/verify/SKILL.md +1 -4
- package/templates/compact-guidance.md +45 -0
- package/templates/{claude-md.md → methodology-guide.md} +10 -6
- package/templates/cursorrules.md +0 -22
package/README.md
CHANGED
|
@@ -7,7 +7,9 @@ Instala skills para **Claude Code** y **Cursor** que guian al desarrollador por
|
|
|
7
7
|
## Requisitos
|
|
8
8
|
|
|
9
9
|
- **Node.js >= 20.19.0** (requerido por OpenSpec)
|
|
10
|
-
- **Claude Code** o **Cursor**
|
|
10
|
+
- **Claude Code >= 2.1.89** (requerido por el hook `compact-bash` para rewrite silencioso de comandos via `updatedInput`) o **Cursor**
|
|
11
|
+
|
|
12
|
+
`refacil-sdd-ai init` verifica la version de Claude Code y muestra advertencia si es inferior. Con version < 2.1.89 el resto de la metodologia funciona, pero el hook `compact-bash` no tendra efecto (Claude Code ignora `updatedInput` en versiones antiguas).
|
|
11
13
|
|
|
12
14
|
## Instalacion y Setup
|
|
13
15
|
|
|
@@ -53,6 +55,11 @@ refacil-sdd-ai init # Instalar skills, hooks y crear configs en el repo
|
|
|
53
55
|
refacil-sdd-ai update # Actualizar skills y hooks a la ultima version
|
|
54
56
|
refacil-sdd-ai check-update # Verifica si hay una version mas reciente en npm (usado por hook)
|
|
55
57
|
refacil-sdd-ai check-review # Verifica que el review se haya completado (usado por hook)
|
|
58
|
+
refacil-sdd-ai compact-bash # Hook de rewrite de comandos Bash bare (usado por hook, no invocar manual)
|
|
59
|
+
refacil-sdd-ai compact stats # Tabla de rewrites + tokens estimados + USD ahorrado
|
|
60
|
+
refacil-sdd-ai compact disable # Desactiva el hook compact-bash sin desinstalarlo
|
|
61
|
+
refacil-sdd-ai compact enable # Reactiva el hook compact-bash
|
|
62
|
+
refacil-sdd-ai compact clear-log # Limpia ~/.refacil-sdd-ai/compact.log
|
|
56
63
|
refacil-sdd-ai clean # Eliminar skills y remover hooks SDD-AI del repo
|
|
57
64
|
refacil-sdd-ai help # Ver ayuda
|
|
58
65
|
```
|
|
@@ -327,7 +334,8 @@ El paquete instala hooks en `.claude/settings.json` durante `init` y `update`:
|
|
|
327
334
|
|
|
328
335
|
| Hook | Evento | Que hace |
|
|
329
336
|
|------|--------|----------|
|
|
330
|
-
| `check-update` | `SessionStart` | Verifica si hay nueva version del paquete en npm y la instala automaticamente |
|
|
337
|
+
| `check-update` | `SessionStart` | Verifica si hay nueva version del paquete en npm y la instala automaticamente. Tambien **sincroniza el bloque `compact-guidance`** en `AGENTS.md` (ver [Eficiencia de tokens](#eficiencia-de-tokens-bloque-auto-gestionado-en-agentsmd)). |
|
|
338
|
+
| `compact-bash` | `PreToolUse` (Bash) | Reescribe **silenciosamente** comandos Bash bare (git/tests/docker logs) a su forma compacta usando `updatedInput`. Sin turnos extra y sin que Claude vea el cambio. Requiere Claude Code >= 2.1.89. |
|
|
331
339
|
| `check-review` | `PreToolUse` (Bash) | Intercepta `git push` y verifica que exista `.review-passed` en cada cambio activo de `openspec/changes/`. Si falta, **bloquea el push** y emite instrucciones para ejecutar `/refacil:review`. El hook no invoca skills por si mismo. |
|
|
332
340
|
|
|
333
341
|
#### Flujo del hook de review
|
|
@@ -378,6 +386,121 @@ openspec/changes/fix-session-timeout-redis/
|
|
|
378
386
|
|
|
379
387
|
El archivo `.review-passed` contiene: veredicto, fecha, resumen, cantidad de hallazgos y si hubo blockers.
|
|
380
388
|
|
|
389
|
+
### Hook `compact-bash` — rewrite silencioso de comandos Bash
|
|
390
|
+
|
|
391
|
+
Segunda capa de reduccion de tokens, **sin costo conversacional**. Antes de que Claude Code ejecute un comando Bash, el hook `compact-bash` inspecciona el comando y, si matchea una regla, lo reescribe usando el campo `updatedInput` del hook. Claude **no ve el cambio**, no hay turno adicional, no hay bloqueo.
|
|
392
|
+
|
|
393
|
+
**Reglas activas (19 reglas)**:
|
|
394
|
+
|
|
395
|
+
Fase 1 — git, tests base, docker logs:
|
|
396
|
+
|
|
397
|
+
| Comando bare | Reescrito a | Ahorro tipico |
|
|
398
|
+
|---|---|---|
|
|
399
|
+
| `git log` | `git log --oneline -20` | ~85% |
|
|
400
|
+
| `git status` | `git status -s` | ~70% |
|
|
401
|
+
| `git diff` (sin args) | `git diff --stat` | ~80% |
|
|
402
|
+
| `git show` | `git show --stat` | ~70% |
|
|
403
|
+
| `docker logs <container>` | `docker logs --tail 100 <container>` | ~80%+ |
|
|
404
|
+
| `npm test` / `yarn test` / `pnpm test` | `… 2>&1 \| tail -80` | ~90% |
|
|
405
|
+
| `jest` | `jest --silent --reporters=summary` | ~85% |
|
|
406
|
+
| `pytest` | `pytest -q` | ~60% |
|
|
407
|
+
|
|
408
|
+
Fase 2A — linters, type checkers, build, sistema:
|
|
409
|
+
|
|
410
|
+
| Comando bare | Reescrito a | Ahorro tipico |
|
|
411
|
+
|---|---|---|
|
|
412
|
+
| `eslint` | `eslint . --format compact --quiet` | ~70% |
|
|
413
|
+
| `eslint <path>` | `eslint <path> --format compact` | ~60% |
|
|
414
|
+
| `biome check` | `biome check --reporter=summary` | ~65% |
|
|
415
|
+
| `tsc` / `npx tsc …` | `… 2>&1 \| head -80` | variable |
|
|
416
|
+
| `prettier --check <path>` | `prettier --check <path> --loglevel warn` | ~50% |
|
|
417
|
+
| `npm audit` | `npm audit 2>&1 \| tail -10` | ~80% |
|
|
418
|
+
| `npm ls` | `npm ls --depth=0` | ~90% |
|
|
419
|
+
| `cargo build` / `cargo test` / `cargo check` | `… --quiet` | ~50% |
|
|
420
|
+
| `go test …` (sin flags) | `… 2>&1 \| tail -80` | ~70% |
|
|
421
|
+
| `mvn test` | `mvn test -q` | ~60% |
|
|
422
|
+
| `./gradlew test` / `gradle test` | `… -q` | ~60% |
|
|
423
|
+
| `ps aux` | `ps -eo pid,pcpu,pmem,comm \| head -30` | ~80% |
|
|
424
|
+
|
|
425
|
+
**Detector de intencion**: si el comando ya tiene flags explicitos (`git log -p`, `git log --all`, `jest --watch`, `docker logs --tail 50 …`), el hook **no interviene**. Tu intencion manda.
|
|
426
|
+
|
|
427
|
+
**Escape mecanismo**: prefija `COMPACT=0` al comando para desactivar el rewrite puntualmente: `COMPACT=0 git log`.
|
|
428
|
+
|
|
429
|
+
**Pipes y redirecciones** en test bare: si ya hay `|` o `>`, el detector no las envuelve de nuevo.
|
|
430
|
+
|
|
431
|
+
**Subcomandos de control**:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
refacil-sdd-ai compact stats # tabla de rewrites + tokens estimados + USD
|
|
435
|
+
refacil-sdd-ai compact disable # desactiva el hook sin desinstalar
|
|
436
|
+
refacil-sdd-ai compact enable # reactiva el hook
|
|
437
|
+
refacil-sdd-ai compact clear-log # limpia ~/.refacil-sdd-ai/compact.log
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Telemetria**: cada rewrite genera una linea JSON en `~/.refacil-sdd-ai/compact.log` con timestamp, ruleId y estimacion de tokens ahorrados. El log se usa para el comando `compact stats` que agrega por regla y calcula costo estimado (a razon de $3/MTok input de Sonnet, conservador). La telemetria es local; nada se envia a la nube.
|
|
441
|
+
|
|
442
|
+
**Flujo del hook**:
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
Claude emite "git log"
|
|
446
|
+
│
|
|
447
|
+
▼
|
|
448
|
+
Hook compact-bash recibe tool_input via stdin (JSON)
|
|
449
|
+
│
|
|
450
|
+
▼
|
|
451
|
+
¿Matchea alguna regla y no tiene intent flags?
|
|
452
|
+
│
|
|
453
|
+
├─ SI ──► stdout JSON:
|
|
454
|
+
│ {
|
|
455
|
+
│ "hookSpecificOutput": {
|
|
456
|
+
│ "hookEventName": "PreToolUse",
|
|
457
|
+
│ "permissionDecision": "allow",
|
|
458
|
+
│ "updatedInput": { "command": "git log --oneline -20", ... },
|
|
459
|
+
│ "permissionDecisionReason": "compact-bash: git log → --oneline -20"
|
|
460
|
+
│ }
|
|
461
|
+
│ }
|
|
462
|
+
│ Claude Code ejecuta el comando reescrito. Claude no lo ve.
|
|
463
|
+
│
|
|
464
|
+
└─ NO ──► stdout vacio → Claude Code ejecuta el comando original.
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Eficiencia de tokens (bloque auto-gestionado en AGENTS.md)
|
|
468
|
+
|
|
469
|
+
La metodologia SDD-AI genera consumo elevado de contexto (artefactos, specs, prompts de skills). Para compensarlo, `refacil-sdd-ai` mantiene un bloque de guia de eficiencia de tokens dentro de `AGENTS.md` que instruye a la IA sobre como pedir salidas compactas (Read con offset/limit, `git log --oneline`, tests solo con failures, etc.).
|
|
470
|
+
|
|
471
|
+
**Caracteristicas**:
|
|
472
|
+
- Delimitado por marcadores HTML: `<!-- refacil-sdd-ai:compact-guidance:start -->` y `<!-- refacil-sdd-ai:compact-guidance:end -->`
|
|
473
|
+
- Fuente de verdad: `templates/compact-guidance.md` dentro del paquete
|
|
474
|
+
- Se sincroniza automaticamente en tres momentos:
|
|
475
|
+
1. `refacil-sdd-ai init` (al instalar)
|
|
476
|
+
2. `refacil-sdd-ai update` (al actualizar manualmente)
|
|
477
|
+
3. Hook `check-update` en cada `SessionStart` (garantiza que repos ya instalados reciban las guias nuevas sin intervencion manual)
|
|
478
|
+
- Si `AGENTS.md` aun no existe (el usuario no ha corrido `/refacil:setup`), la sincronizacion no hace nada — no se crea el archivo a espaldas del usuario
|
|
479
|
+
- `refacil-sdd-ai clean` remueve el bloque de forma limpia
|
|
480
|
+
|
|
481
|
+
> **IMPORTANTE**: No edites manualmente el contenido entre los marcadores. Cualquier cambio se sobrescribira en la proxima sesion. Si tienes sugerencias sobre reglas de eficiencia, proponlas al paquete (`templates/compact-guidance.md`).
|
|
482
|
+
|
|
483
|
+
**Flujo del refresh automatico**:
|
|
484
|
+
|
|
485
|
+
```
|
|
486
|
+
Nueva sesion de Claude Code
|
|
487
|
+
│
|
|
488
|
+
▼
|
|
489
|
+
Hook SessionStart → ejecuta "refacil-sdd-ai check-update"
|
|
490
|
+
│
|
|
491
|
+
├─► syncCompactGuidance()
|
|
492
|
+
│ │
|
|
493
|
+
│ ├─ AGENTS.md no existe → skip (sin ruido)
|
|
494
|
+
│ ├─ sin marcadores → append del bloque al final
|
|
495
|
+
│ ├─ con marcadores → reemplaza contenido entre ellos
|
|
496
|
+
│ └─ contenido identico → no-op (no ensucia git status)
|
|
497
|
+
│
|
|
498
|
+
└─► chequeo de version npm
|
|
499
|
+
│
|
|
500
|
+
└─ si hay version nueva → npm update -g + refacil-sdd-ai update
|
|
501
|
+
(que vuelve a sincronizar el bloque)
|
|
502
|
+
```
|
|
503
|
+
|
|
381
504
|
## Como funciona
|
|
382
505
|
|
|
383
506
|
1. **`refacil-sdd-ai init`** copia las skills a `.claude/skills/` y `.cursor/skills/`, crea `CLAUDE.md` + `.cursorrules`, y configura hooks automaticos en `.claude/settings.json`
|
|
@@ -393,6 +516,7 @@ refacil-sdd-ai init → Skills en .claude/ y .cursor/
|
|
|
393
516
|
|
|
394
517
|
/refacil:setup → OpenSpec instalado
|
|
395
518
|
AGENTS.md generado (analisis automatico del repo)
|
|
519
|
+
+ bloque compact-guidance inyectado automaticamente
|
|
396
520
|
|
|
397
521
|
/refacil:propose → Artefactos en openspec/changes/
|
|
398
522
|
/refacil:apply → Codigo implementado
|
|
@@ -415,6 +539,7 @@ refacil-sdd-ai init → Skills en .claude/ y .cursor/
|
|
|
415
539
|
CLAUDE.md # Claude Code — apunta a AGENTS.md
|
|
416
540
|
.cursorrules # Cursor — apunta a AGENTS.md
|
|
417
541
|
AGENTS.md # Generado por /refacil:setup (NO por el CLI)
|
|
542
|
+
# Incluye bloque compact-guidance auto-gestionado por el hook SessionStart
|
|
418
543
|
openspec/ # Generado por /refacil:setup via OpenSpec
|
|
419
544
|
```
|
|
420
545
|
|
|
@@ -436,6 +561,29 @@ npm uninstall -g refacil-sdd-ai
|
|
|
436
561
|
- [Claude Code](https://claude.ai/code) — CLI de Anthropic
|
|
437
562
|
- [Cursor](https://cursor.sh) — IDE con IA
|
|
438
563
|
|
|
564
|
+
## Cambios recientes
|
|
565
|
+
|
|
566
|
+
**v2.6.0** — Fase 2 del hook `compact-bash`:
|
|
567
|
+
- 11 reglas nuevas: `eslint`, `biome check`, `tsc`, `prettier --check`, `npm audit`, `npm ls`, `cargo build/test/check`, `go test`, `mvn test`, `gradle test`, `ps aux` (total: 19 reglas)
|
|
568
|
+
- Telemetria local en `~/.refacil-sdd-ai/compact.log`
|
|
569
|
+
- Subcomandos `compact stats | disable | enable | clear-log`
|
|
570
|
+
- `ps-aux` solo se activa en Unix (Linux/Mac); en Windows la regla no interviene
|
|
571
|
+
|
|
572
|
+
**v2.5.0** — Fase 1 del hook `compact-bash`:
|
|
573
|
+
- Hook `PreToolUse` que reescribe silenciosamente comandos Bash bare via `updatedInput` (requiere Claude Code >= 2.1.89)
|
|
574
|
+
- Reglas iniciales: git log/status/diff/show, docker logs, npm/yarn/pnpm test, jest, pytest
|
|
575
|
+
- Escape mecanismo `COMPACT=0 <cmd>`
|
|
576
|
+
|
|
577
|
+
**v2.4.0** — Reduccion de tokens en la metodologia:
|
|
578
|
+
- Boilerplate "Antes de empezar" unificado en 9 skills
|
|
579
|
+
- Validacion de OpenSpec simplificada en `prereqs/SKILL.md`
|
|
580
|
+
- Templates consolidados (`claude-md.md` + `cursorrules.md` → `methodology-guide.md` unico)
|
|
581
|
+
- Compactacion de `review/SKILL.md`, `bug/SKILL.md`, `test/SKILL.md`
|
|
582
|
+
|
|
583
|
+
**v2.3.0** — Bloque `compact-guidance` auto-gestionado en `AGENTS.md`:
|
|
584
|
+
- Hook `SessionStart` sincroniza el bloque en cada sesion
|
|
585
|
+
- Fuente de verdad: `templates/compact-guidance.md`
|
|
586
|
+
|
|
439
587
|
## Licencia
|
|
440
588
|
|
|
441
589
|
MIT
|
package/bin/cli.js
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const {
|
|
6
|
+
syncCompactGuidance,
|
|
7
|
+
removeCompactGuidance,
|
|
8
|
+
} = require('../lib/compact-guidance');
|
|
9
|
+
const compactBash = require('../lib/compact/bash');
|
|
10
|
+
const compactTelemetry = require('../lib/compact/telemetry');
|
|
5
11
|
|
|
6
12
|
const SKILLS = [
|
|
7
13
|
'setup',
|
|
@@ -64,44 +70,47 @@ function extractSddSection(templateContent) {
|
|
|
64
70
|
return templateContent.substring(idx);
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
function readMethodologyGuide() {
|
|
74
|
+
return fs.readFileSync(
|
|
75
|
+
path.join(packageRoot, 'templates', 'methodology-guide.md'),
|
|
76
|
+
'utf8',
|
|
77
|
+
);
|
|
78
|
+
}
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
function writeGuideFile(destPath, header, label) {
|
|
81
|
+
const guide = readMethodologyGuide();
|
|
82
|
+
const content = `# ${header}\n\n${guide}`;
|
|
83
|
+
|
|
84
|
+
if (fs.existsSync(destPath)) {
|
|
85
|
+
const existing = fs.readFileSync(destPath, 'utf8');
|
|
73
86
|
if (existing.includes(SDD_SECTION_MARKER)) {
|
|
74
|
-
console.log(
|
|
87
|
+
console.log(` ${label} ya tiene la seccion SDD-AI. Sin cambios.`);
|
|
75
88
|
return false;
|
|
76
89
|
}
|
|
77
|
-
const sddSection = extractSddSection(
|
|
78
|
-
fs.writeFileSync(
|
|
79
|
-
console.log(
|
|
90
|
+
const sddSection = extractSddSection(guide);
|
|
91
|
+
fs.writeFileSync(destPath, existing.trimEnd() + '\n\n' + sddSection + '\n');
|
|
92
|
+
console.log(` ${label} existente — seccion SDD-AI agregada al final.`);
|
|
80
93
|
return true;
|
|
81
94
|
}
|
|
82
95
|
|
|
83
|
-
fs.writeFileSync(
|
|
96
|
+
fs.writeFileSync(destPath, content);
|
|
84
97
|
return true;
|
|
85
98
|
}
|
|
86
99
|
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(' .cursorrules ya tiene la seccion SDD-AI. Sin cambios.');
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
const sddSection = extractSddSection(template);
|
|
98
|
-
fs.writeFileSync(dest, existing.trimEnd() + '\n\n' + sddSection + '\n');
|
|
99
|
-
console.log(' .cursorrules existente — seccion SDD-AI agregada al final.');
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
100
|
+
function createClaudeMd() {
|
|
101
|
+
return writeGuideFile(
|
|
102
|
+
path.join(projectRoot, 'CLAUDE.md'),
|
|
103
|
+
'CLAUDE.md',
|
|
104
|
+
'CLAUDE.md',
|
|
105
|
+
);
|
|
106
|
+
}
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
return
|
|
108
|
+
function createCursorRules() {
|
|
109
|
+
return writeGuideFile(
|
|
110
|
+
path.join(projectRoot, '.cursorrules'),
|
|
111
|
+
'Cursor Rules',
|
|
112
|
+
'.cursorrules',
|
|
113
|
+
);
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
function removeSkills() {
|
|
@@ -121,6 +130,29 @@ function removeSkills() {
|
|
|
121
130
|
return removed;
|
|
122
131
|
}
|
|
123
132
|
|
|
133
|
+
function checkClaudeCodeVersion() {
|
|
134
|
+
const { execSync } = require('child_process');
|
|
135
|
+
try {
|
|
136
|
+
const output = execSync('claude --version 2>&1', {
|
|
137
|
+
encoding: 'utf8',
|
|
138
|
+
timeout: 5000,
|
|
139
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
140
|
+
}).trim();
|
|
141
|
+
const match = output.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
142
|
+
if (!match) return { ok: null, version: null };
|
|
143
|
+
const maj = Number(match[1]);
|
|
144
|
+
const min = Number(match[2]);
|
|
145
|
+
const patch = Number(match[3]);
|
|
146
|
+
const ok =
|
|
147
|
+
maj > 2 ||
|
|
148
|
+
(maj === 2 && min > 1) ||
|
|
149
|
+
(maj === 2 && min === 1 && patch >= 89);
|
|
150
|
+
return { ok, version: `${maj}.${min}.${patch}` };
|
|
151
|
+
} catch (_) {
|
|
152
|
+
return { ok: null, version: null };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
124
156
|
function checkNodeVersion() {
|
|
125
157
|
const version = process.version; // e.g. v20.19.5
|
|
126
158
|
const major = parseInt(version.split('.')[0].replace('v', ''));
|
|
@@ -167,9 +199,28 @@ function installHook() {
|
|
|
167
199
|
changed = true;
|
|
168
200
|
}
|
|
169
201
|
|
|
170
|
-
// PreToolUse
|
|
202
|
+
// PreToolUse
|
|
171
203
|
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
|
|
172
204
|
|
|
205
|
+
// compact-bash (must run BEFORE check-review so rewrite is visible to subsequent hooks)
|
|
206
|
+
const hasCompactHook = settings.hooks.PreToolUse.some(
|
|
207
|
+
(h) => h._sdd_compact === true,
|
|
208
|
+
);
|
|
209
|
+
if (!hasCompactHook) {
|
|
210
|
+
settings.hooks.PreToolUse.unshift({
|
|
211
|
+
_sdd_compact: true,
|
|
212
|
+
matcher: 'Bash',
|
|
213
|
+
hooks: [
|
|
214
|
+
{
|
|
215
|
+
type: 'command',
|
|
216
|
+
command: 'refacil-sdd-ai compact-bash',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
changed = true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// check-review
|
|
173
224
|
const hasReviewHook = settings.hooks.PreToolUse.some(
|
|
174
225
|
(h) => h._sdd_review === true,
|
|
175
226
|
);
|
|
@@ -225,7 +276,7 @@ function uninstallHook() {
|
|
|
225
276
|
if (Array.isArray(settings.hooks.PreToolUse)) {
|
|
226
277
|
const original = settings.hooks.PreToolUse.length;
|
|
227
278
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
|
228
|
-
(h) => h._sdd_review !== true,
|
|
279
|
+
(h) => h._sdd_review !== true && h._sdd_compact !== true,
|
|
229
280
|
);
|
|
230
281
|
if (settings.hooks.PreToolUse.length !== original) changed = true;
|
|
231
282
|
if (settings.hooks.PreToolUse.length === 0) delete settings.hooks.PreToolUse;
|
|
@@ -245,6 +296,15 @@ function checkUpdate() {
|
|
|
245
296
|
const { execSync } = require('child_process');
|
|
246
297
|
const localVersion = require(path.join(packageRoot, 'package.json')).version;
|
|
247
298
|
|
|
299
|
+
// Always ensure AGENTS.md has current compact-guidance block (silent unless error)
|
|
300
|
+
try {
|
|
301
|
+
syncCompactGuidance(projectRoot, packageRoot);
|
|
302
|
+
} catch (err) {
|
|
303
|
+
process.stderr.write(
|
|
304
|
+
`[refacil-sdd-ai] No se pudo sincronizar compact-guidance: ${err.message}\n`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
248
308
|
try {
|
|
249
309
|
const latest = execSync('npm view refacil-sdd-ai version', {
|
|
250
310
|
encoding: 'utf8',
|
|
@@ -319,8 +379,7 @@ function checkReview() {
|
|
|
319
379
|
const reason =
|
|
320
380
|
missing.length === 1
|
|
321
381
|
? `[refacil-sdd-ai] Review pendiente para: ${names}. ` +
|
|
322
|
-
'
|
|
323
|
-
'Informa al usuario que el review se esta ejecutando automaticamente antes de subir el codigo. ' +
|
|
382
|
+
'Deten el push y ejecuta /refacil:review sobre ese cambio antes de subir codigo. ' +
|
|
324
383
|
'Si el review aprueba, reintenta el git push. ' +
|
|
325
384
|
'Si el review requiere correcciones, informa los hallazgos al usuario y NO reintentar el push.'
|
|
326
385
|
: `[refacil-sdd-ai] Hay multiples cambios sin review aprobado: ${names}. ` +
|
|
@@ -347,6 +406,18 @@ function init() {
|
|
|
347
406
|
console.log(` Node.js ${process.version} OK`);
|
|
348
407
|
}
|
|
349
408
|
|
|
409
|
+
// Check Claude Code version (for compact-bash hook)
|
|
410
|
+
const claudeCheck = checkClaudeCodeVersion();
|
|
411
|
+
if (claudeCheck.ok === true) {
|
|
412
|
+
console.log(` Claude Code ${claudeCheck.version} OK`);
|
|
413
|
+
} else if (claudeCheck.ok === false) {
|
|
414
|
+
console.log(`\n ADVERTENCIA: Claude Code ${claudeCheck.version} detectado.`);
|
|
415
|
+
console.log(' El hook compact-bash requiere Claude Code >= 2.1.89 para rewrite silencioso.');
|
|
416
|
+
console.log(' Con version inferior se instala igual pero el rewrite no tendra efecto.');
|
|
417
|
+
console.log(' Actualiza con: npm install -g @anthropic-ai/claude-code\n');
|
|
418
|
+
}
|
|
419
|
+
// ok === null: claude no esta en PATH, silencioso
|
|
420
|
+
|
|
350
421
|
// Install skills
|
|
351
422
|
const count = installSkills();
|
|
352
423
|
console.log(` ${count} skills instaladas en .claude/skills/ y .cursor/skills/`);
|
|
@@ -366,6 +437,18 @@ function init() {
|
|
|
366
437
|
console.log(' Hook check-update agregado a .claude/settings.json');
|
|
367
438
|
}
|
|
368
439
|
|
|
440
|
+
// Sync compact-guidance block in AGENTS.md (if it exists)
|
|
441
|
+
try {
|
|
442
|
+
const result = syncCompactGuidance(projectRoot, packageRoot);
|
|
443
|
+
if (result.status === 'appended') {
|
|
444
|
+
console.log(' Bloque compact-guidance agregado a AGENTS.md');
|
|
445
|
+
} else if (result.status === 'replaced') {
|
|
446
|
+
console.log(' Bloque compact-guidance actualizado en AGENTS.md');
|
|
447
|
+
}
|
|
448
|
+
} catch (err) {
|
|
449
|
+
console.error(` Advertencia: no se pudo sincronizar compact-guidance: ${err.message}`);
|
|
450
|
+
}
|
|
451
|
+
|
|
369
452
|
console.log('\n Siguientes pasos:\n');
|
|
370
453
|
console.log(' 1. REINICIA tu sesion de Claude Code o Cursor');
|
|
371
454
|
console.log(' (las skills nuevas no se detectan hasta reiniciar)\n');
|
|
@@ -386,6 +469,18 @@ function update() {
|
|
|
386
469
|
console.log(' Hook check-update agregado a .claude/settings.json');
|
|
387
470
|
}
|
|
388
471
|
|
|
472
|
+
// Sync compact-guidance block in AGENTS.md
|
|
473
|
+
try {
|
|
474
|
+
const result = syncCompactGuidance(projectRoot, packageRoot);
|
|
475
|
+
if (result.status === 'appended') {
|
|
476
|
+
console.log(' Bloque compact-guidance agregado a AGENTS.md');
|
|
477
|
+
} else if (result.status === 'replaced') {
|
|
478
|
+
console.log(' Bloque compact-guidance actualizado en AGENTS.md');
|
|
479
|
+
}
|
|
480
|
+
} catch (err) {
|
|
481
|
+
console.error(` Advertencia: no se pudo sincronizar compact-guidance: ${err.message}`);
|
|
482
|
+
}
|
|
483
|
+
|
|
389
484
|
console.log('\n REINICIA tu sesion de Claude Code o Cursor para aplicar los cambios.\n');
|
|
390
485
|
}
|
|
391
486
|
|
|
@@ -398,11 +493,75 @@ function clean() {
|
|
|
398
493
|
} else {
|
|
399
494
|
console.log(' No se encontraron hooks SDD-AI para remover.');
|
|
400
495
|
}
|
|
496
|
+
|
|
497
|
+
// Remove compact-guidance block from AGENTS.md if present
|
|
498
|
+
try {
|
|
499
|
+
const result = removeCompactGuidance(projectRoot);
|
|
500
|
+
if (result.status === 'removed') {
|
|
501
|
+
console.log(' Bloque compact-guidance removido de AGENTS.md');
|
|
502
|
+
}
|
|
503
|
+
} catch (err) {
|
|
504
|
+
console.error(` Advertencia: no se pudo limpiar compact-guidance: ${err.message}`);
|
|
505
|
+
}
|
|
506
|
+
|
|
401
507
|
console.log(' AGENTS.md, CLAUDE.md y .cursorrules no fueron eliminados.');
|
|
402
508
|
console.log('\n Nota: Los comandos opsx:* de OpenSpec no se eliminan.');
|
|
403
509
|
console.log(' Para eliminar OpenSpec: rm -rf openspec/ .claude/commands/opsx .cursor/commands/opsx\n');
|
|
404
510
|
}
|
|
405
511
|
|
|
512
|
+
// --- Compact subcommands (stats / enable / disable / clear-log) ---
|
|
513
|
+
|
|
514
|
+
function handleCompactSubcommand(sub) {
|
|
515
|
+
switch (sub) {
|
|
516
|
+
case 'stats':
|
|
517
|
+
showCompactStats();
|
|
518
|
+
break;
|
|
519
|
+
case 'disable':
|
|
520
|
+
compactTelemetry.disable();
|
|
521
|
+
console.log(' compact-bash deshabilitado. Reactiva con: refacil-sdd-ai compact enable');
|
|
522
|
+
break;
|
|
523
|
+
case 'enable':
|
|
524
|
+
compactTelemetry.enable();
|
|
525
|
+
console.log(' compact-bash habilitado.');
|
|
526
|
+
break;
|
|
527
|
+
case 'clear-log':
|
|
528
|
+
compactTelemetry.clearLog();
|
|
529
|
+
console.log(' compact.log limpiado.');
|
|
530
|
+
break;
|
|
531
|
+
default:
|
|
532
|
+
console.log('Uso: refacil-sdd-ai compact <stats|disable|enable|clear-log>');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function showCompactStats() {
|
|
537
|
+
const s = compactTelemetry.stats();
|
|
538
|
+
if (s.totalRewrites === 0) {
|
|
539
|
+
console.log('\n No hay rewrites registrados todavia. El hook compact-bash aun no se ha disparado o esta deshabilitado.\n');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const sorted = Object.entries(s.byRule).sort((a, b) => b[1].saved - a[1].saved);
|
|
544
|
+
|
|
545
|
+
console.log(`\n compact-bash stats (${s.totalRewrites} rewrites en total)\n`);
|
|
546
|
+
for (const [id, data] of sorted) {
|
|
547
|
+
const kTokens = (data.saved / 1000).toFixed(1);
|
|
548
|
+
console.log(
|
|
549
|
+
` ${id.padEnd(18)} ${String(data.count).padStart(6)} rewrites ~${kTokens.padStart(7)}k tokens estimados`,
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
const totalK = (s.totalSaved / 1000).toFixed(1);
|
|
553
|
+
const costUsd = ((s.totalSaved / 1_000_000) * 3).toFixed(2);
|
|
554
|
+
console.log(` ${'-'.repeat(62)}`);
|
|
555
|
+
console.log(
|
|
556
|
+
` ${'Total'.padEnd(18)} ${String(s.totalRewrites).padStart(6)} rewrites ~${totalK.padStart(7)}k tokens (~$${costUsd} USD, Sonnet input)`,
|
|
557
|
+
);
|
|
558
|
+
console.log(`\n Log: ${compactTelemetry.LOG_PATH}`);
|
|
559
|
+
if (compactTelemetry.isDisabled()) {
|
|
560
|
+
console.log(' Estado: DESHABILITADO (no se registran nuevos rewrites)');
|
|
561
|
+
}
|
|
562
|
+
console.log('');
|
|
563
|
+
}
|
|
564
|
+
|
|
406
565
|
function help() {
|
|
407
566
|
console.log(`
|
|
408
567
|
refacil-sdd-ai — Metodologia SDD-AI con OpenSpec
|
|
@@ -410,8 +569,14 @@ function help() {
|
|
|
410
569
|
Comandos:
|
|
411
570
|
init Instala skills en .claude/ y .cursor/, crea CLAUDE.md y .cursorrules
|
|
412
571
|
update Re-copia skills (para actualizar a nueva version del paquete)
|
|
413
|
-
check-update Verifica si hay una version mas reciente en npm
|
|
572
|
+
check-update Verifica si hay una version mas reciente en npm y sincroniza compact-guidance en AGENTS.md
|
|
414
573
|
check-review Verifica que el review se haya completado (usado por hook PreToolUse)
|
|
574
|
+
compact-bash Reescribe comandos Bash bare para reducir tokens (usado por hook PreToolUse)
|
|
575
|
+
compact Subcomandos del hook compact-bash:
|
|
576
|
+
compact stats - Estadisticas de rewrites y ahorro estimado
|
|
577
|
+
compact disable - Desactiva el rewrite temporalmente
|
|
578
|
+
compact enable - Re-activa el rewrite
|
|
579
|
+
compact clear-log - Borra el log historico
|
|
415
580
|
clean Elimina skills y remueve hooks SDD-AI de .claude/settings.json
|
|
416
581
|
help Muestra esta ayuda
|
|
417
582
|
|
|
@@ -423,7 +588,7 @@ function help() {
|
|
|
423
588
|
|
|
424
589
|
Requisitos:
|
|
425
590
|
- Node.js >= 20.19.0 (requerido por OpenSpec)
|
|
426
|
-
- Claude Code o Cursor
|
|
591
|
+
- Claude Code >= 2.1.89 (requerido por compact-bash para rewrite silencioso) o Cursor
|
|
427
592
|
`);
|
|
428
593
|
}
|
|
429
594
|
|
|
@@ -444,6 +609,12 @@ switch (command) {
|
|
|
444
609
|
case 'check-review':
|
|
445
610
|
checkReview();
|
|
446
611
|
break;
|
|
612
|
+
case 'compact-bash':
|
|
613
|
+
compactBash.run();
|
|
614
|
+
break;
|
|
615
|
+
case 'compact':
|
|
616
|
+
handleCompactSubcommand(process.argv[3]);
|
|
617
|
+
break;
|
|
447
618
|
case 'clean':
|
|
448
619
|
clean();
|
|
449
620
|
break;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { findRule } = require('./rules');
|
|
3
|
+
const telemetry = require('./telemetry');
|
|
4
|
+
|
|
5
|
+
function run() {
|
|
6
|
+
let input;
|
|
7
|
+
try {
|
|
8
|
+
const stdin = fs.readFileSync(0, 'utf8');
|
|
9
|
+
if (!stdin.trim()) return;
|
|
10
|
+
input = JSON.parse(stdin);
|
|
11
|
+
} catch (_) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (input.tool_name !== 'Bash') return;
|
|
16
|
+
if (telemetry.isDisabled()) return;
|
|
17
|
+
|
|
18
|
+
const origCommand = input.tool_input && input.tool_input.command;
|
|
19
|
+
if (typeof origCommand !== 'string') return;
|
|
20
|
+
|
|
21
|
+
const rule = findRule(origCommand);
|
|
22
|
+
if (!rule) return;
|
|
23
|
+
|
|
24
|
+
let newCommand;
|
|
25
|
+
try {
|
|
26
|
+
newCommand = rule.rewrite(origCommand);
|
|
27
|
+
} catch (_) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!newCommand || newCommand === origCommand) return;
|
|
32
|
+
|
|
33
|
+
telemetry.logRewrite(rule.id, rule.savedTokensEst);
|
|
34
|
+
|
|
35
|
+
const output = {
|
|
36
|
+
hookSpecificOutput: {
|
|
37
|
+
hookEventName: 'PreToolUse',
|
|
38
|
+
permissionDecision: 'allow',
|
|
39
|
+
permissionDecisionReason: `compact-bash: ${rule.reason}`,
|
|
40
|
+
updatedInput: {
|
|
41
|
+
...input.tool_input,
|
|
42
|
+
command: newCommand,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
process.stdout.write(JSON.stringify(output));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { run };
|