Module 5: Skills and Hooks: automating your workflow
Permission System: access control in Claude Code
Permission System: access control in Claude Code
Overview
Claude Code is an agent that can read your codebase, write files, run terminal commands, install packages, and make commits. That power is what makes it useful — and it's exactly what makes it dangerous if it goes unchecked. The permission system is the mechanism that guarantees Claude Code only acts inside the boundaries you define.
This capsule covers Claude Code's permission model: which actions require approval, how to configure trust levels, what the --dangerously-skip-permissions flag does (and why its name is a warning), and how to design a permission policy for real projects where security matters.
The permission system is the natural close to this module. Skills give you control over what Claude does. Hooks give you control over how it's validated. Permissions give you control over what Claude can do. Together, they form a complete governance system.
The permission model
Claude Code operates on a simple principle: ask before acting. Before running any action that modifies your system, Claude asks for confirmation.
Actions that require approval
| Action | Risk | Default approval |
|---|---|---|
| Read files | Low | ✅ Automatic (doesn't ask) |
| Search in files (grep/glob) | Low | ✅ Automatic |
| Write/edit files | Medium | ⚠️ Asks for confirmation |
| Create new files | Medium | ⚠️ Asks for confirmation |
| Run terminal commands | High | ⚠️ Asks for confirmation |
| Install packages | High | ⚠️ Asks for confirmation |
| Make HTTP requests | Medium | ⚠️ Asks for confirmation |
| Git operations (commit, push) | High | ⚠️ Asks for confirmation |
| Delete files | High | ⚠️ Asks for confirmation |
What the permission request looks like
When Claude wants to run an action that requires approval, it shows a prompt like this:
Claude wants to execute: npm test
Allow? [y/n/always]
Your options:
| Option | What it does |
|---|---|
y (yes) | Allows this action once |
n (no) | Blocks this action |
always | Always allows this action (for this session and future ones) |
The decision flow
Claude wants to run an action
│
▼
Is it in allowedTools? ──── Yes ──▶ Run without asking
│
No
│
▼
Did the user already say "always"? ──── Yes ──▶ Run without asking
│
No
│
▼
Show the permission prompt
│
├── y → Run (this time only)
├── n → Don't run
└── always → Run + remember from now on
Permission levels (modes)
Claude Code has 6 permission modes, from most restrictive to most permissive:
- Default (
default) — only reads are auto-approved; asks for confirmation on writes and commands - Accept edits (
acceptEdits) — auto-approves file writes; asks for confirmation on commands - Plan (
plan) — read-only mode for research and design before executing - Auto mode (
auto) — a classifier decides automatically (research preview, March 2026) - Don't ask (
dontAsk) — auto-approves an explicit set of tools you defined, asks for the rest - Bypass permissions (
bypassPermissions) — approves everything (--dangerously-skip-permissions)
Switching modes with Shift+Tab: the default cycle rotates through default → acceptEdits → plan. The auto and bypassPermissions modes get added to the cycle if you enable them explicitly. dontAsk never appears in the cycle (it's only activated via configuration). The current mode is shown in the footer.
Level 1: Ask every time (default)
Claude asks for confirmation every time it wants to run an action that modifies your system.
Claude wants to write to: src/components/Button.tsx
Allow? [y/n/always]
Claude wants to execute: npm install axios
Allow? [y/n/always]
Claude wants to execute: git commit -m "Add Button"
Allow? [y/n/always]
When to use it: New projects, codebases you don't know well, working with agents you haven't vetted.
Level 2: Allow for session
When you answer y to a permission prompt, Claude can run that specific action for the rest of the current session. In the next session, it'll ask again.
This applies when Claude asks you for permission and you say "yes" — it isn't remembered across sessions.
When to use it: Most day-to-day development. You say "yes" to the actions you expect, and Claude stops asking for the rest of the session.
Level 3: Always allow (configured in settings)
You configure in settings which tools Claude can use without asking. This persists across sessions.
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write",
"Execute"
]
}
When to use it: After you've built trust. Personal projects where you know exactly what Claude does. Repetitive workflows where confirmations are pure friction.
Level 4: Auto Mode (research preview, March 2026)
The big 2026 shift: instead of manually approving each action, Auto Mode puts a classifier in front of the permission prompts. Safe actions run uninterrupted; destructive or suspicious actions get blocked and surfaced to you.
It's the middle ground between:
- Ask every time (too much friction for long sessions)
--dangerously-skip-permissions(too much risk — it approves everything)
How the classifier works:
Claude wants to run an action
│
▼
Classifier evaluates the risk
│
├── Safe action (edit a file in src/, run a test) → ✅ Runs without asking
├── Questionable action (delete, force push) → ⚠️ Surfaced to you to decide
└── Dangerous action (rm -rf /, sudo) → ❌ Blocked
Turning it on:
# During a session: Shift+Tab to cycle to auto mode
# You'll see it in the footer: "auto mode on"
Or set it as the default in settings:
{
"permissions": {
"defaultMode": "auto"
}
}
When to use it:
- Long sessions where approving every action becomes noise
- Developers who trust the classifier to filter out the dangerous stuff
- As a safer alternative to
--dangerously-skip-permissions
When NOT to use it:
- Critical production projects where every action needs human review
- Your first session with Claude in a new codebase (better to start with default)
- When you're debugging the agent's behavior
Requirements for Auto Mode (May 2026):
- Plan: Max, Team, Enterprise, or direct API. Not available on the Pro plan.
- Model: Sonnet 5 or Opus 5 on Team/Enterprise/API; Opus 5 on Max.
- Provider: direct Anthropic API only. Doesn't work via Bedrock, Vertex, or Foundry.
- Claude Code version: v2.1.83 or higher.
- Admin controls: on Team/Enterprise the admin has to enable it explicitly; they can block it with
permissions.disableAutoMode: "disable"in settings.
Fallback behavior: if the classifier blocks 3 consecutive actions, or 20 total in one session, Auto Mode pauses and reverts to manual prompts. In headless mode (-p), repeated blocks abort the whole session.
Important note: Auto Mode is still in research preview as of May 2026. The classifier improves over time, but it isn't infallible. Always review the output and the commits before you push.
Configuring permissions in settings
Where they're configured
Permissions are configured in the same settings files as hooks:
| File | Scope | Example use |
|---|---|---|
.claude/settings.json | Project (shared with the team) | Project permissions for the whole team |
.claude/settings.local.json | Personal (not shared) | Your own personal permissions |
The allowedTools format
{
"allowedTools": [
"Read",
"Write",
"Execute",
"Grep",
"Glob"
]
}
Each entry in allowedTools is the name of a tool Claude can use without asking permission.
Configurable tools
| Tool | What it allows |
|---|---|
Read | Read files (already allowed by default in most configurations) |
Write | Write and edit files |
Execute | Run terminal commands |
Grep | Search content inside files |
Glob | Search for files by pattern |
WebFetch | Make HTTP requests |
Granular permissions with patterns
You can get more specific using patterns in allowedTools:
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write(src/**)",
"Execute(npm test)",
"Execute(npm run lint)",
"Execute(git status)",
"Execute(git diff)"
]
}
In this example:
- Claude can read and search files freely
- Claude can write only inside
src/(not configs, not scripts) - Claude can run only
npm test,npm run lint,git status, andgit diffwithout asking - For any other command, Claude will ask for permission
Example: a conservative configuration
{
"allowedTools": [
"Read",
"Grep",
"Glob"
]
}
Claude can read and search, but asks permission for everything else. Ideal for sensitive projects.
Example: a permissive configuration
{
"allowedTools": [
"Read",
"Write",
"Execute",
"Grep",
"Glob",
"WebFetch"
]
}
Claude has full access without asking. Only for personal projects where you trust Claude completely.
Example: a balanced configuration (recommended)
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write(src/**)",
"Write(tests/**)",
"Execute(npm test*)",
"Execute(npm run *)",
"Execute(git status)",
"Execute(git diff*)",
"Execute(git add *)",
"Execute(git log*)"
]
}
Claude can read, search, write code and tests, run npm scripts, and check git status. For commits, pushes, package installs, and commands outside npm/git, it asks for permission.
The --dangerously-skip-permissions flag
What it does
claude --dangerously-skip-permissions
This flag disables the entire permission system. Claude can do anything without asking: write files, run commands, install packages, delete directories, push to remote.
Why the name is a warning
The name --dangerously-skip-permissions is not an accident. It's an explicit warning. When you use this flag:
- ⚠️ Claude can run
rm -rfwithout asking - ⚠️ Claude can
git push --forcewithout asking - ⚠️ Claude can install malicious packages without asking
- ⚠️ Claude can modify system configuration files without asking
- ⚠️ Claude can run any command in your terminal as your user
When it's acceptable
The flag exists for headless automation, where there's no human around to approve actions:
# In a CI/CD pipeline (the environment is ephemeral/disposable)
claude --dangerously-skip-permissions -p "Run tests and fix failures"
# In a Docker container (isolated from the main system)
docker run my-claude-image claude --dangerously-skip-permissions -p "Lint the codebase"
# In a controlled sandbox
claude --dangerously-skip-permissions -p "Generate the report"
In these cases, the agent operates in an isolated environment where it can't cause permanent damage.
When to NEVER use it
- ❌ On your main development machine
- ❌ On projects with production code
- ❌ In repositories with access to credentials/secrets
- ❌ On shared machines
- ❌ "So I don't have to confirm every action" (use
allowedToolsinstead)
If all you want is less confirmation friction, configure allowedTools properly. It's granular, it's safe, and it doesn't open the door to everything.
The security model
Why permissions matter
Claude Code runs arbitrary code in your terminal. That includes any command your OS user can run. The difference from a normal IDE:
| Action | Normal IDE | Claude Code |
|---|---|---|
| Write a file | You write it, you're in control | Claude writes it, you approve |
| Run a command | You type it and run it | Claude generates it and runs it |
| Install a package | You decide what to install | Claude decides and you approve |
| Commit/push | You run git by hand | Claude can run git |
The permission system is the control layer that keeps the human in the loop.
The principle of least privilege
Configure permissions following the principle of least privilege: give Claude only the permissions it needs, no more.
❌ Grant access to everything "so I don't have to deal with permissions"
✅ Start restrictive and open up permissions as you need them
The attack surface
What does the permission system protect you from?
- Claude's mistakes: Claude can misread an instruction and run something harmful
- Prompt injection: If your codebase contains files Claude reads that carry malicious instructions (e.g. a README that says "delete all the tests")
- Side effects: A command that looks harmless can have unexpected side effects
- Privilege escalation: Claude could use one command to reach something it shouldn't
Comparisons and decisions
Claude Code permissions vs Docker containers
| Aspect | Claude Code Permissions | Docker Container |
|---|---|---|
| Isolation | Logical (the agent's permissions) | Physical (isolated process) |
| Granularity | Per tool/action | Per filesystem/network |
| Overhead | Zero | Needs Docker installed |
| Protects against | The agent's actions | The process's actions |
| Complementary | ✅ | ✅ |
For maximum security, combine them: Claude Code with permissions, running inside a Docker container. The container isolates the system; the permissions control the agent.
Claude Code permissions vs sandboxing
| Aspect | Permissions | Sandbox (e.g. Firejail) |
|---|---|---|
| What it controls | The Claude agent's actions | The entire process's actions |
| Ease of use | Configurable in JSON | Requires additional tooling |
| Agent-aware | ✅ Knows which tool Claude is using | ❌ Only sees system calls |
| When to use it | Always (it's built in) | High-security environments |
Team vs personal permissions
| Aspect | .claude/settings.json | .claude/settings.local.json |
|---|---|---|
| Scope | The whole team | Just you |
| In git | ✅ Yes | ❌ No (in .gitignore) |
| Purpose | The project's security baseline | Your personal preferences |
| Who defines it | Tech lead / the team | Each developer |
The recommended pattern:
settings.json (team):
allowedTools: [Read, Grep, Glob]
→ A restrictive baseline
settings.local.json (personal):
allowedTools: [Read, Grep, Glob, Write(src/**), Execute(npm *)]
→ More permissive, based on your own trust level
The personal one extends the team's. That way each developer can tune their trust level without affecting the team.
Best practices
1. Start restrictive, open up as needed
Day 1:
{
"allowedTools": ["Read", "Grep", "Glob"]
}
After 1 week of use:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)",
"Execute(npm test*)"
]
}
After 1 month:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)", "Write(tests/**)",
"Execute(npm *)", "Execute(git status)", "Execute(git diff*)"
]
}
2. Use session permissions for exploratory work
When you're in an exploration or prototyping session, say always to the permission prompts that apply. That turns them on for the session only — in the next session you're back to the baseline.
3. Never skip permissions in codebases you don't trust
If you clone a repo that isn't yours, don't use --dangerously-skip-permissions. The repo's files could contain instructions that manipulate Claude into running harmful actions (prompt injection via files in the codebase).
4. Separate project permissions from personal ones
.claude/settings.json → Team: a restrictive baseline
.claude/settings.local.json → Personal: your trust level
Always add settings.local.json to .gitignore.
5. Combine permissions with hooks
Permissions are the first line of defense. Hooks are the second:
Permission: Claude can write in src/
PreToolUse hook: But not if the file contains secrets
PostToolUse hook: And after writing, run the linter
That's defense in depth: layers of control that reinforce each other.
6. Document the permission policy in CLAUDE.md
## Permissions
This project uses restrictive permissions. Claude has automatic permission to:
- Read and search files
- Write in src/ and tests/
- Run npm scripts
For everything else, Claude will ask for confirmation.
Do NOT use --dangerously-skip-permissions on this project.
Common patterns
Pattern 1: Personal development
For personal projects where you're the only developer and you trust Claude:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write",
"Execute(npm *)",
"Execute(git add *)",
"Execute(git commit *)",
"Execute(git status)",
"Execute(git diff*)",
"Execute(git log*)",
"Execute(python *)",
"Execute(node *)"
]
}
Claude can write anywhere and run npm, git (no push), Python, and Node. It can't push, install global packages, or run commands outside that list.
Pattern 2: A team with CI/CD
For teams where CI/CD validates everything:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)",
"Write(tests/**)",
"Execute(npm test*)",
"Execute(npm run lint*)"
]
}
More restrictive: Claude can write code and tests, run tests and lint. Everything else requires approval. CI/CD handles full validation on push.
Pattern 3: A sensitive project (fintech, healthcare)
For projects with sensitive data or compliance requirements:
{
"allowedTools": [
"Read",
"Grep",
"Glob"
]
}
Automatic reads only. Everything else requires explicit approval. Every Claude action ends up documented by the manual approval.
Pattern 4: Headless CI/CD
For automated pipelines where Claude runs unsupervised:
# In an ephemeral Docker container
docker run --rm \
-v $(pwd):/workspace \
-e ANTHROPIC_API_KEY=$API_KEY \
claude-image \
claude --dangerously-skip-permissions \
-p "Run tests, fix any failures, and create a summary report"
The container is ephemeral (it's destroyed afterward), so the --dangerously-skip-permissions flag is acceptable. The maximum damage Claude can do is bounded by the container.
Pitfalls and edge cases
Pitfall 1: Granting broad Execute permissions
The mistake:
{
"allowedTools": ["Execute"]
}
The problem: Execute without a pattern lets Claude run any command: rm, curl, sudo, chmod, and so on.
The fix: Always use patterns with Execute:
{
"allowedTools": [
"Execute(npm *)",
"Execute(git status)",
"Execute(python -m pytest*)"
]
}
Pitfall 2: Confusing session permissions with settings
The mistake: Believing that saying always at a permission prompt configures settings permanently.
The reality: always in the interactive prompt sets the permission for the session. For permanent configuration, edit settings.json.
Pitfall 3: Settings.local.json in git
The mistake: Forgetting to add .claude/settings.local.json to .gitignore.
The problem: Your personal permissions (possibly more permissive) get shared with the team.
The fix:
# .gitignore
.claude/settings.local.json
Pitfall 4: --dangerously-skip-permissions as a "shortcut"
The mistake: Using the flag because "I'm tired of confirming every action."
The fix: Configure allowedTools with the actions you want pre-approved. It's just as fast and much safer.
Pitfall 5: Not reviewing the team's permissions
The mistake: Each developer configures their permissions independently, with no team baseline.
The fix: Define settings.json as the team's baseline:
{
"allowedTools": ["Read", "Grep", "Glob"]
}
Each developer can extend it in settings.local.json, but the project's baseline stays restrictive.
Complete worked example
Scenario: you're setting up a permission policy for a team of 5 developers working on a full-stack project.
settings.json (shared with the team)
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write(src/**)",
"Write(tests/**)",
"Write(docs/**)",
"Execute(npm test*)",
"Execute(npm run lint*)",
"Execute(npm run format*)",
"Execute(git status)",
"Execute(git diff*)",
"Execute(git log*)"
],
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"command": "./scripts/hooks/security-check.sh"
},
{
"matcher": "Execute",
"command": "./scripts/hooks/safety-gate.sh"
}
],
"PostToolUse": [
{
"matcher": "Write",
"command": "./scripts/hooks/format.sh"
},
{
"matcher": "Write",
"command": "./scripts/hooks/lint.sh"
}
]
}
}
settings.local.json (senior developer, personal)
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write",
"Execute(npm *)",
"Execute(git *)",
"Execute(docker *)",
"Execute(python *)"
]
}
settings.local.json (junior developer, personal)
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write(src/**)",
"Write(tests/**)",
"Execute(npm test*)",
"Execute(npm run *)"
]
}
CLAUDE.md (permissions section)
## Permission policy
### Project baseline (settings.json)
- Claude can read and search files freely
- Claude can write in src/, tests/, and docs/
- Claude can run npm test, npm run lint, npm run format
- Claude can check git state (status, diff, log)
- Everything else requires approval
### For developers
- Set your personal level in .claude/settings.local.json
- Do NOT use --dangerously-skip-permissions
- If you need a permission that isn't in the baseline, ask the team
### Security hooks
- PreToolUse: security check (blocks secrets) + safety gate (blocks dangerous commands)
- PostToolUse: automatic format + lint
The flow in action
Junior developer:
> "Add validation to the login endpoint"
Claude wants to write src/routers/auth.py
→ allowedTools includes Write(src/**) → ✅ Proceeds without asking
→ security-check.sh hook → ✅ No secrets
→ Claude writes the file
→ format.sh hook → Prettier formats it
→ lint.sh hook → ESLint validates → 0 errors
Claude wants to run npm test
→ allowedTools includes Execute(npm test*) → ✅ Proceeds without asking
Claude wants to run git commit
→ Not in allowedTools → ⚠️ Asks for permission
Developer: "y" (yes, this once)
Claude makes the commit.
Practice exercises
Exercise 1: Basic — Configure conservative permissions
Configure a conservative permission policy for a new project.
Requirements:
- Claude can read and search freely
- Claude can write only in
src/andtests/ - Claude can run only
npm testandnpm run lint - Everything else requires approval
Solution
Create .claude/settings.json:
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write(src/**)",
"Write(tests/**)",
"Execute(npm test*)",
"Execute(npm run lint*)"
]
}
Verify: ask Claude to write a file at the project root (it should ask for permission) and then in src/ (it shouldn't ask).
Exercise 2: Basic — Personal permissions
Configure your personal permissions to extend the project's baseline.
Requirements:
- Extend the baseline with permission to run git (status, diff, add, commit)
- Add permission to run npm scripts
- Keep the baseline's write restrictions
- Add
settings.local.jsonto.gitignore
Solution
Create .claude/settings.local.json:
{
"allowedTools": [
"Read",
"Grep",
"Glob",
"Write(src/**)",
"Write(tests/**)",
"Execute(npm *)",
"Execute(git status)",
"Execute(git diff*)",
"Execute(git add *)",
"Execute(git commit *)",
"Execute(git log*)"
]
}
Add to .gitignore:
.claude/settings.local.json
Exercise 3: Intermediate — A team policy
Design a permission policy for a team of 3: 1 tech lead, 1 senior, 1 junior.
Requirements:
- A restrictive project baseline (in
settings.json) - Each role has a different
settings.local.json - The junior can't run git push or install packages
- The senior can git commit but not push
- The tech lead can push to branches (not to main)
- Document the policy in CLAUDE.md
Solution
settings.json (baseline):
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)", "Write(tests/**)",
"Execute(npm test*)", "Execute(npm run lint*)"
]
}
Junior settings.local.json:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)", "Write(tests/**)",
"Execute(npm test*)", "Execute(npm run *)",
"Execute(git status)", "Execute(git diff*)"
]
}
Senior settings.local.json:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)", "Write(tests/**)", "Write(docs/**)",
"Execute(npm *)",
"Execute(git status)", "Execute(git diff*)",
"Execute(git add *)", "Execute(git commit *)",
"Execute(git log*)"
]
}
Tech lead settings.local.json:
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write",
"Execute(npm *)",
"Execute(git *)"
]
}
Exercise 4: Intermediate — Permissions + Hooks combined
Configure a system where permissions and hooks reinforce each other:
- Permissions allow writes in
src/ - A PreToolUse hook blocks the write if it contains secrets
- A PostToolUse hook runs lint after writing
- A permission allows
npm testwithout asking
Solution
{
"allowedTools": [
"Read", "Grep", "Glob",
"Write(src/**)",
"Execute(npm test*)"
],
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"command": "./scripts/hooks/security-check.sh"
}
],
"PostToolUse": [
{
"matcher": "Write",
"command": "./scripts/hooks/lint.sh"
}
]
}
}
The permissions open the door (Write in src/). The hooks validate what goes through that door (no secrets, clean lint). That's defense in depth.
Exercise 5: Advanced — A permission audit
Create a script that shows your project's active permissions (Claude Code has no login hook, but you can run this script by hand or wire it into a PostToolUse hook as an audit).
Requirements:
- Reads
.claude/settings.jsonand.claude/settings.local.json - Shows which tools are pre-approved
- Shows whether hooks are configured
- Shows a warning if the permissions are too broad
Solution
Create scripts/hooks/audit-permissions.sh:
#!/bin/bash
echo "=== Active permissions ==="
# Read the project settings
if [ -f ".claude/settings.json" ]; then
echo "📋 Project (settings.json):"
grep -o '"[A-Za-z]*\([^"]*\)\?"' .claude/settings.json 2>/dev/null | while read -r tool; do
echo " ✅ $tool"
done
fi
# Read the personal settings
if [ -f ".claude/settings.local.json" ]; then
echo ""
echo "👤 Personal (settings.local.json):"
grep -o '"[A-Za-z]*\([^"]*\)\?"' .claude/settings.local.json 2>/dev/null | while read -r tool; do
echo " ✅ $tool"
done
fi
# Warn if Execute without a pattern is allowed
if grep -q '"Execute"' .claude/settings.json .claude/settings.local.json 2>/dev/null; then
echo ""
echo "⚠️ WARNING: Execute without a pattern is allowed (every command)"
fi
# Check for hooks
if grep -q '"hooks"' .claude/settings.json 2>/dev/null; then
echo ""
echo "🪝 Hooks configured: ✅"
else
echo ""
echo "🪝 Hooks: ❌ Not configured"
fi
echo "========================"
chmod +x scripts/hooks/audit-permissions.sh
Note: Claude Code exposes the SessionStart event for exactly this case — you can configure it in .claude/settings.json to run the script automatically at the start of every session. Alternatively, run it by hand (./scripts/hooks/audit-permissions.sh) or reference it from a skill you invoke with /audit-permissions.
Exercise 6: Challenge — A complete governance system
Configure a complete system combining Skills, Hooks, and Permissions for a project:
- 3 skills (create-component, write-test, review-code)
- Hooks across the 5 real events (PreToolUse, PostToolUse, Notification, Stop, SubagentStop)
- Permissions (settings.json baseline + settings.local.json personal)
- Documentation in CLAUDE.md
Deliverable: The full .claude/ directory with skills, settings, hook scripts, and the permissions section in CLAUDE.md.
Guide
The final structure:
.claude/
├── skills/
│ ├── create-component.md
│ ├── write-test.md
│ └── review-code.md
├── settings.json
└── settings.local.json
scripts/hooks/
├── session-start.sh
├── security-check.sh
├── safety-gate.sh
├── format.sh
├── lint.sh
└── task-summary.sh
Use the examples from capsules 02, 03, 04, and 05 of this module as your reference. Adapt everything to your project's conventions.
Verify that:
- The skills get invoked correctly with
/name - The hooks run at the right moments
- The permissions block what they're supposed to block
- The CLAUDE.md documentation is clear to a new developer on the team
Summary
What you learned in this capsule:
- Claude Code asks before acting: by default, every action that modifies your system requires approval
- 3 permission levels: ask every time (default), session permission (temporary), always allow (settings)
allowedToolsin settings.json configures permanent permissions with granular patterns--dangerously-skip-permissionsdisables the entire system. Only for automation in isolated environments (containers, CI/CD)- The principle of least privilege: start restrictive, open up as you need to
- Team vs personal permissions:
settings.json(team) +settings.local.json(personal) - Defense in depth: permissions (what it can do) + hooks (what gets validated) + CI/CD (what gets checked on push)
Summary of the whole module
You've finished Module 5: Skills and Hooks. Here's what you now know how to do:
| Capsule | What you learned |
|---|---|
| 01 — Introduction | Mental model: Skills = commands, Hooks = automation, Permissions = control |
| 02 — Skills | Creating slash commands in .claude/skills/ for repetitive tasks |
| 03 — Hooks | Configuring automatic scripts across the 5 lifecycle events |
| 04 — Validations | Building quality pipelines with lint, format, tests, and security checks |
| 05 — Permissions | Controlling what Claude can do with granular permission levels |
Your Claude Code isn't generic anymore. It has custom skills, validation hooks, and permissions configured for your project. It's a tool built to measure.
Next module: 06 - Subagents — delegating work to specialized agents. If Skills and Hooks are how Claude Code works for you, subagents are how Claude Code delegates work to other agents.
Additional resources
Official documentation
- Settings — Complete configuration of permissions and scopes
- Hooks — Hooks as a complement to permissions
- Interactive Mode — Interactive permissions and shortcuts
- Headless Mode — Using
--dangerously-skip-permissionsin automation
Security
- Claude Code Best Practices — Anthropic's security recommendations
- Plugins Reference — How permissions interact with plugins
Complementary
- CLI Reference — Command-line flags including
--dangerously-skip-permissions - GitHub Actions — Permissions in CI/CD