Gli hook di Claude Code sono comandi (o chiamate HTTP, prompt e qualche altro tipo di handler) che Claude Code esegue automaticamente in punti precisi di una sessione, ad esempio prima che venga eseguito un tool, quando serve un permesso o quando Claude finisce di rispondere. Li definisci sotto la chiave hooks in un file di impostazioni come ~/.claude/settings.json. Ogni hook riceve su stdin un JSON con i dati dell'evento e può rispondere tramite l'exit code o un oggetto JSON su stdout: così può scrivere log, inviare notifiche, bloccare un'azione o rispondere al posto tuo.
01Com'è fatto un hook
La configurazione di un hook ha tre livelli:
- L'evento hook, ad esempio
PreToolUseoStop. - Un gruppo matcher, che filtra quando scatta, ad esempio "solo per il tool Bash".
- Uno o più handler, cioè ciò che viene effettivamente eseguito.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}
Questo esegue uno script di lint dopo che Claude scrive o modifica un file. Per aggiungere un secondo evento, aggiungi una chiave allo stesso livello dentro lo stesso oggetto hooks.
02Dove si trovano gli hook
~/.claude/settings.jsonTutti i tuoi progettiNo, solo sulla tua macchina.claude/settings.jsonUn progettoSì, si può fare commit.claude/settings.local.jsonUn progettoNohooks/hooks.json di un pluginFinché il plugin è attivoSìGli hook di file diversi si sommano, non si sostituiscono. Digita /hooks in Claude Code per vedere tutti gli hook attivi e da dove arrivano.
In una sessione interattiva, Claude Code non esegue gli hook, nemmeno i tuoi, finché non accetti la finestra di fiducia del workspace per quella cartella.
03Tipi di handler
La maggior parte degli hook è "type": "command", che esegue un comando shell. Ce ne sono altri quattro:
http: invia il JSON dell'evento in POST a un URL e legge il body della risposta.mcp_tool: chiama un tool su un server MCP che hai configurato.prompt: fa una domanda singola a un modello Claude e legge una decisione in JSON.agent: avvia un subagent che può leggere file prima di decidere (sperimentale).
Gli hook di tipo command girano con tutti i permessi del tuo utente, tramite sh -c su macOS e Linux e Git Bash (o PowerShell) su Windows. Aggiungi "async": true per eseguirne uno in background; in quel caso però non può più prendere decisioni.
04Matcher
Il matcher stabilisce quali occorrenze di un evento eseguono l'hook. Il valore confrontato dipende dall'evento: il nome del tool per gli eventi dei tool, il tipo di notifica per Notification, il modo in cui è partita la sessione per SessionStart e così via.
"*",""o l'assenza di matcher corrispondono a tutto.- Lettere, cifre,
_,-, spazi,|e,vengono trattati come nomi esatti.Edit|Writecorrisponde a uno qualsiasi dei due tool. - Tutto il resto è un'espressione regolare JavaScript, quindi
mcp__memory__.*corrisponde a tutti i tool di un server MCP.
I matcher distinguono maiuscole e minuscole. Alcuni eventi, tra cui Stop e UserPromptSubmit, ignorano i matcher. Per gli eventi dei tool, un campo if con una regola di permesso come "Bash(git *)" restringe ulteriormente un singolo handler.
05Input: cosa riceve il tuo hook
Ogni hook riceve su stdin un JSON con alcuni campi comuni, tra cui session_id, transcript_path, cwd e hook_event_name, più i campi specifici dell'evento. Un hook PreToolUse per una chiamata Bash, ad esempio, riceve anche tool_name e tool_input.command. Leggilo con jq o con qualsiasi parser JSON:
#!/bin/bash
input=$(cat)
cmd=$(jq -r '.tool_input.command // empty' <<<"$input")
echo "Claude wants to run: $cmd" >> ~/claude-commands.log
exit 0
06Output: exit code e JSON
Un hook può rispondere in due modi.
Exit code. 0 indica successo. 2 è un errore bloccante: sugli eventi che possono bloccare, l'azione si ferma e il tuo stderr diventa la motivazione. Qualsiasi altro codice, compreso il classico 1, è un errore non bloccante: l'azione prosegue e vedi un avviso di errore dell'hook. Se un hook deve far rispettare una regola, esci con 2, non con 1.
JSON su stdout. Per un controllo più preciso, esci con 0 e stampa un singolo oggetto JSON. Alcuni campi funzionano ovunque, come continue: false per fermare del tutto Claude. Altri dipendono dall'evento: Stop usa decision: "block" con un reason, mentre PreToolUse e PermissionRequest mettono la decisione dentro hookSpecificOutput. Non stampare nient'altro su stdout: basta una riga di troppo dal profilo della shell perché Claude Code non riesca a leggere il JSON.
07Tutti gli eventi hook
SessionStartUna sessione inizia o riprendeNo (aggiunge contesto)SetupAvvio con --init-only, oppure --init / --maintenance con -pNoInstructionsLoadedViene caricato un file CLAUDE.md o .claude/rules/*.mdNoUserPromptSubmitInvii un prompt, prima che Claude lo vedaSìUserPromptExpansionUn comando digitato si espande in un promptSìPreToolUsePrima di ogni chiamata a un toolSìPermissionRequestUna chiamata a un tool richiede una decisione sui permessiConsente o nega tramite JSONPermissionDeniedLa modalità auto nega una chiamata a un toolNoPostToolUseDopo che una chiamata a un tool va a buon fineNo (il tool è già stato eseguito)PostToolUseFailureDopo che una chiamata a un tool fallisceNoPostToolBatchDopo un gruppo di chiamate parallele ai tool, prima della chiamata successiva al modelloSìNotificationClaude Code invia una notificaNoMessageDisplayMentre viene mostrato il testo della risposta di ClaudeNo (può modificare ciò che viene mostrato)SubagentStart / SubagentStopUn subagent si avvia o terminaSolo SubagentStopTaskCreated / TaskCompletedUn task viene creato o segnato come completatoSìStopClaude finisce di rispondereSì (fa continuare Claude)StopFailureIl turno termina con un errore dell'APINoTeammateIdleUn membro di un agent team sta per andare in idleSìConfigChangeUn file di impostazioni cambia durante una sessioneSìCwdChangedLa directory di lavoro cambiaNoDirectoryAddedViene aggiunta una directory con /add-dirNoFileChangedUn file monitorato cambia su discoNoWorktreeCreate / WorktreeRemoveUn worktree viene creato o rimossoSìPreCompact / PostCompactPrima e dopo la compattazione del contestoSolo PreCompactPreModelSwitch / PostModelSwitchPrima e dopo il cambio di modelloSolo PreModelSwitchElicitation / ElicitationResultUn server MCP ti chiede un input, e dopo che hai rispostoSìSessionEndUna sessione terminaNoAnthropic aggiunge eventi di tanto in tanto, quindi consulta il riferimento sugli hook per l'elenco aggiornato e i campi di input di ogni evento.
08Quali eventi contano per le notifiche
Quattro eventi coprono quasi tutto ciò di cui vuoi essere avvisato:
Stopper "Claude ha finito". Vedi la guida allo Stop hook.Notificationper "Claude ti sta aspettando", con tipi comepermission_prompteidle_prompt. Vedi la guida al Notification hook.PermissionRequestper essere avvisato, o rispondere, nell'istante in cui serve un permesso. Vedi la guida a PermissionRequest.PreToolUsesul toolAskUserQuestion, per vedere o rispondere alle domande a scelta multipla di Claude. Vedi come rispondere alle domande dal telefono.
Per una configurazione desktop pronta da incollare, parti da come ricevere una notifica quando Claude Code finisce.
09Dove si inserisce Claude Code Notifier
Claude Code Notifier è costruito interamente su questi hook. Il suo tool da riga di comando gratuito, ccn, funziona su Mac, Windows e Linux. L'installazione aggiunge sette voci di hook a ~/.claude/settings.json, ognuna delle quali esegue ccn hook, e crea un backup una tantum del file. ccn gira per pochi millisecondi a ogni evento e poi termina, quindi non resta nulla in esecuzione.
Da solo mostra notifiche desktop. Con l'app per iPhone o Android abbinata, gli stessi hook riportano indietro le tue risposte: consentire o negare un permesso, rispondere a una domanda o inviare l'istruzione successiva quando Claude si ferma. È indipendente e non è realizzato da Anthropic. Se ti serve solo un log o un passaggio di lint, qualche riga scritta da te è più semplice. Trovi i dettagli nella pagina di ccn.
10FAQ
Come provo un hook senza aspettare Claude?
Passa un JSON di esempio allo script tramite pipe e controlla l'exit code: echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh; echo $?. Poi esegui /hooks in Claude Code per verificare che sia registrato sull'evento giusto.
Perché il mio hook non parte?
Le cause tipiche sono JSON non valido in settings.json (virgole finali, commenti), un matcher che non corrisponde esattamente (distingue maiuscole e minuscole), uno script non eseguibile o una cartella che non hai ancora segnato come attendibile. Avvia Claude Code con claude --debug e leggi il log di debug per vedere cosa è stato eseguito. L'elenco completo è nella checklist di troubleshooting.
Come disattivo tutti gli hook per un po'?
Imposta "disableAllHooks": true in un file di impostazioni. Per saltarli in una sola esecuzione, avvia Claude Code con --settings '{"disableAllHooks": true}'. Non esiste un interruttore per un singolo hook: devi rimuoverne la voce. Nessuno dei due metodi disattiva gli hook impostati dalla tua organizzazione nelle impostazioni gestite.
Qual è il timeout predefinito degli hook?
Dieci minuti (600 secondi) per gli hook di tipo command, HTTP e tool MCP, 30 secondi per gli hook di tipo prompt e 60 per quelli di tipo agent. Alcuni eventi usano valori predefiniti più brevi, e puoi impostare timeout in secondi su qualsiasi handler.