Ciclo di vita dei hook
Gli hook si attivano in punti specifici durante una sessione di Claude Code. 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:- una volta per sessione:
SessionStarteSessionEnd - una volta per turno:
UserPromptSubmit,StopeStopFailure - ad ogni chiamata dello strumento all’interno del ciclo agentico:
PreToolUseePostToolUse
Come si risolve un hook
Per vedere come questi elementi si combinano, considerare questo hookPreToolUse che blocca i comandi shell distruttivi. Il matcher si restringe alle chiamate dello strumento Bash e la condizione if si restringe ulteriormente ai comandi Bash che corrispondono a rm *, quindi block-rm.sh viene eseguito solo quando entrambi i filtri corrispondono:
permissionDecision di "deny" se contiene rm -rf:
Bash "rm -rf /tmp/build". 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:- Scegliere un hook event a cui rispondere, come
PreToolUseoStop - Aggiungere un matcher group per filtrare quando si attiva, come “solo per lo strumento Bash”
- Definire uno o più hook handlers 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 funzionalità generale.
Posizioni dei hook
Il luogo in cui si definisce un hook determina il suo ambito:
Per i dettagli sulla risoluzione del file di impostazioni, consultare settings. Gli amministratori aziendali possono utilizzare
allowManagedHooksOnly per bloccare i hook dell’utente, del progetto e del plugin. Gli hook dai plugin forzatamente abilitati nelle impostazioni gestite enabledPlugins sono esenti, quindi gli amministratori possono distribuire hook verificati attraverso un marketplace dell’organizzazione. Consultare Hook configuration.
Modelli di 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; racchiudere il modello in ^ e $, come in ^Edit$, quando è necessaria una corrispondenza di intera stringa.
I separatori di virgola e la tolleranza dello spazio bianco circostante richiedono Claude Code v2.1.191 o successivo.
I trattini nel set di corrispondenza esatta richiedono Claude Code v2.1.195 o successivo. Nelle versioni precedenti un nome con trattino come code-reviewer viene valutato come un’espressione regolare non ancorata, quindi si attiva anche per senior-code-reviewer; ancorarlo come ^code-reviewer$ su quelle versioni per corrispondere solo a quel nome.
FileChanged e StopFailure utilizzano un set di corrispondenza esatta più ristretto di sole lettere, cifre, _ e |. Un trattino, uno spazio o una 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 il suo elenco di osservazione. Consultare FileChanged.
Ogni tipo di evento corrisponde a un campo diverso:
Il matcher viene eseguito su un campo dall’input JSON che Claude Code invia al hook su stdin. Per gli eventi degli strumenti, quel campo è
tool_name. Ogni sezione hook event elenca l’insieme completo di 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:
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay e CwdChanged non supportano i matcher e si attivano sempre ad ogni occorrenza. Se si aggiunge un campo matcher a questi eventi, viene silenziosamente ignorato.
Per gli eventi degli strumenti, è possibile filtrare più strettamente impostando il campo if sui singoli gestori del hook. if utilizza la sintassi delle regole di autorizzazione 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 strumenti MCP
Gli strumenti del server MCP appaiono come strumenti regolari negli eventi degli strumenti (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), quindi è possibile farvi corrispondere lo stesso modo in cui si fa corrispondere qualsiasi altro nome di strumento.
Gli strumenti MCP seguono il modello di denominazione mcp__<server>__<tool>, ad esempio:
mcp__memory__create_entities: strumento create entities del server Memorymcp__filesystem__read_file: strumento read file del server Filesystemmcp__github__search_repositories: strumento 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 stringa esatta e non corrisponde a nessuno strumento.
mcp__memory__.*corrisponde a tutti gli strumenti dal servermemorymcp__brave-search__.*corrisponde a tutti gli strumenti da un server il cui nome contiene un trattinomcp__.*__write.*corrisponde a qualsiasi strumento il cui nome inizia conwriteda qualsiasi server
mcp__brave-search viene valutato come un’espressione regolare non ancorata e corrisponde a ogni strumento da quel server. La forma mcp__brave-search__.* funziona su ogni versione.
Gli strumenti da un server MCP fornito da plugin utilizzano un segmento server con ambito che include il nome del plugin: mcp__plugin_<plugin-name>_<server-name>__<tool>. Un matcher scritto rispetto alla chiave del server semplice non si attiva mai per questi strumenti. Per un plugin denominato my-plugin che fornisce un server con la chiave db, uno strumento query appare come mcp__plugin_my-plugin_db__query, quindi il matcher per ogni strumento da quel server è mcp__plugin_my-plugin_db__.*. Utilizzare lo stesso nome dello strumento con ambito nel campo if di un gestore. Consultare Plugin-provided MCP servers per come viene costruito il nome con ambito.
Questo esempio registra tutte le operazioni del server memory e convalida le operazioni di scrittura da qualsiasi server MCP:
Campi del gestore del hook
Ogni oggetto nell’arrayhooks interno è un gestore del hook: il comando shell, endpoint HTTP, strumento MCP, prompt LLM o agente che viene eseguito quando il matcher corrisponde. Ci sono cinque tipi:
- Command hooks (
type: "command"): eseguono un comando shell. Lo script riceve l’input JSON dell’evento su stdin e comunica i risultati attraverso codici di uscita e stdout. - HTTP hooks (
type: "http"): inviano l’input JSON dell’evento come richiesta HTTP POST a un URL. L’endpoint comunica i risultati attraverso il corpo della risposta utilizzando lo stesso formato JSON di output dei command hook. - MCP tool hooks (
type: "mcp_tool"): chiamano uno strumento su un server MCP già connesso. L’output di testo dello strumento viene trattato come stdout del command hook. - Prompt hooks (
type: "prompt"): inviano un prompt a un modello Claude per la valutazione a turno singolo. Il modello restituisce una decisione sì/no come JSON. Consultare Prompt-based hooks. - Agent hooks (
type: "agent"): generano un subagent che può utilizzare strumenti come Read, Grep e Glob per verificare le condizioni prima di restituire una decisione. Gli agent hook sono sperimentali e potrebbero cambiare. Consultare Agent-based hooks.
args, e gli HTTP hook vengono deduplicati per URL.
I gestori vengono eseguiti nella directory corrente con l’ambiente di Claude Code. La variabile di ambiente $CLAUDE_CODE_REMOTE è impostata su "true" negli ambienti web remoti e non è impostata nella CLI locale. A partire da v2.1.199, $CLAUDE_CODE_BRIDGE_SESSION_ID è impostato sull’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 autorizzazione. Non esiste sintassi &&, || o di elenco per combinare le regole; per applicare più condizioni, definire un gestore del hook separato per ciascuna.
Per i modelli Bash, se il comando hook viene eseguito dipende dalla forma del modello e dal comando Bash che Claude sta invocando. Gli assegnamenti VAR=value iniziali vengono rimossi prima della corrispondenza.
Il filtro fallisce anche in modo aperto, eseguendo l’hook indipendentemente dal modello, quando il comando Bash non può essere analizzato. Poiché il filtro
if è best-effort, utilizzare il sistema di autorizzazione piuttosto che un hook per applicare un’autorizzazione o un diniego rigido.
Campi del command hook
Oltre ai campi comuni, i command hook accettano questi campi:
Un command hook viene eseguito come exec form quando
args è impostato, e come shell form quando args è omesso. Impostare args ogni volta che l’hook fa riferimento a un segnaposto di percorso, poiché ogni elemento viene passato come un argomento senza virgolette. Omettere args quando è necessario utilizzare funzionalità shell come pipe o &&, o quando nessuno dei due problemi si applica.
Exec form viene eseguito quando args è presente. Claude Code risolve command come 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 segnaposti 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 alcuna 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. Impostare 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 strumenti installano in node_modules/.bin non sono eseguibili e non possono essere generati senza una shell. Per eseguirli in exec form, invocare lo script sottostante con node direttamente, ad esempio "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Il modello node più percorso-script funziona su ogni piattaforma perché node.exe è un vero binario. Per eseguire uno shim .cmd o .bat per nome, utilizzare 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 del plugin inoltre sostituiscono i valori ${user_config.*}, in exec form solo: il valore viene sostituito in command e in ogni elemento args come stringa semplice, quindi nessuna shell lo rianalizza.
Un hook del plugin in shell form il cui command fa riferimento a ${user_config.*} fallisce con un errore invece di eseguirsi. Per utilizzare un valore di opzione da un hook in shell form, leggere la variabile di ambiente $CLAUDE_PLUGIN_OPTION_<KEY>, come $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL per un’opzione webhook_url, oppure impostare args per passare l’hook a exec form. Prima di v2.1.207, i comandi degli hook del plugin in shell form sostituivano anche ${user_config.*}.
In exec form,
command è solo il nome o il percorso dell’eseguibile. Se command è un nome semplice senza separatore di percorso e contiene spazi bianchi insieme a args, Claude Code registra un avviso perché la generazione avrà esito negativo: non esiste un eseguibile denominato node script.js. Spostare 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 del HTTP hook
Oltre ai campi comuni, gli HTTP hook accettano questi campi:
Claude Code invia l’input JSON del hook come corpo della richiesta POST con
Content-Type: application/json. Il corpo della risposta utilizza lo stesso formato JSON di output dei command hook.
La gestione degli errori differisce dai command hook: le risposte non-2xx, i guasti di connessione e i timeout producono tutti errori non bloccanti che consentono l’esecuzione di continuare. Per bloccare una chiamata dello strumento o negare un’autorizzazione, restituire una risposta 2xx con un corpo JSON contenente decision: "block" o un hookSpecificOutput con permissionDecision: "deny".
Questo esempio invia gli eventi PreToolUse a un servizio di convalida locale, autenticandosi con un token dalla variabile di ambiente MY_TOKEN:
Campi del MCP tool hook
Oltre ai campi comuni, gli MCP tool hook accettano questi campi:
L’output di testo dello strumento viene trattato come stdout del command hook: se analizzato come output JSON valido viene elaborato come una decisione, altrimenti viene mostrato come testo semplice. Se il server denominato non è connesso, o lo strumento restituisce
isError: true, l’hook produce un errore non bloccante e l’esecuzione continua.
Gli MCP tool hook sono disponibili su ogni hook event una volta che Claude Code si è connesso ai server MCP. SessionStart e Setup in genere si attivano prima che i server finiscano di connettersi, quindi gli hook su questi eventi dovrebbero aspettarsi l’errore “not connected” alla prima esecuzione.
Questo esempio chiama lo strumento security_scan sul server MCP my_server dopo ogni Write o Edit, passando il percorso del file modificato:
Campi del prompt hook e agent hook
Oltre ai campi comuni, i prompt hook e agent hook accettano questi campi:Fare riferimento agli script per percorso
Utilizzare questi segnaposti per fare riferimento agli script del 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. Claude Code inoltre imposta questa variabile nell’ambiente dei server MCP stdio e dei server LSP del plugin.${CLAUDE_PLUGIN_ROOT}: la directory di installazione del plugin, per gli script forniti con un plugin. Cambia ad ogni aggiornamento del plugin.${CLAUDE_PLUGIN_DATA}: la directory di dati persistenti del plugin, per le dipendenze e lo stato che dovrebbero sopravvivere agli aggiornamenti del plugin.
args come un argomento senza tokenizzazione shell, quindi i percorsi con spazi o caratteri speciali non hanno bisogno di virgolette. In shell form, racchiudere ogni segnaposto tra virgolette doppie.
- Script del progetto
- Script del plugin
Questo esempio utilizza
${CLAUDE_PROJECT_DIR} per eseguire un controllo dello stile dalla directory .claude/hooks/ del progetto dopo qualsiasi chiamata dello strumento Write o Edit:Hook in skills e agents
Oltre ai file di impostazioni e ai plugin, gli hook possono essere definiti direttamente in skills e subagents utilizzando il frontmatter. Questi hook sono limitati al ciclo di vita del componente e vengono eseguiti solo quando quel componente è attivo. Tutti gli hook event sono supportati. Per i subagent, gli hookStop vengono automaticamente convertiti in SubagentStop poiché questo è l’evento che si attiva quando un subagent termina.
Gli hook utilizzano lo stesso formato di configurazione dei hook basati su impostazioni ma sono limitati alla durata del componente e vengono puliti quando termina.
Questa skill definisce un hook PreToolUse che esegue uno script di convalida della sicurezza prima di ogni comando Bash:
Il menu /hooks
Digitare /hooks in Claude Code per aprire un browser di sola lettura per i hook configurati. Il menu mostra ogni hook event con un conteggio dei hook configurati, consente di approfondire i matcher e mostra i dettagli completi di ogni gestore del hook. Utilizzarlo per verificare la configurazione, controllare da quale file di impostazioni proviene un hook o ispezionare il comando, il prompt o l’URL di un hook.
Il menu visualizza tutti e cinque i tipi di hook: command, prompt, agent, http e mcp_tool. Ogni hook è etichettato con un prefisso [type] e una fonte che indica dove è stato definito:
User: da~/.claude/settings.jsonProject: da.claude/settings.jsonLocal: da.claude/settings.local.jsonPlugin: dahooks/hooks.jsondi un pluginSession: registrato in memoria per la sessione correnteBuilt-in: registrato internamente da Claude Code
Disabilitare o rimuovere i hook
Per rimuovere un hook, eliminare la sua voce dal file di impostazioni JSON. Per disabilitare temporaneamente tutti gli hook senza rimuoverli, impostare"disableAllHooks": true nel file di impostazioni. 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 i hook attraverso le impostazioni della politica gestita, disableAllHooks impostato nelle impostazioni dell’utente, del progetto o locali non può disabilitare quei hook gestiti. Solo disableAllHooks impostato a livello di impostazioni gestite può disabilitare i hook gestiti.
Le modifiche dirette ai hook nei file di impostazioni vengono normalmente acquisite 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 a partire da v2.1.139. 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. 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 non è garantito che sia presente. Non esiste una variabile di ambiente $CLAUDE_MODEL. Un processo hook eredita l’ambiente padre, quindi può leggere $ANTHROPIC_MODEL se lo imposti nella tua shell, ma quel valore non cambia quando cambi modelli con /model durante una sessione. Un insieme di variabili non viene ereditato: Claude Code rimuove le variabili dell’esportatore OTEL_* da ogni sottoprocesso che genera, inclusi gli hook.
Ad esempio, un hook PreToolUse per un comando Bash riceve questo su stdin:
tool_name e tool_input 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. Exit 0 significa successo. Claude Code analizza stdout per i campi di output JSON. L’output JSON viene elaborato solo su exit 0. Per la maggior parte degli eventi, stdout viene scritto nel log di debug ma non mostrato nella trascrizione. Le eccezioni sonoUserPromptSubmit, UserPromptExpansion e SessionStart, dove stdout viene aggiunto come contesto che Claude può vedere e su cui agire.
Exit 2 significa un errore bloccante. Claude Code ignora stdout e qualsiasi JSON in esso. Invece, il testo stderr viene restituito a Claude come messaggio di errore. L’effetto dipende dall’evento: PreToolUse blocca la chiamata dello strumento, UserPromptSubmit rifiuta il prompt e così via. Consultare exit code 2 behavior per l’elenco completo.
Qualsiasi altro codice di uscita è un errore non bloccante per la maggior parte degli eventi hook. La trascrizione mostra un avviso <hook name> hook error seguito dalla prima riga di stderr, in modo da poter identificare la causa senza --debug. L’esecuzione continua e lo stderr completo viene scritto nel log di debug.
Ad esempio, uno script di comando hook che blocca i comandi Bash pericolosi:
Comportamento del codice di uscita 2 per evento
Il codice di uscita 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, Setup e SubagentStart, lo stderr del codice di uscita 2 viene visualizzato nella trascrizione come un avviso <hook name> hook error, nello stesso modo di 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.
A partire da Claude Code v2.1.199, SessionStart, Setup e SubagentStart mostrano lo stderr del codice di uscita 2 nella trascrizione. Le versioni precedenti lo scrivevano solo nel log di debug.
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:- 2xx con corpo vuoto: successo, equivalente al codice di uscita 0 senza output
- 2xx con corpo di testo semplice: successo, il testo viene aggiunto come contesto
- 2xx con corpo JSON: successo, analizzato utilizzando lo stesso schema JSON output dei command hook
- Stato non-2xx: errore non bloccante, l’esecuzione continua
- Guasto di connessione o timeout: errore non bloccante, l’esecuzione continua
Output JSON
I codici di uscita consentono di bloccare o stare in silenzio, ma l’output JSON offre un controllo più granulare. Invece di uscire con il codice 2 per bloccare, uscire 0 e stampare un oggetto JSON su stdout. Claude Code legge campi specifici da quel JSON per controllare il comportamento, incluso il decision control per bloccare, consentire o escalare all’utente.È necessario scegliere un approccio per hook, non entrambi: utilizzare i codici di uscita da soli per la segnalazione oppure uscire 0 e stampare JSON per il controllo strutturato. Claude Code elabora JSON solo su exit 0. Se si esce con 2, qualsiasi JSON viene ignorato.
additionalContext, systemMessage e stdout semplice, sono limitate a 10.000 caratteri. L’output che supera questo limite viene salvato in un file e sostituito con un’anteprima e un percorso di file, nello stesso modo in cui vengono gestiti i risultati degli strumenti di grandi dimensioni.
L’oggetto JSON supporta tre tipi di campi:
- Campi universali come
continuefunzionano su tutti gli eventi. Questi sono elencati nella tabella seguente. decisionereasondi livello superiore vengono 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 indipendentemente dal tipo di evento:
Emettere notifiche del terminale
Il campoterminalSequence richiede Claude Code v2.1.141 o successivo.
Gli hook vengono eseguiti senza un terminale di controllo, quindi la scrittura di 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
Notification. La sequenza di escape viene costruita con printf escape ottali 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. Su Windows, costruire la stringa di escape in PowerShell o uno script e emettere lo stesso oggetto JSON.
terminalSequence è la sostituzione supportata per gli hook che in precedenza scrivevano sequenze di escape direttamente su /dev/tty. La lista di autorizzazione è limitata alle sequenze che non possono spostare il cursore o alterare i colori, quindi un hook non può mai corrompere un prompt sullo schermo.Aggiungere contesto per Claude
Il campoadditionalContext passa una stringa dal 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, Setup 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
additionalContext per lo stesso evento, Claude riceve tutti i valori. Se un valore supera 10.000 caratteri, Claude Code scrive il testo completo in un file nella directory della sessione e passa a Claude il percorso del file con un’anteprima breve.
Utilizzare additionalContext per informazioni che Claude dovrebbe conoscere sullo stato corrente dell’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.
Una volta iniettato, il testo viene salvato nella trascrizione della sessione. Per gli eventi a metà sessione come PostToolUse o UserPromptSubmit, la ripresa con --continue o --resume riproduce il testo salvato piuttosto che rieseguire l’hook per i turni passati, quindi i valori come timestamp o SHA di commit diventano obsoleti al ripristino. Gli hook SessionStart vengono eseguiti di nuovo al ripristino con source impostato su "resume", 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. Utilizzare 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 control per l’insieme completo di opzioni.PermissionRequest:updatedInputall’interno dell’oggettodecision. Consultare PermissionRequest decision control per l’insieme completo di opzioni.PostToolUse:updatedToolOutputsostituisce il risultato dello strumento. Consultare PostToolUse decision control per l’insieme completo di opzioni.UserPromptSubmit: 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
Utilizzato da
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange e PreCompact. L’unico valore è "block". Per consentire all’azione di procedere, omettere decision dal JSON o uscire 0 senza alcun JSON:Hook events
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 per corrispondere al ciclo di vita: dalla configurazione della sessione attraverso il ciclo agentico alla fine della sessione. Ogni sezione descrive quando l’evento si attiva, quali matcher supporta, l’input JSON che riceve e come controllare il comportamento attraverso 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 problemi esistenti o modifiche recenti al codebase, o per configurare le variabili di ambiente. Per il contesto statico che non richiede uno script, utilizzare CLAUDE.md invece. SessionStart viene eseguito ad ogni sessione, quindi mantenere questi hook veloci. Solo gli hooktype: "command" e type: "mcp_tool" sono supportati.
Il valore del matcher corrisponde a come è stata avviata la sessione:
Input di SessionStart
Oltre ai campi di input comuni, gli hook SessionStart ricevonosource e facoltativamente model, agent_type e session_title:
Controllo della decisione di SessionStart
Qualsiasi testo che lo script del hook stampa su stdout viene aggiunto come contesto per Claude. Oltre ai campi di output JSON disponibili per tutti gli hook, è possibile restituire questi campi specifici dell’evento:suppressOutput o sessionTitle.
Utilizzare reloadSkills quando un hook SessionStart installa o aggiorna skill. La scoperta delle skill normalmente viene eseguita prima del completamento degli hook SessionStart, quindi i file che l’hook scrive in ~/.claude/skills/ o .claude/skills/ altrimenti apparirebbero solo nella sessione successiva. Questo esempio sincronizza un repository di skill condiviso e richiede la nuova scansione:
Persistere le variabili di ambiente
Gli hook SessionStart hanno accesso alla variabile di ambienteCLAUDE_ENV_FILE, che fornisce un percorso di file in cui è possibile persistere le variabili di ambiente per i comandi Bash successivi.
Per impostare le singole variabili di ambiente, scrivere le istruzioni export in CLAUDE_ENV_FILE. Utilizzare l’aggiunta (>>) 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 si avvia Claude Code con--init-only, o con --init o --maintenance in modalità non interattiva con il flag -p. Non si attiva all’avvio normale. Utilizzarlo per l’installazione di dipendenze una tantum o la pulizia pianificata che si attiva esplicitamente da CI o script, separato dall’avvio della sessione normale. Per l’inizializzazione per sessione, utilizzare SessionStart invece.
Il valore del matcher corrisponde al flag CLI che ha attivato l’hook:
--init-only esegue gli hook Setup e gli hook SessionStart con il matcher startup, quindi esce senza avviare una conversazione. --init e --maintenance attivano gli hook Setup solo quando combinati con -p; in una sessione interattiva questi due flag attualmente non attivano gli hook Setup.
Poiché Setup non si attiva ad ogni avvio, un plugin che ha bisogno di una dipendenza installata non può fare affidamento solo su Setup. Il modello pratico è controllare la dipendenza al primo utilizzo e installare se assente, ad esempio un hook o una skill che testa per ${CLAUDE_PLUGIN_DATA}/node_modules ed esegue npm install se assente. Consultare la directory dei dati persistenti per dove archiviare le dipendenze installate.
Input di Setup
Oltre ai campi di input comuni, gli hook Setup ricevono un campotrigger impostato su "init" o "maintenance":
Controllo della decisione di Setup
Gli hook Setup non possono bloccare. Qualsiasi exit code non zero, incluso 2, fa emergere stderr all’utente come avviso<hook name> hook error, e l’esecuzione continua. In modalità non interattiva, l’output dell’hook appare solo quando si avvia con --verbose.
Per passare informazioni nel contesto di Claude, restituire additionalContext nell’output JSON; lo stdout semplice viene scritto nel log di debug solo. Oltre ai campi di output JSON disponibili per tutti gli hook, è possibile restituire questi campi specifici dell’evento:
CLAUDE_ENV_FILE. Le variabili scritte in quel file persistono nei comandi Bash successivi per la sessione, proprio come negli hook SessionStart. Solo gli hook type: "command" e type: "mcp_tool" sono supportati.
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 con entusiasmo e di nuovo in seguito quando i file vengono caricati in modo pigro, ad esempio quando Claude accede a una sottodirectory che contiene un CLAUDE.md annidato o quando le regole condizionali con frontmatter paths: corrispondono. L’hook non supporta il blocco o il controllo della decisione. Viene eseguito in modo asincrono per scopi di osservabilità.
Il matcher viene eseguito su load_reason. Ad esempio, utilizzare "matcher": "session_start" per attivarsi solo per i file caricati all’avvio della sessione, o "matcher": "path_glob_match|nested_traversal" per attivarsi solo per i caricamenti pigri.
Input di InstructionsLoaded
Oltre ai campi di input comuni, gli hook InstructionsLoaded ricevono questi campi:Controllo della decisione di InstructionsLoaded
Gli hook InstructionsLoaded non hanno controllo della decisione. Non possono bloccare o modificare il caricamento delle istruzioni. Utilizzare questo evento per la registrazione di audit, il tracciamento della conformità o l’osservabilità.UserPromptSubmit
Viene eseguito quando l’utente invia un prompt, prima che Claude lo elabori. Ciò consente di aggiungere contesto aggiuntivo in base al prompt/conversazione, convalidare i prompt o bloccare determinati tipi di prompt. Gli hookUserPromptSubmit hanno un timeout predefinito di 30 secondi per i tipi command, http e mcp_tool, più breve del default di 600 secondi per questi tipi su altri eventi. Poiché questo hook viene eseguito prima di ogni prompt e blocca l’elaborazione del modello fino al completamento, un hook bloccato blocca la sessione. Se l’hook ha bisogno di più tempo, impostare il campo timeout nella voce dell’hook.
Un hook UserPromptSubmit che raggiunge il suo timeout viene annullato e il suo output, incluso qualsiasi additionalContext, viene scartato. Il prompt raggiunge comunque Claude senza quel contesto. A partire da v2.1.196, la trascrizione mostra un avviso che nomina l’hook, il timeout che si è attivato e che l’output è stato scartato. Le versioni precedenti annullano l’hook senza avviso.
Un hook di callback Agent SDK su UserPromptSubmit che raggiunge il suo timeout blocca il prompt con un messaggio che nomina l’hook e il timeout, perché un callback lì può agire come un gate di politica che non deve fallire in modo aperto. La sessione continua. Prima di 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 che l’utente ha inviato.
Controllo della decisione di UserPromptSubmit
Gli hookUserPromptSubmit possono controllare se un prompt dell’utente viene elaborato e aggiungere contesto. Tutti i campi di output JSON sono disponibili.
Ci sono due modi per aggiungere contesto alla conversazione su exit code 0:
- Stdout di testo semplice: qualsiasi testo non-JSON scritto su stdout viene aggiunto come contesto
- JSON con
additionalContext: utilizzare il formato JSON seguente per un controllo maggiore. Il campoadditionalContextviene aggiunto come contesto
additionalContext viene iniettato come un promemoria di sistema che Claude legge senza una voce di trascrizione visibile.
Per bloccare un prompt, restituire un oggetto JSON con decision impostato su "block":
UserPromptExpansion
Viene eseguito quando un comando slash digitato dall’utente si espande in un prompt prima di raggiungere Claude. Utilizzare questo per bloccare comandi specifici dall’invocazione diretta, iniettare contesto per una skill particolare o registrare quali comandi gli utenti invocano. 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 /skillname direttamente bypassa PreToolUse. UserPromptExpansion si attiva su quel percorso diretto.
Corrisponde a command_name. Lasciare il matcher vuoto per attivarsi su ogni slash command 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 skill e comandi personalizzati, o mcp_prompt per i prompt del server MCP.
Controllo della decisione di UserPromptExpansion
Gli hookUserPromptExpansion possono bloccare l’espansione o aggiungere contesto. Tutti i campi di output JSON sono disponibili.
MessageDisplay
Viene eseguito mentre un messaggio dell’assistente viene trasmesso sullo schermo. Claude Code visualizza il messaggio in incrementi: ogni volta che un batch di righe appena completate è pronto per il rendering, l’hook viene eseguito una volta con quelle righe e Claude Code esegue il rendering del testo di sostituzione dell’hook al loro posto. Un messaggio lungo produce più chiamate; un messaggio breve può produrne solo una. Utilizzare MessageDisplay per:- rimuovere il markdown per una visualizzazione minima
- trasformare il testo che un’applicazione Agent SDK mostra ai suoi utenti
- oscurare le chiavi API o i nomi host interni dalle risposte di Claude
timeout nella voce dell’hook.
MessageDisplay è solo per la visualizzazione: il testo di sostituzione cambia solo ciò che viene renderizzato sullo schermo. La trascrizione e ciò che Claude vede mantengono il testo originale, quindi Claude non vede mai la sostituzione e la modalità verbose mostra l’originale. L’hook riceve solo il testo del messaggio dell’assistente, quindi i risultati degli strumenti e il testo che digitate vengono renderizzati invariati.
MessageDisplay non supporta i matcher e si attiva per ogni messaggio dell’assistente che trasmette testo; i messaggi senza testo, come le risposte solo con chiamate di strumenti, non lo attivano.
Nelle esecuzioni non interattive, incluse le query Agent SDK e claude -p, MessageDisplay viene eseguito una volta per messaggio dell’assistente invece che una volta per batch 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 per ogni messaggio riceve lo stesso testo totale in entrambe le modalità.
Input di MessageDisplay
Oltre ai campi di input comuni, gli hook MessageDisplay ricevono identificatori per il turno e il messaggio, la posizione di questa chiamata all’interno del messaggio e il nuovo testo indelta. I confini dei batch dipendono da come il testo viene trasmesso, quindi utilizzare index e final per tracciare l’avanzamento attraverso un messaggio piuttosto che aspettarsi 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 della decisione. Non possono bloccare il messaggio o modificare ciò che viene archiviato nella trascrizione o inviato a Claude.
Questo esempio rimuove la formattazione markdown dalle risposte di Claude per una visualizzazione in testo semplice. Lo script legge ogni batch da stdin, rimuove i marcatori di grassetto e i backtick del codice inline da
delta e restituisce il risultato come displayContent.
- macOS/Linux
- Windows (PowerShell)
Registrare un hook di comando per l’evento nel file di impostazioni:Salvare questo script in Lo script ha bisogno di
.claude/hooks/plain-display.sh nel progetto e renderlo eseguibile con chmod +x:jq nel vostro PATH.jq è mancante, Claude Code visualizza il testo originale e nota il guasto solo nell’output di debug, non nella sessione.
PreToolUse
Viene eseguito dopo che Claude crea i parametri dello strumento e prima dell’elaborazione della chiamata dello strumento. Corrisponde al nome dello strumento:Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode e qualsiasi nome di strumento MCP.
Utilizzare il PreToolUse decision control per consentire, negare, chiedere o rinviare il permesso di utilizzare lo strumento.
Input di PreToolUse
Oltre ai campi di input comuni, gli hook PreToolUse ricevonotool_name, tool_input e tool_use_id. I campi tool_input dipendono dallo strumento:
Esegue comandi shell.
Crea o sovrascrive un file.
Sostituisce una stringa in un file esistente.
Legge il contenuto del file.
Trova i file che corrispondono a un modello glob.
Cerca il contenuto dei file con espressioni regolari.
Recupera ed elabora il contenuto web.
Cerca il web.
Genera un subagent.
In
PostToolUse, tool_response per una chiamata Agent completata contiene il testo finale del subagent insieme alla telemetria di utilizzo. Leggere questi campi per registrare il costo per subagent da un hook:
Per i subagent in background, lo strumento ritorna immediatamente dopo il lancio, quindi
tool_response non contiene campi di utilizzo. Ha status: "async_launched", agentId, description, prompt, outputFile e resolvedModel invece.
Il campo resolvedModel nomina il modello su cui il subagent effettivamente viene eseguito, che può differire dal valore model in tool_input, ad esempio quando availableModels o un altro override si applica. Richiede Claude Code v2.1.174 o successivo.
Chiede all’utente da una a quattro domande a scelta multipla.
Presenta un piano e chiede all’utente di approvarlo prima che Claude lasci la modalità piano. Claude scrive il piano in un file su disco prima di chiamare lo strumento, quindi l’
tool_input letterale dal modello è tipicamente vuoto. Claude Code inietta 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, più flag di stato interni. Leggere tool_response.plan per il contenuto del piano piuttosto che rileggere il file da disco.
Controllo della decisione di PreToolUse
Gli hookPreToolUse possono controllare se una chiamata dello strumento procede. A differenza di altri hook che utilizzano un campo decision di livello superiore, PreToolUse restituisce la sua decisione all’interno di un oggetto hookSpecificOutput. Ciò gli dà un controllo più ricco: quattro risultati (consentire, negare, chiedere o rinviare) più la capacità di modificare l’input dello strumento prima dell’esecuzione.
Quando più hook PreToolUse restituiscono decisioni diverse, la precedenza è
deny > defer > ask > allow.
Quando un hook restituisce "ask", il prompt di autorizzazione visualizzato all’utente include un’etichetta che identifica da dove proviene l’hook: ad esempio, [User], [Project], [Plugin] o [Local]. Ciò aiuta gli utenti a capire quale fonte di configurazione sta richiedendo la conferma.
AskUserQuestion e ExitPlanMode richiedono l’interazione dell’utente e normalmente bloccano in modalità non interattiva con il flag -p. Restituire permissionDecision: "allow" insieme a updatedInput soddisfa quel requisito: l’hook legge l’input dello strumento da stdin, raccoglie la risposta attraverso la propria interfaccia utente e la restituisce in updatedInput in modo che lo strumento venga eseguito senza richiedere. Restituire "allow" da solo non è sufficiente per questi strumenti. Per AskUserQuestion, ripetere l’array questions originale e aggiungere un oggetto answers che mappa il testo di ogni domanda alla risposta scelta. Le risposte multi-select uniscono le etichette con virgole. Claude non imposta questo campo; fornirlo tramite updatedInput per rispondere a livello di programmazione.
Strumenti connettore che l’organizzazione ha impostato su ask richiedono il prompt anche quando un hook restituisce "allow".
A partire da v2.1.199, uno strumento MCP il cui server lo contrassegna con _meta["anthropic/requiresUserInteraction"] è più rigoroso: un hook non può saltare il suo prompt di approvazione con "allow", con o senza updatedInput, perché Claude Code non può confermare che l’hook ha raccolto l’interazione di cui lo strumento ha bisogno.
PreToolUse in precedenza utilizzava i campi
decision e reason di livello superiore, ma questi sono deprecati per questo evento. Utilizzare invece hookSpecificOutput.permissionDecision e hookSpecificOutput.permissionDecisionReason. I valori deprecati "approve" e "block" si mappano a "allow" e "deny" rispettivamente. Gli altri eventi come PostToolUse e Stop continuano a utilizzare decision e reason di livello superiore come formato corrente.Rinviare una chiamata dello strumento per dopo
"defer" è per le integrazioni che eseguono claude -p come subprocess e leggono il suo output JSON, come un’app Agent SDK o un’interfaccia utente personalizzata costruita su Claude Code. Consente a quel processo chiamante di mettere in pausa Claude in una chiamata dello strumento, raccogliere input attraverso la sua interfaccia e riprendere da dove era rimasto. Claude Code onora questo valore solo in modalità non interattiva con il flag -p. Nelle sessioni interattive registra un avviso e ignora il risultato del hook.
Lo strumento AskUserQuestion è il caso tipico: Claude vuole chiedere qualcosa all’utente, ma non c’è un terminale per rispondere. Il round trip 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 dello strumento in sospeso preservata nella trascrizione. - Il processo chiamante legge
deferred_tool_usedal risultato SDK, visualizza la domanda nella sua interfaccia utente e attende una risposta. - Il processo chiamante esegue
claude -p --resume <session-id>. La stessa chiamata dello strumento attivaPreToolUsedi nuovo. - L’hook restituisce
permissionDecision: "allow"con la risposta inupdatedInput. Lo strumento viene eseguito e Claude continua.
deferred_tool_use contiene l’id, il name e l’input dello strumento. L’input è i parametri che Claude ha generato per la chiamata dello strumento, acquisiti prima dell’esecuzione:
cleanupPeriodDays che elimina i file di sessione dopo 30 giorni per impostazione predefinita. Se la risposta non è pronta quando riprendi, l’hook può restituire "defer" di nuovo e il processo esce nello 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 dello strumento nel turno. Se Claude effettua più chiamate dello strumento contemporaneamente, "defer" viene ignorato con un avviso e lo strumento procede attraverso il flusso di autorizzazione normale. Il vincolo esiste perché resume può solo rieseguire uno strumento: non c’è modo di rinviare una chiamata da un batch senza lasciare le altre irrisolte.
Se lo strumento rinviato 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 ha fornito lo strumento non è connesso per la sessione ripresa. Il payload deferred_tool_use è ancora incluso in modo da poter identificare quale strumento è scomparso.
--resume ripristina la modalità di autorizzazione che era attiva quando lo strumento è stato rinviato, quindi non è necessario passare di nuovo --permission-mode. Le eccezioni sono plan e bypassPermissions, che non vengono mai trasportati. Passare --permission-mode esplicitamente su resume sovrascrive il valore ripristinato.PermissionRequest
Viene eseguito quando all’utente viene mostrata una finestra di dialogo di autorizzazione. Utilizzare il PermissionRequest decision control per consentire o negare per conto dell’utente. Corrisponde al nome dello strumento, 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. Un array permission_suggestions facoltativo contiene le opzioni “consenti sempre” che l’utente normalmente vedrebbe nella finestra di dialogo di autorizzazione. La differenza è quando l’hook si attiva: gli hook PermissionRequest vengono eseguiti quando una finestra di dialogo di autorizzazione sta per essere mostrata all’utente, mentre gli hook PreToolUse vengono eseguiti prima dell’esecuzione dello strumento indipendentemente dallo stato di autorizzazione.
Controllo della decisione di PermissionRequest
Gli hookPermissionRequest possono consentire o negare le richieste di autorizzazione. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del hook può restituire un oggetto decision con questi campi specifici dell’evento:
Permission update entries
Il campo di outputupdatedPermissions e il campo di input permission_suggestions utilizzano entrambi lo stesso array di oggetti di voce. Ogni voce ha un type che determina i suoi altri campi e una destination che controlla dove viene scritta la modifica.
setMode con bypassPermissions ha effetto solo se la sessione è stata avviata con la modalità bypass già disponibile: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions o permissions.defaultMode: "bypassPermissions" nelle impostazioni, e la modalità non è disabilitata da permissions.disableBypassPermissionsMode. Altrimenti l’aggiornamento è un no-op. bypassPermissions non viene mai persistito come defaultMode indipendentemente da destination.destination su ogni voce determina se la modifica rimane in memoria o persiste in un file di impostazioni.
Un hook può ripetere uno dei
permission_suggestions che ha ricevuto come suo proprio output updatedPermissions, che è equivalente all’utente che seleziona quell’opzione “consenti sempre” nella finestra di dialogo.
PostToolUse
Viene eseguito immediatamente dopo il completamento riuscito di uno strumento. Corrisponde al nome dello strumento, stessi valori di PreToolUse.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, che tool_response, il risultato che ha restituito. Lo schema esatto per entrambi dipende dallo strumento.
Controllo della decisione 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 hook può restituire questi campi specifici dell’evento:
L’esempio seguente sostituisce l’output di una chiamata
Bash. Il valore di sostituzione corrisponde alla forma di output dello strumento Bash:
PostToolUseFailure
Viene eseguito quando l’esecuzione di uno strumento non riesce: lo strumento ha generato un errore o uno strumento MCP ha restituito un risultato di errore. Utilizzare questo per registrare i guasti, inviare avvisi o fornire feedback correttivo a Claude. Corrisponde al nome dello strumento, stessi valori di PreToolUse.Questo evento non si attiva per le chiamate dello strumento rifiutate prima dell’esecuzione: un nome di strumento sconosciuto, input che non supera la convalida dello schema o dello strumento specifico, o un rifiuto di autorizzazione. I rifiuti di convalida vengono restituiti come risultati
tool_use_error e si verificano prima che gli hook vengano eseguiti, quindi non attivano né PreToolUse né questo evento. I rifiuti di autorizzazione attivano PreToolUse ma non questo evento; consultare PermissionDenied.Input di PostToolUseFailure
Gli hook PostToolUseFailure ricevono gli stessi campitool_name e tool_input di PostToolUse, insieme alle informazioni di errore come campi di livello superiore:
Controllo della decisione di PostToolUseFailure
Gli hookPostToolUseFailure possono fornire contesto a Claude dopo un guasto dello strumento. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del hook può restituire questi campi specifici dell’evento:
PostToolBatch
Viene eseguito una volta dopo che ogni chiamata dello 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 contemporaneamente quando Claude effettua chiamate dello strumento parallele. PostToolBatch si attiva esattamente una volta con l’intero batch, quindi è il posto giusto per iniettare contesto che dipende dall’insieme di strumenti che hanno eseguito piuttosto che da qualsiasi singolo strumento. Non c’è matcher per questo evento.
Input di PostToolBatch
Oltre ai campi di input comuni, gli hook PostToolBatch ricevonotool_calls, un array che descrive ogni chiamata dello 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 lo ha emesso. Per Read, ciò significa testo con prefisso numero di riga piuttosto che contenuti di file grezzi. Le risposte possono essere grandi, quindi analizzare solo i campi di cui hai bisogno.
La forma
tool_response differisce da quella di PostToolUse. PostToolUse passa l’oggetto Output strutturato dello strumento, come {filePath: "...", success: true} per Write; PostToolBatch passa il contenuto tool_result serializzato che il modello vede.Controllo della decisione di PostToolBatch
Gli hookPostToolBatch possono iniettare contesto per Claude. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del hook può restituire questi campi specifici dell’evento:
decision: "block" o continue: false interrompe il ciclo agentico prima della prossima chiamata del modello.
PermissionDenied
Viene eseguito quando il classificatore della modalità automatica nega una chiamata dello strumento. Questo hook si attiva solo in modalità automatica: non viene eseguito quando si nega manualmente una finestra di dialogo di autorizzazione, quando un hookPreToolUse blocca una chiamata o quando una regola deny corrisponde. Utilizzare questo per registrare i rifiuti del classificatore, regolare la configurazione o dire al modello che può riprovare la chiamata dello strumento.
Corrisponde al nome dello strumento, 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.
Controllo della decisione di PermissionDenied
Gli hook PermissionDenied possono dire al modello che può riprovare la chiamata dello strumento negata. Restituire un oggetto JSON conhookSpecificOutput.retry impostato su true:
retry è true, Claude Code aggiunge un messaggio alla conversazione dicendo al modello che può riprovare la chiamata dello strumento. Il rifiuto stesso non viene invertito. Se l’hook non restituisce JSON o restituisce retry: false, il rifiuto rimane e il modello riceve il messaggio di rifiuto originale.
Notification
Viene eseguito quando Claude Code invia notifiche. Corrisponde al tipo di notifica. Omettere il matcher per eseguire gli hook per tutti i tipi di notifica.
I tipi
agent_needs_input e agent_completed richiedono Claude Code v2.1.198 o successivo.
Utilizzare matcher separati per eseguire gestori diversi a seconda del tipo di notifica. Questa configurazione attiva uno script di avviso specifico per l’autorizzazione quando Claude ha bisogno dell’approvazione dell’autorizzazione e una notifica diversa quando Claude è stato 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 si applicano.
SubagentStart
Viene eseguito quando un subagent di Claude Code viene generato tramite lo strumento Agent. Supporta i matcher per filtrare per nome del tipo di agente. Per gli agenti incorporati, questo è il nome dell’agente comegeneral-purpose, Explore o Plan. Per i subagent personalizzati, questo è il campo name dal frontmatter dell’agente, non il nome del file.
Per i subagent forniti da un plugin, il tipo di agente è l’identificatore con ambito plugin come my-plugin:reviewer, non il nome del frontmatter nudo. I due punti posizionano un nome con ambito plugin sul percorso dell’espressione regolare, quindi ancorare 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 per il subagent e agent_type con il nome dell’agente che il matcher filtra.
SubagentStop
Viene eseguito quando un subagent di Claude Code ha finito di rispondere. Corrisponde al tipo di agente, 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 utilizzato per il filtraggio del matcher. Il transcript_path è la trascrizione della sessione principale, mentre agent_transcript_path è la trascrizione propria del subagent archiviata in una cartella subagents/ annidato. Il campo last_assistant_message contiene il contenuto del testo della risposta finale del subagent, quindi gli hook possono accedervi senza analizzare il file della trascrizione.
Gli hook SubagentStop ricevono anche gli array background_tasks e session_crons descritti in Stop input, disponibili in Claude Code v2.1.145 o successivo. Entrambi gli array sono limitati alla sessione padre, non al subagent.
hookSpecificOutput.additionalContext con hookEventName impostato su "SubagentStop", per il feedback non-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 prossima istruzione. Per iniettare contesto nella sessione padre dopo il ritorno di un subagent, utilizzare un hook PostToolUse sullo strumento Agent invece.
TaskCreated
Viene eseguito quando un’attività sta per essere creata tramite lo strumentoTaskCreate. Utilizzare questo per applicare le convenzioni di denominazione, richiedere descrizioni delle attività o impedire la creazione di determinate attività.
Quando un hook TaskCreated esce con il codice 2, l’attività non viene creata e il messaggio stderr viene restituito al modello come feedback. Per interrompere completamente il compagno di squadra invece di rieseguirlo, restituire JSON con {"continue": false, "stopReason": "..."}. Gli hook TaskCreated non supportano i matcher e si attivano ad 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 della decisione di TaskCreated
Gli hook TaskCreated supportano due modi per controllare la creazione dell’attività:- Codice di uscita 2: l’attività non viene creata e il messaggio stderr viene restituito al modello come feedback.
- JSON
{"continue": false, "stopReason": "..."}: interrompe completamente il compagno di squadra, corrispondendo al comportamento dell’hookStop. IlstopReasonviene mostrato all’utente.
TaskCompleted
Viene eseguito quando un’attività sta per essere contrassegnata come completata. Questo si attiva in due situazioni: quando qualsiasi agente contrassegna esplicitamente un’attività come completata attraverso lo strumento TaskUpdate, o quando un compagno di squadra di un agent team finisce il suo turno con attività in corso. Utilizzare questo per applicare i criteri di completamento come il passaggio dei test o dei controlli di linting prima che un’attività possa chiudersi. Quando un hookTaskCompleted esce con il codice 2, l’attività non viene contrassegnata come completata e il messaggio stderr viene restituito al modello come feedback. Per interrompere completamente il compagno di squadra invece di rieseguirlo, restituire JSON con {"continue": false, "stopReason": "..."}. Gli hook TaskCompleted non supportano i matcher e si attivano ad 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 della decisione di TaskCompleted
Gli hook TaskCompleted supportano due modi per controllare il completamento dell’attività:- Codice di uscita 2: l’attività non viene contrassegnata come completata e il messaggio stderr viene restituito al modello come feedback.
- JSON
{"continue": false, "stopReason": "..."}: interrompe completamente il compagno di squadra, corrispondendo al comportamento dell’hookStop. IlstopReasonviene mostrato all’utente.
Stop
Viene eseguito quando l’agente Claude Code principale ha finito di rispondere. Non viene eseguito se l’arresto si è verificato a causa di un’interruzione dell’utente. Gli errori API attivano StopFailure invece.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à continuando a causa di un hook di arresto. Controllare questo valore o elaborare la trascrizione per impedire a Claude Code di eseguire indefinitamente. Il campo last_assistant_message contiene il contenuto del testo della risposta finale di Claude, quindi gli hook possono accedervi senza analizzare il file della trascrizione.
Gli array background_tasks e session_crons, disponibili in Claude Code v2.1.145 o successivo, consentono agli hook di distinguere “la sessione è terminata” da “la sessione è in pausa in attesa che il lavoro in background la risvegli”. Entrambi gli array sono presenti quando il registro delle attività è raggiungibile e sono vuoti quando non c’è nulla in volo o programmato.
Ogni voce in background_tasks descrive un’attività in volo e utilizza questi campi:
Ogni voce in
session_crons descrive un risveglio programmato con ambito di sessione, proveniente da CronCreate, ScheduleWakeup e /loop:
Questo esempio mostra un input Stop con un’attività shell in volo e un cron ricorrente:
Controllo della decisione di Stop
Gli hookStop e SubagentStop possono controllare se Claude continua. Oltre ai campi di output JSON disponibili per tutti gli hook, lo script del hook può restituire questi campi specifici dell’evento:
additionalContext quando l’hook funziona come previsto e fornisce a Claude una guida, come “eseguire la suite di test prima di terminare”. Mantiene la conversazione attraverso gli stessi loop protections di decision: "block", vale a dire l’input stop_hook_active e il limite di 8 continuazioni consecutive, ma la trascrizione lo etichetta come Stop hook feedback e nessuna notifica di errore dell’hook viene mostrata:
StopFailure
Viene eseguito invece di Stop quando il turno termina a causa di un errore API. L’output e il codice di uscita vengono ignorati. Utilizzare questo per registrare i guasti, inviare avvisi o intraprendere azioni di recupero quando Claude non può completare una risposta a causa di limiti di velocità, 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 è utilizzato per il filtraggio del matcher.
TeammateIdle
Viene eseguito quando un compagno di squadra di un agent team sta per andare inattivo dopo aver finito il suo turno. Utilizzare questo per applicare gate di qualità prima che un compagno di squadra smetta di lavorare, come richiedere il passaggio dei controlli di linting o verificare che i file di output esistano. Quando un hookTeammateIdle esce con il codice 2, il compagno di squadra riceve il messaggio stderr come feedback e continua a lavorare invece di andare inattivo. Per interrompere completamente il compagno di squadra invece di rieseguirlo, restituire JSON con {"continue": false, "stopReason": "..."}. Gli hook TeammateIdle non supportano i matcher e si attivano ad ogni occorrenza.
Input di TeammateIdle
Oltre ai campi di input comuni, gli hook TeammateIdle ricevonoteammate_name e team_name.
Controllo della decisione di TeammateIdle
Gli hook TeammateIdle supportano due modi per controllare il comportamento del compagno di squadra:- Codice di uscita 2: il compagno di squadra riceve il messaggio stderr come feedback e continua a lavorare invece di andare inattivo.
- JSON
{"continue": false, "stopReason": "..."}: interrompe completamente il compagno di squadra, corrispondendo al comportamento dell’hookStop. IlstopReasonviene mostrato all’utente.
ConfigChange
Viene eseguito quando un file di configurazione cambia durante una sessione. Utilizzare questo per controllare le modifiche alle impostazioni, applicare le politiche di sicurezza o bloccare le modifiche non autorizzate ai file di configurazione. Gli hook ConfigChange si attivano per le modifiche ai file di impostazioni, alle impostazioni della politica gestita e ai file di skill. Il camposource nell’input dice quale tipo di configurazione è cambiato e il campo file_path facoltativo fornisce il percorso al file modificato.
Il matcher filtra sulla fonte di configurazione:
Questo esempio registra tutte le modifiche di configurazione per il controllo 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 al file specifico che è stato modificato.
Controllo della decisione di ConfigChange
Gli hook ConfigChange possono bloccare le modifiche di configurazione dall’avere effetto. Utilizzare il codice di uscita 2 o un JSONdecision per impedire la modifica. Quando bloccato, le nuove impostazioni non vengono applicate alla sessione in esecuzione.
policy_settings non possono essere bloccate. Gli hook si attivano ancora per le fonti policy_settings, quindi è possibile utilizzarli per la registrazione di audit, ma qualsiasi decisione di blocco viene ignorata. Ciò garantisce che le impostazioni gestite dall’azienda abbiano sempre effetto.
CwdChanged
Viene eseguito quando la directory di lavoro cambia durante una sessione, ad esempio quando Claude esegue un comandocd. Utilizzare questo per reagire ai cambi di directory: ricaricare le variabili di ambiente, attivare toolchain specifiche del progetto o eseguire script di configurazione automaticamente. Si accoppia con 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 per la sessione, proprio come negli hook SessionStart.
CwdChanged non supporta i matcher e si attiva ad ogni cambio di directory.
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 della decisione. Non possono bloccare il cambio di directory.
FileChanged
Viene eseguito quando un file monitorato cambia su disco. Utile per ricaricare le variabili di ambiente quando i file di configurazione del progetto vengono modificati. Ilmatcher per questo evento serve due ruoli:
- Costruire l’elenco di osservazione: il valore viene diviso su
|e ogni segmento viene registrato come nome di file letterale nella directory di lavoro, quindi".envrc|.env"monitora esattamente quei due file. I modelli regex non sono utili qui: un valore come^\.envmonitorerebbe un file letteralmente denominato^\.env. - Filtrare quali hook vengono eseguiti: quando un file monitorato cambia, lo stesso valore filtra quali gruppi di hook vengono eseguiti utilizzando le regole di matcher standard rispetto al basename del file modificato.
CLAUDE_ENV_FILE. Le variabili scritte in quel file persistono nei comandi Bash successivi per la sessione, proprio come negli hook SessionStart.
Input di FileChanged
Oltre ai campi di input comuni, gli hook FileChanged ricevonofile_path e 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 della decisione. Non possono bloccare il cambio di file dall’occorrenza.
WorktreeCreate
Viene eseguito quando un worktree sta per essere creato, sia daclaude --worktree che da un subagent che utilizza isolation: "worktree". Per impostazione predefinita Claude Code crea la copia di lavoro isolata con git worktree. Configurando un hook WorktreeCreate si sostituisce quel comportamento git predefinito, consentendo di utilizzare un sistema di controllo della versione diverso come SVN, Perforce o Mercurial.
Poiché l’hook sostituisce completamente il comportamento predefinito, .worktreeinclude non viene elaborato. Se è necessario copiare i file di configurazione locali come .env nel nuovo worktree, farlo all’interno dello script del hook.
L’hook deve restituire il percorso assoluto della directory del worktree creato. Claude Code utilizza questo percorso come directory di lavoro per la sessione isolata. Consultare WorktreeCreate output per come ogni tipo di hook restituisce il percorso.
Questo esempio crea una copia di lavoro SVN e stampa il percorso per Claude Code da utilizzare. Sostituire l’URL del repository con il proprio:
name del worktree dall’input JSON su stdin, controlla una copia fresca in una nuova directory e stampa il percorso della directory. L’echo sull’ultima riga è quello che Claude Code legge come percorso del worktree. Reindirizzare qualsiasi altro output a stderr in modo che non interferisca con il percorso.
Input di WorktreeCreate
Oltre ai campi di input comuni, gli hook WorktreeCreate ricevono il camponame. Questo è 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 utilizzano il modello di decisione di blocco/consentimento standard. Invece, il successo o il fallimento dell’hook determina il risultato. L’hook deve restituire il percorso assoluto della directory del worktree creato:- Command hooks (
type: "command"): stampano 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 vostroechovengono ignorati. Reindirizzare qualsiasi altro output dell’hook a stderr. - HTTP hooks (
type: "http"): restituiscono{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }nel corpo della risposta.
-p si bloccava per circa 30 secondi prima di uscire con il codice 0.
WorktreeRemove
Viene eseguito quando un worktree sta per essere rimosso, sia quando si esce da una sessione--worktree e si sceglie di rimuoverla, sia quando un subagent con isolation: "worktree" termina. Questa è la controparte di pulizia di WorktreeCreate.
Per i worktree basati su git, Claude Code gestisce la pulizia automaticamente con git worktree remove. Se si è configurato un hook WorktreeCreate per un sistema di controllo della versione non-git, accoppiarlo con un hook WorktreeRemove per gestire la pulizia. Senza uno, la directory del worktree viene lasciata su disco.
Claude Code passa il percorso restituito da WorktreeCreate come worktree_path nell’input del 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 al worktree in corso di rimozione.
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:
Uscire con il codice 2 per bloccare la compattazione. Per un
/compact manuale, il messaggio stderr viene mostrato all’utente. È anche possibile 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 non compattata. 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 non riesce.
Input di PreCompact
Oltre ai campi di input comuni, gli hook PreCompact ricevonotrigger e custom_instructions. Per manual, custom_instructions contiene quello che l’utente passa in /compact. Per auto, custom_instructions è vuoto.
PostCompact
Viene eseguito dopo che Claude Code completa un’operazione di compattazione. Utilizzare questo evento per reagire al nuovo stato compattato, ad esempio per registrare il riepilogo generato o aggiornare lo stato esterno. Gli stessi valori di matcher si applicano come perPreCompact:
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.
SessionEnd
Viene eseguito quando una sessione di Claude Code termina. Utile per le attività di pulizia, la registrazione delle statistiche della sessione o il salvataggio dello stato della sessione. Supporta i matcher per filtrare per motivo di uscita. Il camporeason nell’input del 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. Consultare la tabella dei motivi sopra per tutti i valori.
/clear e al cambio di sessioni tramite /resume interattivo. Se un hook ha bisogno di più tempo, impostare un timeout per hook nella configurazione del hook. Il budget complessivo viene automaticamente aumentato al timeout per hook più alto configurato nei file di impostazioni, fino a 60 secondi. I timeout impostati sui hook forniti dal plugin non aumentano il budget. Per sovrascrivere il budget in modo esplicito, impostare la variabile di ambiente CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS in millisecondi.
Elicitation
Viene eseguito quando un server MCP richiede l’input dell’utente a metà attività. Per impostazione predefinita, Claude Code mostra una finestra di dialogo interattiva per l’utente per rispondere. Gli hook possono intercettare questa richiesta e rispondere a livello di programmazione, saltando completamente la finestra di dialogo. Il campo matcher corrisponde al nome del server MCP.Input di Elicitation
Oltre ai campi di input comuni, gli hook Elicitation ricevonomcp_server_name, message e campi facoltativi mode, url, elicitation_id e requested_schema.
Per l’elicitazione in modalità modulo (il caso più comune):
Output di Elicitation
Per rispondere a livello di programmazione senza mostrare la finestra di dialogo, restituire un oggetto JSON conhookSpecificOutput:
Il codice di uscita 2 nega l’elicitazione e mostra stderr all’utente.
ElicitationResult
Viene eseguito dopo che un utente risponde a un’elicitazione MCP. Gli hook possono osservare, modificare o bloccare la risposta prima che venga inviata al server MCP. Il campo matcher corrisponde al nome del server MCP.Input di ElicitationResult
Oltre ai campi di input comuni, gli hook ElicitationResult ricevonomcp_server_name, action e campi facoltativi mode, elicitation_id e content.
Output di ElicitationResult
Per sovrascrivere la risposta dell’utente, restituire un oggetto JSON conhookSpecificOutput:
Il codice di uscita 2 blocca la risposta, cambiando l’azione effettiva in
decline.
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):
PermissionDeniedPermissionRequestPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
command, http e mcp_tool ma non prompt o agent:
ConfigChangeCwdChangedElicitationElicitationResultFileChangedInstructionsLoadedNotificationPostCompactPreCompactSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart e Setup supportano gli hook command e mcp_tool. 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, Haiku per impostazione predefinita
- 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. Claude Code invia il prompt combinato e l’input a un modello Claude veloce, che restituisce una decisione JSON.
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 continuaPreToolUse: la chiamata dello strumento viene negata e il motivo viene restituito a Claude come errore dello strumento, equivalente a un hook di comando conpermissionDecision: "deny"PostToolUse: 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 dacontinuePostToolUseFailure,TaskCreatedeTaskCompleted: il motivo viene restituito a Claude come errore dello strumento, simile aPreToolUseTeammateIdle: 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 "ok" è false, 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.
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 elabora la decisione nello stesso modo di un hook di prompt
Configurazione dell’hook agente
Impostaretype su "agent" e fornire una stringa prompt. I campi di configurazione sono gli stessi degli hook di prompt, con un timeout predefinito più lungo:
Lo schema di risposta è lo stesso degli hook di prompt:
{ "ok": true } per consentire o { "ok": false, "reason": "..." } per bloccare.
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 per un massimo di 120 secondi. Quando lo script termina, l’output viene consegnato al turno di conversazione successivo:
timeout imposta il tempo massimo in secondi per il processo in background. Se non specificato, gli hook asincroni utilizzano lo stesso timeout predefinito di 10 minuti degli hook sincroni.
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, se l’hook ha prodotto una risposta JSON con un campoadditionalContext, quel contenuto viene consegnato a Claude come contesto al turno di conversazione successivo. Un campo systemMessage viene mostrato a voi, non a Claude.
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 diversi vincoli rispetto agli hook sincroni:- Solo gli hook
type: "command"supportanoasync. Gli hook basati su prompt non possono essere eseguiti in modo asincrono. - Gli hook asincroni non possono bloccare le chiamate dello strumento o restituire decisioni. Nel momento in cui l’hook si completa, l’azione che lo ha attivato è già stata eseguita.
- 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
I command hook vengono eseguiti con i permessi completi dell’utente del sistema.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. Gli hook generano PowerShell direttamente, quindi questo funziona indipendentemente dal fatto che CLAUDE_CODE_USE_POWERSHELL_TOOL sia impostato. 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. A partire dalla v2.1.198, 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.
Prima della v2.1.198, questa riscrittura si applicava solo agli hook dei plugin. Nelle versioni precedenti, un hook settings.json necessita della forma $env: o della forma exec, dove ${CLAUDE_PROJECT_DIR} viene sostituito in ogni elemento args indipendentemente da dove l’hook è definito.
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:, che funziona su ogni versione:
Debug dei hook
I dettagli dell’esecuzione dei hook, inclusi quali hook corrispondono, i loro codici di uscita e l’output completo di stdout e stderr, 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.
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 cicli infiniti di Stop hook 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.