30 eventi di hook si attivano in una sessione Claude Code. Solo 3 possono rispondere.
L'elenco completo degli eventi di hook di Claude Code, quando si attiva ciascuno, quali 15 possono bloccare, e la regola di stdout che si mangia in silenzio l'output della maggior parte degli hook. Un riferimento sul campo costruito eseguendo hook in produzione su migliaia di sessioni di agenti.
Due guasti si ripresentano di continuo quando si collegano degli hook a Claude Code, e non si somigliano per niente.
Il primo: aggiungi un hook, non succede nulla. Nessun errore, nessun avviso, nessuna riga di log. L'hook semplicemente non viene mai eseguito.
Il secondo: l'hook viene chiaramente eseguito, se ne vedono gli effetti sul disco, ma il messaggio che stampa per l'agente non arriva mai. L'agente si comporta come se l'hook non avesse detto nulla.
Entrambi vengono dallo stesso posto. Il sistema di hook è più ampio e meno uniforme della manciata di eventi di cui parla la maggior parte degli articoli, e le regole su chi può parlare all'agente non sono quelle che immagineresti. Questo è il riferimento che avremmo voluto avere. Costruiamo AgentsRoom sopra questi hook, e tutto ciò che segue è citato dal riferimento ufficiale oppure misurato in produzione.
Gli eventi sono 30, non sei
La maggior parte delle guide copre PreToolUse, PostToolUse, UserPromptSubmit, Stop, Notification e SubagentStop. Quei sei sono reali, e portano gran parte del lavoro utile. Sono anche un quinto di ciò che esiste.
L'elenco completo, raggruppato per ciò che ciascun evento osserva:
| Gruppo | Eventi |
|---|---|
| Sessione | SessionStart, SessionEnd, Setup |
| Prompt | UserPromptSubmit, UserPromptExpansion |
| Strumenti | PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch |
| Permessi | PermissionRequest, PermissionDenied |
| Turno | Stop, StopFailure |
| Sottoagenti e task | SubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle |
| Contesto | PreCompact, PostCompact, InstructionsLoaded |
| Ambiente | FileChanged, CwdChanged, ConfigChange |
| Worktree | WorktreeCreate, WorktreeRemove |
| Interfaccia | Notification, MessageDisplay |
| Elicitation MCP | Elicitation, ElicitationResult |

L'ordine di attivazione degli eventi in una singola sessione. Il blocco degli strumenti si ripete a ogni chiamata di strumento, e l'intero blocco del prompt si ripete a ogni turno.
Alcuni di questi eventi cambiano il modo di ragionare sul sistema. PostToolUseFailure esiste, quindi il ramo «lo strumento ha funzionato?» è un evento, non qualcosa che si deduce da un payload. PostToolBatch si attiva una volta sola dopo la risoluzione di un lotto di chiamate di strumenti in parallelo, ed è il posto giusto per lanciare un linter una volta invece che una volta per modifica. InstructionsLoaded si attiva alla lettura di CLAUDE.md, il che dà un punto di aggancio per verificare che l'agente abbia davvero caricato le regole che credi abbia caricato.
La regola di stdout che si mangia l'output della maggior parte degli hook
È la cosa più utile di questa pagina.
Con il codice di uscita 0, Claude Code analizza stdout in cerca di campi JSON di output. Ma se quello stdout venga mai mostrato all'agente dipende dall'evento, e le eccezioni sono un elenco corto. Dal riferimento:
Per la maggior parte degli eventi, stdout viene scritto nel log di debug ma non mostrato nella trascrizione. Le eccezioni sono
UserPromptSubmit,UserPromptExpansioneSessionStart, dove stdout viene aggiunto come contesto che Claude può vedere e su cui può agire.
Tre eventi su trenta. Se fai echo "attenzione: questa migrazione è distruttiva" da un hook PostToolUse aspettandoti che l'agente lo legga, non lo leggerà mai. Il tuo testo è finito nel log di debug.
Esistono esattamente due modi per mettere del testo sotto gli occhi dell'agente da un qualsiasi altro evento:
- Uscire con 2 e scrivere su stderr. Con l'uscita 2, Claude Code ignora stdout e qualsiasi JSON al suo interno, e restituisce stderr all'agente come messaggio di errore.
- Uscire con 0 e stampare un oggetto JSON che porta
hookSpecificOutput.additionalContext.
Nota l'asimmetria del primo punto. Uscita 0 significa che conta stdout e non stderr. Uscita 2 significa che conta stderr e stdout viene scartato del tutto. Invertire questi due è il motivo per cui un hook può sembrare perfettamente corretto e restare comunque muto.
Qualsiasi altro codice di uscita è un errore non bloccante. La trascrizione mostra un avviso <hook name> hook error con la prima riga di stderr, l'esecuzione continua, e lo stderr completo finisce nel log di debug.

