Usage
Migrating an existing ~/.claude/
Section titled “Migrating an existing ~/.claude/”If a host already has real files at ~/.claude/{CLAUDE.md, agents/, skills/, ...} and you want to
bring them into the sync, the required sequence is nomad init --snapshot -> nomad push ->
nomad pull:
# From the host that has the canonical config (the originals are not modified):$ nomad init --snapshot # stages shared/ and writes hosts/<NOMAD_HOST>.json from ~/.claude/$ nomad push # publish the captured state to the private remote
# Then, on this host or any other host that has the private remote checked out:$ nomad pull # materializes the symlinksnomad pull is what actually migrates the host. applySharedLinks runs a two-pass scan: any
pre-existing non-symlink at a SHARED_LINKS path whose counterpart exists under shared/ is
renamed into ~/.cache/claude-nomad/backup/<ts>/ first, then the symlink is created. Your
originals are preserved under that timestamped backup directory, not deleted. Paths whose
shared/<name> is absent from the remote are left untouched, so a partial publish does not delete
data on the destination host.
If the remote has not been populated yet (you skipped nomad init --snapshot and nomad push),
nomad pull is a no-op for SHARED_LINKS: there is nothing on the remote to symlink against, so
your local ~/.claude/ files stay in place. The auto-move only triggers once the canonical state
is published.
Prefer an explicit tarball rollback and a confirmation prompt before any deletion? Write the
equivalent under scripts/: tar the SHARED_LINKS entries under ~/.claude/ first, copy into
shared/, prompt, then nomad pull. The auto-move path above is the recommended default.
Upgrading the CLI
Section titled “Upgrading the CLI”nomad update updates the nomad binary from npm:
$ nomad updateWhat this means for you: it runs npm update -g claude-nomad and refreshes the binary on your
PATH. It does NOT pull your sync data; run nomad pull separately when you want to apply remote
changes to this host.
nomad doctor reports when your local install is behind the latest npm release:
warning claude-nomad: <local> -> <latest> (run nomad update). When the latest version cannot
be determined (offline, or an unexpected registry response), the line says
version check skipped: could not determine latest version instead of disappearing, so a
skipped check is never mistaken for “current”.
Cross-OS resume
Section titled “Cross-OS resume”Claude Code embeds the original cwd in each session transcript. When you resume on a different
host where that path doesn’t exist, the picker prints a cd <orig-cwd> && claude --resume <id>
line that fails (the source-host path isn’t there).
Run this instead:
$ eval "$(nomad doctor --resume-cmd <session-id>)"Or pipe through bash:
$ nomad doctor --resume-cmd <session-id> | bashnomad doctor --resume-cmd <id> reads the .jsonl’s recorded cwd, reverse-looks up the
logical project in path-map.json, finds your current host’s abspath for that logical, and prints
cd <local-abspath> && claude --resume <id> to stdout. The command is read-only: it never
modifies any transcript byte.
If the session isn’t mapped on this host, you’ll see:
cross session <id> not mapped on this host; add the logical to path-map.jsonOther fatal surfaces: missing ~/.claude/projects/, session id absent from every encoded dir, no
cwd field anywhere in the transcript, missing path-map.json, recorded cwd not present in any
logical’s host map. All errors go to stderr prefixed with the fail glyph; the success line goes to
stdout as a bare shell command (no glyph) so eval works.
Reading push and pull output
Section titled “Reading push and pull output”nomad push and nomad pull print a grouped tree, the same left-gutter layout you already see
from nomad doctor. There is a header line naming the command and host, then a few named sections
(Sessions, Extras, and so on), each with its items hanging off connectors. A status glyph
leads every line: pass-glyph green for something that synced, info-glyph dim for an informational
count, warning-glyph yellow for a warning, and fail-glyph red for a failure. What this means for
you: instead of one long flat list with a line per project, related work is grouped and the noise
is collapsed.
A clean nomad push looks like this (one pass row per project whose sessions were copied up, the
projects this host does not track folded into a single count, then the secret-scan result and a
one-line summary):
push on host=workstationSessions ├ ✓ claude-nomad ├ ✓ my-side-project └ ℹ︎ 4 not in path-map (run nomad doctor to list)Extras └ ✓ claude-nomad/.planningLeak scan └ ✓ no leaksSummary └ ✓ summary: cleanThe ℹ︎ 4 not in path-map row is the collapse: rather than printing one line per project that this
host does not sync, push and pull now show a single count and point you at nomad doctor, which
lists those projects by name if you want the detail. The Leak scan section is the secret check
that runs before anything is published: ✓ no leaks when the staged transcripts are clean. If a
secret IS found, that row turns into ✗ gitleaks detected secrets in N session transcript(s) and
the full recovery block (which sessions, how to scrub them) still prints below the tree, exactly
as before. The same Leak scan row shows up under nomad push --dry-run, which runs that secret
scan as a read-only preview (nothing is written to the sync repo) and exits non-zero if the
preview finds anything.
A nomad pull is the mirror image, leading with the settings file it regenerated and then the
sessions and extras it copied down for this host:
pull on host=workstation (backup=2026-05-27T14-02-09Z)Settings └ ✓ settings.json (base + workstation.json)Sessions ├ ✓ claude-nomad └ ℹ︎ 2 not in path-map (run nomad doctor to list)Extras └ ✓ claude-nomad/.planningSummary └ ✓ summary: cleanThe Summary row is the final verdict for the run. It reads ✓ summary: clean when everything
synced, or a warning naming the counts when something was skipped:
⚠︎ summary: 3 unmapped on pull (run nomad doctor to list)⚠︎ summary: 2 unmapped on push, 1 collisions (run nomad doctor to list)Pass-glyph lines go to stdout; warning and fail-glyph lines go to stderr. An early, pre-tree
fatal abort (for example gitleaks missing when push checks for it, or a rebase conflict before
anything is staged) suppresses the tree entirely, so you do not see “summary: clean” stacked under
an error. A later leak-scan finding is different: by then the tree has already been built, so it
still renders in full with a fail-glyph Leak scan row and the recovery block below it. Projects
with no entry in path-map.json for this host count as unmapped and fold into the collapsed
info-count row; the hint points at nomad doctor, which lists them by logical name.
nomad pull --dry-run keeps its own readable preview format (a unified diff of the
settings.json changes plus the transcripts a real pull would overwrite) rather than the grouped
tree, so that preview stays easy to scan; only a real nomad pull prints the tree above.
nomad diff is unchanged.
Run tests
Section titled “Run tests”$ npm install$ npx vitest run