Skip to content

ardasevinc/claude-code-migrate

Repository files navigation

ccm - Claude Code Migrate

CLI tool for syncing portable Claude Code and Codex setup between machines via SSH/SCP or local backup archives.

ccm is intentionally not a dotdir mirror. It ships the parts of an agent setup that should travel between hosts, preserves runtime state on the target, and adapts known machine-specific Codex settings after copy.

Versioning:

  • Package uses semver (1.2.0, etc.)
  • Git release tags use vX.Y.Z (example: v1.2.0)

Providers:

  • claude (~/.claude)
  • codex (~/.codex)

Shared skills are migrated from ~/.agents/skills and ~/.agents/lazy-skills when any provider is active.

What Syncs

ccm push uses overlay semantics: it overwrites/adds selected portable config on the target host, but does not delete remote-only runtime state such as auth, sessions, logs, history, caches, and local databases.

Portable surfaces today:

  • Claude: CLAUDE.md, settings.json, agents/, native skills/, hooks/statusline/keybindings when present, and selected MCP config from ~/.claude.json
  • Codex: config.toml, AGENTS.md, agents/, rules/, skills/ except skills/.system, and local Codex marketplace sources referenced from config.toml
  • Shared agents: ~/.agents/skills/, ~/.agents/lazy-skills/, and ~/.agents/.skill-lock.json

Runtime/local state is not migrated broadly. For example, auth.json, sessions, logs, SQLite state, plugin data, and local caches are preserved on the target.

After pushing Codex, ccm normalizes path-based Codex MCP command values when the same binary is available on the remote PATH.

Host-Aware Codex Plugins

Codex plugin enablement is host-aware. ccm copies portable marketplace sources, probes the target host, evaluates enabled plugins against policy, and installs missing allowed plugins through the remote codex plugin add command.

Built-in policy keeps clearly platform-specific plugins off incompatible hosts:

Plugin Default policy
build-ios-apps@openai-curated macOS + xcodebuild
build-macos-apps@openai-curated macOS + xcodebuild
test-android-apps@openai-curated adb available
computer-use@openai-bundled macOS GUI host

Unknown enabled plugins are treated as portable by default. Disallowed plugins are disabled in the target config.toml; they are not uninstalled from the target cache.

ccm push --dry-run stays non-mutating, but for Codex it now also probes the target and previews plugin policy/install decisions.

Installation

Install the published CLI:

bun add -g claude-code-migrate
# or
npm install -g claude-code-migrate

ccm runs on Bun 1.3.14 or newer on macOS and Linux. npm is a supported installation channel; using npm install does not make the CLI compatible with the Node.js runtime.

For local development from a checkout:

bun install
bun link

Usage

Initialize config

ccm config --init
# Edit ~/.config/claude-code-migrate/config.toml

Backup

ccm backup                      # all enabled providers + shared
ccm backup codex                # codex + shared
ccm backup codex ./out.tar.gz   # codex + shared to custom path
ccm backup ./out.tar.gz         # all enabled providers + shared
ccm backup --dry-run

New backups use the v2 archive manifest. It records the producing ccm version and, for every payload file, its canonical path, size, mode, and SHA-256 digest. ccm verifies the finished archive before publishing it.

Inspect and verify

ccm inspect ./ccm-backup.tar.gz          # metadata summary, no destination extraction
ccm inspect ./ccm-backup.tar.gz --files  # include file metadata
ccm inspect ./ccm-backup.tar.gz --json
ccm verify ./ccm-backup.tar.gz
ccm verify ./ccm-backup.tar.gz --json

inspect exits 0 for a structurally valid v1 or v2 archive. verify exits 0 only when v2 file integrity is verified, 1 for a valid legacy v1 archive whose integrity metadata is unavailable, and 3 for an invalid, unreadable, or unsupported archive. JSON output follows the same exit semantics and is suitable for automation.

Legacy v1 backups remain restorable throughout the ccm 1.x release line. To upgrade one, restore it with a trusted 1.x ccm installation, review the restored files, then create a fresh v2 backup. Do not treat that conversion as retroactive verification of the v1 archive.

Archive reads and restores use bounded streaming extraction. ccm rejects unsafe paths and entry types, duplicate or non-portable destinations, malformed manifests, integrity mismatches, and archives that exceed its compressed, expanded, per-file, entry-count, manifest, or path limits.

