diffox
Blog

Cos’è un hook e come si scrive negli agenti di programmazione?

L’agente non rispetta sempre ciò che scrivi nel file delle regole; un hook invece gira ogni volta. Ecco come si scrive in cinque strumenti, con esempi.

CondividiXLinkedIn

In sintesi. Un hook è un comando scritto da te che l’agente di programmazione esegue da solo in momenti precisi. Che venga eseguito o no non dipende da una decisione dell’agente, quindi si usa per le regole che vanno rispettate sempre. C’è in Claude Code, Codex, Cursor, Gemini CLI e GitHub Copilot. Il 18 ottobre 2026 abbiamo verificato ogni informazione nella documentazione dello strumento stesso; gli esempi vengono dal repository che usiamo ogni giorno. Il 28 ottobre 2026 abbiamo aggiunto Antigravity CLI di Google.

  • Il file delle regole da solo non basta. Una regola scritta in AGENTS.md o CLAUDE.md per l’agente è un’informazione, non la garanzia che la rispetterà. In tutti e cinque gli strumenti c’è un hook che interviene prima che l’agente esegua un comando.
  • Il modo di bloccare è lo stesso, la gestione degli errori no. Se l’hook esce con il codice 2, tutti e cinque gli strumenti bloccano l’azione. Con un altro codice di errore, come 1, Claude Code, Cursor e Gemini CLI vanno avanti, Copilot invece blocca. La documentazione di Codex non lo dice.
  • Un hook che si pianta spegne anche la regola. Se l’hook va in timeout, Claude Code, Cursor e Copilot vanno avanti. Puoi cambiare questo comportamento con onFailure in Claude Code e con failClosed in Cursor. Quella di Codex e di Gemini CLI non ne parla.
  • Nel nostro repository usiamo tre hook. Uno, all’apertura della sessione, dice in che stato è la directory; uno blocca il merge fatto senza approvazione; uno avvisa l’agente quando il branch cambia.

Perché il file delle regole non basta?

5strumenti verificati nella loro documentazione
2il codice di uscita che blocca l’azione in tutti e cinque
33eventi a cui si può agganciare un hook (Claude Code)
3hook che usiamo nel nostro repository

Una regola puoi darla a un agente di programmazione in due modi. Il primo è il file delle regole: in AGENTS.md, CLAUDE.md o GEMINI.md scrivi «chiedimi prima di fare il merge». Il secondo è un hook: trasformi la stessa regola in un piccolo script che l’agente esegue da solo in un momento preciso.

La differenza la spiega nel modo più chiaro la documentazione di Claude Code: «Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead.» Cioè, per l’agente il file delle regole è solo un’informazione, e l’agente non sempre ne tiene conto. Se vuoi bloccare un’azione qualunque cosa decida l’agente, usi un hook.

Al 18 ottobre 2026 gli hook ci sono in tutti e cinque gli strumenti di cui parliamo: Claude Code, Codex di OpenAI, Cursor, Gemini CLI di Google e GitHub Copilot (la CLI e l’agente cloud). In tutti la logica è la stessa: l’hook si aggancia a un evento, riceve i dettagli dell’evento in JSON e, se serve, blocca un comando prima che venga eseguito.

Gli esempi vengono dal repository in cui sviluppiamo diffox e che usiamo ogni giorno. Lì usiamo Claude Code; ciò che raccontiamo non è una funzione del prodotto, ma il modo in cui lavora il team.

Dove si definiscono gli hook nei cinque strumenti?

In ogni strumento gli hook si scrivono in un file di impostazioni; la differenza sta soprattutto nel nome del file e dell’evento. Per imporre una regola si usa più spesso l’evento che scatta subito prima che l’agente usi un tool (per esempio esegua un comando o modifichi un file):

