Hooks de Cursor: guía completa y por qué a veces no se disparan

Los hooks de Cursor permiten que un script observe o bloquee lo que hace el agente: cada comando de shell, cada llamada MCP, cada lectura y edición de archivos. Son potentes y fallan en silencio. Aquí está todo lo que aprendimos al configurarlos de verdad.

Por Aslan, equipo de Pet Island · Actualizado el 26 oct 2026 · 7 min de lectura

RESPUESTA CORTA

Coloca un hooks.json con "version": 1 en ~/.cursor/ (todos los proyectos) o en .cursor/ dentro de un repositorio, asigna un comando a cada evento e imprime JSON en stdout. Usa beforeShellExecution y beforeMCPExecution para permitir, denegar o preguntar, y establece failClosed: true si un hook roto debe bloquear en lugar de dejar pasar el comando. Cuando parezca que se ignora un hook, abre primero el canal de salida Hooks: la mayoría de los problemas son una ruta, un espacio de trabajo que no es de confianza, un JSON no válido o un evento que la CLI de Cursor todavía no envía.

Qué son los hooks de Cursor

Un hook es un comando que Cursor ejecuta en un punto fijo del ciclo del agente. Cursor envía a la entrada estándar del comando un objeto JSON que describe lo que está a punto de ocurrir, espera y lee una respuesta JSON de su salida estándar. Algunos hooks solo observan (registran la edición, formatean el archivo). Otros deciden: un hook beforeShellExecution puede dejar que un comando se ejecute, detenerlo o preguntarte primero.

Eso convierte a los hooks en la herramienta adecuada para tres tareas: protecciones (no ejecutar nunca rm -rf ni git push --force sin una persona), automatización (formatear o pasar el linter después de cada edición) y visibilidad (registrar o reenviar lo que hace el agente para saber cuándo te necesita).

Dónde está hooks.json

Cursor lee hooks de cuatro lugares y los ejecuta todos. Se ejecuta cada hook que coincida, venga de la fuente que venga, y Cursor combina sus respuestas.

NivelArchivoNotas
Usuario~/.cursor/hooks.jsonSe aplica a todos los proyectos. Los comandos relativos se resuelven desde ~/.cursor/.
Proyecto<project>/.cursor/hooks.jsonSe sube al repositorio. Solo se ejecuta en un espacio de trabajo de confianza. Los comandos relativos se resuelven desde la raíz del proyecto.
EquipoPanel web de CursorSolo Enterprise; se sincroniza con todos los miembros.
EmpresaC:\ProgramData\Cursor\hooks.json (Windows), /Library/Application Support/Cursor/hooks.json (macOS), /etc/cursor/hooks.json (Linux, WSL)Lo gestiona TI.

Cuando las respuestas no coinciden, gana la más estricta: un deny de cualquier fuente gana a ask, y ask gana a allow. El archivo mínimo que funciona tiene este aspecto (es el ejemplo de la documentación de Cursor):

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

version debe ser 1. Cursor vigila el archivo y lo recarga al guardarlo. Si un cambio no surte efecto, reinicia Cursor. Solo con eso se resuelve un número sorprendente de informes del foro.

Los eventos en los que puedes usar hooks

La lista actual de la documentación de Cursor, agrupada por su finalidad:

GrupoEventos¿Puede bloquear?
Shell y MCPbeforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecutionLos dos hooks before…: allow, deny o ask
Cualquier herramientapreToolUse, postToolUse, postToolUseFailurepreToolUse: allow o deny (ask no se aplica hoy)
ArchivosbeforeReadFile, afterFileEditbeforeReadFile: allow o deny
PromptbeforeSubmitPromptSí, con continue: false
Ciclo del agentestop, afterAgentResponse, afterAgentThought, preCompactNo (stop puede enviar un mensaje de seguimiento)
SubagentessubagentStart, subagentStopsubagentStart: allow o deny (ask cuenta como deny)
Sesión y aplicaciónsessionStart, sessionEnd, workspaceOpenNo
Autocompletado con TabbeforeTabFileRead, afterTabFileEditbeforeTabFileRead: allow o deny

Cada evento recibe unos cuantos campos comunes (conversation_id, generation_id, model, hook_event_name, cursor_version, workspace_roots) más los suyos propios. beforeShellExecution recibe el command completo y su cwd; stop recibe un status que puede ser completed, aborted o error.

Permitir, denegar y preguntar

Un hook que decide imprime un objeto JSON. Para el hook de shell, tiene este aspecto:

stdout de un hook beforeShellExecutionCopiar
{
"permission": "ask",
"user_message": "This pushes to main. Run it?",
"agent_message": "The user wants to confirm pushes."
}
  • permission es allow, deny o ask.
  • user_message es lo que ves en Cursor.
  • agent_message es lo que se le dice al agente, para que cambie de rumbo en lugar de reintentar el mismo comando.

