Hook di Cursor: la guida completa, e perché a volte non si attivano

Gli hook di Cursor permettono a uno script di osservare o bloccare ciò che fa l'agente: ogni comando shell, ogni chiamata MCP, ogni lettura e modifica di file. Sono potenti, e falliscono in silenzio. Questo è tutto ciò che abbiamo imparato configurandoli sul serio.

Di Aslan, team di Pet Island · Aggiornato il 26 ott 2026 · 7 min di lettura

RISPOSTA BREVE

Metti un hooks.json con "version": 1 in ~/.cursor/ (tutti i progetti) o in .cursor/ di un repository, associa ogni evento a un comando e stampa JSON su stdout. Usa beforeShellExecution e beforeMCPExecution per consentire, negare o chiedere, e imposta failClosed: true se un hook guasto deve bloccare invece di lasciar passare il comando. Quando un hook sembra ignorato, apri prima il canale di output Hooks: la maggior parte dei problemi dipende da un percorso, da un workspace non attendibile, da un JSON non valido o da un evento che la CLI di Cursor non invia ancora.

Cosa sono gli hook di Cursor

Un hook è un comando che Cursor esegue in un punto preciso del ciclo dell'agente. Cursor invia sullo standard input del comando un oggetto JSON che descrive ciò che sta per succedere, attende e legge una risposta JSON dal suo standard output. Alcuni hook si limitano a osservare (registrare la modifica, formattare il file). Altri decidono: un hook beforeShellExecution può lasciar eseguire un comando, fermarlo o chiedere prima a te.

Questo rende gli hook lo strumento giusto per tre compiti: protezioni (mai eseguire rm -rf o git push --force senza una persona), automazione (formattare o eseguire il lint dopo ogni modifica) e visibilità (registrare o inoltrare ciò che fa l'agente per sapere quando ha bisogno di te).

Dove si trova hooks.json

Cursor legge gli hook da quattro posizioni e li esegue tutti. Ogni hook corrispondente di ogni origine viene eseguito, e Cursor unisce le loro risposte.

LivelloFileNote
Utente~/.cursor/hooks.jsonSi applica a ogni progetto. I comandi relativi vengono risolti da ~/.cursor/.
Progetto<project>/.cursor/hooks.jsonVersionato nel repository. Viene eseguito solo in un workspace attendibile. I comandi relativi vengono risolti dalla radice del progetto.
TeamDashboard web di CursorSolo Enterprise, sincronizzato con tutti i membri.
EnterpriseC:\ProgramData\Cursor\hooks.json (Windows), /Library/Application Support/Cursor/hooks.json (macOS), /etc/cursor/hooks.json (Linux, WSL)Gestito dal reparto IT.

Quando le risposte non concordano, vince la più restrittiva: un deny da qualsiasi origine prevale su ask, e ask prevale su allow. Il file funzionante più piccolo ha questo aspetto (è l'esempio della documentazione di Cursor):

~/.cursor/hooks.jsonCopia
{
"version": 1,
"hooks": {
"afterFileEdit": [{ "command": "./hooks/format.sh" }]
}
}

version deve essere 1. Cursor osserva il file e lo ricarica quando salvi. Se una modifica non ha effetto, riavvia Cursor. Già questo risolve un numero sorprendente di segnalazioni nel forum.

Gli eventi che puoi intercettare

L'elenco attuale dalla documentazione di Cursor, raggruppato per scopo:

GruppoEventiPuò bloccare?
Shell e MCPbeforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecutionI due hook before…: consentire, negare o chiedere
Qualsiasi strumentopreToolUse, postToolUse, postToolUseFailurepreToolUse: consentire o negare (ask oggi non viene applicato)
FilebeforeReadFile, afterFileEditbeforeReadFile: consentire o negare
PromptbeforeSubmitPromptSì, con continue: false
Ciclo dell'agentestop, afterAgentResponse, afterAgentThought, preCompactNo (stop può inviare un messaggio di follow-up)
SubagentisubagentStart, subagentStopsubagentStart: consentire o negare (ask vale come deny)
Sessione e appsessionStart, sessionEnd, workspaceOpenNo
Completamenti TabbeforeTabFileRead, afterTabFileEditbeforeTabFileRead: consentire o negare

Ogni evento riceve alcuni campi comuni (conversation_id, generation_id, model, hook_event_name, cursor_version, workspace_roots) più i propri. beforeShellExecution riceve il command completo e la sua cwd; stop riceve uno status che vale completed, aborted o error.

Consentire, negare e chiedere

Un hook decisionale stampa un solo oggetto JSON. Per l'hook della shell ha questo aspetto:

stdout di un hook beforeShellExecutionCopia
{
"permission": "ask",
"user_message": "This pushes to main. Run it?",
"agent_message": "The user wants to confirm pushes."
}
  • permission è allow, deny o ask.
  • user_message è ciò che vedi in Cursor.
  • agent_message è ciò che viene detto all'agente, così può cambiare strada invece di riprovare lo stesso comando.

I codici di uscita contano quanto il JSON. L'uscita 0 significa “usa il mio JSON”. L'uscita 2 blocca l'azione, come deny. Qualsiasi altro codice di uscita viene trattato come un hook fallito, e l'azione prosegue, a meno che tu non imposti failClosed. Per gli hook di autorizzazione, un JSON non valido o non conforme allo schema blocca l'azione.

La documentazione di Cursor indica ask come supportato per beforeShellExecution e beforeMCPExecution. A marzo 2026 lo staff di Cursor ha scritto nel forum che gli hook applicavano solo deny e che allow e ask venivano “basically ignored” (praticamente ignorati), soprattutto con l'elenco dei comandi consentiti o la sandbox attivi. Non abbiamo trovato annunci di una correzione. Se una protezione deve reggere, restituisci deny, non ask, e provala nella tua versione.

failClosed e timeout

Ogni voce di hook può avere delle opzioni accanto a command:

.cursor/hooks.jsonCopia
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "./.cursor/hooks/guard.sh",
"timeout": 30,
"failClosed": true
}
]
}
}
  • failClosed: true trasforma un crash, un timeout, un'uscita diversa da zero o un output vuoto in un blocco. Il valore predefinito è false: un controllo guasto lascia passare tutto in silenzio. Per un hook di sicurezza, quasi sempre vuoi true.
  • timeout è in secondi. Lascia abbastanza margine se il tuo hook attende una persona: chi legge un diff ha bisogno di più di cinque secondi.
  • matcher filtra quando viene eseguito l'hook. Per gli hook della shell viene confrontato con la stringa del comando completa; per gli hook degli strumenti con il tipo di strumento, come Shell, Read, Write o MCP:<tool_name>.

