Claude Code Hooks: la guida completa (2026)

Claude Code Hooks: la guida completa (2026)

Una skill può essere ignorata. Non è un difetto delle skill, è tutto il design: Claude legge la descrizione, decide se il task attuale corrisponde, e carica il corpo solo se pensa di sì. La maggior parte delle volte quel giudizio è corretto. A volte non lo è, e il task in cui conta di più, la code review appena prima di un merge, la voce di log che deve esistere a prescindere da tutto, è esattamente il task in cui "probabilmente" non basta.

Gli hook sono l'altra metà di Claude Code. Un hook è un comando shell che l'harness esegue quando un evento specifico scatta, che una skill l'avrebbe pensato o no. Nessun giudizio del modello si mette tra l'evento e il comando. Gira, ogni volta, in ordine, e il suo exit code può persino fermare Claude sul colpo. Se hai mai voluto dire "formatta sempre questo file dopo una modifica" o "non far mai toccare a Claude questa cartella", un hook è lo strumento fatto apposta per quella frase.

Questa guida copre cosa sono gli hook, lo schema di settings.json dietro di loro, sei ricette che puoi incollare oggi stesso, come hook e skill lavorano insieme, e i modi di fallimento che ti mangiano un pomeriggio se non sai dove guardare.

Cos'è davvero un hook

Claude Code lancia eventi con nome durante una sessione: prima che uno strumento giri, dopo che uno strumento è girato, quando Claude finisce di rispondere, quando comparirebbe una notifica. Un hook collega un comando shell a uno di quegli eventi, filtrato opzionalmente su strumenti specifici. L'harness esegue il tuo comando, gli passa il contesto come JSON su stdin, poi legge l'exit code per decidere cosa succede dopo.

Gli eventi che userai più spesso:

  • PreToolUse — scatta prima che una chiamata a uno strumento venga eseguita. Un hook qui può bloccare la chiamata del tutto.
  • PostToolUse — scatta dopo che una chiamata a uno strumento finisce. Utile per formattazione, test o logging di ciò che è appena successo.
  • Stop — scatta quando Claude finisce il suo turno e sta per restituirti il controllo.
  • Notification — scatta quando Claude Code mostrerebbe una notifica di sistema (richieste di permesso, prompt di inattività).
  • UserPromptSubmit — scatta quando invii un messaggio, prima che Claude lo veda.

Questo è il meccanismo. Il motivo per cui conta è la garanzia che dà, una garanzia che una skill strutturalmente non può dare.

Il modello mentale: deterministico vs discrezionale

Questa è l'unica idea che vale la pena ricordare se non ricordi altro da questa guida.

Una skill è discrezionale. Claude legge la sua descrizione all'inizio di una sessione, e in seguito decide, in base alla tua richiesta, se caricarla e seguirla. Le skill buone scattano in modo affidabile, ma "affidabile" resta una probabilità, non una garanzia. Claude può fraintendere un prompt ambiguo, o due skill possono avere descrizioni sovrapposte che confondono l'abbinamento, un modo di fallire che copriamo più a fondo nella nostra guida sul perché le skill non scattano.

Un hook è deterministico. Non chiede a Claude se deve girare. Non legge una descrizione e giudica la rilevanza. L'harness vede l'evento, e il comando gira, punto. Se l'evento è PostToolUse sullo strumento Edit, il tuo formattatore gira dopo ogni modifica, inclusa quella che Claude ha fatto pensando a tutt'altro.

Questa differenza si mappa direttamente su quando scegliere quale:

Skill Hook
Gira quando Claude lo giudica rilevante Ogni volta che l'evento scatta
Può essere saltato Sì, da un abbinamento sbagliato o un contesto affollato No
Adatto per Giudizio, struttura, "come fare bene X" Applicazione forzata, "X deve sempre succedere"
Modo di fallire Mancato scatto silenzioso Exit code cattivo silenzioso, o blocco di tutto

Se la frase che stai applicando inizia con "Claude dovrebbe sempre..." o "Claude non deve mai...", vuoi un hook. Se inizia con "quando Claude sta facendo X, dovrebbe affrontarlo come..." vuoi una skill. Formattare il codice dopo ogni modifica è un hook; scrivere Python idiomatico è una skill. Bloccare i commit su main è un hook; strutturare un buon messaggio di commit è una skill.

