# Cross Device Sync

> Set up a private GitHub repo that mirrors a Claude Code configuration (settings.json, custom skills, plugin/MCP enablement) across multiple machines. Use when the user wants to sync their `~/.claude/` between PC and laptop, between work and home, or onboard a new device with their existing setup. Handles secrets exclusion (.credentials.json, OAuth tokens), per-device MCP re-auth, CLI prerequisites, and idempotent restore. Windows is fully supported; Mac/Linux templates are best-effort.

- Skill: `fluory/cross-device-sync` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add fluory/cross-device-sync`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fluory/cross-device-sync/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: Fluory (https://skillmd.com/u/fluory)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/fluory/cross-device-sync

---


# Cross-Device Claude Code Sync

This skill walks the user through setting up a **private GitHub repo** that
mirrors their Claude Code configuration across machines, with strict safety
defaults: credentials never get committed, per-device state stays local, and
plugin caches regenerate themselves.

The repo this skill creates is the user's own — this skill does not host
anything centrally.

---

## When this skill is the right tool

Activate this skill when the user says things like:

- "How do I sync my Claude Code setup between my PC and laptop?"
- "Get the same Claude config on another machine."
- "Mirror `~/.claude/` to my work computer."
- "Set up a new device with my Claude Code plugins and skills."
- "Use a GitHub repo to sync my Claude configuration."

If the user wants to sync **memory** (claude-mem, project transcripts) or
**session state** rather than configuration, this skill is NOT the right tool —
warn them off and explain why (per-project, per-device, conflict-prone).

---

## What this skill DOES sync

These travel between machines via the private repo:

- `~/.claude/settings.json` — master config with enabled plugins, env, theme.
- Custom skill content. On Windows the live `~/.claude/skills/<name>` entries
  are typically junctions pointing at `~/.agents/skills/<name>` (or similar).
  We version the **target** content, not the junctions.
- The CLI inventory (which tools to install) — via the bundled `prereqs` script.
- The MCP/connector list — implicit via `settings.json` because most MCPs come
  from enabled plugins.

## What this skill does NOT sync (security + correctness)

The bundled `.gitignore` template excludes:

- `.credentials.json` — Claude auth token. **Never commit.**
- `mcp-needs-auth-cache.json`, `security_warnings_state_*.json` — per-device auth.
- `~/.claude.json` (the user-level file in `$HOME`, not inside `.claude/`) —
  runtime state, projects list, OAuth account, growthbook cache.
- `projects/`, `sessions/`, `session-env/`, `tasks/`, `todos/` — per-device.
- `plugins/cache/`, `plugins/marketplaces/`, `plugins/data/` — regenerates.
- `shell-snapshots/`, `file-history/`, `backups/`, `downloads/`, `telemetry/`,
  `cache/`, `ide/`, `history.jsonl`, `.last-cleanup` — local runtime state.
- `settings.local.json` — machine-specific overrides by design.
- `plans/` — often contain absolute machine-specific paths.
- `**/__pycache__/`, `*.pyc` — Python build artifacts (some skills carry Python).

Each MCP/connector auth (Firebase, Atlassian, Zapier, Vercel, Supabase, Stripe,
GitHub `gh`, Azure, Shopify, etc.) is per-device by design — the user
re-authenticates the first time they invoke each connector on the new machine.
This is a security feature, not a bug.

---

## Workflow when this skill activates

Follow this sequence. Each step is checkpointed; do not skip ahead.

### Step 1 — Confirm the operating system

Ask which OS the user is on for **both** machines. This determines whether to
use the Windows templates (`templates/windows/`) or the Unix templates
(`templates/unix/`). Mixed setups (e.g. PC = Windows, laptop = Mac) are
supported by generating both scripts in the same repo.

### Step 2 — Inspect the current `.claude/` directory

Run `Get-ChildItem $HOME/.claude` (PowerShell) or `ls -la $HOME/.claude` (Bash).
Specifically check for:

- Symlinks/junctions in `skills/`. If present, follow the targets and note
  where the actual skill content lives (often `~/.agents/skills/`).
- `settings.json` — confirm it exists and parses as JSON.
- The presence of `.credentials.json` — confirm the gitignore will exclude it.
- Any `agents/`, `commands/`, `hooks/` folders — note for the sync scope.

Do NOT print the contents of `.credentials.json` or `~/.claude.json` to the
session; they contain secrets and runtime state.

### Step 3 — Create the private repo

Recommend `gh repo create <name> --private --source . --remote origin --push`.
Use a descriptive name like `claude-setup`, `dotclaude`, or `my-claude-sync`.
**Always private.** Even on private repos, GitHub Secret Scanning will catch
and invalidate exposed Claude tokens — so the .gitignore matters even there.

### Step 4 — Scaffold the local repo

Create this structure inside the new clone:

```
<repo>/
├── .gitignore               # from templates/gitignore-template
├── README.md                # short personalized README
├── prereqs.<ps1|sh>         # CLI installer for the user's OS
├── bootstrap.<ps1|sh>       # restores setup on a new machine
├── push-from-machine.<ps1|sh>  # mirrors live state back to repo
├── connectors/
│   └── README.md            # per-MCP re-auth guide (from template)
├── claude/
│   └── settings.json        # copy of live ~/.claude/settings.json
└── agents/
    └── skills/              # actual skill content (no symlinks/junctions)