C'è un compromesso. A luglio 2026 un utente su macOS si è imbattuto in “MainThreadShellExec not initialized”: l'host della shell di Cursor non era pronto e, poiché l'hook era fail-closed, la shell dell'agente veniva bloccata. Lo staff di Cursor ha detto che la correzione era stata rilasciata e ha consigliato di mantenere failClosed: true. Se vedi quell'errore su una build più vecchia, la soluzione temporanea era “Developer: Reload Window”.

Gli hook su Windows

Su Windows, l'app desktop di Cursor esegue i comandi degli hook tramite PowerShell e passa il JSON in pipe. Uno script PowerShell lo legge da $input; qualsiasi altro programma legge lo standard input come di consueto. Tre cose creano spesso problemi:

  1. Gli script bash non funzionano così come sono. Un guard.sh di un collega su macOS fallisce in PowerShell. Usa uno script .ps1, un programma compilato, oppure chiama esplicitamente l'interprete (python .cursor/hooks/guard.py). Una discussione nel forum di gennaio 2026 mostra una configurazione con Git Bash che fallisce con syntax error near unexpected token '[Convert]::FromBase64String', cioè codice PowerShell che arriva a bash.
  2. Criteri di esecuzione. Se PowerShell si rifiuta di eseguire script, avviali con powershell -NoProfile -ExecutionPolicy Bypass -File …, stampa il JSON e termina con exit 0.
  3. I programmi GUI restituiscono il controllo troppo presto. Questo ci è costato una giornata. Se il tuo hook è un programma Windows compilato per il sottosistema GUI, PowerShell lo avvia e prosegue senza attendere. Cursor legge un output vuoto, quindi l'hook sembra non aver fatto nulla (oppure, con failClosed, blocca tutto). Compila l'hook come programma console, oppure avvolgilo in modo che PowerShell ne attenda la chiusura.

Anche gli hook di progetto richiedono un workspace attendibile. Un membro dello staff di Cursor ha elencato cosa controllare quando non fanno nulla: la riga del log Hooks “Hook N produced no output”, il workspace indicato come attendibile in Settings → Hooks ed errori come spawn, ENOENT, EPERM o ExecutionPolicy nella console.

Gli hook nella CLI di Cursor

La CLI di Cursor (cursor-agent) legge gli stessi ~/.cursor/hooks.json e .cursor/hooks.json, ma non ha mai inviato tutti gli eventi che invia l'editor. A gennaio 2026 si attivavano solo i due hook della shell. Ad aprile lo staff di Cursor ha confermato che beforeShellExecution, afterShellExecution, afterFileEdit, postToolUse, stop e sessionStart funzionavano nella CLI, mentre afterAgentResponse e afterAgentThought ancora no (confermato di nuovo a maggio). Per qualsiasi evento non presente in questo elenco, fai una prova prima di farci affidamento.

Gli agenti cloud sono ancora più restrittivi: gli hook a livello utente di ~/.cursor/hooks.json lì non vengono eseguiti affatto.

