No deja leer ni tocar archivos .env ni correr rm -rf. Si la guardia falla, frena igual.

Los tres mods de la clase «Mods de Claude Code» de la Academia de Nicolás Dubois. Un mod es código que corre adentro de Claude Code: puede frenar algo antes de que pase, dibujar en pantalla o sumar un comando.
| Mod | Qué hace |
|---|---|
| guardia | No deja leer ni tocar archivos .env (sí el .env.example) ni correr rm -rf. Si la guardia misma falla, frena igual. |
| reloj-cache | Arriba del prompt, cuántos minutos le quedan a la caché y cuántos tokens guarda, con un botón Compactar. Avisa cuando faltan 5 minutos. La duración (60 min) y el aviso (5 min) se cambian en /config. |
| grabando | /grabando prende y apaga el modo grabación: en pantalla tapa mails, teléfonos y montos, en tus mensajes, en las respuestas y en lo que devuelven las herramientas. Se acuerda si estaba prendido. |
En la terminal, adentro de Claude Code (cambiá guardia por reloj-cache o grabando):
/plugin install guardia --marketplace licnicolasdubois/mods-academia
Te pregunta si querés agregar el marketplace (y) y en qué alcance instalarlo: para todos tus proyectos o para uno solo.
Desde la línea de comandos:
claude plugin install guardia --marketplace licnicolasdubois/mods-academia
En la app de escritorio, donde /plugin no anda: el + al lado del prompt → Plugins → Add marketplace con licnicolasdubois/mods-academia → Add plugin.
Cada mod trae sus pruebas:
claude plugin validate guardia
claude plugin test guardia
.catch para que, si falla, frene en vez de dejar pasar.grabando tapa lo que ves en pantalla, no lo que lee Claude: sirve para grabar o compartir pantalla, no es una protección de datos.hooks/register.ts 30 lines1import type { Register } from 'claude-code'
2
3// .env and .env.<anything>, except .env.example (the template that is meant to be shared).
4const ENV_FILE = /(^|\/)\.env(\.(?!example$)[\w.-]+)?$/
5const ENV_IN_COMMAND = /(^|[\s'"=/])\.env(\.(?!example\b)[\w.-]+)?(?=$|[\s'";|&)])/
6// rm with both -r and -f, in any order or spelling: -rf, -fr, -Rf, -r -f, --recursive --force.
7const RM_RF = /\brm\b(?=[^;&|]*\s-(?:[a-zA-Z]*[rR][a-zA-Z]*|-recursive)\b)(?=[^;&|]*\s-(?:[a-zA-Z]*f[a-zA-Z]*|-force)\b)/
8
9export const register: Register = on => {
10 on('tool.call', { tool: ['Read', 'Edit', 'Write'] }, ($, e, next) =>
11 ENV_FILE.test(e.file_path)
12 ? { deny: `guardia: ${e.file_path} está protegido. Las claves no se leen ni se tocan desde Claude.` }
13 : next(e),
14 ).catch(($, e, next) =>
15 next.called ? next(e) : { deny: 'guardia: la guardia falló, así que frena por las dudas.' },
16 )
17
18 on('tool.call', { tool: 'Bash' }, ($, e, next) => {
19 if (RM_RF.test(e.command)) {
20 return { deny: 'guardia: rm -rf está bloqueado. Si de verdad hay que borrar, hacelo vos a mano.' }
21 }
22 if (ENV_IN_COMMAND.test(e.command)) {
23 return { deny: 'guardia: ese comando toca un archivo .env, y las claves no se leen desde Claude.' }
24 }
25 return next(e)
26 }).catch(($, e, next) =>
27 next.called ? next(e) : { deny: 'guardia: la guardia falló, así que frena por las dudas.' },
28 )
29}
30