Comprobación automática de código con Hooks: calidad garantizada al escribir

Usa PostToolUse Hooks para disparar lint automáticamente tras cada escritura de Claude. El código de salida 2 devuelve los errores a Claude para corrección automática.


Claude Code escribe código rápido, pero no siempre recuerda ejecutar el lint. Le pides que "revise mientras escribe" y a veces lo hace, a veces no. Los Hooks resuelven este problema: trasladan la responsabilidad de la comprobación de código de Claude al sistema.

Este artículo se centra en una sola cosa: usar PostToolUse Hooks para disparar automáticamente comprobaciones después de que Claude escriba código, asegurando que cada cambio de archivo cumpla tus estándares.


El enfoque: PostToolUse + filtrado por tipo de archivo

La lógica de un Hook de comprobación de código es sencilla:

  1. Claude llama a Write o Edit para escribir un archivo
  2. El Hook intercepta este evento y obtiene la ruta del archivo
  3. Según la extensión, decide qué herramienta de comprobación ejecutar
  4. Comprobación exitosa: código de salida 0, Claude continúa
  5. Comprobación fallida: código de salida 2, los errores se envían a Claude para que los corrija

El código de salida 2 es la clave: hace que Claude reciba el error y lo corrija automáticamente, formando un ciclo "escribir→comprobar→corregir".


Configuración básica: proyectos de un solo lenguaje

Ruby (RuboCop)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
          }
        ]
      }
    ]
  }
}

JavaScript/TypeScript (ESLint)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" =~ \\.(js|ts|jsx|tsx)$ ]] && npx eslint --fix \"$file\" || true'"
          }
        ]
      }
    ]
  }
}

Python (ruff)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.py ]] && ruff check --fix \"$file\" && ruff format \"$file\" || true'"
          }
        ]
      }
    ]
  }
}

Configuración avanzada: proyectos multilenguaje

Los proyectos reales suelen mezclar lenguajes. Mover la lógica de comprobación a un script independiente es más fácil de mantener que acumular comandos en el JSON.

Crear .claude/hooks/lint.sh:

#!/bin/bash
set -e

input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // empty')

[[ -z "$file" ]] && exit 0
[[ ! -f "$file" ]] && exit 0

ext="${file##*.}"

case "$ext" in
  rb)    rubocop -A "$file" --no-color -q ;;
  js|ts|jsx|tsx) npx eslint --fix "$file" --quiet ;;
  py)    ruff check --fix "$file"; ruff format "$file" ;;
  go)    gofmt -w "$file"; golangci-lint run "$file" 2>&1 ;;
  *)     exit 0 ;;
esac

Referenciar el script en settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{ "type": "command", "command": "bash .claude/hooks/lint.sh" }]
      }
    ]
  }
}

Hacer que Claude corrija automáticamente

Código de salida 2 + stderr es lo que activa la autocorrección de Claude:

run_lint() {
  local output exit_code
  output=$(rubocop "$file" --no-color 2>&1)
  exit_code=$?
  if [[ $exit_code -ne 0 ]]; then
    echo "$output" >&2
    exit 2
  fi
  rubocop -A "$file" --no-color -q
}

Excluir archivos que no deben comprobarse

skip_patterns=("vendor/" "node_modules/" "db/schema.rb" ".min.js" "_test.go")
for pattern in "${skip_patterns[@]}"; do
  [[ "$file" == *"$pattern"* ]] && exit 0
done

Resultado en la práctica

Con esta configuración, el flujo de trabajo es:

  1. Pides a Claude que escriba un archivo Ruby
  2. Claude lo escribe con la herramienta Write
  3. El Hook ejecuta rubocop -A automáticamente — detecta 3 problemas
  4. RuboCop corrige 2 automáticamente; el 3º requiere criterio humano
  5. El 3º error se envía a Claude vía stderr
  6. Claude corrige el código, el Hook se dispara de nuevo
  7. Segunda comprobación: éxito

No necesitas ejecutar ningún comando de comprobación manualmente.


Global vs. nivel de proyecto

Global (~/.claude/settings.json): Cuando usas las mismas reglas en todos los proyectos.

Nivel de proyecto (.claude/settings.json en git): Recomendado para equipos. Cualquiera que abra el repositorio obtiene la configuración correcta automáticamente.

Ambos coexisten; los hooks de proyecto se fusionan con los globales y ambos se ejecutan.


Resumen

La comprobación automática de código con Hooks se reduce a tres pasos:

  1. PostToolUse + matcher "Write|Edit" para interceptar escrituras de archivos
  2. Obtener la ruta del archivo y enrutarla al linter correcto según la extensión
  3. En caso de fallo, salir con código 2 para que Claude vea el error y lo corrija

Empieza con un lenguaje. Cuando funcione, añade más. Haz commit de los scripts en .claude/hooks/ para compartirlos con el equipo.