Quale canale raggiunge l'agente, per ciascun codice di uscita. Il percorso tratteggiato è quello che si dà per scontato e che non esiste.
Esattamente la metà può bloccare
Quindici eventi fermano l'azione con l'uscita 2. Quindici la ignorano e tirano dritto.
Possono bloccare: PreToolUse, PermissionRequest, UserPromptSubmit, UserPromptExpansion, Stop, SubagentStop, TeammateIdle, TaskCreated, TaskCompleted, ConfigChange, PostToolBatch, PreCompact, Elicitation, ElicitationResult, WorktreeCreate.
Non possono bloccare: PostToolUse, PostToolUseFailure, PermissionDenied, StopFailure, Notification, SubagentStart, SessionStart, Setup, SessionEnd, CwdChanged, FileChanged, PostCompact, WorktreeRemove, InstructionsLoaded, MessageDisplay.
La conseguenza pratica: una protezione va su PreToolUse, mai su PostToolUse. PostToolUse si attiva dopo che lo strumento è riuscito. Uscire con 2 lì non annulla la scrittura, stampa solo un errore mentre il danno è già sul disco. Se vuoi fermare un rm -rf, hai esattamente un posto in cui farlo.
Che PostToolBatch blocchi mentre PostToolUse no merita una seconda occhiata. Significa che un controllo a livello di lotto può ancora fermare il turno dopo che le modifiche parallele sono atterrate, ed è la cosa più vicina a un veto post-scrittura che il sistema offra.
I matcher sono esatti, finché all'improvviso non lo sono più
Il campo matcher cambia strategia di valutazione in base ai propri caratteri, e niente ti dice quale strada ha preso.
| Matcher | Valutato come |
|---|---|
"*", "", o assente | corrisponde a tutto |
Solo lettere, cifre, _, -, spazi, ,, | | stringa esatta, o elenco di stringhe esatte separate da | o , |
| Qualsiasi altra cosa | espressione regolare JavaScript non ancorata |
Il tranello è «non ancorata». Il riferimento è esplicito: l'espressione viene testata con RegExp.prototype.test, che riesce su una corrispondenza in un punto qualsiasi del valore. Quindi Edit.* corrisponde a Edit e a NotebookEdit. Se intendevi un solo strumento, scrivi ^Edit$.
Due comportamenti dipendenti dalla versione, da conoscere prima di mettersi a debuggare la cosa sbagliata:
- I separatori virgola e la tolleranza agli spazi richiedono Claude Code v2.1.191 o successivo.
- I trattini sono entrati nell'insieme di caratteri della corrispondenza esatta in v2.1.195. Prima, un matcher come
code-reviewerera trattato come una regex non ancorata, quindi si attivava anche persenior-code-reviewer.
In AgentsRoom restringiamo il nostro hook di attribuzione dei file con Write|Edit|MultiEdit|NotebookEdit, che resta sulla strada della stringa esatta e corrisponde a quei quattro strumenti e a nient'altro. Gli hook di ciclo di vita che installiamo non hanno alcun matcher, perché ci riguardano sempre.
Sei posti possono definire hook, e si fondono
L'istinto è cercare un ordine di precedenza. Non ce n'è uno, ed è proprio questa la parte interessante.
| Posizione | Ambito |
|---|---|
~/.claude/settings.json | tutti i tuoi progetti, locale alla tua macchina |
.claude/settings.json | un progetto, versionabile |
.claude/settings.local.json | un progetto, gitignorato da Claude Code |
| Managed policy settings | tutta l'organizzazione, controllato dall'amministratore |
hooks/hooks.json del plugin | finché il plugin è attivo |
| Frontmatter di skill o agent | finché il componente è attivo |
Dal riferimento:
Le voci di hook si fondono tra i livelli di settings invece di sostituirsi a vicenda: i settings utente, di progetto e locali aggiungono i propri hook senza rimuovere quelli gestiti, e l'impostazione
disableAllHooksnon può disattivare gli hook gestiti dall'esterno dei settings gestiti.
Quindi un hook di progetto non sovrascrive mai un hook globale, gli si somma sopra. Sei sorgenti, tutte additive. Un formattatore PostToolUse definito nei tuoi settings utente e di nuovo nel progetto viene eseguito due volte per modifica, e l'unico sintomo è la sensazione che le cose siano lente.

