npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

claude-nomad

v0.69.5

Published

Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.

Readme

claude-nomad

tests test count codeql codecov NPM Version npm downloads node

claude-nomad - Sync your Claude Code setup. Same environment. Any machine.

Your entire Claude Code setup, on every machine. History included, every push secret-scanned.

Open Claude Code on a second machine and it is a blank slate: none of your custom skills, slash commands, tuned settings, or past conversations. claude-nomad keeps all of it in sync through a private Git repo you control. Run nomad sync on any machine and everything is there, conversations included; it pulls in your latest config first, then publishes your local changes, so you never have to remember which one to run first.

Dotfiles managers and rsync copy files. claude-nomad understands Claude Code's state, so your session history survives different file paths and your secrets never ride along.

Full documentation: https://funkadelic.github.io/claude-nomad/

Features

  • Sessions follow you across machines. Start a conversation on your desktop, run claude --resume on your laptop, and it is there. claude-nomad rewrites the machine-specific file paths Claude Code embeds in every transcript, so history survives projects living at different paths on different hosts.
  • One shared setup, per-machine exceptions. Your own skills, slash commands, rules, and your CLAUDE.md live in one place and follow you everywhere. hooks/ and agents/ are installed per-host by @opengsd/gsd-core via npm and are not synced (syncing them caused version-skew churn). Skills sync as a filtered copy: your own skills travel, while gsd-* skills and Claude Code's own account-skills folders (skills/synced/, skills/.trash/, skills/.staging/) stay behind (see SHARED_LINKS in src/core/config.ts and src/sync/skills-sync.ts). Claude Code keeps its copy of the skills enabled on your claude.ai account there. Settings merge a shared base with a per-host override, so one machine can run a different model or MCP URL without forking the rest. GSD-owned hook entries (scripts whose basename starts with gsd-) are filtered out of the generated ~/.claude/settings.json during pull and stripped from shared/settings.base.json on the next push; GSD reinstalls the correct per-host hook set itself. A pull never overwrites a non-gsd hook you add to your live settings; run nomad capture-settings to save it so it syncs to your other machines.
  • Every push is secret-scanned. Only an explicit allow-list of paths ever leaves the machine, credentials never sync, and gitleaks scans the exact files about to be published. The push aborts on any hit, with an interactive menu to redact, allow, or drop the finding. Always publish through nomad push: the sync repo is an ordinary Git repo, so a manual git push from it skips the scan entirely and can leak a secret that nomad push would have caught. Steady-state pushes scan only the transcripts that changed since the last successful push (incremental); a cold start, a gitleaks version change, a config file change, or --full-scan forces a full rescan. nomad doctor --check-shared also runs a read-only advisory over memory notes and skill files already synced to the repo, flagging (but never blocking on) a secret that already made it through in a past push; the fix is the same interactive Redact step nomad push offers.
  • Preview before you trust it. nomad diff shows offline what a pull would change (gsd-owned hook churn is filtered the same as on pull, so the preview matches what a real pull writes), and --dry-run on pull and push prints the plan without writing anything.
  • One command tells you what is wrong. nomad doctor is a read-only health check: wedged sync repo, broken hook references, hooks that would crash on session start because of a missing --preserve-symlinks-main flag, version drift, oversized backup cache, missing git committer identity in the sync repo (a push fails at commit time without one), a never-synced file such as a credential or a per-host settings file that Git already tracks inside the repo's shared/ folder (once it is committed the pull-side guard can no longer see it, so doctor names the file and the folder name that blocks it), path-map entries whose local project folder no longer exists on this machine, a multi-host repo where this machine's hostname-derived key matches no hosts/<HOST>.json or path-map entry (a sign NOMAD_HOST is unset here, so per-host settings and session sync will not line up with the other hosts), synced skills with local edits that differ from the shared copy, a native Windows shared-config copy that has drifted from the repo's copy (a warning, not a failure), and settings drift in both directions: keys present in the repo merge but absent from your live settings.json (behind; the next nomad pull will restore them, fix: nomad pull) and keys present locally but not yet in the repo (ahead; local-only additions, fix: nomad capture-settings), plus a warning for a local setting the next pull will remove because the repo dropped it. Each issue includes a fix hint. By default the report is compact: it shows only checks that need action plus a one-line verdict. Add --verbose (or --all / -v) to see the full per-check tree, including everything that passed.
  • Self-healing sync. Every overwrite is backed up first, and nomad pull --force-remote recovers two kinds of stuck sync repo: a repo stuck mid-rebase or mid-merge (aborts the operation, parks stranded work on a branch, refuses if shared config is at risk), and a repo where the rebase was interrupted but the git index was left with unmerged entries and no active operation (clears the index via git reset --mixed HEAD, surfaces any orphaned stash entry left by the interrupted autostash, never discards working-tree edits). On a repo that is not stuck, the flag reports there is nothing to recover and pulls normally instead of doing nothing silently.
  • Easy off. nomad eject replaces every managed ~/.claude/ symlink with a real copy in one step, so your setup keeps working after you delete the sync checkout and uninstall the CLI.

