Auto-Approve

Smart permission gate that auto-approves safe tool calls, working-dir edits, and uses an LLM classifier for everything else.

Overview

The auto-approve hook is a PreToolUse hook that reduces permission prompts by classifying tool calls through a 3-step pipeline:

  1. Rule-based — known-safe actions are auto-approved, destructive ones are blocked instantly
  2. Working-dir check — file edits (Write/Edit) inside your project directory are auto-approved
  3. LLM Classifier — everything else is evaluated by Claude Sonnet for a context-aware decision

When the classifier blocks an action, Claude receives the reason and attempts an alternative approach automatically.

Not 100% safe

Auto-approve reduces interruptions but does not guarantee safety. The LLM classifier can make mistakes. We recommend using it in environments where you have version control so you can revert unexpected changes.

Token cost

LLM classifier calls count toward your token usage. The extra cost comes from shell commands, network operations, and MCP tools. Read-only actions and working-dir file edits do not trigger classifier calls.

Enable

Auto-approve is off by default. Enable it in .coding-friend/config.json:

{
  "autoApprove": true
}

Or use the interactive setup: cf config or cf init.

How It Works

┌─────────────────────────┐
│     Tool call arrives    │
└───────────┬─────────────┘
            ▼
┌─────────────────────────┐
│  Step 1: Rule-based     │  ~0ms
│  ┌────────────────────┐ │
│  │ DENY pattern?      │──── ▶ Block immediately
│  │ Read-only tool?    │──── ▶ Allow
│  │ Safe Bash command? │──── ▶ Allow
│  │ Risky Bash prefix? │──── ▶ Ask user
│  └────────────────────┘ │
└───────────┬─────────────┘
            │ not matched
            ▼
┌─────────────────────────┐
│  Step 2: Working-dir    │  ~0ms
│  ┌────────────────────┐ │
│  │ Write/Edit in cwd? │──── ▶ Allow
│  │ Write/Edit outside │──── ▶ Ask user
│  └────────────────────┘ │
└───────────┬─────────────┘
            │ not Write/Edit
            ▼
┌─────────────────────────┐
│  Step 3: LLM Classifier │  ~2-5s
│  (Claude Sonnet)        │
│  ┌────────────────────┐ │
│  │ SAFE               │──── ▶ Allow
│  │ DANGEROUS          │──── ▶ Block (with reason)
│  │ NEEDS_REVIEW       │──── ▶ Ask user
│  │ Error / timeout    │──── ▶ Ask user (fail-open)
│  └────────────────────┘ │
└─────────────────────────┘

Step 1: Rule-Based (instant)

Pattern matching runs first. It covers the vast majority of tool calls with zero latency.

Auto-approved (allow):

ToolCondition
ReadAny file (privacy-block handles sensitive files separately)
GlobAny pattern
GrepAny search
TodoWriteAlways safe
AgentAlways safe
BashRead-only commands: ls, cat, head, tail, wc, file, which, echo, pwd, date, tree, git status, git log, git diff, git branch, git show, git remote, git tag, npm test, npx jest, npx vitest, npx tsc --noEmit (only simple commands — pipes, redirects, and chains are never auto-approved)

Blocked (deny):

ToolCondition
Bashrm -rf / or ~ or $HOME
Bashgit push --force, git push -f, git reset --hard
Bashchmod 777
Bashcurl ... | bash, wget ... | sh
Bashmkfs, dd if=, fork bombs, > /dev/sda
Bashshutdown, reboot, sudo rm
Bashkill -9, pkill

Known risky (ask user):

ToolCondition
Bashgit push (non-force), npm install, npm publish, docker, curl, wget, ssh, scp

Step 2: Working-Dir Check (instant)

File operations (Write and Edit) are checked against your project directory:

  • Inside working directory → auto-approved (no prompt)
  • Outside working directory → requires user confirmation

Path resolution uses path.resolve() to prevent traversal attacks (../../etc/passwd resolves outside cwd and is correctly blocked).

Step 3: LLM Classifier (~2-5s)

Everything not resolved by Steps 1 or 2 goes to the LLM classifier. This includes:

  • Unknown Bash commands (not in allow or deny lists)
  • WebFetch and WebSearch
  • MCP tools
  • Any other unrecognized tool

The classifier runs on Claude Sonnet and returns one of:

ResponseAction
SAFEAuto-approve
DANGEROUSBlock with actionable reason
NEEDS_REVIEWShow permission prompt
Error/timeoutFall back to permission prompt (fail-open)

Feedback loop: When the classifier blocks an action, the reason is returned to Claude so it can try an alternative approach. For example: "Command 'npm run deploy' blocked: may push to production. Try a dry-run flag or a local-only alternative."

Dangerous Rules Audit

When you enable auto-approve, the CLI checks your Claude Code settings for overly broad allow rules that would bypass the classifier:

  • Bash(*) — grants execution of any shell command
  • Bash(python*), Bash(node*), Bash(sh*) — grants arbitrary interpreter execution
  • Bash(npm run*), Bash(npx*) — grants execution of any script
  • Agent(*) — grants unrestricted subagent delegation

These rules are dangerous because they auto-approve commands before the classifier sees them. The CLI offers to remove them automatically. Narrow rules like Bash(npm test *) are kept.

A warning also appears at session start if dangerous rules are detected while auto-approve is active.

Comparison

FeatureAuto-Approve Hook--enable-auto-mode--dangerously-skip-permissions
AvailabilityAll Coding Friend usersTeam/Enterprise onlyAll users
Read-only operationsAuto-approveAuto-approveAuto-approve
Working-dir editsAuto-approveAuto-approveAuto-approve
Dangerous operationsBlock with reasonBlockAuto-approve
Unknown operationsLLM classifier (Sonnet)Sonnet 4.6 classifierAuto-approve
Feedback loopYes — reason returned to ClaudeYesN/A
Latency~0ms (rules) / ~2-5s (LLM)Small overheadNone
Token costLLM calls count toward usageHigherStandard
Dangerous rulesAudited + stripped on activationDropped on enterN/A

Safety Design

  • Fail-open: Any error (malformed input, config issues, LLM failure) results in normal behavior — the permission prompt is shown as usual
  • Deny patterns checked first: Destructive commands are caught before any allow/ask matching
  • Runs after security hooks: privacy-block and scout-block execute first and can block before auto-approve runs
  • No override of deny rules: Even when auto-approve says "allow", deny rules in Claude Code settings still take precedence
  • Path traversal protection: Working-dir check uses path.resolve() to prevent ../ escape attacks
  • Prompt injection defense: LLM classifier prompt wraps tool input in <tool_input> tags with explicit instructions to treat it as data, not instructions