Auto-Approve (LLM)
Remi can route Claude Code permission prompts through a Large Language Model (LLM) and automatically approve, deny, or escalate each request. The goal is to eliminate notification fatigue for routine approvals (file reads, git status, test runs) while keeping human review for anything ambiguous or dangerous.
Auto-approve is disabled by default and fully opt-in.
How it works
When Claude Code asks for permission to run a tool, a hook fires to the Remi daemon. With auto-approve enabled, the daemon:
- Checks your deny list. Any substring match → injects "No" into the PTY, logs
DENIED, no notification. Deny always wins. - Checks your allow list. Any substring match → injects "Yes", no notification, no LLM call.
- Otherwise calls the configured LLM with the tool name and input. The LLM returns one of three decisions:
- approve: injects "Yes" into the PTY, Claude continues, no notification.
- deny: injects "No", Claude is blocked, logs the reason.
- escalate: falls through to the normal flow — the prompt reaches your phone/browser.
If the LLM is unreachable, times out, or returns an unparseable response, the request escalates to you. Errors never silently block Claude.
Supported backends
Auto-approve uses the OpenAI-compatible chat completions API. Any provider that speaks that API works:
| Provider | Default base URL | Notes |
|---|---|---|
| Ollama | http://localhost:11434/v1 | Recommended: fully local, no network calls |
| OpenRouter | https://openrouter.ai/api/v1 | Routes to Claude, GPT, or any hosted model |
| Custom URL | Any OpenAI-compatible endpoint | Self-hosted, vLLM, LiteLLM, etc. |
Quick start
-
Install a local LLM (recommended for privacy):
ollama pull gemma4:e2bModels validated to pass the security test suite:
gemma4:e2bandqwen3.5:4b. -
Run Remi with auto-approve:
remi --auto-approveThe default model is
gemma4:e2bvia Ollama. Override with--auto-approve-model. -
Use Claude Code normally. Routine commands (
git status,ls,bun test) get auto-approved in ~2-6 seconds. Write operations escalate to your phone.
CLI flags
--auto-approve Enable auto-approve (overrides config)
--no-auto-approve Disable
--auto-approve-model MODEL e.g. gemma4:e2b, qwen3.5:4b, anthropic/claude-3-haiku
--auto-approve-provider PROVIDER ollama | openrouter | custom URL
--auto-approve-api-key KEY Required for OpenRouter and hosted providers
--auto-approve-allow STR Substring allow pattern (repeatable, appends to config)
--auto-approve-deny STR Substring deny pattern (repeatable, appends to config)
--auto-approve-instructions TEXT Natural-language guidance appended to the LLM prompt
Config file
Persistent settings go in ~/.remi/config.toml:
[auto_approve]
enabled = true
provider = "ollama"
model = "gemma4:e2b"
api_key = "" # Required for OpenRouter
base_url = "http://localhost:11434/v1"
timeout = 10 # Seconds; escalates on timeout
log_decisions = true # Log every approve/deny to ~/.remi/remi.log
# User-defined rules. Substring matching.
allow = [
"git status",
"git log",
"git diff",
"bun test",
"bunx biome",
"gh issue",
"gh pr",
"Read", "Glob", "Grep", # Tool names (non-Bash tools) match by exact name
]
deny = [
"rm -rf /",
"sudo ", # Trailing space avoids matching "sudoku"
"curl | sh",
"| bash",
]
# Natural-language guidance appended to the LLM's system prompt
instructions = """
Approve all test runs and linting.
Escalate anything touching .env or secrets/.
"""
Environment variables
REMI_AUTO_APPROVE=true|false
REMI_AUTO_APPROVE_MODEL=<model>
REMI_AUTO_APPROVE_PROVIDER=<name>
REMI_AUTO_APPROVE_BASE_URL=<url>
REMI_AUTO_APPROVE_API_KEY=<key>
REMI_AUTO_APPROVE_ALLOW=pat1,pat2,... # Comma- or newline-separated
REMI_AUTO_APPROVE_DENY=pat1,pat2,...
REMI_AUTO_APPROVE_INSTRUCTIONS=<text>
Priority: CLI flag > env var > config file > built-in default.
Pattern syntax
Allow and deny lists
Plain substring match. No glob, no regex.
For Bash:
- Pattern
"git push"matches any Bash command containing the stringgit push— including compound commands likecd /foo && git push origin main. - Trailing space disambiguates prefixes: pattern
"sudo "matchessudo rm -rf /but notsudoku. - Pipe patterns match as substrings:
"| bash"catchescurl ... | bash.
For other tools (Read, Write, Edit, Glob, Grep, WebFetch, etc.):
- Pattern is the exact tool name. Pattern
"Read"matches any invocation of the Read tool. - Tool name is not substring-matched against
file_pathorcontent— only the name.
Design note: substring matching is intentionally permissive. It solves Claude Code's compound-command limitation (where Bash(git push:*) fails on cd /foo && git push). The deny list is your safety rail: any match short-circuits to deny before the LLM is consulted.
Order of evaluation
1. deny list (substring match) → deny, log, no LLM call
2. allow list (substring match) → approve, no LLM call
3. LLM → approve | deny | escalate
└─ error/timeout → escalate
Model requirements
Auto-approve decisions are security-sensitive. Tiny models (under 2B parameters) have been observed to approve dangerous commands. Remi ships a test suite of 38 scenarios covering destructive operations, reverse shells, obfuscation, and data exfiltration. Validated results (2026-04):
| Model | Params | Score | Note |
|---|---|---|---|
qwen3.5:4b | 4.7B | 38/38 | Passes all scenarios |
gemma4:e2b | 5.1B | 38/38 | Default — noticeably faster than qwen3.5 |
qwen3.5:2b | 2.3B | 37/38 | Approved sudo su as "standard admin" — borderline |
qwen3.5:0.8b | 873M | 31/38 | Unsafe — approved fork bomb, reverse shell, SUID escalation |
Use 4B+ parameter models. Smaller models lack the reasoning capacity for security judgment.
Multi-session behavior
Auto-approve is scoped to the main interactive session that owns the Remi daemon's PTY. Events from background subagents and team members are identified by an agent_id field on the hook payload and are dropped before reaching auto-approve or your phone. They never produce phone notifications and never inject 1 into your chat prompt.
If you run multiple Claude Code sessions in different directories, each gets its own Remi daemon and its own auto-approve — they are independent.
Logs
Every decision logs to ~/.remi/remi.log:
[AutoApprove abc12345] Bash: approve (2341ms) - git log is a routine read
[AutoApprove abc12345] Injected "1" into PTY (approved)
[AutoApprove abc12345] DENIED Bash: deny-matched pattern: "rm -rf /"
[AutoApprove abc12345] ERROR Bash: LLM timeout after 10001ms
[Hooks] Dropped subagent PermissionRequest: agent=a1b2c3d4 type=general-purpose tool=Bash
The 8-character tag is the Remi session ID prefix. Grep by tag to isolate one session's activity when multiple daemons are running.
Troubleshooting
No GPU spike / no activity — the daemon may not have locked onto your Claude session. Check for [Hooks] Event bridge active and [Hooks] Transcript from PreToolUse: claude=... in the log. If two Remi daemons run in the same directory, both lock each other out (known limitation).
Auto-approve approves too much — switch to a stricter model, add items to your deny list, or provide tighter instructions.
Auto-approve escalates everything — check that Ollama is running (curl localhost:11434/api/tags) and the model is pulled. Each escalation logs the reason.
Latency is too high — gemma4:e2b is the fastest validated model. You can also try smaller models at the cost of safety (see Model requirements).
Privacy
- Ollama (recommended): all evaluation happens on your machine. Zero network calls.
- OpenRouter or other hosted providers: the tool name and input are sent to the provider. Reconsider for sensitive commands.
The evaluation prompt contains the tool name and raw tool_input (e.g. the full Bash command or file path). It does NOT contain your Claude Code conversation, code contents, or Remi daemon state.