Article Information
Category: Development
Published: 26 March, 2026
Author: Chris de Gruijter
Reading Time: 12 min
Tags

How I Configured Claude Code: Agents, Hooks, Skills, and a 4-Layer Config System
Published: 26 March, 2026
Claude Code is one of those tools that's useful out of the box, but becomes genuinely powerful once you invest a couple of hours in configuration. The defaults are fine. A well-designed config system that persists across every project, loads automatically, and keeps Claude informed about your standards, agents, and workflows — that's something different.
I spent time building exactly that: a 4-layer configuration system that spans global rules, workspace-level context, agency-level context, and per-project detail. It's version-controlled, symlinked so changes propagate everywhere instantly, and includes 29 specialist sub-agents, a hooks system that auto-formats and checks code after every edit, slash command skills, and persistent memory across sessions. This post documents how the whole thing works.
The Core Idea: Layers That Inherit
Claude Code reads CLAUDE.md files from multiple directories and stacks them together. When you open a project, Claude has access to all of the following simultaneously:
~/.claude/ # Global — all projects, all sessions
/home/cjvdeg/Projects/.claude/ # All projects under /Projects/
/home/cjvdeg/Projects/Webfluentia/.claude/ # All Webfluentia work
/home/cjvdeg/Projects/Webfluentia/<project>/.claude/ # Per-projectEach layer adds more specific context without repeating what the layer above already provides. The global layer sets coding standards that apply everywhere. The projects layer sets owner info and tech stack defaults. The agency layer describes business context. The per-project layer covers stack specifics, dev commands, and the secrets path.
The result is that every Claude session — regardless of which project I'm in — starts with full context. I don't re-explain my coding preferences, my agent roster, or my deployment setup. Claude already knows.
Layer 1: Global Config, Version-Controlled via Symlink
The global config lives at ~/.claude/. Rather than letting that be an ad-hoc folder that drifts over time, I symlinked it to a version-controlled directory:
~/.claude → symlink → /home/cjvdeg/Projects/AI/claude/This means every change to my global config is tracked in git. If something breaks, I can roll it back. If I want to share a hook or agent with someone, I can just link the file. Changes propagate everywhere immediately — there's no syncing step.
The global CLAUDE.md doesn't contain rules inline. Instead it uses @ includes to load separate rule files:
# User Guidelines
These rules apply to every project and session.
@rules/coding-style.md
@rules/security.md
@rules/testing.md
@rules/git-workflow.md
@rules/patterns.md
@rules/performance.md
@rules/agents.md
@rules/hooks.mdEach rules file is focused on one domain. coding-style.md covers immutability, file size limits, and error handling. security.md has a mandatory pre-commit checklist and the secret management pattern. testing.md enforces 80% coverage and the TDD workflow. Keeping them separate makes it easy to update one rule without touching the others.
Layer 2: All-Projects Permissions
The /Projects/.claude/settings.json auto-allows a set of commands that are safe for every project I work on:
{
"permissions": {
"allow": [
"Bash(pnpm:*)",
"Bash(npm run:*)",
"Bash(npm install:*)",
"Bash(node:*)",
"Bash(python3:*)",
"Bash(wrangler:*)",
"Bash(sqlite3:*)"
]
}
}Without this, Claude would ask for permission on every pnpm install or wrangler deploy. These are all commands I run constantly and trust entirely, so pre-approving them at the projects level removes the friction without opening up anything sensitive.
Layer 3: Agency Context
The Webfluentia layer CLAUDE.md gives Claude business context it needs when working on any client project: what the agency does, which projects are active versus paused, what the common patterns are across sites (docs structure, deployment scripts, the generate-blog agentic pipeline), and how secrets are organised.
This layer is what prevents Claude from treating each client project as an isolated island. It knows that all sites use the same Secrets/ tree, the same Cloudflare Pages deployment pattern, and the same Turborepo monorepo conventions — without me having to explain this every session.
Layer 4: Per-Project Detail
Each active project has its own .claude/ folder containing two files:
- CLAUDE.md — stack specifics (framework version, apps/ports, key dependencies, dev commands, secrets path)
- settings.local.json — pre-approves the
ln -scommand that symlinks this project's.envfrom/Secrets/
The settings.local.json at project level is deliberately minimal — just the symlink command for that specific project's secrets. This keeps the permission surface small while ensuring Claude can set up the project in a fresh checkout without asking.
// /Projects/Webfluentia/sts-next-wf/.claude/settings.local.json
{
"permissions": {
"allow": [
"Bash(ln -s /home/cjvdeg/Secrets/Webfluentia/sts-next-wf/.env /home/cjvdeg/Projects/Webfluentia/sts-next-wf/.env)"
]
}
}29 Specialist Agents
All agents live at ~/.claude/agents/. Because the global config is version-controlled, they're available everywhere. The agents.md rules file tells Claude when to use each one proactively — I don't have to invoke them manually for the most common cases.
The roster covers the full development lifecycle:
- planner — before any complex feature or refactor
- architect — for system design and architectural decisions
- tdd-guide — enforces write-tests-first for new features and bug fixes
- code-reviewer — runs after writing or modifying code (proactive)
- security-reviewer — before commits, whenever auth or secrets are involved
- typescript-reviewer, python-reviewer, database-reviewer — language and domain specialists
- build-error-resolver, *-build-resolver variants — language-specific build failure diagnosis
- e2e-runner — Playwright end-to-end tests for critical flows
- a11y-reviewer — accessibility checks after UI changes
- docs-lookup — fetches live library documentation so Claude uses current API signatures
- chief-of-staff — email and Slack triage
- loop-operator — monitors and manages autonomous agent loops
- personal-blog-writer — writes and publishes blog posts for this site
The agents.md rule specifies that complex feature requests trigger the planner agent, code that was just written triggers code-reviewer, and bugs or new features trigger tdd-guide — all without me having to type the agent name. Claude reads the task and decides which specialist to invoke.
Parallel execution is also specified in the rules: independent operations (security analysis, performance review, type checking) should be launched simultaneously, not sequentially. This cuts wall-clock time significantly on larger tasks.
Hooks: Automation Without Thinking About It
The hooks system runs shell scripts automatically at specific points in the Claude Code session. All hooks are configured in ~/.claude/hooks/hooks.json. The ones that save the most friction:
PreToolUse Hooks
- Dev server blocker — blocks
npm run dev/pnpm devif not inside a tmux session. Prints the correct tmux command to use instead. This prevents detached dev servers that nobody killed. - tmux reminder — warns when running long-running commands (install, test, cargo, docker) outside of tmux
- git push review — pauses before any
git pushand prompts to confirm or abort. Gives one last chance to check before pushing. - doc blocker — blocks creation of
.mdand.txtfiles unless they match an allowlist (README.md, CLAUDE.md, AGENTS.md, CONTRIBUTING.md). Prevents Claude from writing unnecessary documentation files.
PostToolUse Hooks
- Prettier — auto-formats
.ts/.tsx/.js/.jsxfiles immediately after every edit. No manual formatting step. - TypeScript check — runs
tsc --noEmitafter editing.ts/.tsxfiles. Type errors surface immediately, before I've moved on. - console.log warning — warns if a console.log was introduced in an edited file. Catches debug statements before they reach a commit.
- PR logger — after
gh pr create, logs the PR URL and prints the review command.
Session Lifecycle Hooks
There's also a memory persistence system running on session lifecycle events. SessionStart loads context from the previous session. PreCompact saves state before the context window compacts. Stop runs a final console.log audit across all git-modified files, persists session state, and evaluates the session for extractable patterns.
Slash Command Skills
Skills live in ~/.claude/skills/ and surface as slash commands. The ones I use most:
- /plan — restates requirements, assesses risks, creates a step-by-step plan before writing any code
- /tdd — scaffolds interfaces and generates tests first, then implements
- /code-review — full code review pass on current changes
- /build-fix — builds and iteratively fixes errors
- /refactor-clean — dead code cleanup pass
- /test-coverage — checks and improves test coverage
- /execute-plan — picks up and executes the next phase from a plan.md file
- /learn — extracts reusable patterns from the current session
- /multi-agent-plan — multi-agent planning with an Opus orchestrator for complex architecture work
/context-dev, /context-review, and /context-research switch between behavioural modes — development (write code, follow TDD), review (critique without rewriting), and research (explore, don't commit). Useful for making intent explicit at the start of a task.
Model Selection Strategy
The performance rules file specifies which Claude model to use for different task types:
- Haiku 4.5 — lightweight agents with frequent invocation, worker agents in multi-agent systems, simple code generation. About 90% of Sonnet capability at 3x the cost savings.
- Sonnet 4.5 — main development work, orchestrating multi-agent workflows, complex coding tasks. Best daily driver.
- Opus 4.5 — complex architectural decisions, maximum reasoning requirements, research and analysis. Used sparingly.
Having this written down matters because Claude will use it when orchestrating sub-agents. A planner agent spinning up parallel workers will correctly assign Haiku to the lightweight tasks and Sonnet or Opus to the reasoning-heavy ones.
Memory Persistence
The memory system lives at ~/.claude/memory/ with a MEMORY.md index file. Four types of memory are tracked:
- user — who I am, preferences, expertise, working style
- feedback — corrections and confirmed approaches from past sessions
- project — ongoing work, goals, architectural decisions
- reference — where to find things (Linear boards, Grafana dashboards, deploy URLs)
The session lifecycle hooks write to and read from this directory automatically. The net effect is that Claude retains context across sessions — not just within a single conversation. If I made an architectural decision last week and it was recorded as a project memory, Claude knows about it today without me re-explaining.
MCP Server Catalog
MCP server configs are stored at ~/.claude/mcp-configs/mcp-servers.json as templates. They're not active by default — to use a server you copy its config to ~/.claude.json and fill in the real API keys. This keeps the catalog available without cluttering every session with servers that aren't relevant to the current project.
The catalog includes GitHub, Firecrawl, Supabase, memory, sequential-thinking, Vercel, Railway, various Cloudflare servers (docs, Workers builds, Workers bindings, observability), ClickHouse, Context7, Magic UI, and filesystem. The rule of thumb in my config is to keep under 10 active MCPs — beyond that, the context overhead starts affecting response quality.
What Makes the Whole Thing Work
The parts that individually seem minor — the symlink making the config version-controlled, the rule files being split by domain, the permission pre-approvals — compound into something that genuinely changes the workflow. Claude Code goes from a capable tool you have to guide carefully to something that already knows your standards and handles a lot of the scaffolding and checking work automatically.
The two most impactful pieces in practice are the agent roster with proactive invocation rules and the PostToolUse hooks. Prettier and TypeScript running after every edit means I'm never formatting or chasing type errors manually. The code-reviewer agent running automatically after writing code means reviews happen on every change, not just before commits.
If you're using Claude Code and haven't built out a config system yet, start with three things: split your CLAUDE.md into focused rule files, add a Prettier hook, and write down which agents you want Claude to use proactively. That alone will change how the sessions feel.
Frequently Asked Questions
What is a CLAUDE.md file and why does it matter?
CLAUDE.md is a special file that Claude Code reads to understand context about your project and coding standards. Claude reads CLAUDE.md files from multiple directories simultaneously — global, workspace, and per-project — and stacks them together. This means you can define global coding standards once and have them apply to every project automatically, while still being able to add project-specific context at the per-project level.
How do Claude Code agents work differently from just asking Claude to do something?
Agents are specialist sub-agents defined in ~/.claude/agents/, each with a specific role, instructions, and tool access. When Claude Code invokes an agent, it spawns a focused sub-context for that agent's task — a security-reviewer only reviews security, a tdd-guide only enforces test-driven workflow. This specialisation produces better output than asking a general Claude session to do everything. Agents can also be invoked in parallel for independent tasks, cutting total time significantly.
Do the hooks slow down the Claude Code workflow?
The Prettier hook adds maybe 200ms after each edit. The TypeScript check adds 1-3 seconds depending on project size. In practice this is fast enough that it's not noticeable, and the time saved by catching formatting or type errors immediately (rather than at build time or in a separate linting step) is far larger than the overhead. The PreToolUse hooks that block or warn are near-instant.
What's the point of the @rules/ include pattern vs putting everything in one CLAUDE.md?
Splitting rules into separate files by domain makes each file easy to read, maintain, and update independently. If I want to tighten the security rules, I edit security.md without touching anything else. If I want to update the agent roster, I edit agents.md. One large CLAUDE.md file would become unwieldy and hard to version-control meaningfully — small focused files with clear names are much easier to work with.
How does the per-project settings.local.json .env symlink pattern work?
All project secrets live in /home/cjvdeg/Secrets/Webfluentia/<project>/ rather than inside the project directory. The app still expects a .env at the project root, so a symlink is created pointing from the project root to the secrets directory. The settings.local.json for each project pre-approves the exact ln -s command for that project's symlink — so Claude can run it without asking for permission. This keeps secrets out of the code tree while still making them accessible to the running app.
Can this config structure work for someone with a single project, not an agency?
Absolutely. The layer structure scales down fine — you can use just the global layer and a single per-project CLAUDE.md. The most valuable parts for any developer are the global rules files (coding standards, testing requirements), the agent roster (even just planner + code-reviewer + tdd-guide), and the PostToolUse hooks for Prettier and TypeScript checking. The agency-specific layers are just additional specificity that makes sense for my setup.
How do you keep the 29 agents from overwhelming the context window?
Agents are only invoked when their specific trigger condition is met — they don't all load simultaneously. The agents.md rules file specifies which agent to use for which task type, and Claude invokes the relevant one on demand. Between invocations, the agent definitions don't consume context. The practical limit isn't the number of agents defined but rather how many are running in parallel at any given moment — I keep that to 3-5 for typical tasks.