Hooks Cursor : le guide complet, et pourquoi ils ne se déclenchent pas toujours

Les hooks de Cursor permettent à un script de surveiller ou de bloquer ce que fait l’agent : chaque commande shell, chaque appel MCP, chaque lecture et modification de fichier. Ils sont puissants, mais ils échouent en silence. Voici tout ce que nous avons appris en les mettant en place pour de vrai.

Par Aslan, équipe Pet Island · Mis à jour le 26 oct. 2026 · 7 min de lecture

RÉPONSE COURTE

Placez un hooks.json avec "version": 1 dans ~/.cursor/ (tous les projets) ou dans .cursor/ d’un dépôt, associez chaque événement à une commande et écrivez du JSON sur stdout. Utilisez beforeShellExecution et beforeMCPExecution pour autoriser, refuser ou demander, et définissez failClosed: true si un hook défaillant doit bloquer au lieu de laisser passer la commande. Quand un hook semble ignoré, ouvrez d’abord le canal de sortie Hooks : la plupart des problèmes viennent d’un chemin, d’un espace de travail non approuvé, d’un JSON invalide ou d’un événement que la CLI Cursor n’envoie pas encore.

Ce que sont les hooks Cursor

Un hook est une commande que Cursor exécute à un point précis de la boucle de l’agent. Cursor envoie sur l’entrée standard de la commande un objet JSON décrivant ce qui est sur le point de se produire, attend, puis lit une réponse JSON sur sa sortie standard. Certains hooks se contentent d’observer (journaliser la modification, formater le fichier). D’autres décident : un hook beforeShellExecution peut laisser une commande s’exécuter, l’arrêter ou vous demander d’abord.

Les hooks sont donc l’outil adapté à trois usages : les garde-fous (ne jamais exécuter rm -rf ou git push --force sans un humain), l’automatisation (formater ou analyser le code après chaque modification) et la visibilité (journaliser ou transmettre ce que fait l’agent pour savoir quand il a besoin de vous).

Où se trouve hooks.json

Cursor lit les hooks à quatre emplacements et les exécute tous. Chaque hook correspondant de chaque source s’exécute, et Cursor fusionne leurs réponses.

NiveauFichierRemarques
Utilisateur~/.cursor/hooks.jsonS’applique à tous les projets. Les commandes relatives sont résolues depuis ~/.cursor/.
Projet<project>/.cursor/hooks.jsonVersionné dans le dépôt. S’exécute uniquement dans un espace de travail approuvé. Les commandes relatives sont résolues depuis la racine du projet.
ÉquipeTableau de bord web de CursorEnterprise uniquement, synchronisé pour chaque membre.
EntrepriseC:\ProgramData\Cursor\hooks.json (Windows), /Library/Application Support/Cursor/hooks.json (macOS), /etc/cursor/hooks.json (Linux, WSL)Géré par le service informatique.

Quand les réponses divergent, la plus stricte l’emporte : un deny de n’importe quelle source l’emporte sur ask, et ask l’emporte sur allow. Le plus petit fichier fonctionnel ressemble à ceci (c’est l’exemple de la documentation de Cursor) :

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

version doit valoir 1. Cursor surveille le fichier et le recharge à l’enregistrement. Si une modification ne prend pas effet, redémarrez Cursor. Cela suffit à résoudre un nombre étonnant de signalements sur le forum.

Les événements que vous pouvez intercepter

La liste actuelle de la documentation de Cursor, regroupée par usage :

