# 3 permission presets for Claude Code (strict, balanced, autonomous) and the gap none of them close

> Source: <https://dev.to/luijhy_michaelguerraflo/3-permission-presets-for-claude-code-strict-balanced-autonomous-and-the-gap-none-of-them-close-4pn8>
> Published: 2026-10-28 19:31:26+00:00

Every "Allow this command?" prompt in Claude Code is a small interruption. Click "always allow" enough times and you end up with a `settings.json` nobody has read. Here are three presets I keep in `.claude/settings.json`, from most to least careful, and the one thing none of them can do.

Rules are matched in this order: **deny** beats **ask**, and **ask** beats **allow**. Anything not listed falls back to `defaultMode`.

Read-only git is free. Commits, pushes and installs need a confirmation, and network tools are off.

```
{
  "permissions": {
    "defaultMode": "default",
    "allow": [
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git log:*)"
    ],
    "ask": [
      "Bash(git commit:*)",
      "Bash(git push:*)",
      "Bash(npm install:*)",
      "Bash(pnpm add:*)",
      "Bash(yarn add:*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(curl:*)",
      "Bash(wget:*)",
      "WebFetch"
    ]
  }
}
```

Edits are accepted automatically (`acceptEdits`). Lint, tests and type checks run without asking, and anything that leaves your machine or changes dependencies still asks.

```
{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git log:*)",
      "Bash(git add:*)",
      "Bash(npm run lint:*)",
      "Bash(npm run test:*)",
      "Bash(npm test:*)",
      "Bash(npx tsc --noEmit:*)"
    ],
    "ask": [
      "Bash(git push:*)",
      "Bash(npm install:*)",
      "Bash(pnpm add:*)",
      "Bash(yarn add:*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}
```

All of git and npm are allowed. Use it only where a bad command costs you a branch, not a production database.

```
{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash(git:*)",
      "Bash(npm:*)",
      "Bash(npx:*)",
      "Bash(pnpm:*)",
      "Bash(node:*)",
      "WebFetch",
      "WebSearch"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}
```

Look at the autonomous preset again: `Bash(git:*)` allows `git push --force` and `git reset --hard` too. Permission rules match a command by its prefix. They can't tell a safe `git push` from a destructive one.

There's a second gap. `Read(./.env)` blocks Claude's Read tool, but it isn't a rule about the shell. A `cat .env` through Bash is a different tool and needs its own handling.

That's what **hooks** are for. A `PreToolUse` hook is a script that sees the full command before it runs and can block it with `exit 2`, so it can reject `git push --force` while allowing a normal push, or refuse any command that touches `.env`. Two free ones (one blocks destructive commands, tested against bypasses like `git push origin +main`; one stops secrets from being written into files) are here: [https://github.com/kailucho/claude-code-hooks-seguridad](https://github.com/kailucho/claude-code-hooks-seguridad) (free, MIT).

Presets for convenience, hooks for safety. You want both.

The three presets, the hooks, 6 skills (`/commit`, `/pr`, `/review`, `/tests`, `/plan`, `/debug`) and `CLAUDE.md` templates for React, Node and GraphQL come together in the **Starter Kit for Claude Code** (US$9, unofficial): [https://luijhy.gumroad.com/l/starter-kit-for-claude-code](https://luijhy.gumroad.com/l/starter-kit-for-claude-code)
