---
name: scaffold-claude-dir
description: Set up or fix a .claude/ directory — skills, rules, CLAUDE.md, settings, and the Cursor equivalents. Use this whenever the user wants to add agent configuration to a repo, asks where a skill or rule should go, asks why a skill isn't showing up in the / menu or a rule isn't being followed, mentions SKILL.md or .claude/rules, or is reorganising an existing .claude directory. Also use it before creating any new skill or rule file, since the discovery rules are unintuitive and getting the layout wrong fails silently.
---

# Scaffolding a `.claude/` directory

The single most important thing to know: **misplaced agent config fails silently.** A skill in the wrong directory doesn't error, it just never appears. A rule with a malformed glob matches nothing. Assume nothing works until verified.

## Decide which mechanism to use

Pick before creating files. Getting this wrong is the most common cause of bloated context
and ignored instructions.

| Content | Mechanism | Loads |
|---|---|---|
| Facts true in every session (build commands, layout, conventions) | `CLAUDE.md` | Always |
| Instructions for one part of the codebase | `.claude/rules/*.md` with `paths:` | When a matching file is read |
| A multi-step procedure with a trigger | `.claude/skills/<name>/SKILL.md` | On invocation |
| Something that must happen deterministically | A hook, not any of the above | On the lifecycle event |

If it's a procedure rather than a fact, it belongs in a skill — skill bodies cost nothing
until used, whereas everything in `CLAUDE.md` is paid for on every single turn. If it must
happen (run tests before commit, never touch generated files), a rule won't guarantee it;
instructions are context, not enforcement. Use a `PreToolUse` hook.

## Reserved names

Only these names are read. A directory with any other name — `patterns/`, `prompts/`,
`docs/` — is inert. Nothing warns you; it simply never loads.

| Path | Location | Shape | Format | Access pattern | Notes |
|---|---|---|---|---|---|
| `CLAUDE.md` | root or `.claude/`, and `~/.claude/` | single file | markdown | Loaded in full at session start, every session. Root file is re-injected after compaction. | Target <200 lines; longer reduces adherence |
| `CLAUDE.local.md` | root only | single file | markdown | Session start, appended after `CLAUDE.md` | Gitignore it |
| `AGENTS.md` | root only | single file | markdown | **Not read by Claude Code.** Cursor and most other agents read it natively. | Bridge with `@AGENTS.md` in `CLAUDE.md` |
| `.mcp.json` | root only | single file | JSON | Servers connect at session start; tool schemas deferred and fetched on demand | Team-shared. Personal servers go in `~/.claude.json` |
| `.worktreeinclude` | root only | single file | gitignore syntax | Never enters context. Read only when creating a worktree. | Git only; a `WorktreeCreate` hook bypasses it |
| `settings.json` | `.claude/` and `~/.claude/` | single file | JSON | Never enters context — enforced by the client regardless of what Claude decides | The place for anything that must be guaranteed |
| `settings.local.json` | `.claude/` only | single file | JSON | Same | Gitignored when Claude Code writes to it |
| `rules/` | `.claude/` and `~/.claude/` | directory, **recursive** `**/*.md` | markdown + optional `paths:` frontmatter | Without `paths:`, loaded at session start at `CLAUDE.md` priority. With `paths:`, loaded when a matching file is read — and *not* re-injected after compaction. | Subdirectories are fine and encouraged |
| `skills/` | `.claude/` and `~/.claude/` | directory, `<name>/SKILL.md`, **one level only** | markdown + frontmatter, may bundle supporting files | Name and description sit in a listing that is always in context; the body loads on invocation and then persists for the rest of the session | Directory name sets the command. `disable-model-invocation` removes it from the listing entirely |
| `commands/` | `.claude/` and `~/.claude/` | directory, flat `<name>.md` | markdown + frontmatter | Same mechanism as skills | Legacy — merged into skills. Still supported; skills win on name collision |
| `agents/` | `.claude/` and `~/.claude/` | directory, flat `<name>.md` | markdown + frontmatter | Description is listed so Claude can delegate; the body becomes the subagent's system prompt in a **separate context window** | Recursion undocumented — assume flat |
| `workflows/` | `.claude/` and `~/.claude/` | directory, flat `<name>.js` | JavaScript | Loaded at startup; each file becomes `/<name>` | Authored by Claude via `/workflows`, not by hand |
| `output-styles/` | `.claude/` and `~/.claude/` | directory, flat `<name>.md` | markdown + frontmatter | Appended to the system prompt at session start when selected via `outputStyle` | Change takes effect next session — the system prompt is fixed at startup |
| `agent-memory/` | `.claude/` and `~/.claude/` | directory, `<agent>/MEMORY.md` | markdown | First 200 lines (25KB cap) injected into that subagent's system prompt when it runs | **Claude writes this.** Never hand-author, never share |
| `agent-memory-local/` | `.claude/` only | same | markdown | Same | Gitignored variant, for `memory: local` |
| `projects/<project>/memory/` | `~/.claude/` only | directory, `MEMORY.md` + topic files | markdown | `MEMORY.md` first 200 lines (25KB cap) at session start; topic files read on demand | **Claude writes this.** Machine-local, keyed by repo path |
| `keybindings.json` | `~/.claude/` only | single file | JSON | Client-side only. Read at startup, hot-reloaded on edit. | Ctrl+C/D/M and Caps Lock can't be rebound |
| `themes/` | `~/.claude/` only | directory, flat `<name>.json` | JSON | Client-side only. Hot-reloaded. | |
| `plugins/` | `~/.claude/` only | directory | managed | Installed plugin content; plugin skills are namespaced `plugin:skill` and never collide | Managed by `claude plugin`, don't edit by hand |
| `~/.claude.json` | beside `~/.claude/`, not inside | single file | JSON | App state, trust decisions, personal MCP servers | Mostly managed via `/config` |

