Tool Substitution
When the agent reaches for a legacy CLI tool, Warden can either rewrite the command to a modern alternative or teach the agent to use it. The result is faster execution, less noisy output, and better defaults.
| Agent uses | Warden runs | Why |
|---|---|---|
grep | rg (ripgrep) | Faster regex search; skips .gitignored paths, so less output |
find | fd | Faster file discovery with sane defaults |
curl | xh | Friendlier HTTP client |
cat | bat | Syntax highlighting, line numbers |
ls | eza | Better directory listings with git status |
du | dust | Visual disk usage tree |
tar/zip | ouch | Auto-detecting archive tool |
sort|uniq | huniq | Preserves insertion order |
ts-node | tsx | Faster TypeScript execution |
sd | — | Blocked on Windows: mangles newlines and encodings |
Substitutions only fire when the target tool is installed. The warden init wizard detects and offers to install missing tools.
Eleven built-in rewrite rules back this table. Seven are structured — they parse the command, swap the program, and translate flags: grep, find, cat, curl, ls, du, plus an rg --include → rg -g fixup. Four are standalone rules: ts-node → tsx, the archive tools → ouch, sort | uniq → huniq, and the Windows sd block.
Each rule resolves to one of three outcomes. Teach leaves the command alone and tells the agent the modern equivalent. Apply rewrites the command in place, mapping the flags across. Deny blocks it outright — sd on Windows is the only built-in that does this. ls, du, the rg --include fixup, and plain ts-node invocations apply; the rest teach. Change the default with [rewrite] default_mode, or override one rule:
# ~/.warden/rules.toml
[rewrite]
default_mode = "teach" # or "apply_if_possible"
[[rewrite.rules]]
id = "grep-rg"
mode = "apply_if_possible"
Why Substitutions Exist
AI agents default to the tools they’ve seen most in training data — grep, find, curl, cat. Modern alternatives are faster, produce less output (saving context tokens), and have better defaults for the kinds of searches agents perform.
Warden intercepts the call at the hook level and either rewrites it directly or names the modern equivalent alongside the result. Agents pick the replacement up and keep using it for the rest of the session. This isn’t about tool ideology — it’s about practical outcomes: faster results, less context waste.
Before and After
grep vs rg — a teach rule. The agent searches for a function name across a large codebase:
# What the agent writes:
grep -r "handlePayment" src/
# What Warden attaches to the result:
Structured rewrite [grep-rg]: consider using `rg "handlePayment" src/` instead.
# What the agent runs next:
rg "handlePayment" src/ -t ts
The command still runs; the hint travels back with it. The rg version is faster, respects .gitignore by default (no node_modules noise), and produces a cleaner output format that consumes fewer context tokens. Agents pick up the substitution and keep using it for the rest of the session.
ls vs eza — an apply rule. Nothing is said at all:
# What the agent writes:
ls -la src/
# What actually runs:
eza --long --all src/
Flags are translated by the rule’s flag map — -la to --long --all, -R to --tree, -1 to --oneline. The agent sees only the output.
sd on Windows — a deny rule. The command is blocked and the agent is told what to use instead: the Edit tool, which preserves line endings and encoding.
The rg --include fixup is worth calling out separately: it rewrites rg --include=GLOB to rg -g GLOB, correcting a flag agents routinely carry over from grep.
Availability Check
Substitutions are not unconditional. Before acting on a grep call, Warden checks whether rg is actually installed. If the target tool isn’t available, the rule is skipped and the original command runs untouched — a rewrite to a missing binary breaks the command, and a hint pointing at one is noise.
Availability is probed from PATH and cached, and the cache is re-taken daily.
The warden init wizard detects which modern tools are missing and offers to install them via cargo install or your system package manager. You can also install them manually at any time — Warden will start using them on the next session.
Disabling Specific Substitutions
If a substitution doesn’t fit your workflow, disable it by rule ID in ~/.warden/rules.toml:
[restrictions]
disable = ["substitution.grep"] # Stops redirecting grep → rg
Substitutions are one of the two namespaces a project-level .warden/rules.toml may also disable (the other is advisory.), so a repo can opt out for its whole team.
The ID is substitution. plus the command being replaced. There are eleven registered:
| Rule ID | Substitution |
|---|---|
substitution.grep | grep to rg |
substitution.find | find to fd |
substitution.curl | curl to xh |
substitution.cat | cat to bat |
substitution.ts-node | ts-node to tsx |
substitution.ls | ls to eza |
substitution.sd | sd blocked (Windows) |
substitution.du | du to dust |
substitution.tar | tar, zip, unzip to ouch |
substitution.sort | sort | uniq to huniq |
substitution.rg | rg --include to rg -g |
Run warden --debug restrictions list --category Substitution to print the same list from the binary.
Every ID in the table above is honoured, as are any substitution patterns you add in rules.toml. A substitution is also skipped when its replacement isn’t installed — Warden never rewrites grep to rg on a machine without rg.
Windows Note: sd Is Blocked, Not Substituted
On Windows, sd (the sed replacement) is blocked entirely rather than substituted. It mangles line endings and file encodings on Windows. Warden’s deny message directs the agent to use the Edit tool for file modifications instead, which preserves encoding correctly.