GroupeÉvénementsPeut-il bloquer ?
Shell et MCPbeforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecutionLes deux hooks before… : autoriser, refuser ou demander
Tout outilpreToolUse, postToolUse, postToolUseFailurepreToolUse : autoriser ou refuser (ask n’est pas appliqué aujourd’hui)
FichiersbeforeReadFile, afterFileEditbeforeReadFile : autoriser ou refuser
PromptbeforeSubmitPromptOui, avec continue: false
Boucle de l’agentstop, afterAgentResponse, afterAgentThought, preCompactNon (stop peut envoyer un message de suivi)
Sous-agentssubagentStart, subagentStopsubagentStart : autoriser ou refuser (ask compte comme un refus)
Session et applicationsessionStart, sessionEnd, workspaceOpenNon
Complétions TabbeforeTabFileRead, afterTabFileEditbeforeTabFileRead : autoriser ou refuser

Chaque événement reçoit quelques champs communs (conversation_id, generation_id, model, hook_event_name, cursor_version, workspace_roots) en plus des siens. beforeShellExecution reçoit la command complète et son cwd ; stop reçoit un status qui vaut completed, aborted ou error.

Autoriser, refuser et demander

Un hook décisionnel écrit un seul objet JSON. Pour le hook shell, cela ressemble à ceci :

stdout d’un hook beforeShellExecutionCopier
{
"permission": "ask",
"user_message": "This pushes to main. Run it?",
"agent_message": "The user wants to confirm pushes."
}
  • permission vaut allow, deny ou ask.
  • user_message est ce que vous voyez dans Cursor.
  • agent_message est ce qui est communiqué à l’agent, pour qu’il change d’approche au lieu de réessayer la même commande.

Les codes de sortie comptent autant que le JSON. Le code 0 signifie « utilise mon JSON ». Le code 2 bloque l’action, comme deny. Tout autre code de sortie est traité comme un échec du hook, et l’action se poursuit, sauf si vous définissez failClosed. Pour les hooks d’autorisation, un JSON invalide ou non conforme au schéma bloque l’action.

La documentation de Cursor indique que ask est pris en charge par beforeShellExecution et beforeMCPExecution. En mars 2026, l’équipe de Cursor a écrit sur le forum que les hooks n’appliquaient que deny et que allow et ask étaient « basically ignored » (pratiquement ignorés), en particulier avec la liste d’autorisation de commandes ou le bac à sable activés. Nous n’avons trouvé aucune annonce de correctif. Si un garde-fou doit tenir, renvoyez deny, pas ask, et testez-le dans votre propre version.

failClosed et timeout

Chaque entrée de hook peut avoir des options à côté de command :

.cursor/hooks.jsonCopier
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "./.cursor/hooks/guard.sh",
"timeout": 30,
"failClosed": true
}
]
}
}
  • failClosed: true transforme un plantage, un dépassement du délai, un code de sortie non nul ou une sortie vide en blocage. La valeur par défaut est false : une garde défaillante laisse silencieusement tout passer. Pour un hook de sécurité, vous voulez presque toujours true.
  • timeout est exprimé en secondes. Prévoyez assez de marge si votre hook attend un humain : une personne qui lit un diff a besoin de plus de cinq secondes.
  • matcher filtre les cas où le hook s’exécute. Pour les hooks shell, il est comparé à la chaîne de commande complète ; pour les hooks d’outils, au type d’outil, comme Shell, Read, Write ou MCP:<tool_name>.

Il y a un compromis. En juillet 2026, un utilisateur sous macOS a rencontré « MainThreadShellExec not initialized » : l’hôte shell de Cursor n’était pas prêt, et comme le hook était en fail-closed, le shell de l’agent était bloqué. L’équipe de Cursor a indiqué que le correctif avait été publié et a recommandé de garder failClosed: true. Si vous voyez cette erreur sur une version plus ancienne, le contournement consistait à lancer « Developer: Reload Window ».

Les hooks sous Windows

Sous Windows, l’application de bureau Cursor exécute les commandes des hooks via PowerShell et leur transmet le JSON par un pipe. Un script PowerShell le lit depuis $input ; tout autre programme lit l’entrée standard comme d’habitude. Trois points piègent souvent :

  1. Les scripts bash ne s’exécutent pas tels quels. Un guard.sh écrit par un collègue sous macOS échoue sous PowerShell. Utilisez un script .ps1, un programme compilé, ou appelez explicitement votre interpréteur (python .cursor/hooks/guard.py). Un fil du forum de janvier 2026 montre une configuration Git Bash qui échoue avec syntax error near unexpected token '[Convert]::FromBase64String', c’est-à-dire du code PowerShell qui arrive dans bash.
  2. La stratégie d’exécution. Si PowerShell refuse d’exécuter des scripts, lancez-les avec powershell -NoProfile -ExecutionPolicy Bypass -File …, écrivez le JSON et terminez par exit 0.
  3. Les programmes graphiques rendent la main trop tôt. Celui-ci nous a coûté une journée. Si votre hook est un programme Windows compilé pour le sous-système graphique, PowerShell le lance et continue sans attendre. Cursor lit une sortie vide, donc le hook semble n’avoir rien fait (ou, avec failClosed, bloque tout). Compilez le hook comme programme console, ou encapsulez-le pour que PowerShell attende sa fin.

Les hooks de projet nécessitent aussi un espace de travail approuvé. Un membre de l’équipe de Cursor a listé ce qu’il faut vérifier quand ils ne font rien : la ligne « Hook N produced no output » dans le journal Hooks, l’espace de travail affiché comme approuvé dans Settings → Hooks, et des erreurs comme spawn, ENOENT, EPERM ou ExecutionPolicy dans la console.

Les hooks dans la CLI Cursor

La CLI Cursor (cursor-agent) lit les mêmes fichiers ~/.cursor/hooks.json et .cursor/hooks.json, mais elle n’a jamais envoyé tous les événements qu’envoie l’éditeur. En janvier 2026, seuls les deux hooks shell se déclenchaient. En avril, l’équipe de Cursor a confirmé que beforeShellExecution, afterShellExecution, afterFileEdit, postToolUse, stop et sessionStart fonctionnaient dans la CLI, alors que afterAgentResponse et afterAgentThought ne fonctionnaient toujours pas (confirmé à nouveau en mai). Pour tout événement absent de cette liste, testez avant de vous y fier.

Les agents cloud sont encore plus stricts : les hooks utilisateur de ~/.cursor/hooks.json ne s’y exécutent pas du tout.

Quand un hook ne se déclenche pas : liste de vérification

  1. Ouvrez le canal de sortie Hooks (View → Output → Hooks) ou l’onglet Hooks dans Customize. Il indique quels hooks ont été chargés, chaque exécution et les erreurs.
  2. Vérifiez le fichier JSON lui-même. "version": 1 présent, noms d’événements écrits exactement (ils sont sensibles à la casse), JSON valide.
  3. Vérifiez le chemin. Les commandes relatives sont résolues depuis la racine du projet pour les hooks de projet, et depuis ~/.cursor/ pour les hooks utilisateur.
  4. Approuvez l’espace de travail. Les hooks de projet ne s’exécutent pas dans un dossier non approuvé.
  5. Exécutez le hook à la main. Envoyez-lui un exemple de JSON par un pipe et regardez stdout. Tout ce qui n’est pas un unique objet JSON (un echo égaré, un avertissement) casse l’analyse.
  6. Redémarrez Cursor. L’enregistrement recharge le fichier, mais plusieurs signalements du forum n’ont été résolus que par un redémarrage complet.
  7. Gardez en tête les limites connues. ask et allow ont été signalés comme ignorés en présence de la liste d’autorisation et du bac à sable ; subagentStart/subagentStop ont été signalés comme ne se déclenchant pas dans certaines versions ; la CLI ignore certains événements. Les commits faits depuis le panneau Source Control intégré ne sont pas interceptés : il n’existe pas d’événement de hook pre-commit.

Hooks, auto-run et Auto-review

Les hooks sont distincts des paramètres d’approbation propres à Cursor (Settings → Agents → Approvals & Execution). Auto-run et Auto-review décident si Cursor vous demande dans son panneau ; votre hook ajoute sa propre décision par-dessus. C’est pourquoi « mon hook a répondu ask, mais la commande s’est exécutée » est un signalement si fréquent : un autre chemin d’autorisation l’avait déjà autorisée.

Autre chose que nous avons appris à nos dépens : avec Auto-review, Cursor peut poser des questions sur certaines commandes dans sa propre fenêtre. Un hook ne voit jamais cette question. Elle se passe dans Cursor, pas dans le pipeline des hooks.

La place de Pet Island

Si ce que vous attendez des hooks, c’est « montre-moi chaque commande risquée et laisse-moi décider sans changer de fenêtre », c’est exactement ce que fait Pet Island avec Cursor. Il installe les hooks pour vous (beforeShellExecution et beforeMCPExecution, avec failClosed activé pour qu’un utilitaire arrêté ne laisse jamais passer une commande) et affiche chaque demande sur une petite île en haut de votre écran, avec la commande complète. Vous l’autorisez ou la refusez depuis l’île ; l’agent de Cursor attend.

Les commandes et les modifications de fichiers de l’éditeur Cursor fonctionnent depuis l’île. Les questions et les plans s’affichent sur l’île, et vous y répondez dans Cursor. Dans la CLI Cursor, les commandes fonctionnent depuis l’île ; les autres événements dépendent de ce qu’envoie la CLI. La désinstallation ne supprime que les entrées ajoutées par Pet Island ; vos propres hooks restent en place.

FAQ

Les hooks Cursor fonctionnent-ils sous Windows ?

Oui. L’application de bureau les exécute via PowerShell et transmet le JSON sur l’entrée standard ($input dans un script PowerShell). Les scripts bash écrits pour macOS nécessitent une version PowerShell ou Python, et les programmes du sous-système graphique doivent être encapsulés pour que PowerShell les attende.

Pourquoi mon hook renvoie-t-il ask alors que la commande s’exécute quand même ?

L’équipe de Cursor a indiqué en mars 2026 que les hooks n’appliquaient de façon fiable que deny ; allow et ask pouvaient être contournés par la liste d’autorisation ou le bac à sable. Utilisez deny pour tout ce qui ne doit pas s’exécuter, et consultez le canal de sortie Hooks pour voir ce que votre hook a réellement renvoyé.

À quoi sert failClosed ?

Avec failClosed défini sur true, un plantage, un dépassement du délai, un code de sortie non nul ou une sortie vide bloque l’action. Sans lui, un hook défaillant laisse l’action se poursuivre.

Où voir les erreurs des hooks ?

Dans le canal de sortie Hooks (View → Output → Hooks) et dans l’onglet Hooks de Customize.

Les hooks fonctionnent-ils dans la CLI Cursor ?

En partie. La CLI lit les mêmes fichiers hooks.json. En mai 2026, l’équipe de Cursor a confirmé les hooks shell, afterFileEdit, postToolUse, stop et sessionStart, mais pas afterAgentResponse ni afterAgentThought.

Un hook peut-il empêcher un commit git depuis le panneau Source Control ?

Non. Cursor n’a pas d’événement de hook pre-commit, et les commits effectués via l’interface Source Control intégrée ne sont pas interceptés. Utilisez pour cela un hook git pre-commit classique.

Sources

Chaque paramètre et chaque comportement décrits sur cette page ont été vérifiés dans ces pages le 26 octobre 2026. Les agents évoluent vite : si quelque chose ne correspond plus, c’est la page officielle qui fait foi.

À LIRE AUSSI

Répondez à tous vos agents depuis le haut de votre écran.

Windows et Mac · 7 jours gratuits, puis 8,99 $ une seule fois · 1 $ de chaque vente nourrit des animaux errants

Essayer Pet Island gratuitement