Sane defaults
A middle ground between asking every time and skipping every check.
Out of the box your agent asks before it edits a file or runs a command. That is safe, but chatty over text, where every prompt is a buzz on your phone. At the other end, yolo mode skips every check. Sane defaults sit in between: auto-approve the safe, everyday work and keep a gate on the rest. They are opt-in, and you can widen or narrow them whenever you like.
The idea
Auto-allow the safe majority. Gate the risky minority. The working directory is treated as the sandbox: writing inside it is fine, and the moment anything reaches outside, the gate returns.
| Goes through | Still asks |
|---|---|
| Reading and navigating files | Writing or reading outside the working directory |
| Editing files inside the working directory | Arbitrary shell commands |
Read-only shell commands (ls, cat, grep, find…) | Deleting, moving, or overwriting files |
| Web search and fetch | Reading secrets (.env, ssh and cloud keys): denied outright |
OpenCode
OpenCode reads its permissions from opencode.json. Reads, globs, and greps are already free; this block opens up in-directory edits and a read-only shell list, and keeps the gate on everything else:
{
"permission": {
"edit": "allow", // writes inside the project, no prompt
"external_directory": "ask", // anything outside the project stops and asks
"webfetch": "allow",
"bash": {
"*": "ask", // unknown commands ask
"ls": "allow", "ls *": "allow",
"cat *": "allow",
"grep *": "allow", "find *": "allow",
"head *": "allow", "tail *": "allow",
"wc *": "allow", "stat *": "allow",
"pwd": "allow", "which *": "allow"
}
}
}
edit: allow lets the agent write and edit files in the project without tapping you. external_directory: ask is the fence: a write or read that lands outside the project brings back the prompt. The bash map pre-approves read-only commands so routine poking around (checking a path exists, grepping a file) runs quietly, while everything else still asks.
A safelisted command only clears when the whole line qualifies. Chain something onto it and the gate comes back: ls .; rm -rf build still stops for approval, because rm is not on the list. The list is read-only on purpose; nothing that deletes, moves, or overwrites belongs on it.
Claude Code
Claude Code reads .claude/settings.json. Same shape, different syntax: allow the read-only tools and in-project editing, deny the secrets, and leave Bash asking except for a read-only handful.
{
"permissions": {
"allow": [
"Read", "Grep", "Glob", "Edit", "Write", "WebFetch", "WebSearch",
"Bash(ls:*)", "Bash(cat:*)", "Bash(grep:*)", "Bash(find:*)",
"Bash(head:*)", "Bash(tail:*)", "Bash(pwd)", "Bash(which:*)"
],
"ask": ["Bash"],
"deny": [
"Read(./.env)", "Read(./**/.env*)",
"Read(~/.ssh/**)", "Read(~/.aws/**)"
]
}
}
Claude Code checks each part of a chained command on its own, so the same guarantee holds: a read-only allow cannot carry a write past the gate.
The other end: yolo
Starting with -yolo (Claude Code's --dangerously-skip-permissions) drops every prompt. That is the right call for a throwaway sandbox where nothing on the box matters. It is the wrong call anywhere real credentials live, because a single bad instruction has nothing left to stop it. Sane defaults exist so you rarely need to reach for it. See Security for the full threat model.