Claude Code lives in two folders

Claude Code's entire configuration layer is two folders of plain text files. Here's what goes in each one and how to actually use them.

Last Tuesday I had four terminals open. Three VS Code windows. A different Claude session in each — one pulling database schemas, one reading legacy repos, one turning those notes into instructions for an agent I'm building, and one I genuinely cannot account for.

I was alt-tabbing between them like a raccoon checking which trash can had the good stuff. Copy-pasting prompts out of a file called prompts_GOOD.md. Explaining to each session, again, that I'm on Windows, and no, please don't hand me PowerShell paths inside WSL commands.

It worked. It was also dumb.

The fix isn't more terminals. It's realizing Claude Code is configured by files on disk — and once that clicks, you stop doing this by hand every morning.

Two folders hold all of it#

  • ~/.claude/ — your stuff. Follows you into every project.
  • <repo>/.claude/ — this project's stuff. Lives in git, so your team gets it too.

Both folders take the same files. When they disagree, the project wins. That's the whole override model, and it's why a team can share a setup without stomping on how you personally like to work (fig. 1).

fig-01-two-folders

Here's what goes inside either one:

file what it does
CLAUDE.md Instructions Claude reads at session start. A system prompt you don't retype.
settings.json Permissions, default model, hooks, env vars.
commands/<name>.md A slash command. /name fires your saved prompt.
agents/<name>.md A subagent — its own job, its own context window.
skills/<name>/SKILL.md A skill. A saved procedure with steps.
.mcp.json MCP server definitions. Usually project-only.

There are .local versions too — CLAUDE.local.md and settings.local.json. Those get gitignored automatically, so that's where per-machine weirdness goes. The stuff that shouldn't follow the repo to anyone else's laptop.

CLAUDE.md suggests, settings.json enforces#

This is the one distinction that matters most, and it's the one people miss.

CLAUDE.md is context. The model reads it and tries to honor it. settings.json gates whether a tool call actually runs. Write "don't delete files without asking" in CLAUDE.md and you've got a strong nudge. Write it as a deny rule in settings.json and it's a wall, no matter what the model decides it wants to do.

Preferences go in CLAUDE.md. Hard limits go in settings.json.

Here's an example ~/.claude/CLAUDE.md:

# Environment
- WSL2 Ubuntu on Windows. VS Code via Remote-WSL.
- Plans before edits. Small diffs.

# Defaults
- Read-only against any database unless explicitly told otherwise.
- Treat .env, *.pem, anything in secrets/ as off-limits to read.
- Match existing code style in a repo over personal defaults.

And the settings.json that backs it up:

{
  "model": "opusplan",
  "includeCoAuthoredBy": false,
  "permissions": {
    "allow": [
      "Read", "Grep", "Glob",
      "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)"
    ],
    "ask": ["Write", "Edit", "Bash(git push:*)"],
    "deny": [
      "Bash(rm -rf:*)",
      "Read(.env)", "Read(**/*.pem)", "Read(**/secrets/**)"
    ]
  }
}

In the example above: reads are free, writes ask, destructive and secret-adjacent things are simply off the table. Start there and loosen it when something annoys you.

Three things you can save: a prompt, a procedure, a worker#

Commands, skills, and subagents get conflated constantly. They're three different tools (fig. 2).

fig-02-three-primitives

A slash command is a saved prompt. .claude/commands/<name>.md. Reach for it when the wording is the only thing you're reusing. It takes arguments — $1, $ARGUMENTS — same as any script.

A skill is a saved procedure. .claude/skills/<name>/SKILL.md. Reach for it when the work has real steps, expects a specific output shape, or wants helper files sitting next to it — templates, scripts, a checklist.

A subagent is a saved worker with its own brain. .claude/agents/<name>.md. It runs in a separate context window with its own tool permissions. When it reads twenty files hunting for a pattern, only its summary comes back to you. The twenty file reads stay in its context and vanish when it exits.

That last one is the real trick for long sessions, and it's also the expensive one — every subagent carries its own system prompt and context, so fan-out workflows can burn several times the tokens of a single thread. Worth it for parallel work and noisy investigation. Not worth it for a one-line edit.

A subagent is just markdown with frontmatter:

---
name: schema-investigator
description: Read-only DB and schema analysis. Use proactively for schema pulls
  and "what does this table do" questions.
tools: Read, Grep, Glob, Bash
model: sonnet
---
Investigate schemas and report findings. Never modify data or run DDL/DML.

When invoked:
1. Locate connection config without echoing secrets.
2. Enumerate requested objects.
3. Return a tight summary, not raw dumps.

tools is the safety line — an agent without Write can't write, even if you ask nicely. model pins it to a model regardless of what your session is running. And description is what the main session reads to decide whether to hand off work, so write it like a job posting, not a label.

Homework: point it at your scripts folder#

You know the folder. ~/scripts/, or ~/sql/, or wherever the bodies are buried. Forty files named final.py, final_v2.py, final_REAL.py. Three do nearly the same thing. Two haven't run since 2022 and you're not sure whether they're broken or whether the server they talked to just doesn't exist anymore.

Make .claude/commands/inventory-scripts.md:

---
description: Inventory a scripts directory and propose a sane organization
argument-hint: [path]
allowed-tools: Read, Glob, Grep, Bash
model: sonnet
---
Look at directory `$1`. Do not move or delete anything.

1. Find all .py, .sh, .sql, .ps1 files. Group by extension. Note count and size.
2. Read the first 30 lines of each (skip files >1MB). Infer purpose in one sentence.
3. Flag suspected duplicates: similar names, similar imports, similar first lines.
4. Flag risk: hardcoded paths, embedded secrets, references to hosts or databases
   that may no longer exist.
5. Propose a structure by purpose (backups/, db-pulls/, monitoring/, one-offs/,
   archive/), with renames for the `final_REAL_v3` situation.
6. Write the plan to PLAN.md. Do not execute.

Then run it:

> /inventory-scripts ~/scripts

After runs, read PLAN.md. If it isn't unhinged, follow up in plain English: "Execute the plan. Move, don't delete. Write INDEX.md describing every script. Log moves to MOVES.log so I can undo it."

Now you have an indexed scripts library instead of a folder you're mildly afraid of. The first time it works you'll feel a little strange about it. That's correct — you just handed off an annoying chore to a config file you wrote once. The same command works on ~/sql/, on old dotfiles, on the /tmp/stuff/ directory that's been sitting there for two years.

Pick the model per job, not per session#

There's no autorouter for model selection. You get to chose, at whatever time you want: /model live in a session, --model on the command line at launch, or the model key in settings.json as the default, or model: in the frontmatter of a skill or subagent.

Model selection:

Model reach for it when
sonnet default. Fits about 90% of the work.
opus architecture, hard debugging, synthesizing across many inputs.
haiku mechanical bulk — renames, formatting, simple transforms.
opusplan your session default when there's real planning. Opus plans, Sonnet executes.

When it comes to models, opusplan is the sleeper. Most coding work doesn't need Opus writing the edits; it needs Opus deciding which edits. Substantially cheaper, no drop in quality where it counts.

It also runs outside the editor#

claude -p "<prompt>" runs headless. No session, no editor — reads stdin, prints to stdout, exits. Add --output-format json and it's just another thing in a pipeline:

# Triage SSH brute-force attempts from auth.log
cat /var/log/auth.log \
  | claude -p "Group failed SSH attempts by source IP. Output JSON:
               [{ip, count, first_seen, ports[]}]" \
           --output-format json \
  | jq '.[] | select(.count > 10)'

That's the jump from "AI in my editor" to "AI as a step in a script I already had." Log triage, schema docs, cert inventory — a lot of admin chores collapse into one-liners once the commands and subagents exist.

There's a --dangerously-skip-permissions flag that bypasses confirmations. It's named that way for a reason. Sandboxes only. Against anything with real data, that confirmation prompt is the only thing between a runaway tool call and your filesystem.

When it's set up, complex work is one command#

Reading four legacy repos and turning them into a spec for a downstream agent used to be an afternoon and four browser tabs. Configured, it's:

  1. One session. /add-dir each repo into context.
  2. /build-agent-spec fans out to a repo-summarizer subagent per repo, in parallel. Each runs Sonnet, read-only, writes to summaries/<repo>.md.
  3. The same command hands the collected summaries to an instruction-writer subagent on Opus, which writes the final spec.

One command. The files did the coordinating. And once the interactive version works, the exact same run goes headless with claude -p "/build-agent-spec ..." and lives in cron.

Here's the whole layout:

~/.claude/                    # yours, global
  CLAUDE.md
  settings.json
  commands/<name>.md
  agents/<name>.md
  skills/<name>/SKILL.md

<repo>/.claude/               # this project's, in git
  settings.json
  settings.local.json         # gitignored
  rules/*.md                  # path-scoped instructions
  commands/, agents/, skills/
  .mcp.json
<repo>/CLAUDE.md              # project instructions, in git
<repo>/CLAUDE.local.md        # personal project notes, gitignored

One habit covers all of this: if you've typed a prompt or an explanation twice, it belongs in a file. A CLAUDE.md line, a slash command, a skill, or a subagent. Once the three primitives are clear, picking which one is mechanical.

I still have four terminals open sometimes. Just not for the same job.

Cheat block#

Worth scrolling back to.

In-session slash commands:

command purpose
/init bootstrap a project CLAUDE.md from the repo
/memory show all loaded instructions — debug missing rules here
/model switch model live in the current session
/agents create and manage subagents interactively
/add-dir bring another directory into the session's context
/rewind roll back Claude's edits to an earlier state
/compact summarize current context to free token space
/clear reset context entirely
/ide connect an external terminal to a VS Code window

Keyboard:

key action
Shift+Tab cycle Default → Auto-Accept → Plan modes
Ctrl+Esc open the VS Code extension panel
Ctrl+B background a running task
Alt+K insert @file#lines from the current editor selection

CLI flags:

flag purpose
--model <alias> override model for this launch
-p "..." / --print headless, non-interactive
--output-format json parseable output
--resume reopen a past conversation
--add-dir <path> extra working directory at launch
--dangerously-skip-permissions bypass confirmations; sandbox only