Gli hook in cinque agenti di programmazione: il file di impostazioni, l’evento che scatta prima dell’uso di un tool e il modo di bloccare l’azione
StrumentoFile di impostazioni (progetto · utente)Prima dell’uso di un toolPer bloccare
Claude Code.claude/settings.json · ~/.claude/settings.jsonPreToolUseexit 2 oppure permissionDecision: "deny"
Codex.codex/hooks.json · ~/.codex/hooks.jsonPreToolUseexit 2 oppure permissionDecision: "deny"
Cursor.cursor/hooks.json · ~/.cursor/hooks.jsonpreToolUse; per i comandi di shell beforeShellExecutionexit 2 oppure permission: "deny"
Gemini CLI.gemini/settings.json · ~/.gemini/settings.jsonBeforeToolexit 2 oppure decision: "deny"
GitHub Copilot.github/hooks/*.json · ~/.copilot/hooks/*.jsonpreToolUseexit 2 oppure permissionDecision: "deny"

Fonte: la documentazione sugli hook di ogni strumento, 18 ottobre 2026. La decisione in JSON si dà con l’output che l’hook scrive a schermo (stdout). Codex legge la stessa configurazione anche in config.toml. In Copilot il file utente vale solo per la CLI; l’agente cloud legge il file del repository dal branch predefinito.

Quando l’evento scatta, il tuo hook riceve i dettagli in JSON: quale tool è stato chiamato e con quale input. In Claude Code l’input che arriva prima di un comando Bash è fatto così:

ESEMPIO. L’hook riceve un oggetto JSON. Claude Code · stdin · PreToolUse: { /   "session_id": "abc123", /   "cwd": "/home/dev/progetto", /   "hook_event_name": "PreToolUse", /   "tool_name": "Bash", /   "tool_input": { /     "command": "gh pr merge 42 --squash" /   } / }. tool_name: Quale tool: il matcher guarda questo nome. tool_input.command: Il comando da eseguire. Codice di uscita: La decisione dello script: 2 blocca.
Un esempio abbreviato del JSON che riceve in Claude Code un hook PreToolUse che scatta prima del tool Bash. I nomi dei campi vengono dalla documentazione di Claude Code sugli hook, 18 ottobre 2026; negli altri strumenti i campi hanno nomi diversi.

Gli strumenti sanno leggere anche i file degli altri: Copilot legge anche il file .claude/settings.json del repository, Cursor può caricare gli hook di Claude Code e Gemini CLI definisce la variabile CLAUDE_PROJECT_DIR perché gli script scritti per Claude Code continuino a funzionare. Quindi un hook si può scrivere una volta e usare in più strumenti. Ma i nomi degli eventi e i campi di output sono diversi: prova l’hook in ciascuno strumento.

Il 18 giugno 2026 Google ha smesso di servire Gemini CLI agli utenti gratuiti e agli abbonati Google AI Pro e Ultra, indirizzandoli ad Antigravity CLI; con una chiave API a pagamento o una licenza aziendale puoi continuare a usare Gemini CLI. Secondo l’annuncio di Google, anche Antigravity CLI ha gli hook; abbiamo letto la sua documentazione il 28 ottobre 2026. Gli hook si scrivono in .agents/hooks.json per un progetto e in ~/.gemini/config/hooks.json per l’utente. L’evento che scatta prima che si usi un tool si chiama PreToolUse, come in Claude Code. Per bloccare l’azione, l’hook restituisce decision: "deny" nel JSON. Il timeout predefinito è di 30 secondi; la documentazione non dice che cosa fanno i codici di uscita.

Come fa un hook a bloccare un’azione?

L’hook comunica la sua decisione in due modi: con il codice di uscita o con il JSON che scrive a schermo. In tutti e cinque gli strumenti, se l’hook esce con il codice 2 l’azione si ferma, e la spiegazione che l’hook ha scritto sull’output di errore (stderr) arriva all’agente. Gli strumenti si comportano invece in modo diverso quando l’hook esce con un altro codice di errore o va in timeout:

Che cosa succede se l’hook che scatta prima dell’uso di un tool esce con un codice di errore diverso da 2 o va in timeout
StrumentoCodice di errore diverso da 2TimeoutImpostazione che blocca l’azione
Claude CodeL’azione prosegueL’azione prosegue"onFailure": "block"
CodexLa documentazione non lo diceLa documentazione non lo dice—
CursorL’azione prosegueL’azione prosegue"failClosed": true
Gemini CLIMostra un avviso, l’azione prosegueLa documentazione non lo dice—
GitHub CopilotL’azione si fermaL’azione prosegue—

Fonte: la documentazione sugli hook di ogni strumento, 18 ottobre 2026. «La documentazione non lo dice»: nelle pagine che abbiamo letto quel giorno non abbiamo trovato una frase che descriva questo caso. In Claude Code l’impostazione esiste dalla versione 2.1.295. Su Windows senza Git Bash, un hook di comando PreToolUse in forma shell che fallisce può bloccare l’azione anche senza onFailure.

Parti dalla prima colonna. In Unix, per un errore si usa di solito 1. Claude Code, Cursor e Gemini CLI lo trattano come un errore che non blocca l’azione; Copilot invece, con lo stesso codice, la blocca. Per questo la documentazione di Claude Code ti dice di usare exit 2 negli hook che impongono una regola. Gemini CLI, al contrario, consiglia di uscire con 0 e scrivere JSON anche quando si blocca un’azione di proposito; se l’hook scrive a schermo qualcosa che non è JSON, l’azione viene permessa.

Il problema della seconda colonna è più difficile da notare: se il percorso dell’hook è sbagliato o lo script si pianta, è come se la regola non ci fosse. Dove l’azione deve fermarsi per forza, attiva onFailure in Claude Code e failClosed in Cursor.

L’esempio qui sotto è una versione semplificata, per Claude Code, del controllo sul merge del nostro repository. Prima il file di impostazioni:

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/merge-gate.sh"
          }
        ]
      }
    ]
  }
}

Poi lo script:

.claude/hooks/merge-gate.sh
#!/bin/bash
# L'input arriva in JSON da stdin. Se non si legge, l'azione si ferma.
COMMAND=$(jq -r '.tool_input.command // empty') || {
  echo "Hook: input non leggibile, azione bloccata." >&2
  exit 2
}

case "$COMMAND" in
  *"MERGE_APPROVED=1 gh pr merge"*) exit 0 ;;  # approvazione scritta nel comando
  *"gh pr merge"*)
    echo "Il merge lo decide il proprietario del progetto. Chiedi prima." >&2
    exit 2 ;;                                 # l'azione si ferma
esac
exit 0                                        # nessuna obiezione

Quando lo script esce con 2, Claude Code non esegue il comando e passa all’agente la frase che hai scritto sull’output di errore. La stessa decisione puoi darla anche uscendo con 0 e scrivendo JSON: nel campo permissionDecision va deny (rifiuta), ask (chiedi) o allow (consenti). Gli hook PreToolUse girano prima delle impostazioni dei permessi; una decisione deny vale anche in modalità bypassPermissions, quella che salta tutte le richieste di permesso. Codex non supporta ancora il valore ask: l’hook viene considerato fallito e l’azione prosegue.

Quali hook usiamo nel nostro repository?

Nel file .claude/settings.json del nostro repository ci sono 3 hook. Li abbiamo scritti tutti e tre perché non si ripetesse un errore che ci era già capitato.

I tre hook del nostro repository. Uno informa, uno blocca, uno avvisa. Il quarto lo esegue git. SessionStart Stato della directory: Dice il branch, i file non committati e i worktree. PreToolUse · Bash Controllo sul merge: Senza approvazione blocca il comando di merge. PreToolUse · Edit|Write Avviso di cambio branch: Avvisa l’agente se branch o commit sono cambiati. git · pre-push Hook di git: Scatta prima del push; lo esegue git, non l’agente.
I tre hook di Claude Code del nostro repository e un hook di git. Il terzo scatta prima dei tool Edit|Write|NotebookEdit. Fonte: il file di impostazioni del repository e gli script degli hook.

Apertura della sessione. Quando una sessione parte, l’hook SessionStart dice su quale branch si è, quanti file non sono stati committati e quali worktree sono aperti, poi dà un verdetto: «puoi lavorare in questa directory» oppure «prima apri un worktree a parte». La regola era già scritta in CLAUDE.md. Ma una sessione l’ha letta, non l’ha applicata alla propria situazione e ha pianificato modifiche in una directory che in quaranta minuti ha cambiato branch tre volte. Una regola che dipende dalla memoria dell’agente non protegge; l’hook fa questa domanda al posto dell’agente.

Controllo sul merge. Nel nostro repository non facciamo il merge di una pull request senza l’approvazione del proprietario del progetto. Ma un agente, quando i test passano, può provare a eseguire gh pr merge come passo successivo. L’hook PreToolUse che scatta prima del tool Bash blocca questo comando con il codice 2 e dice all’agente di chiedere prima l’approvazione. Quando l’approvazione arriva, il comando viene rieseguito con una variabile d’ambiente davanti; così, nel log della sessione, l’approvazione sta proprio accanto al merge. L’hook blocca l’azione anche quando non riesce a leggere l’input: la prima versione, davanti a un comando che non riusciva a leggere, usciva con 0 e lo lasciava passare.

Avviso di cambio branch. Il verdetto dato all’apertura descrive la situazione di quel momento e col tempo può invecchiare: se di due sessioni aperte sullo stesso branch una passa a un branch nuovo, cambia anche il punto in cui lavora l’altra. Il secondo hook PreToolUse scatta prima dei tool che modificano i file e, prima di ogni modifica, confronta branch e commit con quello che era stato detto l’ultima volta alla sessione. Se c’è una differenza, manda all’agente una nota che comincia con questa riga: «drift: this checkout MOVED since the session was last told what it is». Non blocca l’azione; passa la nota con il campo additionalContext ed esce con 0.

Un hook di git è un’altra cosa. Nello stesso repository c’è anche un hook pre-push: impedisce il push diretto sui branch protetti. Non lo esegue l’agente di programmazione ma git; per questo vale per chiunque faccia push da quel clone, agenti compresi, qualunque strumento usino. Tutti e due girano solo sulla tua macchina: con --no-verify il pre-push si può saltare, quindi non sostituisce i controlli lato server.

Che differenza c’è tra hook, skill e file delle regole?

Tutti e tre decidono come si comporta l’agente, ma entrano in gioco in momenti diversi. Ecco il confronto nella documentazione di Claude Code:

CLAUDE.md, skill e hook: quando entrano in gioco, se si applicano ogni volta, quanto spazio occupano nel contesto e a che cosa servono
CLAUDE.mdSkillHook
Quando entra in giocoAll’inizio di ogni sessioneQuando scrivi /nome o quando Claude la ritiene adatta al compitoOgni volta che si verifica l’evento a cui è agganciato
Si applica ogni volta?No, per l’agente è un’informazioneNo, Claude la interpretaSì, gira ogni volta che c’è l’evento
Spazio nel contestoTutto, a ogni richiestaLa descrizione a ogni sessione, tutta quando si usaNessuno, finché non produce output
A che cosa serveRegole del tipo «fai sempre questo», comandiFlussi di lavoro riutilizzabili, testi di riferimentoFormattare, bloccare un comando pericoloso, notifiche

Fonte: documentazione di Claude Code, pagina «Extend Claude Code», 18 ottobre 2026.

In breve: se deve essere l’agente a decidere come applicare i passi, usa una skill; se l’operazione va fatta ogni volta allo stesso modo, usa un hook. La checklist per pubblicare una release è una skill; formattare ogni file salvato è un hook. Come dice la stessa pagina, un’istruzione nel file delle regole o in una skill è una richiesta, non una garanzia. Le regole che, una volta violate, non si possono più rimediare (un push sul branch sbagliato, un merge fatto troppo presto) non affidarle solo a un’istruzione.

Gli hook non si definiscono solo nel file di impostazioni: anche skill e subagenti possono averne nella propria definizione. In Claude Code l’hook di un subagente vale solo mentre quel subagente è in esecuzione; l’hook di una skill, invece, dopo che la skill è stata chiamata una volta, continua a girare fino alla fine della sessione. In Copilot l’hook di un agente personalizzato gira solo mentre quell’agente è selezionato.

Non trasformare ogni regola in un hook

Un hook gira ogni volta; e un hook scritto male sbaglia ogni volta. Prima di scriverne uno, controlla questi punti:

  • Le informazioni che non cambiano tienile nel file delle regole. La documentazione di Claude Code consiglia CLAUDE.md per le informazioni fisse. Un hook è più adatto a passare lo stato del momento (su quale branch sei, com’è andata l’ultima CI).
  • Non lasciare a un hook le decisioni che richiedono un giudizio. A una domanda come «questa modifica ha bisogno di test?» un semplice controllo sul testo non sa rispondere; un passo del genere si scrive in una skill.
  • Non bloccare senza motivo. Due dei nostri tre hook danno solo informazioni e non bloccano l’azione. Un hook che ferma il lavoro a torto viene presto disattivato, invece che corretto.
  • Un hook che cerca un testo non è una misura di sicurezza. Il nostro controllo sul merge non intercetta un comando di merge scritto dentro una variabile o dentro eval. Ferma l’agente che si dimentica di chiedere, non chi cerca apposta di aggirare la regola. Anche la documentazione di Codex descrive gli hook come «a useful guardrail, not a complete enforcement boundary». Per permessi e divieti rigidi, la documentazione di Claude Code consiglia le regole dei permessi.
  • Esamina un hook scritto da altri come faresti con il codice. Gli hook di tipo comando girano con i permessi del tuo utente. Codex non esegue un hook arrivato dal repository finché non lo hai esaminato e approvato; Cursor esegue gli hook di progetto solo nei workspace di cui ti fidi; e Gemini CLI tratta un hook modificato come un hook nuovo e non attendibile.

Gli hook girano sulla tua macchina, prima che l’azione avvenga; non guardano se il codice scritto è corretto. Quello è il lavoro della code review: quando diffox esamina una pull request, legge anche le regole da rispettare nel file delle regole del repository (AGENTS.md, o CLAUDE.md se manca) e le usa come regole della review.

Domande frequenti

Che cos’è un hook e quali strumenti di programmazione lo supportano?

Un hook è un comando scritto da te che l’agente di programmazione esegue da solo in momenti precisi. Al 18 ottobre 2026 c’è in Claude Code, Codex, Cursor, Gemini CLI e GitHub Copilot. Nel file di impostazioni scrivi quale comando deve girare e su quale evento. Siccome l’esecuzione non dipende da una decisione dell’agente, la regola si applica ogni volta.

Come si blocca un comando con un hook in Claude Code?

Se un hook agganciato all’evento PreToolUse esce con il codice 2, Claude Code non esegue il comando e passa all’agente la spiegazione che l’hook ha scritto sull’output di errore. Uscire con 0 vuol dire «nessuna obiezione»; un altro codice, come 1, conta come un errore che non blocca l’azione. L’hook può anche uscire con 0 e scrivere JSON: il campo permissionDecision vale deny, ask o allow.

Come si usano gli hook in Cursor?

Gli hook si scrivono nel file .cursor/hooks.json per il progetto e in ~/.cursor/hooks.json per l’utente. Prima che l’agente usi un tool scatta preToolUse, prima che esegua un comando di shell scatta beforeShellExecution. Se l’hook esce con 2 o restituisce permission: "deny" nel JSON, l’azione si ferma. Con un altro codice di errore l’azione prosegue; puoi cambiarlo con failClosed: true. Gli hook di progetto girano solo nei workspace di cui ti fidi.

Codex e Gemini CLI hanno gli hook?

Sì. In Codex gli hook si scrivono in .codex/hooks.json o in config.toml e sono attivi per impostazione predefinita; un hook arrivato dal repository Codex non lo esegue finché non lo hai esaminato e approvato. In Gemini CLI gli hook stanno in settings.json, e l’evento che scatta prima dell’uso di un tool si chiama BeforeTool. In tutti e due, se l’hook esce con il codice 2, l’azione si ferma.

Come si definisce un hook in GitHub Copilot?

Metti un file JSON nella cartella .github/hooks/ del repository oppure, per uso personale in Copilot CLI, nella cartella ~/.copilot/hooks/. Il file contiene version: 1 e un oggetto hooks. Prima dell’uso di un tool scatta preToolUse; se l’hook scrive a schermo permissionDecision: "deny" o esce con 2, l’azione si ferma. L’agente cloud legge il file degli hook dal branch predefinito del repository.

Che cosa succede se un hook va in crash o in timeout?

Nella maggior parte degli strumenti l’azione prosegue. In Claude Code un hook che dà errore o va in timeout non blocca l’azione; anche in Cursor un hook fallito la lascia passare; in Copilot il timeout lascia passare l’azione in ogni caso. Per cambiarlo ci sono "onFailure": "block" in Claude Code e "failClosed": true in Cursor. In Copilot, invece, se un hook preToolUse esce con un codice di errore diverso da 2, l’azione si ferma.

Che differenza c’è tra hook e skill?

Un hook è un comando che lo strumento esegue quando si verifica un certo evento, e gira ogni volta. Una skill è invece un file di istruzioni che l’agente legge e applica (in Claude Code SKILL.md): si carica quando la chiami per nome o quando l’agente la ritiene adatta al compito, e che cosa fare lo decide l’agente. Se l’operazione va fatta ogni volta allo stesso modo usa un hook; se l’agente deve adattare i passi alla situazione, usa una skill.