Why I Built Warden
I spend my days building cloud SDKs and .NET tooling. When AI coding agents arrived, I did what every developer does — I went all in. Claude Code, Gemini CLI, multiple agents running in parallel across different repos. It was great until it wasn’t.
The wake-up calls
The first time an agent ran rm -rf on a directory it had misidentified as temp files, I caught it by luck — I happened to be watching the terminal. The second time an agent looped for 47 turns trying to fix a type error it had introduced three turns earlier, I wasn’t watching. I came back to a burnt context window and zero progress.
Then there was the agent that helpfully added // Generated by Claude to every file it touched. And the one that ran git push --force to main because it thought that was the fastest way to resolve a merge conflict.
These aren’t hypothetical failure modes. These are Tuesday.
The gap in the market
The obvious answer is “just write better instructions.” So I did — carefully crafted CLAUDE.md files, detailed rules, explicit prohibitions. The problem: those instructions live inside the context window. The model can ignore them. It can hallucinate past them. And when context gets compacted (which happens every session), the rules are often the first thing to go.
Other tools exist. Bash wrappers that alias dangerous commands. Prompt engineering frameworks. Output formatters. But none of them operate where it matters: at the tool call level, before the action executes.
I needed something that sits between the agent and my codebase. Something that evaluates every tool call — read, write, bash, MCP — and can block, redirect, or rewrite it. Something the model can’t talk its way around.
Why it had to be Rust
This thing runs on every single tool call, so its cost is paid a few hundred times a session. On my machine a hook call sits around 57ms at the median — and most of that is not my code. It is the operating system starting a process, which the host does before Warden gets a say.
So the honest framing is not “free”. It is: the per-call cost is dominated by process spawn, and the work Warden does inside that process has to stay small enough to disappear into it. That is what the design buys. Every rule is a compiled pattern evaluated in a single pass, not a list walked rule by rule. Session state lives in a background server, so a hook doesn’t re-read and re-parse it every call. The whole engine — safety, governance, intelligence — is one static binary with no runtime dependencies. No Node runtime, no Python interpreter, no Docker container. One binary, three platforms.
A language with a startup cost of its own, or a garbage collector deciding to run mid-hook, would spend the budget before doing any work.
I considered Go (fast enough, but the binary size and GC pauses bothered me) and TypeScript (the ecosystem is right, but a Node startup on every tool call is the whole budget). Rust was the only option that gave me the headroom I needed with deterministic behavior.
What makes this different
Warden is not a prompt wrapper. It doesn’t inject text into your system prompt and hope the model follows it. It’s not an API validator that checks responses after the fact.
It’s a runtime layer. When Claude Code calls the Bash tool with rm -rf ~/project, Warden intercepts that call before it executes, checks it against compiled patterns, and returns deny. The model never gets a chance to ignore it, argue with it, or be talked out of it by something it read in a file.
Not everything is a deny, and that distinction took me a while to get right. Blocking grep so the agent reissues it as rg costs a full extra model round-trip to save a little output — a bad trade. So grep, find, cat and curl run as written and the agent is told what to use next time; it picks the habit up and keeps it for the session. Only the swaps that are safe to make silently — ls, du — are rewritten in place. Denial is reserved for the things that should never run.
The same mechanism handles output compression. When npm test produces 500 lines of output, Warden pipes it through a filter that keeps the failures and the summary before it enters the context window. I built it because I was watching build logs eat context that should have held source code. Whether that buys you ten turns or fifty depends on your project and how noisy your tools are — I have not measured it across enough projects to give you a number, and I would rather give you none than an invented one.
The result
Sessions stay productive longer. Context windows don’t overflow with test output and build logs. Dangerous commands get caught before they execute, not after you notice the damage. And the agent gets steered toward better tools — rg instead of grep, fd instead of find — without any prompt engineering.
The best part: when Warden is doing its job well, you don’t notice it. Healthy sessions run silently. It only becomes visible when something goes wrong — and by then, it’s already handled.
What’s next
Right now Warden supports Claude Code, Gemini CLI, and Codex CLI. I’m working on broader agent support, editor integrations, and a governance layer that lets teams define and enforce coding policies across their entire AI-assisted workflow.
If you’re using AI coding agents in production — or even just on side projects you care about — take a look. It’s fully local, free to use, and installs in one command: npx @bitmilldev/warden init.
The docs are at bitmill.dev.