Los códigos de salida importan tanto como el JSON. Salir con 0 significa “usa mi JSON”. Salir con 2 bloquea la acción, igual que deny. Cualquier otro código de salida se trata como un hook fallido, y la acción sigue adelante, salvo que establezcas failClosed. En los hooks de permisos, un JSON no válido o que no se ajusta al esquema bloquea la acción.

La documentación de Cursor indica que ask es compatible con beforeShellExecution y beforeMCPExecution. En marzo de 2026, personal de Cursor escribió en el foro que los hooks solo aplicaban deny y que allow y ask se “basically ignored” (prácticamente se ignoraban), sobre todo con la lista de comandos permitidos o el sandbox activados. No hemos encontrado ningún anuncio de corrección. Si una protección tiene que funcionar sí o sí, devuelve deny, no ask, y pruébala en tu propia versión.

failClosed y timeout

Cada entrada de hook puede llevar opciones junto a command:

.cursor/hooks.jsonCopiar
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "./.cursor/hooks/guard.sh",
"timeout": 30,
"failClosed": true
}
]
}
}
  • failClosed: true convierte un fallo, un tiempo de espera agotado, una salida distinta de cero o una salida vacía en un bloqueo. El valor predeterminado es false: una protección rota lo deja pasar todo sin avisar. Para un hook de seguridad, casi siempre quieres true.
  • timeout se expresa en segundos. Deja margen suficiente si tu hook espera a una persona: alguien que lee un diff necesita más de cinco segundos.
  • matcher filtra cuándo se ejecuta el hook. En los hooks de shell se compara con la cadena de comando completa; en los hooks de herramientas, con el tipo de herramienta, como Shell, Read, Write o MCP:<tool_name>.

Hay una contrapartida. En julio de 2026, un usuario de macOS se topó con “MainThreadShellExec not initialized”: el host de shell del propio Cursor no estaba listo y, como el hook era fail-closed (bloquea si falla), la shell del agente quedó bloqueada. El personal de Cursor dijo que la corrección ya se había publicado y recomendó mantener failClosed: true. Si ves ese error en una compilación antigua, la solución temporal era “Developer: Reload Window”.

Hooks en Windows

En Windows, la app de escritorio de Cursor ejecuta los comandos de los hooks a través de PowerShell y les pasa el JSON por una tubería. Un script de PowerShell lo lee de $input; cualquier otro programa lee la entrada estándar como siempre. Hay tres cosas en las que mucha gente tropieza:

  1. Los scripts de bash no funcionan tal cual. Un guard.sh de un compañero con macOS falla en PowerShell. Usa un script .ps1, un programa compilado o llama a tu intérprete explícitamente (python .cursor/hooks/guard.py). Un hilo del foro de enero de 2026 muestra una configuración con Git Bash que falla con syntax error near unexpected token '[Convert]::FromBase64String', es decir, código de PowerShell que llega a bash.
  2. Directiva de ejecución. Si PowerShell se niega a ejecutar scripts, inícialos con powershell -NoProfile -ExecutionPolicy Bypass -File …, imprime el JSON y termina con exit 0.
  3. Los programas GUI vuelven demasiado pronto. Este nos costó un día. Si tu hook es un programa de Windows compilado para el subsistema GUI, PowerShell lo inicia y sigue adelante sin esperar. Cursor lee una salida vacía, así que el hook parece no haber hecho nada (o, con failClosed, lo bloquea todo). Compila el hook como programa de consola, o envuélvelo para que PowerShell espere a que termine.

Los hooks de proyecto también necesitan un espacio de trabajo de confianza. Un miembro del personal de Cursor enumeró qué revisar cuando no hacen nada: la línea “Hook N produced no output” en el log de Hooks, que Settings → Hooks muestre el espacio de trabajo como de confianza, y errores como spawn, ENOENT, EPERM o ExecutionPolicy en la consola.

Hooks en la CLI de Cursor

La CLI de Cursor (cursor-agent) lee los mismos ~/.cursor/hooks.json y .cursor/hooks.json, pero nunca ha enviado todos los eventos que envía el editor. En enero de 2026 solo se disparaban los dos hooks de shell. En abril, el personal de Cursor confirmó que beforeShellExecution, afterShellExecution, afterFileEdit, postToolUse, stop y sessionStart funcionaban en la CLI, mientras que afterAgentResponse y afterAgentThought seguían sin hacerlo (se volvió a confirmar en mayo). Con cualquier evento que no esté en esa lista, haz pruebas antes de confiar en él.

Los agentes en la nube son aún más estrictos: allí no se ejecutan en absoluto los hooks de usuario de ~/.cursor/hooks.json.

