Advanced (Actualizado: 1/6/2026)

Guía de Claude Code Hooks: controles seguros antes y después de trabajar

Guía práctica de Claude Code Hooks para principiantes: bloquear comandos peligrosos, guardar logs, formatear y ejecutar tests.

Guía de Claude Code Hooks: controles seguros antes y después de trabajar

Claude Code Hooks son comprobaciones automáticas que se ejecutan antes o después de que Claude Code haga una acción. En lugar de recordar en cada prompt “no ejecutes comandos peligrosos”, “formatea el archivo” o “corre los tests”, puedes convertir esas reglas en parte del workflow del proyecto.

El modelo mental más útil para empezar es este: PreToolUse es el freno, PostToolUse es el mantenimiento, UserPromptSubmit es el registro de entrada y Stop es la revisión antes de cerrar. Hooks no sustituyen revisión humana, permisos ni CI. Sirven para que las comprobaciones repetidas sean más difíciles de olvidar.

Esta guía sigue la referencia oficial de Claude Code Hooks y la documentación oficial de settings. Para preparar bien el contexto del proyecto, mira también buenas prácticas de CLAUDE.md. Para una capa de seguridad más amplia, combínalo con la guía de permisos de Claude Code.

Cuatro eventos que conviene aprender primero

Claude Code tiene muchos eventos de Hooks, pero la mayoría de equipos obtiene valor con estos cuatro.

EventoUso principalEjemplo
UserPromptSubmitAntes de que el prompt llegue a ClaudeGuardar la solicitud, detectar secretos, añadir contexto ligero
PreToolUseJusto antes de ejecutar una herramientaBloquear comandos Bash destructivos o acciones de producción
PostToolUseDespués de una herramienta exitosaEjecutar formatter, lint o tests relacionados
StopCuando Claude va a terminar la respuestaRevisar conflictos, guardar resumen, recordar verificaciones pendientes

Usa PreToolUse cuando la acción no debe ocurrir. Usa PostToolUse cuando la acción ya ocurrió y quieres limpiar o verificar. Usa UserPromptSubmit para entender la calidad de las peticiones. Usa Stop para no cerrar una sesión con un problema obvio.

Las reglas compartidas suelen vivir en .claude/settings.json. Los experimentos personales encajan mejor en .claude/settings.local.json, porque no deberían entrar al repositorio. En un equipo, documenta qué Hooks bloquean, cuáles solo registran información y quién los mantiene.

Configuración mínima para copiar

La siguiente configuración guarda prompts, bloquea comandos Bash peligrosos, ejecuta verificaciones después de editar archivos y guarda un resumen al final.

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/log-prompt.mjs"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/block-dangerous-command.mjs"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/run-quality-checks.mjs",
            "timeout": 120
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node .claude/hooks/stop-summary.mjs"
          }
        ]
      }
    ]
  }
}

El detalle importante es la estructura anidada. Primero eliges el evento, luego un grupo con matcher cuando aplica, y dentro defines handlers como type: "command". Todavía existen ejemplos antiguos con command directamente junto a matcher; no son el patrón que conviene copiar hoy.

Use case 1: bloquear comandos peligrosos con PreToolUse

El primer use case real es seguridad en Bash. Cuando el agente trabaja rápido, el momento crítico es antes de que se ejecute un comando destructivo. PreToolUse puede leer la entrada de Bash y devolver una decisión de denegación.

Guarda este archivo como .claude/hooks/block-dangerous-command.mjs.

import fs from "node:fs";

const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const command = String(input.tool_input?.command || "");

const denyPatterns = [
  /rm\s+-rf\s+(\/|\*|\.|\$HOME)/i,
  /cat\s+\.env(\.|$|\s)/i,
  /printenv/i,
  /aws\s+.*\s+delete-/i,
  /gcloud\s+.*\s+delete/i,
  /kubectl\s+delete\s+(namespace|deployment|secret)/i,
  /DROP\s+DATABASE/i
];

const matched = denyPatterns.find((pattern) => pattern.test(command));

if (matched) {
  console.log(JSON.stringify({
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: `Blocked by project hook: ${matched}`
    }
  }));
  process.exit(0);
}

Esto no es un producto de seguridad completo. Las expresiones regulares pueden ser incompletas. Aun así, evitan errores muy comunes: mostrar .env, borrar carpetas amplias, eliminar recursos cloud, destruir recursos de Kubernetes o ejecutar SQL destructivo. Para producción, añade permisos de Claude Code, ramas protegidas, CI y aprobación humana.

Use case 2: registrar prompts con UserPromptSubmit

El segundo use case es observabilidad del prompt. Muchas sesiones fallidas no empiezan por un modelo débil, sino por una petición vaga: “arréglalo”, “límpialo”, “hazlo mejor”. Si guardas la solicitud, puedes analizar qué tipo de prompt produce buen trabajo y cuál produce ruido.

Guarda este archivo como .claude/hooks/log-prompt.mjs.

import fs from "node:fs";
import path from "node:path";

const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const dir = path.join(process.cwd(), ".claude", "hook-logs");
fs.mkdirSync(dir, { recursive: true });

const record = {
  time: new Date().toISOString(),
  event: input.hook_event_name,
  cwd: input.cwd,
  prompt: input.prompt || input.user_prompt || ""
};

