Permissions and Context in Gemini CLI and Claude Code
TL;DR: Gemini CLI splits permissions into an approval mode plus a policy engine, where TOML rules win by numeric priority. Claude Code uses one JSON rule list evaluated deny, then ask, then allow, and rule specificity never changes that order. Both read a markdown memory file named GEMINI.md or CLAUDE.md, but Claude concatenates parent directories all the way up to the filesystem root while Gemini scans the workspace and loads subdirectory files just in time. Everything below is sourced from official docs or measured on this machine.
What I actually tested
The version numbers are not a vague "latest". They come from --version after a local npm install on 2026-10-18:
- gemini: 0.64.0 (
@google/gemini-cli@0.64.0, engines requirenode >=20) - claude: 2.1.302 (Claude Code) (
@anthropic-ai/claude-code@2.1.302)
The machine is Windows 11 with Node v24.15.0. The two packages install at 94.0 MB (449 files) and 276.9 MB, about 290 million bytes across 7 files. gemini --help is 3,989 characters with 29 options; claude --help is 22,102 characters with 78, roughly 2.7x and 170% more. That gap alone tells you how the two projects think.
One caveat up front: I had no working Gemini or Anthropic API key, so this says nothing about model quality. I tested what I could verify without one: whether the binary starts, how config gets parsed, and what the failure output looks like. Every exit code below is a number this machine produced.
Layer one: who is allowed to touch your files
Gemini CLI starts with approval modes. --approval-mode accepts exactly four values, quoted from the 0.64.0 help text: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools), and plan (read-only mode).
Here is the first trap: you cannot set yolo in settings.json. The settings reference lists general.defaultApprovalMode, but the validator answers with Invalid enum value. Expected 'default' | 'auto_edit' | 'plan', received 'yolo' (exit code 1). Blanket approval has to come from the command line as --yolo or --approval-mode=yolo. To make it impossible instead, set "security": {"disableYoloMode": true}; in the shipped code that flag is OR-ed with admin.secureModeEnabled.
Claude Code's equivalent is the permission mode, with six values: default, acceptEdits, plan, auto, dontAsk, bypassPermissions. The docs describe default as "Prompts for permission on first use of each tool", label it Manual in the CLI, and accept manual as an alias.
There is a version-sensitive gotcha here. The settings page states that auto and bypassPermissions "don't take effect from project or local settings; set them in user or managed settings instead, or pass --permission-mode for one session", and adds: "Before v2.1.257, bypassPermissions took effect from any file." So a bypassPermissions default you committed to a project settings file kept working before 2.1.257 and silently stops after. To block the modes outright, set permissions.disableBypassPermissionsMode or permissions.disableAutoMode to "disable".
Layer two: where rules live and which one wins
This is where the two designs part company.
Gemini CLI rules are TOML, dropped into ~/.gemini/policies/*.toml:
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git"
decision = "ask_user"
priority = 100
Conflicts resolve by priority: final_priority = tier_base + (toml_priority / 1000), with tiers ordered Default(1), Extension(2), Workspace(3), User(4), Admin(5).
⚠️ The official page contradicts itself here, so I am reporting it rather than picking a side: the tier table gives Admin base 5 and User base 4, but the worked examples lower in the same page say "A priority: 100 rule in a User policy TOML becomes 3.100" and "A priority: 20 rule in an Admin policy TOML becomes 4.020", and the admin section calls Admin "Tier 4". Three different stories. If you are rolling this out centrally, write one high-priority rule and measure it instead of trusting the numbers.
More important is the warning printed on that page: the Workspace tier is currently broken. The docs say .gemini/policies in a workspace is "currently non-functional" and "will not have any effect", pointing at issue #18186. Per-repository policy has to land in the User or Admin tier for now.
Claude Code rules are JSON inside settings.json:
{
"permissions": {
"allow": ["Bash(npm run test *)"],
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}
The order is fixed. From the docs: "Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn't change the order." A broad Bash(aws *) deny swallows a narrow Bash(aws s3 ls) allow. The same holds across scopes: a user-level deny beats a project-level allow, because deny from any scope is evaluated before allow.
If you remember one line, make it this: Gemini decides by "whoever holds the bigger number", Claude decides by "deny is always biggest". The first is flexible and makes you do arithmetic; the second is rigid and hard to get wrong.
Three more details worth carrying:
1. A global Gemini deny (one without argsPattern) does something useful: the docs say the tool is "completely excluded from the model's memory", so the model never even sees it as an option, which also saves context.
2. Claude checks file permissions against Edit(path) and Read(path) rules only. A path rule written for Write(docs/) is accepted but never consulted, and warns at startup. Use Edit(docs/).
3. Wildcard placement matters in Claude: Bash(git *) allows every git command, Bash(git log *) only allows git log. The docs put it as "Put the * after the subcommand."
Trust works differently too. In Claude, project .claude/settings.json allow rules apply only after you accept the workspace trust dialog, while deny and ask apply right away. Gemini's folder trust decides whether headless mode starts at all, which is error two below.
Context files: same idea, different reach
Both tools read a markdown memory file, GEMINI.md and CLAUDE.md by default. Gemini lets you rename it through context.fileName, which takes an array; Claude also reads AGENTS.md. The loading rules differ more than you would expect:
| Gemini CLI | Claude Code | |
|---|---|---|
| Global | ~/.gemini/GEMINI.md | ~/.claude/CLAUDE.md |
| Project | GEMINI.md in workspace and parent dirs | ./CLAUDE.md or ./.claude/CLAUDE.md |
| Private | per-project private memory folder | ./CLAUDE.local.md (gitignored) |
| Rules dir | none | .claude/rules/**.md, ~/.claude/rules/ discovered recursively |
| Org-wide | admin system directories | managed CLAUDE.md (C:\Program Files\ClaudeCode\CLAUDE.md on Windows) |
| On demand | scans a directory and its ancestors when a tool touches it (JIT) | subdirectory files load once Claude reads or edits there |
| Imports | @file.md | @path, recursive up to four hops |
Two differences are easy to miss:
- Parent concatenation: Claude states that content is "ordered from the filesystem root down to your working directory", so notes in
~/work/CLAUDE.mdfollow you into~/work/proj. Gemini describes its hierarchy as global, then workspace and parent directories, then JIT, and I would verify the real behavior on the target machine before relying on it. - Size ceiling: Claude documents "loads a CLAUDE.md file of up to 4 MiB in full and skips a larger file", plus the advice that "Shorter files produce better adherence". I found no equivalent number in the Gemini docs, so I am not inventing one.
Automatic memory shows the same split in philosophy. Claude's auto memory loads the first 200 lines or 25KB of MEMORY.md at session start, whichever comes first. Gemini's Auto Memory is marked experimental and off by default: you enable experimental.autoMemory, and even then a session qualifies only after sitting idle for at least three hours with at least 10 user messages, and candidates go to a review inbox that never touches a memory file without your approval. One product remembers for you by default; the other refuses to until you say yes.
Three earlier posts on this site go deeper on the context side: how much of the 200K context window you actually get, where the 65K token headroom goes, and how to split work between CLAUDE.md and skills.
Troubleshooting: seven errors I reproduced
Error one: exit code 41, no auth method configured
Please set an Auth method in your /.gemini/settings.json or specify one of the following environment variables before running: GEMINI_API_KEY, GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_GENAI_USE_GCA
GEMINI_API_KEY was empty and security.auth was absent. Set security.auth.selectedType in your user settings or export the key. Do not put it in the project .gemini/settings.json, because folder trust can lock that file out entirely (next error).
Error two: exit code 55, workspace not trusted in headless
Gemini CLI is not running in a trusted directory. To proceed, either use `--skip-trust`, set the `GEMINI_CLI_TRUST_WORKSPACE=true` environment variable, or trust this directory in interactive mode.
This is the one that stops CI pipelines. I tested all three fixes: pass --skip-trust, set GEMINI_CLI_TRUST_WORKSPACE=true, or set "security": {"folderTrust": {"enabled": true}} in user settings and register the directory.
⚠️ The docs disagree with themselves here, and I confirmed the real behavior: the trusted folders page says the feature "is disabled by default", the settings table lists security.folderTrust.enabled with a default of true, and the shipped isFolderTrustEnabled reads settings.security?.folderTrust?.enabled ?? true. On a clean directory with no settings at all, -p produced exit code 55; after setting folderTrust to false explicitly, the CLI moved past trust into the auth stage. Trust is on by default and the trusted folders page is stale. In CI, never lean on the default: pass --skip-trust or the env var and an upgrade cannot surprise you.
Error three: exit code 52, broken settings.json
Error in /.gemini/settings.json: Expected double-quoted property name in JSON at position 44 (line 1 column 45)
Please fix the configuration file(s) and try again.
Harder than the Claude equivalent: the CLI refuses to start. The message also gives a character offset rather than a line you can jump to, so run python -m json.tool on the file first.
Error four: trying to default to YOLO in settings.json
Invalid configuration in /.gemini/settings.json:
Error in: general.defaultApprovalMode
Invalid enum value. Expected 'default' | 'auto_edit' | 'plan', received 'yolo'
Exit code 1, and it keeps going after printing this. yolo only exists as a command-line value. Use --approval-mode=yolo for a session, or security.disableYoloMode: true to forbid it.
Error five: silent denials in CI
Not an error string but a semantic trap. The policy engine docs say ask_user decisions are treated as deny in non-interactive mode. Whatever you let the model ask you about locally turns into a flat refusal under -p, and the log may not explain why.
Fix it by shipping explicit allow rules in the User or Admin tier, or by writing commandPrefix rules that override the default ask_user for specific commands. Do not fall back on --yolo: with security.disableYoloMode on, that path drops to default.
Error six: a wrong permissions type voids the whole Claude settings file
Not logged in · Please run /login
These settings files did not load, so nothing in them is in effect, including hooks and permission rules. Each loads again once the problem listed for it is fixed:
/claude/settings.json (permissions.allow): Expected array, but received string
I wrote permissions.allow as a string. Note the blast radius: hooks stop working too, not just permissions. Make it an array and the file loads again. When something behaves as if your rules vanished, read this block before anything else.
Error seven: the two home directories collide
I first set only GEMINI_CLI_HOME and Claude kept writing into ~/.claude. Redirect both:
$env:GEMINI_CLI_HOME = "D:\tmp\gcli\home"
$env:CLAUDE_CONFIG_DIR = "D:\tmp\gcli\home\claude"
First-run footprint differs: Gemini writes .gemini/projects.json (72 bytes) plus .project_root markers under history/ and tmp/. Claude writes .claude.json (719 bytes, including a machineID and userID), a backups/.claude.json.backup., and session transcripts at projects/ around 80 KB each. Budget for that if you cache or sandbox the home directory.
Migration cheat sheet
| Claude Code | Gemini CLI |
|---|---|
permissions.allow: ["Bash(npm test *)"] | TOML with toolName="run_shell_command", commandPrefix="npm test", decision="allow" |
permissions.deny: ["Read(./.env)"] | same rule with decision="deny" (a global deny also hides the tool from the model) |
--dangerously-skip-permissions | --yolo or --approval-mode=yolo |
--permission-mode plan | --approval-mode plan |
permissions.defaultMode | general.defaultApprovalMode (rejects yolo) |
| managed settings | Admin tier policies plus system settings (C:\ProgramData\gemini-cli\policies on Windows) |
--add-dir | --include-directories |
project .claude/settings.json | project .gemini/settings.json (ignored when the folder is untrusted) |
CLAUDE_CONFIG_DIR | GEMINI_CLI_HOME |
FAQ
Q: Can both run on one machine?
A: Yes, that is how I tested them. Keep the home directories apart (error seven) or debugging config turns into guesswork.
Q: Which one supports per-repository policy today?
A: Claude, via .claude/settings.json checked into the repo, with allow rules waiting on workspace trust. Gemini's Workspace tier is documented as non-functional (issue #18186), so use User or Admin.
Q: How do I make a tool invisible to the model?
A: In Gemini, write a deny rule without argsPattern; the docs say it is removed from the model's memory entirely. In Claude, a Bash(*) deny "remove[s] the tool from Claude's context".
Q: How long should the memory file be?
A: Claude gives a hard number: over 4 MiB and the whole file is skipped, with the note that shorter files produce better adherence. I found no published limit for Gemini, so keep yours conservative.
Q: Which version numbers should I trust?
A: The ones from gemini --version, claude --version, and the npm registry. Third-party tutorials lag badly; every number here was measured locally on 2026-10-18.
Where this leaves you
The difference is not which tool is safer. It is where you put the decision. Gemini hands it to a rule file whose priority you can compute, which suits teams that need central rollout and tiered control. Claude hands it to a fixed deny-first order and a prompt per call, which suits anyone who would rather the behavior be predictable and be asked one extra time.
If your target is CI, fix folder trust (error two) and the ask_user to deny conversion (error five) first. Everything else is habit.
👉 Join Xiaomi MiMo Platform: Leading AI model platform with cost-effective inference
👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: