claudeport
v0.3.0
Published
Sync your Claude Code configuration (settings, skills, agents, plugin selections) across machines via your own git repo
Maintainers
Readme
claudeport
Sync your Claude Code configuration — settings, skills, agents, commands, plugin selections, CLAUDE.md, keybindings — across machines, through a git repo you own. No server, no accounts.
workstation ~/.claude ⇄ your git repo ⇄ notebook ~/.claudeInstall
npm install -g claudeportRequires Node ≥ 20 and git on your PATH.
Quickstart
Create an empty, private repo (GitHub/GitLab/anywhere), then on your first machine:
claudeport init [email protected]:you/claude-config.git # seeds the repo from ~/.claudeOn every other machine:
claudeport init [email protected]:you/claude-config.git # adopts the config from the repoDay to day:
claudeport status # what's out of sync
claudeport diff # exact changes
claudeport push # publish this machine's config
claudeport pull # apply the repo's config hereWhat syncs
The repo contains a claudeport.json manifest listing what to sync (relative to ~/.claude). The default:
{
"version": 1,
"paths": [
"settings.json",
"skills/",
"agents/",
"commands/",
"CLAUDE.md",
"keybindings.json",
"plugins/installed_plugins.json",
"plugins/known_marketplaces.json"
]
}Edit it by hand (and push) to change what syncs everywhere, or run the interactive picker:
claudeport manifest # choose paths — whole folders or individual files — then pushmanifest discovers your top-level ~/.claude entries, lets you toggle each one (whole folder, specific files, or off), and writes claudeport.json for you. Denied paths never appear. Run claudeport push afterwards to sync the change.
Plugin code is never synced — only your selections travel. After a pull that changes plugins, restart Claude Code; if a plugin appears missing, reinstall it via /plugin.
What never syncs
A hardcoded denylist wins over the manifest, and these cannot be synced even if you add them to the manifest:
- Secrets — anything matching
*credentials*,*.pem, or*.key. - Machine-local state & noise — the
projects,sessions,session-env,file-history,shell-snapshots,cache,paste-cache,plugins/cache,telemetry,backups,security,downloads, andtodosdirectories. - Volatile files —
history.jsonl,policy-limits.json, andremote-settings.json.
Safety
pullshows what will change and asks before touching anything (--yesto skip).- Files it overwrites or deletes are backed up first to
~/.claude/backups/claudeport-<timestamp>/. pushrefuses when the repo moved ahead — pull first, git style. No silent clobbering.- Use a private repo: your settings may reveal hostnames, hook commands, and workflow details.
Trust model
Only sync a repo you fully control. pull writes the repo's settings.json, skills/, commands/, and agents/ into ~/.claude — and those can contain hooks and instructions that run arbitrary commands. Pulling from a repo someone else can write to is equivalent to running their code on your machine. The confirm prompt lists which files change; run claudeport diff first if you want to see their contents.
How it works
Your repo is cloned to ~/.claudeport. push copies manifest-tracked files from ~/.claude into the clone, commits, and pushes. pull fast-forwards the clone and copies files back. Plain files, plain git — you can inspect, revert, or recover anything with normal git commands in ~/.claudeport.
Configuration
By default claudeport reads ~/.claude and clones into ~/.claudeport. Override
either with the config command, which persists to a file in your native config dir
(~/.config/claudeport/config.json on Linux/macOS, %APPDATA%\claudeport\config.json
on Windows):
claudeport config set claude-dir ~/custom/.claude # where your Claude config lives
claudeport config set sync-dir ~/custom/.claudeport # where the repo is cloned
claudeport config get # list current values
claudeport config unset claude-dir
claudeport config path # print the config file locationEach dir is resolved as env var > config file > default, so the env vars still work as a per-shell / CI override:
| Setting | Env var | Config key | Default |
| --- | --- | --- | --- |
| Claude config dir | CLAUDE_CONFIG_DIR | claude-dir | ~/.claude |
| Clone location | CLAUDEPORT_DIR | sync-dir | ~/.claudeport |
Not (yet) synced
MCP server configs (they often embed secrets and machine-specific paths), per-machine setting overrides, and project-level .claude/ dirs are deliberately out of scope for v1.
Development
Run the real claudeport command straight from a checkout, instead of node dist/index.js:
pnpm install
pnpm build # compile src/ -> dist/ (the linked command runs the build output)
npm link # symlink `claudeport` onto your PATH, pointing at this repo's dist/Now claudeport <cmd> works from any directory and reflects your local code. While iterating, keep
a rebuild running in a second terminal so edits go live on save:
pnpm build:watch # tsc --watch; recompiles into dist/ on every changeRun the tests with pnpm test (or pnpm test:watch). When you're done, remove the global link:
npm unlink -g claudeportLocal two-machine testing
You can simulate two machines syncing through a repo on a single box, using two terminals and a
throwaway local git "remote" — without touching your real ~/.claude. A sandbox under
.local-test/ (gitignored) holds a bare remote.git plus isolated config/clone dirs for each
simulated machine; environment variables redirect the CLI into it.
pnpm test:local:setup # builds the CLI, creates .local-test/ with a seeded machine A
# Terminal 1 — machine A (the "workstation")
source scripts/local-test-env.sh a
claudeport init "$CLAUDEPORT_TEST_REMOTE" # seeds the remote from A's config, pushes
# Terminal 2 — machine B (the "notebook")
source scripts/local-test-env.sh b
claudeport init "$CLAUDEPORT_TEST_REMOTE" # clones the populated remote, applies it to BNow iterate: edit files under .local-test/machineA/.claude, then claudeport push in
terminal A and claudeport pull in terminal B. claudeport status and claudeport diff work
on either side. Sourcing the env script also defines a claudeport shell function that runs
node dist/index.js, so rebuild with pnpm build (or keep pnpm build:watch running) to pick
up code changes.
Reset the sandbox to a clean seeded state with pnpm test:local:reset, and remove it entirely
with pnpm test:local:teardown.
License
MIT