Anatomia di un hook in settings.json

Gli hook vivono sotto la chiave hooks in .claude/settings.json (a livello di progetto) o ~/.claude/settings.json (a livello utente), un file diverso da CLAUDE.md ed è bene non confonderli: CLAUDE.md è prosa che Claude legge, settings.json è configurazione che l'harness esegue. Se non hai ancora configurato nessuno dei due, la nostra guida a CLAUDE.md e il nostro percorso di setup completo coprono il resto dello stack in cui vive questo file. Ecco un esempio di hook minimo ma completo, annotato:

{
  "hooks": {
    // Il nome dell'evento — PreToolUse, PostToolUse, Stop, Notification, ecc.
    "PostToolUse": [
      {
        // matcher filtra quali chiamate a strumenti attivano questo hook.
        // Omettilo (o usa "*") per abbinare ogni strumento.
        "matcher": "Edit|Write",
        "hooks": [
          {
            // "command" è attualmente l'unico tipo di hook.
            "type": "command",
            // Il comando shell da eseguire. Riceve il JSON dell'evento su stdin.
            "command": "npx prettier --write \"$(echo $CLAUDE_TOOL_INPUT | jq -r .file_path)\"",
            // Opzionale: termina il comando se resta bloccato.
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Alcune cose che vale la pena sottolineare perché fanno inciampare la gente:

Il campo matcher opera sul nome dello strumento, non sui percorsi dei file o sul contenuto. "Edit|Write" corrisponde agli strumenti Edit e Write; "Bash" corrisponde alle chiamate shell. Se devi filtrare per percorso file o contenuto del comando, fallo dentro il tuo script leggendo il payload JSON, non nel matcher.

Ogni chiave di evento contiene un array di blocchi matcher, e ogni blocco matcher contiene un array di comandi hook, quindi puoi collegare più comandi a un matcher, o un comando a più matcher, senza duplicare la configurazione.

Il comando riceve il payload dell'evento come JSON su stdin: nome dello strumento, input dello strumento, e per PostToolUse, il risultato dello strumento. Un hook che agisce sul file specifico in modifica legge quel JSON invece di dare per scontato che la directory di lavoro della shell racconti tutta la storia.

Gli exit code portano significato. Exit 0 significa "va bene, continua". Un exit diverso da zero su un hook PreToolUse blocca la chiamata allo strumento e restituisce stderr a Claude come motivo. Un exit diverso da zero su PostToolUse viene solo loggato; lo strumento è già girato, quindi non c'è più nulla da bloccare.

Sei ricette che puoi usare oggi

Sono deliberatamente strette. Copia il blocco, aggiusta il comando, e conferma che fa quello che ti aspetti su un file usa-e-getta prima di fidartene su lavoro vero.

1. Formattazione automatica dopo ogni modifica

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npx prettier --write . --ignore-unknown"
          }
        ]
      }
    ]
  }
}

Esegue Prettier dopo ogni modifica o scrittura. Per repo grandi, sostituisci il generico . con un percorso derivato dal JSON di input dell'hook così formatti solo il file toccato.

2. Blocca le modifiche a percorsi protetti

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/guard_paths.py"
          }
        ]
      }
    ]
  }
}

guard_paths.py legge il percorso del file dallo stdin JSON, lo controlla contro una denylist (migrations/, .env, infra/prod/), ed esce con 1 e un messaggio su stderr se corrisponde. È la cosa più vicina a un vero confine di permesso che Claude Code abbia.

3. Esegui i test dopo modifiche al codice sorgente

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test -- --onlyChanged --silent"
          }
        ]
      }
    ]
  }
}

Dà a Claude un segnale immediato quando una modifica rompe un test, invece di aspettare che tu te ne accorga in fase di revisione. Tieni il comando di test stretto (--onlyChanged, un sottoinsieme veloce) o questo diventa la ricetta sei della sezione "quando non usarli" più sotto.

4. Notifica desktop quando Claude finisce

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Specifico di macOS (sostituisci con notify-send su Linux). Utile una volta che inizi a far girare turni autonomi più lunghi e smetti di guardare il terminale tutto il tempo.

5. Logga ogni comando bash

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> \"$CLAUDE_PROJECT_DIR/.claude/bash-history.log\""
          }
        ]
      }
    ]
  }
}