Sei sorgenti, un solo insieme fuso. Qui niente sovrascrive niente.
Questo spiega anche perché .claude/settings.local.json è il posto giusto in cui uno strumento può installare un hook nel progetto di qualcun altro. È limitato al progetto, Claude Code lo gitignora, e viene caricato senza alcun flag da riga di comando. È lì che AgentsRoom scrive le proprie voci, così il .claude/settings.json versionato dell'utente non viene mai toccato e i suoi colleghi non ereditano mai un percorso specifico di una macchina.
Cosa ci ha insegnato eseguire hook in produzione
AgentsRoom installa hook in ogni progetto che apre, per tracciare lo stato degli agenti in modo deterministico e attribuire i file modificati all'agente giusto. Alcune cose si vedono solo a quella scala.
I nomi di evento sconosciuti vengono ignorati in silenzio. Non è documentato, e noi ci contiamo. Quando aggiungiamo un nuovo evento di ciclo di vita al nostro installer, gli utenti su una CLI più vecchia si ritrovano un settings.local.json che contiene un nome di evento di cui il loro binario non ha mai sentito parlare. Niente si rompe, niente avvisa, la voce viene saltata. È questo che rende sicuro rilasciare l'installer prima di una versione della CLI. Ed è anche, inevitabilmente, il motivo per cui un refuso produce silenzio totale invece di un errore.
agent_id è ciò che ti dice che sei dentro un sottoagente. Il campo è presente solo quando l'hook si attiva dentro una chiamata di sottoagente. Conta più di quanto sembri: Stop si attiva quando un sottoagente finisce il suo turno, non solo l'agente principale. Una regola ingenua del tipo «segna la sessione come conclusa su Stop» segna l'intera sessione come finita al primo ritorno di un sottoagente qualsiasi. Saltiamo gli eventi di fine turno che portano agent_id esattamente per questo motivo.
Non leggere transcript_path per il turno in corso. Il riferimento avverte che la trascrizione viene scritta in modo asincrono e può essere in ritardo rispetto alla conversazione in memoria, quindi i messaggi più recenti potrebbero non essere ancora lì quando il tuo hook si attiva. Stop e SubagentStop ricevono last_assistant_message proprio perché tu non debba mai correre dietro al file.
Gli hook sono l'unico segnale di stato affidabile. Prima degli hook, analizzavamo il PTY per capire se un agente stesse ragionando, aspettando o avesse finito. Questo si rompe nel momento in cui la CLI disegna attraverso il buffer di schermo alternativo del terminale, che è quello che fa /tui fullscreen. Gli hook si attivano in modo identico sotto qualsiasi renderer. Se stai costruendo qualcosa che osserva un agente dall'esterno, questo è il livello su cui costruire, e l'analisi del terminale resta al massimo un ripiego.
async: true non costa nulla. Un comando di hook può dichiarare async: true, e l'agente non lo aspetta. Il nostro hook fa una POST verso un endpoint locale con un tetto di 2 secondi e ritorna; la latenza del turno dell'agente non ne risente nemmeno quando l'applicazione ricevente è chiusa. Se il tuo hook si limita a osservare e non decide mai, rendilo async e smetti di pagarlo.
Non lasciare mai che un hook scriva spazzatura nel terminale. Il nostro script inghiotte ogni eccezione, anche al livello più alto. Un traceback non gestito da un hook non fallisce solo in silenzio: stampa uno stack trace Python nella sessione di terminale dell'utente, nel bel mezzo del suo lavoro.
Timeout
I valori di default sono generosi, con tre eccezioni che non lo sono:
| Tipo di hook | Timeout di default |
|---|---|
command, http, mcp_tool | 600 s |
prompt | 30 s |
agent | 60 s |
UserPromptSubmit (command, http, mcp_tool) | 30 s |
MessageDisplay (command, http, mcp_tool) | 10 s |
SessionEnd | 1,5 s condivisi tra tutti gli hook, alzati per allinearsi a un timeout per hook più lungo, fino a 60 s |
Il budget di SessionEnd è quello che sorprende. È un budget condiviso, non un'allocazione per hook, quindi tre hook di pulizia si dividono 1,5 secondi a meno che non lo si alzi esplicitamente.
La versione breve
- Esistono 30 eventi. Sei sono famosi.
- stdout raggiunge l'agente solo su
UserPromptSubmit,UserPromptExpansioneSessionStart. Ovunque altrove, usa l'uscita 2 con stderr, oppureadditionalContextnel JSON. - 15 eventi bloccano con l'uscita 2, 15 la ignorano. Le protezioni vanno su
PreToolUse. - I matcher sono stringhe esatte finché un carattere speciale non li trasforma in una regex non ancorata.
- Sei sorgenti di settings si fondono in modo additivo. Niente sovrascrive niente.
- Un nome di evento scritto male fallisce nel silenzio più completo.
Se preferisci vedere questi eventi attivarsi invece di ragionarci sopra, è esattamente quello che abbiamo costruito: AgentsRoom mostra ogni attivazione di hook per agente, per progetto, per esecuzione, su decine di agenti in parallelo e sessioni di sottoagenti. Gli hook che configuri nei tuoi settings continuano a funzionare esattamente come li hai scritti, perché esegue la vera CLI.
Continua a leggere
Come Scalare gli Agenti di Codifica AI in un Team di Sviluppo
Un sviluppatore con un agente di codifica è una storia di produttività. Cinque sviluppatori con venti agenti sono un problema di coordinamento. Ecco cosa si rompe per primo quando un team si espande, e la configurazione che tiene: file di contesto impegnati, chiara proprietà dei file, revisione per raggio d'azione e costi che puoi effettivamente vedere.
Leggi l'articoloDovresti ancora rivedere il codice del tuo agente AI?
I tuoi agenti scrivono codice migliore della metà delle pull request che eri solito unire. Quindi leggi ancora ogni riga? Il caso onesto per entrambe le parti, i 10 segnali che ti dicono che un agente ha fatto un errore e quanto review merita effettivamente ogni cambiamento.
Leggi l'articoloQuale agente AI dovresti scegliere per i tuoi progetti?
Un agente frontend che scrive il tuo testo di marketing è un fallimento silenzioso. Come abbinare ogni compito al giusto agente AI: ruoli, esperti del catalogo, agenti personalizzati e cosa fare quando non hai idea di chi dovrebbe svolgere il lavoro.
Leggi l'articolo
Scarica AgentsRoom
Eseguire i vostri agenti AI (Claude, Codex, Antigravity CLI, OpenCode, Aider, Grok Build, Mistral Vibe, Kimi Code) su tutti i vostri progetti, da una singola finestra.
App companion: monitora i tuoi agenti in movimento
Usa Claude, Codex, Antigravity CLI o un altro provider IA.
Invia bug e richieste direttamente nel tuo backlog pubblico.
Uno sguardo ad AgentsRoom in azione.