Cuando un hook no se dispara: lista de comprobación

  1. Abre el canal de salida Hooks (View → Output → Hooks) o la pestaña Hooks en Customize. Muestra qué hooks se cargaron, cada ejecución y los errores.
  2. Revisa el propio archivo JSON. Que esté "version": 1, que los nombres de los eventos estén escritos exactamente (distinguen mayúsculas de minúsculas) y que el JSON sea válido.
  3. Revisa la ruta. Los comandos relativos se resuelven desde la raíz del proyecto en los hooks de proyecto y desde ~/.cursor/ en los hooks de usuario.
  4. Confía en el espacio de trabajo. Los hooks de proyecto no se ejecutan en una carpeta que no es de confianza.
  5. Ejecuta el hook a mano. Pásale un JSON de ejemplo por una tubería y mira stdout. Cualquier cosa que no sea un único objeto JSON (un echo perdido, una advertencia) rompe el análisis.
  6. Reinicia Cursor. Al guardar se recarga el archivo, pero varios informes del foro solo se resolvieron con un reinicio completo.
  7. Conoce las limitaciones. Se ha informado de que ask y allow se ignoran junto a la lista de permitidos y el sandbox; de que subagentStart/subagentStop no se disparan en algunas versiones; y la CLI omite algunos eventos. Los commits desde el panel Source Control integrado no se interceptan: no existe un evento de hook pre-commit.

Hooks, ejecución automática y Auto-review

Los hooks son independientes de los ajustes de aprobación del propio Cursor (Settings → Agents → Approvals & Execution). La ejecución automática y Auto-review deciden si Cursor te pregunta en su panel; tu hook añade su propia decisión encima. Por eso es tan habitual el informe de “mi hook dijo ask, pero el comando se ejecutó sin más”: otra vía de permisos ya lo había permitido.

Otra cosa que aprendimos por las malas: con Auto-review, Cursor puede preguntar por algunos comandos en su propia ventana. Un hook nunca ve esa pregunta. Ocurre dentro de Cursor, no en la cadena de hooks.

Dónde encaja Pet Island

Si lo que quieres de los hooks es “muéstrame cada comando arriesgado y déjame decidir sin cambiar de ventana”, eso es lo que hace Pet Island con Cursor. Instala los hooks por ti (beforeShellExecution y beforeMCPExecution, con failClosed activado para que un asistente caído nunca deje pasar un comando) y muestra cada solicitud en una pequeña isla en la parte superior de la pantalla con el comando completo. La permites o la deniegas allí; el agente de Cursor espera.

Los comandos y las ediciones de archivos del editor de Cursor funcionan desde la isla. Las preguntas y los planes aparecen en la isla, y los respondes en Cursor. En la CLI de Cursor, los comandos funcionan desde la isla; los demás eventos dependen de lo que envíe la CLI. Al desinstalar solo se eliminan las entradas que añadió Pet Island; tus propios hooks se quedan.

PREGUNTAS FRECUENTES

¿Funcionan los hooks de Cursor en Windows?

Sí. La app de escritorio los ejecuta a través de PowerShell y pasa el JSON por la entrada estándar ($input en un script de PowerShell). Los scripts de bash escritos para macOS necesitan una versión en PowerShell o Python, y los programas del subsistema GUI deben envolverse para que PowerShell espere a que terminen.

¿Por qué mi hook devuelve ask pero el comando se ejecuta igualmente?

El personal de Cursor dijo en marzo de 2026 que los hooks solo aplicaban de forma fiable deny; allow y ask podían quedar anulados por la lista de permitidos o el sandbox. Usa deny para todo lo que no deba ejecutarse, y revisa el canal de salida Hooks para ver qué devolvió realmente tu hook.

¿Qué hace failClosed?

Con failClosed en true, un fallo, un tiempo de espera agotado, una salida distinta de cero o una salida vacía bloquean la acción. Sin él, un hook roto deja que la acción siga adelante.

¿Dónde veo los errores de los hooks?

En el canal de salida Hooks (View → Output → Hooks) y en la pestaña Hooks de Customize.

¿Funcionan los hooks en la CLI de Cursor?

En parte. La CLI lee los mismos archivos hooks.json. En mayo de 2026, el personal de Cursor confirmó los hooks de shell, afterFileEdit, postToolUse, stop y sessionStart, pero no afterAgentResponse ni afterAgentThought.

¿Puede un hook detener un git commit hecho desde el panel Source Control?

No. Cursor no tiene un evento de hook pre-commit, y los commits hechos desde la interfaz de Source Control integrada no se interceptan. Para eso, usa un hook pre-commit normal de git.

Fuentes

Cada ajuste y comportamiento de esta página se comprobó con estas páginas el 26 de octubre de 2026. Los agentes cambian rápido; si algo ya no coincide, manda la página oficial.

SIGUE LEYENDO

Responde a todos tus agentes desde la parte superior de la pantalla.

Windows y Mac · 7 días gratis, después un único pago de $8.99 · $1 de cada venta alimenta a animales callejeros

Prueba Pet Island gratis