Home › Articles

Stop Claude Code, Cursor and Codex asking permission for every command

Updated 2026-11-03

In short: the prompts go away when you tell each tool which actions are routine, not when you turn approvals off. Pre-approve the exact commands you run all day (tests, lint, build, git add/commit), let a sandbox or an auto-review mode handle the rest, and keep pushes, publishes and deletes behind a prompt. Below is the setup for Claude Code, Cursor and Codex, copy-paste configs for each, why an allowlisted command still asks, and a small script that flags allow rules that are broader than they look.

Why the prompts keep coming

All three tools ask by default before the agent runs a shell command it hasn't been cleared for. Each one gives you three ways to cut that down, and they're easy to mix up:

Then there's the "no prompts at all" switch each tool has. That one removes the safety net instead of tuning it, and the docs for all three say to use it only in an isolated environment.

The quick answer for each tool

Claude CodeCursorCodex
Allowlist filepermissions.allow in .claude/settings.json or ~/.claude/settings.jsonterminalAllowlist in .cursor/permissions.json or ~/.cursor/permissions.jsonprefix_rule() in .codex/rules/*.rules or ~/.codex/rules/
Rule formatBash(npm run test *)npm run test (prefix)pattern = ["npm", "install"]
Reviewer modeAuto mode (Shift+Tab)Auto-review (the default since Cursor 3.6)approvals_reviewer = "auto_review", or Approve for me in the app
Sandbox that skips prompts/sandbox → auto-allowAllowlist mode with sandboxing onBuilt in: the default workspace-write sandbox
Still asks anywayask rules, in every modeFile-Deletion and External-File Protectiondecision = "prompt" rules
No-prompts switch (containers only)--dangerously-skip-permissionsRun Everything--yolo

Claude Code

1. Check which mode you're in

Press Shift+Tab to cycle modes and watch the status bar: ⏸ manual mode on asks before most edits and commands, ⏵⏵ accept edits on lets file edits through, and ⏵⏵ auto mode on hands the decisions to a classifier. Since v2.1.283, auto mode is the built-in starting mode for interactive terminal and VS Code sessions. If you still start in Manual, one of these is usually why:

Auto mode isn't a guarantee: the docs warn that it "reduces permission prompts but does not guarantee safety". If file edits are your main source of prompts, acceptEdits is the smaller step. It approves edits and common filesystem commands (mkdir, touch, rm, rmdir, mv, cp, sed) inside your working directory and leaves other commands alone.

2. Allow the commands you run all day

In .claude/settings.json (committed, for the team) or ~/.claude/settings.json (just you):

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run test *)",
      "Bash(npm run build)",
      "Bash(npx tsc --noEmit)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm publish *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)"
    ]
  }
}

How the patterns behave, per the permissions docs:

Choosing "Yes, and don't ask again" on a Bash prompt saves the rule to .claude/settings.local.json at the repo root, so your list grows on its own. A file-edit approval only lasts until the session ends. Run /permissions to see every rule and the file it came from, and /status to check that a settings file loaded (a trailing comma makes the file invalid).

Allow rules in a project's .claude/settings.json only take effect after you accept the workspace trust dialog, which lists them. Deny and ask rules apply right away, since they only restrict.

3. Or let the sandbox approve commands

Run /sandbox and choose auto-allow. Commands then run inside an OS-level boundary (writes limited to the working directory and a temp folder, network through a proxy that asks the first time a command needs a new domain), and a command that runs inside it doesn't prompt. Deny rules, ask rules like Bash(git push *) and rm of critical paths still go through the normal checks. On Linux and WSL2 the sandbox needs bubblewrap and socat, and if it can't start, Claude Code runs commands unsandboxed unless you set sandbox.failIfUnavailable. The sandbox covers shell commands only, not Claude's file tools, MCP servers or hooks.

4. Scripts and CI

claude -p starts in Manual mode, and there's no one to answer a prompt, so calls that need approval are denied. Pass the commands up front, or choose a mode:

claude -p "run the test suite and fix failures" --allowedTools "Bash(npm test *)" "Edit"
claude -p "run the test suite and fix failures" --permission-mode auto

For locked-down CI, --permission-mode dontAsk denies anything that isn't pre-approved. --dangerously-skip-permissions belongs only in a container or VM, run as a non-root user. In that mode allow rules have no effect, deny rules still block, and explicit ask rules still prompt.

Cursor

1. Pick a Run Mode

In Cursor Settings → Agents → Approvals & Execution you get three modes. Auto-review (the default since 3.6) runs allowlisted calls, runs other shell commands in the sandbox where it can, and sends the rest to a classifier. Allowlist runs only what's on your list, plus supported commands in the sandbox if sandboxing is on. Run Everything runs every tool call. The old "Ask Every Time" mode was deprecated in 3.5; Allowlist with an empty list does the same. Cursor says plainly that Auto-review "is not a security boundary", because the classifier can make mistakes either way.

2. Why an allowlisted command still asks

The allowlist works on command prefixes: git covers git status and git push, while git status covers only that. Matching is case-sensitive. A Cursor team member explained on the forum in September 2026 that the list is "matched per command, not per line", so cd api && npm test | tail -20 runs on its own only if cd, npm and tail are all listed. Their suggestions:

Some lines still prompt when every command is listed. In another thread (August 2026), Cursor staff confirmed that a command chaining && into a heredoc can prompt, and that matching is literal, so "$HOME/.dotnet/dotnet" and $HOME/.dotnet/dotnet are different entries. Running the commands separately avoids the first; adding the exact string the agent runs fixes the second.

3. Keep the allowlist in a file

.cursor/permissions.json (the file accepts comments):

{
  // Replaces the in-app terminal allowlist while this key is set.
  "terminalAllowlist": [
    "npm run lint",
    "npm run test",
    "npm run build",
    "npx tsc --noEmit",
    "git add",
    "git commit",
    "cd",
    "ls",
    "head",
    "tail",
    "grep",
    "wc"
  ],
  // Only read in Auto-review mode: steering, not enforcement.
  "autoRun": {
    "block_instructions": [
      "Every git push, npm publish or deploy command should go through approval first."
    ]
  }
}

Some prompts aren't about the allowlist at all. File-Deletion Protection (including rm), External-File Protection (files outside the workspace) and Browser Protection can require approval in any mode. The Cursor CLI has a separate system: Shell(git), Read(...) and Write(...) entries under permissions.allow and permissions.deny in ~/.cursor/cli-config.json or .cursor/cli.json, with deny winning.

Codex

1. Know what the default already allows

Codex's default Auto preset is --sandbox workspace-write --ask-for-approval on-request. Codex reads files, makes edits and runs commands in the workspace without asking. It asks when a command needs the network (off by default) or wants to write outside the workspace. So if Codex keeps asking, the question is usually which boundary the command crosses.

2. Widen the boundary, narrowly

# .codex/rules/default.rules (or ~/.codex/rules/default.rules)
# Rules decide what may run OUTSIDE the sandbox. Commands that fit inside it don't ask.

prefix_rule(
    pattern = ["npm", "install"],
    decision = "allow",
    justification = "Installing packages needs the network",
    match = ["npm install", "npm install zod"],
    not_match = ["npm publish", "npx npm install"],
)

prefix_rule(
    pattern = ["gh", "pr", ["view", "checks", "diff"]],
    decision = "allow",
    justification = "Reading pull requests is fine",
    match = ["gh pr view 42", "gh pr checks 42"],
    not_match = ["gh pr merge 42", "gh issue view 42"],
)

prefix_rule(
    pattern = ["git", "push"],
    decision = "prompt",
    justification = "Pushes are shared; ask first",
    match = ["git push", "git push origin main"],
)

prefix_rule(
    pattern = ["git", "reset", "--hard"],
    decision = "forbidden",
    justification = "Use git stash or a new branch instead of reset --hard",
    match = ["git reset --hard HEAD~1"],
)

Each rule matches a command prefix (a list inside the list means "any of these"), and when several rules match, the strictest decision wins: forbidden, then prompt, then allow. The match and not_match lines are tests Codex checks when it loads the file. When we got one wrong, codex execpolicy check refused to load the file. Test a rule before you rely on it:

codex execpolicy check --pretty --rules .codex/rules/default.rules -- npm install zod

Codex splits bash -lc "a && b" into separate commands only when the script is plain words joined by &&, ||, ; or |. Redirects, variables, wildcards or substitutions make it check the whole script as one command, so an allow rule for git add never lets git add . && rm -rf / through. Clicking "allow" in the terminal UI writes a rule to ~/.codex/rules/default.rules, and project rules load only when the project is trusted. Rules are still marked experimental.

3. Let a reviewer answer instead of you

approval_policy = "on-request"
approvals_reviewer = "auto_review"

In the ChatGPT desktop app this is Approve for me. The docs describe it as "a reviewer swap, not a permission grant": the sandbox stays the same, and only requests that already need approval go to the reviewer agent. It uses extra model calls, and in the current implementation it stops the turn after 3 denials in a row or 10 in the last 50 reviews. The docs' advice for a noisy reviewer is to fix the boundary first with writable_roots and precise prefixes like ["cargo", "test"], "over broad patterns such as ["python"] or ["curl"]".

-a never turns prompts off but keeps the sandbox. --yolo (--dangerously-bypass-approvals-and-sandbox) turns off both, and the docs suggest it only inside a container that is itself the boundary. If an old config still says approval_policy = "untrusted", remove it: that value is retired and can stop Codex from starting.

Broad rules that look narrow

RuleWhy it's broader than it looksInstead
Bash(git *), Cursor gitCovers git push, reset --hard and clean -fOne rule per subcommand you want: git add, git commit
python, node, bash -cAn interpreter runs any code you give itThe exact script: python3 manage.py test
npx, docker run, devbox runRuns whatever comes after itThe full command: npx tsc --noEmit
curl, wgetCan send files and secrets anywhereLeave them prompting, or use the sandbox's domain allowlist
Bash (no parentheses)Every commandUse a sandbox or a reviewer mode, plus a guard hook

If you do allow Bash wholesale, Claude Code's docs suggest pairing it with a PreToolUse hook that rejects the commands you never want, because a hook that exits 2 blocks before allow rules are checked. We built one for all three tools in hooks in Claude Code, Cursor and Codex. Allow rules also don't stop the agent reading secrets with an allowed command like grep; for that, see how to stop Claude Code, Cursor and Codex reading your .env file.

Audit what you've already approved

"Don't ask again" clicks pile up. This script reads the allow rules from all three tools for the current repo and your home folder, and flags the ones that cover a whole program, an interpreter or any command. It exits 1 when it finds something, so it can run in CI or a pre-commit hook. Python 3.11+ only, no dependencies:

#!/usr/bin/env python3
"""List the auto-approve rules Claude Code, Cursor and Codex will use in this repo and flag the broad ones.
Run anywhere in the repo: python3 audit_allowlists.py  (exit 1 if anything is flagged)"""
import json, pathlib, re, subprocess, sys, tomllib

git = subprocess.run(["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True)
H, P = pathlib.Path.home(), pathlib.Path(git.stdout.strip() if git.returncode == 0 else ".")
RUNS_ANYTHING = {"bash", "sh", "zsh", "fish", "pwsh", "python", "python3", "node", "deno", "bun", "ruby", "perl",
                 "php", "eval", "exec", "sudo", "env", "xargs", "npx", "bunx", "uvx", "docker", "ssh", "curl", "wget"}
PUSHES = {"git", "gh", "npm", "pnpm", "yarn", "rm", "kubectl", "terraform", "aws", "gcloud", "az", "vercel"}
flags = []

def check(where, rule, words, wildcard=True):
    """words = the fixed words before any wildcard; wildcard = the rule also covers longer commands."""
    if not words:
        flags.append(f"{where}: {rule}  -> matches any program")
    elif not wildcard:
        return  # an exact command is as narrow as it gets
    elif len(words) == 1 and words[0] in RUNS_ANYTHING | PUSHES:
        flags.append(f"{where}: {rule}  -> covers every '{words[0]}' command")
    elif words[0] in RUNS_ANYTHING and words[1] in ("-c", "-lc", "-e", "eval", "exec", "run"):
        flags.append(f"{where}: {rule}  -> runs any code")

def cursor_words(rule):  # "npm:install*" = base command "npm" + args glob "install*"
    base, _, args = rule.partition(":")
    return base.split() + args.split("*")[0].split()

def load_json(path):
    text = re.sub(r"^\s*//.*$", "", path.read_text(), flags=re.M)  # Cursor files are JSONC
    try:
        return json.loads(text)
    except ValueError as e:
        flags.append(f"{path}: not valid JSON ({e}), so the tool may ignore it")
        return {}