See the full feature tour for the rest: opt-in per-project sync, transcript redaction, backup pruning, and more.

Quickstart

nomad works with two directories:

  • ~/claude-nomad/ is your private sync repo. This is the one you edit.
  • ~/.claude/ is Claude Code's live config. nomad regenerates it on every pull.

Edit the repo, never the live config. In particular, never hand-edit ~/.claude/settings.json: it is rebuilt from the repo on every pull and your changes are lost. Change shared/settings.base.json (or hosts/<HOST>.json) in the repo instead, or run nomad capture-settings to pull local changes back into the repo (see Changing settings).

First host (once, ever):

# 1. Install the CLI.
$ npm i -g claude-nomad

# 2. Create your private sync repo and scaffold it. If you already have a
#    ~/.claude/ worth keeping, init offers to seed the repo from it.
$ nomad init                   # prompts for a repo name (default: claude-nomad-config)
$ nomad init --repo my-config  # set the repo name without the prompt
$ nomad init --snapshot        # seed from existing ~/.claude/ without being asked

# 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 push

Each additional host:

$ npm i -g claude-nomad
$ gh repo clone <your-username>/claude-nomad-config ~/claude-nomad
export NOMAD_HOST=<your-host-label>   # add to ~/.zshrc or ~/.bashrc
$ nomad pull

Everyday loop on any host:

$ nomad doctor   # confirm setup
$ nomad sync     # pull config, then publish local changes, in one step

nomad sync is the command to reach for day to day: it always pulls first (so changes from your other machines land before anything is pushed, and work that exists only on this machine is kept, not deleted) and then pushes, under one lock, so there is no ordering to remember. Output is compact by default: a run prints only a short Sync summary, not the full status tree; pass nomad sync --verbose (or --all / -v) to see the full tree. nomad pull and nomad push are still available as lower-level commands for cases sync does not cover: recovering a wedged repo with nomad pull --force-remote, or resolving a detected secret without the interactive menu via nomad push --redact-all / --allow / --allow-all (see Changing settings and Recovery flows). The FAQ covers what sync does and the push/pull order it enforces.

Windows

claude-nomad runs natively on Windows (PowerShell or cmd), and WSL2 works too. The everyday loop is the same either way.

Native Windows cannot use symlinks, so claude-nomad keeps a real copy of your shared config in ~/.claude/ instead of a symlink. nomad pull and nomad sync both mirror your local copies into the repo before they fetch, so an edit you have not published yet is captured rather than overwritten. If a file cannot be read, because another program is holding it open or its permissions changed, the pull warns naming the file and the reason, skips that one file, and finishes everything else normally. Close whatever is holding it, or fix its permissions, and run again to pick it up. A shared name whose copy in the sync repo is there but points at content that is not, usually because the machine that shared it no longer has the original, is skipped the same way, with a warning naming it. Remove it from the sync repo, or restore what it points at, by hand. A copy in the sync repo that cannot be read at all is skipped and named too, with its own wording: nothing checked where it leads, so the warning asks you to look at its permissions in the sync repo rather than to restore anything.