Quando un hook non si attiva: una checklist

  1. Apri il canale di output Hooks (View → Output → Hooks) o la scheda Hooks in Customize. Mostra quali hook sono stati caricati, ogni esecuzione e gli errori.
  2. Controlla il file JSON. "version": 1 presente, nomi degli eventi scritti esattamente (distinguono tra maiuscole e minuscole), JSON valido.
  3. Controlla il percorso. I comandi relativi vengono risolti dalla radice del progetto per gli hook di progetto e da ~/.cursor/ per gli hook utente.
  4. Considera attendibile il workspace. Gli hook di progetto non vengono eseguiti in una cartella non attendibile.
  5. Esegui l'hook a mano. Passagli in pipe un JSON di esempio e guarda stdout. Qualsiasi cosa diversa da un unico oggetto JSON (un echo di troppo, un avviso) rompe il parsing.
  6. Riavvia Cursor. Il salvataggio ricarica il file, ma diverse segnalazioni nel forum sono state risolte solo con un riavvio completo.
  7. Conosci le lacune. ask e allow sono stati segnalati come ignorati insieme all'elenco dei comandi consentiti e alla sandbox; subagentStart/subagentStop sono stati segnalati come non attivati in alcune versioni; la CLI salta alcuni eventi. I commit dal pannello Source Control integrato non vengono intercettati: non esiste un evento di hook pre-commit.

Hook, auto-run e Auto-review

Gli hook sono separati dalle impostazioni di approvazione di Cursor (Settings → Agents → Approvals & Execution). Auto-run e Auto-review decidono se Cursor ti chiede conferma nel suo pannello; il tuo hook vi sovrappone la propria decisione. Ecco perché “il mio hook ha detto ask, ma il comando è stato eseguito” è una segnalazione così comune: un altro percorso di autorizzazione lo aveva già consentito.

Un'altra cosa che abbiamo imparato a nostre spese: con Auto-review, Cursor può chiedere di alcuni comandi nella sua finestra. Un hook non vede mai quella domanda. Avviene dentro Cursor, non nella pipeline degli hook.

Dove si inserisce Pet Island

Se dagli hook vuoi “mostrami ogni comando rischioso e fammi decidere senza cambiare finestra”, è quello che fa Pet Island con Cursor. Installa gli hook per te (beforeShellExecution e beforeMCPExecution, con failClosed attivo così un helper fermo non lascia mai passare un comando) e mette ogni richiesta su una piccola isola nella parte alta dello schermo, con il comando completo. Lo consenti o lo neghi lì; l'agente di Cursor attende.

I comandi e le modifiche ai file dall'editor di Cursor funzionano dall'isola. Domande e piani compaiono sull'isola, e rispondi da Cursor. Nella CLI di Cursor i comandi funzionano dall'isola; gli altri eventi dipendono da ciò che invia la CLI. La disinstallazione rimuove solo le voci aggiunte da Pet Island; i tuoi hook restano.

FAQ

Gli hook di Cursor funzionano su Windows?

Sì. L'app desktop li esegue tramite PowerShell e passa il JSON sullo standard input ($input in uno script PowerShell). Gli script bash scritti per macOS richiedono una versione PowerShell o Python, e i programmi del sottosistema GUI vanno avvolti in modo che PowerShell li attenda.

Perché il mio hook restituisce ask ma il comando viene eseguito comunque?

A marzo 2026 lo staff di Cursor ha detto che gli hook applicavano in modo affidabile solo deny; allow e ask potevano essere scavalcati dall'elenco dei comandi consentiti o dalla sandbox. Usa deny per tutto ciò che non deve essere eseguito, e controlla il canale di output Hooks per vedere cosa ha restituito davvero il tuo hook.

Cosa fa failClosed?

Con failClosed impostato su true, un crash, un timeout, un'uscita diversa da zero o un output vuoto bloccano l'azione. Senza, un hook guasto lascia proseguire l'azione.

Dove vedo gli errori degli hook?

Nel canale di output Hooks (View → Output → Hooks) e nella scheda Hooks in Customize.

Gli hook funzionano nella CLI di Cursor?

In parte. La CLI legge gli stessi file hooks.json. A maggio 2026 lo staff di Cursor ha confermato gli hook della shell, afterFileEdit, postToolUse, stop e sessionStart, ma non afterAgentResponse né afterAgentThought.

Un hook può fermare un commit git dal pannello Source Control?

No. Cursor non ha un evento di hook pre-commit, e i commit fatti dall'interfaccia Source Control integrata non vengono intercettati. Per questo usa un normale hook git pre-commit.

Fonti

Ogni impostazione e comportamento descritti in questa pagina sono stati verificati su queste pagine il 26 ottobre 2026. Gli agenti cambiano in fretta: se qualcosa non corrisponde più, fa fede la pagina ufficiale.

CONTINUA A LEGGERE

Rispondi a tutti i tuoi agenti dalla parte alta dello schermo.

Windows e Mac · 7 giorni gratis, poi 8,99 $ una tantum · 1 $ di ogni vendita sfama gli animali randagi

Prova Pet Island gratis