Quickstart
Requirements
Section titled “Requirements”- Node.js 22.22.1 or newer (24 LTS recommended; the npm
enginesfield declares the 22.22.1 floor and surfaces a warning on older runtimes – npm only blocks the install whenengine-strict=trueis configured) - Git
gitleaks(required fornomad push, which exits with an error if it is not on PATH;nomad doctoralso checks it against the pinned 8.30.x and warns when it is absent or mismatched)gh(GitHub CLI), required bynomad initto create and wire the private sync repo. Whenghis missing or unauthenticated,nomad initexits with a FATAL and shows install /gh auth loginguidance. On hosts where the private repo is already set up (all subsequent hosts),ghis only needed bynomad doctor’s Actions-drift check and auto-disable; pull and push work without it.
Optional:
- curl or wget, the HTTP fetcher behind
the version-staleness check (
nomad doctorlatest-release line) andnomad doctor --check-schema. curl is tried first and wget is the fallback, so either one works. The checks soft-skip (no error, no exit-code change) when neither is present, so the rest of the CLI works without it;nomad doctorshows a single “HTTP fetcher” row that names the binary in use (for exampleHTTP fetcher: curl 8.5.0) when either is installed, and warns only when both are absent.
First host (once, ever)
Section titled “First host (once, ever)”# 1. Install the CLI.$ npm i -g claude-nomad
# 2. Create your private sync repo and scaffold it. nomad init uses gh to# create the repo, wire origin, and disable Actions, then scaffolds locally.$ nomad init # prompts for a repo name (default: claude-nomad-config)$ nomad init --repo my-config # non-interactive: use this name, no prompt
# 3. Add a stable host label to ~/.zshrc or ~/.bashrc, then reload.export NOMAD_HOST=<your-host-label>
# 4. Publish the scaffold to your private repo.$ nomad pushThen the everyday loop on any host:
$ nomad doctor # confirm setup$ nomad sync # pull config, then publish local changes, in one stepnomad sync always pulls first and then pushes, so there is no ordering to remember. The
lower-level nomad pull and nomad push remain available when you need their extra flags; see
the command reference.
Each additional host
Section titled “Each additional host”$ npm i -g claude-nomad$ gh repo clone <your-username>/<your-repo-name> ~/claude-nomad # default: claude-nomad-configexport NOMAD_HOST=<your-host-label> # add to ~/.zshrc or ~/.bashrc$ nomad pullWindows
Section titled “Windows”claude-nomad also runs natively on Windows, no WSL required (WSL2 still works fine too, if you prefer it). The steps are the same as First host and Each additional host above, with a couple of PowerShell-specific swaps:
# 1. Install the CLI.> npm i -g claude-nomad
# 2. Create your private sync repo and scaffold it.> nomad init
# 3. Add a stable host label. PowerShell has no ~/.bashrc equivalent, so set it# as a persistent user environment variable instead, then restart your# terminal so the new value is picked up.> [System.Environment]::SetEnvironmentVariable('NOMAD_HOST', '<your-host-label>', 'User')# Using cmd instead of PowerShell? The equivalent one-liner is:# setx NOMAD_HOST <your-host-label>
# 4. Publish the scaffold to your private repo.> nomad pushA few Windows-specific things worth knowing:
- Installing gh:
winget install GitHub.cli(orscoop install gh), thengh auth login. Needed beforenomad initon the first host; later hosts only clone with it. - Installing gitleaks:
winget install gitleaks.gitleaks(orscoop install gitleaksif you use Scoop).nomad doctorprints the same hint whenever gitleaks is missing from PATH. - Shared config is copied, not symlinked. On macOS and Linux, files like
CLAUDE.mdand your skills live in the sync repo and are symlinked into~/.claude/, so there is one source of truth on disk. Creating a symlink on Windows needs Developer Mode or admin rights, so on Windows these are real copies instead. What this means for you: after editing a shared file on Windows, runnomad pushbefore your nextnomad pullornomad sync.nomad syncalways pulls first, and the pull half overlays the repo’s copy onto yours (the prior content is snapshotted to the backup dir first, so it is recoverable, but it is still reverted in place); pushing first is what actually captures your edit. This is the same behavior claude-nomad’sskills/sync already has on every platform. - A
.gitleaksignoreallow entry may not travel across hosts. gitleaks fingerprints each finding using the file path exactly as it saw it: backslashes on Windows, forward slashes on macOS/Linux. If you allow a finding withnomad push --allow(ornomad allow) on Windows, the identical finding can reappear as “new” the first time it is scanned from a macOS/Linux host, and the same happens in reverse. This is a known gitleaks limitation, not a claude-nomad bug; just allow it again from the other host. - Deep session paths and Windows’s path-length limit.
nomad doctorchecksgit config core.longpathsand the WindowsLongPathsEnabledregistry value, and warns if either is off. Turning both on avoids problems with Windows’s classic 260-character path limit, which a deeply nested project’s encoded session path can otherwise exceed. - Line endings stay put. A fresh
nomad initwrites a.gitattributeswith* -text, so Git never converts line endings between hosts. If you are joining a sync repo created before this file existed, add that one line from any host (or watch for thenomad doctorwarning that nudges you), otherwise a Windows checkout with the commoncore.autocrlf=trueGit default would rewrite every text file’s line endings, and every host would then see the whole tree as permanently changed.
Privacy by default
Section titled “Privacy by default”Your private sync repo must stay private. Session transcripts contain the full text of your
conversations. nomad init disables Actions on the new repo as soon as it is created, via the
GitHub API call gh api -X PUT repos/<owner>/<repo>/actions/permissions -F enabled=false. What
this means for you: the repo nomad init creates ships no workflows of its own, so this is a
precaution, not a fix for a known problem. It guarantees that no CI (which could echo transcript
content into build logs) can ever run against your private data repo, even if a workflow file is
added later; you do not need to remember to do it.
Pass --keep-actions to skip the disable step (for example, when your org already enforces an
Actions policy).
Per-project Claude config: in-repo vs sidecar
Section titled “Per-project Claude config: in-repo vs sidecar”Some projects keep their Claude config (a .claude/ directory with hooks/, agents/,
commands/, a project settings.json) committed to the project’s own git repo. Others keep it
as a local sidecar, listed in .gitignore so it is never committed. nomad’s per-project extras
are for the sidecar case only.
- Committed to the project repo: do nothing in nomad. Cloning the repo on another machine
already brings
.claude/, and the repo is the source of truth. Adding it to nomad’sextraswould create a second, competing copy, and because a pull mirrors the synced copy over your working tree (last write wins) it can overwrite or revert the committed version. - Git-ignored sidecar: add
.claudeto that project’s entry in theextrasfield ofpath-map.jsonso nomad carries it across your machines. On push nomad strips host-local and ephemeral state (session transcripts,settings.local.json, caches), syncing only config.
The same rule applies to .planning/ and a project-level CLAUDE.md: sync them through extras only
when the project git-ignores them. See How it works for the exact
fields and the filtering boundary.
Setup: first host in detail
Section titled “Setup: first host in detail”nomad init creates the private repo via gh, wires it as origin, disables Actions, scaffolds
the directory layout. You then run nomad push to publish. The gh CLI must be installed and
authenticated before you run it.
# Install the CLI.$ npm i -g claude-nomad
# Create the private sync repo and scaffold it. You will be prompted for a# repo name (default: claude-nomad-config). Pass --repo to skip the prompt.$ nomad init# or non-interactively:$ nomad init --repo my-config
# If ~/.claude/ is already populated on this host, capture it as the starting# point instead of an empty scaffold. Stages shared/ and writes# hosts/<NOMAD_HOST>.json from your current ~/.claude/settings.json.# Does NOT touch the originals.$ nomad init --snapshotnomad init refuses to clobber existing scaffold artifacts, so re-running on a populated repo is
a safe no-op (it errors out naming the offender). nomad pull against an unscaffolded repo fails
fast with FATAL: repo not initialized; run 'nomad init' to scaffold instead of silently leaving
a half-state.
Add a stable host label to your shell rc, then reload it:
export NOMAD_HOST=<your-host-label> # add to ~/.zshrc or ~/.bashrcNOMAD_HOST overrides os.hostname(), which returns noisy values like WINDOWS-I5NT6OH on WSL
or <name>.local on macOS. Pick a clean label per machine (e.g., wsl-laptop, macbook,
homelab-nuc). nomad doctor reports the resolved host so you can confirm.
Edit path-map.json to add your logical projects (see How it works), then:
$ nomad doctor # read-only state check; reports host, repo state, and any check # needing action (compact by default; -v shows all passing checks), # each marked checkmark (pass) / cross (fail) / warning (warn)$ nomad doctor --check-shared # read-only gitleaks preflight over the session transcripts a push # would stage$ nomad diff # preview what nomad pull would change on this host; no lock, # no network, no mutation$ nomad push # send current state to the private remote$ nomad pull # apply on another host (or this one after a remote update)nomad pull --dry-run is the network-aware twin of nomad diff: it acquires the lock and runs
git pull so you see what the next real pull would do given the latest remote, then exits without
mutating.
If the destination host already has populated ~/.claude/{CLAUDE.md, agents/, ...}, the first
nomad pull will refuse to overwrite real files. See Usage for the safe migration
flow.
Setup: each additional host in detail
Section titled “Setup: each additional host in detail”# Install the CLI.$ npm i -g claude-nomad
# Clone your private data repo (<your-repo-name> defaults to claude-nomad-config).$ gh repo clone <your-username>/<your-repo-name> ~/claude-nomad# or with plain git:$ git clone git@github.com:<your-username>/<your-repo-name>.git ~/claude-nomad
# Add to ~/.zshrc or ~/.bashrc, then reload.export NOMAD_HOST=<your-host-label>
$ nomad pull # apply config to ~/.claude/npm i -g claude-nomad puts a nomad binary on your PATH. What this means for you: there is no
compile step, no extra transpiler to install, and nothing is fetched from the network the first
time you run nomad, so the first run works offline. (The Node version floor and the
engine-strict caveat are in the Requirements section above.)