Gaëtan Wittebolle.
UmamiContactFR
FR
← guides
Claude Code, the guide
Chapter 9 / 19
  • 07Specialized agents
  • 08The shortcuts that speed everything up
  • 09Permissions

Part 3 · Advanced tools

Permissions

Chapter 9 · 5 min reading

This chapter explains how Claude Code's permission system works, what it can do on its own and what needs your approval. You always stay in control. Reading time: 5 minutes.

Claude always asks before acting

Claude Code will never do anything risky without explicitly asking you. That's a fundamental safety rule.

But there's a practical issue: by default, it asks for confirmation on every command, even the most mundane ones. Run a typecheck? Confirmation. Run lint? Confirmation. Read a file? Confirmation. After 50 confirmations in 1 hour, you go nuts.

The solution: pre-authorize safe commands while keeping validation for risky actions.

The 3 risk levels

Safe, automatic

Claude does this freely, without asking you anything:

  • Read files
  • Navigate the code
  • Analyze the project
  • Search for patterns in the code

Moderate, asks for confirmation

Claude shows you what it's about to do and waits for your OK:

  • Modify files
  • Install packages
  • Run commands (tests, build, migrations)
  • Create new files

Risky, always asked

Claude explains in detail and waits for your explicit validation:

  • Push code to GitHub (git push)
  • Delete files or branches
  • Irreversible actions (database reset, etc.)
  • Unknown or unusual commands

Pre-authorize safe commands

To avoid clicking "Yes" 50 times per session, create a config file that automatically allows repetitive, risk-free commands.

Create the file

Create the file .claude/settings.local.json at the root of your project:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(npx supabase *)",
      "Bash(npx vitest *)",
      "Bash(git status*)",
      "Bash(git diff*)",
      "Bash(git log*)",
      "Bash(git add *)",
      "Bash(git commit*)"
    ]
  }
}

Wildcard syntax: use * as a wildcard. Bash(npm run *) allows npm run dev, npm run build, npm run test, etc. with a single rule. No need to list every variant.

What it allows

CommandWhy it's safe
npm run typecheckChecks types, modifies nothing
npm run lintChecks style, modifies nothing
npm run testRuns tests, modifies nothing
npm run buildCompiles the app, doesn't change source code
npx supabaseLocal Supabase commands
git status/diff/logRead-only on Git state
git add/commitLocal save (no push)

What still asks for confirmation

  • git push (sending code online)
  • npm install (adding dependencies)
  • File deletion
  • Unknown commands

Trust mode

If you're in a heavy dev session and you fully trust Claude, you can switch to "trust" mode from the interface. Claude won't ask for confirmation on anything anymore.

Watch out: trust mode is powerful but risky. Use it only when you know exactly what Claude is going to do (e.g. running a plan you've validated). Don't leave it on permanently.

The day-to-day experience

With a well-configured permissions file, here's what it looks like in practice:

  • Claude writes code → you see files getting modified live in VS Code
  • Claude runs typecheck → goes through automatically, no confirmation
  • Claude runs lint → goes through automatically
  • Claude wants to install a package → asks "Install framer-motion?" → you approve
  • Claude wants to commit → goes through automatically with the right message
  • Claude wants to push → asks "Push to branch feat/streak?" → you approve

The result: you only click on decisions that actually matter.

The safety net: the deny list

More powerful than the allowlist: the deny list. It blocks dangerous commands even in full trust mode. It's your ultimate safeguard.

In your ~/.claude/settings.json (global, for all projects):

{
  "permissions": {
    "deny": [
      "Bash(rm -rf*)",
      "Bash(sudo*)",
      "Bash(supabase db reset*)",
      "Bash(git push --force*)",
      "Bash(git push -f*)",
      "Bash(git reset --hard*)",
      "Bash(git clean -f*)"
    ]
  }
}

These commands are blocked no matter what, even if you're in trust mode, even if Claude thinks it's necessary. rm -rf can wipe out your entire project. supabase db reset destroys all data and credentials. git push --force can overwrite your team's work. Better safe than sorry.

The difference between allow and deny:

  • allow (.claude/settings.local.json per project): commands allowed without confirmation
  • deny (~/.claude/settings.json global): commands forbidden everywhere, even in trust mode

Hooks 101

A hook is a shell command or script that Claude Code triggers automatically when a specific editor event happens. You wire the event to the script, and it runs on its own in the background. You don't have to tell Claude to remember to do it.

The 5 events that fire a hook

  • PreToolUse: before Claude uses a tool (e.g. block a command before it runs)
  • PostToolUse: after a tool finishes (e.g. format a file after an Edit)
  • UserPromptSubmit: every time you send a message to Claude
  • SessionStart: when Claude Code launches (e.g. load context)
  • Stop: when Claude finishes its response

Example 1: Auto-Prettier after every Edit

Every time Claude modifies or creates a file, it gets formatted with Prettier. No more messy indentation sneaking into your PRs.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATH\""
          }
        ]
      }
    ]
  }
}

Example 2: Load your personal index at startup

At the beginning of every session, Claude reads your index file (notes, project rules, whatever you want). Saves you from re-pasting context each time.

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cat ~/Claude\\ OS/brain/INDEX.md"
          }
        ]
      }
    ]
  }
}

Where to put the config

  • ~/.claude/settings.json: hooks applied to all your projects (global user config)
  • .claude/settings.json at a project root: hooks specific to that project (e.g. custom linter, team rules)

Security note: hooks run with your full system permissions. A script that calls rm, makes a network request, or reads sensitive files will execute without asking. Before you copy a hook from a GitHub repo or a blog post, read every line. If you're not sure what it does, don't paste it.

Takeaway: configure the allowlist once per project and the deny list once globally. You eliminate 90% of useless confirmations while blocking dangerous commands. It's the perfect balance between flow and safety.

← Chapter 8

The shortcuts that speed everything up

Chapter 10 →

Memory between sessions