Blog

Take Your Claude Code Setup to a New Mac

You spend months teaching Claude how you work — your skills, your commit style, the quirks of your stack. Then you open a fresh laptop and it's a stranger again. All of that knowledge was sitting in one folder the whole time: ~/.claude.

Here's how to put that folder under git so your setup follows you anywhere — and, just as importantly, which parts you must keep out of git so you don't leak a credential.

June 2026 · Alex Miller

Two laptops connected by a git branch line, with the ~/.claude folder syncing between them

What's Actually in ~/.claude

Before you sync anything, it helps to know what you're looking at. The folder mixes three very different kinds of things: portable config, durable memory, and machine-local state you should never copy.

~/.claude/
├── CLAUDE.md            ← your global preferences
├── settings.json        ← model, hooks, statusline, plugins
├── settings.local.json  ← per-machine permissions  (DO NOT SYNC)
├── keybindings.json
├── statusline-command.sh
├── hooks/               ← global hooks
├── skills/              ← global skills
├── projects/            ← session transcripts + memory  (500 MB+)
│   └── <encoded-repo-path>/
│       └── memory/      ← what Claude has learned
└── history.jsonl        ← your prompt history

# auth lives in the macOS Keychain, not a file here

Two lines in there are traps. settings.local.json holds permission grants tied to paths on this specific machine. And your login isn't a file at all — Claude Code keeps it in the macOS Keychain, which is why copying the folder never carries your auth. That's a feature. Secrets can't leak by accident.

Split It Into Two Repos

The cleanest setup uses two repos, because the stuff in ~/.claude answers to two different owners.

A private dotfiles repo

Your global config, skills, and hooks. The things you want on every machine, in every project. One repo, cloned once per Mac.

The project's own repo

A project's memory belongs with that project's code, so it commits straight into the repo it describes — and travels with a normal git clone.

The Trick: Move the File, Symlink It Back

You can't put ~/.claude itself in git without dragging along 500 MB of transcripts and a pile of secrets. So you move just the pieces you want into a repo, and leave a symlink behind. Claude Code follows the link and never knows the difference.

# Move the real file into a repo, then symlink it back.
# ~/.claude keeps working — it just points at the repo now.

REPO=~/dev/claude-dotfiles/claude
mkdir -p "$REPO"

for it in CLAUDE.md settings.json keybindings.json \
          statusline-command.sh stop-hook-label.sh hooks skills; do
  mv ~/.claude/$it "$REPO/$it"
  ln -s "$REPO/$it" ~/.claude/$it
done

One gotcha if you're on zsh: an unquoted $VAR in a for loop doesn't word-split the way it does in bash. Spell out the list literally, as above, and it behaves on both shells.

Prove There Are No Secrets

This is the step people skip and regret. A dotfiles repo is the easiest place in the world to accidentally publish an API key. Run a guard before every push, and put settings.local.json and .env in your .gitignore first.

# Before you ever push, prove no secrets snuck in.
git ls-files | grep -E "settings.local.json|credentials|\.env" \
  && echo "STOP — secret staged" \
  || echo "clean"

One Script for the New Mac

Mine sits at github.com/fotoflo/claude-dotfiles. On a fresh laptop the whole restore is three lines:

# Mine lives here. It's private — these are my real skills and
# prefs — so this is the pattern, not a repo you can clone.
# Fork your own and swap the URL.

git clone https://github.com/fotoflo/claude-dotfiles ~/dev/claude-dotfiles
~/dev/claude-dotfiles/install.sh
claude   # log in — auth comes from the Keychain, not the repo

That last line — install.sh — backs up anything already sitting in ~/.claude, then lays down the symlinks. Run it twice and nothing breaks, so it's safe to re-run any time you add a skill on one machine and want it on the other. Here's the whole script:

#!/usr/bin/env bash
# install.sh — run once on the new Mac after cloning the repo.
set -euo pipefail
C="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/claude"
DEST="$HOME/.claude"
BK="$DEST/.backup-$(date +%s)"

for it in CLAUDE.md settings.json keybindings.json \
          statusline-command.sh stop-hook-label.sh hooks skills; do
  [ -e "$C/$it" ] || continue
  [ -L "$DEST/$it" ] && rm "$DEST/$it"
  [ -e "$DEST/$it" ] && { mkdir -p "$BK"; mv "$DEST/$it" "$BK/"; }
  ln -s "$C/$it" "$DEST/$it"
done

echo "Done. Now run 'claude' and log in."

Want to start clean? I split mine in two: a private repo for my actual config and skills, and a public template you can fork — github.com/fotoflo/claude-dotfiles-template. Same install.sh, generic example skills, none of my client-specific ones. Fork it, drop in your own, and you're running in under a minute.

Memory Rides With the Code

The memory folder is the part that makes a new laptop feel like home. It's where Claude records what it has learned about a project — conventions, decisions, the reasons behind them. Because that knowledge is specific to one codebase, it belongs inside that codebase's repo.

Same move as before: shift the real folder into the repo, symlink the path Claude reads from, commit. Now a teammate who clones the project inherits everything Claude figured out — for free.

# Memory is project-specific, so it belongs with the project.
# Move it into the repo, symlink the path Claude reads from.

REPO=~/dev/myapp
mv ~/.claude/projects/-Users-me-dev-myapp/memory "$REPO/.claude/memory"
ln -s "$REPO/.claude/memory" ~/.claude/projects/-Users-me-dev-myapp/memory

cd "$REPO" && git add .claude/memory && git commit -m "chore: vendor Claude memory"

What About the Conversations?

Config and memory get you 90% of the way. If you also want to reopen an actual thread on the other Mac — scroll back, hit --resume — that's the transcripts, and those are a sync problem rather than a git one. They're big and they change every keystroke.

Point Syncthing or iCloud at ~/.claude/projects/. Two rules: clone the project to the same absolute pathon both machines, and don't run a live session against the same file from two Macs at once.

# Clone the project to the SAME absolute path on the new Mac
# (the transcript folder name is derived from it).
git clone <repo-url> ~/dev/myapp

# Drop the synced session file into the matching folder, then:
claude --resume   # pick the conversation

The Whole Thing, in Order

1

Make a private dotfiles repo

Move CLAUDE.md, settings.json, skills, and hooks into it. Symlink each one back into ~/.claude.

2

Gitignore the landmines

settings.local.json, .credentials.json, and any .env. Then run the secret guard before your first push.

3

Vendor memory into each project

Move the project's memory folder into its repo, symlink the path Claude reads from, and commit it.

4

On the new Mac: clone and run install.sh

It backs up and symlinks. Then run `claude` and log in — auth comes from the Keychain, not the repo.

5

Optional: sync transcripts

Syncthing or iCloud on ~/.claude/projects/ if you want to resume conversations, not just config.

Stay in the loop

Drop your email or WhatsApp to get notified when I'm in your city, drop a new video, or release new tools.

You're on the list!

Talk to you soon.

Want your whole team set up like this?

I run AI workshops on Claude Code — skills, workflows, and the setup that makes AI-assisted development actually stick across a team.

Book a 30-Minute Call