Founding Member: $499/mes de por vida
Trucos técnicos

Un temporal con dueño para cada sesión

Dos hooks para Claude Code: cada sesión recibe su propia carpeta temporal (TMPDIR, TMP y TEMP apuntan ahí) y un candado frena los mktemp que escribirían fuera de ella. Lo desechable solo se borra con recibo, nunca a ciegas.

HookPremiumTrucos técnicoscross platform

🌱 Empieza aquí · Un temporal con dueño para cada sesión

Esta semilla le da a cada conversación de tu asistente su propia carpeta de archivos temporales. Lo que la sesión genera de paso (pruebas, descargas a medias, borradores) cae ahí y no en la carpeta temporal que comparte todo tu equipo. Así se sabe de quién es cada cosa, y lo desechable solo se borra cuando tú lo pides y hay prueba de que nadie lo está usando.

Para instalarla

Abre esta carpeta con Claude Code y dile a tu asistente:

"Siembra esta semilla 🌱"

Con eso basta de tu parte. Tu asistente hace todo lo demás y al final te dice cómo comprobar que quedó funcionando. Vas a ver algunas palabras en inglés en la pantalla; ni caso, él te guía.

¿Otras formas de pedírselo? "instala esta semilla", "pon a funcionar el temporal de sesión", "haz el setup de esto". Te entiende igual.


Para el asistente que siembra 🤖

Este componente son dos hooks (ganchos de automatización) de Claude Code más un programa de apoyo:

  • kernel/temporal-de-sesion.sh — gancho SessionStart (se dispara al iniciar, reanudar, limpiar o compactar una sesión). Abre el espacio de la sesión y escribe TMPDIR, TMP y TEMP en el archivo CLAUDE_ENV_FILE, que Claude Code carga en cada comando Bash de esa sesión.
  • kernel/registro-temporal.py — el registro que abre espacios, emite el recibo y recoge lo liberado. El gancho de arranque lo llama; tú lo llamas al cierre (paso 4).
  • kernel/mktemp-con-plantilla-guard.py — gancho PreToolUse para Write|Edit|MultiEdit. Bloquea (exit 2) que un guion .sh, .bash o .zsh introduzca un mktemp que escribiría fuera de TMPDIR.
  • kernel/settings.ejemplo.json — el bloque de registro de los dos ganchos.

Requisitos: bash y python3 en el PATH (en Windows, Git Bash, que es el que usa Claude Code). Para emitir recibos hace falta lsof (viene en macOS; en Linux, paquete lsof). No ejecutes los ganchos por tu cuenta fuera de las pruebas de este documento.

1. Copia los archivos

macOS / Linux / Windows con Git Bash (corre este bloque desde la carpeta de la semilla):

SEMILLA="$(pwd)"
DESTINO="$HOME/.claude/hooks/temporal-de-sesion"
mkdir -p "$DESTINO"
cp "$SEMILLA/kernel/temporal-de-sesion.sh" "$SEMILLA/kernel/registro-temporal.py" \
   "$SEMILLA/kernel/mktemp-con-plantilla-guard.py" "$DESTINO/"
chmod +x "$DESTINO/temporal-de-sesion.sh" "$DESTINO/registro-temporal.py" "$DESTINO/mktemp-con-plantilla-guard.py"
ls -la "$DESTINO"; echo "exit=$?"

Windows (PowerShell), desde la carpeta de la semilla:

$Destino = Join-Path $env:USERPROFILE ".claude\hooks\temporal-de-sesion"
New-Item -ItemType Directory -Force -Path $Destino | Out-Null
Copy-Item -Path "kernel\temporal-de-sesion.sh","kernel\registro-temporal.py","kernel\mktemp-con-plantilla-guard.py" -Destination $Destino
Get-ChildItem $Destino

2. Registra los dos ganchos en settings.json

Agrega el contenido de kernel/settings.ejemplo.json a ~/.claude/settings.json sin borrar los ganchos que ya existan. Este bloque lo hace con respaldo previo y sin duplicar si se corre dos veces (corre desde la carpeta de la semilla):

SEMILLA="$(pwd)"
AJUSTES="$HOME/.claude/settings.json"
mkdir -p "$HOME/.claude"; [ -f "$AJUSTES" ] || echo '{}' > "$AJUSTES"
cp "$AJUSTES" "$AJUSTES.respaldo-temporal-de-sesion"
python3 - "$AJUSTES" "$SEMILLA/kernel/settings.ejemplo.json" <<'PY'
import json, sys
ajustes, ejemplo = sys.argv[1], sys.argv[2]
datos = json.load(open(ajustes))
nuevos = json.load(open(ejemplo))["hooks"]
hooks = datos.setdefault("hooks", {})
for evento, grupos in nuevos.items():
    lista = hooks.setdefault(evento, [])
    for grupo in grupos:
        if grupo not in lista:
            lista.append(grupo)
json.dump(datos, open(ajustes, "w"), indent=2, ensure_ascii=False)
print("ganchos registrados en", ajustes)
PY
echo "exit=$?"

Si la persona usa CLAUDE_CONFIG_DIR para mover su carpeta de Claude Code, cambia $HOME/.claude por esa carpeta en los dos bloques y en las rutas de settings.ejemplo.json.

Ajustes opcionales (variables de entorno; si no se definen, valen los valores por omisión):

  • TEMPORAL_RAIZ → <TU-RUTA> donde vivirán los espacios. Default: ~/.cache/temporal-de-sesion/sesiones (Windows: %LOCALAPPDATA%\temporal-de-sesion\sesiones). Debe ser una ruta real, sin enlaces simbólicos y fuera de carpetas sincronizadas con la nube.
  • TEMPORAL_ATAJO_BASE y TEMPORAL_ATAJO_PREFIJO → dónde nace el enlace corto de TMPDIR (default /private/tmp/ses-<8 caracteres del id> en macOS, /tmp/ses-… en Linux).
  • TEMPORAL_LSOF → ruta de lsof si no está en /usr/sbin, /usr/bin ni en el PATH.
  • TEMPORAL_TRANSCRIPTS → carpeta de transcripts de Claude Code con la que el reporte separa sesiones «con pantalla» (abiertas en la terminal) de «sin pantalla» (automáticas). Default: ~/.claude/projects.

3. Verificación post-siembra (obligatoria)

a) Los archivos quedaron en su lugar:

DESTINO="$HOME/.claude/hooks/temporal-de-sesion"
ls -la "$DESTINO/temporal-de-sesion.sh" "$DESTINO/registro-temporal.py" "$DESTINO/mktemp-con-plantilla-guard.py"; echo "exit=$?"
grep -c "temporal-de-sesion" "$HOME/.claude/settings.json"

Salida esperada: tres líneas con los archivos, exit=0 y un número 2 (las dos entradas registradas). Si sale No such file or directory, repite el paso 1; si sale 0, repite el paso 2.

b) Prueba de comportamiento (simula el arranque de una sesión con un id de prueba y una raíz desechable; no toca tus sesiones reales. pwd -P quita los enlaces simbólicos de la ruta, porque el registro rechaza raíces que los crucen, como /tmp en macOS):

DESTINO="$HOME/.claude/hooks/temporal-de-sesion"
PRUEBA="$(cd "$(mktemp -d "${TMPDIR:-/tmp}/prueba-temporal.XXXXXX")" && pwd -P)"
export TEMPORAL_RAIZ="$PRUEBA/sesiones" TEMPORAL_ATAJO_BASE="$PRUEBA" CLAUDE_ENV_FILE="$PRUEBA/entorno"
: > "$CLAUDE_ENV_FILE"
echo '{"session_id":"prueba0001-semilla"}' | bash "$DESTINO/temporal-de-sesion.sh"; echo "gancho=$?"
cat "$CLAUDE_ENV_FILE"
echo '{"tool_name":"Write","tool_input":{"file_path":"/x/guion.sh","content":"D=$(mktemp -d)"}}' \
  | python3 "$DESTINO/mktemp-con-plantilla-guard.py" >/dev/null 2>&1; echo "candado=$?"
rm -rf "$PRUEBA"

