Gli hook di Claude Code sono comandi shell definiti dall’utente che vengono eseguiti in vari punti del ciclo di vita di Claude Code. Gli hook forniscono controllo deterministico sul comportamento di Claude Code, garantendo che certe azioni avvengano sempre piuttosto che affidarsi all’LLM per scegliere di eseguirle.

Per la documentazione di riferimento sugli hook, vedi Riferimento hook.

Esempi di casi d’uso per gli hook includono:

  • Notifiche: Personalizza come ricevi notifiche quando Claude Code è in attesa del tuo input o permesso per eseguire qualcosa.
  • Formattazione automatica: Esegui prettier sui file .ts, gofmt sui file .go, ecc. dopo ogni modifica di file.
  • Logging: Traccia e conta tutti i comandi eseguiti per conformità o debug.
  • Feedback: Fornisci feedback automatizzato quando Claude Code produce codice che non segue le convenzioni della tua codebase.
  • Permessi personalizzati: Blocca modifiche ai file di produzione o directory sensibili.

Codificando queste regole come hook piuttosto che istruzioni di prompt, trasformi suggerimenti in codice a livello di app che viene eseguito ogni volta che è previsto che venga eseguito.

Devi considerare le implicazioni di sicurezza degli hook mentre li aggiungi, perché gli hook vengono eseguiti automaticamente durante il loop dell’agente con le credenziali del tuo ambiente corrente. Ad esempio, codice di hook malevolo può esfiltare i tuoi dati. Rivedi sempre l’implementazione dei tuoi hook prima di registrarli.

Per le migliori pratiche di sicurezza complete, vedi Considerazioni sulla Sicurezza nella documentazione di riferimento degli hook.

Panoramica degli Eventi Hook

Claude Code fornisce diversi eventi hook che vengono eseguiti in punti diversi del flusso di lavoro:

  • PreToolUse: Viene eseguito prima delle chiamate agli strumenti (può bloccarle)
  • PostToolUse: Viene eseguito dopo il completamento delle chiamate agli strumenti
  • UserPromptSubmit: Viene eseguito quando l’utente invia un prompt, prima che Claude lo elabori
  • Notification: Viene eseguito quando Claude Code invia notifiche
  • Stop: Viene eseguito quando Claude Code finisce di rispondere
  • SubagentStop: Viene eseguito quando i task del subagent si completano
  • PreCompact: Viene eseguito prima che Claude Code stia per eseguire un’operazione di compattazione
  • SessionStart: Viene eseguito quando Claude Code inizia una nuova sessione o riprende una sessione esistente
  • SessionEnd: Viene eseguito quando la sessione di Claude Code termina

Ogni evento riceve dati diversi e può controllare il comportamento di Claude in modi diversi.

Guida Rapida

In questa guida rapida, aggiungerai un hook che registra i comandi shell che Claude Code esegue.

Prerequisiti

Installa jq per l’elaborazione JSON nella riga di comando.

Passo 1: Apri la configurazione degli hook

Esegui il comando slash /hooks e seleziona l’evento hook PreToolUse.

Gli hook PreToolUse vengono eseguiti prima delle chiamate agli strumenti e possono bloccarle fornendo feedback a Claude su cosa fare diversamente.

Passo 2: Aggiungi un matcher

Seleziona + Add new matcher… per eseguire il tuo hook solo sulle chiamate dello strumento Bash.

Digita Bash per il matcher.

Puoi usare * per abbinare tutti gli strumenti.

Passo 3: Aggiungi l’hook

Seleziona + Add new hook… e inserisci questo comando:

jq -r '"\(.tool_input.command) - \(.tool_input.description // "No description")"' >> ~/.claude/bash-command-log.txt

Passo 4: Salva la tua configurazione

Per la posizione di archiviazione, seleziona User settings poiché stai registrando nella tua directory home. Questo hook si applicherà quindi a tutti i progetti, non solo al tuo progetto corrente.

Poi premi Esc fino a tornare al REPL. Il tuo hook è ora registrato!

Passo 5: Verifica il tuo hook

Esegui /hooks di nuovo o controlla ~/.claude/settings.json per vedere la tua configurazione:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
          }
        ]
      }
    ]
  }
}

Passo 6: Testa il tuo hook

Chiedi a Claude di eseguire un comando semplice come ls e controlla il tuo file di log:

cat ~/.claude/bash-command-log.txt

Dovresti vedere voci come:

ls - Lists files and directories

Altri Esempi

Per un esempio di implementazione completo, vedi l’esempio di validatore di comandi bash nella nostra codebase pubblica.

