Ciclo di vita dei hook
Claude Code esegue i hook in punti specifici durante una sessione. Quando un evento si attiva e un matcher corrisponde, Claude Code passa il contesto JSON dell’evento al gestore del hook. Per i hook di comando, l’input arriva su stdin. Per i hook HTTP, arriva come corpo della richiesta POST. Il gestore può quindi ispezionare l’input, intraprendere un’azione e facoltativamente restituire una decisione. Gli eventi si dividono in tre cadenze:- per sessione:
SessionStarteSessionEnd - per turno:
UserPromptSubmit,StopeStopFailure - ad ogni chiamata dello strumento all’interno del ciclo agentico:
PreToolUseePostToolUse, ad eccezione delle chiamateEndConversation, che saltano entrambe
Come si risolve un hook
Per vedere come l’evento, il matcher e il gestore si combinano insieme, considerare questo hookPreToolUse che blocca i comandi shell distruttivi.
- macOS/Linux
- Windows (PowerShell)
Il Lo script legge l’input JSON da stdin, estrae il comando e restituisce una Questo script, come gli altri esempi Bash su questa pagina che analizzano l’input JSON, utilizza
matcher si restringe alle chiamate dello strumento Bash e la condizione if si restringe ulteriormente ai sottocomandi Bash che corrispondono a rm *, quindi block-rm.sh viene eseguito solo quando entrambi i filtri corrispondono:permissionDecision di "deny" se contiene rm -rf. Salvarlo in .claude/hooks/block-rm.sh nel progetto e renderlo eseguibile con chmod +x .claude/hooks/block-rm.sh in modo che Claude Code possa eseguirlo:jq, quindi installare jq e assicurarsi che sia nel PATH prima di provarli.Bash "rm -rf /tmp/build" rispetto alla configurazione macOS/Linux. Ecco cosa accade:
1
L'evento si attiva
L’evento
PreToolUse si attiva. Claude Code invia l’input dello strumento come JSON su stdin al hook:2
Il matcher controlla
Il matcher
"Bash" corrisponde al nome dello strumento, quindi questo gruppo di hook si attiva. Se si omette il matcher o si utilizza "*", il gruppo si attiva ad ogni occorrenza dell’evento.3
La condizione if controlla
La condizione
if "Bash(rm *)" corrisponde perché rm -rf /tmp/build è un sottocomando che corrisponde a rm *, quindi questo gestore viene eseguito. Se il comando fosse stato npm test, il controllo if avrebbe fallito e block-rm.sh non sarebbe mai stato eseguito, evitando il sovraccarico di spawn del processo. Il campo if è facoltativo; senza di esso, ogni gestore nel gruppo corrispondente viene eseguito.4
Il gestore del hook viene eseguito
Lo script ispeziona il comando completo e trova Se il comando fosse stato una variante più sicura di
rm -rf, quindi stampa una decisione su stdout:rm come rm file.txt, lo script avrebbe raggiunto exit 0 invece. Il codice di uscita 0 senza output significa che l’hook non ha alcuna decisione da segnalare, quindi la chiamata dello strumento continua attraverso il normale flusso di autorizzazione. L’hook può negare la chiamata, ma rimanere in silenzio non la approva.5
Claude Code agisce sul risultato
Claude Code legge la decisione JSON, blocca la chiamata dello strumento e mostra a Claude il motivo.
Configurazione
Gli hook sono definiti in file di impostazioni JSON. La configurazione ha tre livelli di annidamento:- Scegli un evento hook a cui rispondere, come
PreToolUseoStop - Aggiungi un gruppo matcher per filtrare quando si attiva, come “solo per lo strumento Bash”
- Definisci uno o più handler hook da eseguire quando corrisponde
Questa pagina utilizza termini specifici per ogni livello: hook event per il punto del ciclo di vita, matcher group per il filtro, e hook handler per il comando shell, endpoint HTTP, strumento MCP, prompt, o agente che viene eseguito. “Hook” da solo si riferisce alla funzione generale.
Posizioni degli hook
Il luogo in cui definisci un hook determina il suo ambito:
Le sessioni cloud non leggono il tuo
~/.claude/settings.json locale. In un ambiente self-hosted, Claude Code esegue anche gli hook che l’operatore ha seminato da ~/.claude/ dell’host runner, e esegue gli hook nel file di impostazioni gestite dell’immagine runner quando quel file è tra le fonti gestite che Claude Code applica, il che per impostazione predefinita significa solo quando né le impostazioni gestite dal server né una policy Claude Code consegnata da MDM forniscono il livello gestito. Vedi cosa viene trasferito dalla tua configurazione per quali file di impostazioni e plugin, e quindi quali hook, raggiungono una sessione cloud.
Per i dettagli sulla risoluzione dei file di impostazioni, vedi settings.
Gli hook dai file di impostazioni, dalle impostazioni di policy gestite e dai plugin vengono eseguiti anche all’interno di subagenti. Quando un subagent chiama uno strumento, gli eventi dello strumento come PreToolUse e PostToolUse attivano gli stessi hook configurati della conversazione principale, e l’input contiene i campi di input comuni agent_id e agent_type che identificano il subagent.
Gli amministratori possono utilizzare allowManagedHooksOnly nelle impostazioni gestite per limitare quali hook vengono eseguiti:
- I tuoi hook utente, progetto, locale e plugin sono bloccati. Gli hook dai plugin forzatamente abilitati nelle impostazioni gestite
enabledPluginssono esenti - Claude Code restringe anche le tue impostazioni
statusLine,fileSuggestion, esubagentStatusLinealle impostazioni gestite - Claude Code disabilita anche i plugin con una
commandsource, inclusi i plugin forzatamente abilitati nelle impostazioni gestiteenabledPlugins, a meno chedisableCommandPluginSourcesnon sia esplicitamente impostato sufalse. Lecommandsources richiedono Claude Code v2.1.229 o successivo - Claude Code blocca anche i comandi
headersHelperdel marketplace a meno chedisableCommandPluginSourcesnon sia esplicitamente impostato sufalse, tranne per un marketplace che le impostazioni gestite stesse dichiarano
allowManagedHooksOnly.
Le voci degli hook si uniscono tra i livelli di impostazioni piuttosto che sostituirsi a vicenda: le impostazioni utente, progetto e locale aggiungono i loro hook senza rimuovere quelli gestiti, e l’impostazione disableAllHooks non può disabilitare gli hook gestiti da fuori le impostazioni gestite.
Le allowlist degli hook HTTP si applicano agli hook da ogni fonte, incluse le impostazioni di policy gestite:
allowedHttpHookUrls: quando definito a qualsiasi livello di impostazioni, Claude Code esegue un handler hook HTTP solo se il suo URL corrisponde all’allowlist unitohttpHookAllowedEnvVars: quando definito, Claude Code interpola solo le variabili di ambiente in quella lista negli header degli hook
Modelli matcher
Il campomatcher filtra quando gli hook si attivano. Come viene valutato un matcher dipende dai caratteri che contiene:
Un matcher sul percorso dell’espressione regolare viene testato con
RegExp.prototype.test di JavaScript, che ha successo su una corrispondenza in qualsiasi punto del valore. Edit.* corrisponde sia a Edit che a NotebookEdit; racchiudi il pattern in ^ e $, come in ^Edit$, quando hai bisogno di una corrispondenza di intera stringa.
FileChanged e StopFailure utilizzano un set di corrispondenza esatta più ristretto di sole lettere, cifre, _, e |. Un trattino, spazio, o virgola in un matcher per questi due eventi lo mantiene sul percorso dell’espressione regolare, e solo | separa le alternative. Ogni altro evento con supporto matcher nella tabella che segue accetta | o ,.
L’evento FileChanged non segue queste regole quando costruisce la sua lista di osservazione. Vedi FileChanged.
Ogni tipo di evento corrisponde su un campo diverso:
La corrispondenza di
StopFailure su cloud_credential_error richiede Claude Code v2.1.267 o successivo, la prima versione che segnala i fallimenti di caricamento delle credenziali sotto quel valore piuttosto che server_error o unknown.
Per la maggior parte degli eventi, Claude Code valuta il matcher rispetto a un campo dall’input JSON che invia al tuo hook su stdin. Per gli eventi dello strumento, quel campo è tool_name. Per PreModelSwitch e PostModelSwitch, Claude Code valuta il matcher rispetto al nome canonico che deriva da to_model, come descritto sotto PreModelSwitch. Ogni sezione hook event elenca l’insieme completo dei valori matcher e lo schema di input per quell’evento.
Questo esempio esegue uno script di linting solo quando Claude scrive o modifica un file:
matcher a un evento senza supporto matcher, viene silenziosamente ignorato.
Per gli eventi dello strumento, puoi filtrare più strettamente impostando il campo if sui singoli handler hook. if utilizza la sintassi delle regole di permesso per corrispondere al nome dello strumento e agli argomenti insieme, quindi "Bash(git *)" viene eseguito quando qualsiasi sottocomando dell’input Bash corrisponde a git * e "Edit(*.ts)" viene eseguito solo per i file TypeScript.
Corrispondere ai tool MCP
I tool del server MCP appaiono come tool regolari negli eventi dello strumento (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), quindi puoi farli corrispondere allo stesso modo di qualsiasi altro nome di strumento.
I tool MCP seguono il modello di denominazione mcp__<server>__<tool>, ad esempio:
mcp__memory__create_entities: tool create entities del server Memorymcp__filesystem__read_file: tool read file del server Filesystemmcp__github__search_repositories: tool search del server GitHub
.* al prefisso del server. .* è obbligatorio: un matcher come mcp__memory o mcp__brave-search contiene solo caratteri di corrispondenza esatta, quindi viene confrontato come una stringa esatta e non corrisponde a nessun tool.
mcp__memory__.*corrisponde a tutti i tool dal servermemorymcp__brave-search__.*corrisponde a tutti i tool da un server il cui nome contiene un trattinomcp__.*__write.*corrisponde a qualsiasi tool il cui nome inizia conwriteda qualsiasi server
mcp__plugin_<plugin-name>_<server-name>__<tool>. Un matcher scritto rispetto alla chiave del server nudo non si attiva mai per questi tool. Per un plugin denominato my-plugin che raggruppa un server sotto la chiave db, un tool query appare come mcp__plugin_my-plugin_db__query, quindi il matcher per ogni tool da quel server è mcp__plugin_my-plugin_db__.*. Utilizza lo stesso nome di tool con scope nel campo if di un handler. Vedi Plugin-provided MCP servers per come viene costruito il nome con scope.
Questo esempio registra tutte le operazioni del server memory e convalida le operazioni di scrittura da qualsiasi server MCP:
Campi handler hook
Ogni oggetto nell’arrayhooks interno è un handler hook: il comando shell, endpoint HTTP, tool MCP, prompt LLM, o agente che viene eseguito quando il matcher corrisponde. Ci sono cinque tipi:
- Command hooks (
type: "command"): esegui un comando shell. Il tuo script riceve l’input JSON dell’evento su stdin e comunica i risultati indietro attraverso codici di uscita e stdout. - HTTP hooks (
type: "http"): invia l’input JSON dell’evento come richiesta HTTP POST a un URL. L’endpoint comunica i risultati indietro attraverso il corpo della risposta utilizzando lo stesso formato di output JSON degli hook di comando. - MCP tool hooks (
type: "mcp_tool"): chiama un tool su un server MCP configurato. L’output di testo del tool viene trattato come stdout di hook di comando. - Prompt hooks (
type: "prompt"): invia un prompt a un modello Claude per la valutazione a turno singolo. Il modello restituisce la sua decisione come JSON. Vedi Prompt-based hooks. - Agent hooks (
type: "agent"): genera un subagent che può utilizzare tool come Read, Grep, e Glob per verificare le condizioni prima di restituire una decisione. Gli agent hook sono sperimentali e potrebbero cambiare. Vedi Agent-based hooks.
$CLAUDE_CODE_REMOTE è "true" negli ambienti web remoti e non è impostata nella CLI locale. Claude Code v2.1.199 e successivo imposta $CLAUDE_CODE_BRIDGE_SESSION_ID all’ID della sessione Remote Control mentre la sessione locale ha una connessione Remote Control attiva.
Campi comuni
Questi campi si applicano a tutti i tipi di hook:
Il campo
if contiene esattamente una regola di permesso. Non c’è sintassi &&, ||, o lista per combinare le regole; per applicare più condizioni, definisci un handler hook separato per ciascuna.
In una condizione if per uno strumento di file, un pattern di directory a segmento singolo come "Edit(src/**)" corrisponde solo alla directory src nella directory di lavoro e ai file sotto di essa. Per corrispondere a una directory denominata src a qualsiasi profondità, scrivi "Edit(**/src/**)". Prima di v2.1.214, "Edit(src/**)" corrispondeva a una directory denominata src a qualsiasi profondità sotto la directory di lavoro.
Per i pattern Bash, se il tuo comando hook viene eseguito dipende dalla forma del pattern e dal comando Bash che Claude sta invocando. Gli assegnamenti VAR=value iniziali vengono rimossi prima della corrispondenza.
Quando Claude Code non può determinare quali comandi esegue l’input Bash, esegue il tuo hook indipendentemente dal pattern. Poiché il filtro
if è best-effort, utilizza il sistema di permessi piuttosto che un hook per applicare un allow o deny rigido.
Campi command hook
Oltre ai campi comuni, gli hook di comando accettano questi campi:
Un hook di comando viene eseguito come exec form quando
args è impostato, e shell form quando args è omesso. Imposta args ogni volta che l’hook fa riferimento a un placeholder di percorso, poiché ogni elemento viene passato come un argomento senza virgolette. Ometti args quando hai bisogno di funzioni shell come pipe o &&, o quando nessuno dei due problemi si applica.
Exec form viene eseguito quando args è presente. Claude Code risolve command come un eseguibile su PATH e lo genera direttamente con args come vettore di argomenti. Non c’è shell, quindi ogni elemento args è un argomento esattamente come scritto, e i placeholder di percorso come ${CLAUDE_PLUGIN_ROOT} vengono sostituiti in command e in ogni elemento args come stringhe semplici. I caratteri speciali come apostrofi, $, e backtick passano attraverso verbatim perché non c’è shell per interpretarli. Non avviene tokenizzazione shell su nessuna piattaforma.
Shell form viene eseguito quando args è assente. La stringa command viene passata a una shell: sh -c su macOS e Linux, Git Bash su Windows, o PowerShell quando Git Bash non è installato. Imposta il campo shell per scegliere esplicitamente. La shell tokenizza la stringa, espande le variabili, e interpreta pipe, &&, reindirizzamenti, e glob.
Su Windows, exec form richiede che
command si risolva in un vero eseguibile come .exe. Gli shim .cmd e .bat che npm, npx, eslint, e altri tool installano in node_modules/.bin non sono eseguibili e non possono essere generati senza una shell. Per eseguirli in exec form, invoca lo script sottostante con node direttamente, ad esempio "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Il pattern node più script-path funziona su ogni piattaforma perché node.exe è un vero binario. Per eseguire uno shim .cmd o .bat per nome, utilizza shell form.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT, e CLAUDE_PLUGIN_DATA sul processo generato, quindi uno script può leggere process.env.CLAUDE_PLUGIN_ROOT indipendentemente da come è stato lanciato.
Gli hook plugin inoltre sostituiscono i valori ${user_config.*}, solo in exec form: il valore viene sostituito in command e in ogni elemento args come una stringa semplice, quindi nessuna shell lo ri-analizza.
Un hook plugin in shell form il cui command fa riferimento a ${user_config.*} fallisce con un errore invece di essere eseguito. Per utilizzare un valore di opzione da un hook in shell form, leggi la variabile di ambiente $CLAUDE_PLUGIN_OPTION_<KEY>, come $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL per un’opzione webhook_url, o imposta args per passare l’hook a exec form. Prima di v2.1.207, i comandi degli hook plugin in shell form sostituivano anche ${user_config.*}.
In exec form,
command è solo il nome o il percorso dell’eseguibile. Se command è un nome nudo senza separatore di percorso e contiene spazi insieme a args, Claude Code registra un avviso perché la generazione fallirà: non c’è un eseguibile denominato node script.js. Sposta i token extra in args. I percorsi assoluti con spazi, come C:\Program Files\nodejs\node.exe, sono un singolo eseguibile valido e non attivano l’avviso.Campi HTTP hook
Oltre ai campi comuni, gli hook HTTP accettano questi campi:
Claude Code invia l’input JSON dell’hook come corpo della richiesta POST con
Content-Type: application/json. Il corpo della risposta utilizza lo stesso formato di output JSON degli hook di comando.
La gestione degli errori differisce dagli hook di comando; vedi HTTP response handling.
Questo esempio invia gli eventi PreToolUse a un servizio di convalida locale, autenticandosi con un token dalla variabile di ambiente MY_TOKEN:
Campi MCP tool hook
Oltre ai campi comuni, gli hook MCP tool accettano questi campi:
Questo esempio chiama il tool
security_scan sul server MCP my_server dopo ogni Write o Edit, passando il percorso del file modificato:
isError: true, l’hook produce un errore non bloccante e l’esecuzione continua.
Su eventi dove un hook può bloccare o cambiare il risultato, come PreToolUse o Stop, Claude Code attende un server in connessione prima di chiamare il tool, per al massimo MCP_TIMEOUT e entro il timeout dell’hook stesso. Su eventi osservazionali, come Notification o SessionEnd, non attende.
Un server che mostra lo stato cached si connette quando l’hook chiama il suo tool. Se il server non è connesso a quel punto, l’hook produce un errore non bloccante e l’esecuzione continua. L’hook non avvia mai un flusso OAuth, quindi autentica il server da /mcp prima.
SessionStart al lancio, incluso con --continue o --resume, e ogni evento Setup si attivano prima che i server MCP della sessione siano disponibili agli hook. Claude Code salta i loro hook mcp_tool senza chiamare il tool, e il debug log registra mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), o lo stesso messaggio che nomina Setup. Quando SessionStart si attiva di nuovo più tardi nella sessione, dopo /clear o una compattazione, i suoi hook mcp_tool vengono eseguiti. Per qualsiasi cosa la sessione abbia bisogno al lancio, utilizza un hook type: "command" su SessionStart invece.
Campi prompt e agent hook
Oltre ai campi comuni, gli hook prompt e agent accettano questi campi:Riferisci gli script per percorso
Utilizza questi placeholder per fare riferimento agli script degli hook relativi alla radice del progetto o del plugin, indipendentemente dalla directory di lavoro quando l’hook viene eseguito:${CLAUDE_PROJECT_DIR}: la radice del progetto dove è iniziata la sessione. Claude Code imposta anche questa variabile nell’ambiente dei server MCP stdio e dei server LSP dei plugin.${CLAUDE_PLUGIN_ROOT}: la directory di installazione del plugin, per gli script raggruppati con un plugin. Vedi variabili di ambiente del plugin per come il percorso si comporta tra gli aggiornamenti.${CLAUDE_PLUGIN_DATA}: la directory di dati persistenti del plugin, per le dipendenze e lo stato che dovrebbero sopravvivere agli aggiornamenti del plugin.
I worktree sono diversi. Se Claude entra in un worktree durante la sessione, Claude Code mantiene
${CLAUDE_PROJECT_DIR} dove era e passa il percorso del worktree ai tuoi hook in un modo diverso:${CLAUDE_PROJECT_DIR}rimane fermo: punta ancora alla radice del progetto dove è iniziata la sessione, quindi un comando come${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.shesegue ancora lo script nel checkout principale.cwdsegue Claude: il campocwdnell’input JSON dell’hook è la radice del worktree dopo che Claude entra in un worktree, e la nuova directory dopo che Claude eseguecd. Leggilo quando un hook ha bisogno di sapere quale directory Claude sta utilizzando.
- Project scripts
- Plugin scripts
Questo esempio utilizza
${CLAUDE_PROJECT_DIR} per eseguire un verificatore di stile dalla directory .claude/hooks/ del progetto dopo qualsiasi chiamata dello strumento Write o Edit:Hook in skill e agenti
Oltre ai file di impostazioni e ai plugin, gli hook possono essere definiti direttamente negli skill e nei subagenti utilizzando il frontmatter, nello stesso formato di configurazione degli hook basati su impostazioni. Per quanto tempo Claude Code li mantiene registrati dipende dal componente:- Hook del subagent: Claude Code li esegue solo mentre quel subagent è in esecuzione e li rimuove quando finisce. Claude Code converte un hook
Stopqui inSubagentStop, l’evento che si attiva quando un subagent si completa. - Hook dello skill: Claude Code li registra quando tu o Claude invocate lo skill e continua a eseguirli per il resto della sessione, su turni dopo il turno dello skill stesso. Per fare in modo che Claude Code rimuova un hook dopo la sua prima esecuzione riuscita, imposta
once: truesu di esso.
PreToolUse che esegue uno script di convalida della sicurezza prima di ogni comando Bash:
-p in una cartella che non hai ancora fidata.
Gli hook del frontmatter in un subagent del progetto vengono eseguiti solo dopo che accetti la finestra di dialogo di fiducia dell’area di lavoro per la cartella da cui proviene il file dell’agente. Una sessione -p non conta come accettazione. Cosa viene eseguito prima di fidarti di una cartella confronta questo con la regola del file di impostazioni, e la pagina dei subagenti elenca quali ambiti sono esenti. Prima di v2.1.218, questi hook potevano essere eseguiti da cartelle che non avevi fidata.
Il menu /hooks
Digita /hooks in Claude Code per aprire un browser di sola lettura per i tuoi hook configurati. L’elenco etichetta ogni hook con la sua provenienza, come le impostazioni utente, le impostazioni di progetto, le impostazioni locali, un plugin o la sessione corrente.
Seleziona un hook per vedere il testo completo di ciò che esegue e dove è definito, come il percorso del suo file di impostazioni o il nome del suo plugin.
Per sfogliare tutti gli eventi hook, compresi quelli senza hook configurati, seleziona All events alla fine dell’elenco.
Disabilita o rimuovi gli hook
Per rimuovere un hook definito in un file di impostazioni, elimina la sua voce da quel file. Per disabilitare temporaneamente tutti gli hook senza rimuoverli, imposta"disableAllHooks": true nel tuo file di impostazioni. Claude Code legge il valore rimasto dopo che la precedenza delle impostazioni si applica, quindi un "disableAllHooks": false nel .claude/settings.json di un progetto sostituisce un true nelle tue impostazioni utente. Per disattivare gli hook per un’esecuzione qualunque siano le impostazioni del progetto, passa --settings '{"disableAllHooks": true}', che ha la precedenza sulle impostazioni di progetto e locale. Non c’è modo di disabilitare un singolo hook mantenendolo nella configurazione.
L’impostazione disableAllHooks rispetta la gerarchia delle impostazioni gestite. Se un amministratore ha configurato gli hook attraverso le impostazioni di policy gestite, disableAllHooks impostato nelle impostazioni utente, progetto, o locale non può disabilitare quegli hook gestiti. Solo disableAllHooks impostato a livello di impostazioni gestite può disabilitare gli hook gestiti. Per la portata completa di ogni livello, vedi disableAllHooks.
Le modifiche dirette agli hook nei file di impostazioni vengono normalmente rilevate automaticamente dal file watcher.
Input e output del hook
I command hook ricevono dati JSON tramite stdin e comunicano i risultati attraverso codici di uscita, stdout e stderr. Gli HTTP hook ricevono lo stesso JSON come corpo della richiesta POST e comunicano i risultati attraverso il corpo della risposta HTTP. Questa sezione copre i campi e il comportamento comuni a tutti gli eventi. Ogni sezione dell’evento sotto Hook events include il suo schema di input specifico e le opzioni di controllo della decisione. Su macOS e Linux, i command hook vengono eseguiti nella loro propria sessione senza un terminale di controllo. Il processo hook e qualsiasi processo figlio non possono aprire/dev/tty o inviare sequenze di escape direttamente all’interfaccia Claude Code. Windows non ha /dev/tty.
Per visualizzare un messaggio all’utente su qualsiasi piattaforma, restituire systemMessage nell’output JSON. Alcuni eventi lo scartano o lo consegnano altrove, e ogni sezione dell’evento lo specifica. Per attivare una notifica desktop, impostare un titolo della finestra o suonare il campanello, restituire terminalSequence invece.
Campi di input comuni
Gli hook event ricevono questi campi come JSON, oltre ai campi specifici dell’evento documentati in ogni sezione hook event. Per i command hook, questo JSON arriva tramite stdin. Per gli HTTP hook, arriva come corpo della richiesta POST.
Quando si esegue con
--agent o all’interno di un subagent, vengono inclusi due campi aggiuntivi:
Solo gli hook
SessionStart possono ricevere un campo model, e Claude Code non lo include sempre. Gli hook PreModelSwitch e PostModelSwitch ricevono from_model e to_model invece, quindi utilizzare un hook PostModelSwitch per seguire il modello mentre cambia durante una sessione.
Non esiste una variabile di ambiente $CLAUDE_MODEL. L’hook può leggere $ANTHROPIC_MODEL se lo imposti nella tua shell, ma quel valore non cambia quando cambi modelli con /model durante una sessione.
Un processo hook eredita l’ambiente padre, a parte le variabili dell’esportatore OTEL_* che Claude Code rimuove da ogni sottoprocesso che genera e, quando CLAUDE_CODE_SUBPROCESS_ENV_SCRUB è impostato su 1, le variabili che rimuove.
Ad esempio, un hook PreToolUse per un comando Bash riceve questo su stdin:
tool_name, tool_input e tool_use_id sono specifici dell’evento. Ogni sezione hook event documenta i campi aggiuntivi per quell’evento.
Output del codice di uscita
Il codice di uscita dal comando del hook dice a Claude Code se l’azione deve procedere, essere bloccata o essere ignorata. Il codice di uscita non agisce da solo. Claude Code legge i campi di output JSON da stdout su ogni codice di uscita, non solo 0, e per gli eventi che utilizzano il modello di decisione standard, un oggetto analizzato che passa la convalida dello schema ha effetto insieme al codice. Il blocco di Exit 2 è l’unico risultato che JSON non può sovrascrivere. Due tabelle possiedono le eccezioni per evento: Exit code 2 behavior per event dice cosa fanno i codici di uscita per ogni evento, e Decision control dice quali campi di decisione ogni evento onora. I campi universali comesystemMessage funzionano su la maggior parte degli eventi e sono elencati nella tabella JSON output.
Exit code 0
Exit 0 significa successo, ed è il codice di uscita previsto quando stampi JSON per il controllo strutturato. Per la maggior parte degli eventi, Claude Code scrive stdout nel log di debug e non lo mostra nella trascrizione. Le eccezioni sonoUserPromptSubmit, UserPromptExpansion, SessionStart e PostModelSwitch, dove Claude Code aggiunge stdout in testo semplice come contesto che Claude può vedere e su cui agire.
Se Claude Code legge il tuo stdout come JSON output o come testo semplice dipende da come inizia e finisce, ignorando gli spazi bianchi circostanti:
- Inizia con
{e finisce con}: Claude Code lo analizza come JSON. Quando l’output è due o più righe che si analizzano ciascuna come JSON da sole, e nessuna riga è un oggetto JSON output che imposta un campo, Claude Code tratta l’intero output come testo semplice. Quando una di quelle righe imposta un campo, l’intero output è un errore di analisi, descritto di seguito. - Inizia con
{ma non finisce con}: Claude Code lo tratta come testo semplice. - Inizia con qualsiasi altra cosa: Claude Code lo tratta come testo semplice, un array JSON o una stringa JSON tra virgolette inclusa.
<hook name> hook error con il messaggio di convalida. Lo stesso accade su qualsiasi codice di uscita diverso da 2, mentre exit 2 blocca ancora.
Per gli eventi che utilizzano il modello di decisione standard, quando Claude Code tenta di analizzare il tuo stdout come JSON e non può, segnala un errore non bloccante su ogni codice di uscita diverso da 2. La trascrizione mostra un avviso <hook name> hook error con il messaggio di analisi. Sugli eventi che aggiungono stdout in testo semplice come contesto, Claude Code non aggiunge il testo. Prima di v2.1.248, Claude Code trattava quello stdout come testo semplice.
Stderr da un hook che esce 0 va solo nel log di debug, mai nella trascrizione, e Claude non lo vede. Per leggerlo tu stesso, abilita debug logging. Per visualizzare un avviso a Claude da un hook PostToolUse o PostToolUseFailure, esci 2 invece in modo che Claude veda stderr anche se lo strumento è già stato eseguito.
Exit code 2
Exit 2 significa un errore bloccante. Su eventi che possono bloccare, exit 2 blocca indipendentemente dal fatto che stampi JSON: anche unpermissionDecision JSON di "allow" non può sovrascriverlo. Claude Code legge comunque qualsiasi JSON output valido su stdout. Su Elicitation e ElicitationResult, l’hookSpecificOutput di un hook exit-2 viene ignorato.
Il messaggio di blocco è il motivo dalla decisione di blocco del tuo JSON quando ne fa una, e il tuo testo stderr altrimenti. Cosa fa il blocco varia per evento: PreToolUse blocca la chiamata dello strumento, UserPromptSubmit rifiuta il prompt, e così via. Exit code 2 behavior per event elenca l’effetto per ogni evento, e ogni sezione dell’evento dice dove va il messaggio.
Un hook che esce 2 mentre stampa JSON che non supera la convalida dello schema JSON output blocca comunque: Claude Code utilizza stderr come motivo di blocco e registra l’errore di convalida nel log di debug. Prima di v2.1.214, Claude Code trattava quella combinazione come un errore non bloccante e l’azione procedeva.
Questo script blocca i comandi rm uscendo 2 e lascia ogni altro comando al flusso di autorizzazione normale:
Altri codici di uscita
Qualsiasi altro codice di uscita non blocca da solo per la maggior parte degli eventi hook. Cosa accade dipende dal tuo stdout:- Con un oggetto analizzato che passa la convalida dello schema, per gli eventi che utilizzano il modello di decisione standard, Claude Code ignora il codice di uscita e solo JSON decide il risultato:
- Ogni campo che l’evento supporta è onorato, inclusi
permissionDecision,additionalContext,updatedInputesystemMessage, e l’hook non viene segnalato come errore. - Decision control elenca i campi di decisione per evento; i campi universali come
systemMessageseguono la tabella JSON output.
- Ogni campo che l’evento supporta è onorato, inclusi
- Con un oggetto analizzato che non supera la convalida dello schema, per gli eventi che utilizzano il modello di decisione standard, è lo stesso errore non bloccante di su exit 0: l’azione procede, e l’avviso
<hook name> hook errorporta il messaggio di convalida. - Con stdout che Claude Code tenta di analizzare come JSON e non può, Claude Code segnala lo stesso errore non bloccante di exit 0 per gli eventi che utilizzano il modello di decisione standard. L’azione procede, e l’avviso porta il messaggio di analisi.
- Con stdout che Claude Code tratta come testo semplice, o con stdout vuoto, è un errore non bloccante per la maggior parte degli eventi hook: l’azione procede, e la trascrizione mostra un avviso
<hook name> hook errorseguito dalla prima riga di stderr, con il prefissoFailed with non-blocking status code:. Per acquisire lo stderr completo, abilita debug logging.
WorktreeCreate non riesce nella creazione su qualsiasi uscita diversa da zero indipendentemente da ciò che dice il tuo JSON, e gli eventi che scartano completamente l’output del hook, come StopFailure, ignorano il tuo JSON su ogni codice di uscita, a parte i campi di effetto collaterale come terminalSequence, che ancora si attivano.
Un hook che non può avviarsi finisce nello stesso bucket non bloccante. Quando il percorso dello script non esiste o non è eseguibile, la shell esce con un codice come 127 e vedi lo stesso avviso con il messaggio dell’interprete, ad esempio Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory. Per la maggior parte degli eventi hook, l’azione procede. Quando configuri un hook di policy, guarda questo avviso alla sua prima esecuzione: un percorso digitato male in settings.json lascia il gate silenziosamente disabilitato.
Timeout
A parte un command hook che esegui conasync: true, Claude Code annulla un hook command, http o mcp_tool che raggiunge il suo timeout, scartando l’output del hook, quindi su la maggior parte degli eventi un hook scaduto non rende alcuna decisione.
Su PreModelSwitch, un hook annullato al suo timeout blocca il cambio di modello. Su PreToolUse, le due famiglie di hook differiscono:
- Un hook
command,httpomcp_toolscaduto non blocca la chiamata dello strumento. La chiamata continua attraverso il flusso di autorizzazione normale, quindi non contare su un hook bloccato per agire come gate. - Un hook di callback Agent SDK che supera il suo timeout blocca la chiamata dello strumento.
Exit code 2 behavior per event
Exit code 2 è il modo in cui un hook segnala “fermarsi, non farlo”. L’effetto dipende dall’evento, perché alcuni eventi rappresentano azioni che possono essere bloccate (come una chiamata dello strumento che non è ancora accaduta) e altri rappresentano cose che sono già accadute o non possono essere prevenute.
Per
SessionStart, SubagentStart e PostModelSwitch, Claude Code rende lo stderr del codice di uscita 2 nella trascrizione come un avviso <hook name> hook error, nello stesso modo in cui rende un errore non bloccante. Claude non lo vede, e la sessione o il subagent procede. Per SubagentStart, l’avviso appare nella trascrizione del subagent stesso, non nella conversazione padre.
Gestione della risposta HTTP
Gli HTTP hook utilizzano i codici di stato HTTP e i corpi della risposta invece dei codici di uscita e stdout. I risultati di seguito si applicano a la maggior parte degli eventi; un evento con il suo proprio contratto di fallimento nella tabella per evento, comeWorktreeCreate, applica quel contratto a un hook HTTP fallito anche:
- 2xx con corpo vuoto: successo, equivalente al codice di uscita 0 senza output
- 2xx con corpo di oggetto JSON: analizzato utilizzando lo stesso schema JSON output dei command hook. Un corpo che non supera la convalida dello schema è un errore non bloccante
- 2xx con qualsiasi altro corpo, come testo semplice: errore non bloccante, gestito nello stesso modo di uno stato non-2xx. Claude Code non aggiunge il testo al contesto di Claude
- Stato non-2xx: errore non bloccante, l’esecuzione continua
- Guasto di connessione: errore non bloccante, l’esecuzione continua
- Timeout: l’hook viene annullato, come descritto sotto Timeouts
Output JSON
I codici di uscita ti permettono solo di bloccare o stare in silenzio, ma l’output JSON ti dà un controllo più granulare. Invece di uscire con il codice 2 per bloccare, esci 0 e stampa un oggetto JSON su stdout. Claude Code legge campi specifici da quel JSON per controllare il comportamento, incluso decision control per bloccare, consentire o escalare all’utente.Scegli un approccio per hook: usa i codici di uscita da soli per la segnalazione, o esci 0 e stampa JSON per il controllo strutturato. Se li mescoli, exit 2 mantiene il suo effetto di blocco, e Claude Code legge comunque i campi JSON, con l’eccezione di elicitazione notata sotto Exit code 2.
additionalContext, systemMessage e initialUserMessage del tuo hook, e il suo stdout semplice, sono limitate a 10.000 caratteri:
- Ambito: Claude Code misura ogni stringa da sola, anche quando più hook vengono eseguiti per lo stesso evento. Per l’output JSON, ogni campo viene misurato separatamente; lo stdout semplice viene misurato nel complesso.
- Oltre il limite: Claude Code salva l’output in un file nella directory della sessione e lo sostituisce con il percorso del file e un’anteprima di fino ai primi 2.000 caratteri. Un grande risultato Bash valido viene gestito nello stesso modo, descritto sotto Output limits. A differenza di quel limite Bash, questo limite non ha un’impostazione o una variabile di ambiente per aumentarlo.
- Lettura del file: Claude Code non chiede a Claude di leggere il file, quindi mantieni tutto ciò che Claude deve sempre vedere entro il limite.
- Campi universali come
continuesono elencati nella tabella di seguito. Ogni evento li accetta, ma alcuni eventi li scartano o consegnanosystemMessageda qualche parte diversa dalla trascrizione. Ogni sezione dell’evento lo specifica.terminalSequencefunziona su quegli eventi anche, con le eccezioni elencate sotto Emit terminal notifications. decisionereasondi livello superiore sono utilizzati da alcuni eventi per bloccare o fornire feedback.hookSpecificOutputè un oggetto annidato per gli eventi che necessitano di un controllo più ricco. Richiede un campohookEventNameimpostato sul nome dell’evento.
Per fermare Claude completamente:
PreToolUse e PostToolUse, l’arresto si applica anche quando la chiamata dello strumento fallisce o si completa mentre Claude sta ancora trasmettendo una risposta.
Emettere notifiche del terminale
Gli hook vengono eseguiti senza un terminale di controllo, quindi scrivere sequenze di escape direttamente su/dev/tty non riesce. Invece, restituire la sequenza di escape nel campo terminalSequence e Claude Code la emetterà per voi attraverso il suo percorso di scrittura del terminale. Questo è privo di race condition, funziona all’interno di tmux e GNU screen, e funziona su Windows dove non esiste /dev/tty.
Il campo accetta una stringa di una o più sequenze di escape nella lista di autorizzazione:
- OSC
0,1,2: titoli della finestra e dell’icona - OSC
9: notifiche iTerm2, ConEmu, Windows Terminal e WezTerm, incluso9;4progresso della barra delle applicazioni - OSC
99: notifiche Kitty - OSC
777: notifiche urxvt, Ghostty e Warp - BEL nudo
systemMessage e continue, come Notification e StopFailure. Ha due limiti:
- Claude Code scrive la sequenza solo in una sessione interattiva, e solo mentre la sua interfaccia è sullo schermo. In modalità non interattiva con il flag
-pe in Agent SDK, ignora il campo. - Un hook
WorktreeCreatecommand non può restituire JSON, perché Claude Code legge il suo stdout come il percorso del worktree. Un hook HTTPWorktreeCreaterestituisce JSON e può includere il campo.
Notification. La sequenza di escape viene costruita con escape ottali printf in modo che i byte di controllo non compaiano mai sulla riga di comando della shell, e jq -n --arg costruisce l’output JSON in modo che le virgolette, le barre rovesciate e le nuove righe nel messaggio di notifica siano correttamente sfuggite:
{ "terminalSequence": "..." } è la stessa da qualsiasi shell o linguaggio.
Aggiungere contesto per Claude
Il campoadditionalContext passa una stringa dal tuo hook nel contesto della finestra di Claude. Claude Code avvolge la stringa in un promemoria di sistema e la inserisce nella conversazione nel punto in cui l’hook si è attivato. Claude legge il promemoria nella prossima richiesta del modello, ma non appare come messaggio di chat nell’interfaccia.
Restituire additionalContext all’interno di hookSpecificOutput insieme al nome dell’evento:
- SessionStart e SubagentStart: all’inizio della conversazione, prima del primo prompt
- UserPromptSubmit e UserPromptExpansion: insieme al prompt inviato
- PreToolUse, PostToolUse, PostToolUseFailure e PostToolBatch: accanto al risultato dello strumento
- Stop e SubagentStop: alla fine del turno. La conversazione continua in modo che Claude possa agire sul feedback. Consultare Stop decision control
- PostModelSwitch: con la prossima richiesta dopo il cambio. Consultare PostModelSwitch decision control per i tempi
additionalContext per lo stesso evento, Claude riceve tutti i valori.
Se un valore supera 10.000 caratteri, Claude Code scrive il testo in un file nella directory della sessione e passa a Claude il percorso del file con un’anteprima di fino ai primi 2.000 caratteri invece. Claude può leggere il file, ma Claude Code non lo chiede.
Usa additionalContext per informazioni che Claude dovrebbe conoscere sullo stato corrente del tuo ambiente o sull’operazione appena eseguita:
- Stato dell’ambiente: il ramo corrente, la destinazione di distribuzione o i flag di funzionalità attivi
- Regole di progetto condizionali: quale comando di test si applica al file appena modificato, quali directory sono di sola lettura in questo worktree
- Dati esterni: problemi aperti assegnati a voi, risultati CI recenti, contenuto recuperato da un servizio interno
bun test” si leggono come informazioni di progetto. Il testo inquadrato come comandi di sistema fuori banda può attivare le difese di iniezione di prompt di Claude, il che causa a Claude di far emergere il testo a voi invece di trattarlo come contesto.
Claude Code salva il testo iniettato nella trascrizione della sessione. Per gli eventi a metà sessione come PostToolUse o UserPromptSubmit, quando riprendi con --continue o --resume, Claude Code riproduce il testo salvato piuttosto che rieseguire l’hook per i turni passati, quindi i valori come timestamp o SHA di commit diventano obsoleti. Gli hook SessionStart vengono eseguiti di nuovo al ripristino con source impostato su "resume", o "fork" se hai aggiunto --fork-session, quindi possono aggiornare il loro contesto.
Controllo della decisione
Non ogni evento supporta il blocco o il controllo del comportamento attraverso JSON. Gli eventi che lo fanno utilizzano ciascuno un insieme diverso di campi per esprimere quella decisione. Usa questa tabella come riferimento rapido prima di scrivere un hook:
Alcuni eventi possono anche riscrivere il contenuto piuttosto che solo consentire o bloccare:
PreToolUse:updatedInputdirettamente sottohookSpecificOutputsostituisce gli argomenti di uno strumento prima che venga eseguito. Consultare PreToolUse decision controlPermissionRequest:updatedInputall’interno dell’oggettodecision. Consultare PermissionRequest decision controlPostToolUse:updatedToolOutputsostituisce il risultato dello strumento. Consultare PostToolUse decision controlUserPromptSubmit: non può sostituire il prompt; solo iniettaadditionalContextinsieme ad esso
PreToolUse per gli input dello strumento in uscita e PostToolUse per i risultati dello strumento in entrata.
Ecco esempi di ogni modello in azione:
- Decisione di livello superiore
- PreToolUse
- PermissionRequest
L’unico valore per
decision è "block". Per consentire all’azione di procedere, omettere decision dal JSON, o uscire 0 senza alcun JSON:Eventi degli hook
Ogni evento corrisponde a un punto nel ciclo di vita di Claude Code in cui gli hook possono essere eseguiti. Le sezioni seguenti sono ordinate in base al ciclo di vita: dalla configurazione della sessione, attraverso il ciclo agentico, fino alla fine della sessione. Ogni sezione descrive quando l’evento si attiva, quali matcher supporta, l’input JSON che riceve e come controllarne il comportamento tramite l’output.SessionStart
Viene eseguito quando Claude Code avvia una nuova sessione o riprende una sessione esistente. Utile per caricare il contesto di sviluppo, come le issue esistenti o le modifiche recenti al tuo codebase, o per configurare variabili d’ambiente. Per un contesto statico che non richiede uno script, usa invece CLAUDE.md. SessionStart viene eseguito a ogni sessione, quindi mantieni questi hook veloci. Sono supportati solo gli hooktype: "command" e type: "mcp_tool". Consulta Campi degli hook per strumenti MCP per sapere quando vengono eseguiti gli hook mcp_tool.
Il valore del matcher corrisponde al modo in cui è stata avviata la sessione:
Prima della v2.1.214, le sessioni derivate riportavano la sorgente
"resume".
Quando avvii una sessione interattiva, riprendi una conversazione all’avvio con --continue o --resume, oppure esegui /clear, gli hook SessionStart vengono eseguiti in background. Puoi digitare subito, e una conversazione ripresa appare senza attendere gli hook. La prima risposta di Claude attende comunque il completamento degli hook, così il loro contesto raggiunge Claude.
Quando cambi conversazione con /resume all’interno di una sessione, il cambio invece attende il completamento degli hook. Se esegui /clear o passi a un’altra conversazione mentre gli hook in background sono ancora in esecuzione, nulla di ciò che restituiscono si applica alla sessione.
La stessa attesa si applica all’avvio, inclusa una sessione ripresa: un prompt che invii mentre gli hook SessionStart sono ancora in esecuzione non raggiunge Claude finché non terminano.
Durante entrambe le attese, premi Esc per riportare il prompt nell’input senza inviarlo. Gli hook continuano a essere eseguiti.
Input di SessionStart
Oltre ai campi di input comuni, gli hook SessionStart ricevonosource e, facoltativamente, model, agent_type e session_title:
Una sessione a cui non hai dato un nome può comunque avere un titolo generato. Quel titolo non è un titolo personalizzato e non compare in
session_title.
Quando source è "resume" o "fork" e la trascrizione contiene almeno una risposta di Claude, gli hook SessionStart ricevono anche i quattro campi seguenti. Il tuo hook può usarli per riportare quanto costa riprendere una conversazione datata prima della prima richiesta, ad esempio in un systemMessage. Questi campi richiedono Claude Code v2.1.251 o successiva.
Questo esempio mostra l’input per una sessione ripresa 90 minuti dopo la sua ultima risposta:
Controllo delle decisioni di SessionStart
Claude Code aggiunge al contesto di Claude lo stdout che tratta come testo semplice. Oltre ai campi di output JSON disponibili per tutti gli hook, puoi restituire questi campi specifici dell’evento:sessionTitle.
Usa reloadSkills quando un hook SessionStart installa o aggiorna delle skill. Il rilevamento delle skill viene normalmente eseguito prima che gli hook SessionStart terminino, quindi i file che l’hook scrive in ~/.claude/skills/ o .claude/skills/ apparirebbero altrimenti solo nella sessione successiva. Questo esempio sincronizza un repository di skill condiviso e richiede la nuova analisi:
fatal: su stderr. Lo stderr di un hook SessionStart che termina con 0 è solo informativo, quindi la richiesta reloadSkills si applica comunque.
Rendere persistenti le variabili d’ambiente
Gli hook SessionStart hanno accesso alla variabile d’ambienteCLAUDE_ENV_FILE, che fornisce il percorso di un file in cui puoi rendere persistenti le variabili d’ambiente per i successivi comandi Bash.
Per impostare singole variabili d’ambiente, scrivi istruzioni export in CLAUDE_ENV_FILE. Usa l’accodamento (>>) per preservare le variabili impostate da altri hook:
CLAUDE_ENV_FILE è disponibile per gli hook SessionStart, Setup, CwdChanged e FileChanged. Gli altri tipi di hook non hanno accesso a questa variabile.Setup
Si attiva solo quando avvii Claude Code con--init-only, oppure con --init o --maintenance in modalità non interattiva con il flag -p. Non si attiva al normale avvio. Usalo per l’installazione una tantum di dipendenze o per una pulizia pianificata che attivi esplicitamente da CI o da script, separatamente dal normale avvio della sessione. Per l’inizializzazione per sessione, usa invece SessionStart.
Il valore del matcher corrisponde al flag CLI che ha attivato l’hook:
Quando esegui
claude --init-only, Claude Code esegue gli hook Setup e gli hook SessionStart con il matcher startup, poi esce senza avviare una conversazione.
Quando avvii o continui una conversazione con -p, devi anche fornire un prompt, come argomento o tramite pipe su stdin. Puoi omettere il prompt quando un hook SessionStart fornisce initialUserMessage o quando riprendi una sessione con una chiamata a uno strumento differita.
In caso di successo, --init-only non stampa nulla nel terminale. Per verificare che gli hook siano stati eseguiti, avvia con claude --debug-file <path> --init-only, sostituendo <path> con la posizione di un file di log, e cerca nel log le voci degli hook Setup e SessionStart.
Poiché Setup non si attiva a ogni avvio, un plugin che necessita di una dipendenza installata non può fare affidamento solo su Setup. Lo schema pratico è verificare la dipendenza al primo utilizzo e installarla se manca, ad esempio con un hook o una skill che controlla ${CLAUDE_PLUGIN_DATA}/node_modules ed esegue npm install se assente. Consulta la directory dei dati persistenti per sapere dove archiviare le dipendenze installate. Se distribuisci il tuo plugin tramite un marketplace, potresti non aver bisogno di questo schema: Claude Code installa automaticamente le dipendenze idonee dei pacchetti Node.js quando memorizza il plugin nella cache.
Input di Setup
Oltre ai campi di input comuni, gli hook Setup ricevono un campotrigger impostato su "init" o "maintenance":
Controllo delle decisioni di Setup
Gli hook Setup non possono bloccare; l’esecuzione continua con qualsiasi codice di uscita. Con ogni codice di uscita, Claude Code scarta i campi di output JSON di un hook Setup, comesystemMessage, continue e hookSpecificOutput.additionalContext. Con -p, lo stdout, lo stderr e il codice di uscita di un hook Setup compaiono nell’output dell’esecuzione solo come eventi hook_response quando avvii con --output-format stream-json --verbose.
Gli hook Setup hanno accesso a CLAUDE_ENV_FILE. Le variabili scritte in quel file persistono nei successivi comandi Bash della sessione, esattamente come negli hook SessionStart. Su Setup vengono eseguiti solo gli hook type: "command". Un hook type: "mcp_tool" su Setup viene sempre saltato, come descritto in Campi degli hook per strumenti MCP.
InstructionsLoaded
Si attiva quando un fileCLAUDE.md o .claude/rules/*.md viene caricato nel contesto. Questo evento si attiva all’avvio della sessione per i file caricati subito e di nuovo in seguito quando i file vengono caricati in modo differito, ad esempio quando Claude accede a una sottodirectory che contiene un CLAUDE.md annidato o quando corrispondono regole condizionali con frontmatter paths:. L’hook non supporta il blocco né il controllo delle decisioni. Viene eseguito in modo asincrono a scopo di osservabilità.
Questo evento non si attiva quando Claude legge direttamente AGENTS.md tramite l’impostazione Project instructions. Si attiva invece quando un CLAUDE.md importa il tuo AGENTS.md, con load_reason impostato su include come per qualsiasi altro file importato, e quando CLAUDE.md è un collegamento simbolico ad esso, come un normale caricamento di CLAUDE.md.
Il matcher viene confrontato con load_reason. Ad esempio, usa "matcher": "session_start" per attivarlo solo per i file caricati all’avvio della sessione, oppure "matcher": "path_glob_match|nested_traversal" per attivarlo solo per i caricamenti differiti.
Input di InstructionsLoaded
Oltre ai campi di input comuni, gli hook InstructionsLoaded ricevono questi campi:Controllo delle decisioni di InstructionsLoaded
Gli hook InstructionsLoaded non hanno controllo delle decisioni. Non possono bloccare né modificare il caricamento delle istruzioni. Claude Code scarta i loro campi di output JSON, comesystemMessage e continue. Usa questo evento per log di audit, tracciamento della conformità o osservabilità.
UserPromptSubmit
Viene eseguito quando viene inviato un prompt, prima che Claude lo elabori. Questo ti consente di aggiungere contesto in base al prompt o alla conversazione, convalidare i prompt o bloccare determinati tipi di prompt. Gli hookUserPromptSubmit non vengono attivati solo sui prompt che digiti. Claude Code li esegue anche quando:
- Viene attivata un’attività pianificata, inclusa un’iterazione di
/loop - Un subagent in background riferisce alla sessione che l’ha avviato
- Un messaggio inviato da un’altra sessione arriva alla tua conversazione principale
UserPromptSubmit hanno un timeout predefinito di 30 secondi per i tipi command, http e mcp_tool, più breve del valore predefinito di 600 secondi per quei tipi nella maggior parte degli altri eventi. Poiché questo hook viene eseguito prima di ogni prompt e blocca l’elaborazione del modello finché non termina, un hook bloccato blocca la sessione. Se il tuo hook ha bisogno di più tempo, imposta il campo timeout nella voce dell’hook.
Fatta eccezione per un hook di comando che esegui con async: true, un hook UserPromptSubmit di comando, HTTP o di strumento MCP che raggiunge il suo timeout viene annullato e il suo output, incluso qualsiasi additionalContext, viene scartato. Il prompt raggiunge comunque Claude senza quel contesto. La trascrizione mostra un avviso che indica l’hook, il timeout scattato e che l’output è stato scartato.
Un hook di callback dell’Agent SDK su UserPromptSubmit che raggiunge il suo timeout blocca il prompt con un messaggio che indica l’hook e il timeout, perché un callback in quel punto può fungere da controllo di policy che non deve fallire in modo permissivo. La sessione continua. Prima della v2.1.208, un timeout di callback su quell’evento terminava il turno con un errore di esecuzione.
Input di UserPromptSubmit
Oltre ai campi di input comuni, gli hook UserPromptSubmit ricevono il campoprompt contenente il testo inviato. Il contenuto incollato che è stato compresso in un segnaposto [Pasted text #N] arriva espanso sul posto. Nelle sessioni in cui Claude Code contrassegna il testo incollato per Claude, quel contenuto espanso si trova tra una riga <pasted_content id="…"> e una riga </pasted_content id="…">, quindi tieni conto di queste righe se il tuo hook analizza il prompt.
Gli hook UserPromptSubmit ricevono anche session_title quando la sessione ha un titolo personalizzato, con lo stesso significato del campo session_title di SessionStart.
Controllo delle decisioni di UserPromptSubmit
Gli hookUserPromptSubmit possono controllare se un prompt inviato viene elaborato e aggiungere contesto. Sono disponibili tutti i campi di output JSON.
Ci sono due modi per aggiungere contesto alla conversazione con codice di uscita 0:
- Stdout in testo semplice: Claude Code aggiunge al contesto di Claude lo stdout che tratta come testo semplice
- JSON con
additionalContext: usa il formato JSON seguente per un maggiore controllo. Il campoadditionalContextviene aggiunto come contesto
additionalContext vengono ciascuno inseriti come promemoria di sistema che inizia con il nome dell’hook; Claude li legge entrambi. Per verificare il recapito, controlla il log di debug.
Per bloccare un prompt, restituisci un oggetto JSON con decision impostato su "block":
Un hook che blocca terminando con 2 segue lo stesso percorso di
reason: il messaggio di blocco mostra il testo di stderr all’utente e non viene aggiunto al contesto.
Cosa lascia un prompt bloccato
Un prompt bloccato non raggiunge mai Claude, ma il suo testo non viene rimosso ovunque. Per impostazione predefinita, il messaggio di blocco mostrato all’utente termina conOriginal prompt: seguito dal testo inviato, e Claude Code scrive quel messaggio nel file di trascrizione della sessione su disco. Per escludere il testo dal messaggio, stampa un JSON con "suppressOriginalPrompt": true all’interno di hookSpecificOutput. Funziona sia che l’hook blocchi con decision: "block" sia terminando con 2. Un hook con uscita 2 che non stampa JSON riceve sempre il testo del prompt nel suo messaggio di blocco.
suppressOriginalPrompt modifica solo il messaggio di blocco. Il testo inviato può comunque comparire in file locali come la trascrizione della sessione e la cronologia dei prompt, quindi un hook di blocco non è un modo per tenere un segreto fuori dal disco. Per limitare o rimuovere questi file, consulta Archiviazione in testo semplice e Cancellare i dati locali.
UserPromptExpansion
Viene eseguito quando un comando digitato dall’utente si espande in un prompt prima di raggiungere Claude. Usalo per impedire l’invocazione diretta di comandi specifici, inserire contesto per una particolare skill o registrare quali comandi invocano gli utenti. Ad esempio, un hook che corrisponde adeploy può bloccare /deploy a meno che non sia presente un file di approvazione, oppure un hook che corrisponde a una skill di revisione può aggiungere la checklist di revisione del team come additionalContext.
Questo evento copre il percorso che PreToolUse non copre: un hook PreToolUse che corrisponde allo strumento Skill si attiva solo quando Claude chiama lo strumento, ma digitare direttamente /skillname aggira PreToolUse. UserPromptExpansion si attiva su quel percorso diretto.
Effettua la corrispondenza su command_name. Lascia vuoto il matcher per attivarlo su ogni comando di tipo prompt.
Input di UserPromptExpansion
Oltre ai campi di input comuni, gli hook UserPromptExpansion ricevonoexpansion_type, command_name, command_args, command_source e la stringa prompt originale. Il campo expansion_type è slash_command per le skill e i comandi personalizzati, oppure mcp_prompt per i prompt dei server MCP.
Controllo delle decisioni di UserPromptExpansion
Gli hookUserPromptExpansion possono bloccare l’espansione o aggiungere contesto. Sono disponibili tutti i campi di output JSON.
Un hook che blocca terminando con 2 segue lo stesso percorso di
reason: il messaggio di blocco mostra il testo di stderr all’utente.
MessageDisplay
Viene eseguito mentre un messaggio dell’assistente viene trasmesso sullo schermo. Claude Code mostra il messaggio a incrementi: ogni volta che un gruppo di righe appena completate è pronto per essere visualizzato, l’hook viene eseguito una volta con quelle righe e Claude Code visualizza al loro posto il testo sostitutivo dell’hook. Un messaggio lungo produce diverse chiamate; un messaggio breve può produrne una sola. Usa MessageDisplay per:- rimuovere il markdown per una visualizzazione minimale
- trasformare il testo che un’applicazione Agent SDK mostra ai suoi utenti
- oscurare chiavi API o nomi host interni dalle risposte di Claude
timeout nella voce dell’hook.
MessageDisplay riguarda solo la visualizzazione: il testo sostitutivo cambia solo ciò che viene mostrato sullo schermo. La trascrizione e ciò che vede Claude mantengono il testo originale, quindi Claude non vede mai la sostituzione, e la modalità verbose mostra l’originale. L’hook riceve solo il testo dei messaggi dell’assistente, quindi i risultati degli strumenti e il testo che digiti vengono visualizzati senza modifiche.
MessageDisplay non supporta i matcher e si attiva per ogni messaggio dell’assistente che trasmette testo; i messaggi senza testo, come le risposte composte solo da chiamate agli strumenti, non lo attivano.
Nelle esecuzioni non interattive, incluse le query dell’Agent SDK e claude -p, MessageDisplay viene eseguito una volta per messaggio dell’assistente invece che una volta per gruppo di righe. La singola chiamata arriva dopo il completamento del messaggio e contiene il testo completo del messaggio: index è 0, final è true e delta contiene l’intero messaggio. Un hook che raccoglie il testo delta di ogni messaggio riceve lo stesso testo totale in entrambe le modalità.
Input di MessageDisplay
Oltre ai campi di input comuni, gli hook MessageDisplay ricevono gli identificatori del turno e del messaggio, la posizione di questa chiamata all’interno del messaggio e il nuovo testo indelta. I limiti dei gruppi dipendono da come viene trasmesso il testo, quindi usa index e final per seguire l’avanzamento in un messaggio invece di aspettarti che le righe siano raggruppate in un modo particolare.
Output di MessageDisplay
Oltre ai campi di output JSON disponibili per tutti gli hook, gli hook MessageDisplay possono restituiredisplayContent per sostituire il delta sullo schermo:
Gli hook MessageDisplay non hanno controllo delle decisioni. Non possono bloccare il messaggio né modificare ciò che viene archiviato nella trascrizione o inviato a Claude. Claude Code agisce su
displayContent dal loro output JSON e scarta systemMessage e continue.
Questo esempio rimuove la formattazione markdown dalle risposte di Claude per una visualizzazione in testo semplice. Lo script legge ogni gruppo da stdin, rimuove da delta i marcatori del grassetto e i backtick del codice inline, e restituisce il risultato come displayContent.
- macOS/Linux
- Windows (PowerShell)
Registra un hook di comando per l’evento nel tuo file di impostazioni:Salva questo script in
.claude/hooks/plain-display.sh nel tuo progetto e rendilo eseguibile con chmod +x:jq, Claude Code mostra il testo originale e segnala l’errore solo nell’output di debug, non nella sessione.
PreToolUse
Viene eseguito dopo che Claude ha creato i parametri dello strumento e prima di elaborare la chiamata allo strumento. Effettua la corrispondenza su qualsiasi nome di strumento tranneEndConversation: strumenti integrati come Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion e ExitPlanMode, e qualsiasi nome di strumento MCP.
Per eseguire un hook quando un file specifico cambia su disco, indipendentemente da chi l’ha scritto, usa FileChanged invece di far corrispondere per nome gli strumenti di modifica dei file. A differenza di PreToolUse, Claude Code esegue gli hook FileChanged dopo la modifica, e questi non hanno controllo delle decisioni, quindi non possono bloccare la scrittura.
Usa il controllo delle decisioni di PreToolUse per consentire, negare, chiedere o differire la chiamata allo strumento.
Un hook di callback dell’Agent SDK su PreToolUse che supera il suo timeout blocca la chiamata allo strumento, e Claude riceve un risultato di errore che indica il timeout. Un deny esplicito restituito da un altro hook ha comunque la precedenza.
Input di PreToolUse
Oltre ai campi di input comuni, gli hook PreToolUse ricevonotool_name, tool_input e tool_use_id.
Per uno strumento MCP, l’input contiene anche mcp_server, un oggetto con il name del server e un source che indica da dove proviene la definizione del server. I valori di source includono plugin, sdk e ambiti di configurazione come user e project. McpServerProvenance nel riferimento dell’Agent SDK li elenca tutti e spiega come trattarne uno che non riconosci. Basa le decisioni di fiducia su source anziché su name o sul prefisso mcp__<server>__ del nome dello strumento. Il campo mcp_server richiede Claude Code v2.1.274 o successiva.
Per gli strumenti sui file Write, Edit e Read, tool_input.file_path è sempre assoluto:
- Claude Code espande
~e i percorsi relativi prima che gli hook vengano eseguiti, quindi un hook che effettua la corrispondenza sui percorsi non può essere aggirato tramite~o una forma relativa dello stesso percorso - Su Windows, il percorso arriva con separatori backslash, anche quando il tuo hook viene eseguito in Git Bash dove
$PWDappare come/c/project - Un confronto scritto con slash, come un controllo
/src/, non corrisponde mai a un percorso con backslash, e la chiamata allo strumento procede come se l’hook non avesse nulla da bloccare - Normalizza i separatori prima di confrontare:
FILE_PATH="${FILE_PATH//\\//}"in Bash, ofile_path.replace("\\", "/")in Python, poi fai corrispondere un segmento di percorso come/src/anziché ancorare con^, poiché il percorso è assoluto
Write su Windows fornisce:
tool_input dipendono dallo strumento:
Esegue comandi shell.
Quando un comando Bash modifica file in un repository Git, Claude Code può registrare cosa è cambiato. Registra le modifiche in ogni modalità di permesso quando l’impostazione
bashEditDiffEnabled attiva la registrazione; la voce di quell’impostazione indica quali file possono impostarla. Altrimenti le registra solo in modalità auto e in modalità bypassPermissions, e solo quando Claude Code indica a Claude di modificare i file tramite Bash. Imposta bashEditDiffEnabled su false per disattivare la registrazione. I comandi in background e i comandi di sola lettura non includono alcun diff.
Il tuo hook PostToolUse riceve quindi i file modificati in tool_response.bashEditDiff. L’elenco copre ciò che è cambiato nel repository durante l’esecuzione del comando. I file ignorati da Git e i file nei submodule non sono elencati. Richiede Claude Code v2.1.269 o successiva.
L’elenco è fornito al meglio delle possibilità ed è in beta pubblica. Claude Code può perdere una modifica, includere un file che un altro processo ha modificato nello stesso momento o fermarsi ai suoi limiti di dimensione. La struttura del campo potrebbe cambiare. Usa l’elenco per trovare cosa revisionare, non per applicare una policy.
changedFiles e files elencano ciò che il comando ha modificato; i campi rimanenti indicano quanto è completo e quanto è affidabile quell’elenco.
Esegue comandi PowerShell. Consulta lo strumento PowerShell per la disponibilità per piattaforma.
I campi corrispondono a quelli dello strumento Bash, con la stringa del comando in
command:
Usa
Bash|PowerShell come corrispondenza negli hook che ispezionano i comandi shell, così coprono entrambi gli strumenti:
- Su Windows, ovunque lo strumento PowerShell sia abilitato, Claude tratta PowerShell come shell principale e vi instrada i comandi shell.
- Su Windows senza Git Bash, lo strumento è abilitato automaticamente e Claude Code non registra affatto lo strumento Bash.
- Un hook che corrisponde solo a
Bashnon si attiva mai in quel caso.
Sostituisce una stringa in un file esistente.
Legge il contenuto dei file.
Trova i file che corrispondono a un pattern glob.
Cerca nel contenuto dei file con espressioni regolari.
Recupera ed elabora contenuti web.
Esegue ricerche sul web.
Avvia un subagent.
Quando una chiamata Agent in primo piano viene completata, il tuo hook PostToolUse riceve il risultato del subagent e la telemetria dell’esecuzione in
tool_response. Leggi questi campi per ispezionare l’esecuzione; per i totali di token e costi tra i subagent, usa i contatori di token e costi filtrati per query_source "subagent", poiché totalTokens e usage coprono solo la richiesta finale:
Su Claude Code v2.1.271 o successiva, un subagent che viene eseguito con lo strumento
SubagentHandback, che Claude Code fornisce in modalità auto, consegna il suo rapporto tramite quello strumento anziché restituirlo come testo. Il campo content del suo risultato completed contiene quindi una breve nota su quella consegna anziché il rapporto stesso. Per leggere il rapporto, fai corrispondere un hook PreToolUse o PostToolUse a SubagentHandback e leggi tool_input.message.
Per i subagent in background, lo strumento restituisce quando l’attività passa in background, quindi tool_response non contiene campi di utilizzo: un avvio in background restituisce immediatamente, e un’attività in primo piano che Claude Code sposta in background durante l’esecuzione restituisce in quel momento di transizione. Ha status: "async_launched", agentId, description, prompt, outputFile e resolvedModel.
In una risposta completed, resolvedModel indica il modello con cui è partito il subagent, che può differire dal valore model in tool_input, ad esempio quando si applica availableModels o un altro override. In una risposta async_launched, resolvedModel indica il modello in uso quando l’agente è passato in background, quindi un cambio avvenuto prima del passaggio in background si riflette lì. modelsUsed e il comportamento di resolvedModel al momento del passaggio in background richiedono Claude Code v2.1.212 o successiva.
Pone all’utente da una a quattro domande a scelta multipla.
Presenta un piano e chiede all’utente di approvarlo prima che Claude esca dal plan mode. Claude scrive il piano in un file su disco prima di chiamare lo strumento, quindi il
tool_input letterale del modello è in genere vuoto. Claude Code inserisce il contenuto del piano e il percorso del file prima di passare l’input agli hook.
In
PostToolUse, tool_response è un oggetto con i campi plan e filePath che contengono il piano approvato, oltre a flag di stato interni. Leggi tool_response.plan per il contenuto del piano anziché rileggere il file dal disco.
Controllo delle decisioni di PreToolUse
Gli hookPreToolUse possono controllare se una chiamata a uno strumento procede. A differenza di altri hook che usano un campo decision di primo livello, PreToolUse restituisce la sua decisione all’interno di un oggetto hookSpecificOutput. Questo gli offre un controllo più ricco: quattro esiti (allow, deny, ask o defer) più la possibilità di modificare l’input dello strumento prima dell’esecuzione.
Quando più hook PreToolUse restituiscono decisioni diverse, l’ordine di precedenza è
deny > defer > ask > allow.
Un hook che blocca terminando con 2 segue lo stesso percorso di "deny": Claude vede il messaggio di stderr come motivo del rifiuto.
Quando un hook restituisce "ask", la richiesta di permesso mostrata all’utente include un’etichetta che identifica la provenienza dell’hook: [settings] per un hook da qualsiasi file di impostazioni o dal frontmatter di un agente, [plugin:<name>] per l’hook di un plugin, o [skill] per un hook dal frontmatter di una skill. Questo aiuta gli utenti a capire quale fonte di configurazione sta richiedendo la conferma.
Un "ask" di un hook forza una richiesta di permesso anche in modalità auto: il classificatore può comunque negare la chiamata allo strumento, ma non può approvarla silenziosamente. Prima della v2.1.211, il classificatore poteva approvare un comando Bash eseguito fuori dalla sandbox senza mostrare la richiesta voluta dall’hook; il classificatore applicava comunque le proprie regole di sicurezza a quel comando, e un "deny" di un hook veniva sempre rispettato.
-p, Claude Code offre AskUserQuestion e ExitPlanMode solo quando l’esecuzione ha un host dei permessi che riceva la richiesta, come un callback canUseTool dell’Agent SDK. Questi strumenti richiedono l’interazione dell’utente. Restituire permissionDecision: "allow" insieme a updatedInput soddisfa questo requisito: l’hook legge l’input dello strumento da stdin, raccoglie la risposta tramite la tua interfaccia e la restituisce in updatedInput così lo strumento viene eseguito senza chiedere. Restituire solo "allow" non è sufficiente per questi strumenti. Per AskUserQuestion, restituisci l’array questions originale e aggiungi un oggetto answers che associ il testo di ogni domanda alla risposta scelta.
Uno strumento MCP che il suo server contrassegna con _meta["anthropic/requiresUserInteraction"] è più restrittivo: un hook non può saltarne la richiesta di approvazione con "allow", con o senza updatedInput, perché Claude Code non può verificare che l’hook abbia raccolto l’interazione di cui lo strumento ha bisogno.
PreToolUse in precedenza usava i campi di primo livello
decision e reason, ma questi sono deprecati per questo evento. Usa invece hookSpecificOutput.permissionDecision e hookSpecificOutput.permissionDecisionReason. I valori deprecati "approve" e "block" corrispondono rispettivamente a "allow" e "deny". Altri eventi come PostToolUse e Stop continuano a usare decision e reason di primo livello come formato attuale.Differire una chiamata a uno strumento
"defer" è pensato per le integrazioni che eseguono claude -p come sottoprocesso e ne leggono l’output JSON, come un’app Agent SDK o un’interfaccia personalizzata costruita su Claude Code. Permette a quel processo chiamante di mettere in pausa Claude a una chiamata a uno strumento, raccogliere input tramite la propria interfaccia e riprendere da dove si era interrotto. Claude Code rispetta questo valore solo in modalità non interattiva con il flag -p. Nelle sessioni interattive registra un avviso e ignora il risultato dell’hook.
Lo strumento AskUserQuestion è il caso tipico: Claude vuole chiedere qualcosa all’utente, ma non c’è un terminale in cui rispondere. Un’esecuzione -p offre AskUserQuestion solo quando ha un host dei permessi, come uno strumento MCP che passi con --permission-prompt-tool, quindi avvia l’esecuzione con uno di essi. Il ciclo completo funziona così:
- Claude chiama
AskUserQuestion. L’hookPreToolUsesi attiva. - L’hook restituisce
permissionDecision: "defer". Lo strumento non viene eseguito. Il processo esce constop_reason: "tool_deferred"e la chiamata allo strumento in sospeso conservata nella trascrizione. - Il processo chiamante legge
deferred_tool_usedal risultato dell’SDK, presenta la domanda nella propria interfaccia e attende una risposta. - Il processo chiamante esegue
claude -p --resume <session-id>con lo stesso host dei permessi. La stessa chiamata allo strumento attiva di nuovoPreToolUse. - L’hook restituisce
permissionDecision: "allow"con la risposta inupdatedInput. Lo strumento viene eseguito e Claude continua.
deferred_tool_use contiene id, name e input dello strumento. L’input è costituito dai parametri che Claude ha generato per la chiamata allo strumento, catturati prima dell’esecuzione:
cleanupPeriodDays, che elimina i file di sessione dopo 30 giorni per impostazione predefinita, secondo le regole di pulizia di conservazione. Se la risposta non è pronta quando riprendi, l’hook può restituire di nuovo "defer" e il processo esce allo stesso modo. Il processo chiamante controlla quando interrompere il ciclo restituendo infine "allow" o "deny" dall’hook.
"defer" funziona solo quando Claude effettua una singola chiamata a uno strumento nel turno. Se Claude effettua più chiamate agli strumenti contemporaneamente, "defer" viene ignorato con un avviso e lo strumento procede attraverso il normale flusso dei permessi. Il vincolo esiste perché la ripresa può rieseguire un solo strumento: non c’è modo di differire una chiamata di un gruppo senza lasciare irrisolte le altre.
Se lo strumento differito non è più disponibile quando riprendi, il processo esce con stop_reason: "tool_deferred_unavailable" e is_error: true prima che l’hook si attivi. Questo accade quando un server MCP che forniva lo strumento non è connesso per la sessione ripresa. Il payload deferred_tool_use viene comunque incluso così puoi identificare quale strumento è venuto a mancare.
Per riprendere una sessione differita in plan mode, passa
--permission-prompt-tool insieme a --resume così che Claude Code possa presentare il piano per l’approvazione. Se passi determinati altri flag di avvio, l’esecuzione ripresa non torna al plan mode; consulta Riprendere in plan mode con -p. Richiede Claude Code v2.1.246 o successiva.Quando riprendi con -p, Claude Code non ripristina nessun’altra modalità di permesso memorizzata. Avvia l’esecuzione nella modalità di permesso in cui si avvierebbe una nuova esecuzione claude -p, quindi passa di nuovo --permission-mode o --dangerously-skip-permissions se la sessione differita ne usava uno. Quando riprendi con claude --resume <session-id> senza -p, Claude Code ripristina la modalità di permesso memorizzata, con le eccezioni elencate in modalità di permesso alla ripresa.PermissionRequest
Viene eseguito quando Claude Code sta per chiederti il permesso di usare uno strumento. Nelle sessioni che non possono mostrare una richiesta, come i subagent in background in modalità non interattiva, Claude Code esegue comunque questi hook, e se nessun hook restituisce una decisione, nega la chiamata allo strumento. Per una chiamata che raggiunge un--permission-prompt-tool o il callback canUseTool dell’Agent SDK, gli hook vengono eseguiti insieme al tuo host, e si applica chi decide per primo.
Usa il controllo delle decisioni di PermissionRequest per consentire o negare per conto dell’utente.
Usa questo evento quando ti serve un segnale nel momento in cui Claude chiede il permesso di usare uno strumento. Claude Code esegue un hook Notification con il tipo permission_prompt solo dopo che la richiesta è rimasta in attesa per circa sei secondi.
Claude Code non esegue gli hook PermissionRequest per la richiesta di rete di un comando in sandbox. Per ottenere un segnale per quella richiesta, usa il tipo di notifica permission_prompt.
Effettua la corrispondenza sul nome dello strumento, con gli stessi valori di PreToolUse.
Input di PermissionRequest
Gli hook PermissionRequest ricevono i campitool_name e tool_input come gli hook PreToolUse, ma senza tool_use_id. Per uno strumento MCP, ricevono anche l’oggetto mcp_server. Un array facoltativo permission_suggestions contiene gli aggiornamenti dei permessi che Claude Code suggerisce per questa richiesta, come l’aggiunta di una regola allow o il cambio della modalità di permesso.
L’array permission_suggestions non è un elenco esatto delle opzioni che vedi, perché ogni finestra di dialogo dei permessi costruisce le proprie opzioni. Alcune finestre, come quella per le modifiche ai file, non leggono affatto l’array e ricavano le opzioni dalla richiesta stessa. Una finestra che lo legge può comunque nascondere un’opzione il cui suggerimento rimane nell’array, ad esempio quando allowManagedPermissionRulesOnly nasconde le opzioni di salvataggio delle regole. Può anche offrire opzioni che non hanno una voce di suggerimento, come Yes, and switch to auto mode, che cambia la modalità di permesso direttamente anziché tramite un aggiornamento dei permessi.
Gli hook PreToolUse vengono eseguiti prima di ogni chiamata a uno strumento, che richieda o meno un permesso. Gli hook PermissionRequest vengono eseguiti solo quando Claude Code sta per chiederti un permesso, o quando altrimenti negherebbe automaticamente una chiamata che non può mostrare una richiesta. Nessuno dei due eventi si attiva per EndConversation.
Controllo delle decisioni di PermissionRequest
Gli hookPermissionRequest possono consentire o negare le richieste di permesso. Oltre ai campi di output JSON disponibili per tutti gli hook, il tuo script di hook può restituire un oggetto decision con questi campi specifici dell’evento:
Un hook che termina con 2 senza un oggetto
decision lascia invariato il flusso dei permessi, e il suo stderr viene scartato. Solo l’oggetto decision può concedere o negare la richiesta.
Voci di aggiornamento dei permessi
Il campo di outputupdatedPermissions e il campo di input permission_suggestions usano entrambi lo stesso array di oggetti voce. Ogni voce ha un type che determina gli altri campi e una destination che controlla dove viene scritta la modifica.
setMode con bypassPermissions ha effetto solo se hai avviato la sessione con la modalità bypass già disponibile: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions o permissions.defaultMode: "bypassPermissions" nelle impostazioni utente, --settings o impostazioni gestite. Altrimenti l’aggiornamento non ha alcun effetto. L’aggiornamento non ha effetto anche quando permissions.disableBypassPermissionsMode disabilita la modalità, o quando la sessione si avvia in modalità ristretta.bypassPermissions non viene mai reso persistente come defaultMode, indipendentemente da destination.destination di ogni voce determina se la modifica rimane in memoria o viene resa persistente in un file di impostazioni.
Un hook può restituire uno dei
permission_suggestions ricevuti come proprio output updatedPermissions.
PostToolUse
Viene eseguito subito dopo che uno strumento è stato completato con successo. Fa corrispondere il nome dello strumento, con gli stessi valori di PreToolUse. Usa una corrispondenza più ampia quando il nome dello strumento non è il filtro giusto:- Per eseguire un hook dopo che qualsiasi strumento è stato completato con successo, ometti il
matchero impostalo su"*". Il tuo hook può quindi scoprire da solo cosa è cambiato, ad esempio eseguendogit status --porcelain, che elenca anche i file non tracciati chegit diffnon rileva. Per le chiamate agli strumenti che falliscono, aggiungi lo stesso hook sotto PostToolUseFailure. - Per eseguire un hook quando un file specifico cambia su disco, qualunque cosa lo abbia scritto, usa FileChanged. Claude Code non esegue un hook
PostToolUseche corrisponde aEdit|Writequando un comandoBasho un processo esterno a Claude Code riscrive lo stesso file.
Input di PostToolUse
Gli hookPostToolUse si attivano dopo che uno strumento è già stato eseguito con successo. L’input include sia tool_input, gli argomenti inviati allo strumento, sia tool_response, il risultato che ha restituito. Lo schema esatto di entrambi dipende dallo strumento. I percorsi in tool_input degli strumenti per i file arrivano nello stesso formato di PreToolUse: sempre assoluti, con i separatori nativi della piattaforma, quindi barre rovesciate su Windows. Per uno strumento MCP, l’input include anche l’oggetto mcp_server.
Controllo delle decisioni di PostToolUse
Gli hookPostToolUse possono fornire feedback a Claude dopo l’esecuzione dello strumento. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del tuo hook può restituire questi campi specifici dell’evento:
L’esempio seguente sostituisce l’output di una chiamata
Bash. Il valore sostitutivo corrisponde alla forma dell’output dello strumento Bash:
Annotare un risultato per il classificatore della modalità auto
RestituisciclassifierContext per inviare una breve nota sul risultato della chiamata allo strumento al classificatore della modalità auto anziché a Claude. Il classificatore non riceve mai i risultati degli strumenti in sé, quindi questo campo è il modo supportato per comunicargli qualcosa su ciò che una chiamata ha restituito prima che esamini le azioni successive. Il campo richiede Claude Code v2.1.236 o successiva.
L’esempio seguente indica al classificatore da dove proviene l’output di una query:
- Hook configurati in Claude Code: per gli hook provenienti da file di impostazioni, plugin, skill e frontmatter degli agenti, il classificatore tratta la nota come contesto non verificato fornito dall’applicazione. La nota non stabilisce mai l’intento dell’utente e, se afferma che hai approvato o richiesto qualcosa, il classificatore verifica tale affermazione confrontandola con i tuoi messaggi nella conversazione
- Callback in-process dell’Agent SDK: quando un’applicazione che incorpora Claude Code registra l’hook come callback dell’SDK TypeScript e restituisce la nota durante la sessione attiva, il classificatore può considerare come intento dell’utente una dichiarazione dell’utente riportata nella nota. Tale dichiarazione può soddisfare un requisito di consenso che il classificatore accetterebbe da un messaggio inviato da te, ma non rimuove mai un blocco che nemmeno un tuo messaggio potrebbe rimuovere. Dopo la ripresa di una sessione, Claude Code tratta le note ripristinate come contesto non verificato. Quando hook di entrambi i gruppi annotano la stessa chiamata, il classificatore tratta la nota combinata come non verificata
- Lunghezza: Claude Code limita le note per una singola chiamata a uno strumento a 2.000 caratteri e tronca il resto. Il limite è condiviso tra tutti gli hook che rispondono a quella chiamata
- Solo risposte sincrone: Claude Code ignora il campo nella risposta di un hook che viene eseguito in background, perché quella risposta arriva dopo che Claude Code ha registrato il risultato dello strumento
- Chiamate che il classificatore non registra: la trascrizione del classificatore omette le consultazioni di sola lettura come letture di file e ricerche. Claude Code scarta una nota associata a una di queste chiamate
- Interazione con le riscritture: quando la nota descrive un output che stai sostituendo con
updatedToolOutput, restituisci entrambi i campi nella stessa risposta dell’hook. Claude Code scarta la nota se quella riscrittura viene rifiutata o se la riscrittura di un altro hook la sostituisce. Claude Code consegna una nota restituita senza riscrittura anche quando un altro hook riscrive l’output
PostToolUseFailure
Viene eseguito quando uno strumento che ha iniziato l’esecuzione fallisce: lo strumento ha generato un errore o uno strumento MCP ha restituito un risultato di errore. Usalo per registrare i fallimenti, inviare avvisi o fornire feedback correttivo a Claude. Fa corrispondere il nome dello strumento, con gli stessi valori di PreToolUse.Questo evento non si attiva per le chiamate agli strumenti rifiutate prima dell’esecuzione: un nome di strumento sconosciuto, un input che non supera la convalida dello schema o quella specifica dello strumento, o un permesso negato. I rifiuti di convalida vengono restituiti come risultati
tool_use_error e avvengono prima dell’esecuzione degli hook, quindi non attivano né PreToolUse né PostToolUseFailure. I permessi negati attivano PreToolUse ma non questo evento; consulta PermissionDenied.Input di PostToolUseFailure
Gli hook PostToolUseFailure ricevono gli stessi campitool_name e tool_input di PostToolUse, insieme alle informazioni sull’errore come campi di primo livello. Per uno strumento MCP, ricevono anche l’oggetto mcp_server. Ad esempio, un comando npm test fallito potrebbe restituire:
La stringa
error è generalmente lo stesso testo che Claude riceve come risultato dello strumento fallito. Il suo formato varia in base allo strumento e al tipo di fallimento. Basa il tuo hook su tool_name, is_interrupt e sulla prima riga Exit code N; tratta il resto della stringa come testo di visualizzazione, non come un formato stabile.
- Per Bash e PowerShell, un comando eseguito e terminato produce una prima riga
Exit code N, seguita da qualsiasi output prodotto dal comando come un unico blocco con stdout e stderr intercalati - Un payload può anche contenere un semplice messaggio di errore senza riga del codice di uscita, quando Claude Code non è riuscito ad avviare il processo della shell stesso
- Claude Code tronca al centro le stringhe lunghe attorno a un indicatore
... [N characters truncated] ...e può inserire righe proprie, comeCommand timed out after 2m 0s
Controllo delle decisioni di PostToolUseFailure
Gli hookPostToolUseFailure possono fornire contesto a Claude dopo il fallimento di uno strumento. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del tuo hook può restituire questi campi specifici dell’evento:
PostToolBatch
Viene eseguito una volta dopo che ogni chiamata a uno strumento in un batch è stata risolta, prima che Claude Code invii la richiesta successiva al modello.PostToolUse si attiva una volta per strumento, il che significa che si attiva in modo concorrente quando Claude effettua chiamate agli strumenti in parallelo. PostToolBatch si attiva esattamente una volta con l’intero batch, quindi è il punto giusto per iniettare contesto che dipende dall’insieme degli strumenti eseguiti anziché da un singolo strumento. Non esiste un matcher per questo evento.
Input di PostToolBatch
Oltre ai campi di input comuni, gli hook PostToolBatch ricevonotool_calls, un array che descrive ogni chiamata a uno strumento nel batch:
tool_response contiene lo stesso contenuto che il modello riceve nel blocco tool_result corrispondente. Il valore è una stringa serializzata o un array di blocchi di contenuto, esattamente come lo strumento l’ha emesso. Per Read, ciò significa testo con prefisso del numero di riga anziché il contenuto grezzo del file. Le risposte possono essere di grandi dimensioni, quindi analizza solo i campi di cui hai bisogno.
La forma di
tool_response è diversa da quella di PostToolUse. PostToolUse passa l’oggetto Output strutturato dello strumento, come {filePath: "...", type: "create"} per Write; PostToolBatch passa il contenuto tool_result serializzato che vede il modello.Controllo delle decisioni di PostToolBatch
Gli hookPostToolBatch possono iniettare contesto per Claude. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del tuo hook può restituire questi campi specifici dell’evento:
decision: "block" o continue: false interrompe il ciclo agentico prima della chiamata successiva al modello. Il messaggio di blocco proviene dal reason o dallo stopReason del JSON, oppure da stderr con uscita 2. Lo vedi come avviso nella trascrizione e rimane nella conversazione, quindi Claude lo vede quando la conversazione prosegue.
PermissionDenied
Viene eseguito quando la modalità auto nega una chiamata a uno strumento, anche quando la nega senza un verdetto del classificatore perché un controllo di sicurezza separato dalla modalità auto ha rifiutato la richiesta del classificatore stesso o la sua risposta non è stata analizzata correttamente. Questo hook si attiva solo in modalità auto: non viene eseguito quando neghi manualmente una finestra di dialogo dei permessi, quando un hookPreToolUse blocca una chiamata o quando corrisponde una regola deny. Usalo per registrare i dinieghi, modificare la configurazione o dire al modello che può riprovare la chiamata allo strumento.
Fa corrispondere il nome dello strumento, con gli stessi valori di PreToolUse.
Input di PermissionDenied
Oltre ai campi di input comuni, gli hook PermissionDenied ricevonotool_name, tool_input, tool_use_id e reason. Per uno strumento MCP, ricevono anche l’oggetto mcp_server.
Controllo delle decisioni di PermissionDenied
Gli hook PermissionDenied possono dire al modello che può riprovare la chiamata allo strumento negata. Restituisci un oggetto JSON conhookSpecificOutput.retry impostato su true:
retry è true, Claude Code aggiunge un messaggio alla conversazione che dice al modello che può riprovare la chiamata allo strumento. Claude Code non annulla il diniego stesso. Se il tuo hook non restituisce JSON o restituisce retry: false, il diniego resta valido e il modello riceve il messaggio di rifiuto originale.
Claude Code ignora retry: true quando il classificatore non ha prodotto alcun verdetto sull’azione: la sua risposta non è stata analizzata correttamente, oppure un controllo di sicurezza separato dalla modalità auto ha rifiutato la richiesta del classificatore stesso. Per questi dinieghi, Claude Code indica già al modello nel messaggio di rifiuto se riprovare più tardi o andare avanti.
Notification
Viene eseguito quando Claude Code invia notifiche. Fa corrispondere il tipo di notifica. Ometti il matcher per eseguire gli hook per tutti i tipi di notifica. Ricevi questi eventi hook anche con le notifiche desktop disattivate: l’impostazionepreferredNotifChannel, incluso notifications_disabled, cambia solo il modo in cui vieni avvisato, non se il tuo hook viene eseguito.
I tipi
quota_auto_resume_fired, quota_auto_resume_stale e quota_auto_resume_disabled richiedono Claude Code v2.1.234 o successiva.
Nelle sessioni nel terminale, permission_prompt per la richiesta di rete di un comando in sandbox richiede Claude Code v2.1.246 o successiva.
agent_needs_input per la domanda di configurazione del terminale di un compagno di team richiede Claude Code v2.1.248 o successiva.
I tipi
permission_prompt, idle_prompt, elicitation_dialog ed elicitation_url_dialog condividono le tempistiche con le notifiche desktop, quindi nelle sessioni nel terminale li vedi solo quando sembri essere lontano dal terminale:- Aspettati
permission_promptquando non digiti da circa sei secondi. Il timer parte quando appare la richiesta di permesso e ogni pressione di tasto lo posticipa. Per eseguire un hook immediatamente quando Claude chiede il permesso di usare uno strumento, usa invece PermissionRequest. - Aspettati
idle_promptcirca 60 secondi dopo che Claude ha finito di rispondere, e solo se da allora non hai digitato nulla e nessun agente in background, come un subagent in background, è ancora in esecuzione. Claude Code non inviaidle_promptmentre attende il reset di un limite di utilizzo di claude.ai. Quando l’attesa termina da sola, si attiva invece uno dei tipiquota_auto_resume_*. - Aspettati
elicitation_dialogper un modulo di elicitazione, oelicitation_url_dialogper una richiesta di URL nel browser, quando non digiti da circa sei secondi. Entrambi condividono la stessa soglia di sei secondi dipermission_prompt: il timer parte quando appare la finestra di dialogo e ogni pressione di tasto lo posticipa.
permission_prompt in modo diverso nelle sessioni in cui invia le richieste di permesso alla callback canUseTool dell’Agent SDK, che è il modo in cui Claude Desktop e l’estensione VS Code ospitano Claude Code:
- Aspettati
permission_promptcirca sei secondi dopo che Claude chiede il permesso. Claude Code non lo posticipa mentre digiti. - Se tu o un hook PermissionRequest rispondete prima, Claude Code non esegue
permission_prompt. - Imposta
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSsu1per disattivarepermission_promptin queste sessioni.
permission_prompt non si attivava in queste sessioni.
Usa matcher separati per eseguire gestori diversi a seconda del tipo di notifica. Questa configurazione attiva uno script di avviso specifico per i permessi quando Claude ha bisogno di un’approvazione dei permessi e una notifica diversa quando Claude è inattivo:
Input di Notification
Oltre ai campi di input comuni, gli hook Notification ricevonomessage con il testo della notifica, un title facoltativo e notification_type che indica quale tipo si è attivato.
systemMessage e continue ma emette comunque terminalSequence, su cui si basa l’esempio di notifica desktop. Gli hook Notification sono pensati per effetti collaterali come l’inoltro della notifica a un servizio esterno.
SubagentStart
Viene eseguito quando Claude genera un subagent con lo strumento Agent, quando Claude riprende un subagent e ogni volta che un compagno in-process di un team di agenti gestisce un nuovo messaggio. Supporta i matcher per filtrare in base al nome del tipo di agente. Per gli agenti integrati, si tratta del nome dell’agente comegeneral-purpose, Explore o Plan. Per i subagent personalizzati, si tratta del campo name del frontmatter dell’agente, non del nome del file.
Per i subagent forniti da un plugin, il tipo di agente è l’identificatore con ambito del plugin come my-plugin:reviewer, non il semplice nome del frontmatter. I due punti fanno sì che un nome con ambito del plugin venga trattato come espressione regolare, quindi ancora il matcher con ^ e $ per una corrispondenza esatta: ^my-plugin:reviewer$.
Input di SubagentStart
Oltre ai campi di input comuni, gli hook SubagentStart ricevonoagent_id con l’identificatore univoco del subagent e agent_type con il nome dell’agente su cui filtra il matcher.
SubagentStop
Viene eseguito quando un subagent di Claude Code ha finito di rispondere. Fa corrispondere il tipo di agente, con gli stessi valori di SubagentStart.Input di SubagentStop
Oltre ai campi di input comuni, gli hook SubagentStop ricevonostop_hook_active, agent_id, agent_type, agent_transcript_path e last_assistant_message. Il campo agent_type è il valore usato per il filtro del matcher. transcript_path è la trascrizione della sessione principale, mentre agent_transcript_path è la trascrizione del subagent stesso, archiviata in una cartella annidata subagents/. Il campo last_assistant_message contiene il contenuto testuale della risposta finale del subagent, quindi gli hook possono accedervi senza analizzare il file della trascrizione.
Non tutti gli eventi SubagentStop provengono da un subagent generato da Claude. Claude Code esegue anche agenti interni per alcune delle sue funzionalità, come i suggerimenti di prompt e le domande laterali con /btw, e SubagentStop si attiva anche quando uno di questi termina. Per questi eventi, agent_type è il nome dell’agente con cui viene eseguita la sessione stessa, ad esempio quello impostato con --agent o con l’impostazione agent, e una stringa vuota quando la sessione viene eseguita senza.
Un matcher che nomina tipi di agente non corrisponde a un agent_type vuoto. Un hook il cui matcher è omesso, "" o "*", oppure è un’espressione regolare che corrisponde a una stringa vuota, viene eseguito anche per gli eventi con un agent_type vuoto.
Su Claude Code v2.1.271 o successiva, un subagent che viene eseguito con lo strumento SubagentHandback consegna il proprio resoconto tramite quello strumento prima di fermarsi. Il campo last_assistant_message contiene quindi l’eventuale testo conclusivo del subagent, che non è il resoconto consegnato. Il resoconto è l’input message di quella chiamata, che un hook PreToolUse o PostToolUse con matcher su SubagentHandback riceve come tool_input.message.
Gli hook SubagentStop ricevono anche gli array background_tasks e session_crons descritti in Input di Stop. Entrambi gli array hanno come ambito la sessione padre, non il subagent.
hookSpecificOutput.additionalContext con hookEventName impostato su "SubagentStop", per un feedback non di errore che mantiene il subagent in esecuzione. Restituire decision: "block" con un reason mantiene il subagent in esecuzione e consegna reason al subagent come sua istruzione successiva. Un hook che blocca uscendo con codice 2 consegna il proprio messaggio stderr allo stesso modo. Per iniettare contesto nella sessione padre dopo che un subagent ha restituito il risultato, usa invece un hook PostToolUse sullo strumento Agent.
TaskCreated
Viene eseguito quando un task viene creato tramite lo strumentoTaskCreate. Usalo per imporre convenzioni di denominazione, richiedere descrizioni dei task o impedire la creazione di determinati task. In una sessione senza gli strumenti Task, questo evento non si attiva.
Gli hook TaskCreated non supportano i matcher e si attivano a ogni occorrenza.
Input di TaskCreated
Oltre ai campi di input comuni, gli hook TaskCreated ricevonotask_id, task_subject e, facoltativamente, task_description, teammate_name e team_name.
Controllo delle decisioni di TaskCreated
Un hook TaskCreated può bloccare la creazione in due modi. In entrambi i casi, Claude Code elimina il task e restituisce il tuo messaggio a Claude come errore dello strumento. Claude Code ignoracontinue: false da questo evento e Claude continua a lavorare.
- Codice di uscita 2: Claude Code restituisce il testo di stderr come messaggio.
- JSON
{"decision": "block", "reason": "..."}: Claude Code restituiscereasoncome messaggio.
TaskCompleted
Viene eseguito quando un task viene contrassegnato come completato. Si attiva in due situazioni: quando un qualsiasi agente contrassegna esplicitamente un task come completato tramite lo strumento TaskUpdate, o quando un compagno di un team di agenti termina il proprio turno con task in corso. Usalo per imporre criteri di completamento, come il superamento dei test o dei controlli di lint, prima che un task possa essere chiuso. Gli hook TaskCompleted non supportano i matcher e si attivano a ogni occorrenza.Input di TaskCompleted
Oltre ai campi di input comuni, gli hook TaskCompleted ricevonotask_id, task_subject e, facoltativamente, task_description, teammate_name e team_name.
Controllo delle decisioni di TaskCompleted
Gli hook TaskCompleted supportano due modi per controllare il completamento dei task:- Codice di uscita 2: il task non viene contrassegnato come completato e il messaggio stderr viene restituito al modello come feedback.
- JSON
{"continue": false, "stopReason": "..."}: quando l’evento è stato attivato da un compagno di team che termina il proprio turno, ferma completamente il compagno di team, in modo analogo al comportamento dell’hookStop. LostopReasonviene mostrato all’utente. Quando l’evento è stato attivato dallo strumentoTaskUpdate, Claude Code ignoracontinue: false; il codice di uscita 2 blocca comunque il completamento.
Stop
Viene eseguito quando l’agente principale di Claude Code ha finito di rispondere. Non viene eseguito se l’arresto è avvenuto a causa di un’interruzione da parte dell’utente. Gli errori API attivano invece StopFailure.Input di Stop
Oltre ai campi di input comuni, gli hook Stop ricevonostop_hook_active, last_assistant_message, background_tasks e session_crons. Il campo stop_hook_active è true quando Claude Code sta già proseguendo a seguito di uno stop hook. Controlla questo valore o elabora la trascrizione per evitare di bloccare su una condizione che non si risolverà mai. Claude Code applica un limite di 8 prosecuzioni consecutive: dopo che gli stop hook hanno fatto proseguire il turno otto volte di fila, Claude Code sovrascrive il blocco successivo e termina il turno. Per aumentare il limite, imposta CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Il campo last_assistant_message contiene il contenuto testuale della risposta finale di Claude, quindi gli hook possono accedervi senza analizzare il file della trascrizione. Per gli hook che agiscono sul turno appena completato, come gli hook di lettura ad alta voce o di notifica, usa questo campo anziché leggere transcript_path: non è garantito che il file della trascrizione includa il messaggio finale al momento di Stop in tutte le versioni.
Gli array background_tasks e session_crons consentono agli hook di distinguere tra “la sessione è terminata” e “la sessione è in pausa in attesa che un lavoro in background la riattivi”. Entrambi gli array sono presenti quando il registro dei task è raggiungibile e sono vuoti quando non c’è nulla in corso o pianificato.
Ogni voce in background_tasks descrive un task in corso e usa questi campi:
Ogni voce in
session_crons descrive una riattivazione pianificata con ambito di sessione, proveniente da CronCreate, ScheduleWakeup e /loop:
Questo esempio mostra un input di Stop con un task shell in corso e un cron ricorrente:
Controllo delle decisioni di Stop
Gli hookStop e SubagentStop possono controllare se Claude prosegue. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del tuo hook può restituire questi campi specifici dell’evento:
Un hook che blocca uscendo con codice 2 viene instradato allo stesso modo di
reason: Claude riceve il messaggio stderr come spiegazione del motivo per cui deve proseguire.
additionalContext quando l’hook funziona come previsto e fornisce indicazioni a Claude, come “esegui la suite di test prima di terminare”. Mantiene attiva la conversazione attraverso le stesse protezioni dai cicli di decision: "block", ovvero l’input stop_hook_active e il limite di 8 prosecuzioni consecutive, ma la trascrizione lo etichetta come Stop hook feedback e non viene mostrata alcuna notifica di errore dell’hook:
StopFailure
Viene eseguito al posto di Stop quando il turno termina a causa di un errore API. Claude Code ignora l’output e il codice di uscita dell’hook, a parteterminalSequence. Usalo per registrare i fallimenti, inviare avvisi o intraprendere azioni di ripristino quando Claude non riesce a completare una risposta a causa di rate limit, problemi di autenticazione o altri errori API.
Input di StopFailure
Oltre ai campi di input comuni, gli hook StopFailure ricevonoerror, error_details facoltativo e last_assistant_message facoltativo. Il campo error identifica il tipo di errore ed è usato per il filtro del matcher.
TeammateIdle
Viene eseguito quando un compagno di un team di agenti sta per diventare inattivo dopo aver terminato il proprio turno. Usalo per imporre controlli di qualità prima che un compagno di team smetta di lavorare, ad esempio richiedendo il superamento dei controlli di lint o verificando che i file di output esistano. Gli hook TeammateIdle non supportano i matcher e si attivano a ogni occorrenza.Input di TeammateIdle
Oltre ai campi di input comuni, gli hook TeammateIdle ricevonoteammate_name e team_name.
Controllo delle decisioni di TeammateIdle
Gli hook TeammateIdle supportano due modi per controllare il comportamento dei compagni di team:- Codice di uscita 2: il compagno di team riceve il messaggio stderr come feedback e continua a lavorare invece di diventare inattivo.
- JSON
{"continue": false, "stopReason": "..."}: ferma completamente il compagno di team, in modo analogo al comportamento dell’hookStop. LostopReasonviene mostrato all’utente.
ConfigChange
Viene eseguito quando un file di configurazione cambia durante una sessione. Usalo per verificare le modifiche alle impostazioni, applicare criteri di sicurezza o bloccare modifiche non autorizzate ai file di configurazione. Claude Code esegue gli hook ConfigChange quando cambia un file di impostazioni, un file di criteri gestiti o un file di skill. Per i criteri gestiti, li esegue solo quando cambiamanaged-settings.json o un file in managed-settings.d/. Applica le impostazioni gestite dal server e le modifiche alle preferenze gestite di macOS o ai criteri del registro di Windows senza eseguirli. Su WSL con wslInheritsWindowsSettings, applica inoltre un file di impostazioni gestite lato Windows modificato durante il suo controllo periodico dei criteri senza eseguirli.
Il matcher filtra in base all’origine della configurazione:
Questo esempio registra tutte le modifiche alla configurazione per la verifica della sicurezza:
Input di ConfigChange
Oltre ai campi di input comuni, gli hook ConfigChange ricevonosource e, facoltativamente, file_path. Il campo source indica quale tipo di configurazione è cambiato e file_path fornisce il percorso del file specifico che è stato modificato.
Controllo delle decisioni di ConfigChange
Gli hook ConfigChange possono impedire che le modifiche alla configurazione abbiano effetto. Usa il codice di uscita 2 o unadecision JSON per impedire la modifica. Quando viene bloccata, le nuove impostazioni non vengono applicate alla sessione in esecuzione.
policy_settings non possono essere bloccate. Gli hook si attivano comunque per le origini policy_settings quando cambia un file di impostazioni gestite sulla macchina, quindi puoi usarli per registrare tali modifiche, ma qualsiasi decisione di blocco viene ignorata. Questo garantisce che le impostazioni gestite dall’azienda abbiano sempre effetto. Claude Code non esegue gli hook ConfigChange quando le impostazioni gestite dal server arrivano o vengono aggiornate.
Claude Code agisce sulla decisione di blocco dall’output JSON di un hook ConfigChange e scarta systemMessage e continue. Una modifica bloccata non mostra alcun messaggio né a te né a Claude, sia che tu blocchi con reason sia con stderr con uscita 2. Claude Code scrive solo una riga nel log di debug.
CwdChanged
Viene eseguito quando un comando della shell nella conversazione principale cambia la directory di lavoro, ad esempio quando Claude esegue un comandocd. Usalo per reagire ai cambi di directory: ricaricare le variabili d’ambiente, attivare toolchain specifiche del progetto o eseguire automaticamente script di configurazione. Si abbina a FileChanged per strumenti come direnv che gestiscono l’ambiente per directory.
Gli hook CwdChanged hanno accesso a CLAUDE_ENV_FILE. Le variabili scritte in quel file persistono nei comandi Bash successivi fino al successivo evento CwdChanged, quando Claude Code le cancella.
CwdChanged non supporta i matcher e si attiva a ogni occorrenza.
Input di CwdChanged
Oltre ai campi di input comuni, gli hook CwdChanged ricevonoold_cwd e new_cwd.
Output di CwdChanged
Oltre ai campi di output JSON disponibili per tutti gli hook, gli hook CwdChanged possono restituirewatchPaths per impostare dinamicamente quali percorsi di file FileChanged monitora:
Gli hook CwdChanged non hanno controllo delle decisioni. Non possono bloccare il cambio di directory.
Claude Code legge
watchPaths e systemMessage dal loro output JSON e scarta continue. Nelle sessioni interattive, mostra il systemMessage come breve notifica nel terminale. Il messaggio non raggiunge il flusso di messaggi dell’SDK.
DirectoryAdded
Viene eseguito dopo che aggiungi una directory di lavoro a sessione in corso con il comando/add-dir, o dopo che un client SDK ne aggiunge una con la richiesta di controllo register_repo_root. Usalo per preparare un repository appena aggiunto, ad esempio installandone le dipendenze.
Claude Code non attiva questo evento quando:
- Passi una directory con il flag di avvio
--add-dir; SessionStart copre quelle directory - Aggiungi una directory nella scheda Workspace di
/permissions - Aggiungi una directory che è già una directory di lavoro o che si trova all’interno di una
Input di DirectoryAdded
Oltre ai campi di input comuni, gli hook DirectoryAdded ricevonodirectory e source.
continue dal loro output JSON e gestisce il resto in modo diverso in base all’origine:
slash_command: Claude Code consegna ilsystemMessagedell’hook a Claude come contesto nel turno successivo della conversazione, anziché mostrarlo a te. Nella trascrizione appare un conteggio degli hook falliti. L’output completo dei fallimenti va nel log di debugregister_repo_root: Claude Code scrive l’output disystemMessagee l’output dei fallimenti solo nel log di debug
FileChanged
Viene eseguito quando un file monitorato cambia su disco. Claude Code rileva le modifiche con un watcher del filesystem, non ispezionando le chiamate agli strumenti, quindi esegue l’hook indipendentemente da cosa abbia modificato il file: una chiamata allo strumentoEdit o Write, uno script che Claude esegue con Bash o un processo completamente esterno a Claude Code. Un uso comune è ricaricare le variabili d’ambiente quando cambiano i file di configurazione del progetto.
Il matcher per questo evento svolge due ruoli:
- Costruire l’elenco di monitoraggio: il valore viene suddiviso su
|e ogni segmento viene registrato come nome di file letterale nella directory di lavoro, quindi".envrc|.env"monitora esattamente quei due file. I pattern regex non sono utili qui: un valore come^\.envmonitorerebbe un file chiamato letteralmente^\.env. - Filtrare quali hook vengono eseguiti: quando un file monitorato cambia, lo stesso valore filtra quali gruppi di hook vengono eseguiti usando le regole standard dei matcher sul nome base del file modificato.
data.csv dopo qualsiasi modifica, inclusa la riscrittura del file da parte di un comando Bash o di uno script esterno:
file_path dell’input JSON su stdin. Il suo controllo grep verifica la stessa cosa che perl rimuove, un CR alla fine di una riga, quindi l’esecuzione successiva a una normalizzazione termina senza toccare il file. Un controllo meno rigoroso genera un ciclo infinito, perché perl -i riscrive il file anche quando non sostituisce nulla e Claude Code esegue di nuovo l’hook dopo ogni riscrittura. Salva questo script in /path/to/normalize-line-endings.sh e rendilo eseguibile:
data.csv con un comando Bash. Claude Code esegue l’hook e il file termina con fine riga LF.
Per monitorare file che non puoi nominare in anticipo, restituisci watchPaths da un hook per aggiornare dinamicamente l’elenco di monitoraggio. Claude Code avvia il watcher solo quando qualcosa nomina un file da monitorare, quindi inizializza l’elenco con un gruppo FileChanged il cui matcher nomina almeno un file, oppure con un hook SessionStart o CwdChanged che restituisce watchPaths. Il matcher filtra comunque quali gruppi di hook vengono eseguiti quando un file monitorato cambia, quindi assegna al gruppo che gestisce i percorsi dinamici un matcher omesso, che corrisponde a ogni file monitorato e non aggiunge nulla all’elenco di monitoraggio. Anche un matcher "*" corrisponde a ogni file, ma Claude Code lo registra nell’elenco di monitoraggio come qualsiasi altro valore, ovvero come un file chiamato letteralmente *.
Gli hook FileChanged hanno accesso a CLAUDE_ENV_FILE. Le variabili scritte in quel file persistono nei comandi Bash successivi fino al successivo evento CwdChanged, quando Claude Code le cancella.
Input di FileChanged
Oltre ai campi di input comuni, gli hook FileChanged ricevonofile_path ed event.
Output di FileChanged
Oltre ai campi di output JSON disponibili per tutti gli hook, gli hook FileChanged possono restituirewatchPaths per aggiornare dinamicamente quali percorsi di file vengono monitorati:
Gli hook FileChanged non hanno controllo delle decisioni. Non possono impedire che la modifica del file avvenga.
Claude Code legge
watchPaths e systemMessage dal loro output JSON e scarta continue. Nelle sessioni interattive, mostra il systemMessage come breve notifica nel terminale. Il messaggio non raggiunge il flusso di messaggi dell’SDK.
WorktreeCreate
Viene eseguito quando viene creato un worktree, che sia daclaude --worktree, da un subagent che usa isolation: "worktree" o per una sessione in background che Claude Code isola nel proprio worktree. Per impostazione predefinita, Claude Code crea la copia di lavoro isolata con git worktree. Configurare un hook WorktreeCreate sostituisce quel comportamento git predefinito, consentendoti di usare un diverso sistema di controllo di versione come SVN, Perforce o Mercurial.
Poiché l’hook sostituisce interamente il comportamento predefinito, .worktreeinclude non viene elaborato. Se devi copiare file di configurazione locali come .env nel nuovo worktree, fallo all’interno dello script del tuo hook.
L’hook deve restituire il percorso della directory del worktree creato. Claude Code usa questo percorso come directory di lavoro per la sessione isolata. Consulta Output di WorktreeCreate per come ciascun tipo di hook restituisce il percorso.
Claude Code agisce sull’esito positivo dell’hook e sul percorso restituito, e scarta systemMessage e continue.
Questo esempio crea una copia di lavoro SVN e stampa il percorso che Claude Code deve usare. Sostituisci l’URL del repository con il tuo:
name del worktree dall’input JSON su stdin, effettua il checkout di una copia nuova in una nuova directory e stampa il percorso della directory. L’echo sull’ultima riga è ciò che Claude Code legge come percorso del worktree. Reindirizza qualsiasi altro output su stderr in modo che non interferisca con il percorso.
Input di WorktreeCreate
Oltre ai campi di input comuni, gli hook WorktreeCreate ricevono il camponame. Si tratta di un identificatore slug per il nuovo worktree, specificato dall’utente o generato automaticamente, ad esempio bold-oak-a3f2.
Output di WorktreeCreate
Gli hook WorktreeCreate non usano il modello decisionale standard di consenso/blocco. L’esito è invece determinato dal successo o dal fallimento dell’hook. L’hook deve restituire il percorso della directory del worktree creato:- Hook di comando (
type: "command"): stampa il percorso come ultima riga non vuota di stdout. Claude Code rimuove i codici di escape ANSI prima di leggere quella riga, quindi i banner di avvio della shell stampati prima del tuoechovengono ignorati. Reindirizza qualsiasi altro output dell’hook su stderr. - Hook HTTP (
type: "http"): restituisci{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }nel corpo della risposta.
. o .. al suo interno. Se il percorso risultante non è una directory in cui Claude Code può entrare, la sessione stampa un errore che indica il percorso ed esce con codice 1.
Claude Code rifiuta un percorso assoluto che contiene segmenti . o .., e qualsiasi percorso che attraversa un collegamento simbolico sotto la radice del repository, perché un collegamento simbolico sottoposto a commit nel repository potrebbe reindirizzare il worktree al di fuori di esso. L’errore indica il componente rifiutato. Restituisci un percorso normalizzato che non attraversi un collegamento simbolico all’interno del repository. Prima della v2.1.216, la creazione del worktree seguiva il percorso dell’hook senza questo controllo.
WorktreeRemove
Viene eseguito quando un worktree sta per essere rimosso. È la controparte di pulizia di WorktreeCreate. L’evento si attiva quando:- esci da una sessione
--worktreee scegli di rimuoverlo - un subagent con
isolation: "worktree"termina - elimini una sessione in background il cui worktree è stato creato dall’hook
git worktree remove. Se hai configurato un hook WorktreeCreate, abbinalo a un hook WorktreeRemove per controllare la pulizia dei worktree che crea:
- Nessun hook WorktreeRemove: quando esci da una sessione
--worktreee scegli la rimozione, Claude Code ripiega sugit worktree remove --forcesul percorso restituito dal tuo hook WorktreeCreate, quindi un worktree riconosciuto da git viene rimosso. Un worktree che git non riconosce, per esempio uno creato dal tuo hook con un sistema di controllo versione diverso da git, rimane su disco. Per sapere cosa fa l’eliminazione di una sessione in background con un worktree creato da un hook, consulta le regole di eliminazione della vista agenti. - L’hook esce con 0: il worktree viene considerato rimosso. Claude Code non legge nient’altro dall’hook, quindi assicurati che il tuo hook abbia eliminato la directory.
- L’hook esce con un valore diverso da zero: la rimozione fallisce se la directory in
worktree_pathesiste ancora in seguito, e il worktree rimane su disco senza fallback su git. Un hook che ha eliminato la directory prima di uscire con un valore diverso da zero viene considerato come rimozione riuscita. Per sapere come viene segnalato l’errore, consulta Input di WorktreeRemove.
systemMessage e continue.
Per l’eliminazione di una sessione in background, Claude Code verifica il percorso del worktree memorizzato prima di eseguire l’hook e rifiuta un percorso che sia un collegamento simbolico o che ne attraversi uno sotto la radice del repository. L’hook viene eseguito per un worktree che contiene ancora file solo quando confermi l’eliminazione nella vista agenti; per un worktree di questo tipo, claude rm mantiene invece la sessione e il worktree. Prima della v2.1.216, l’hook veniva eseguito sul percorso memorizzato senza questi controlli.
Claude Code passa il percorso restituito da WorktreeCreate come worktree_path nell’input dell’hook. Questo esempio legge quel percorso e rimuove la directory:
Input di WorktreeRemove
Oltre ai campi di input comuni, gli hook WorktreeRemove ricevono il campoworktree_path, che è il percorso assoluto del worktree in fase di rimozione.
worktree_path esiste ancora in seguito, la rimozione fallisce:
- Il worktree rimane su disco, e il comando e lo stderr dell’hook vanno nel log di debug.
- Se stavi eliminando una sessione in background, anche la sessione rimane. Il messaggio di rifiuto nella vista agenti riporta come è terminato l’hook, ad esempio
exited 1, cita l’inizio del suo stderr e indica se eliminare di nuovo la sessione rimuove comunque la directory.
PreCompact
Viene eseguito prima che Claude Code stia per eseguire un’operazione di compattazione. Il valore del matcher indica se la compattazione è stata attivata manualmente o automaticamente:
Esci con codice 2 per bloccare la compattazione. Per un
/compact manuale, il messaggio stderr viene mostrato all’utente. Puoi anche bloccare restituendo JSON con "decision": "block".
Bloccare la compattazione automatica ha effetti diversi a seconda di quando si attiva. Se la compattazione è stata attivata in modo proattivo prima del limite di contesto, Claude Code la salta e la conversazione continua senza compattazione. Se la compattazione è stata attivata per recuperare da un errore di limite di contesto già restituito dall’API, l’errore sottostante emerge e la richiesta corrente fallisce.
Claude Code scarta i campi systemMessage e continue di un hook PreCompact.
Input di PreCompact
Oltre ai campi di input comuni, gli hook PreCompact ricevonotrigger e custom_instructions. Per manual, custom_instructions contiene ciò che l’utente passa a /compact ed è null quando non passa nulla. Per auto, custom_instructions è null.
PostCompact
Viene eseguito dopo che Claude Code completa un’operazione di compattazione. Usa questo evento per reagire al nuovo stato compattato, ad esempio per registrare il riepilogo generato o aggiornare uno stato esterno. Claude Code scarta i campisystemMessage e continue di un hook PostCompact.
Si applicano gli stessi valori del matcher di PreCompact:
Input di PostCompact
Oltre ai campi di input comuni, gli hook PostCompact ricevonotrigger e compact_summary. Il campo compact_summary contiene il riepilogo della conversazione generato dall’operazione di compattazione.
PreModelSwitch
Viene eseguito prima che Claude Code applichi un cambio di modello richiesto da te o da un client. Usalo per bloccare un cambio, richiedere una conferma o mostrare quanto costerà il cambio prima che avvenga. PreModelSwitch richiede Claude Code v2.1.251 o successivo. Claude Code lo esegue per queste richieste:/model <name>e il selettore di/model- Il selettore di modello
Option+PoAlt+P - L’impostazione Model in
/config - L’attivazione della modalità veloce quando questa cambia il modello della sessione
- Una richiesta
set_model, o un cambio di modello in una richiestaapply_flag_settings, da un host dell’Agent SDK o da Remote Control
[1m]. Un alias come opus, un ID di modello con data e un ID specifico del provider, come un ID di modello di Amazon Bedrock, corrispondono tutti all’unico nome canonico a cui si risolvono, quindi claude-opus-5 copre ogni variante di scrittura di Opus 5.
Quando Claude Code non riesce a determinare un nome canonico per la destinazione, ad esempio un ID di modello personalizzato noto solo al tuo gateway LLM, esegue ogni hook PreModelSwitch indipendentemente dal matcher. Un hook che blocca dovrebbe quindi controllare to_model dal suo input anziché affidarsi al solo matcher.
Scrivi il matcher come nome esatto, come elenco separato da | come claude-opus-4-6|claude-opus-5, o come espressione regolare come .*opus.*. Questo esempio usa un matcher con nome esatto e controlla anche to_model dall’input dell’hook, quindi rifiuta un passaggio a Opus 4.6 uscendo con codice 2 e lascia passare qualsiasi altra destinazione:
- macOS/Linux
- Windows (PowerShell)
Il comando controlla
to_model con jq:/model claude-opus-4-6 da una sessione che usa un modello diverso. Claude Code mantiene il modello corrente e segnala che un hook PreModelSwitch ha bloccato il cambio, con il tuo messaggio come motivo.
Input di PreModelSwitch
Oltre ai campi di input comuni, gli hook PreModelSwitch ricevono i campi di questa tabella. Gli ultimi cinque descrivono quanto costa reinviare la conversazione al nuovo modello, così un hook può mostrare quella cifra prima che avvenga il cambio.
Questo esempio mostra l’input per
/model opus in una sessione che usa Sonnet 5:
Controllo decisionale di PreModelSwitch
Gli hookPreModelSwitch possono annullare il cambio, chiedere all’utente di confermarlo o lasciarlo procedere. Il codice di uscita 2 o un decision: "block" di primo livello annulla il cambio.
Per un controllo più preciso, restituisci permissionDecision e permissionDecisionReason in un oggetto hookSpecificOutput, come per PreToolUse. PreModelSwitch accetta "allow", "deny" e "ask". Non accetta "defer", updatedInput o additionalContext. La tabella seguente descrive entrambi i campi:
Solo
/model in una sessione interattiva può mostrare la richiesta di conferma di "ask". Su ogni altra superficie, inclusa la modalità non interattiva con il flag -p, /config e le richieste set_model, Claude Code tratta "ask" come un rifiuto.
Questo esempio chiede all’utente di confermare e cita il numero di token da context_tokens:
deny > ask > allow.
Claude Code mostra all’utente qualsiasi systemMessage restituito dal tuo hook indipendentemente dalla decisione, quindi un hook di report dei costi può restituire {"systemMessage": "..."} e uscire con 0.
Un hook PreModelSwitch che non risponde prima del suo timeout blocca il cambio. Al contrario, su PreToolUse, un hook di comando andato in timeout lascia proseguire la chiamata allo strumento. Il timeout predefinito per questo evento è di 30 secondi. PreModelSwitch esegue solo hook command, http e mcp_tool, quindi i valori predefiniti di prompt e agent non si applicano.
Un hook che esce con un codice diverso da 0 o 2 e non stampa alcuna decisione JSON non blocca: Claude Code mostra il suo stderr e applica il cambio, come descritto in Altri codici di uscita.
PostModelSwitch
Viene eseguito dopo che il modello della sessione cambia. Usalo per fornire a Claude indicazioni specifiche per il modello senza modificare ogni CLAUDE.md, ad esempio un’istruzione valida per tutta l’organizzazione che si applica a determinati modelli. PostModelSwitch richiede Claude Code v2.1.251 o successivo. Non può bloccare, perché il modello è già cambiato. Claude Code esegue gli hook PostModelSwitch dopo uno qualsiasi di questi cambi:- Un cambio richiesto da te o da un client
- Un fallback automatico del modello, che cambia il modello della sessione
- Un’impostazione come
opusplanche entra o esce dal plan mode - Claude Code che ripristina il modello quando riprendi una sessione
/model opus da una sessione Sonnet, poi chiedi a Claude quali indicazioni ha sul modello corrente.
Input di PostModelSwitch
Gli hook PostModelSwitch ricevono gli stessi campi di PreModelSwitch, conhook_event_name impostato su "PostModelSwitch" e due valori aggiuntivi di source: "auto" per un fallback automatico o un altro cambio effettuato autonomamente da Claude Code, e "resume" per il modello ripristinato quando riprendi una sessione.
requested_model è null quando source è "auto". Quando source è "resume", è l’impostazione del modello salvata che Claude Code ha ripristinato.
Controllo decisionale di PostModelSwitch
Claude Code prende lo stdout in testo semplice del tuo hook all’uscita con 0, oadditionalContext dall’output JSON, e lo consegna a Claude con la richiesta successiva al cambio. Oltre ai campi di output JSON disponibili per tutti gli hook, puoi restituire:
Se l’hook non ha terminato entro cinque secondi dall’invio del prompt successivo, Claude Code invia quella richiesta senza l’output e lo allega invece alla richiesta seguente. Se il modello cambia più volte prima della richiesta successiva, Claude Code consegna solo l’output relativo al modello di destinazione dell’ultimo cambio.
SessionEnd
Viene eseguito quando una sessione di Claude Code termina. Utile per attività di pulizia, registrazione delle statistiche della sessione o salvataggio dello stato della sessione. Supporta i matcher per filtrare in base al motivo di uscita. Il camporeason nell’input dell’hook indica perché la sessione è terminata:
Input di SessionEnd
Oltre ai campi di input comuni, gli hook SessionEnd ricevono un camporeason che indica perché la sessione è terminata. Consulta la tabella dei motivi sopra per tutti i valori.
systemMessage.
Gli hook SessionEnd hanno un timeout predefinito di 1,5 secondi. Si applica quando esci, esegui /clear o cambi sessione con /resume interattivo. Puoi concedere più tempo a un hook in due modi:
timeoutper singolo hook: impostatimeoutnella configurazione di quell’hook. Il budget complessivo aumenta automaticamente fino a corrispondere altimeoutper singolo hook più alto nei tuoi file di impostazioni, fino a 60 secondi. Se aumenti il budget in questo modo, un hook senza un propriotimeoutmantiene comunque il valore predefinito. I timeout impostati sugli hook forniti dai plugin non aumentano il budget.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: imposta questa variabile d’ambiente in millisecondi per sovrascrivere esplicitamente il budget. Il valore che imposti diventa anche il timeout per ogni hook senza un propriotimeout.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS aumentava solo il budget complessivo, e un hook senza un proprio timeout veniva comunque annullato dopo 1,5 secondi.
Elicitation
Viene eseguito quando un server MCP richiede un input dell’utente durante un’attività. Per impostazione predefinita, Claude Code mostra una finestra di dialogo interattiva a cui l’utente può rispondere. Gli hook possono intercettare questa richiesta e rispondere in modo programmatico, saltando completamente la finestra di dialogo. Il campo matcher viene confrontato con il nome del server MCP.Input di Elicitation
Oltre ai campi di input comuni, gli hook Elicitation ricevonomcp_server_name, message e i campi facoltativi mode, url, elicitation_id e requested_schema.
Per l’elicitation in modalità form, il caso più comune:
Output di Elicitation
Per rispondere in modo programmatico senza mostrare la finestra di dialogo, restituisci un oggetto JSON conhookSpecificOutput:
Il codice di uscita 2 nega l’elicitation. Claude Code non mostra il tuo messaggio stderr da nessuna parte.
Claude Code agisce su
hookSpecificOutput dall’output JSON di un hook Elicitation e scarta systemMessage e continue.
ElicitationResult
Viene eseguito dopo che un utente risponde a un’elicitation MCP. Gli hook possono osservare, modificare o bloccare la risposta prima che venga rinviata al server MCP. Il campo matcher viene confrontato con il nome del server MCP.Input di ElicitationResult
Oltre ai campi di input comuni, gli hook ElicitationResult ricevonomcp_server_name, action e i campi facoltativi mode, elicitation_id e content.
Output di ElicitationResult
Per sovrascrivere la risposta dell’utente, restituisci un oggetto JSON conhookSpecificOutput:
Il codice di uscita 2 blocca la risposta, cambiando l’azione effettiva in
decline. Claude Code non mostra il tuo messaggio stderr da nessuna parte.
Claude Code agisce su hookSpecificOutput dall’output JSON di un hook ElicitationResult e scarta systemMessage e continue.
Hook basati su prompt
Oltre agli hook di comando, HTTP e MCP tool, Claude Code supporta gli hook basati su prompt (type: "prompt") che utilizzano un LLM per valutare se consentire o bloccare un’azione, e gli hook basati su agenti (type: "agent") che generano un verificatore agentico con accesso agli strumenti. Non tutti gli eventi supportano ogni tipo di hook.
Gli eventi che supportano tutti e cinque i tipi di hook (command, http, mcp_tool, prompt e agent):
PermissionDeniedPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
PermissionRequest supporta gli hook command, http, mcp_tool e prompt ma non gli hook agent. Se configuri un hook agent su questo evento, Claude Code lo salta e il flusso di autorizzazione procede invariato. Per consentire o negare da un hook, restituisci l’oggetto decisione da un hook di comando o HTTP.
Gli eventi che supportano gli hook command, http e mcp_tool ma non prompt o agent:
ConfigChangeCwdChangedDirectoryAddedElicitationElicitationResultFileChangedInstructionsLoadedMessageDisplayNotificationPostCompactPostModelSwitchPreCompactPreModelSwitchSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart e Setup supportano gli hook command e mcp_tool, e i campi degli hook MCP tool descrivono quando i loro hook mcp_tool vengono eseguiti. Non supportano gli hook http, prompt o agent.
Come funzionano gli hook basati su prompt
Invece di eseguire un comando Bash, gli hook basati su prompt:- Inviano l’input del hook e il prompt a un modello Claude, per impostazione predefinita quello che Claude Code utilizza per la funzionalità in background
- L’LLM risponde con JSON strutturato contenente una decisione
- Claude Code elabora automaticamente la decisione
Configurazione del prompt hook
Impostaretype su "prompt" e fornire una stringa prompt invece di un command. Utilizzare il segnaposto $ARGUMENTS per iniettare i dati di input JSON del hook nel testo del prompt.
Questo hook Stop chiede all’LLM di valutare se tutti i compiti sono completi prima di consentire a Claude di terminare:
Schema di risposta
L’LLM deve rispondere con JSON contenente:
Ciò che accade con
ok: false dipende dall’evento:
StopeSubagentStop: il motivo viene reinviato a Claude come sua prossima istruzione e il turno continua, a meno che la risposta non imposti ancheimpossible: true, nel qual caso Claude Code consente lo stop e il turno terminaPreToolUse: la chiamata dello strumento viene negata; per impostazione predefinita il turno termina e il motivo della negazione appare nella chat come una riga di avviso. ImpostarecontinueOnBlock: trueper reinviare il motivo a Claude come errore dello strumento in modo che possa adattarsi e continuare, equivalente a un hook di comando conpermissionDecision: "deny". Prima della v2.1.210, il motivo della negazione veniva restituito a Claude come errore dello strumento e il turno continuavaPostToolUse: per impostazione predefinita il turno termina e il motivo appare nella chat come una riga di avviso. ImpostarecontinueOnBlock: trueper reinviare il motivo a Claude e continuare il turno invecePostToolBatch,UserPromptSubmiteUserPromptExpansion: il turno termina e il motivo appare come una riga di avviso. Questi eventi terminano il turno sudecision: "block"indipendentemente dacontinuePostToolUseFailureeTaskCreated: il motivo viene restituito a Claude come errore dello strumento e il turno continua, indipendentemente dacontinueOnBlockTaskCompleted: quando si attiva perché un’attività è contrassegnata come completata durante un turno, il motivo viene restituito a Claude come errore dello strumento e il turno continua, indipendentemente dacontinueOnBlock. Quando si attiva perché un compagno di squadra si ferma, si comporta comeTeammateIdlee arresta il compagno di squadra per impostazione predefinitaTeammateIdle: per impostazione predefinita il compagno di squadra si ferma e il motivo appare come una riga di avviso. ImpostarecontinueOnBlock: trueper reinviare il motivo al compagno di squadra e mantenerlo al lavoro invecePermissionRequest:ok: falsenon ha effetto. Per negare un’approvazione da un hook, utilizzare un hook di comando che restituiscehookSpecificOutput.decision.behavior: "deny"PermissionDenied:ok: falsenon ha effetto perché il rifiuto è già avvenuto. L’unico output che questo evento legge èhookSpecificOutput.retry, che gli hook di prompt e agenti non possono impostare. Vengono eseguiti su questo evento, ma il loro output viene scartato. Utilizzare un hook di comando per restituireretry
Controllare più condizioni prima di fermarsi
Questo hookStop utilizza un prompt dettagliato per controllare tre condizioni prima di consentire a Claude di fermarsi. Gli hook SubagentStop utilizzano lo stesso formato per valutare se un subagent dovrebbe fermarsi. Se il modello restituisce "ok": false perché la condizione non è ancora soddisfatta, Claude continua a lavorare con il motivo fornito come sua prossima istruzione:
Hook basati su agenti
Gli hook basati su agenti (type: "agent") sono come gli hook basati su prompt ma con accesso agli strumenti multi-turno. Invece di una singola chiamata LLM, un hook agente genera un subagent che può leggere file, cercare codice e ispezionare il codebase per verificare le condizioni. Gli hook agente supportano gli stessi eventi degli hook basati su prompt, ad eccezione di PermissionRequest.
Come funzionano gli hook basati su agenti
Quando un hook agente si attiva:- Claude Code genera un subagent con il prompt e l’input JSON del hook
- Il subagent può utilizzare strumenti come Read, Grep e Glob per investigare
- Dopo fino a 50 turni, il subagent restituisce una decisione strutturata
{ "ok": true/false } - Claude Code consente l’azione se
okètrue. Seokèfalse, Claude Code gestisce il blocco nello stesso modo di un hook di prompt concontinueOnBlock: truesu quell’evento, come elencato sotto Schema di risposta
Configurazione dell’hook agente
Impostaretype su "agent" e fornire una stringa prompt, utilizzando $ARGUMENTS come segnaposto per l’input JSON del hook. I campi di configurazione sono gli stessi degli hook di prompt, ad eccezione del fatto che gli hook agente hanno un timeout predefinito più lungo di 60 secondi e nessun campo continueOnBlock.
Lo schema di risposta è { "ok": true } per consentire o { "ok": false, "reason": "..." } per bloccare. Su ok: false, Claude Code gestisce un hook agente nello stesso modo in cui gestisce un hook di prompt con continueOnBlock: true sullo stesso evento; gli hook agente non hanno un campo continueOnBlock e non supportano il campo impossible dell’hook di prompt.
Questo hook Stop verifica che tutti i test unitari passino prima di consentire a Claude di finire:
Eseguire i hook in background
Per impostazione predefinita, gli hook bloccano l’esecuzione di Claude fino al completamento. Per le attività a lunga esecuzione come distribuzioni, suite di test o chiamate API esterne, impostare"async": true per eseguire l’hook in background mentre Claude continua a lavorare. Gli hook asincroni non possono bloccare o controllare il comportamento di Claude: i campi di risposta come decision, permissionDecision e continue non hanno effetto, perché l’azione che avrebbero controllato è già stata completata.
Configurare un hook asincrono
Aggiungere"async": true alla configurazione di un command hook per eseguirlo in background senza bloccare Claude. Questo campo è disponibile solo sui hook type: "command".
Questo hook esegue uno script di test dopo ogni chiamata dello strumento Write. Claude continua a lavorare immediatamente mentre run-tests.sh viene eseguito. Quando lo script termina, l’output viene consegnato al turno di conversazione successivo:
timeout su di esso. Claude Code continua ad applicare timeout su un hook che si esegue con asyncRewake.
Claude Code consegna i risultati di un hook asincrono solo mentre la sessione è in esecuzione:
- In modalità non interattiva con il flag
-p, Claude Code termina qualsiasi hook asincrono ancora in esecuzione al teardown e lo finalizza con esitocancelled - Se il lavoro del vostro hook deve sopravvivere a una sessione
claude -p, avviate un processo completamente staccato da esso
Come vengono eseguiti gli hook asincroni
Quando un hook asincrono si attiva, Claude Code avvia il processo del hook e continua immediatamente senza aspettare il completamento. L’hook riceve lo stesso input JSON tramite stdin di un hook sincrono. Dopo che il processo in background esce, Claude Code consegna i campiadditionalContext e systemMessage dalla risposta JSON dell’hook a Claude al turno di conversazione successivo. A differenza di systemMessage di un hook sincrono, nessuno dei due campi viene mostrato a voi.
Claude Code convalida quella risposta JSON rispetto allo stesso schema di output degli hook sincroni e scarta qualsiasi campo il cui valore ha il tipo errato, come un systemMessage che non è una stringa, invece di consegnarlo. Eseguire con --debug per vedere un avviso che nomina ogni campo scartato. Prima della v2.1.202, l’output JSON malformato da un hook asincrono poteva causare l’arresto della sessione e l’arresto si ripeteva ogni volta che la sessione veniva ripresa.
Le notifiche di completamento degli hook asincroni sono soppresse per impostazione predefinita. Per vederle, abilitare la modalità verbose con Ctrl+O o avviare Claude Code con --verbose.
Eseguire i test dopo le modifiche ai file
Questo hook avvia una suite di test in background ogni volta che Claude scrive un file, quindi segnala i risultati a Claude quando i test terminano. Salvare questo script in.claude/hooks/run-tests-async.sh nel progetto e renderlo eseguibile con chmod +x:
.claude/settings.json nella radice del progetto. Il flag async: true consente a Claude di continuare a lavorare mentre i test vengono eseguiti:
Limitazioni
Gli hook asincroni hanno vincoli aggiuntivi rispetto agli hook sincroni:- L’output del hook viene consegnato al turno di conversazione successivo. Se la sessione è inattiva, la risposta attende fino alla prossima interazione dell’utente. Eccezione: un hook
asyncRewakeche esce con il codice 2 riattiva Claude immediatamente anche quando la sessione è inattiva. - Ogni esecuzione crea un processo in background separato. Non c’è deduplicazione tra più attivazioni dello stesso hook asincrono.
Considerazioni sulla sicurezza
Disclaimer
Fiducia nell’area di lavoro
Claude Code verifica la fiducia nell’area di lavoro prima di eseguire qualsiasi hook da un file di impostazioni. Ciò che conta come attendibile dipende dal tipo di sessione:- Sessione interattiva: Claude Code trattiene i hook da ogni file di impostazioni, incluso il vostro
~/.claude/settings.json, fino a quando non accettate la finestra di dialogo di fiducia nell’area di lavoro per la cartella, o per una directory padre la cui fiducia si estende ad essa - Sessione
-po SDK: Claude Code non mostra mai la finestra di dialogo e tratta la cartella come attendibile, quindi i hook sottoposti a commit nel.claude/settings.jsondi un repository vengono eseguiti in una cartella che non avete mai considerato attendibile
claude -p su un repository che non avete scritto, rivedete i file di impostazioni .claude/, iniziate con --bare, o disattivate i hook per quella esecuzione con --settings '{"disableAllHooks": true}'. I hook nel frontmatter in un subagent di progetto seguono una regola più ristretta rispetto ai hook dei file di impostazioni. Ciò che viene eseguito prima di considerare attendibile una cartella elenca ogni tipo di contenuto del repository per tipo di sessione.
Migliori pratiche di sicurezza
Tenere presenti queste pratiche quando si scrivono i hook:- Convalidare e disinfettare gli input: non fidarsi mai ciecamente dei dati di input
- Citare sempre le variabili shell: utilizzare
"$VAR"non$VAR - Bloccare l’attraversamento del percorso: controllare
..nei percorsi dei file - Utilizzare percorsi assoluti: specificare percorsi completi per gli script. Nel modulo exec, utilizzare
${CLAUDE_PROJECT_DIR}e il percorso non necessita di virgolette. Nel modulo shell, racchiuderlo tra virgolette doppie - Saltare i file sensibili: evitare
.env,.git/, chiavi, ecc.
Strumento Windows PowerShell
Su Windows, è possibile eseguire singoli hook in PowerShell impostando"shell": "powershell" su un command hook. Claude Code rileva automaticamente pwsh.exe, l’eseguibile di PowerShell 7 e versioni successive, e ricade su powershell.exe per Windows PowerShell 5.1.
${CLAUDE_PROJECT_DIR} o $env:CLAUDE_PROJECT_DIR. Claude Code riscrive i segnaposti ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} e ${CLAUDE_PLUGIN_DATA} in un comando in forma shell di PowerShell nella forma ${env:NAME} di PowerShell, indipendentemente dal fatto che l’hook sia definito in settings.json, un plugin o una skill. PowerShell quindi risolve il valore dall’ambiente esportato dopo l’analisi, quindi il segnaposto funziona all’interno di stringhe tra virgolette doppie ma non all’interno di stringhe tra virgolette singole, dove PowerShell non espande mai le variabili.
Non scrivere la forma nuda $CLAUDE_PROJECT_DIR in un hook di PowerShell. PowerShell la analizza come una variabile locale non definita e la risolve in $null, il che lascia il percorso dello script senza il prefisso della directory radice del progetto. Claude Code non riscrive quella forma; invece registra un avviso nel log di debug.
L’esempio seguente mostra un hook settings.json che esegue uno script di progetto con la forma $env::
Debug dei hook
I dettagli dell’esecuzione dei hook vengono scritti nel file di log di debug. Avviare Claude Code conclaude --debug-file <path> per scrivere il log in una posizione nota, oppure eseguire claude --debug e leggere il log in ~/.claude/debug/<session-id>.txt. Il flag --debug non stampa nel terminale.
Ad esempio, un hook PostToolUse su Write il cui comando stampa hook-ran produce voci come:
CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose per visualizzare righe di log aggiuntive come i conteggi dei matcher del hook e la corrispondenza delle query.
Per la risoluzione dei problemi comuni come i hook che non si attivano, i Stop hook che continuano a bloccare, o gli errori di configurazione, consultare Limitations and troubleshooting nella guida. Per una procedura diagnostica più ampia che copre /context, /doctor e la precedenza delle impostazioni, consultare Debug your config.