# 1. Install prerequisites and the CLI. (Using Scoop instead? scoop install gh gitleaks.)
> winget install GitHub.cli
> winget install gitleaks.gitleaks
> 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 push

See the full explainer at https://funkadelic.github.io/claude-nomad/quickstart/#windows for how the copy-sync behaves on pull and push, why a .gitleaksignore allow entry may not travel to a macOS, Linux, or WSL2 host, the 260-character path limit, and the line-endings setting.

Make your sessions follow you

Session history only syncs for projects you list in path-map.json, and a fresh init starts with none, so no sessions sync until you add a mapping. Each entry maps a logical project name to the absolute path it lives at on each host:

{
  "projects": {
    "my-app": {
      "laptop": "/Users/you/code/my-app",
      "desktop": "/home/you/projects/my-app"
    }
  }
}

The host keys (laptop, desktop) are the same labels you set in NOMAD_HOST on each machine. After editing path-map.json, nomad push publishes the matching sessions and nomad pull on another host copies them into place, rewriting the embedded file paths so claude --resume finds them at that host's path.

Changing settings

There are two ways a settings change reaches the repo, and the right one depends on where you made it:

  • You are deciding the change: edit shared/settings.base.json (shared by every host) or hosts/<HOST>.json (one machine only) in the repo, then nomad push.
  • Something else already wrote it (Claude Code or a tool added keys to your live ~/.claude/settings.json): run nomad capture-settings to promote those keys into the repo before the next nomad pull overwrites them. Add --host to land machine-specific values (such as absolute paths) in hosts/<HOST>.json instead of the shared base.

If nomad pull finds settings on this machine that your sync repo does not track, it leaves ~/.claude/settings.json as it is, so those settings are not lost. It lists the settings by name and suggests two fixes: run nomad capture-settings to save them to the repo (add --host for values that belong to this machine only), or delete them from ~/.claude/settings.json if you no longer want them. Your shared files, skills, sessions, and project extras still update, and the command exits with a non-zero status so a scripted or cron-driven pull does not report success. Once you have saved or deleted the settings, the next pull proceeds normally.

When your repo already has hooks and you add another one on this machine, nomad pull stops the same way. It names the new hook by its event, and by its command too when the command is short enough to show, for example PreToolUse hook 'python3 ~/x.py'. Save it with nomad capture-settings, which adds it to the shared hooks for that event. A hook another machine removed from the repo is removed here too and named by the pull, the same as any other setting, and nomad capture-settings does not save it back.

With --host, capture writes that event's whole hook list, shared entries included, into this machine's host file. Later changes to that event's hooks in the shared file then stop reaching this machine, and capture warns you before it writes. If your host file already sets its own hooks for that event, or sets hooks to null, a plain nomad capture-settings does not save the hook and tells you to add --host. A --host capture over hooks: null replaces the null, so the shared hooks for every other event reach this machine again, and capture warns about that too.

Credential settings are the one exception. Keys that can hold a secret (env, apiKeyHelper, awsAuthRefresh, awsCredentialExport, otelHeadersHelper) are never printed, so pull cannot list them and will not stop for them. When it finds some your repo does not track, it writes settings.json as usual and tells you how many it replaced. Keep values like these in ~/.claude/settings.local.json, which nomad never touches.

A setting that another machine removed from the repo is removed here too. Each time nomad writes your settings, on a pull or a nomad capture-settings, it records which settings it wrote on this machine, with a fingerprint of each value and of each hook. A setting in that record that the repo no longer carries, and that still has the value nomad wrote, is one nomad put there, so the next pull removes it. The pull names each setting it removes (so does nomad pull --dry-run), and the file from before the pull stays in ~/.cache/claude-nomad/backup/. A setting missing from the record is one you added, and one you changed since is yours now, so either is kept and listed as above. A removal is handled the same whether it arrives with this pull or reached your repo earlier through an edit you made, a nomad push, or a nomad pull --dry-run.

A machine that has not finished a pull yet, or whose cache folder was cleared, has nothing recorded. There a removal that reached the repo earlier is still listed as a setting you added. Save it or delete it as above and pull again.

During nomad push and nomad pull, long-running steps (rebase, secret scan, git push, session sync) show an animated progress indicator on an interactive terminal so the CLI does not look hung. In CI and when output is piped, only plain text lines are printed, with no ANSI control codes, so log output remains grep-stable.

When nomad push detects a potential secret, it drops into an interactive menu (TTY) or aborts with a recovery hint (non-TTY/CI). Three non-interactive recovery paths are available without the menu:

  • nomad push --full-scan: ignore the per-host push manifest and rescan all transcripts, then rewrite the manifest on success. Use this after upgrading gitleaks, after editing a gitleaks config file, or whenever you want to be certain nothing slipped through an incremental scan. Composes freely with --dry-run and all resolution modes.
  • nomad push --redact-all: scrub every finding from the local transcript in place, then push. All-or-nothing: if any finding cannot be redacted (an active session, or one that does not map to a synced transcript), nothing is changed and the push stops so you can handle those sessions.
  • nomad push --allow <rule>: record findings matching one gitleaks rule id as false positives (appends their fingerprints to .gitleaksignore), then re-scan and push.
  • nomad push --allow-all: record every current finding as a false positive, then re-scan and push.
  • nomad allow <fingerprint>...: pre-record specific fingerprints in .gitleaksignore without going through a push cycle.

All allow paths always re-scan after writing the allowlist; a surviving finding still aborts the push. See Recovery flows for the full decision tree.

If a previous nomad pull left the sync repo in a stuck state, run nomad pull --force-remote to auto-recover. It handles two cases: a repo stuck mid-rebase or mid-merge (aborts the in-progress operation, parks stranded commits on a nomad/stranded-<ts> branch, resets to origin/main, then re-pulls; refuses if stranded or dirty tracked changes touch synced config), and a repo where the rebase was torn down but the git index still has unmerged entries with no active rebase or merge in progress (clears the stuck index via git reset --mixed HEAD, preserving working-tree edits, surfaces any orphaned autostash entry, then re-pulls; stops after the index repair if any conflicted file still carries conflict markers, so they are never copied into your live config). Run nomad doctor first if you are unsure which state you are in; the Repository section names the specific problem and points at the right fix.

If an external tool (such as Claude Code or GSD) wrote new keys into your ~/.claude/settings.json that are not yet in your shared repo, run nomad capture-settings to promote them, or to save a hook it added under an event your repo already tracks, before the next nomad pull overwrites them. With --host, the keys land in hosts/<NOMAD_HOST>.json instead of shared/settings.base.json (useful for machine-specific values such as absolute paths). --dry-run shows what would be written without touching anything. Before it writes, capture-settings shows the destination and the keys and asks you to confirm; pass --yes (or -y) to skip the prompt, which is required when running without an interactive terminal. nomad push also warns when it detects ahead-drift so you have a prompt to act before the push completes.

Claude Code plugin

An optional companion plugin puts nomad one slash away inside Claude Code and warns you at session start when your synced setup has drifted. It is a thin layer over the CLI: install claude-nomad first (minimum version >= 0.35.0), then add the plugin.

/plugin marketplace add funkadelic/claude-nomad
/plugin install nomad@claude-nomad