### Access patterns worth internalising

The column above collapses into four behaviours, and picking the wrong one is how context
gets wasted:

- **Always in context** — `CLAUDE.md`, unscoped rules. Permanent per-token cost every turn.
- **Listed, then loaded on demand** — skills, commands, agents. Cheap until used; the description is the only permanent cost.
- **Triggered by file reads** — path-scoped rules. Free until Claude touches a match.
- **Never in context** — settings, keybindings, themes, `.worktreeinclude`. Enforced or applied by the client, so these are the only ones that *guarantee* anything.

### Curating across repos

`rules/`, `skills/`, `commands/`, `agents/`, and `output-styles/` are all safely shareable.

Never share `agent-memory/`, `agent-memory-local/`, or `projects/` — Claude writes those per-checkout, and linking them means teammates clobbering each other's notes.

## Skills vs rules: the asymmetry

**Rules recurse. Skills do not.** This is the single most common mistake. Every `.md` anywhere under `.claude/rules/` is found, so `rules/backend/api.md` works fine. Under `.claude/skills/`, only `<skill-name>/SKILL.md` at exactly one level down is found, so `skills/db/migrate/SKILL.md` is invisible — the intermediate category directory makes the skill vanish with no warning.


### Scope and precedence

For skills, enterprise beats personal beats project on name collisions, and any of them overrides a bundled skill of the same name. Plugin skills are namespaced `plugin:skill` and never collide. 

For rules, user-level loads first, so project rules effectively win.

### Nested `.claude/` in monorepos

A `.claude/skills/` in a subdirectory is a separate, valid location — discovered lazily the first time Claude touches a file in that subtree, not at startup. On a name collision the nested one is namespaced by path (`apps/web/.claude/skills/deploy` → `/apps/web:deploy`).

## File rules

### `SKILL.md` frontmatter

Only `description` really matters; everything else is optional.

| Field | Use |
|---|---|
| `name` | Display label. Does **not** set the command — the directory name does. |
| `description` | The trigger. Say what it does *and* when to use it. |
| `when_to_use` | Extra trigger phrases, appended to description. |
| `paths` | Globs limiting auto-activation to matching files. |
| `disable-model-invocation` | `true` = only the user can run it. For anything with side effects. |
| `user-invocable` | `false` = hide from `/` menu, Claude-only background knowledge. |
| `allowed-tools` | Pre-approved for the invoking turn only. |
| `disallowed-tools` | Removed from the pool while active. |
| `context: fork` / `agent` / `background` | Run in a subagent. |
| `model` / `effort` | Override for the duration. |
| `argument-hint` / `arguments` | Autocomplete hint and named `$arg` substitution. |
| `allowed-tools`, `hooks`, `shell`, `metadata` | Situational. |

The combined `description` + `when_to_use` is truncated at 1,536 characters in the listing, so lead with the primary use case. Descriptions get shortened further when many skills compete for the listing budget — the ones you invoke least lose their descriptions first.

Two things that trip people up:

- The command name comes from the **directory**, not the `name` field (except in plugins). `skills/deploy-staging/SKILL.md` is `/deploy-staging` regardless of frontmatter.
- Malformed YAML doesn't error. The body loads with empty metadata, so `/name` still works but Claude has no description to match on and will never auto-trigger it. Run with `--debug` if a skill triggers manually but never automatically.

Body: keep under 500 lines. Once invoked, the content stays in context for the rest of the session, so every line is a recurring cost. Move long reference material into sibling files and point at them from `SKILL.md`. Write standing instructions rather than one-time steps, since the file is not re-read on later turns.

Useful substitutions in the body: `$ARGUMENTS`, `$0`/`$1`, `${CLAUDE_SKILL_DIR}` (for referencing bundled scripts regardless of install location), `${CLAUDE_PROJECT_DIR}`.

`` !`cmd` `` runs a shell command and inlines its output *before* Claude sees the file.

### Rule frontmatter

Only one field:

```markdown
---
paths:
  - "src/api/**/*.ts"
  - "tests/**/*.test.ts"
---
```

No `paths` means the rule loads unconditionally at launch with the same priority as
`CLAUDE.md` — use that sparingly, it's permanent context cost. With `paths`, it loads when Claude reads a matching file.

Glob gotchas: `[` starts a bracket expression, so a literal `[` in a filename must be escaped (`photos \[2024/**`) or the pattern silently matches nothing. Brace expansion is budgeted at 1,000 expanded patterns per rule; anything over is used unexpanded and matches nothing.

### Writing instructions that actually get followed

- Be verifiable: "Use 2-space indentation", not "format code properly."
- Keep `CLAUDE.md` under ~200 lines. Longer files measurably reduce adherence.
- Don't add verification nagging ("always double-check your work") — on current models this causes redundant re-checking rather than better output. State the goal and let the model exercise judgment.
- Don't restate what's inferable from the codebase. Directory listings and dependency lists are wasted tokens; pitfalls, rationale, and conventions that differ from tool defaults are not.
- Check for contradictions across `CLAUDE.md`, nested `CLAUDE.md`, and rules. On conflict the model picks one arbitrarily.