```

Copy each script from the matching `templates/<os>/` directory. Do NOT
change the script logic unless the user has unusual paths — these scripts
are tested.

### Step 5 — Copy live configuration into the repo

- `settings.json`: straight copy.
- Skills: mirror the contents of the actual skill-content directory
  (`~/.agents/skills/` on Windows, or wherever the symlinks resolve to)
  into `<repo>/agents/skills/`. Use `robocopy /MIR /XD __pycache__ /XF *.pyc`
  on Windows or `rsync -a --exclude __pycache__ --exclude '*.pyc'` on Unix.

### Step 6 — SECURITY GATE before first commit

After staging but **before committing**:

```
git ls-files | grep -iE 'credentials|\.env|oauthAccount|secret|token'
```

This MUST produce no output (or only documentation/placeholders containing
those words in obviously-non-secret contexts). If anything real shows up,
abort: untrack the file, fix the `.gitignore`, re-stage. Do not commit until
this check passes.

Also check for Python artifacts that should have been excluded:

```
git ls-files | grep -iE '__pycache__|\.pyc$|\.pyo$'
```

Both must be empty.

### Step 7 — Initial commit + push

Commit message format:
```
Initial Claude Code setup sync
- settings.json with N enabled plugins
- M custom skills under agents/skills/
- bootstrap + prereqs + push-from-machine scripts
```

### Step 8 — Walk through the second-machine setup

On the new machine, in order:

1. `git clone <repo-url> <local-path>`
2. Run `prereqs.<ps1|sh>` to install CLIs (winget on Windows, brew on macOS,
   apt/dnf on Linux + `npm i -g` for Node tooling).
3. Restart the shell to refresh `PATH`.
4. Start Claude Code and log in — this creates a **fresh** `.credentials.json`
   on the new machine. The new machine has its own token.
5. Run `bootstrap.<ps1|sh>` — restores skills + settings.json.
6. Restart Claude Code — plugins (and the MCPs/connectors they bring) load
   automatically from the marketplace. Expect 1–2 minutes the first time.
7. Open `connectors/README.md` and authenticate each MCP/connector the user
   actually needs. Don't bulk-authenticate; do them on first use.
8. For "Claude in Chrome": install the Chrome extension manually — it's a
   browser extension, not an MCP install.

### Step 9 — Ongoing sync workflow

Teach the user the round-trip:

```
# Machine A: changed something
.\push-from-machine.ps1    # or ./push-from-machine.sh
git diff                   # review
git add -A && git commit -m "sync: <what>" && git push

# Machine B: pull in changes
git pull
.\bootstrap.ps1            # or ./bootstrap.sh
# Restart Claude Code if settings/plugins changed
```

---

## Important details to handle correctly

### Windows junctions vs Unix symlinks

On Windows, custom skills are usually **junctions** (created with
`New-Item -ItemType Junction`). On Unix it's typically `ln -s`. The bootstrap
script in each templates folder handles this correctly. The repo stores
**neither junctions nor symlinks** — it stores the actual content under
`agents/skills/<name>/`, and the bootstrap script creates the link on each
machine.

### Different usernames between machines

If the user has different Windows usernames on PC vs laptop, the absolute paths
differ but `$HOME` resolves correctly. All bundled scripts use `$HOME`/`$env:USERPROFILE`,
never hardcoded `C:\Users\<name>\`. Do not introduce hardcoded paths.

### Mixed-OS pairs

If one machine is Windows and another is Mac/Linux, generate **both** sets of
scripts in the same repo. Each machine just runs the variant matching its OS.

### What if the user has manually-configured MCP servers in `~/.claude.json`?

`~/.claude.json` is mostly runtime state but the `mcpServers` key, if populated,
contains user-defined MCP server configs (from `claude mcp add` invocations).
These are NOT covered by the plugin-driven sync. If the user has manual
`mcpServers` entries:

- Extract only the `mcpServers` portion (e.g. with PowerShell `ConvertFrom-Json`
  and re-export just that key).
- Commit it as `mcp-servers.json` in the repo.
- Add a step to `bootstrap` that merges this back via `claude mcp add` calls.
- **Never commit the rest of `~/.claude.json`** — it has OAuth + secrets.

Check this with: `(Get-Content $HOME\.claude.json | ConvertFrom-Json).mcpServers`
on PowerShell or `jq .mcpServers ~/.claude.json` on Unix.

---

## Templates bundled with this skill

The `templates/` directory inside this skill ships:

- `gitignore-template` — comprehensive, ready to copy as `.gitignore`.
- `connectors-readme.md` — per-MCP re-auth guide for ~20 common connectors.
- `windows/bootstrap.ps1` — restore + junctions + settings.json.
- `windows/prereqs.ps1` — winget + `npm i -g` + bun installer.
- `windows/push-from-machine.ps1` — live → repo mirror.
- `unix/bootstrap.sh` — symlinks + settings.json (best-effort, untested
  cross-distro; PRs welcome).
- `unix/prereqs.sh` — brew / apt detection + Node + bun.
- `unix/push-from-machine.sh` — rsync mirror.
- `repo-README-template.md` — README to drop into the user's new repo.

Use the templates as the canonical implementation. If the user's situation is
unusual (custom skill paths, dotfile-manager integration, multiple Claude Code
configs side-by-side), adapt; otherwise, just copy the templates verbatim.

---

## Anti-patterns to refuse

- **Public repo for sync**: even if the user insists, refuse. Their token will
  get caught and invalidated. Recommend a private repo + a separate public repo
  if they want to publish parts of their skills as plugins.
- **Committing `.credentials.json`** "just temporarily": no. Every commit is
  permanent in git history.
- **Syncing memory or session transcripts** as part of this setup: conflict-prone,
  pollutes the repo, defeats per-device privacy. If they want this, point them at
  a separate dedicated sync (e.g. cloud-folder mount) — not this skill.
- **Bypassing the security gate** because they're "in a hurry": no. The gate
  takes 2 seconds. Skipping it has destroyed people's tokens before.