Salida esperada: gancho=0, tres líneas export TMPDIR=…/ses-prueba00/, export TMP=…/borrador y export TEMP=…/borrador, y candado=2 (el candado frenó el mktemp sin plantilla). En Windows no hay enlace corto: TMPDIR sale con la ruta larga en forma /c/....

Prueba señuelo (lo que NO debe pasar): sin CLAUDE_ENV_FILE el gancho no cambia nada.

DESTINO="$HOME/.claude/hooks/temporal-de-sesion"
echo '{"session_id":"prueba0002-semilla"}' | env -u CLAUDE_ENV_FILE bash "$DESTINO/temporal-de-sesion.sh"; echo "gancho=$?"

Salida esperada: Temporal de sesión: no hay CLAUDE_ENV_FILE; TMPDIR sin cambio. y gancho=0.

c) En una sesión nueva de verdad, pide a la persona que abra Claude Code y te diga "muéstrame mi TMPDIR". Corre echo "$TMPDIR": debe terminar en /ses-<8 caracteres>/ (o en .../borrador/ si el atajo no se pudo crear).

4. Al cierre de cada sesión: el recibo

Nada se borra solo. Cuando la persona cierre su trabajo y te pida "emite el recibo del temporal" (o "limpia el temporal de esta sesión"), corre dentro de esa misma sesión:

REGISTRO="$HOME/.claude/hooks/temporal-de-sesion/registro-temporal.py"
python3 -B "$REGISTRO" recibo --session "$CLAUDE_CODE_SESSION_ID"; echo "recibo=$?"
python3 -B "$REGISTRO" recoger --session "$CLAUDE_CODE_SESSION_ID"; echo "recoger=$?"

El recibo aparta el borrador actual como borrador.gen-N, deja uno vacío para lo que siga, y revisa con lsof que nadie tenga nada abierto adentro. Solo si lo puede afirmar, lo marca liberada y recoger lo borra contra su manifiesto. Si algo sigue abierto o lsof no está, sale recibo=1 con el motivo y la carpeta se conserva. en-curso/ nunca se toca. El recibo de otra sesión se niega: hay que reanudarla y emitirlo desde ella. Si sale recibo=2, el registro no pudo evaluar (por ejemplo, esa sesión no tiene espacio porque arrancó antes de instalar la semilla, o CLAUDE_CODE_SESSION_ID llegó vacío, que es la variable con la que Claude Code le dice a Bash qué sesión es) y no se tocó nada.

Para ver cuánto ocupa lo que sigue sin recibo:

REGISTRO="$HOME/.claude/hooks/temporal-de-sesion/registro-temporal.py"
python3 -B "$REGISTRO" reporte

Cuando todo pase, avísale a la persona en una línea qué gana con esto.


✅ ¿Cómo sé que funcionó?

Cada vez que abras una conversación, sus archivos temporales van a vivir en una carpeta con su nombre, no revueltos con los de todo el equipo.

Lo único que te toca a ti: al terminar una conversación, dile a tu asistente "emite el recibo del temporal". Él comprueba que nadie use esos archivos y solo entonces los borra. Si no se lo pides, no se borra nada: se queda guardado, y tu computadora ya no lo limpia por su cuenta como hacía con su carpeta temporal.

Si quedaron borradores de sesiones anteriores sin recibo, al arrancar vas a ver un renglón como "Temporal con dueño: 0.42 GB sin recibo en 3 sesiones · 0.42 GB de 3 con pantalla", con el comando para revisarlos («con pantalla» son las conversaciones que abriste tú; «sin pantalla», las que corrieron solas). Para soltar uno, reanuda esa conversación y pide ahí su recibo. Si una conversación ya no se puede reanudar, su carpeta se queda; la semilla no trae un comando para borrarla por ti, y decidir borrarla a mano es cosa tuya.

Y si tu asistente intenta escribir un guion con un mktemp que se saldría de esa carpeta, el candado lo frena y le muestra cómo escribirlo bien. 🌱

¿Quieres implementarla con acompañamiento?

Agenda una asesoría directa con JP y aterrízala en tu caso · desde $600 MXN.

Agendar una asesoría