Successful and failed mutating restores and pushes also write private execution receipts under ${XDG_STATE_HOME:-~/.local/state}/ccm/receipts/. Use the canonical rcpt_... filename stem:

ccm receipts
ccm inspect latest
ccm verify latest
ccm inspect rcpt_0123456789abcdef0123456789abcdef
ccm inspect rcpt_0123456789abcdef0123456789abcdef --json
ccm verify rcpt_0123456789abcdef0123456789abcdef
ccm verify rcpt_0123456789abcdef0123456789abcdef --remote arda@devbox

Receipt verification re-observes only the receipt's symbolic managed roots and, when applicable, Codex plugin state, then compares fingerprints; it does not print file contents. Push receipts use the original target when it can be matched safely from the configured target or profiles. Otherwise, supply it through --remote; the target must match the receipt's redacted endpoint binding. Older schema-v1 receipts remain inspectable but report drift verification as unavailable because they do not carry an observation scope.

Doctor and diff

ccm doctor                 # local checks plus the configured remote target
ccm doctor --local         # explicitly skip remote checks
ccm doctor --remote host   # override the configured remote target
ccm diff                   # push diff against the configured target
ccm diff push codex host   # explicit push diff
ccm diff restore ./backup.tar.gz codex

Remote push planning reports observation progress in human output. JSON output remains one pure object. Observation is always fresh because an exact diff must not rely on stale managed-state data.

Push

ccm push                        # all enabled providers + shared (host from config)
ccm push codex                  # codex + shared
ccm push user@host              # all enabled providers + shared
ccm push claude user@host       # claude + shared
ccm push --all user@host        # all providers + shared, regardless of config enablement
ccm push --providers claude,codex user@host
ccm push codex user@host --dry-run           # compact transfer + Codex plugin plan
ccm push codex user@host --dry-run --verbose # full file list when needed

When exactly one profile has the selected target host, push and push diff apply it automatically. Use --profile <name> to select one explicitly or --no-auto-profile to push without automatic profile selection. Multiple matching profiles fail closed.

Restore

ccm restore ./ccm-backup.tar.gz        # restore all providers in archive
ccm restore ./ccm-backup.tar.gz codex  # restore only codex
ccm restore ./ccm-backup.tar.gz --dry-run

If a local restore stops in a recoverable transaction, ccm transactions prints the exact recovery commands. ccm recover --rollback and ccm recover --accept infer the transaction only when exactly one journal matches; pass the transaction ID whenever the choice is ambiguous.

Restored Codex hook trust is deliberately removed. Review the restored hook commands and approve them again on the destination host before relying on them.

Config file

Location: ~/.config/claude-code-migrate/config.toml

[target]
type = "ssh"
host = "[email protected]"

[providers.claude]
enabled = true
settings_local = false
mcp_config = true

[providers.codex]
enabled = true

# Optional host-aware Codex plugin overrides.
# Built-in defaults already handle obvious platform-specific OpenAI plugins.
# mode: auto | always | never | preserve
#
# [providers.codex.plugin_policies."build-ios-apps@openai-curated"]
# mode = "auto"
# os = ["darwin"]
# commands = ["xcodebuild"]
#
# [providers.codex.plugin_policies."computer-use@openai-bundled"]
# mode = "never"

[backup]
path = "~/backups/ccm"

Codex plugin policy fields:

  • mode = "auto" evaluates optional host requirements.
  • mode = "always" keeps the plugin enabled and installs it if available.
  • mode = "never" disables the plugin on the target.
  • mode = "preserve" restores the target's previous enabled/disabled value after overlay copy.
  • os = ["darwin" | "linux" | "windows"] restricts auto to specific operating systems.
  • commands = ["xcodebuild"] requires commands to resolve with command -v on the target.
  • gui = true requires a GUI-capable target.

Full Gate

bun run full-gate

full-gate runs Biome lint, Biome format check, TypeScript typecheck, and Vitest.

Useful scripts:

bun run test        # Vitest once
bun run test:watch  # Vitest watch mode
bun run check       # alias for gate

License

MIT

About

CLI tool for migrating Claude Code configurations (~/.claude/) between machines via SSH or local backup archives

Resources

License

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors