{"slug": "deterministic-rule-based-auto-approver-for-claude-codex", "title": "Deterministic Rule based auto-approver for Claude/Codex", "summary": "Anumati, a deterministic auto-approver for AI coding agents, now supports both Claude Code and OpenAI Codex, automatically allowing safe shell commands like git status, npx tsc, and cargo test while prompting for risky ones. The tool uses explicit matchers with a strict grammar, is allow-only, and splits commands at top-level operators to ensure every sub-command is safe. It offers self-building config via suggestions and can be installed via npm or npx.", "body_md": "**Stop approving the same Bash commands over and over.** anumati is a\ndeterministic **auto-approver** for AI coding agents. From a small config of\n**named matchers** it auto-allows *safe* shell commands — so `git status`,\n`npx tsc`, `cargo test`, `jq`, and friends just run, while genuinely risky\ncommands still get a prompt. It's **agent-agnostic**: one shared config drives\nboth [Claude Code](https://code.claude.com) (via its `PreToolUse` hook) and\n[OpenAI Codex](https://developers.openai.com/codex) (via its `PermissionRequest`\nhook).\n\nWhen something falls through, anumati tells you the exact one-liner to allow it\nnext time — so your config **builds itself from real usage**.\n\n``` bash\n$ git status && cargo test | tail -20\n  ✓ auto-approved (no prompt)\n\n$ terraform apply\n  ⤳ prompt shown  ·  💡 anumati: no matcher covers \"terraform\"\n```\n\n- **Deterministic, not vibes.** Approval is decided by explicit matchers with a\nstrict grammar — not an LLM guessing whether a command is safe. The same\ncommand always gets the same answer.\n- **Safe by construction.** Matchers allow only read-only / build / test shapes.\nRedirects that write files,`$(...)` substitution, network`curl` to unlisted\ndomains,`git push` ,`rm` — all fall through to a real prompt. anumati is**allow-only** : it can approve a call or step aside, but never blocks anything\nitself, and never widens what the agent would otherwise refuse.\n- **Composes across a whole command line.**`git status && cargo build | tail` is approved only if*every* piece is independently safe — you can't smuggle`rm -rf /` in by chaining it onto an allowed command.\n- **Self-building config.** Every passthrough comes with a verified suggestion\n(`anumati add …` ) and a logged reason, so you extend coverage from what you\nactually run.\n- **Bash-only by design.** anumati vets`Bash` — the hard problem.`Read` /`Write` /`Edit` stay with the agent's own permission flow.\n\nEvery time the agent is about to run a **Bash** command, anumati checks it\nagainst your allow rules:\n\n1. **A rule matches** → auto-approved, no prompt.\n2. **No rule matches** → the agent shows its normal permission prompt, and\nanumati prints a 💡 suggestion for allowing it next time.\n\nA command is approved one of two ways: a single matcher accepts the whole thing,\nor — failing that — anumati splits it at top-level `&&`, `;`, `||`, `&`, and\nnewlines and approves only if **every** sub-command is independently accepted.\n\n``` php\nflowchart TD\n    A[Bash command] --> B{\"A single rule's matcher<br/>accepts the whole command?\"}\n    B -- yes --> ALLOW([✅ allow])\n    B -- no --> D[\"Split at top-level && ; || & and newlines<br/>pipes stay glued to their segment\"]\n    D --> F[\"For each sub-command:<br/>does some rule accept it?\"]\n    F --> G{\"Every sub-command<br/>approved?\"}\n    G -- yes --> ALLOW\n    G -- no --> PASS([⤳ passthrough])\n```\n\nA disallowed sub-command always fails its own check, so chaining a bad command\nonto a good one can't sneak it through. Pipes are never split across rules (a\npipe feeds data into the next command, so only the matcher owning the pipeline\ncan judge it). Configs cascade: a project config at\n`<cwd>/.anumati/permissions.json` is checked before your global\n`~/.anumati/permissions.json` (the legacy `~/.claude/` locations are still\nhonored, so existing setups keep working).\n\nThe full model — matchers, composition rules, and safety guarantees — is in\n[`docs/CONFIGURATION.md`](/adityamatt/anumati/blob/main/docs/CONFIGURATION.md).\n\n```\nnpm install -g anumati\n```\n\nOr run without installing via `npx anumati ~/.claude/permissions.json`.\n\nUpdate to the latest version any time with `anumati update` (it checks the\npublished version and, if newer, runs the global reinstall for you; the hook\npicks it up on the next command). `anumati update --check` just reports whether\na newer version exists.\n\nOne command sets up everything:\n\n```\nanumati init\n```\n\nIt prompts for **which agent(s)** (Claude Code / Codex / both) and **project**\n(this folder) or **root** (global) scope, then:\n\n1. **Writes a starter config** of broadly-useful, low-risk rules — read-only\ninspection, git reads,`cd` /`sleep` /`echo` /`sed` /`jq` ,`npx tsc` ,`cargo` ,`go` , test runners (`vitest` /`pytest` /`jest` ), and pure-compute`python3` /`node` . Enough to be useful immediately. It also seeds the\nparameterized matchers (`curl` ,`gh` ,`pip3-install` ,`git-write` ,`git-push` ,`node-script` ) as**inert placeholders** — empty allowlists that\napprove nothing until you fill them in, so you can see the matcher exists and\nwhich key to populate (`anumati add <matcher> …` ) without hunting the docs.\n2. **Scaffolds an audit log** next to the config.\n3. **Wires the chosen agent(s)** , merged non-destructively — Claude Code's`PreToolUse` hook in`settings.json` , and/or Codex's`PermissionRequest` hook\nin`~/.codex/hooks.json` .\n4. **Adds a SessionStart banner** (\"⚡ anumati active\") so you can see it's on.\n5. **Writes command-style guidance** to the sibling`CLAUDE.md` , nudging the\nagent to emit approvable commands.\n\n**Reload the agent** for it to take effect (Claude Code: `/hooks` or restart;\nCodex: approve the anumati hook once when it prompts to review it). Then just\nwork — routine commands stop prompting, and when something new falls through\nyou'll see a `💡 anumati add …` suggestion.\n\nGrow your config as you go:\n\n```\nanumati add curl --domain api.github.com   # allow curl to a domain\nanumati add git-write --git-ops add,commit  # allow specific git writes\nanumati stats                               # see your auto-approve rate\nanumati apply --all                         # apply accumulated suggestions\n```\n\n- **[Configuration reference](/adityamatt/anumati/blob/main/docs/CONFIGURATION.md)** — every matcher, rule\nfield, audit/sound/debug option, and CLI subcommand.\n- **[Claude Code + Codex](/adityamatt/anumati/blob/main/docs/MULTI-AGENT.md)** — running anumati across both\nagents from one shared config: per-agent wiring, the Codex integration, and`anumati migrate` .\n- **[Command-style guide](/adityamatt/anumati/blob/main/docs/COMMAND-STYLE.md)** — how to write commands that\nland on the auto-approve path (also installed into`CLAUDE.md` by`init` ).\n\nSee [`CONTRIBUTING.md`](/adityamatt/anumati/blob/main/CONTRIBUTING.md) for local setup — including running\nClaude Code against your local build via `npm link` — plus how to test, add a\nmatcher, and open a PR.\n\nMIT", "url": "https://wpnews.pro/news/deterministic-rule-based-auto-approver-for-claude-codex", "canonical_source": "https://github.com/adityamatt/anumati", "published_at": "2026-09-08 11:00:53+00:00", "updated_at": "2026-09-08 11:33:20.398078+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents", "ai-safety"], "entities": ["anumati", "Claude Code", "OpenAI Codex", "npm", "npx"], "alternates": {"html": "https://wpnews.pro/news/deterministic-rule-based-auto-approver-for-claude-codex", "markdown": "https://wpnews.pro/news/deterministic-rule-based-auto-approver-for-claude-codex.md", "text": "https://wpnews.pro/news/deterministic-rule-based-auto-approver-for-claude-codex.txt", "jsonld": "https://wpnews.pro/news/deterministic-rule-based-auto-approver-for-claude-codex.jsonld"}}