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 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** instalado
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 createClaudeMd() {
68
- const dest = path.join(projectRoot, 'CLAUDE.md');
69
- const template = fs.readFileSync(path.join(packageRoot, 'templates', 'claude-md.md'), 'utf8');
73
+ function readMethodologyGuide() {
74
+ return fs.readFileSync(
75
+ path.join(packageRoot, 'templates', 'methodology-guide.md'),
76
+ 'utf8',
77
+ );
78
+ }
70
79
 
71
- if (fs.existsSync(dest)) {
72
- const existing = fs.readFileSync(dest, 'utf8');
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(' CLAUDE.md ya tiene la seccion SDD-AI. Sin cambios.');
87
+ console.log(` ${label} ya tiene la seccion SDD-AI. Sin cambios.`);
75
88
  return false;
76
89
  }
77
- const sddSection = extractSddSection(template);
78
- fs.writeFileSync(dest, existing.trimEnd() + '\n\n' + sddSection + '\n');
79
- console.log(' CLAUDE.md existente — seccion SDD-AI agregada al final.');
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(dest, template);
96
+ fs.writeFileSync(destPath, content);
84
97
  return true;
85
98
  }
86
99
 
87
- function createCursorRules() {
88
- const dest = path.join(projectRoot, '.cursorrules');
89
- const template = fs.readFileSync(path.join(packageRoot, 'templates', 'cursorrules.md'), 'utf8');
90
-
91
- if (fs.existsSync(dest)) {
92
- const existing = fs.readFileSync(dest, 'utf8');
93
- if (existing.includes(SDD_SECTION_MARKER)) {
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
- fs.writeFileSync(dest, template);
104
- return true;
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: check-review
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
- 'Ejecuta /refacil:review automaticamente ahora sobre ese cambio pendiente. ' +
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 instalado
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 };