Una traccia di audit che non dipende dal fatto che tu ricordi di controllare il transcript. Su una macchina condivisa o un repo con un requisito di compliance, questo è quasi obbligatorio.

6. Gate di lint prima del commit

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/block_bad_commit.sh"
          }
        ]
      }
    ]
  }
}

block_bad_commit.sh legge lo stdin, controlla se il comando è un git commit, e in tal caso esegue prima il tuo linter, uscendo con un codice diverso da zero se fallisce. Questo trasforma "per favore fai il lint prima di committare" da una richiesta che Claude potrebbe dimenticare a una regola che non può aggirare.

FREE STARTER PACK

Stai configurando gli hook insieme alle tue prime skill? Ti mandiamo le nostre 3 skill con il punteggio più alto più la checklist di installazione che usiamo prima di ogni test SkillProof. Gratis.

Scarica il pacchetto gratuito

Hook e skill insieme

Non sono strumenti in competizione; le configurazioni migliori usano entrambi per ciò in cui ciascuno è bravo. Un esempio pratico: un team che abbiamo testato voleva ogni commit scritto nel loro stile di casa, modo imperativo, un prefisso di ambito, un corpo che spiega il perché, e voleva anche che i commit venissero bloccati se il diff toccava una migrazione di database senza un file di rollback corrispondente.

La parte di stile è giudizio. Cosa conta come un buon "perché" varia in base alla modifica, e non c'è uno script shell che scriva prosa buona in modo affidabile. Quello è il lavoro di una skill: qualcosa come Git Workflow Coach caricato ogni volta che Claude sta per fare un commit, insegnando la struttura e dando esempi di messaggi di commit buoni contro pigri. Claude la legge, applica giudizio, e scrive un messaggio che si adatta al pattern senza essere un riempimento di template. Se il team esegue anche un rigoroso red-green-refactor, Test-Driven Development è lo stesso tipo di aggiunta di giudizio-non-legge: modella come Claude affronta il lavoro, un hook non può fare quella parte.

La regola sulla migrazione non è giudizio, è legge: o il file di rollback esiste o non esiste, e il team non voleva che "Claude ha deciso che questa non ne aveva bisogno" fosse un'opzione. Quello è l'hook PreToolUse della ricetta sei, adattato per controllare il file abbinato invece di eseguire un linter, bloccando la chiamata git commit del tutto se manca.

Facendoli girare insieme ottieni un buon messaggio di commit che è anche garantito passare il controllo di migrazione, perché la skill gestisce la parte che ha bisogno di un cervello e l'hook gestisce la parte che ha bisogno di un muro. Nessuno dei due sostituisce l'altro. La skill non può garantire la conformità, e un hook che scrive "fix: varie modifiche" su ogni commit sarebbe inutile. La nostra pagina migliori skill di coding classifica il lato giudizio di questo abbinamento per punteggio testato, se stai scegliendo la prima skill da far girare insieme ai tuoi hook.

Debuggare gli hook

Gli hook falliscono in silenzio più spesso che rumorosamente. Ecco cosa di solito si rompe:

Le virgolette. I comandi degli hook sono stringhe shell dentro stringhe JSON, quindi una " non sfuggita rompe il parsing del JSON prima ancora che il tuo comando giri. Nel dubbio, metti la logica vera in un file di script e fai in modo che il comando dell'hook lo invochi soltanto (bash .claude/hooks/my-hook.sh) invece di scrivere inline un one-liner complesso.

Exit code che non significano quello che pensi. Uno script hook che incontra un errore non correlato (dipendenza mancante, permesso negato) esce con codice diverso da zero esattamente come un hook che vuole deliberatamente bloccare. Se un hook PreToolUse inizia a bloccare ogni chiamata a strumento e non l'hai scritto per essere così rigoroso, controlla se lo script sta effettivamente fallendo invece che giudicando.

Assunzioni su PATH. Gli hook girano in un ambiente shell che potrebbe non corrispondere al tuo terminale interattivo. Un comando che funziona bene quando lo digiti tu può fallire dentro un hook perché nvm, un virtualenv, o uno strumento installato via plugin di shell non è nel PATH in quel contesto. Usa percorsi assoluti ai binari, o fai il source dell'ambiente giusto all'inizio dello script.

