Every tool call passes through the permission system before executing. This gives you fine-grained control over what the agent is allowed to do.

Permission modes

Set the mode via CLI flag or config:

ModeBehavior
ask (default)Prompt before mutations, auto-allow reads
allowAuto-approve everything
denyBlock all mutations
planRead-only tools only (strictest)
accept_editsAuto-approve file edits, ask for shell commands
autoAuto-approve file edits and provably read-only shell commands; ask for everything else
# CLI
agent --permission-mode plan

# Skip all checks (CI/scripting only)
agent --dangerously-skip-permissions

auto mode

auto is the middle ground between "ask for everything" and "approve everything". It removes the prompt for the shell commands that cannot change anything, and keeps it for the ones that can.

Approved without asking

  • File edits, exactly as under accept_edits — and still subject to the protected-directory (.git/, .husky/, node_modules/) and team-memory guards, which run before any mode is consulted.
  • Shell commands that are provably read-only: ls, cat, head, tail, wc, grep/rg, find (without -exec/-delete), diff, stat, echo, pwd, git status/diff/log/show/blame, and the rest of a fixed allowlist of inspection tools.

Still asks

  • Anything that writes: rm, mv, cp, chmod, chown, dd, mkfs, …
  • Anything that reaches the network: curl, wget, ssh, nc, git push, git fetch, npm install, pip install, …
  • Anything that escalates privilege: sudo, doas, su, pkexec.
  • Process and service control: kill, pkill, systemctl, docker run.
  • Output redirection (echo x > file), pipes into a shell (cat f | sh), eval, command substitution ($(…), backticks), subshells, process substitution, variable expansion and inline variable assignments — the gate cannot see what those actually run.
  • Any chain in which any segment is not provably safe. ls && rm -rf / asks; the decision is a conjunction over every segment, never a verdict on the first one.
  • Any command that fails to parse, and any binary not on the allowlist.
  • Every non-shell, non-edit tool: MCP tools, subagents, web fetch/search, cron. There is no allowlist of "probably fine" tools.

auto is not a bypass mode. It relaxes only the default decision. An explicit deny rule still denies and an explicit ask rule still prompts, even for a command auto would otherwise approve. If you want everything approved, that is allow — a separate, deliberate choice. The interactive Shift+Tab cycle (manual → normal → accept-edits → auto → plan) has no always-approve entry by design.

The gate fails closed: a command is auto-approved only when the parser, the destructive-pattern scanner and the effect classifier all agree that every segment is read-only. A command that cannot be positively classified is asked about.

Permission rules

Configure per-tool rules in your settings file:

# .agent/settings.toml or ~/.config/agent-code/config.toml

[permissions]
default_mode = "ask"

# Auto-approve git commands
[[permissions.rules]]
tool = "Bash"
pattern = "git *"
action = "allow"

# Block destructive commands
[[permissions.rules]]
tool = "Bash"
pattern = "rm -rf *"
action = "deny"

# Allow writes only to /tmp
[[permissions.rules]]
tool = "FileWrite"
pattern = "/tmp/*"
action = "allow"

# Deny fetches against an internal host
[[permissions.rules]]
tool = "WebFetch"
pattern = "*internal.example.com*"
action = "deny"

For non-shell tools, pattern is matched against the tool's subject fields (file_path, url, path, pattern, query, prompt). Any present field may match. If none of those fields is present, a patterned rule fails closed: allow does not grant, and deny/ask still apply.

Rules resolve by severity, not by position: deny beats ask, which beats allow. If no rule matches, the default mode applies.

Severity ordering matters because config layers concatenate their rules arrays (every layer's rules stay active), so which rule comes first is not something you can reliably control across ~/.config, .agent/settings.toml and .agent/settings.local.toml. A deny you wrote is never silently outranked by a broader allow from another layer, and a stricter mode from one layer (for example a project-level auto) outranks a looser one (a user-level allow) even when the looser rule appears first.

Shell commands are matched per invocation

A pattern on a Bash rule is matched against each invocation in the command, not against the raw text, and the two directions are deliberately asymmetric:

  • an allow must match every invocation, and refuses outright when the command contains substitutions, subshells, redirection or variable assignment — constructs whose effective text is not knowable at gate time;
  • a deny or ask matches when any invocation matches.

So pattern = "git *" with action = "allow" approves git status, but not git status && rm -rf /. Adding a segment or a wrapper can only ever cost permissions, never grant them.

Built-in safety

Protected directories

Write tools (FileWrite, FileEdit, MultiEdit, NotebookEdit) are blocked from writing to these directories regardless of your permission rules:

  • .git/ — prevent repository corruption
  • .husky/ — prevent hook tampering
  • node_modules/ — prevent dependency modification

Read access to these directories is unaffected.

Shell safety

The Bash tool includes additional safety checks beyond the permission system:

  • Destructive command detection: warns before rm -rf, git reset --hard, DROP TABLE, chmod -R 777, and other dangerous patterns
  • System path blocking: prevents writes to /etc, /usr, /bin, /sbin, /boot, /sys, /proc
  • Output truncation: large outputs are persisted to disk instead of flooding context

Plan mode

Plan mode restricts the agent to read-only operations. Use it when you want the agent to analyze and plan without making changes:

> /plan
Plan mode enabled. Only read-only tools available.

> analyze the architecture and suggest improvements
(agent reads files, searches code, but cannot edit or run commands)

> /plan
Plan mode disabled. All tools available.

Denial tracking

Permission denials are recorded with the tool name, reason, and input summary. View them with /permissions:

> /permissions
Permission mode: Ask
Rules:
  Bash git * -> Allow
  Bash rm * -> Deny