1. Why You Need a Configuration System
Most developers start with Claude CLI the same way: open a terminal, ask a question, get an answer — then repeat themselves every session explaining the tech stack, coding conventions, and project structure.
The root cause is missing context. A configuration system's core value is ensuring the AI already understands your project, your preferences, and what it's allowed to do before a single message is typed.
A well-structured configuration lets you:
- Give the AI permanent knowledge of your tech stack and conventions, no re-explaining needed
- Require explicit confirmation before dangerous operations like
git push --force - Automate repetitive tasks like formatting on file save
- Share a consistent AI experience across your entire team
2. File Hierarchy Overview
Claude CLI configuration follows a proximity principle: configuration closer to the project takes higher priority. Personal settings never bleed into shared team configuration.
~/.claude/ # Global user-level config
├── settings.json # Global permissions, model, behavior
├── CLAUDE.md # Personal global preferences (not committed)
├── keybindings.json # Custom keyboard shortcuts
└── projects/
└── <project-hash>/
└── memory/ # Auto-generated project memory files
<project-root>/
├── CLAUDE.md # Project-level briefing (committed, team-shared)
├── .claude/
│ ├── settings.json # Project permissions and behavior (committed)
│ ├── settings.local.json # Local overrides (add to .gitignore)
│ └── skills/ # Project-specific Skill definitions
│ └── my-skill.md
└── src/
└── components/
└── CLAUDE.md # Subdirectory-level notes (optional)
Configuration Priority (highest to lowest)
Subdirectory CLAUDE.md
> Project .claude/settings.local.json
> Project .claude/settings.json
> Project root CLAUDE.md
> Global ~/.claude/settings.json
> Global ~/.claude/CLAUDE.md
Rule of thumb: team conventions go in project-level config, personal preferences go in global config, sensitive local overrides go in settings.local.json (gitignored).
3. CLAUDE.md: Your Project's AI Briefing Document
CLAUDE.md is automatically loaded by Claude CLI at the start of every conversation. Think of it as an onboarding document the AI reads before touching your codebase.
What a Good CLAUDE.md Contains
# Project Name
## Tech Stack
- Backend: Go 1.22, Chi router
- Frontend: React 18 + TypeScript, Vite
- Database: PostgreSQL 15, sqlc for type-safe queries
- Testing: Go stdlib testing + testify; Vitest for frontend
## Project Structure
- `cmd/` — service entry points
- `internal/` — business logic (no external imports)
- `pkg/` — reusable libraries
- `web/` — frontend source
## Development Commands
- `make dev` — start dev environment (requires Docker)
- `make test` — run all tests
- `make lint` — run golangci-lint + eslint
- `make generate` — regenerate sqlc and protobuf files
## Coding Conventions
- Go: all errors must be handled, no bare `panic`, functions over 80 lines need splitting
- TypeScript: no `any`, functional components, state via Zustand
- Commit messages follow Conventional Commits
- All public APIs require integration test coverage
## Important Constraints
- Database migration files are immutable — only add new ones, never edit existing
- Changes to `internal/auth` require a security review
- Never modify anything under `vendor/`
Subdirectory CLAUDE.md
For complex projects, add scoped instructions closer to specific areas of the codebase:
# web/src/components/CLAUDE.md
## Component Conventions
- All components typed as `React.FC<Props>`
- Props interface must match the component name, e.g. `ButtonProps`
- Use CSS Modules; filename must match component name
- Never call APIs directly inside components — use hooks
⚠️ What Should NOT Be in CLAUDE.md
- Secrets, tokens, or passwords — even for development environments
- Large code samples — they consume context; the AI reads source files more accurately anyway
- Frequently changing information — like current version numbers; stale data misleads the AI
4. settings.json: Behavior and Permission Control
settings.json defines what Claude CLI can and cannot do. This is the core of your security boundary.
The Permission Model
Claude CLI uses an allowlist model: everything requires confirmation by default, and explicitly allowed operations run automatically.
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(make *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git add *)",
"Bash(git commit *)",
"Read(**)",
"Edit(**)",
"Write(src/**)"
],
"deny": [
"Bash(git push --force*)",
"Bash(rm -rf *)",
"Write(.env*)"
]
}
}
Tool Permission Syntax
ToolName(argument-match-pattern)
-
Bash(npm *)— allow all npm commands -
Bash(npm run test)— allow only this exact command -
Read(**)— allow reading any file (**matches multiple path segments) -
Write(src/**)— allow writing only inside thesrc/directory
Model and Behavior Configuration
{
"model": "claude-opus-4-7",
"env": {
"NODE_ENV": "development",
"LOG_LEVEL": "debug"
},
"hooks": {
"PreToolUse": [...],
"PostToolUse": [...],
"Stop": [...]
}
}
Team Config vs Personal Config
| File | Commit to git | Purpose |
|---|---|---|
.claude/settings.json |
Yes | Shared team permission baseline |
.claude/settings.local.json |
No (gitignore) | Personal local overrides |
~/.claude/settings.json |
— | Cross-project personal defaults |
5. Hooks: The Key to Automated Workflows
Hooks are shell commands configured in settings.json that run automatically when specific events fire. They are the primary mechanism for automating your AI workflow.
Available Hook Events
| Event | Fires When | Common Uses |
|---|---|---|
PreToolUse |
Before the AI calls a tool | Block dangerous operations, audit logging |
PostToolUse |
After the AI calls a tool | Validate results, trigger follow-up actions |
Notification |
When the AI needs to alert the user | Desktop notifications |
Stop |
When the AI finishes a response turn | Run tests, generate summaries |
Practical Hook Examples
1. Auto-format after AI edits a file
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "if [[ \"$CLAUDE_TOOL_INPUT\" == *.go ]]; then gofmt -w \"$CLAUDE_TOOL_INPUT\"; fi"
}
]
}
]
}
}
2. Block writes to production config
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'config/production' && echo 'Direct modification of production config is not allowed' && exit 2 || exit 0"
}
]
}
]
}
}
3. Desktop notification when AI finishes
{
"hooks": {
"Stop": [
{
"type": "command",
"command": "notify-send 'Claude' 'Task complete'"
}
]
}
}
Hook Exit Code Convention
| Exit Code | Meaning |
|---|---|
0 |
Proceed normally |
1 |
Log a warning, proceed anyway |
2 |
Block the operation; AI receives an error message |
6. MCP Servers: Extending AI's Reach
MCP (Model Context Protocol) is the standard protocol for connecting AI to external systems. Through MCP Servers, the AI can query databases, call internal APIs, and interact with third-party services.
Configuring MCP Servers
Declare them in settings.json:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_URL": "postgresql://localhost/mydb"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
},
"internal-api": {
"command": "python",
"args": [".claude/mcp/internal-api-server.py"],
"env": {
"API_BASE_URL": "http://localhost:8080"
}
}
}
}
Security Principles
- Reference sensitive tokens via environment variables (
${VAR_NAME}), never hardcode them - Internal MCP Servers should default to read-only; expose write operations as explicit tools with permission controls
- Document each MCP Server's purpose in
CLAUDE.mdso the AI uses them intentionally
7. Skills: Encapsulating Reusable AI Workflows
Skills are structured instruction sets stored in .md files and triggered via /skill-name commands. They are the primary tool for standardizing AI workflows across a team.
Skill File Structure
# .claude/skills/create-feature.md
---
name: create-feature
description: Scaffold a standard feature module (handler + service + repo + tests)
triggers:
- /create-feature
---
## Usage
/create-feature <feature-name> [--module <module-name>]
## Steps
1. Under `internal/<module>/`, create these files:
- `handler.go` — HTTP layer
- `service.go` — business logic layer
- `repo.go` — data access layer
- `*_test.go` — unit tests for each
2. Register routes in the `RegisterRoutes` function in `handler.go`
3. Add dependency injection bindings in `internal/wire.go`
4. Create a database migration file if schema changes are needed
## Output Checklist
List all created files and modified files when done.
Project-Level vs Global Skills
.claude/skills/ # Project-specific; committed to git; team-shared
~/.claude/skills/ # Personal global; reusable across all projects
What's Worth Turning Into a Skill
- Code generation with a fixed output structure (CRUD modules, API endpoints)
- Multi-step coordinated tasks (DB migration + code generation + tests)
- Tasks where the team has explicit conventions (PR descriptions, review checklists)
- Recurring analysis tasks (dependency security audits, benchmark comparisons)
8. Agent SDK: Building Custom AI Agents
When the interactive Claude CLI model isn't enough, the Agent SDK lets you build purpose-built agents — for example, automated code review in CI/CD pipelines, or automated triage of monitoring alerts.
Core Concept: The Agent Loop
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "read_file",
"description": "Read the contents of a project file",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "File path"}
},
"required": ["path"]
}
}
]
def run_agent(task: str) -> str:
messages = [{"role": "user", "content": task}]
while True:
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
tools=tools,
messages=messages
)
if response.stop_reason == "end_turn":
return extract_text(response)
if response.stop_reason == "tool_use":
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
CI/CD Integration Example
# .github/workflows/ai-review.yml
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run AI Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python .claude/agents/pr-reviewer.py ${{ github.event.pull_request.number }}
Agent Design Principles
- Single responsibility: each agent does one thing; split complex tasks across multiple collaborating agents
- Idempotent operations: retrying a failed agent run should not produce side effects
- Audit logging: record every tool call the agent makes for debugging
-
Hard limits: set
max_tokensand loop iteration caps to prevent runaway agents
9. Development Best Practices
9.1 Front-Load Context Into Configuration
Stop repeating yourself. Put recurring context into CLAUDE.md once.
## Project-Specific Vocabulary
- "DTO" = Data Transfer Object, lives in `internal/dto/`
- "UC" = Use Case, lives in `internal/usecase/`
- "current PR" = changes in `git diff main...HEAD`
Also, use /clear to reset the conversation context when switching to an unrelated task. Long sessions accumulate stale context that degrades output quality.
9.2 Principle of Least Privilege
Resist the temptation to give the AI broad permissions for convenience:
// Bad: dangerously broad
{ "allow": ["Bash(*)", "Write(**)"] }
// Good: precise, intentional grants
{
"allow": [
"Bash(npm run *)",
"Bash(git add src/**)",
"Write(src/**)",
"Write(test/**)"
],
"deny": [
"Bash(git push *)",
"Write(.env*)",
"Write(config/production*)"
]
}
9.3 Progressive Automation
Start automating low-risk operations and expand trust incrementally:
Phase 1: Read-only automation (file reads, code search, running tests)
↓
Phase 2: Local write automation (file edits, local git operations)
↓
Phase 3: Guarded remote operations (create PR, with confirmation required)
↓
Phase 4: Fully automated CI/CD flows (within clearly scoped task boundaries)
9.4 Write Constrained Prompts
Structure matters more than length. Give the AI constraints, not freedom:
// Vague — produces mediocre results
"Optimize this code"
// Constrained — produces targeted results
"Refactor the validateToken function in src/auth/middleware.go:
- Goal: extract the duplicated error handling logic
- Constraint: do not change the function signature or touch test files
- Verify: run `make test` after refactoring to confirm tests pass"
9.5 Use AI for Structured Code Review
Give the AI an explicit review mandate instead of asking it to "look at the code":
Review internal/payment/calculator.go for:
1. security — SQL injection, auth bypass, input validation
2. correctness — boundary conditions, concurrency safety, error handling
3. performance — N+1 queries, unnecessary allocations
4. conventions — compliance with project rules in CLAUDE.md
Flag each issue with a severity: critical / warning / suggestion
9.6 Team Collaboration Conventions
## What to Commit
Always commit:
- CLAUDE.md (all levels)
- .claude/settings.json
- .claude/skills/*.md
Never commit:
- .claude/settings.local.json
- Any file containing tokens or credentials
- ~/.claude/ (personal global config)
## PR Template Additions
- [ ] Did conventions change? Update CLAUDE.md.
- [ ] Is this task repetitive? Consider creating a Skill.
- [ ] Do new operations need permission entries in settings.json?
10. Common Anti-Patterns
Using AI as a Search Engine
// Anti-pattern: asking for information with a deterministic answer
"How do goroutines work?"
// Better: use AI for project-specific reasoning
"In internal/worker/processor.go, what data race risks should I
watch for when converting the current serial processing to concurrent?"
Blindly Trusting AI Output
AI can produce incorrect code with high confidence. Code generated by AI for critical paths — authentication, payments, data migrations — must be human-reviewed and test-covered before merging. Treat AI output as a draft, not a finished product.
Overloading a Single Session
Doing too much in one session leads to context pollution — early incorrect assumptions contaminate later outputs. Start a new session for each independent task.
Letting AI Guess Intent
// Anti-pattern: open-ended instruction
"Add some tests to this module"
// Better: specify exactly what you expect
"Write unit tests for the Calculate function in internal/payment/calculator.go.
Cover: happy path, zero amount, negative amount, and unsupported currency."
Ignoring the AI's Uncertainty Signals
When the AI says "I'm not sure" or "you may want to verify this" — don't skip past it. These signals usually mean it's working at the boundary of its knowledge or lacks context. Human intervention at these moments is far more efficient than letting the AI guess.













