We maintain agent-guardrails, an open-source safety layer that stops an AI coding agent from running a dangerous command before it runs. For most of its life it was a Claude Code hook pack. Then we looked at what OpenAI Codex CLI and Google Gemini CLI had shipped - and realized something useful: all three coding-agent CLIs had quietly converged on the same hook shape. That convergence means one guardrail policy can govern all three, through thin per-host adapters. This post is the design behind that, and an honest account of what is real today versus what is still aspirational.
First, what agent-guardrails actually is today
Honesty matters here, because security buyers read the code. agent-guardrails today is a Claude Code hook pack, not an abstract framework. It contains five bash interceptors, asettings.json deny list, a hooks.json wiring file, and some markdown policy docs. Each interceptor extracts the command an agent is about to run, greps it against a blocklist, and either allows it (exit 0) or blocks it (exit 2).
Three facts from auditing our own code shaped everything that followed:
- Zero model-vendor coupling. There is no Python, no LLM SDK, no model API call anywhere in it. The framework never talks to a model - it only inspects commands a host agent already decided to run.
- The coupling is to the host agent, not the model. It is tied to Claude Code's hook protocol, config directory, and command envelope - not to Anthropic's model API.
- The enforcement logic is already vendor-neutral. A regex blocklist over a command string works on any host. Only the shell around it is host-specific.
That last point is the whole opportunity. The core enforcement is proven and portable. The only thing keeping it Claude-only is the integration shell - and that shell is small.
The convergence: three CLIs, one hook shape
Sometime in the first half of 2026, all three major coding-agent CLIs shipped a pre-tool-execution hook with the same essential shape: the host pipes a JSON object describing the tool call to a command on stdin, and that command can allow, deny, or rewrite the call before it runs. The substance is identical across all three. The differences live at the edges.
Claude Code
- Event: PreToolUse
- Config: .claude/settings.json
- Deny: exit code 2
- Bash tool: Bash
OpenAI Codex CLI
- Event: PreToolUse
- Config: ~/.codex/hooks.json
- Deny: JSON permissionDecision
- Bash tool: Bash
Google Gemini CLI
- Event: BeforeTool
- Config: .gemini/settings.json
- Deny: JSON decision or exit 2
- Bash tool: run_shell_command
What is identical
- Intercept a shell command before it runs
- Decide allow or deny
- Return a reason to the agent
- Same shared substance
The differences are small and entirely at the edges: the event name (PreToolUse versusBeforeTool), the config file and format, the exact stdin JSON envelope, the verdict mechanism (exit code versus JSON), and the Bash tool's name. The shared substance - intercept a shell command before it runs, decide allow or deny - is identical. That is the seam.
The four seams every adapter implements
If the substance is shared and only the shell differs, then a multi-host guardrail layer is just a contract: one shared core that decides allow or deny, plus one thin adapter per host that handles the host-specific bits. Every adapter implements the same four seams. This is the interface.
.claude/settings.json plus hooks.json. Codex wants ~/.codex/hooks.json. Gemini wants .gemini/settings.json.PreToolUse on Bash for Claude and Codex; BeforeTool on run_shell_command for Gemini.permissionDecision: deny. Gemini emits a JSON decision: deny or exit 2.Notice what is not in the adapter: any allow or deny logic. All of that lives in one shared core. The adapters do only envelope parsing, verdict translation, and config emission. That is what keeps them thin and what keeps the policy the single source of truth.
One policy, many hosts
Today the blocklist is duplicated across five bash scripts. The refactor is to replace that duplication with one neutral file - a YAML policy that is the single source of truth - which the core loads and evaluates against the canonical command. First block wins, same as today. The adapters never read the policy directly; they hand the core a command and get back a verdict.
A rule looks like this: a regex pattern applied to the command string, a severity (block, today), a reason returned to the agent, and a stable id for audit references. That is enough to govern the dangerous-command surface across all three hosts from one file. When you want to blockterraform destroy without a target across Claude, Codex, and Gemini, you write it once.
What this is not (the honest limits)
I want to be careful not to overclaim, because the overclaim is exactly what a security buyer will catch. The four-seam adapter governs tool execution only - the moment before a command runs. It is one of four intervention points a complete agent governance framework would cover:
- Input - filtering what goes into the model. Not built.
- Tool call - intercepting the command before it runs. This is what ships.
- Tool response - filtering what comes back from a tool. Not built (PostToolUse is not wired).
- Output - filtering what the agent produces. Not built.
So the honest description today is "governs agent tool execution across Claude, Codex, and Gemini." The phrase "full four-point governance framework" is reserved for after the other three points ship. Codex and Gemini both support post-execution events, so the seams exist to build them - but building them is a separate workstream, and saying so here is what protects credibility with the people who will actually read the code.
Why this matters
Most consultancies bring services. The differentiator in agent governance is a verifiable, open-source artifact a buyer can inspect before they sign. "Governs coding-agent tool execution across Claude Code, OpenAI Codex, and Gemini CLI from one policy" is a real, checkable, multi-vendor claim - not a positioning aspiration. The durable asset is the policy schema and the adapter contract, not any one vendor relationship. If a buyer's security lead wants to verify that we actually blockDROP DATABASE on all three runtimes, they can clone the repo and run it.
That is the point of writing it up like this, in public, with the limits stated plainly. The convergence made multi-host guardrails possible. The honesty about what is built versus what is not is what makes it worth trusting.
The takeaway
If you are governing AI coding agents inside a regulated environment, the useful news is not that a new framework appeared. It is that the three tools your engineers already use converged on the same hook, which means you do not have to pick a vendor to start governing - and you do not have to maintain three separate safety configs. One policy, four seams per host, one shared core. That is the whole interface, and it is small enough to inspect.