| name | Claude Code Setup |
|---|---|
| tagline_fr | Mon workflow Claude Code, anonymisé et documenté. |
| tagline_en | My Claude Code workflow, anonymized and documented. |
| about_en | Claude Code hooks, agents and commands for running 10+ personal apps without babysitting every diff — MIT, 2 blocking hooks included. |
| facts_fr | 7 commandes, 6 agents, 4 hooks — dont 2 bloquants. |
| facts_en | 7 commands, 6 agents, 4 hooks — 2 of them blocking. |
If claude-code-best-practice is the encyclopedia, this is the field guide.
An opinionated, production-tested Claude Code configuration for solo developers managing multiple apps.
Not a tutorial — a real setup that runs daily across 10+ personal projects,
shared so you can steal the parts that work for you.
The Story · What's Inside · Key Decisions · Quick Start · Adapting · Philosophy
I'm not a developer. I've spent 20 years close to code without writing much of it — I understand architecture, I can debug a concept, but I don't read diffs. I haven't looked at a line of code in my projects for months.
What I do is build things. I maintain a portfolio of 10+ personal apps — budget trackers, reading lists, encyclopedias, games, tools for my family. Different stacks, different audiences, different deployment targets. One development environment: Claude Code.
After months of trial, error, and way too many hours watching Claude Code attempt the same failing fix for the fifth time, I built a system. Commands that orchestrate agents. Agents that remember what they learned. Skills that preload project conventions. Hooks that nudge without blocking.
This repo is that system, anonymized and documented. It's opinionated because opinions are what's missing from most "best practices" repos — I'll tell you what I chose and why I chose it over the alternative.
As Boris Cherny, who created Claude Code, put it: "Every engineer uses their tools differently." This is my way.
Everything here is about writing code. I run other Claude Code automation that isn't — a writing pipeline and a job search — and none of it is published, deliberately: those plugins, their skills, and the scripts that drive them are excluded at the source, and the sync script refuses to publish a file that carries them. If you spot a reference to something that isn't in this repo, that's why. What you see is the whole of the code-facing half, not a sample of a larger whole.
┌───────────────────────────────────────────────────────────┐
│ You type a command │
│ /audit /cleanup /new-app /next /sync /tech-debt │
│ /sync-setup │
└─────────────────────────────┬─────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ Agents │
│ │
│ implementer ───── sonnet │
│ troubleshooter ── inherit (never weaker than the caller) │
│ docs-checker ─── sonnet (audits README, CLAUDE.md) │
│ portfolio-sync ─ sonnet (hub stories frontmatter) │
│ portfolio-audit haiku (compliance checks) │
│ dummy-visitor ── sonnet (bilingual naive-visitor review) │
│ │
│ ┌────────────┐ ┌──────────────────────────────────┐ │
│ │ Memory │ │ Skills │ │
│ │ per agent │ │ portfolio-conventions (preload) │ │
│ │ per project│ │ ── the rest load on demand ── │ │
│ └────────────┘ │ scheduled-jobs │ │
│ │ claude-md-hygiene │ │
│ │ python-uv │ │
│ │ ci-and-branch-protection │ │
│ │ testing-conventions │ │
│ │ memory-and-plans │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────┬─────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ Hooks │
│ │
│ stale-readme-guard "Docs still current?" (advisory) │
│ auto-format ─────── "Format the file" (advisory) │
│ secret-scan ─────── "API key in source?" (blocking) │
│ push-build-gate ─── "Build + tests green?" (blocking) │
└───────────────────────────────────────────────────────────┘
Tip
For a visual, interactive version of this architecture, open the workflow guide — it's bilingual and opens in your browser's language, French or English (FR/EN toggle in the header, and it remembers your choice).
The guide, on the scenario I run most: triage the environment first, then a one-way L1 → L2 → L3 escalation, then a regression test written before the fix.
Commands (7) — entry points that orchestrate everything
| Command | What it does | When to use it |
|---|---|---|
/sync |
Validates the stories collection frontmatter across the portfolio hub | Weekly maintenance |
/audit |
Parallel docs-checker + portfolio-audit | Before releases, compliance sweeps |
/new-app |
Full scaffold with portfolio compliance from day one | Starting a new project |
/next |
Executes the next unblocked task from a multi-phase plan track, then stops | One session per phase of a large plan |
/cleanup |
Disk hygiene sweep of ~/.claude first, then stale plans, plugin audit, memory compaction |
Weekly housekeeping |
/tech-debt |
Monthly triage → deep review → auto-fix | Monthly health check |
/sync-setup |
Sync this repo from live ~/.claude/ config (anonymize + audit) |
After workflow changes |
Agents (6) — the workers, each with a specific role and model
The model selection matters. I don't pay opus prices for a compliance check that haiku handles perfectly. Heavier, cross-file work doesn't live on a fixed agent — it escalates by dispatch, at the model tier the task calls for.
Skills (7) — preloaded knowledge and user-invocable utilities
- portfolio-conventions: condensed version of cross-project standards (naming, signature, dark mode, docs, the three-layer inventory that replaced the per-repo manifest, quality gates, display order). Loaded into
troubleshooterandportfolio-sync. - scheduled-jobs: why each scheduled job exists, at the hour it is scheduled, and which plausible "fixes" are wrong — the login-keychain trap outside a GUI session, per-job PATH, the 0/1/2 exit convention. Current state is derived by
claude-scripts/jobs-inventory.sh, never written into the skill — the prose version of that inventory drifted repeatedly before it was replaced by a script. - claude-md-hygiene: how to cut a CLAUDE.md without losing a fact — the four-bucket taxonomy (guard / instruction / domain knowledge / archaeology), the trigger-line shape without which an extracted skill never loads, the
.gitignoreprerequisite, and the line-coverage check that must be made to fail once before its silence counts as evidence. - python-uv: why
uvis the only Python manager here, where the enforcement actually lives (a config file, emphatically not a shell rc — a scheduled job never sources one), and which plausible "fixes" are wrong. - ci-and-branch-protection: the zero-coverage hole in an aggregate CI job (
jq 'all(.[]; …)'over an empty set returnstrue, so the obvious gate reports success on nothing), why branch protection is advisory on a private repo on the free tier, and the three traps that permanently deadlock a solo merge. - testing-conventions: the regression-test comment format, property-test setup, the financial-math tolerance that was measured too tight at
1e-10, and the measured blind spots of a green suite. - memory-and-plans: how the two memory systems resolve on disk, why per-agent stores are project-scoped and must never be consolidated upward or committed.
The first two are the survivors of an earlier, larger set; the rest arrived the other way round. Everything after scheduled-jobs was extracted from the global CLAUDE.md, which is billed into every session under it and had grown past the point where that was worth paying. The rule that decides the split: a guard — anything that stops a wrong action, including "this looks like an obvious improvement but was measured to be wrong" — stays in CLAUDE.md; the measurement behind it, the falsifying control and the retraction story move into a skill. claude-scripts/claude-md-weight.sh measures the result and owns the threshold, so no number here can drift.
Each extracted skill is reached from CLAUDE.md by a line naming the skill and the situation that should trigger it. A bare "see the X skill" does not fire — nothing loads it.
Why skills instead of just writing longer agent prompts? Because skills are reusable across agents, versionable independently, and don't bloat agents that don't need them.
Hooks (4) — 2 advisory + 2 blocking
- secret-scan (PreToolUse → Write|Edit): blocks writes containing API key patterns (
sk-,AKIA,ghp_, etc.), excludes.env.example(blocking) - push-build-gate (PreToolUse → Bash
git push): runs the build and then the test suite before the push goes out, and blocks on a build failure, a compiler warning, or a red suite. A suite that never finishes is bounded and non-blocking — fail closed on the guard's verdict, open on the guard's own malfunction. Skip the test stage withTEST_GATE_SKIP=1 git push. Its dispatch logic has its own harness,hooks/push-build-gate/test_hook.sh. A third tier,payload_gate.py, reads the Next.js build's own route table: it blocks a render-mode flip (a route that used to be prerendered is now dynamic) and warns on payload growth against a per-repo baseline — 3x versus either the last push or the first reading, plus a 1 MiB backstop per prerendered file. Accept and re-record withPAYLOAD_GATE_ACCEPT=1 git push. It no-ops on anything that isn't a Next.js build (blocking) - auto-format (PostToolUse → Write|Edit): runs Prettier / Ruff / rustfmt on the edited file if the project has the corresponding config — silent if not (advisory)
- stale-readme-guard (PreToolUse → Bash
git push): checks unpushed commits for deploy/dep changes without a README.md update (advisory)
Half the hooks are advisory — in a system where I don't review code, I need Claude Code to exercise judgment, not pass checklists. There are two blocking exceptions now: secret scanning, because accidentally committing an API key is irreversible, and the push build gate, because shipping a broken build is a different kind of irreversible — it's live the moment it deploys.
The CLAUDE.md at the root is the backbone — ~120 lines of rules that apply to every project. The most important ones:
Important
The 3-level escalation cascade. Each level forces a different approach. No retrying the same level twice. Every fix attempt requires a stated root cause hypothesis — no "let me try a different approach" without a new theory.
flowchart LR
A["🐛 Bug"] --> B["L1: Direct fix
hypothesis → fix"]
B -->|works| Z["✅ Done"]
B -->|"still broken"| C["L2: Systematic debugging
investigate → hypothesize → fix → verify"]
C -->|works| Z
C -->|"still broken"| D["🛑 STOP"]
D --> E["L3: Troubleshooter
inherit · structural diagnosis"]
E --> F["📋 New plan"]
F --> G["🔧 Implementer
executes plan"]
G --> Z
style D fill:#dc2626,color:#fff
style E fill:#8B5CF6,color:#fff
style G fill:#3B82F6,color:#fff
style Z fill:#10B981,color:#fff
Warning
Bug triage before code. When I report a bug, Claude Code must rule out environment issues first — stale cache, service worker, old build. The most common "bugs" in my portfolio weren't bugs at all.
Note
Docs in the same commit as code. There's no per-repo manifest file — docs are README.md (including its YAML frontmatter: name/tagline_fr/tagline_en/facts_fr/facts_en) and CLAUDE.md, and both ship with the feature, not as an afterthought. If the commit changes behavior, it changes documentation.
The elegance check. Before presenting non-trivial work, pause and ask: "Is there a more elegant way?" I don't review code — Claude Code is the entire quality bar.
The generalization check. Before implementing a specific request, consider whether it's a special case of a more general pattern. If the general solution is roughly the same effort, implement the general version. If not, implement the specific case cleanly — it's easier to generalize clean code later than to simplify over-engineered code.
These aren't arbitrary choices. Each one came from a specific failure. Read the full story for the context behind each decision.
| Decision | Alternative I considered | Why I went this way |
|---|---|---|
| 3-level escalation cascade | Binary "2 fails → architect" | The old binary rule skipped a crucial step: structured debugging. Level 2 (systematic debugging skill) catches tricky-but-not-architectural bugs without invoking opus. |
| Troubleshooter never codes | One agent diagnoses and implements | When the same agent diagnoses and codes, it's biased toward solutions it can implement quickly rather than the right solution. |
| Advisory hooks + 2 blockers | All advisory or all blocking | Most hooks should nudge, not gate. The exceptions: secret scanning (an irreversible leak) and the push build gate (an irreversible bad deploy) both block. |
| Property-based tests for invariants | Unit tests only | Unit tests verify examples. Property tests verify laws — "transfers preserve totals" catches edge cases no human would write. |
| Model selection per task | Always use the best model | Haiku is perfect for audits. Sonnet handles 80% of implementation. Opus is for architecture and complex cross-file work. Matching model to task is a quality decision, not just a cost one. |
| Agent memory over lesson files | Flat markdown files per project | Files had no structure, no auto-injection, no compaction. Agent memory is read at startup, written automatically after corrections, and split when it grows too large. |
| Skills as preloaded context | Dynamic tool calls | Skills need to be available before the agent starts thinking. Dynamic loading means the agent might not know what it needs to know when making its first decision. |
| Derived inventory + README frontmatter + editorial file (no hand-typed manifest) | One manifest per repo (.portfolio.yml) |
A hand-typed manifest went stale the moment you renamed an app — 26 files × ~18 fields nothing but the tooling policing them ever read. The inventory is built by observing GitHub, stack, and live-URL health at build time, so nothing hand-typed can drift. |
Note
The setup is fully modular — you can copy everything, cherry-pick individual pieces, or just read and adapt the patterns to your own system.
git clone https://github.com/w2ur/claude-code-setup.git
cp -r claude-code-setup/commands/ ~/.claude/commands/
cp -r claude-code-setup/agents/ ~/.claude/agents/
cp -r claude-code-setup/skills/ ~/.claude/skills/
cp -r claude-code-setup/hooks/ ~/.claude/hooks/
cp claude-code-setup/CLAUDE.md ~/.claude/CLAUDE.mdCopying the scripts is not enough — hooks don't run until they're registered in ~/.claude/settings.json. Merge hooks/settings.hooks.json into your own settings file (jq -s '.[0] * .[1]' ~/.claude/settings.json claude-code-setup/hooks/settings.hooks.json > /tmp/settings.merged.json && mv /tmp/settings.merged.json ~/.claude/settings.json if you don't already have a hooks key — see hooks/README.md for the manual-merge path if you do, plus how to verify a hook actually fired).
Then edit CLAUDE.md and the agent files to replace w2ur, {portfolio-site}, and other placeholders with your own values.
The setup is modular. Want just the escalation system? Copy the global CLAUDE.md's bug-handling cascade, the implementer agent, and the troubleshooter agent. Want just the maintenance workflow? Copy /cleanup and /tech-debt. Each piece works independently — the full system is better, but partial adoption works fine.
Browse the files, understand the patterns, and build your own version. The philosophy doc explains the "why" behind each choice — that's often more valuable than the "what."
This system was built for a very specific situation — here's how to adapt it to yours
If you have 1-3 apps: You don't need half of this. Drop portfolio-sync and portfolio-audit — they exist because I have 10+ repos to keep in sync. Keep the implementer and troubleshooter agents, the escalation cascade in the global CLAUDE.md, and the two blocking hooks. That alone is a massive upgrade over bare Claude Code.
If you work in a team: The escalation rules still apply — they're about AI behavior, not team size. The implementer/troubleshooter split actually maps well to teams where juniors implement and seniors review. The memory system needs thought, though — per-developer or shared? I haven't solved that one.
If you have a different stack: My skills are specific to my projects. Throw them out and write your own. The architecture (commands → agents → skills + hooks) doesn't care what language you write in.
If you use a monorepo: The portfolio-sync agent assumes separate repos. You'd need a monorepo-aware version. Everything else works as-is.
Caution
- Application code. Zero lines of app code. Just configuration.
- Personal data. Public apps are mentioned by name (they're live on the internet anyway). Private apps, personal URLs, and paths are anonymized.
- A universal solution. This works for me. Parts of it will work for you. All of it, probably not — and that's fine.
This repo stays in sync with my actual ~/.claude/ setup via /sync-setup — a command that runs a Python sync script to copy, anonymize, and audit for data leaks. After any workflow change (new agent, renamed command, new hook), I run /sync-setup and the repo updates itself. See scripts/ for details.
The global CLAUDE.md also references fourteen helper scripts under ~/.claude/scripts/ — they're published in claude-scripts/, synced and anonymized the same way as everything else.
If something looks outdated, it probably means I changed my setup and haven't synced yet. Open an issue — it's a good nudge.
This is a personal setup, not a framework. I'm not accepting PRs that change the architecture or philosophy. But I welcome:
- Bug reports: if something is broken, inconsistent, or unclear
- Questions: open an issue, I'll answer and improve the docs
- Adaptations: if you built something interesting on top of this, I'd love to hear about it
- claude-code-best-practice — the comprehensive reference (encyclopedic, community-maintained)
- Anthropic's Claude Code docs — official documentation
- William Revah on LinkedIn — where I write about building apps with AI, cognitive biases, and whatever else I'm curious about
MIT — copy, adapt, share. Attribution appreciated but not required.
Built by William — someone who doesn't read code but ships 10+ apps anyway.
Read the philosophy for the full story.
