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:
| Mode | Behavior |
|---|---|
ask (default) | Prompt before mutations, auto-allow reads |
allow | Auto-approve everything |
deny | Block all mutations |
plan | Read-only tools only (strictest) |
accept_edits | Auto-approve file edits, ask for shell commands |
auto | Auto-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
allowmust 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
denyoraskmatches 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 tamperingnode_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