Hooks do Cursor: guia completo e por que às vezes eles não disparam
Os hooks do Cursor permitem que um script observe ou bloqueie o que o agente faz: cada comando de shell, chamada MCP, leitura e edição de arquivo. Eles são poderosos e falham de forma silenciosa. Aqui está tudo o que aprendemos ao configurá-los de verdade.
RESPOSTA CURTA
Coloque um hooks.json com "version": 1 em ~/.cursor/ (todos os projetos) ou em .cursor/ dentro de um repositório, aponte cada evento para um comando e imprima JSON no stdout. Use beforeShellExecution e beforeMCPExecution para permitir, negar ou perguntar, e defina failClosed: true se um hook quebrado precisar bloquear em vez de deixar o comando passar. Quando um hook parecer ignorado, abra primeiro o canal de saída Hooks: a maioria dos problemas é um caminho, um workspace que não é confiável, JSON inválido ou um evento que a Cursor CLI ainda não envia.
O que são os hooks do Cursor
Um hook é um comando que o Cursor executa em um ponto fixo do loop do agente. O Cursor envia pela entrada padrão do comando um objeto JSON descrevendo o que está prestes a acontecer, espera e lê uma resposta JSON da saída padrão. Alguns hooks só observam (registram a edição, formatam o arquivo). Outros decidem: um hook beforeShellExecution pode deixar um comando rodar, impedi-lo ou perguntar a você antes.
Isso faz dos hooks a ferramenta certa para três tarefas: proteções (nunca executar rm -rf ou git push --force sem um humano), automação (formatar ou rodar o lint depois de cada edição) e visibilidade (registrar ou encaminhar o que o agente faz para você saber quando ele precisa de você).
Onde fica o hooks.json
O Cursor lê hooks de quatro lugares e executa todos eles. Todos os hooks correspondentes de todas as fontes rodam, e o Cursor combina as respostas.
| Nível | Arquivo | Observações |
|---|---|---|
| Usuário | ~/.cursor/hooks.json | Vale para todos os projetos. Comandos relativos são resolvidos a partir de ~/.cursor/. |
| Projeto | <project>/.cursor/hooks.json | Versionado no repositório. Só roda em um workspace confiável. Comandos relativos são resolvidos a partir da raiz do projeto. |
| Equipe | Painel web do Cursor | Apenas Enterprise, sincronizado com todos os membros. |
| Enterprise | C:\ProgramData\Cursor\hooks.json (Windows), /Library/Application Support/Cursor/hooks.json (macOS), /etc/cursor/hooks.json (Linux, WSL) | Gerenciado pela TI. |
Quando as respostas divergem, vence a mais restritiva: um deny de qualquer fonte vence ask, e ask vence allow. O menor arquivo funcional é assim (é o exemplo da documentação do Cursor):
version precisa ser 1. O Cursor monitora o arquivo e o recarrega quando você salva. Se uma alteração não fizer efeito, reinicie o Cursor. Só isso resolve um número surpreendente de relatos no fórum.
Os eventos que você pode interceptar
A lista atual da documentação do Cursor, agrupada pela finalidade:
| Grupo | Eventos | Pode bloquear? |
|---|---|---|
| Shell e MCP | beforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecution | Os dois hooks before…: permitir, negar ou perguntar |
| Qualquer ferramenta | preToolUse, postToolUse, postToolUseFailure | preToolUse: permitir ou negar (ask não é aplicado hoje) |
| Arquivos | beforeReadFile, afterFileEdit | beforeReadFile: permitir ou negar |
| Prompt | beforeSubmitPrompt | Sim, com continue: false |
| Loop do agente | stop, afterAgentResponse, afterAgentThought, preCompact | Não (stop pode enviar uma mensagem de acompanhamento) |
| Subagentes | subagentStart, subagentStop | subagentStart: permitir ou negar (ask conta como deny) |
| Sessão e app | sessionStart, sessionEnd, workspaceOpen | Não |
| Sugestões do Tab | beforeTabFileRead, afterTabFileEdit | beforeTabFileRead: permitir ou negar |
Todo evento recebe alguns campos comuns (conversation_id, generation_id, model, hook_event_name, cursor_version, workspace_roots) além dos seus próprios. beforeShellExecution recebe o command completo e seu cwd; stop recebe um status com o valor completed, aborted ou error.
Permitir, negar e perguntar
Um hook que decide imprime um objeto JSON. Para o hook de shell, fica assim:
permissionéallow,denyouask.user_messageé o que você vê no Cursor.agent_messageé o que o agente recebe, para que ele mude de rumo em vez de tentar o mesmo comando de novo.
Os códigos de saída importam tanto quanto o JSON. Sair com 0 significa “use meu JSON”. Sair com 2 bloqueia a ação, assim como deny. Qualquer outro código de saída é tratado como hook com falha, e a ação segue em frente, a menos que você defina failClosed. Em hooks de permissão, JSON inválido ou fora do esquema bloqueia a ação.
A documentação do Cursor indica ask como suportado em beforeShellExecution e beforeMCPExecution. Em março de 2026, a equipe do Cursor escreveu no fórum que os hooks só aplicavam deny e que allow e ask eram “basically ignored” (basicamente ignorados), principalmente com a lista de permissões de comandos ou o sandbox ativados. Não encontramos nenhum anúncio de correção. Se uma proteção precisa funcionar, retorne deny, não ask, e teste na sua própria versão.
failClosed e timeout
Cada entrada de hook pode ter opções ao lado de command:
failClosed: truetransforma uma falha, um timeout, uma saída diferente de zero ou uma saída vazia em bloqueio. O padrão éfalse: uma proteção quebrada deixa tudo passar em silêncio. Para um hook de segurança, você quase sempre quertrue.timeouté em segundos. Deixe folga suficiente se o seu hook espera por um humano: uma pessoa lendo um diff precisa de mais de cinco segundos.matcherfiltra quando o hook roda. Em hooks de shell, ele é comparado com a string completa do comando; em hooks de ferramenta, com o tipo da ferramenta, comoShell,Read,WriteouMCP:<tool_name>.
Há uma contrapartida. Em julho de 2026, um usuário de macOS deu de cara com “MainThreadShellExec not initialized”: o host de shell do próprio Cursor não estava pronto e, como o hook era fail-closed, o shell do agente ficou bloqueado. A equipe do Cursor disse que a correção já tinha sido lançada e recomendou manter failClosed: true. Se você vir esse erro em uma build mais antiga, a solução paliativa era “Developer: Reload Window”.
Hooks no Windows
No Windows, o app desktop do Cursor executa os comandos de hook pelo PowerShell e passa o JSON por pipe. Um script PowerShell o lê de $input; qualquer outro programa lê a entrada padrão normalmente. Três coisas costumam dar problema:
- Scripts bash não rodam como estão. Um
guard.shde um colega de macOS falha no PowerShell. Use um script.ps1, um programa compilado ou chame o interpretador explicitamente (python .cursor/hooks/guard.py). Um tópico do fórum de janeiro de 2026 mostra uma configuração com Git Bash falhando comsyntax error near unexpected token '[Convert]::FromBase64String', ou seja, código PowerShell chegando ao bash. - Política de execução. Se o PowerShell se recusar a executar scripts, inicie-os com
powershell -NoProfile -ExecutionPolicy Bypass -File …, imprima o JSON e termine comexit 0. - Programas GUI retornam cedo demais. Este nos custou um dia. Se o seu hook é um programa Windows compilado para o subsistema GUI, o PowerShell o inicia e segue em frente sem esperar. O Cursor lê uma saída vazia, então parece que o hook não fez nada (ou, com
failClosed, bloqueia tudo). Compile o hook como programa de console ou encapsule-o para que o PowerShell espere ele terminar.
Hooks de projeto também precisam de um workspace confiável. Um membro da equipe do Cursor listou o que verificar quando eles não fazem nada: a linha de log de Hooks “Hook N produced no output”, Settings → Hooks mostrando o workspace como confiável e erros como spawn, ENOENT, EPERM ou ExecutionPolicy no console.
Hooks na Cursor CLI
A Cursor CLI (cursor-agent) lê os mesmos ~/.cursor/hooks.json e .cursor/hooks.json, mas nunca enviou todos os eventos que o editor envia. Em janeiro de 2026, só os dois hooks de shell disparavam. Em abril, a equipe do Cursor confirmou que beforeShellExecution, afterShellExecution, afterFileEdit, postToolUse, stop e sessionStart funcionavam na CLI, enquanto afterAgentResponse e afterAgentThought ainda não (confirmado de novo em maio). Para qualquer evento fora dessa lista, teste antes de depender dele.
Os cloud agents são ainda mais restritos: hooks de usuário de ~/.cursor/hooks.json simplesmente não rodam lá.
Quando um hook não dispara: checklist
- Abra o canal de saída Hooks (View → Output → Hooks) ou a aba Hooks em Customize. Ele mostra quais hooks foram carregados, cada execução e os erros.
- Confira o próprio arquivo JSON.
"version": 1presente, nomes de eventos escritos exatamente (eles diferenciam maiúsculas de minúsculas), JSON válido. - Confira o caminho. Comandos relativos são resolvidos a partir da raiz do projeto em hooks de projeto e a partir de
~/.cursor/em hooks de usuário. - Confie no workspace. Hooks de projeto não rodam em uma pasta não confiável.
- Execute o hook manualmente. Passe um JSON de exemplo pelo pipe e olhe o stdout. Qualquer coisa além de um único objeto JSON (um
echoperdido, um aviso) quebra o parsing. - Reinicie o Cursor. Salvar recarrega o arquivo, mas vários relatos do fórum só foram resolvidos com uma reinicialização completa.
- Conheça as lacunas. Há relatos de
askeallowignorados junto com a lista de permissões e o sandbox; desubagentStart/subagentStopsem disparar em algumas versões; e a CLI pula alguns eventos. Commits feitos pelo painel Source Control integrado não são interceptados: não existe evento de hook pre-commit.
Hooks, auto-run e Auto-review
Os hooks são separados das configurações de aprovação do próprio Cursor (Settings → Agents → Approvals & Execution). O auto-run e o Auto-review decidem se o Cursor pergunta a você no painel dele; seu hook adiciona a própria decisão por cima. É por isso que “meu hook disse ask, mas o comando simplesmente rodou” é um relato tão comum: outro caminho de permissão já tinha permitido.
Mais uma coisa que aprendemos a duras penas: com o Auto-review, o Cursor pode perguntar sobre alguns comandos na própria janela. Um hook nunca vê essa pergunta. Ela acontece dentro do Cursor, não no pipeline de hooks.
Onde o Pet Island entra
Se o que você quer dos hooks é “mostre cada comando arriscado e me deixe decidir sem trocar de janela”, é isso que o Pet Island faz com o Cursor. Ele instala os hooks para você (beforeShellExecution e beforeMCPExecution, com failClosed ativado para que um helper travado nunca deixe um comando passar) e coloca cada solicitação em uma pequena ilha no topo da sua tela, com o comando completo. Você permite ou nega ali; o agente do Cursor espera.
Comandos e edições de arquivo do editor Cursor funcionam pela ilha. Perguntas e planos aparecem na ilha, e você responde no Cursor. Na Cursor CLI, os comandos funcionam pela ilha; os outros eventos dependem do que a CLI envia. Desinstalar remove apenas as entradas que o Pet Island adicionou; seus próprios hooks continuam lá.
PERGUNTAS FREQUENTES
Os hooks do Cursor funcionam no Windows?
Sim. O app desktop os executa pelo PowerShell e passa o JSON pela entrada padrão ($input em um script PowerShell). Scripts bash escritos para macOS precisam de uma versão em PowerShell ou Python, e programas do subsistema GUI precisam ser encapsulados para que o PowerShell espere por eles.
Por que meu hook retorna ask, mas o comando roda mesmo assim?
A equipe do Cursor disse em março de 2026 que os hooks só aplicavam deny de forma confiável; allow e ask podiam ser sobrepostos pela lista de permissões ou pelo sandbox. Use deny para tudo o que não pode rodar e confira o canal de saída Hooks para ver o que o seu hook realmente retornou.
O que o failClosed faz?
Com failClosed definido como true, uma falha, um timeout, uma saída diferente de zero ou uma saída vazia bloqueia a ação. Sem ele, um hook quebrado deixa a ação seguir em frente.
Onde vejo os erros dos hooks?
No canal de saída Hooks (View → Output → Hooks) e na aba Hooks em Customize.
Os hooks funcionam na Cursor CLI?
Em parte. A CLI lê os mesmos arquivos hooks.json. Até maio de 2026, a equipe do Cursor confirmou os hooks de shell, afterFileEdit, postToolUse, stop e sessionStart, mas não afterAgentResponse nem afterAgentThought.
Um hook pode impedir um git commit feito pelo painel Source Control?
Não. O Cursor não tem evento de hook pre-commit, e commits feitos pela interface Source Control integrada não são interceptados. Para isso, use um hook pre-commit normal do git.
Fontes
Todas as configurações e comportamentos desta página foram conferidos com estas páginas em 26 de outubro de 2026. Os agentes mudam rápido; se algo não bater mais, vale o que diz a página oficial.
- Documentação do Cursor: Hooks
- Fórum do Cursor: os hooks do Cursor não foram executados como esperado (equipe: só deny é aplicado)
- Fórum do Cursor: allow/ask do beforeShellExecution ignorados, a lista de permissões tem precedência
- Fórum do Cursor: hook fail-closed e MainThreadShellExec not initialized
- Fórum do Cursor: JSON malformado de um hook permitia o comando em silêncio (corrigido)
- Fórum do Cursor: a Cursor CLI não envia todos os eventos de hook
- Fórum do Cursor: hooks de projeto não são executados no Windows
- Fórum do Cursor: hooks de projeto falham no Windows com Git Bash
- Fórum do Cursor: subagentStart e subagentStop nunca disparam