v3.1 ยท 2026-05-01 ยท CC LAYERING TUTORIAL ยท D2 reopened, sessions-awareness added, D3 stale claims fixed

Claude Code, all the layers, in plain English

A friendly walk-through of every file Claude Code looks at, where it lives, how layers stack, and what wins on conflict. Every claim cites the official Anthropic doc URL or the NotebookLM notebook it came from. Use the buttons in the top-right to switch theme or export your notes.

The 30-second version. Most projects need three files at the project level: CLAUDE.md (project instructions), .mcp.json (MCP servers), and .env (secrets โ€” note: Claude Code itself doesn't read this, your tools do). settings.json is also project-level but optional; defaults work fine. Everything else โ€” skills, rules, agents, hooks, plans, custom commands โ€” is opt-in. Almost everything exists at both levels: a personal copy at ~/.claude/ for stuff that follows you across all projects, and a project copy at <project>/.claude/ for stuff that ships with one repo. When the same thing exists in both places, more-specific usually wins โ€” but skills flip the rule (personal > project), and that's the one trap to remember.
"Wait, can EVERYTHING be project-specific OR user-global?" Yes. Here's the full table at a glance โ€” the layering rules section below explains who wins on conflict.
Thing User-global Project Project-local (gitignored) On conflict
CLAUDE.md ~/.claude/CLAUDE.md ./CLAUDE.md or ./.claude/CLAUDE.md ./CLAUDE.local.md Concatenated โ€” all loaded, project-most-specific last (highest attention)
settings.json ~/.claude/settings.json .claude/settings.json .claude/settings.local.json Local > Project > User; arrays merge; scalars replace
Skills ~/.claude/skills/ .claude/skills/ โ€” Personal > Project โš ๏ธ (the trap)
Rules ~/.claude/rules/ .claude/rules/ โ€” Project > User
Agents (subagents) ~/.claude/agents/ .claude/agents/ โ€” Project > User
Hooks inside ~/.claude/settings.json inside .claude/settings.json inside .claude/settings.local.json All fire in parallel, merged + deduplicated13
Custom commands (legacy) ~/.claude/commands/ .claude/commands/ โ€” Skill of same name wins; otherwise more-specific wins13
Plans (plan-mode files) ~/.claude/plans/ (default) customizable via plansDirectory setting โ€” Single location at a time (configured by setting, not layered)13
MCP servers ~/.claude.json (User scope) .mcp.json at project root (Project scope) ~/.claude.json (Local scope, project-keyed) Local > Project > User
.env (Claude Code itself) Not read by Claude Code at all โ€” your tools do9 โ€”

Quick glossary

User-global
Lives in ~/.claude/ on Mac, /home/dev/.claude/ on VPS. Follows you across every project.
Project-local (or "project-scoped")
Lives inside a project's .claude/ folder. Goes in version control so the team gets it.
Local (just "local")
A third scope โ€” same project, but personal to your machine. Files like CLAUDE.local.md and .claude/settings.local.json. Auto-gitignored.
Walk-up
When Claude starts in a folder, it checks that folder for CLAUDE.md, then the parent, then the grandparent, all the way to the filesystem root. Every match it finds gets concatenated โ€” they don't override each other.1
@import
Inside any CLAUDE.md you can write @path/to/other.md to pull another file in. Works with @~/path for home. Max 5 hops deep. First-time imports of files outside the project show an approval dialog.1
Override
One layer's value replaces another. Used for scalar settings (e.g. "model": "opus"). Most specific wins.
Additive merge
Both layers contribute; nothing gets dropped. Used for arrays in settings (e.g. permissions.allow) โ€” concatenated and deduplicated.2
Deep-merge
For nested objects, every key is merged level by level (instead of the whole object getting replaced). Anthropic does this for managed settings drop-ins.2
Frontmatter
YAML at the top of a markdown file, between two --- lines. Skills, agents, and path-scoped rules use it for metadata.3
Dotfile
A file or folder starting with . (like .claude/). Hidden from Finder by default. Tools use them so they don't visually clutter your home directory.
CWD
Current working directory โ€” wherever you typed claude from. Walk-up starts here.
Plugin
A self-contained bundle of skills/agents/hooks/MCP servers. Plugins use a plugin-name:skill-name namespace so they can't collide with your own.3

The big picture โ€” what's where

Two homes. Color coded:

~/ # your home (Mac: /Users/<you>, Linux: /home/<you>) โ”‚ โ”œโ”€โ”€ .claude/ USER-GLOBAL โ€” follows you everywhere โ”‚ โ”œโ”€โ”€ CLAUDE.md your personal preferences (every session reads this) โ”‚ โ”œโ”€โ”€ settings.json your defaults (model, hooks, permissions) โ”‚ โ”œโ”€โ”€ skills/ personal skills (e.g. ~/.claude/skills/explain-code/SKILL.md) โ”‚ โ”œโ”€โ”€ agents/ personal subagents โ”‚ โ”œโ”€โ”€ rules/ personal rules (path-scoped or always-on) โ”‚ โ”œโ”€โ”€ commands/ legacy โ€” merged into skills (still works) โ”‚ โ”œโ”€โ”€ internal โ€” Claude's auto-memory (don't hand-edit) โ”‚ โ”œโ”€โ”€ internal โ€” full session transcripts โ”‚ โ””โ”€โ”€ internal โ€” plan-mode files (officially documented; default ~/.claude/plans/) โ”‚ โ”œโ”€โ”€ .claude.json USER-GLOBAL โ€” MCP servers (local + user scopes) โ”‚ โ”œโ”€โ”€ other CLIs' homes (peers, no overlap with Claude) โ”‚ โ””โ”€โ”€ projects/ where your code lives (just a folder) โ”‚ โ””โ”€โ”€ my-project/ PROJECT-SCOPED โ€” checked into git โ”œโ”€โ”€ CLAUDE.md team-shared instructions (or .claude/CLAUDE.md, both work) โ”œโ”€โ”€ CLAUDE.local.md your personal notes (auto-gitignored) โ”œโ”€โ”€ .mcp.json MCP servers shared with the team โ”œโ”€โ”€ .env secrets โ€” read by your tools, not by Claude Code itself โ”œโ”€โ”€ .gitignore standard git stuff โ””โ”€โ”€ .claude/ project's Claude config root โ”œโ”€โ”€ settings.json team-shared settings (committed) โ”œโ”€โ”€ settings.local.json YOUR personal overrides (auto-gitignored) โ”œโ”€โ”€ skills/ project skills โ”œโ”€โ”€ agents/ project subagents โ”œโ”€โ”€ rules/ project rules (path-scoped or always-on) โ””โ”€โ”€ commands/ legacy โ€” same as skills

The two-tree mental model. Pretty much every Claude Code config file has this same shape: a personal copy at ~/.claude/, a team copy at <project>/.claude/, and sometimes a .local.-suffixed personal-on-this-project copy. The interesting part is what happens when both copies exist โ€” see "Layering rules" below.

The catalog โ€” every file, plain English

Click any row to expand. Required vs optional, locations, tiny example.

CLAUDE.md — persistent instructions

What it is. A markdown file with rules and context that Claude reads at the start of every session.1 Loaded as a user message after the system prompt โ€” strong influence, not absolute enforcement.

Locations (highest scope to lowest):

Required? No โ€” but if you skip it, Claude has zero project context.

Minimal example:

# My project

- Always use 2-space indent
- API handlers live in `src/api/handlers/`
- Run `npm test` before committing

Gotcha. Claude Code reads CLAUDE.md, NOT AGENTS.md.1 If your repo uses AGENTS.md for other agents, add a one-line CLAUDE.md that does @AGENTS.md.

settings.json — behavior config

What it is. JSON file that controls model, permissions, hooks, and most other Claude Code behavior.2

Locations:

Required? No.

Minimal example:

{
  "permissions": { "allow": ["Bash(npm test)"] },
  "model": "opus"
}
settings.local.json — your personal overrides for this project

What it is. Same JSON shape as settings.json, but personal-to-this-machine. Auto-gitignored.2 Use it for sandbox URLs, experimental flags, or to disable a noisy team-wide hook just for you.

Location: always .claude/settings.local.json at the project root.

.mcp.json — MCP servers shared with the team

What it is. The only file-based MCP config. Lives at the project root and gets committed so your teammates have the same MCP servers.5

Locations: project root only โ€” ./.mcp.json.

But there are also user-scope and local-scope MCP servers โ€” those don't live in .mcp.json. They live in ~/.claude.json (your home directory). The CLI manages them via claude mcp add --scope user|local|project.5

Approval flow: First time you open a project with a .mcp.json, Claude asks you to approve each server. Reset with claude mcp reset-project-choices.

~/.claude.json — the user-level config (different from ~/.claude/settings.json!)

Easy to miss. This is a single JSON file at ~/.claude.json โ€” not inside the ~/.claude/ folder. It stores both user-scope MCP servers (available across all projects) and local-scope MCP servers (per-project, but private to you).5

Different from ~/.claude/settings.json, which is your user-level settings. The two coexist.

.env — secrets

What it is. Plain-text key=value pairs. Claude Code itself does not read .env โ€” your tools do (the apps you're building, helpers like get-secret, etc.). Some other CLIs (Gemini CLI) do read project .env directly.9

Locations: wherever your tools expect them. Common: project root, ~/.env.

Required? Only if you're using credentials.

Always gitignore secrets. No exceptions.

.gitignore — standard git

Not Claude-specific, but worth knowing what Claude expects:

skills/ — reusable playbooks

What it is. Each skill is a folder with a SKILL.md file. The folder name becomes /skill-name. Used for repeatable playbooks (deploy, commit, fix-issue, etc.).3

Locations:

Trap. Personal beats project for skills. (Yes, that's the opposite of agents โ€” see Layering rules.)3

Minimal SKILL.md:

---
description: Deploy the app to production. Use when user says deploy.
disable-model-invocation: true
---

1. Run tests
2. Build
3. Push to deploy target
agents/ — specialized subagents

What it is. Markdown files with YAML frontmatter that define a subagent (its system prompt, tools, model). Claude delegates tasks to them.4

Locations (priority โ€” lower number wins):

Trap. For agents, project beats user โ€” opposite of skills. Memorize this.4

rules/ — modular instructions, optionally path-scoped

What it is. Markdown files in .claude/rules/ that load alongside CLAUDE.md. With YAML frontmatter (paths:), they only load when matching files are touched.1

Locations: ~/.claude/rules/ (user) and .claude/rules/ (project). User loads before project, so project wins on conflict.1

Cool feature: the .claude/rules/ directory supports symlinks, so you can share rule sets across projects.1

Path-scoped example:

---
paths:
  - "src/api/**/*.ts"
---

# API rules
- All endpoints validate input
- Use the standard error format
commands/ — legacy slash commands (now part of skills)

Status: A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy and work the same way.3 Existing commands keep working; new ones should be skills (skills support supporting files, frontmatter controls, etc).

hooks — NOT a folder, just a key in settings.json

What it is. Lifecycle scripts triggered by events (pre/post tool use, session start, etc.). Configured under the "hooks" key inside any settings.json at any scope.6

There is no ~/.claude/hooks/ folder. If you see one in your file tree, it's not Anthropic-recognized.

Plugins are the exception โ€” plugins can ship a hooks/hooks.json file.6

Minimal example (in ~/.claude/settings.json):

{
  "hooks": {
    "Notification": [{
      "matcher": "",
      "hooks": [{ "type": "command", "command": "osascript -e 'display notification ...'" }]
    }]
  }
}
~/.claude/projects/ — internal session storage

This is NOT where your work goes. It's Claude Code's internal storage for session transcripts (JSONL files) and auto-memory.1

Each project gets a directory keyed off the git repo: ~/.claude/projects/<key>/. Inside:

Don't hand-edit these unless you know what you're doing. Use /memory from inside a session to view & edit safely.1

plans/ — plan-mode files (officially documented)

What it is. Plan-mode writes plan files here. There's an official plansDirectory setting that controls the location.13

Default location: ~/.claude/plans/. Override per-project with plansDirectory in .claude/settings.json:

{
  "plansDirectory": ".claude/plans"
}

Layering: plans live in ONE place at a time โ€” whichever plansDirectory wins on standard settings precedence (Local > Project > User). They don't merge across scopes the way CLAUDE.md does.

Layering rules โ€” what wins on conflict

This is the hot zone. Different mechanisms have different rules. The quick-reference matrix:

MechanismLoads howWins on conflictSurprise level
CLAUDE.mdWalks UP, concatenatedAll loaded; project-most-specific has highest attentionStandard
@importInline expansion, max 5 hopsImported text inserted at the import pointStandard
settings.jsonFixed 5-layer scopes (NO walk-up)Local > Project > User; arrays merge, scalars replaceโš ๏ธ Doesn't walk up like CLAUDE.md
SkillsDiscovered at session start (lazy body)Personal > Projectโš ๏ธ Opposite of "more-specific wins"
AgentsDiscovered at session startProject > Userโš ๏ธ Opposite of skills
RulesAlways-on or path-scopedProject > UserStandard
MCP serversThree named scopesLocal > Project > Userโš ๏ธ Local + User both in ~/.claude.json
HooksConfigured in settings.json at any scopeAll fire in parallel (merge + dedup)โš ๏ธ Don't override each other
Plans (plansDirectory)Single dir at a timeHighest-precedence settings.json winsStandard
Custom commandsLazySkill of same name wins; else more-specificStandard
.envNot read by Claude CodeYour tools decideโš ๏ธ Other CLIs DO read it

Full details for each row below.

1. CLAUDE.md walk-up โ€” concatenation, not override

Local (last loaded, highest attention)./CLAUDE.local.md
Project./CLAUDE.md or ./.claude/CLAUDE.md
Parent dirs (walk-up)../CLAUDE.md, ../../CLAUDE.md, โ€ฆ
User~/.claude/CLAUDE.md
Managed/Library/Application Support/ClaudeCode/CLAUDE.md

Rule: All of these are concatenated, not overridden. Order in context: filesystem root โ†’ CWD (most specific last). Most-specific instructions get higher attention because they're nearest the conversation.1

Conflict? Claude may pick one arbitrarily. Manage it by writing more-specific instructions in deeper-scoped files.

2. @import โ€” relative paths, max 5 hops

3. Settings โ€” 5 layers, fixed scopes (NO walk-up)

Managed (cannot be overridden)system-level
CLI args (per-session)--flag
Local.claude/settings.local.json
Project.claude/settings.json
User~/.claude/settings.json
"Settings.json doesn't walk up" โ€” what that means. CLAUDE.md walks UP the directory tree: if you start Claude in ~/projects/myapp/src/api/, it looks for CLAUDE.md in src/api/, then myapp/, then projects/, then ~/, and concatenates everything it finds.1 settings.json does NOT do that.13 It only looks at the five fixed scopes above โ€” your home (~/.claude/), this project's .claude/, and managed/CLI. It will not pick up a stray settings.json sitting in some parent folder. If you put one there, Claude Code ignores it.

Merge rule:2

4. Skills precedence โ€” Personal beats Project โš ๏ธ

Enterprisemanaged location
Personal~/.claude/skills/<name>/
Project.claude/skills/<name>/
Plugin (namespaced โ€” can't conflict)plugin:skill

If a skill name exists in both ~/.claude/skills/ and .claude/skills/, the personal one wins. This is the opposite of "more-specific wins."3

Skills vs agents disagree on precedence. Skills: Personal > Project. Agents: Project > User. This is documented but easy to miss. If you have a name collision and it's behaving "wrong," check which mechanism it is.

5. Agents precedence โ€” Project beats User โš ๏ธ

Managedmanaged location
Project (priority 3).claude/agents/<name>.md
User (priority 4)~/.claude/agents/<name>.md
Plugin (priority 5)plugin agents/

For agents, more-specific (project) wins.4 Standard intuition. But again โ€” opposite direction from skills.

6. Rules precedence โ€” Project beats User

User-level rules (~/.claude/rules/) load before project rules (.claude/rules/), giving project rules higher priority on conflict.1 Same direction as agents.

7. MCP scope hierarchy โ€” Local > Project > User

Local (default scope)~/.claude.json (per-project private)
Project.mcp.json
User~/.claude.json (cross-project)

Three scopes; matched by name (or by endpoint URL for plugins/connectors). Local is the default when you run claude mcp add without --scope.5

The trap: Local AND User scope MCP servers both live in ~/.claude.json (your home), not in .claude/. Only Project-scope lives in .mcp.json.

8. Hooks layering โ€” all fire in parallel, no override

Hooks live inside settings.json, so they technically inherit settings layering. But the way hooks combine is different from how scalar settings combine.

Rule: when the same hook event has handlers at multiple levels (user, project, local, managed, plus plugin and skill/agent frontmatter), all matching hooks fire in parallel. They merge rather than override. Identical hook commands are auto-deduplicated.13

What this means in practice: if you've got a notification hook on Stop in your user settings, and a teammate added a different Stop hook to the project's .claude/settings.json, both fire when Claude stops. Project hooks ADD to user hooks; they don't replace them.

9. Plans (plansDirectory) โ€” single location, configurable

Plan-mode files save to a single directory at a time. Default: ~/.claude/plans/.13 You change it with the plansDirectory setting in any settings.json (so the standard 5-layer settings precedence picks the winner โ€” no walk-up, no concatenation across multiple plan directories).

10. Custom commands (~/.claude/commands/ or .claude/commands/)

Still fully supported. Live at user-global OR project. New work should be skills, but old commands keep working.3

Conflict rule: if both a command and a skill share the same name, skill wins.13 Otherwise, more-specific (project) wins over user-global for same-name commands.

11. .env loading โ€” varies by tool, not by Claude

Claude Code itself does NOT auto-read any .env file. The ~/.env convention you use (with get-secret) is a community/safety pattern, not a Claude Code feature. Some other CLIs (Gemini CLI specifically) DO read project-level .env directly.9

Three concrete scenarios

Walk-throughs so the abstract rules become muscle memory.

Scenario 1 โ€” Same GROK_API_KEY in ~/.env and <project>/.env

Setup. You have GROK_API_KEY=xK1... in ~/.env (your global) and a different value GROK_API_KEY=xK2... in this project's .env.

Step 1. You start a Claude Code session in the project. Claude does not read either .env.9
Step 2. You ask Claude to call the Grok API. Claude runs curl -H "Authorization: Bearer $GROK_API_KEY" ....
Step 3. Whether the bash invocation finds $GROK_API_KEY depends on YOUR shell environment. If your shell sourced ~/.env at startup, that's what's set. If you also exported the project value via your shell's hooks or a direnv tool, the project value wins because it was set later.
Outcome: Claude Code itself contributes nothing. The "winner" is whoever set the variable most recently in your shell process. Action item: if you want a project-specific Grok key, use get-secret GROK_API_KEY ./project/.env (the explicit path argument) so it's deterministic.

Scenario 2 โ€” Same skill name secret-scan in both scopes

Setup. You have ~/.claude/skills/secret-scan/SKILL.md with your generic patterns. The project also has .claude/skills/secret-scan/SKILL.md with team-specific extra patterns.

Step 1. Claude session starts. Both skill directories are scanned.3
Step 2. Skill precedence rule fires: Personal > Project.
Step 3. The ~/.claude/skills/secret-scan/ SKILL.md becomes /secret-scan. The project copy is shadowed and never invoked.
Outcome: Your personal skill wins, even though the project's is "more specific." Action item: if you want the project-specific patterns, give them a different skill name (project-secret-scan) or reference the project file from your personal skill.

Scenario 3 โ€” Conflicting CLAUDE.md files

Setup. ~/.claude/CLAUDE.md says "Always use OAuth for new auth flows." Project CLAUDE.md says "Use Cloudflare service tokens for new auth flows โ€” OAuth is forbidden in this codebase."

Step 1. Walk-up loads both: user-level first, project-level second.1
Step 2. Both instructions go into the conversation context. They are NOT overridden โ€” both are visible to Claude.
Step 3. Because project-level loads LAST (more specific, higher attention weight), Claude is more likely to follow the service-token rule. But "more likely" is not "guaranteed" โ€” Claude may pick one arbitrarily on a tricky case.1
Outcome: Project usually wins on attention, but you might still see drift. Action item: remove the conflicting line from the user CLAUDE.md, OR rewrite the project line to be explicit: "In this project, ignore any user-level OAuth instructions; we use service tokens. Reason: see .claude/rules/auth.md."

Multi-CLI parity (Claude / Gemini / Codex / OpenCode)

If you run multiple CLIs side-by-side, here's the cross-reference.9

Feature Claude Code Gemini CLI Codex CLI OpenCode
User-global config ~/.claude/settings.json
+ ~/.claude.json
~/.gemini/settings.json ~/.codex/config.toml ~/.config/opencode/opencode.json
Project config .claude/settings.json (+.local.json) .gemini/settings.json .codex/config.toml (trusted projects) opencode.json at project root
Project context file CLAUDE.md GEMINI.md AGENTS.md (or codex.md) AGENTS.md
Context walks up? Yes (CWD โ†’ root) Yes โ€” AND down (subdirs to depth 200)14 Yes โ€” top-down (project root โ†’ CWD)15 Yes (CWD up to nearest .git)16
Agents folder ~/.claude/agents/ ยท .claude/agents/ ~/.gemini/agents/ ยท .gemini/agents/14 ~/.codex/agents/ ~/.config/opencode/agents/ ยท .opencode/agents/
Skills folder ~/.claude/skills/ ยท .claude/skills/ ~/.gemini/skills/ ยท .gemini/skills/
+ ~/.agents/skills/ ยท .agents/skills/14
~/.codex/skills/
+ ~/.agents/skills/ ยท .agents/skills/15
~/.config/opencode/skills/ ยท .opencode/skills/
+ flexible (loads from .agents/ too)16
Reads .env? No (your tools do) Yes โ€” walks up CWD โ†’ home; also ~/.gemini/.env14 Yes โ€” project root .env15 Yes โ€” project .env16
MCP config .mcp.json (project) +
~/.claude.json (user/local)
Inside settings.json at mcpServers (all scopes merged)14 Inside config.toml at [mcp_servers.*]15 Inside opencode.json "mcp" block16
~/.agents/skills/ Not recognized natively (would need symlink) Officially recognized14 Officially recognized15 Recognized (flexible scanning)16
Big update โ€” ~/.agents/skills/ is OFFICIAL for three of the four CLIs. Earlier research treated it as community/orchestrator-only convention. The dedicated CLI notebooks confirm Gemini, Codex, and OpenCode all load skills from ~/.agents/skills/ (and .agents/skills/ walked up from CWD) natively. Only Claude Code doesn't โ€” but you can symlink or use the orchestrator approach to bridge.141516

Other walk-up surprises:

If you want one source of truth for project context:

Multi-CLI integration โ€” making them call the same stuff

The parity table above lays out what each CLI looks for. This section answers: can I write something once and have all four CLIs use it? Short version โ€” natively, no. The CLIs do not share directories.9 But there are five well-traveled bridge patterns.

Pattern 1 โ€” Symlinks (single source of truth)

Pick one CLI's skill directory as canonical, then symlink the others into it.9

# Make Claude's skills the canonical home
mkdir -p ~/.claude/skills
ln -s ~/.claude/skills ~/.gemini/skills
ln -s ~/.claude/skills ~/.codex/skills

Now every skill you author at ~/.claude/skills/<name>/SKILL.md appears in all three CLIs. Same trick works for agents/, rules/ (where the CLI has an equivalent), and any custom config folder.

Caveat: the YAML frontmatter fields differ between CLIs. disable-model-invocation is Claude-specific. A skill that uses Claude-only fields will work in Claude but be partly ignored elsewhere. Keep skills written to the lowest-common-denominator frontmatter (name, description, body) for portability.

Pattern 2 โ€” ~/.agents/skills/ as the universal home (officially recognized by 3 of 4 CLIs)

Updated finding: earlier research called this a community-only convention. Per the dedicated Gemini, Codex, and OpenCode notebooks, all three CLIs natively load skills from ~/.agents/skills/ and walk up to find .agents/skills/ in your project tree.141516 Only Claude Code doesn't โ€” that's the gap.

Setup: drop a skill at ~/.agents/skills/<name>/SKILL.md. It's instantly visible to Gemini, Codex, and OpenCode. To make Claude see it too, symlink:

ln -s ~/.agents/skills ~/.claude/skills/_shared

Now Claude sees a _shared namespace whose contents are the universal skill set; the other CLIs see them at the canonical ~/.agents/skills/ path.

Trade-off: still need that one symlink for Claude. Frontmatter compatibility caveat from Pattern 1 still applies โ€” Claude-only frontmatter fields are ignored when the same skill loads in Gemini/Codex.

Pattern 3 โ€” AGENTS.md as universal context (the cleanest bootstrap)

Write your shared rules in AGENTS.md at the project root. Then have every CLI's specific file import it.9

# In AGENTS.md (the universal source of truth)
- Use 2-space indentation
- API handlers live in src/api/handlers/
- Never commit .env files
# In CLAUDE.md
@AGENTS.md

# Claude-specific instructions below
- Use plan mode for changes under src/billing/
# In GEMINI.md (Gemini reads its own file by default โ€” point it at AGENTS.md)
@AGENTS.md

OpenCode and Codex already use AGENTS.md natively. Claude's @import syntax pulls it into CLAUDE.md.1 Gemini supports its own @ imports in GEMINI.md. One file, four CLIs.

Pattern 4 โ€” Universal MCP servers

All four major CLIs support MCP.9 Instead of writing CLI-specific skills, connect every CLI to the same MCP servers. The capabilities you build live in the MCP server (any language), not in the CLI's skill folder.

This is the most portable option for tool-shaped capabilities (database queries, API calls, search). Skills are good for prompts and playbooks; MCP is good for tools.

Pattern 5 โ€” CLI-to-CLI bridge (one CLI as MCP server for another)

Wrap one CLI inside an MCP server so another CLI can call it.9 Example: run a tiny gemini_mcp_server.py that exposes a consult_gemini tool, then add it to Claude's MCP. Now Claude can ask Gemini for a second opinion mid-task.

Useful for: validation passes, multi-model voting, model-shopping. Heavier setup than the other patterns but doesn't require you to translate skills across formats.

Power-user orchestrators (mention only)

Recommendation matrix

Goal Best pattern Why
Same skills/playbooks across all CLIs 1 (symlinks) or 2 (~/.agents/skills/ + orchestrator) Direct file-level sharing with zero translation
Same project context across all CLIs 3 (AGENTS.md + per-CLI @import) Cleanest bootstrap โ€” works without any extra tools
Same TOOLS (database, APIs, search) across all CLIs 4 (universal MCP) MCP is the official cross-CLI tool protocol; build once, connect anywhere
Have one CLI consult another mid-task 5 (CLI-as-MCP) Lets Claude call Gemini for a second opinion, or vice versa
Hot-swap CLI runtimes per task Overstory Use the right model for each subtask without juggling configs
Updated recommendation, your setup:

Macโ†”VPS mirror โ€” what syncs, what doesn't

Your VPS at /home/dev/ mirrors your Mac home. Same shape, different absolute paths. The relative-to-home structure is what matters for muscle memory and scripts.

Heads up โ€” this section is being reworked. P-3's "exact mirror at ~/" assumed each machine's home stays clean enough that "skip OS clutter" is a workable sync rule. The VPS dotfolder reality (24+ folders, mostly machine-specific) disproves that assumption. P-9 proposes a wrapper folder (working name workspace/) as the sync boundary. Tree below shows the P-9 shape; the P-3-flat shape was simpler but doesn't survive contact with real machine state. See D2 above for the full discussion.

Mac: ~/ VPS: /home/dev/ โ”‚ โ•โ•โ• Synced both ways (the sync allowlist) โ•โ•โ• โ”œโ”€โ”€ workspace/ โ”œโ”€โ”€ workspace/ P-9 wrapper โ€” single sync target. Lsyncd + GitHub canonical. โ”‚ โ”œโ”€โ”€ projects/ โ”œโ”€โ”€ projects/ โ”‚ โ”œโ”€โ”€ handoffs/ โ”œโ”€โ”€ handoffs/ โ”‚ โ”œโ”€โ”€ ideas/ โ”œโ”€โ”€ ideas/ โ”‚ โ”œโ”€โ”€ context/ โ”œโ”€โ”€ context/ โ”‚ โ””โ”€โ”€ ... โ””โ”€โ”€ ... โ”œโ”€โ”€ .claude/ โ”œโ”€โ”€ .claude/ CLI configs synced selectively (skills, rules, agents) โ”œโ”€โ”€ .gemini/ โ”œโ”€โ”€ .gemini/ SYNCED similarly โ”œโ”€โ”€ .codex/ โ”œโ”€โ”€ .codex/ SYNCED similarly โ”œโ”€โ”€ .opencode/ โ”œโ”€โ”€ .opencode/ SYNCED similarly โ”œโ”€โ”€ .agents/ โ”œโ”€โ”€ .agents/ universal skills โ€” natively loaded by Gemini/Codex/OpenCode โ”œโ”€โ”€ .env โ”œโ”€โ”€ .env SYNCED via set-secret/get-secret (safety rule) โ”‚ โ•โ•โ• Machine-specific (NOT synced) โ•โ•โ• per-machine MCP local-scope state VPS service state (or Mac dev tooling) tool runtimes โ€” per machine macOS-only โ€” ignored by mirror โ””โ”€โ”€ workspace/<project>/.claude/settings.local.json different per machine BY DESIGN (e.g., tighter permissions on Mac)

Shape parity vs path parity

Two different ways to be "the same":

We chose shape parity. Scripts using ~/workspace/... work on both machines. Scripts using /home/dev/workspace/... only work on VPS.

(Originally locked under P-3 as "exact mirror at ~/" with no wrapper. P-9 reopens this โ€” see D2. The shape-parity principle is unchanged regardless of which option lands.)

What's machine-specific

Cross-machine session awareness (new requirement, 2026-05-01)

Goal: each machine should be aware of the other's CC sessions. VPS Claude should be able to comb through Mac sessions, and vice versa. Plus a session-browser web app indexes BOTH + claude.ai web sessions, with filterable tags.

The trap to avoid. Don't bidirectionally sync ~/.claude/projects/. Each project key has its own auto-memory MEMORY.md โ€” both machines writing to the same project key will conflict and corrupt each other's notes.

The safe pattern โ€” one-way mirror Mac โ†’ VPS, indexer reads from VPS

SourceLocationSync
VPS sessions/home/dev/.claude/projects/...native (already on VPS)
Mac sessions/Users/coach/.claude/projects/...periodic rsync Mac โ†’ VPS into /home/dev/imported-sessions/mac/
claude.ai web sessionscloudAPI pull (or manual export until API ships)

The session-browser web app indexer scans all three sources and tags each session by:

Cheapest first step โ€” a CLAUDE.md hint

Even before sync runs, you can drop a line in each machine's ~/.claude/CLAUDE.md:

# Cross-machine session awareness
The other machine's Claude Code sessions are mirrored read-only at:
  Mac sessions on VPS:  /home/dev/imported-sessions/mac/
  VPS sessions on Mac:  ~/.claude-vps-mirror/  (TBD)

Session-browser web app: https://<your-app>/sessions

Now Claude can find remote sessions on demand (via SSH or local read), even before the indexer is built.

Three decisions to land

D1 โ€” VPS user: keep dev, switch to root, or rename?

โœ“ RECOMMENDATION

Keep dev. Don't run as root. Don't rename to coach.

Why not root

The "[DEV] Claude Code on VPS" notebook is unambiguous: run Claude Code as a non-root named user with sudo. Running as root gives the AI "master keys" โ€” catastrophic if it hallucinates a destructive command or interacts with malicious code.8

This isn't just advice. Claude Code actively refuses to run with --dangerously-skip-permissions if it detects the root user.8 That flag is core to autonomous remote operation, so root literally breaks the use case.

Why not rename to coach

Pending revision impact

None. Current setup already keeps dev. No new candidate revision needed.

D2 โ€” Wrapper folder: none, dev/, work/, or code/?

Four shapes were on the table:

Shape Mac VPS Trade-off
None โ€” projects at home ~/projects/, ~/handoffs/, โ€ฆ /home/dev/projects/, โ€ฆ Cleanest. Mac mixes with macOS clutter (Documents/, Library/).
dev/ wrapper, Mac only ~/dev/projects/, โ€ฆ /home/dev/projects/, โ€ฆ Mac stays tidy. Asymmetric: Mac has one extra level.
dev/ wrapper, both ~/dev/projects/, โ€ฆ /home/dev/dev/projects/, โ€ฆ Symmetric. Recursive name on VPS (user dev has folder dev) โ€” awkward.
work/ wrapper, both ~/work/projects/, โ€ฆ /home/dev/work/projects/, โ€ฆ Symmetric. No recursion. One name discipline applied to both.
โš  SHIFTED โ€” P-9 candidate raised

P-3 Option A is being reconsidered. A new wrapper folder (working name: workspace/) is on the table to make sync rules dramatically simpler. Awaiting reviewer pass.

What changed since v1 of this tutorial

v1 said "P-3 Option A holds: no extra wrapper above ~/projects/." That was based on an assumption that has since broken: you can sync everything in home and just skip OS clutter. New evidence โ€” Jonah's actual VPS state โ€” shows that's not realistic.

The new evidence (VPS dotfolder population)

Jonah's /home/dev/ right now has 24+ dotfolders, almost all machine-specific:

P-3 Option A's sync rule becomes: "sync these visible folders + these dotfolders; skip these other dotfolders + Mac clutter." Two maintenance lists, both growing every time you install a tool.

The P-9 alternative โ€” wrapper folder as sync boundary

Move all sync targets into one wrapper. Sync rule collapses to: "sync the wrapper + this small CLI dotfolder allowlist." One growing list, one stable list.

~/workspace/ โ† single wrapper, fully synced both ways โ”œโ”€โ”€ projects/ โ”œโ”€โ”€ handoffs/ โ”œโ”€โ”€ ideas/ โ”œโ”€โ”€ context/ โ”œโ”€โ”€ apps/ โ”œโ”€โ”€ shared/ โ”œโ”€โ”€ _dev0/ โ””โ”€โ”€ _resources/ PLUS small stable CLI dotfolder allowlist (synced): ~/.claude/ ~/.gemini/ ~/.codex/ ~/.opencode/ ~/.agents/ Everything else stays machine-specific by default.

Wrapper name โ€” Jonah's current ranking

RankNameNotes
1workspace/Verbose but unambiguous. Covers code + ideas + knowledge + handoffs equally. Web-search corroborated as a real dev convention.
2homebase/Warmest. Late addition; just as clear as workspace.
3lab/Three letters. Experiments + production.
4_workspace/Top-sortable variant. Matches _dev0, _resources pattern.
5studio/Vibes-forward.
6basecamp/Climbing metaphor.
7hq/Three letters, lowercase, immediate meaning.
8launchpad/Sci-fi flavor.
โ€”dev/ ยท code/ ยท work/ ยท forge/Rejected (recursion / too narrow / not encompassing / disliked)

Creator-convention evidence

Every successful creator surveyed uses some wrapper โ€” only the name varies:10

CreatorWrapper name
Nate Herkagentic workflows
Jono CatliffClaude
Adam Goodyerrepositories
Nick Saraevbusiness/ + personal/
Network Chuck(no local convention โ€” VPS-as-forever-terminal)
Web-search corroborationReal-world devs converge on workspace/, dev/, src/, repo/, Developer/17

Pending revision impact

P-9 candidate written in pending-revisions.md. architecture-decisions.md NOT edited per the no-silent-rewrites rule. Reviewers asked to weigh in on the wrapper name; everything else in P-9 has solid evidence behind it.

D3 โ€” What's normally at user-home on stock Linux?

โ“˜ REFERENCE

Pure teaching content. No decision to make โ€” just demystifies the question of "what's already here, where am I allowed to put stuff."

/ # filesystem root โ€” system folders, not your home โ”œโ”€โ”€ etc/ # system config (e.g., /etc/passwd, /etc/ssh/sshd_config) โ”œโ”€โ”€ opt/ # third-party packages (e.g., /opt/n8n-autoscaling/) โ”œโ”€โ”€ var/ # logs, mail, databases (/var/log/, /var/lib/postgresql/) โ”œโ”€โ”€ usr/ # system binaries (/usr/bin/, /usr/lib/) โ”œโ”€โ”€ tmp/ # scratch space, cleared on reboot โ”œโ”€โ”€ root/ # the root user's home โ€” distinct from the system root โ”œโ”€โ”€ home/ # where every user's home lives โ”‚ โ””โ”€โ”€ dev/ # your home โ€” what `~` resolves to when logged in as dev โ”‚ โ”œโ”€โ”€ your shell init (default) โ”‚ โ”œโ”€โ”€ login profile (default) โ”‚ โ”œโ”€โ”€ ssh keys + config (you create as needed) โ”‚ โ”œโ”€โ”€ user-installed binaries (~/.local/bin/) โ”‚ โ”œโ”€โ”€ app caches โ”‚ โ”œโ”€โ”€ app configs (XDG standard) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ (everything visible โ€” projects/, .claude/, etc. โ€” is what YOU put here) โ””โ”€โ”€ boot/ dev/ proc/ sys/ # kernel + device interfaces โ€” never touch

Three things to take away

  1. A fresh Linux user-home starts much cleaner than a Mac home. Linux doesn't dump Documents/, Library/, Music/, Pictures/ by default โ€” it's just dotfiles. But it doesn't stay clean: every CLI you install drops a dotfolder (.claude/, .gemini/, .codex/, .pm2/, .notebooklm/, .bun/, etc.) plus visible folders for tool data. After a few months it's just as cluttered as a Mac home โ€” see Jonah's actual VPS at 24+ dotfolders. Both machines need a sync strategy that doesn't rely on home staying clean.
  2. The filesystem root / is NOT your home. /etc/, /opt/, /var/ are system folders managed by the OS and package manager. Don't put your work there. Don't fight them.
  3. The wrapper-folder argument applies on BOTH machines, not just Mac. v1 of this tutorial said the clutter argument was Mac-only. That was wrong โ€” the VPS dotfolder reality disproves it. The cleanest sync rule is "sync the wrapper + an explicit small dotfolder allowlist; everything else stays machine-specific." That's what P-9 proposes (see D2 above).

Files I do not need

Beginner reassurance. If you're starting fresh, you need three files at most: CLAUDE.md, .mcp.json, .env. Everything else is opt-in.

Common things that look mandatory but aren't:

Citations

  1. Anthropic โ€” Memory. https://code.claude.com/docs/en/memory (retrieved 2026-04-30). Walk-up rule, @import syntax, CLAUDE.md scopes, AGENTS.md note, .claude/rules/, auto-memory storage location, plan-mode plans/ folder reference.
  2. Anthropic โ€” Settings. https://code.claude.com/docs/en/settings. 5-layer precedence (Managed โ†’ CLI โ†’ Local โ†’ Project โ†’ User), arrays-merge / scalars-replace / objects-deep-merge, settings.local.json auto-gitignore.
  3. Anthropic โ€” Skills. https://code.claude.com/docs/en/skills. Skill folder layout, Enterprise > Personal > Project > Plugin precedence, frontmatter reference, custom-commands-merged-into-skills.
  4. Anthropic โ€” Sub-agents. https://code.claude.com/docs/en/sub-agents. Agent precedence (Project priority 3 > User priority 4 > Plugin priority 5), .claude/agents/ walk-up.
  5. Anthropic โ€” MCP. https://code.claude.com/docs/en/mcp. Three scopes (Local / Project / User), ~/.claude.json as the home for Local + User scope, .mcp.json as project-only file-based config, approval flow.
  6. Anthropic โ€” Hooks. https://code.claude.com/docs/en/hooks. Hooks live in settings.json (any scope), event types, plugin hooks/hooks.json.
  7. Anthropic โ€” Common workflows. https://code.claude.com/docs/en/common-workflows. Plan mode, --worktree flag, .claude/worktrees/ directory.
  8. NLM โ€” [DEV] Claude Code on VPS (notebook 7db6ea97, retrieved 2026-04-30). Non-root recommendation, --dangerously-skip-permissions root refusal, ~/projects/ structure, worktree project--feature naming convention.
  9. NLM โ€” [DEV] Multi-CLI Orchestration (notebook c4acbd14, retrieved 2026-04-30). Side-by-side parity table, .env handling per CLI, AGENTS.md as cross-CLI convention.
  10. NLM โ€” [DEV] Claude Code Setup (notebook 42f55c2a, retrieved 2026-04-30). Wrapper-folder recommendation (repositories/, projects/, business/), settings precedence walk-through.
  11. Internal research โ€” community patterns. _dev0/migration/review/research/conventions/03-community-patterns.md. Pattern A "Simple Hierarchical" universal across 50+ public repos, Boris Cherny "vanilla setup" quote.
  12. Internal research โ€” prior-chat JSONL mined. _dev0/migration/review/research/conventions/00b-prior-chat-jsonl-mined.md L146-177. ~/.agents/skills/ as community/orchestrator universal registry (later confirmed officially recognized by Gemini, Codex, OpenCode โ€” see citations 14-16).
  13. NLM โ€” [DEV] Claude Code Advanced (notebook e5664a62, retrieved 2026-04-30). settings.json does NOT walk up (fixed scopes only); skills/agents have intentionally opposite precedence; hooks fire in parallel with dedup; plans is officially documented via plansDirectory setting (default ~/.claude/plans/); custom commands still supported, skill wins on name collision.
  14. NLM โ€” [DEV] Gemini CLI (notebook fa8eb9c4, retrieved 2026-04-30). GEMINI.md walks up AND down (subdirs to depth 200); reads .env (CWD up + ~/.env + ~/.gemini/.env); MCP in settings.json at all hierarchy levels merged; skills at ~/.gemini/skills/, .gemini/skills/, AND ~/.agents/skills/, .agents/skills/; agents at ~/.gemini/agents/, .gemini/agents/.
  15. NLM โ€” [DEV] Codex CLI (notebook 81d2e6cb, retrieved 2026-04-30). AGENTS.md walks TOP-DOWN (project root โ†’ CWD); reads .env; MCP in ~/.codex/config.toml + .codex/config.toml (trusted projects); skills at ~/.codex/skills/ + ~/.agents/skills/ + project .agents/skills/ walked up.
  16. NLM โ€” [DEV] OpenCode (notebook cbd59533, retrieved 2026-04-30). AGENTS.md at project root or global; OpenCode walks up to nearest .git; reads .env; MCP in opencode.json "mcp" block; skills at ~/.config/opencode/skills/, .opencode/skills/, plus flexible scanning that picks up .agents/skills/.
  17. Web-search corroboration (2026-05-01). DEV.to community thread "What do you call your folder where you keep your code?" + GitHub Folder-Structure-Conventions guide + World Bank style guide. Real-world dev convention clusters on plain functional names: ~/dev/, ~/workspace/, ~/src/, ~/repo/, ~/Developer/.

Verification log with full claim โ†’ source mapping: _dev0/migration/review/research/conventions/07-verification-log.md. Contradictions resolved: 06-contradictions.md.