fs.appendFileSync(
  path.join(dir, "prompts.jsonl"),
  JSON.stringify(record) + "\n",
  "utf8"
);

Mantén este log local al principio. Un prompt puede contener nombres de clientes, URLs internas, tokens pegados por error o contexto comercial sensible. Añade .claude/hook-logs/ a .gitignore y define retención, acceso y responsable antes de convertirlo en workflow de equipo.

Use case 3: ejecutar format y test después de editar

El tercer use case es el ciclo de calidad diario. Después de que Claude escriba o edite un archivo, ejecuta formatter y una verificación pequeña. Así no tienes que repetir “formatea y prueba” en cada prompt.

Guarda este archivo como .claude/hooks/run-quality-checks.mjs.

import fs from "node:fs";
import { execFileSync } from "node:child_process";

const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const filePath = String(input.tool_input?.file_path || "");

const isSourceFile = /\.(js|jsx|ts|tsx|css|md|mdx|json)$/.test(filePath);
if (!isSourceFile) process.exit(0);

const run = (cmd, args) => {
  try {
    return execFileSync(cmd, args, {
      cwd: process.cwd(),
      encoding: "utf8",
      stdio: ["ignore", "pipe", "pipe"]
    });
  } catch (error) {
    return String(error.stdout || "") + String(error.stderr || "");
  }
};

const messages = [];
messages.push(run("npx", ["prettier", "--write", filePath]));

if (/\.(js|jsx|ts|tsx)$/.test(filePath)) {
  messages.push(run("npm", ["test", "--", "--runInBand"]));
}

console.log(JSON.stringify({
  hookSpecificOutput: {
    hookEventName: "PostToolUse",
    additionalContext: messages.join("\n").slice(-4000)
  }
}));

En un monorepo grande, no ejecutes toda la suite en cada escritura. Primero aplica format, luego tests relacionados, y deja los checks pesados para Hooks async o CI. También conviene recortar la salida que devuelves a Claude para no llenar el contexto con logs interminables.

Use case 4: revisar la sesión con Stop

Stop se dispara cuando Claude está por terminar. Es buen lugar para guardar un resumen o bloquear solo estados claramente incompletos, como conflictos de Git sin resolver.

import fs from "node:fs";
import { execFileSync } from "node:child_process";

const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");

if (input.stop_hook_active) {
  process.exit(0);
}

let status = "";
try {
  status = execFileSync("git", ["status", "--short"], {
    cwd: process.cwd(),
    encoding: "utf8"
  });
} catch {
  process.exit(0);
}

if (status.includes("UU ")) {
  console.error("Git conflict remains. Resolve conflicts before finishing.");
  process.exit(2);
}

fs.mkdirSync(".claude/hook-logs", { recursive: true });
fs.appendFileSync(
  ".claude/hook-logs/stop.jsonl",
  JSON.stringify({
    time: new Date().toISOString(),
    lastAssistantMessage: input.last_assistant_message || "",
    gitStatus: status.slice(0, 2000)
  }) + "\n"
);

Ten cuidado con Stop. Si bloqueas demasiado, el agente puede quedar intentando terminar una y otra vez. Revisa stop_hook_active, mantén condiciones estrechas y empieza registrando más que bloqueando.

Pitfall: errores comunes y cómo evitarlos

El primer pitfall es creer que Hooks reemplaza permisos. Un command hook corre con los permisos de tu usuario del sistema. Si está mal escrito, puede leer secretos o borrar archivos. Valida entradas, evita traversal de rutas, no toques .env ni .git, y cita rutas correctamente.

El segundo pitfall es meter demasiada carga sincrónica. Formatear un archivo está bien. Ejecutar build, suite completa, tests de navegador y deploy después de cada edición suele ser excesivo. Mantén checks rápidos en PostToolUse, mueve lo pesado a async o CI.

El tercer pitfall es guardar logs sin política. Los logs ayudan a depurar, pero también pueden guardar información sensible. Define ubicación, retención, acceso y exclusión de Git antes de hacerlo estándar.

El cuarto pitfall es confundir matcher con lógica de negocio. matcher: "Bash" solo envía eventos Bash al handler. La decisión real debe revisar el comando completo dentro del script.

Nota de verificación estilo Masa

Al probar este patrón, la mayor mejora vino de devolver format y test a Claude mediante additionalContext. Claude puede corregir el siguiente paso sin que una persona copie logs al chat. El bloqueo de comandos peligrosos también aporta, no porque sea perfecto, sino porque frena los errores obvios en el instante correcto.

Para un equipo nuevo, lo introduciría en tres semanas: primero logs de prompts, luego format tras edición, y después una lista pequeña de patrones Bash prohibidos. Ese orden reduce fricción y construye confianza.

Si quieres empaquetarlo para tu equipo, la página de training de Claude Code reúne permisos, CLAUDE.md, Hooks y review workflow. Para trabajo individual, también puede convertirse en una checklist de Gumroad para iniciar proyectos con una base repetible.

Resumen

Claude Code Hooks son guardrails prácticos. Usa UserPromptSubmit para entender las solicitudes, PreToolUse para bloquear acciones riesgosas, PostToolUse para verificar cambios y Stop para no cerrar con problemas evidentes.

Empieza pequeño. Un Hook simple que nadie desactiva vale más que una automatización ambiciosa que estorba al segundo día.

#Claude Code #Hooks #automation #security #testing
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.