Hook di Formattazione Codice

Formatta automaticamente i file TypeScript dopo la modifica:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\"; fi; }"
          }
        ]
      }
    ]
  }
}

Hook di Formattazione Markdown

Correggi automaticamente i tag di linguaggio mancanti e i problemi di formattazione nei file markdown:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/markdown_formatter.py"
          }
        ]
      }
    ]
  }
}

Crea .claude/hooks/markdown_formatter.py con questo contenuto:

#!/usr/bin/env python3
"""
Formattatore markdown per l'output di Claude Code.
Corregge i tag di linguaggio mancanti e i problemi di spaziatura preservando il contenuto del codice.
"""
import json
import sys
import re
import os

def detect_language(code):
    """Rilevamento del linguaggio con il miglior sforzo dal contenuto del codice."""
    s = code.strip()
    
    # Rilevamento JSON
    if re.search(r'^\s*[{\[]', s):
        try:
            json.loads(s)
            return 'json'
        except:
            pass
    
    # Rilevamento Python
    if re.search(r'^\s*def\s+\w+\s*\(', s, re.M) or \
       re.search(r'^\s*(import|from)\s+\w+', s, re.M):
        return 'python'
    
    # Rilevamento JavaScript  
    if re.search(r'\b(function\s+\w+\s*\(|const\s+\w+\s*=)', s) or \
       re.search(r'=>|console\.(log|error)', s):
        return 'javascript'
    
    # Rilevamento Bash
    if re.search(r'^#!.*\b(bash|sh)\b', s, re.M) or \
       re.search(r'\b(if|then|fi|for|in|do|done)\b', s):
        return 'bash'
    
    # Rilevamento SQL
    if re.search(r'\b(SELECT|INSERT|UPDATE|DELETE|CREATE)\s+', s, re.I):
        return 'sql'
        
    return 'text'

def format_markdown(content):
    """Formatta il contenuto markdown con rilevamento del linguaggio."""
    # Correggi i recinti di codice senza etichetta
    def add_lang_to_fence(match):
        indent, info, body, closing = match.groups()
        if not info.strip():
            lang = detect_language(body)
            return f"{indent}```{lang}\n{body}{closing}\n"
        return match.group(0)
    
    fence_pattern = r'(?ms)^([ \t]{0,3})```([^\n]*)\n(.*?)(\n\1```)\s*$'
    content = re.sub(fence_pattern, add_lang_to_fence, content)
    
    # Correggi le righe vuote eccessive (solo fuori dai recinti di codice)
    content = re.sub(r'\n{3,}', '\n\n', content)
    
    return content.rstrip() + '\n'

# Esecuzione principale
try:
    input_data = json.load(sys.stdin)
    file_path = input_data.get('tool_input', {}).get('file_path', '')
    
    if not file_path.endswith(('.md', '.mdx')):
        sys.exit(0)  # Non è un file markdown
    
    if os.path.exists(file_path):
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        formatted = format_markdown(content)
        
        if formatted != content:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(formatted)
            print(f"✓ Corretta formattazione markdown in {file_path}")
    
except Exception as e:
    print(f"Errore nella formattazione markdown: {e}", file=sys.stderr)
    sys.exit(1)

Rendi lo script eseguibile:

chmod +x .claude/hooks/markdown_formatter.py

Questo hook automaticamente:

  • Rileva i linguaggi di programmazione nei blocchi di codice senza etichetta
  • Aggiunge tag di linguaggio appropriati per l’evidenziazione della sintassi
  • Corregge le righe vuote eccessive preservando il contenuto del codice
  • Elabora solo file markdown (.md, .mdx)

Hook di Notifica Personalizzata

Ricevi notifiche desktop quando Claude ha bisogno di input:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'In attesa del tuo input'"
          }
        ]
      }
    ]
  }
}

Hook di Protezione File

Blocca le modifiche ai file sensibili:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 -c \"import json, sys; data=json.load(sys.stdin); path=data.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(p in path for p in ['.env', 'package-lock.json', '.git/']) else 0)\""
          }
        ]
      }
    ]
  }
}

Scopri di più

  • Per la documentazione di riferimento sugli hook, vedi Riferimento hook.
  • Per le migliori pratiche di sicurezza complete e le linee guida di sicurezza, vedi Considerazioni sulla Sicurezza nella documentazione di riferimento degli hook.
  • Per i passaggi di risoluzione dei problemi e le tecniche di debug, vedi Debug nella documentazione di riferimento degli hook.