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
| Command | Why it's safe |
|---|---|
npm run typecheck | Checks types, modifies nothing |
npm run lint | Checks style, modifies nothing |
npm run test | Runs tests, modifies nothing |
npm run build | Compiles the app, doesn't change source code |
npx supabase | Local Supabase commands |
git status/diff/log | Read-only on Git state |
git add/commit | Local 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.jsonper project): commands allowed without confirmation - deny (
~/.claude/settings.jsonglobal): 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 ClaudeSessionStart: 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.jsonat 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.