Global agent memory¶
.claude/CLAUDE.md is the user-level memory for Claude Code —
stowed to ~/.claude/CLAUDE.md, where it is loaded in every repository on the machine. It holds
cross-repository conventions; a project's own CLAUDE.md / AGENTS.md layers repo-specific rules on top.
.claude/CLAUDE.md and .claude/settings.json are both stowed user-level files — they apply to
Claude Code in every repo. The repo's own CLAUDE.md (→ AGENTS.md) is the project-scoped layer that sits on
top of them and is deliberately not stowed.
| File | Scope | Stowed? |
|---|---|---|
.claude/CLAUDE.md |
All repos (user) | Yes → ~/.claude/CLAUDE.md |
.claude/settings.json |
All repos (user) | Yes → ~/.claude/settings.json |
<repo>/CLAUDE.md (→ AGENTS.md) |
This repo only | No (.stowrc ignores top-level entries) |
What it covers¶
The shipped file documents conventions that hold regardless of which repo Claude is working in:
- Temporary files — always route
mktempthrough$TMPDIR(e.g.mktemp -d "$TMPDIR/foo.XXXXXX"); the bare default lands in/var/folders/.../T, which the sandbox blocks. - Commit messages — Conventional Commits (
type(scope): summary), the format release-please and friends parse to derive versions and changelogs. - Creating commits — the
commit.shhandoff that works around the sandbox's inability to reach the SSH signing key. Detailed in its own section below.
See the source file for the authoritative wording.
The commit.sh workflow¶
Commit signing on this machine runs through ssh-agent. Claude Code's sandbox refuses that connection — it gets
EPERM on connect() to the agent's AF_UNIX socket — so anything git commited from inside the sandbox is
unsigned. The workaround is to hand the real, signable commit back to you in a commit.sh script you run
outside the sandbox, where the signing key is reachable. Which form the script takes depends on whether Claude is
working in a git worktree:
Claude does not run git commit at all. It writes the exact git add / git commit invocations it intended
into commit.sh — one commit per git commit call, with real Conventional-Commit messages and any trailers —
then tells you to run it. The signing happens when you execute the script, key in hand.
Claude commits normally at sensible stopping points; those land unsigned on the agent/<name> branch. The
commit.sh it writes instead re-signs the range it authored this session with
git resign <base>. <base> is the parent of the first commit —
HEAD~3 for three commits, or $(git merge-base HEAD <parent-branch>) when the commit count is dynamic.
Both forms obey the same rules:
| Rule | Detail |
|---|---|
| Location | Written at the working-directory root (the repo root, or the worktree root). |
| Never committed | Kept out of version control. The name has no leading dot, so the top-level .* rule misses it — in this repo an explicit commit.sh entry in .gitignore catches it instead. |
| Header | Starts with #!/usr/bin/env sh, then set -eu. |
| Single batch | Overwrites any prior commit.sh — the file is the current batch, not history. |
| Executable | chmod +x'd on write, so you can run it as ./commit.sh. |
| Self-deleting | Ends with rm -- "$0", so a successful run removes the script; under set -eu a failed commit aborts before the rm, leaving it in place to rerun. |
Why the indirection
The sandbox is what keeps an autonomous agent from reaching your signing key (or anything else outside its
allow-list) on its own. commit.sh doesn't poke a hole in that boundary — it moves the one privileged step,
signing, back to a shell you launch, so the agent never touches the key directly.
Stowing it¶
Getting these files to $HOME correctly meant tightening .stowrc. stow's
--ignore patterns match the full relative path, anchored at the end, so two classes of pattern were too broad:
- A bare
--ignore=CLAUDE.mdmatches any path ending inCLAUDE.md— catching both the top-levelCLAUDE.mdsymlink and.claude/CLAUDE.md. The top-level entry is pinned to the repo root with^CLAUDE.md; the transientcommit.shis kept out of$HOMEthe same way (^commit.sh). - The broad
--ignore=.*.json/--ignore=.*.yamlpatterns were removed. By the same suffix rule they were silently skipping.claude/settings.jsonand.claude/themes/cyberdream.jsonwheneverstowhad to descend into an already-existing~/.claude. Root-level lockfiles and manifests are now ignored by explicit^-anchored entries instead.
See Stow & Make for the full mechanism (and why the anchors use ^, not
\A/\z).