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.
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.
| Livello | File | Note |
|---|---|---|
| Utente | ~/.cursor/hooks.json | Si applica a ogni progetto. I comandi relativi vengono risolti da ~/.cursor/. |
| Progetto | <project>/.cursor/hooks.json | Versionato nel repository. Viene eseguito solo in un workspace attendibile. I comandi relativi vengono risolti dalla radice del progetto. |
| Team | Dashboard web di Cursor | Solo Enterprise, sincronizzato con tutti i membri. |
| Enterprise | C:\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):
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:
| Gruppo | Eventi | Può bloccare? |
|---|---|---|
| Shell e MCP | beforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecution | I due hook before…: consentire, negare o chiedere |
| Qualsiasi strumento | preToolUse, postToolUse, postToolUseFailure | preToolUse: consentire o negare (ask oggi non viene applicato) |
| File | beforeReadFile, afterFileEdit | beforeReadFile: consentire o negare |
| Prompt | beforeSubmitPrompt | Sì, con continue: false |
| Ciclo dell'agente | stop, afterAgentResponse, afterAgentThought, preCompact | No (stop può inviare un messaggio di follow-up) |
| Subagenti | subagentStart, subagentStop | subagentStart: consentire o negare (ask vale come deny) |
| Sessione e app | sessionStart, sessionEnd, workspaceOpen | No |
| Completamenti Tab | beforeTabFileRead, afterTabFileEdit | beforeTabFileRead: 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:
permissionèallow,denyoask.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:
failClosed: truetrasforma 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 vuoitrue.timeoutè in secondi. Lascia abbastanza margine se il tuo hook attende una persona: chi legge un diff ha bisogno di più di cinque secondi.matcherfiltra 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, comeShell,Read,WriteoMCP:<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:
- Gli script bash non funzionano così come sono. Un
guard.shdi 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 consyntax error near unexpected token '[Convert]::FromBase64String', cioè codice PowerShell che arriva a bash. - Criteri di esecuzione. Se PowerShell si rifiuta di eseguire script, avviali con
powershell -NoProfile -ExecutionPolicy Bypass -File …, stampa il JSON e termina conexit 0. - 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
- 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.
- Controlla il file JSON.
"version": 1presente, nomi degli eventi scritti esattamente (distinguono tra maiuscole e minuscole), JSON valido. - Controlla il percorso. I comandi relativi vengono risolti dalla radice del progetto per gli hook di progetto e da
~/.cursor/per gli hook utente. - Considera attendibile il workspace. Gli hook di progetto non vengono eseguiti in una cartella non attendibile.
- Esegui l'hook a mano. Passagli in pipe un JSON di esempio e guarda stdout. Qualsiasi cosa diversa da un unico oggetto JSON (un
echodi troppo, un avviso) rompe il parsing. - Riavvia Cursor. Il salvataggio ricarica il file, ma diverse segnalazioni nel forum sono state risolte solo con un riavvio completo.
- Conosci le lacune.
askeallowsono stati segnalati come ignorati insieme all'elenco dei comandi consentiti e alla sandbox;subagentStart/subagentStopsono 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.
- Documentazione di Cursor: Hooks
- Forum di Cursor: gli hook di Cursor non vengono eseguiti come previsto (staff: viene applicato solo deny)
- Forum di Cursor: allow/ask di beforeShellExecution ignorati, l'elenco dei comandi consentiti ha la precedenza
- Forum di Cursor: hook fail-closed e MainThreadShellExec not initialized
- Forum di Cursor: un JSON malformato restituito da un hook consentiva il comando in silenzio (corretto)
- Forum di Cursor: la CLI di Cursor non invia tutti gli eventi degli hook
- Forum di Cursor: gli hook di progetto non vengono eseguiti su Windows
- Forum di Cursor: gli hook di progetto falliscono su Windows con Git Bash
- Forum di Cursor: subagentStart e subagentStop non si attivano mai