@saulwade/swl-ses 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/CLAUDE.md +241 -199
  2. package/README.md +597 -597
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/agentes/accesibilidad-wcag-swl.md +2 -2
  6. package/agentes/arquitecto-swl.md +2 -2
  7. package/agentes/auto-evolucion-swl.md +2 -2
  8. package/agentes/backend-api-swl.md +2 -2
  9. package/agentes/backend-csharp-swl.md +2 -2
  10. package/agentes/backend-go-swl.md +2 -2
  11. package/agentes/backend-java-swl.md +2 -2
  12. package/agentes/backend-node-swl.md +2 -2
  13. package/agentes/backend-python-swl.md +2 -2
  14. package/agentes/backend-rust-swl.md +2 -2
  15. package/agentes/backend-workers-swl.md +2 -2
  16. package/agentes/cloud-infra-swl.md +2 -2
  17. package/agentes/consolidador-swl.md +2 -2
  18. package/agentes/datos-swl.md +2 -2
  19. package/agentes/depurador-swl.md +2 -2
  20. package/agentes/devops-ci-swl.md +2 -2
  21. package/agentes/disenador-ui-swl.md +2 -2
  22. package/agentes/documentador-swl.md +2 -2
  23. package/agentes/frontend-angular-swl.md +2 -2
  24. package/agentes/frontend-css-swl.md +2 -2
  25. package/agentes/frontend-react-swl.md +2 -2
  26. package/agentes/frontend-swl.md +2 -2
  27. package/agentes/frontend-tailwind-swl.md +2 -2
  28. package/agentes/gh-fix-ci-swl.md +275 -275
  29. package/agentes/implementador-swl.md +2 -2
  30. package/agentes/investigador-swl.md +2 -2
  31. package/agentes/investigador-ux-swl.md +2 -2
  32. package/agentes/llm-apps-swl.md +2 -2
  33. package/agentes/migrador-swl.md +2 -2
  34. package/agentes/mobile-android-swl.md +2 -2
  35. package/agentes/mobile-cross-swl.md +2 -2
  36. package/agentes/mobile-ios-swl.md +2 -2
  37. package/agentes/mobile-testing-swl.md +2 -2
  38. package/agentes/nemesis-auditor-swl.md +1 -1
  39. package/agentes/notificador-swl.md +2 -2
  40. package/agentes/observabilidad-swl.md +2 -2
  41. package/agentes/orquestador-swl.md +41 -14
  42. package/agentes/pagos-swl.md +2 -2
  43. package/agentes/perfilador-usuario-swl.md +2 -2
  44. package/agentes/planificador-swl.md +2 -2
  45. package/agentes/producto-prd-swl.md +2 -2
  46. package/agentes/red-team-swl.md +218 -218
  47. package/agentes/release-manager-swl.md +2 -2
  48. package/agentes/rendimiento-swl.md +2 -2
  49. package/agentes/resolutor-build-swl.md +2 -2
  50. package/agentes/revisor-angular-swl.md +2 -2
  51. package/agentes/revisor-codigo-swl.md +36 -3
  52. package/agentes/revisor-csharp-swl.md +2 -2
  53. package/agentes/revisor-go-swl.md +2 -2
  54. package/agentes/revisor-java-swl.md +2 -2
  55. package/agentes/revisor-kotlin-swl.md +2 -2
  56. package/agentes/revisor-nextjs-swl.md +2 -2
  57. package/agentes/revisor-php-swl.md +2 -2
  58. package/agentes/revisor-react-swl.md +2 -2
  59. package/agentes/revisor-rust-swl.md +2 -2
  60. package/agentes/revisor-seguridad-swl.md +2 -2
  61. package/agentes/revisor-swift-swl.md +2 -2
  62. package/agentes/revisor-typescript-swl.md +2 -2
  63. package/agentes/sre-swl.md +2 -2
  64. package/agentes/tdd-qa-swl.md +2 -2
  65. package/comandos/swl/briefing.md +119 -119
  66. package/comandos/swl/contexto.md +0 -2
  67. package/comandos/swl/contribuir.md +233 -233
  68. package/comandos/swl/deuda-codigo.md +97 -0
  69. package/comandos/swl/predecir.md +139 -139
  70. package/comandos/swl/sesiones.md +1 -1
  71. package/gateway/agent-executor.js +1 -1
  72. package/gateway/lib/event-channel.js +191 -191
  73. package/habilidades/agent-deep-links/SKILL.md +148 -148
  74. package/habilidades/agentes-como-servicio/SKILL.md +2 -2
  75. package/habilidades/angular-moderno/SKILL.md +24 -1
  76. package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -276
  77. package/habilidades/backend-async-postgres-testing/SKILL.md +215 -215
  78. package/habilidades/backend-error-design/SKILL.md +221 -221
  79. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  80. package/habilidades/benchmark-memoria/SKILL.md +186 -186
  81. package/habilidades/calidad-anti-patrones-universales/SKILL.md +368 -368
  82. package/habilidades/calidad-contract-testing/SKILL.md +165 -165
  83. package/habilidades/calidad-mutation-testing/SKILL.md +170 -170
  84. package/habilidades/changelog-generator/scripts/parse-commits.js +367 -367
  85. package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
  86. package/habilidades/contenedores-docker/SKILL.md +3 -1
  87. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  88. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  89. package/habilidades/drift-detection/SKILL.md +179 -179
  90. package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +1 -1
  91. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
  92. package/habilidades/eval-framework/SKILL.md +212 -212
  93. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  94. package/habilidades/harness-claude-code/SKILL.md +4 -4
  95. package/habilidades/harness-claude-code/recursos/disciplina-harness-regla.md +253 -252
  96. package/habilidades/legacy-code-rescue/SKILL.md +268 -267
  97. package/habilidades/meta-skills-estandar/SKILL.md +1 -1
  98. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  99. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +2 -2
  100. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  101. package/habilidades/perfil-usuario/SKILL.md +200 -200
  102. package/habilidades/postgresql-experto/SKILL.md +3 -1
  103. package/habilidades/prevencion-sobreingenieria/SKILL.md +70 -92
  104. package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
  105. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -0
  106. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -0
  107. package/habilidades/proceso-ddia-fundamentos/SKILL.md +255 -255
  108. package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
  109. package/habilidades/proceso-debate-adversarial/recursos/personas.md +105 -105
  110. package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
  111. package/habilidades/proceso-dynamic-workflows/SKILL.md +138 -138
  112. package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
  113. package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
  114. package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
  115. package/habilidades/proceso-modular-split/SKILL.md +256 -256
  116. package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +2 -2
  117. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  118. package/habilidades/structured-outputs/SKILL.md +2 -2
  119. package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
  120. package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
  121. package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
  122. package/habilidades/swl-dashboard/SKILL.md +2 -2
  123. package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
  124. package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +1 -1
  125. package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +1 -1
  126. package/hooks/audit-trail.js +4 -4
  127. package/hooks/calidad-pre-commit.js +15 -11
  128. package/hooks/captura-acciones-post.js +3 -2
  129. package/hooks/captura-acciones-session.js +3 -2
  130. package/hooks/ciclo-evolucion-subagente.js +26 -26
  131. package/hooks/ciclo-evolucion.js +26 -26
  132. package/hooks/claudemd-bloat-detector.js +161 -161
  133. package/hooks/claudemd-duplicacion-detector.js +170 -170
  134. package/hooks/contexto-iteracion.js +145 -144
  135. package/hooks/contexto-subagente.js +68 -0
  136. package/hooks/guardrail-modelo.js +14 -4
  137. package/hooks/inyeccion-contexto.js +12 -12
  138. package/hooks/lib/agent-routing.js +107 -107
  139. package/hooks/lib/auto-consolidator.js +335 -335
  140. package/hooks/lib/autonomia.js +208 -208
  141. package/hooks/lib/ciclo-evolucion.js +47 -47
  142. package/hooks/lib/deep-links.js +185 -185
  143. package/hooks/lib/error-classifier.js +308 -308
  144. package/hooks/lib/etapa-auto-evolucion.js +701 -701
  145. package/hooks/lib/etapa-metricas.js +388 -388
  146. package/hooks/lib/etapa-perfil-usuario.js +376 -376
  147. package/hooks/lib/loop-telemetry.js +321 -321
  148. package/hooks/lib/merkle-audit.js +96 -96
  149. package/hooks/lib/model-router.js +2 -2
  150. package/hooks/lib/propose-step.js +358 -358
  151. package/hooks/lib/provenance-tracker.js +191 -191
  152. package/hooks/lib/rate-limit-tracker.js +253 -253
  153. package/hooks/lib/resource-quota.js +122 -122
  154. package/hooks/lib/retry-jitter.js +165 -165
  155. package/hooks/lib/security-net.js +201 -201
  156. package/hooks/lib/skill-auditor.js +588 -588
  157. package/hooks/lib/sync-status.js +228 -228
  158. package/hooks/lib/taint-tracker.js +107 -107
  159. package/hooks/lib/text-similarity.js +241 -241
  160. package/hooks/lib/token-estimator.js +1 -0
  161. package/hooks/lib/toon-compressor.js +245 -245
  162. package/hooks/lib/usage-model.js +1 -1
  163. package/hooks/registro-turnos.js +210 -209
  164. package/hooks/session-briefing.js +98 -98
  165. package/hooks/spec-gate.js +212 -211
  166. package/hooks/sugerir-contribuir.js +2 -1
  167. package/hooks/sugerir-regenerar-inventario.js +171 -170
  168. package/hooks/tdd-gate.js +242 -241
  169. package/hooks/telemetria-skill-routing.js +100 -100
  170. package/hooks/validar-formato-post-subagente.js +140 -140
  171. package/hooks/validar-intent-spec.js +242 -242
  172. package/hooks/validar-memoria-hook.js +218 -218
  173. package/hooks/validar-planning-paths.js +134 -134
  174. package/instintos/autonomia.yaml +27 -27
  175. package/instintos/prompt-appendices.yaml +57 -57
  176. package/llms.txt +29 -29
  177. package/manifiestos/agent-output-schemas.json +57 -57
  178. package/manifiestos/canonical-hashes.json +2948 -1964
  179. package/manifiestos/hook-profiles.json +0 -2
  180. package/manifiestos/hooks-config.json +469 -477
  181. package/manifiestos/invariantes-criticos.json +30 -0
  182. package/manifiestos/modulos.json +1390 -1390
  183. package/manifiestos/planning-paths.json +44 -44
  184. package/manifiestos/skills-lock.json +1282 -1282
  185. package/package.json +93 -93
  186. package/plantillas/auditor-veto-template.md +105 -105
  187. package/plantillas/github-workflows/README.md +47 -47
  188. package/plantillas/github-workflows/release-please.yml +44 -44
  189. package/plantillas/github-workflows/swl-ci.yml +107 -107
  190. package/plantillas/github-workflows/swl-security.yml +51 -51
  191. package/plugin.json +369 -371
  192. package/reglas/accesibilidad.md +10 -10
  193. package/reglas/analisis-previo-tareas-grandes.md +172 -172
  194. package/reglas/api-diseno.md +9 -9
  195. package/reglas/arreglar-al-detectar.md +240 -240
  196. package/reglas/auditorias-documentales-estructurales.md +7 -7
  197. package/reglas/cloud-infra.md +8 -8
  198. package/reglas/consultar-vault-primero.md +195 -195
  199. package/reglas/debatir-antes-de-aceptar.md +158 -158
  200. package/reglas/fragmentos-compartidos.md +183 -183
  201. package/reglas/hooks.md +6 -6
  202. package/reglas/intent-engineering.md +218 -218
  203. package/reglas/markitdown.md +8 -8
  204. package/reglas/monitor-ci.md +309 -309
  205. package/reglas/patrones.md +6 -6
  206. package/reglas/sesiones-paralelas.md +180 -180
  207. package/reglas/sin-duplicacion-reglas-globales.md +182 -182
  208. package/reglas/skills-estandar.md +6 -6
  209. package/reglas/testing.md +7 -7
  210. package/reglas/tests-cleanup.md +224 -224
  211. package/reglas/usar-code-review-graph.md +155 -155
  212. package/reglas/usar-context7.md +226 -226
  213. package/reglas/verificar-citas-normativas.md +548 -548
  214. package/schemas/agent-frontmatter.schema.json +5 -3
  215. package/schemas/agent-message.schema.json +73 -73
  216. package/schemas/agent-output-implementacion.schema.json +114 -114
  217. package/schemas/agent-output-planificacion.schema.json +150 -150
  218. package/schemas/agent-output-review.schema.json +98 -98
  219. package/schemas/diary-entry.schema.json +112 -112
  220. package/schemas/hook-profiles.schema.json +54 -54
  221. package/schemas/hooks-config.schema.json +89 -89
  222. package/schemas/modulos.schema.json +38 -38
  223. package/schemas/perfiles.schema.json +36 -36
  224. package/schemas/plugin.schema.json +77 -77
  225. package/schemas/skill-evals.schema.json +119 -119
  226. package/schemas/skill-frontmatter.schema.json +245 -245
  227. package/scripts/audit-tools/audit-history.js +330 -330
  228. package/scripts/audit-tools/bundle-tracker.js +290 -290
  229. package/scripts/audit-tools/canary-monitor.js +352 -352
  230. package/scripts/audit-tools/code-profiler.js +605 -605
  231. package/scripts/audit-tools/dep-doctor.js +320 -320
  232. package/scripts/audit-tools/env-validator.js +206 -206
  233. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  234. package/scripts/audit-tools/lib/output.js +23 -23
  235. package/scripts/audit-tools/migration-checker.js +392 -392
  236. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  237. package/scripts/benchmark-memoria.js +167 -167
  238. package/scripts/cli/aprobar-plan.js +73 -73
  239. package/scripts/cli/briefing.js +23 -23
  240. package/scripts/cli/ciclo-evolucion.js +26 -26
  241. package/scripts/cli/configurar-ci.js +40 -40
  242. package/scripts/cli/derivar-feature-list.js +25 -25
  243. package/scripts/cli/detectar-host.js +27 -27
  244. package/scripts/cli/diary-entry.js +69 -69
  245. package/scripts/cli/execution-state.js +18 -18
  246. package/scripts/cli/gateway-notify.js +41 -41
  247. package/scripts/cli/liberar-fase.js +42 -42
  248. package/scripts/cli/loop-telemetry.js +125 -125
  249. package/scripts/cli/mark-evolved.js +56 -56
  250. package/scripts/cli/metricas-dora.js +26 -26
  251. package/scripts/cli/near-duplicate.js +55 -55
  252. package/scripts/cli/notificaciones.js +123 -123
  253. package/scripts/cli/propose-step.js +29 -29
  254. package/scripts/cli/schedule-parse.js +19 -19
  255. package/scripts/cli/sugerir-modelo.js +20 -20
  256. package/scripts/cli/verificar-plan.js +36 -36
  257. package/scripts/cli/verificar-trazabilidad.js +35 -35
  258. package/scripts/configurar-branch-protection.js +418 -418
  259. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  260. package/scripts/field-report.js +199 -199
  261. package/scripts/generar-checklists-consolidados.js +273 -273
  262. package/scripts/generar-matriz-lenguajes.js +271 -271
  263. package/scripts/instalador.js +4 -1
  264. package/scripts/lib/artefactos-python.js +43 -43
  265. package/scripts/lib/auditar-invocaciones-comandos.js +104 -104
  266. package/scripts/lib/benchmark-metrics.js +160 -160
  267. package/scripts/lib/budget-enforcer.js +252 -252
  268. package/scripts/lib/ci-reader.js +193 -193
  269. package/scripts/lib/claude-sessions.js +1 -0
  270. package/scripts/lib/configurar-ci.js +380 -380
  271. package/scripts/lib/contadores-inventario.js +217 -217
  272. package/scripts/lib/detectar-host-swl.js +175 -175
  273. package/scripts/lib/detectar-stack-detallado.js +307 -307
  274. package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
  275. package/scripts/lib/detector-reglas-duplicadas.js +220 -220
  276. package/scripts/lib/diary-entry.js +234 -234
  277. package/scripts/lib/eval-metrics-store.js +218 -218
  278. package/scripts/lib/eval-quality.js +171 -171
  279. package/scripts/lib/eval-schemas.js +144 -144
  280. package/scripts/lib/eval-self-correct.js +106 -106
  281. package/scripts/lib/eval-validator.js +185 -185
  282. package/scripts/lib/evidencia-release.js +322 -322
  283. package/scripts/lib/gate-hooks-requires.js +249 -249
  284. package/scripts/lib/gate-licencias.js +212 -212
  285. package/scripts/lib/git-metricas.js +257 -257
  286. package/scripts/lib/jaccard-similarity.js +98 -98
  287. package/scripts/lib/longmemeval-runner.js +125 -125
  288. package/scripts/lib/metricas-dora.js +204 -204
  289. package/scripts/lib/npm-version.js +261 -261
  290. package/scripts/lib/paquetes-conocidos.js +50 -50
  291. package/scripts/lib/plan-lock.js +275 -275
  292. package/scripts/lib/pr-analyzer.js +399 -399
  293. package/scripts/lib/preservar-usuario.js +39 -5
  294. package/scripts/lib/prompt-builder.js +264 -264
  295. package/scripts/lib/reglas-globales-conocidas.json +180 -180
  296. package/scripts/lib/resolver-plan-fase.js +37 -37
  297. package/scripts/lib/rrf-fusion.js +175 -175
  298. package/scripts/lib/schema-version.js +164 -164
  299. package/scripts/lib/scoring-instintos.js +277 -277
  300. package/scripts/lib/semantic-search.js +252 -252
  301. package/scripts/lib/tool-cost-analyzer.js +1 -0
  302. package/scripts/limpiar-artefactos-python.js +131 -131
  303. package/scripts/migrar-csv-a-array.js +168 -168
  304. package/scripts/migrar-fase-dominio.js +200 -200
  305. package/scripts/publicar.js +497 -497
  306. package/scripts/run-eval.js +141 -141
  307. package/scripts/tui/componentes/selector-multi.js +189 -189
  308. package/scripts/tui/componentes/selector-unico.js +158 -158
  309. package/scripts/tui/ejecutores.js +375 -375
  310. package/scripts/tui/index.js +162 -162
  311. package/scripts/tui/lib/colores.js +129 -129
  312. package/scripts/tui/lib/render.js +264 -264
  313. package/scripts/tui/lib/teclas.js +113 -113
  314. package/scripts/tui/pantallas/inspect.js +173 -173
  315. package/scripts/tui/pantallas/install-wizard.js +334 -334
  316. package/scripts/tui/pantallas/menu-principal.js +52 -52
  317. package/scripts/tui/pantallas/progreso.js +274 -274
  318. package/scripts/tui/pantallas/resumen.js +132 -132
  319. package/scripts/tui/pantallas/uninstall-wizard.js +208 -208
  320. package/scripts/tui/pantallas/update-wizard.js +232 -232
  321. package/scripts/tui/pantallas/welcome.js +187 -187
  322. package/scripts/validar-userland-vacio.js +110 -110
  323. package/scripts/validar.js +1 -1
  324. package/scripts/verificar-release.js +51 -0
  325. package/hooks/lib/context-compressor.js +0 -657
  326. package/hooks/linea-estado.js +0 -326
  327. package/hooks/monitor-contexto.js +0 -373