Assunzioni silenziose su stdin. Se il tuo script si aspetta JSON su stdin e non lo riceve, perché l'hai testato eseguendolo direttamente invece che pipandogli un payload di esempio, si comporterà diversamente sotto l'harness rispetto a come faceva sul tuo terminale.

Timeout. Un hook senza timeout che resta bloccato blocca l'intero turno. Imposta un timeout esplicito su qualsiasi cosa tocchi la rete o un sottoprocesso lento.

Quando non usare gli hook

Gli hook sono economici da scrivere e facili da usare troppo. Il modo di fallire non è un hook che fa la cosa sbagliata, è un hook che fa la cosa giusta troppo spesso. Un hook PostToolUse che esegue la tua intera suite di test dopo ogni singola modifica trasforma una modifica di cinque secondi in un'attesa di due minuti, ripetuta per ogni modifica in una sessione che ne fa venti.

La regola pratica: se il comando di un hook impiega più di uno o due secondi, restringi il matcher, restringi cosa controlla, o spostalo su un evento meno frequente. Testare-a-ogni-modifica diventa testare-a-ogni-scrittura diventa testare-prima-del-commit man mano che il controllo diventa più costoso. Adatta il costo dell'hook a quanto spesso il suo evento scatta, e considera se una skill, che carica solo contesto e non esegue un processo, sia adatta meglio per qualsiasi cosa non sia strettamente applicazione forzata.

Vale anche la pena non ricorrere a un hook per risolvere il problema di trigger di una skill. Se una skill non scatta quando dovrebbe, la soluzione è una descrizione migliore, non un hook, dato che gli hook eseguono comandi shell e non possono caricare il contenuto di una skill. Per quel modo di fallire, vedi perché le skill non scattano.

SKILLPROOF PACK

Abbinare gli hook alle skill giuste è la maggior parte di una buona configurazione di Claude Code. Il Developer Toolkit include le nostre skill di coding con il punteggio più alto, pre-verificate per conflitti di trigger, così la metà "skill" di questo abbinamento è già fatta per te.

Prendi il Developer Toolkit — $10

FAQ

Gli hook rallentano ogni sessione di Claude Code?

Solo gli eventi a cui li colleghi, e solo per quanto tempo impiega il tuo comando. Un hook su PostToolUse per Edit gira una volta per modifica; un formattatore veloce è impercettibile, una suite di test completa si sente su ogni modifica, che è il caso coperto sopra sotto "quando non usare gli hook".

Un hook può fermare Claude dal fare qualcosa del tutto?

Sì, è a questo che servono gli hook PreToolUse. Esci con codice diverso da zero e la chiamata allo strumento viene bloccata prima di girare, con stderr tipicamente restituito a Claude come motivo. Questo è il meccanismo dietro la ricetta due (percorsi protetti) e la ricetta sei (gate di lint).

Dove metto la configurazione dei miei hook, impostazioni di progetto o utente?

A livello di progetto (.claude/settings.json, committato) se deve applicarsi a chiunque su quella codebase: formattazione, percorsi protetti, controlli di migrazione. A livello utente (~/.claude/settings.json) per una preferenza personale, come la notifica desktop nella ricetta quattro.

Qual è la differenza tra un hook e una skill che dice "formatta sempre il codice"?

L'hook gira davvero sempre. Una skill che dice a Claude di formattare sempre il codice resta un'istruzione che Claude legge e decide se seguire; è una spinta forte, non una garanzia, e compete con altre cose nel contesto per l'attenzione in un dato turno. Se "sempre" è un requisito e non una preferenza, usa un hook.

Il mio hook non gira affatto. Qual è la prima cosa da controllare?

Conferma che il file di impostazioni sia JSON valido (una virgola finale o una virgoletta non sfuggita possono disabilitare in silenzio l'intero blocco hooks) e che il nome dell'evento e il matcher siano scritti esattamente come previsto; entrambi sono case-sensitive. Dopo, controlla se hai modificato le impostazioni di progetto mentre la sessione legge quelle utente, o viceversa.

★ 9.6/10 × 3

Lo starter pack gratuito

I 3 skill con i nostri punteggi di test più alti, più la checklist di installazione: il setup che metteremmo su una macchina appena formattata. Gratis, via email.

Una email con il pack + un breve digest settimanale con i nuovi risultati dei test. Puoi disiscriverti quando vuoi.