for f in (P / ".claude/settings.json", P / ".claude/settings.local.json", H / ".claude/settings.json"):
    if not f.is_file(): continue
    perms = load_json(f).get("permissions", {})
    if perms.get("defaultMode") == "bypassPermissions": flags.append(f"{f}: defaultMode bypassPermissions")
    for r in perms.get("allow", []):
        m = re.fullmatch(r"Bash(?:\((.*)\))?", r)
        if not m: continue
        spec = (m.group(1) or "*").replace(":*", " *")
        check(f, r, spec.split("*")[0].split(), "*" in spec)

for f in (P / ".cursor/permissions.json", H / ".cursor/permissions.json"):
    if f.is_file():
        for r in load_json(f).get("terminalAllowlist", []):
            check(f, r, cursor_words(r))
for f in (P / ".cursor/cli.json", H / ".cursor/cli-config.json"):
    if f.is_file():
        for r in load_json(f).get("permissions", {}).get("allow", []):
            m = re.fullmatch(r"Shell\((.*)\)", r)
            if m: check(f, r, cursor_words(m.group(1)))

for d in (P / ".codex/rules", H / ".codex/rules"):
    for f in sorted(d.glob("*.rules")) if d.is_dir() else []:
        for block in f.read_text().split("prefix_rule(")[1:]:
            pat = re.search(r"pattern\s*=\s*\[(.*?)\]\s*[,)]", block, re.S)
            dec = re.search(r'decision\s*=\s*"(\w+)"', block)
            if pat and (dec.group(1) if dec else "allow") == "allow":
                words = re.findall(r'"([^"]*)"', pat.group(1).split("[")[0])
                check(f, f"prefix_rule{words}", words)
for f in (P / ".codex/config.toml", H / ".codex/config.toml"):
    if f.is_file():
        try:
            cfg = tomllib.loads(f.read_text())
        except tomllib.TOMLDecodeError as e:
            cfg = {}; flags.append(f"{f}: not valid TOML ({e})")
        if cfg.get("sandbox_mode") == "danger-full-access" and cfg.get("approval_policy") == "never":
            flags.append(f"{f}: no sandbox and no approvals")

print("\n".join(flags) or "No broad auto-approve rules found.")
sys.exit(1 if flags else 0)

It's a pattern check, not a parser for each tool's full syntax, and it doesn't look at managed or team settings. Edit RUNS_ANYTHING and PUSHES for your stack. We tested it on sample repos: a narrow config like the ones above (no flags), a deliberately broad one with 16 risky entries across the three tools (all 16 flagged), rules in a home folder, a run from a subfolder, and broken JSON and TOML files. We tested the Codex rules file with the Codex CLI's own execpolicy check. The Claude Code and Cursor files were checked as valid JSON but not loaded in those tools.

A fifteen-minute setup

  1. Pick the middle-ground mode: auto mode or /sandbox auto-allow in Claude Code, Auto-review in Cursor, the default Auto preset in Codex.
  2. Write down the five to ten commands the agent runs every session (test, lint, typecheck, build, git add, git commit) and add them as exact or subcommand-level rules.
  3. Add an ask/prompt rule for git push and your publish or deploy command. Claude Code ask rules prompt even in auto mode.
  4. Commit the project files (.claude/settings.json, .cursor/permissions.json, .codex/rules/) so the team and every worktree start the same way, and keep personal extras in your home folder.
  5. Run the audit script, then work for a day and add rules only for prompts that come back.