It adds /nomad:sync (preview only), /nomad:pull, /nomad:diff, /nomad:push (preview only), /nomad:doctor, and /nomad:clean, plus a session-start drift check. The plugin versions independently from the CLI, but requires nomad >= 0.35.0 because it calls recent subcommands (nomad diff, nomad clean --backups) and reads the doctor command's status output. See the plugin guide for details.

Requirements

  • Node.js 22.22.1 or newer (24 LTS recommended)
  • Git
  • gitleaks (required for nomad push)
  • gh (GitHub CLI), required by nomad init

Works on macOS, Linux (including WSL2), and native Windows (PowerShell or cmd). See Windows above for the native Windows equivalents of the install and host-label steps and the copy-sync trade-off.

Optional: curl or wget for the version-staleness check and nomad doctor --check-schema. The CLI works without them. The opt-in nomad doctor --check-remote flag reads the locally cached origin/main remote-tracking ref (no curl or wget needed) and verifies that shared/ and a valid path-map.json are present there; it skips with a ⚠︎ when the ref is unavailable, and is non-fatal in all cases.

Exit codes

Every nomad subcommand exits with one of a small set of codes, so a script or cron wrapper can branch on $? without parsing stderr text.

| Code | Name | Meaning | | ---- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | 0 | Success | Completed successfully. | | 1 | Generic failure | Unclassified failure; the default for any error not covered below. | | 2 | Usage | Bad argv: an unknown subcommand, an unknown flag, or a malformed flag value. | | 4 | Conflict | The sync repo is wedged (e.g. an unresolved rebase) and needs manual git resolution. | | 5 | Leak blocked | gitleaks confirmed a secret in the staged tree and the push was aborted. | | 6 | Settings blocked | Pull (or the pull half of nomad sync) found settings on this machine that are not in the sync repo, so it did not write your settings file. | | 130 | Interrupted | You pressed Ctrl+C at an interactive prompt, so nomad stopped without finishing. |

A run skipped because another nomad process already holds the lock also exits 0: this is an intentional no-op skip, not a failure, so a backgrounded shell-rc or cron invocation never raises a false alarm from a concurrent run. Value 3 is reserved for future use.

Crash reports

If nomad ever hits an unexpected bug, it does not dump a raw stack trace. It prints a short "this looks like a bug" banner (with a link to the issue tracker) and writes a small report you can attach to a bug report if you choose. The exit code is unchanged: an unexpected crash exits 1, and a documented failure still exits with its own code from the table above. A prompt you cancel yourself is not a crash: it exits 130 and writes no report.

What this means for you:

  • The report stays on your machine. It is written to ~/.cache/claude-nomad/crash/ and nothing is ever uploaded anywhere. The file sits there until you decide to share it or delete it.
  • The saved report is scrubbed twice. First a structural pass rewrites your home directory to ~ and your hostname to a placeholder (absolute paths are personal information a secret scanner would not catch), and redacts credential-shaped tokens it recognizes (GitHub, GitLab, Slack, and AWS keys, plus any credentials embedded in a URL); this pass needs no external tools. Then a best-effort secret scan runs the same gitleaks-based redaction nomad already uses for session transcripts. The secret scan works on a temporary, owner-only (0o600) copy that already has the structural scrub applied; that scratch file is deleted immediately after the scan, and only the fully redacted report is kept.
  • It degrades safely without gitleaks. If gitleaks is not installed, the structural scrub and the credential-shape backstop still run and the report is still written, with a one-line note that the deeper secret scan did not run. The backstop catches common token shapes but not everything, so still give the report a glance before posting it publicly.
  • It is small and bounded. The report contains only the nomad version, the command you ran (bounded, and including any flag values you typed), the error name and message, a trimmed stack, your platform, the Node.js version, and a timestamp. It never includes an environment dump or the contents of any file.
  • The directory manages itself. Only the most recent reports are kept; older ones are pruned automatically on the next crash. There is no nomad clean flag for the crash directory, by design.

Learn more