Guides
Hooks for Codex, Cursor, Windsurf and Gemini CLI
One command puts a fail-closed Immiscible hook in front of Codex, Cursor, Windsurf or Gemini CLI, for one person or for every person on a machine through the agent’s managed settings. Shell commands, MCP tool calls and file writes are decided by the same rules as Claude Code’s.
Claude Code has its own hook and immiscible init installs it. The other coding agents people run at work each have a hook system of their own, and each can be made to block a call. immiscible install writes the hook for them:
immiscible install codex # or cursor, windsurf, geminiEach asks Immiscible before a shell command and an MCP tool call, and before a file write where the agent has that event. The request is the one the Claude Code hook sends, so the same rule decides for every agent: a read goes ahead, an edit or a test run inside the project goes ahead once the agent is past its intern stage, a force push or a deploy waits for a person, and a secrets file leaving the machine is refused.
#What each agent gets
| Agent | Events | When a person must approve |
|---|---|---|
| Codex | PreToolUse for Bash, apply_patch (each file of a patch is checked) and MCP tools | the hook waits up to four minutes for an answer, then goes ahead or refuses |
| Cursor | beforeShellExecution and beforeMCPExecution, with failClosed: true | Cursor asks you, with the reasons and the approval link |
| Windsurf | pre_run_command, pre_mcp_tool_use and pre_write_code | refused with the approval link; run it again once approved |
| Gemini CLI | BeforeTool for run_shell_command, write_file, replace, web_fetch and MCP tools | the hook waits up to four minutes for an answer, then goes ahead or refuses |
Only Cursor’s hooks can ask the person at the keyboard. Codex and Gemini CLI can only allow or block, so the hook holds the call while someone answers in Slack, Teams, email or the console; the hook’s timeout in the agent’s settings is set above the wait. Windsurf documents no hook timeout, and a hook it stops is treated as a pass, so the Windsurf hook never waits. A call that is held and not answered in time is refused with the approval link; running it again makes a new request.
Immiscible’s own read-only MCP tools (check_action_status, explain_decision, spend_summary, find_waste, unwatched_keys) are never sent back to Immiscible. Its tools that act still are.
#For one person, or for a whole machine
immiscible install cursor --key ask_... # for you: ~/.cursor/hooks.json
sudo immiscible install cursor --scope managed # for everyone on this machine
immiscible install codex --scope managed --dry-run # show the files, write nothing| Agent | --scope user | --scope managed |
|---|---|---|
| Codex | ~/.codex/hooks.json ($CODEX_HOME if set) | /etc/codex/requirements.toml on macOS and Linux, %ProgramData%\OpenAI\Codex\requirements.toml on Windows |
| Cursor | ~/.cursor/hooks.json | /Library/Application Support/Cursor/hooks.json, /etc/cursor/hooks.json, C:\ProgramData\Cursor\hooks.json |
| Windsurf | ~/.codeium/windsurf/hooks.json | /Library/Application Support/Windsurf/hooks.json, /etc/windsurf/hooks.json, C:\ProgramData\Windsurf\hooks.json; the Devin Desktop file in the same places when it already exists, since it hides the Windsurf one |
| Gemini CLI | ~/.gemini/settings.json | /Library/Application Support/GeminiCli/settings.json, /etc/gemini-cli/settings.json, C:\ProgramData\gemini-cli\settings.json (or GEMINI_CLI_SYSTEM_SETTINGS_PATH) |
The user scope puts the hook in ~/.immiscible; the managed scope puts it in an immiscible folder beside the managed file. What the managed scope adds:
- Codex:
allow_managed_hooks_only = true, so hooks from people’s own settings, projects and plugins do not run, and a[hooks]table naming the hook’s folder asmanaged_dir. Immiscible’s lines sit between# >>> immiscibleand# <<< immisciblemarkers; everything else in the file is left as written. If the file turns hooks off ([features] hooks = false), install refuses rather than write a hook that never runs. - Cursor: enterprise hooks run before project and user hooks, and a deny from any hook wins.
- Windsurf: system hooks run before user and workspace hooks.
- Gemini CLI:
hooksConfig.enabled: truein the system settings, which override user and project settings. A person can still list a hook by name in their ownhooksConfig.disabled; install warns when the file it writes lists Immiscible’s.
Device management tools can deploy the same files: run with --dry-run --json to get them, or set IMMISCIBLE_MANAGED_ROOT to write them under one folder for packaging.
#The agent key
The hook reads IMMISCIBLE_URL and IMMISCIBLE_AGENT_KEY from the environment first, then from hook.env beside it. Install writes your server’s address there, and the key with --key. Cursor and Windsurf start hooks from the app rather than your shell, and Gemini CLI gives hooks a reduced environment, so hook.env is usually how the key arrives. In the user scope the file is readable by you alone. In the managed scope every person’s hook reads it, so a key there stands for the machine; leave --key out to have each person set IMMISCIBLE_AGENT_KEY themselves. Until there is a key, the hook refuses every call it checks.
#Failing closed
Every installed command ends in || exit 2. Codex, Windsurf and Gemini CLI block a call on exit code 2 and let it through on any other failure, so a missing node, a missing hook file or a crash blocks the call rather than passing it. Cursor’s entry also sets failClosed: true, because Cursor lets a call through when a hook crashes or times out otherwise. When Immiscible cannot be reached, answers with an error or takes longer than IMMISCIBLE_TIMEOUT_MS (10 seconds by default), the hook refuses the call and says why. If you need to work offline, remove the hook; do not teach it to allow on error.
Each agent reads its hooks when it starts, so restart it, or open a new session, after installing. Codex runs a hook from a person’s own settings only after they trust it under /hooks; until then it does not run at all, which is one reason to prefer the managed scope, whose hooks need no trust step.
#What has been tested
The installers are built to each vendor’s published documentation: Codex hooks, Cursor hooks, Windsurf Cascade hooks and Gemini CLI hooks, as of October 2026. The tests run the command each agent would run, in a shell with the event on standard input as each vendor documents it, against a real Immiscible server: a force push is held, a secrets file leaving the machine is refused, and an unreachable server or a missing hook blocks the call. They do not run Codex, Cursor, Windsurf or Gemini CLI themselves, and the Windows paths are taken from the documentation without being exercised.