FAQ

How do I stop Claude Code asking for permission every time?

Add allow rules for the commands you run all the time, such as "Bash(npm run test *)", to .claude/settings.json or ~/.claude/settings.json, and use acceptEdits mode if file edits are what keeps asking. For fewer prompts across the board, use auto mode (a classifier reviews actions instead of you) or turn on the Bash sandbox with /sandbox and pick auto-allow. Keep an ask rule for git push: ask rules prompt in every mode.

Is --dangerously-skip-permissions safe?

Only in an isolated container or VM. It starts Claude Code in bypassPermissions mode, which skips prompts except for a short list of actions no mode auto-approves. Deny rules still block, but allow rules stop mattering because everything else runs. Codex's equivalent, --dangerously-bypass-approvals-and-sandbox (alias --yolo), turns off both the sandbox and approvals.

Why does Cursor still ask for a command that's on my allowlist?

The allowlist is matched per command, not per line, so every command in a chain has to be on it: cd api && npm test | tail -20 needs cd, npm and tail. Cursor staff also confirmed in August 2026 that a heredoc chained after && can still prompt. Open the Always run option on the approval card to see which commands are missing, or ask the agent to run one command per call.

Why does Codex ask before npm install or curl?

Codex's default Auto preset runs commands inside a workspace-write sandbox with no network access, and asks before anything that needs the network or writes outside the workspace. Turn on network_access under [sandbox_workspace_write] if your work needs it, or add a narrow prefix_rule in .codex/rules that allows a specific command outside the sandbox.

Can a repo I clone pre-approve commands for me?

In Claude Code, allow rules in a project's .claude/settings.json apply only after you accept the workspace trust dialog, which lists them. Codex loads a project's .codex/rules only when the project is trusted. Cursor concatenates a repo's .cursor/permissions.json allowlist with yours, and its docs don't describe a trust step for that file, so read it before you work in an unfamiliar repo.

Sources

Official docs, checked 2026-11-03.

Get the free rules sampler + new tips by email

Confirm your email and we'll send you straight to the sampler download. Then one short email every two weeks covering tool format changes (Cursor, Claude Code, Codex, Windsurf, Copilot), new rules and kit updates. No spam.

We store only your email, with our email provider Resend. See the privacy policy.

Unsubscribe anytime from any email. Rather skip email? Get the sampler on GitHub or download the zip directly.