@@ -1,326 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * Hook: linea-estado.js
6
- * Tipo: PostToolUse
7
- *
8
- * Muestra una línea de estado compacta con:
9
- * - Modelo activo
10
- * - Tarea actual (derivada del tool_name y parámetros)
11
- * - Directorio de trabajo abreviado
12
- * - Barra de progreso de contexto estimado
13
- *
14
- * Además escribe un archivo bridge en /tmp/swl-ctx-{session}.json
15
- * para que otros hooks (monitor-contexto, etc.) lean el estado.
16
- *
17
- * Integración con Claude Code hooks:
18
- * El hook no bloquea (exit 0 siempre).
19
- * Los mensajes informativos se escriben en stderr para que
20
- * Claude Code los muestre en la barra de estado o los ignore;
21
- * sin interferir con el flujo JSON en stdout.
22
- */
23
-
24
- const fs = require('fs');
25
- const path = require('path');
26
- const os = require('os');
27
-
28
- // ---------------------------------------------------------------------------
29
- // Constantes de configuración
30
- // ---------------------------------------------------------------------------
31
-
32
- /** Ancho de la barra de progreso del contexto (en caracteres de bloque). */
33
- const BARRA_ANCHO = 10;
34
-
35
- /**
36
- * Techo por defecto cuando no se detecta el modelo.
37
- * Mantiene la heurística histórica para compatibilidad.
38
- */
39
- const TOKENS_MAXIMOS_DEFAULT = 200_000;
40
-
41
- /**
42
- * Techos de contexto por modelo (en tokens).
43
- *
44
- * IMPORTANTE: las claves son prefijos del `model_id` reportado por Claude Code
45
- * en el evento del hook. La detección hace `modelId.includes(prefijo)` en orden;
46
- * los prefijos más específicos van primero para que `claude-opus-4-8` no
47
- * matchee accidentalmente con la entrada genérica `claude-opus`.
48
- *
49
- * Si el `model_id` contiene el sufijo `[1m]` (ej: `claude-opus-4-8[1m]`), el
50
- * techo se fuerza a 1M independientemente del modelo base — Anthropic usa
51
- * ese sufijo para indicar la variante de 1M context.
52
- */
53
- const TECHOS_POR_MODELO = {
54
- // Opus 4.8 — variante de 1M context (la actual por defecto)
55
- 'claude-opus-4-8': 1_000_000,
56
- // Opus 4.7 — variante de 1M context (histórica)
57
- 'claude-opus-4-7': 1_000_000,
58
- // Resto de modelos modernos: 200k context
59
- 'claude-opus-4-6': 200_000,
60
- 'claude-opus-4-5': 200_000,
61
- 'claude-sonnet-4-6': 200_000,
62
- 'claude-sonnet-4-5': 200_000,
63
- 'claude-haiku-4-5': 200_000,
64
- // Catch-all genéricos (orden: deben ir DESPUÉS de los específicos)
65
- 'claude-opus': 200_000,
66
- 'claude-sonnet': 200_000,
67
- 'claude-haiku': 200_000,
68
- };
69
-
70
- /**
71
- * Modelos conocidos y su etiqueta corta para la barra.
72
- * Clave: prefijo del model_id. Valor: etiqueta legible.
73
- */
74
- const ETIQUETAS_MODELO = {
75
- 'claude-opus': 'opus',
76
- 'claude-sonnet': 'sonnet',
77
- 'claude-haiku': 'haiku',
78
- };
79
-
80
- /**
81
- * Determina el techo de tokens del contexto según el modelo activo.
82
- *
83
- * Reglas (en orden):
84
- * 1. Si el `modelId` contiene `[1m]` → 1_000_000 (variante explícita 1M).
85
- * 2. Si el `modelId` matchea un prefijo de TECHOS_POR_MODELO → ese techo.
86
- * 3. Default → TOKENS_MAXIMOS_DEFAULT (200k).
87
- *
88
- * @param {string} modelId - Identificador completo del modelo, posiblemente
89
- * con sufijo de variante (ej: `claude-opus-4-8[1m]`).
90
- * @returns {number} Techo de tokens del contexto.
91
- */
92
- function techoTokens(modelId) {
93
- if (!modelId) return TOKENS_MAXIMOS_DEFAULT;
94
- const id = String(modelId);
95
- if (id.includes('[1m]') || id.includes('-1m')) return 1_000_000;
96
- for (const [prefijo, techo] of Object.entries(TECHOS_POR_MODELO)) {
97
- if (id.includes(prefijo)) return techo;
98
- }
99
- return TOKENS_MAXIMOS_DEFAULT;
100
- }
101
-
102
- // ---------------------------------------------------------------------------
103
- // Utilidades
104
- // ---------------------------------------------------------------------------
105
-
106
- /**
107
- * Construye la etiqueta corta del modelo activo.
108
- * @param {string} modelId - Identificador completo del modelo.
109
- * @returns {string}
110
- */
111
- function etiquetaModelo(modelId) {
112
- if (!modelId) return 'modelo?';
113
- for (const [prefijo, etiqueta] of Object.entries(ETIQUETAS_MODELO)) {
114
- if (modelId.includes(prefijo)) return etiqueta;
115
- }
116
- // Fallback: primeros 12 caracteres del modelo
117
- return String(modelId).slice(0, 12);
118
- }
119
-
120
- /**
121
- * Determina la tarea actual a partir del nombre de la herramienta
122
- * y sus parámetros de entrada.
123
- * @param {string} toolName - Nombre de la tool que acaba de ejecutarse.
124
- * @param {object} toolInput - Parámetros de entrada de la tool.
125
- * @returns {string}
126
- */
127
- function tareaActual(toolName, toolInput) {
128
- if (!toolName) return 'idle';
129
-
130
- const nombre = String(toolName).toLowerCase();
131
-
132
- switch (nombre) {
133
- case 'write':
134
- case 'edit': {
135
- const filePath = toolInput?.file_path || toolInput?.path || '';
136
- const base = path.basename(String(filePath));
137
- return `editando ${base || 'archivo'}`;
138
- }
139
- case 'read': {
140
- const filePath = toolInput?.file_path || toolInput?.path || '';
141
- const base = path.basename(String(filePath));
142
- return `leyendo ${base || 'archivo'}`;
143
- }
144
- case 'bash': {
145
- // Mostrar primeros 28 chars del comando, sin saltos de línea
146
- const cmd = String(toolInput?.command || '').replace(/\s+/g, ' ').slice(0, 28);
147
- return `bash: ${cmd || '...'}`;
148
- }
149
- case 'grep': {
150
- const patron = String(toolInput?.pattern || '').slice(0, 20);
151
- return `grep: ${patron || '...'}`;
152
- }
153
- case 'glob': {
154
- const patron = String(toolInput?.pattern || '').slice(0, 20);
155
- return `glob: ${patron || '...'}`;
156
- }
157
- case 'task':
158
- case 'agent':
159
- return `agente: ${String(toolInput?.description || '').slice(0, 25) || '...'}`;
160
- default:
161
- return nombre.slice(0, 30);
162
- }
163
- }
164
-
165
- /**
166
- * Abrevia una ruta de directorio para mostrar en la barra de estado.
167
- * Muestra sólo los últimos N segmentos.
168
- * @param {string} dirPath - Ruta completa.
169
- * @param {number} maxSegmentos - Número máximo de segmentos a mostrar.
170
- * @returns {string}
171
- */
172
- function abreviarDirectorio(dirPath, maxSegmentos = 2) {
173
- if (!dirPath) return '.';
174
- // Normalizar separadores en Windows
175
- const normalizado = String(dirPath).replace(/\\/g, '/');
176
- const partes = normalizado.split('/').filter(Boolean);
177
- if (partes.length <= maxSegmentos) return normalizado;
178
- return '…/' + partes.slice(-maxSegmentos).join('/');
179
- }
180
-
181
- /**
182
- * Construye la barra de progreso visual.
183
- * @param {number} porcentaje - Valor entre 0 y 100.
184
- * @returns {string} Ej: "████████░░ 80%"
185
- */
186
- function barraProgreso(porcentaje) {
187
- const pct = Math.max(0, Math.min(100, porcentaje));
188
- const llenos = Math.round((pct / 100) * BARRA_ANCHO);
189
- const vacios = BARRA_ANCHO - llenos;
190
- const barra = '█'.repeat(llenos) + '░'.repeat(vacios);
191
- return `${barra} ${pct}%`;
192
- }
193
-
194
- /**
195
- * Estima el porcentaje de contexto utilizado.
196
- * Claude Code no expone directamente los tokens consumidos en el hook,
197
- * por lo que usamos una heurística basada en el número de invocaciones
198
- * de herramientas acumuladas en la sesión cuando el campo `usage` no está.
199
- *
200
- * La función lee el archivo bridge previo para mantener el contador,
201
- * y lo incrementa en cada invocación.
202
- *
203
- * @param {string} bridgePath - Ruta del archivo bridge.
204
- * @param {number} tokensReportados - Tokens reportados por Claude (si existen).
205
- * @param {number} techo - Techo de tokens del modelo activo.
206
- * @returns {{ porcentaje: number, invocaciones: number, techo: number }}
207
- */
208
- function estimarContexto(bridgePath, tokensReportados, techo) {
209
- const techoEfectivo = (typeof techo === 'number' && techo > 0)
210
- ? techo
211
- : TOKENS_MAXIMOS_DEFAULT;
212
- let invocaciones = 1;
213
-
214
- // Leer estado previo del bridge
215
- try {
216
- const previo = JSON.parse(fs.readFileSync(bridgePath, 'utf8'));
217
- invocaciones = (previo.invocaciones || 0) + 1;
218
- } catch (_) {
219
- // Archivo no existe aún — primera invocación
220
- }
221
-
222
- // Si Claude reporta tokens usados (campo no-estándar, puede no existir)
223
- if (typeof tokensReportados === 'number' && tokensReportados > 0) {
224
- const pct = Math.min(100, Math.round((tokensReportados / techoEfectivo) * 100));
225
- return { porcentaje: pct, invocaciones, techo: techoEfectivo };
226
- }
227
-
228
- // Heurística: cada invocación consume ~500 tokens promedio (conversación + tool)
229
- const tokenesEstimados = invocaciones * 500;
230
- const pct = Math.min(99, Math.round((tokenesEstimados / techoEfectivo) * 100));
231
- return { porcentaje: pct, invocaciones, techo: techoEfectivo };
232
- }
233
-
234
- // ---------------------------------------------------------------------------
235
- // Entrypoint principal
236
- // ---------------------------------------------------------------------------
237
-
238
- // Solo conectar stdin cuando se ejecuta como script (hook),
239
- // no cuando se carga vía require() desde tests u otros módulos.
240
- if (require.main === module) iniciarHook();
241
-
242
- function iniciarHook() {
243
- let inputRaw = '';
244
-
245
- process.stdin.on('data', chunk => {
246
- inputRaw += chunk;
247
- });
248
-
249
- process.stdin.on('end', () => {
250
- try {
251
- const data = JSON.parse(inputRaw);
252
-
253
- // Extraer campos del evento de hook de Claude Code
254
- // Estructura documentada de PostToolUse:
255
- // data.session_id, data.model, data.tool_name, data.tool_input,
256
- // data.tool_result, data.usage (si existe)
257
- const sessionId = String(data.session_id || 'default');
258
- const modelId = String(data.model || '');
259
- const toolName = String(data.tool_name || '');
260
- const toolInput = data.tool_input || {};
261
- const tokensUsados = data.usage?.input_tokens ?? data.usage?.total_tokens ?? 0;
262
-
263
- // Ruta del archivo bridge compartido entre hooks
264
- const bridgePath = path.join(os.tmpdir(), `swl-ctx-${sessionId}.json`);
265
-
266
- // Resolver techo según modelo (Opus 4.8 con sufijo [1m] = 1M; resto 200k)
267
- const techo = techoTokens(modelId);
268
-
269
- // Calcular métricas
270
- const { porcentaje, invocaciones } = estimarContexto(bridgePath, tokensUsados, techo);
271
- const modeloLabel = etiquetaModelo(modelId);
272
- const tarea = tareaActual(toolName, toolInput);
273
- const directorio = abreviarDirectorio(process.cwd());
274
- const barra = barraProgreso(porcentaje);
275
-
276
- // Construir payload del bridge para otros hooks
277
- const bridgePayload = {
278
- sessionId,
279
- modelId,
280
- modeloLabel,
281
- tarea,
282
- directorio,
283
- porcentaje,
284
- invocaciones,
285
- tokensUsados,
286
- techoTokens: techo,
287
- timestamp: new Date().toISOString(),
288
- };
289
-
290
- // Escribir bridge (ignorar error si /tmp no existe en este entorno)
291
- try {
292
- fs.writeFileSync(bridgePath, JSON.stringify(bridgePayload, null, 2), 'utf8');
293
- } catch (errBridge) {
294
- // No bloquear el flujo si no se puede escribir el bridge
295
- // No se pudo escribir bridge — ignorar
296
- }
297
-
298
- // Construir la línea de estado como JSON válido para Claude Code
299
- const lineaEstado = `${modeloLabel} | ${tarea} | ${directorio} | ctx: ${barra}`;
300
-
301
- // PostToolUse hooks: no escribir nada a stdout para permitir
302
- // que Claude Code procese la respuesta sin interferencia.
303
-
304
- } catch (err) {
305
- // Si el hook falla por cualquier razón, nunca bloquear a Claude
306
- // No escribir a stderr — Claude Code lo reporta como hook error
307
- }
308
- });
309
- }
310
-
311
- if (require.main !== module) {
312
- module.exports = {
313
- techoTokens,
314
- estimarContexto,
315
- etiquetaModelo,
316
- tareaActual,
317
- abreviarDirectorio,
318
- barraProgreso,
319
- _internals: {
320
- TECHOS_POR_MODELO,
321
- TOKENS_MAXIMOS_DEFAULT,
322
- ETIQUETAS_MODELO,
323
- BARRA_ANCHO,
324
- },
325
- };
326
- }
@@ -1,373 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * Hook: monitor-contexto.js
6
- * Tipo: PostToolUse
7
- *
8
- * Lee el archivo bridge generado por linea-estado.js y emite advertencias
9
- * cuando el contexto estimado supera umbrales críticos.
10
- *
11
- * Umbrales por defecto (calibrados con evidencia empírica de
12
- * github.com/anthropics/claude-code issue #34685 — degradación detectable
13
- * ~20%, auto-compactación de Claude Code dispara ~40%, sweet spot 40-50%):
14
- *
15
- * ≥ 40%: WARNING — degradación detectable, evitar trabajo complejo
16
- * ≥ 55%: CRITICAL — detenerse, guardar estado, compactar
17
- * ≥ 35% en punto natural: sugerencia de compactación proactiva
18
- *
19
- * Override por env var (útil para experimentos o sesiones cortas):
20
- * SWL_CTX_WARNING (default 40)
21
- * SWL_CTX_CRITICAL (default 55)
22
- * SWL_CTX_PROACTIVA (default 35)
23
- *
24
- * Histórico: hasta v1.1.0 los umbrales eran 65/75/50. Se bajaron en v1.1.1
25
- * tras analizar el issue #34685 que documenta degradación al 20-40%.
26
- *
27
- * Debounce: solo emite aviso cada N_DEBOUNCE invocaciones de herramienta.
28
- * La escalación de severidad (pasar de WARNING a CRITICAL) bypasea el debounce.
29
- *
30
- * El hook nunca bloquea (siempre exit 0); sólo emite mensajes en stdout
31
- * como texto enriquecido que Claude Code puede mostrar al usuario.
32
- */
33
-
34
- const fs = require('fs');
35
- const path = require('path');
36
- const os = require('os');
37
-
38
- const { phase1_PruneToolResults, shouldCompress } = require('./lib/context-compressor');
39
-
40
- // ---------------------------------------------------------------------------
41
- // Constantes de configuración
42
- // ---------------------------------------------------------------------------
43
-
44
- /**
45
- * Lee un umbral numérico desde env var con default. Inválido o fuera de rango
46
- * (0-99) → usar default.
47
- */
48
- function umbralEnv(nombre, def) {
49
- const valor = parseInt(process.env[nombre] || '', 10);
50
- if (Number.isFinite(valor) && valor >= 0 && valor < 100) return valor;
51
- return def;
52
- }
53
-
54
- /**
55
- * Resuelve los tres umbrales aplicando defaults y validando el invariante de
56
- * ordenamiento: PROACTIVA < WARNING < CRITICAL.
57
- *
58
- * Si los env vars violan el orden (p.ej. WARNING=80 y CRITICAL=50, que haría
59
- * CRITICAL inalcanzable lógicamente) o PROACTIVA >= WARNING (haciendo la
60
- * sugerencia proactiva inalcanzable), se descarta la configuración inválida
61
- * y se usan los defaults para los tres. La fallback es silenciosa porque el
62
- * hook nunca debe romper el flujo principal.
63
- */
64
- function resolverUmbrales() {
65
- const defaults = { proactiva: 35, warning: 40, critical: 55 };
66
-
67
- const proactiva = umbralEnv('SWL_CTX_PROACTIVA', defaults.proactiva);
68
- const warning = umbralEnv('SWL_CTX_WARNING', defaults.warning);
69
- const critical = umbralEnv('SWL_CTX_CRITICAL', defaults.critical);
70
-
71
- if (proactiva < warning && warning < critical) {
72
- return { proactiva, warning, critical };
73
- }
74
- return defaults;
75
- }
76
-
77
- const UMBRALES = resolverUmbrales();
78
-
79
- /** Porcentaje de USO a partir del cual se emite WARNING. */
80
- const UMBRAL_WARNING = UMBRALES.warning;
81
-
82
- /** Porcentaje de USO a partir del cual se emite CRITICAL. */
83
- const UMBRAL_CRITICAL = UMBRALES.critical;
84
-
85
- /**
86
- * Número de invocaciones de herramienta entre avisos consecutivos del mismo nivel.
87
- * La escalación de severidad siempre bypasea este valor.
88
- */
89
- const N_DEBOUNCE = 5;
90
-
91
- /** Cada N invocaciones, sugerir compactacion si estamos en punto natural. */
92
- const N_COMPACTACION_PROACTIVA = 40;
93
-
94
- /** Umbral de uso para activar compactacion proactiva (antes del warning). */
95
- const UMBRAL_COMPACTACION_PROACTIVA = UMBRALES.proactiva;
96
-
97
- /**
98
- * Ruta del archivo de estado interno de este monitor.
99
- * Almacena cuándo se emitió el último aviso y con qué severidad.
100
- */
101
- function rutaEstadoMonitor(sessionId) {
102
- return path.join(os.tmpdir(), `swl-monitor-${sessionId}.json`);
103
- }
104
-
105
- // ---------------------------------------------------------------------------
106
- // Tipos de severidad
107
- // ---------------------------------------------------------------------------
108
- const SEVERIDAD = {
109
- NINGUNA: 0,
110
- WARNING: 1,
111
- CRITICAL: 2,
112
- };
113
-
114
- // ---------------------------------------------------------------------------
115
- // Utilidades
116
- // ---------------------------------------------------------------------------
117
-
118
- /**
119
- * Lee el archivo bridge de linea-estado.js.
120
- * @param {string} sessionId
121
- * @returns {object|null} Payload del bridge o null si no existe/falla.
122
- */
123
- function leerBridge(sessionId) {
124
- const bridgePath = path.join(os.tmpdir(), `swl-ctx-${sessionId}.json`);
125
- try {
126
- return JSON.parse(fs.readFileSync(bridgePath, 'utf8'));
127
- } catch (_) {
128
- return null;
129
- }
130
- }
131
-
132
- /**
133
- * Lee el estado interno previo del monitor.
134
- * @param {string} estadoPath
135
- * @returns {{ ultimaInvocacion: number, ultimaSeveridad: number }}
136
- */
137
- function leerEstadoMonitor(estadoPath) {
138
- try {
139
- const datos = JSON.parse(fs.readFileSync(estadoPath, 'utf8'));
140
- return {
141
- ultimaInvocacion: datos.ultimaInvocacion ?? 0,
142
- ultimaSeveridad: datos.ultimaSeveridad ?? SEVERIDAD.NINGUNA,
143
- compresionEmitida: datos.compresionEmitida ?? false,
144
- };
145
- } catch (_) {
146
- return { ultimaInvocacion: 0, ultimaSeveridad: SEVERIDAD.NINGUNA, compresionEmitida: false };
147
- }
148
- }
149
-
150
- /**
151
- * Persiste el estado del monitor.
152
- * @param {string} estadoPath
153
- * @param {object} estado
154
- */
155
- function guardarEstadoMonitor(estadoPath, estado) {
156
- try {
157
- fs.writeFileSync(estadoPath, JSON.stringify(estado, null, 2), 'utf8');
158
- } catch (_) {
159
- // Ignorar errores de escritura — no bloquear el flujo
160
- }
161
- }
162
-
163
- /**
164
- * Determina la severidad actual en función del porcentaje de uso.
165
- * @param {number} porcentajeUso - Porcentaje de contexto consumido (0-100).
166
- * @returns {number} Valor de SEVERIDAD.*
167
- */
168
- function calcularSeveridad(porcentajeUso) {
169
- if (porcentajeUso >= UMBRAL_CRITICAL) return SEVERIDAD.CRITICAL;
170
- if (porcentajeUso >= UMBRAL_WARNING) return SEVERIDAD.WARNING;
171
- return SEVERIDAD.NINGUNA;
172
- }
173
-
174
- /**
175
- * Detecta si la ultima operacion es un punto natural para compactar.
176
- * Puntos naturales:
177
- * - Despues de un git commit exitoso
178
- * - Despues de correr tests exitosamente
179
- * - Cuando tool_name es "Agent" (fin de subagente)
180
- * @param {object} data - Payload del hook
181
- * @returns {boolean}
182
- */
183
- function esPuntoNatural(data) {
184
- const toolName = String(data.tool_name || '');
185
- const toolInput = data.tool_input || {};
186
- const comando = String(toolInput.command || '');
187
-
188
- // Despues de un commit exitoso
189
- if (toolName === 'Bash' && /\bgit\s+commit\b/.test(comando)) return true;
190
-
191
- // Despues de correr tests
192
- if (toolName === 'Bash' && /\b(pytest|jest|vitest|go test|cargo test|dotnet test|phpunit|npm test)\b/.test(comando)) return true;
193
-
194
- // Fin de un subagente
195
- if (toolName === 'Agent') return true;
196
-
197
- return false;
198
- }
199
-
200
- /**
201
- * Genera el mensaje de sugerencia de compactacion proactiva.
202
- * @param {number} porcentajeUso
203
- * @param {number} invocaciones
204
- * @returns {string}
205
- */
206
- function generarMensajeCompactacion(porcentajeUso, invocaciones) {
207
- return [
208
- '',
209
- '--- SUGERENCIA DE COMPACTACION ---',
210
- ` Contexto: ${porcentajeUso}% usado | ${invocaciones} tool calls en sesion`,
211
- ' Punto natural de pausa detectado.',
212
- ' Considera ejecutar /compact o /swl:compactar para liberar contexto',
213
- ' antes de continuar con tareas complejas.',
214
- '',
215
- ].join('\n');
216
- }
217
-
218
- /**
219
- * Genera el mensaje de advertencia según severidad.
220
- * El mensaje se emite en stdout para que Claude Code lo procese
221
- * como feedback informativo dentro de la conversación.
222
- *
223
- * @param {number} severidad
224
- * @param {number} porcentajeUso
225
- * @param {number} porcentajeRestante
226
- * @returns {string}
227
- */
228
- function generarMensaje(severidad, porcentajeUso, porcentajeRestante) {
229
- const barra = '█'.repeat(Math.round(porcentajeUso / 10)) +
230
- '░'.repeat(10 - Math.round(porcentajeUso / 10));
231
-
232
- if (severidad === SEVERIDAD.CRITICAL) {
233
- return [
234
- '',
235
- '╔══════════════════════════════════════════════════════╗',
236
- '║ CONTEXTO CRÍTICO — ACCIÓN REQUERIDA ║',
237
- '╚══════════════════════════════════════════════════════╝',
238
- ` Contexto usado: ${barra} ${porcentajeUso}% (${porcentajeRestante}% restante)`,
239
- '',
240
- ' Acciones recomendadas:',
241
- ' 1. Guarda el estado actual con un commit atómico.',
242
- ' 2. Ejecuta /compact o solicita compactación del contexto.',
243
- ' 3. Divide la tarea en partes más pequeñas.',
244
- ' 4. No inicies trabajo complejo hasta compactar.',
245
- '',
246
- ].join('\n');
247
- }
248
-
249
- if (severidad === SEVERIDAD.WARNING) {
250
- return [
251
- '',
252
- '⚠ ADVERTENCIA DE CONTEXTO',
253
- ` Contexto usado: ${barra} ${porcentajeUso}% (${porcentajeRestante}% restante)`,
254
- ' Evitar tareas complejas. Considera compactar pronto.',
255
- '',
256
- ].join('\n');
257
- }
258
-
259
- return '';
260
- }
261
-
262
- // ---------------------------------------------------------------------------
263
- // Entrypoint principal
264
- // ---------------------------------------------------------------------------
265
-
266
- let inputRaw = '';
267
-
268
- process.stdin.on('data', chunk => {
269
- inputRaw += chunk;
270
- });
271
-
272
- process.stdin.on('end', () => {
273
- try {
274
- const data = JSON.parse(inputRaw);
275
-
276
- const sessionId = String(data.session_id || 'default');
277
- const estadoPath = rutaEstadoMonitor(sessionId);
278
-
279
- // Leer el bridge de linea-estado
280
- const bridge = leerBridge(sessionId);
281
- if (!bridge) {
282
- // Sin bridge no hay datos — salir silenciosamente
283
- return;
284
- }
285
-
286
- const porcentajeUso = bridge.porcentaje ?? 0;
287
- const invocacionesActual = bridge.invocaciones ?? 0;
288
- const porcentajeRestante = 100 - porcentajeUso;
289
- const severidadActual = calcularSeveridad(porcentajeUso);
290
-
291
- // Leer estado previo del monitor (necesario tanto para WARNING/CRITICAL como para proactiva)
292
- const estadoPrevio = leerEstadoMonitor(estadoPath);
293
- const { ultimaInvocacion, ultimaSeveridad } = estadoPrevio;
294
-
295
- // Resetear flag de compresión si el contexto bajó (post-compactación real)
296
- if (severidadActual < SEVERIDAD.CRITICAL && estadoPrevio.compresionEmitida) {
297
- guardarEstadoMonitor(estadoPath, {
298
- ...estadoPrevio,
299
- compresionEmitida: false,
300
- timestamp: new Date().toISOString(),
301
- });
302
- }
303
-
304
- if (severidadActual !== SEVERIDAD.NINGUNA) {
305
- // Determinar si debemos emitir aviso:
306
- // a) Escalación de severidad → siempre emitir (bypasea debounce)
307
- // b) Mismo nivel → solo si han pasado N_DEBOUNCE invocaciones
308
- const esEscalacion = severidadActual > ultimaSeveridad;
309
- const invocacionesDesdeUltimoAviso = invocacionesActual - ultimaInvocacion;
310
- const debounceExpirado = invocacionesDesdeUltimoAviso >= N_DEBOUNCE;
311
-
312
- const debeEmitir = esEscalacion || debounceExpirado;
313
-
314
- if (debeEmitir) {
315
- const mensaje = generarMensaje(severidadActual, porcentajeUso, porcentajeRestante);
316
- if (mensaje) {
317
- // Emitir advertencia como systemMessage (stderr causa hook error)
318
- process.stdout.write(JSON.stringify({ systemMessage: mensaje }));
319
- }
320
-
321
- // Actualizar estado del monitor
322
- guardarEstadoMonitor(estadoPath, {
323
- ultimaInvocacion: invocacionesActual,
324
- ultimaSeveridad: severidadActual,
325
- compresionEmitida: estadoPrevio.compresionEmitida || false,
326
- timestamp: new Date().toISOString(),
327
- });
328
- }
329
- }
330
-
331
- // --- Compresión automática Fase 1 (sin LLM) al llegar a CRITICAL ---
332
- // Solo emitir UNA VEZ por sesión para evitar spam de banners
333
- if (severidadActual === SEVERIDAD.CRITICAL && shouldCompress(porcentajeUso)) {
334
- const yaEmitioBanner = estadoPrevio.compresionEmitida === true;
335
- if (!yaEmitioBanner) {
336
- try {
337
- const mensajeCompresion = [
338
- '',
339
- '--- COMPRESION AUTOMATICA (Fases 1+1b) ---',
340
- ` Contexto: ${porcentajeUso}% usado`,
341
- ' Fase 1: tool results antiguos podados por posición.',
342
- ' Fase 1b: resultados de Read/Bash/Grep/Glob > 15 turnos comprimidos por TTL.',
343
- ' Para compresión avanzada (Fases 2-4), ejecuta /swl:compactar',
344
- ' o pide: "comprime el contexto preservando las decisiones clave".',
345
- '',
346
- ].join('\n');
347
- process.stdout.write(JSON.stringify({ systemMessage: mensajeCompresion }));
348
- // Marcar como emitido para no repetir
349
- guardarEstadoMonitor(estadoPath, {
350
- ...estadoPrevio,
351
- ultimaInvocacion: invocacionesActual,
352
- ultimaSeveridad: severidadActual,
353
- compresionEmitida: true,
354
- timestamp: new Date().toISOString(),
355
- });
356
- } catch (_) {}
357
- }
358
- }
359
-
360
- // --- Compactacion proactiva: sugerir en punto natural antes de alcanzar WARNING ---
361
- if (severidadActual === SEVERIDAD.NINGUNA &&
362
- porcentajeUso >= UMBRAL_COMPACTACION_PROACTIVA &&
363
- invocacionesActual % N_COMPACTACION_PROACTIVA === 0 &&
364
- esPuntoNatural(data)) {
365
- const mensajeCompactacion = generarMensajeCompactacion(porcentajeUso, invocacionesActual);
366
- process.stdout.write(JSON.stringify({ systemMessage: mensajeCompactacion }));
367
- }
368
-
369
- } catch (err) {
370
- // El hook nunca bloquea por errores internos
371
- // No escribir a stderr — Claude Code lo reporta como hook error
372
- }
373
- });