claude-nomad 0.33.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +180 -200
  3. package/dist/nomad.mjs +4603 -0
  4. package/package.json +9 -8
  5. package/src/color.ts +0 -81
  6. package/src/commands.adopt.ts +0 -183
  7. package/src/commands.doctor.check-schema.ts +0 -72
  8. package/src/commands.doctor.check-shared.scan.ts +0 -204
  9. package/src/commands.doctor.check-shared.ts +0 -178
  10. package/src/commands.doctor.checks.deps.ts +0 -97
  11. package/src/commands.doctor.checks.hooks.ts +0 -176
  12. package/src/commands.doctor.checks.pathmap.ts +0 -101
  13. package/src/commands.doctor.checks.repo.ts +0 -182
  14. package/src/commands.doctor.checks.repository.ts +0 -111
  15. package/src/commands.doctor.checks.settings.ts +0 -88
  16. package/src/commands.doctor.engine.ts +0 -90
  17. package/src/commands.doctor.format.ts +0 -22
  18. package/src/commands.doctor.gitleaks-version.ts +0 -132
  19. package/src/commands.doctor.mirror-actions.ts +0 -88
  20. package/src/commands.doctor.ts +0 -113
  21. package/src/commands.doctor.version.ts +0 -196
  22. package/src/commands.drop-session.git.ts +0 -81
  23. package/src/commands.drop-session.scrub-hint.ts +0 -74
  24. package/src/commands.drop-session.ts +0 -159
  25. package/src/commands.pull.ts +0 -169
  26. package/src/commands.push.allowlist.ts +0 -128
  27. package/src/commands.push.recovery.actions.ts +0 -175
  28. package/src/commands.push.recovery.drop.ts +0 -47
  29. package/src/commands.push.recovery.redact.ts +0 -113
  30. package/src/commands.push.recovery.seams.ts +0 -66
  31. package/src/commands.push.recovery.ts +0 -198
  32. package/src/commands.push.sections.ts +0 -171
  33. package/src/commands.push.ts +0 -218
  34. package/src/commands.redact.core.ts +0 -145
  35. package/src/commands.redact.ts +0 -187
  36. package/src/commands.update.git.ts +0 -90
  37. package/src/commands.update.resolve.ts +0 -138
  38. package/src/commands.update.test-helpers.git.ts +0 -107
  39. package/src/commands.update.ts +0 -191
  40. package/src/config.sharedDirs.guard.ts +0 -84
  41. package/src/config.ts +0 -218
  42. package/src/diff-lines.ts +0 -42
  43. package/src/diff.ts +0 -55
  44. package/src/extras-sync.core.ts +0 -96
  45. package/src/extras-sync.diff.ts +0 -40
  46. package/src/extras-sync.guards.ts +0 -29
  47. package/src/extras-sync.remap.ts +0 -138
  48. package/src/extras-sync.ts +0 -79
  49. package/src/gh-actions.ts +0 -137
  50. package/src/init.classify.ts +0 -87
  51. package/src/init.snapshot.ts +0 -56
  52. package/src/init.ts +0 -202
  53. package/src/links.ts +0 -132
  54. package/src/nomad.dispatch.ts +0 -82
  55. package/src/nomad.help.ts +0 -96
  56. package/src/nomad.ts +0 -215
  57. package/src/output-tree.ts +0 -91
  58. package/src/preview.ts +0 -126
  59. package/src/push-checks.ts +0 -170
  60. package/src/push-gitleaks.scan.ts +0 -196
  61. package/src/push-gitleaks.ts +0 -208
  62. package/src/push-leak-verdict.ts +0 -166
  63. package/src/push-preview.ts +0 -160
  64. package/src/remap.ts +0 -229
  65. package/src/resume.ts +0 -160
  66. package/src/settings-keys.ts +0 -124
  67. package/src/summary.ts +0 -102
  68. package/src/update.fork-extras.ts +0 -102
  69. package/src/update.topology.ts +0 -118
  70. package/src/utils.fs.ts +0 -152
  71. package/src/utils.json.ts +0 -55
  72. package/src/utils.lockfile.ts +0 -168
  73. package/src/utils.ts +0 -110
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.34.1](https://github.com/funkadelic/claude-nomad/compare/v0.34.0...v0.34.1) (2026-05-31)
4
+
5
+
6
+ ### Fixed
7
+
8
+ * **dist:** bundle bin to dist/nomad.mjs so global install works ([#202](https://github.com/funkadelic/claude-nomad/issues/202)) ([146813b](https://github.com/funkadelic/claude-nomad/commit/146813b16e67f8168fa4550b40db4ac6dc3238ec))
9
+
10
+ ## [0.34.0](https://github.com/funkadelic/claude-nomad/compare/v0.33.0...v0.34.0) (2026-05-31)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * `nomad update` no longer self-updates via git/fork (it runs only `npm update -g claude-nomad`) and the `--push-origin` flag is removed. `nomad init` now requires the GitHub CLI (`gh`) on first-host setup when REPO_HOME has no `origin` remote.
16
+
17
+ ### Added
18
+
19
+ * **push:** gitleaks allowlist base+overlay merge ([#201](https://github.com/funkadelic/claude-nomad/issues/201)) ([f6f2e8c](https://github.com/funkadelic/claude-nomad/commit/f6f2e8c62ef868b33f560170ddd8c60122eac4cb))
20
+ * standalone-repo onboarding as the default, retire fork model ([#199](https://github.com/funkadelic/claude-nomad/issues/199)) ([8d38df9](https://github.com/funkadelic/claude-nomad/commit/8d38df909c36eeda085f8d4eb78e706c4ad2f115))
21
+
3
22
  ## [0.33.0](https://github.com/funkadelic/claude-nomad/compare/v0.32.4...v0.33.0) (2026-05-30)
4
23
 
5
24
 
package/README.md CHANGED
@@ -41,7 +41,7 @@ box, a personal rig and a work machine. [Get started in three steps.](#quickstar
41
41
 
42
42
  - [Quickstart](#quickstart)
43
43
  - **Concepts**
44
- - [How it works (two-repo model)](#how-it-works-two-repo-model)
44
+ - [How it works](#how-it-works)
45
45
  - [Repo layout](#repo-layout-what-claude-nomad-looks-like-on-a-configured-host)
46
46
  - [What gets synced vs. not](#what-gets-synced-vs-not)
47
47
  - [Path remapping](#path-remapping)
@@ -52,10 +52,10 @@ box, a personal rig and a work machine. [Get started in three steps.](#quickstar
52
52
  - [Requirements](#requirements)
53
53
  - [Setup](#setup)
54
54
  - [Privacy by default](#privacy-by-default)
55
- - [Bootstrap](#bootstrap)
56
- - [Initialize the repo layout](#initialize-the-repo-layout)
55
+ - [First host](#first-host)
56
+ - [Each additional host](#each-additional-host)
57
57
  - [Migrating an existing ~/.claude/](#migrating-an-existing-claude)
58
- - [Upgrading the tool](#upgrading-the-tool)
58
+ - [Upgrading the CLI](#upgrading-the-cli)
59
59
  - **Reference**
60
60
  - [Commands](#commands)
61
61
  - [Recovery flows](#recovery-flows)
@@ -64,31 +64,40 @@ box, a personal rig and a work machine. [Get started in three steps.](#quickstar
64
64
  - [Recovery flow: gitleaks FATAL on a session JSONL](#recovery-flow-gitleaks-fatal-on-a-session-jsonl)
65
65
  - [Recovery flow: push-time interactive menu](#recovery-flow-push-time-interactive-menu)
66
66
  - [`.gitleaks.toml` allowlist policy](#gitleakstoml-allowlist-policy)
67
+ - [Customizing the allowlist with an overlay](#customizing-the-allowlist-with-an-overlay)
67
68
  - [Cross-OS resume](#cross-os-resume)
68
69
  - [Run tests](#run-tests)
69
70
 
70
71
  ## Quickstart
71
72
 
72
- If you already have a private **claude-nomad** mirror (see [Setup](#setup) for the one-time
73
- bootstrap), adding a new host is two one-time steps, then the everyday loop:
73
+ **First host** (once, ever):
74
74
 
75
75
  ```bash
76
+ # 1. Install the CLI.
76
77
  $ npm i -g claude-nomad
77
- ```
78
78
 
79
- ```bash
80
- # Clone your private mirror so nomad has a repo to sync into.
81
- $ git clone git@github.com:<your-username>/claude-nomad.git ~/claude-nomad
79
+ # 2. Create your private sync repo and scaffold it. nomad init uses gh to
80
+ # create the repo, wire origin, and disable Actions, then scaffolds locally.
81
+ $ nomad init # prompts for a repo name (default: claude-nomad-config)
82
+ $ nomad init --repo my-config # non-interactive: use this name, no prompt
82
83
 
83
- # Add to ~/.zshrc or ~/.bashrc:
84
+ # 3. Add a stable host label to ~/.zshrc or ~/.bashrc, then reload.
84
85
  export NOMAD_HOST=<your-host-label>
85
86
 
86
- # Optional: developers running against an alternate checkout can point
87
- # nomad at it via NOMAD_REPO. Default is ~/claude-nomad/.
88
- # export NOMAD_REPO=/path/to/repo
87
+ # 4. Publish the scaffold to your private repo.
88
+ $ nomad push
89
+ ```
90
+
91
+ **Each additional host:**
92
+
93
+ ```bash
94
+ $ npm i -g claude-nomad
95
+ $ gh repo clone <your-username>/claude-nomad-config ~/claude-nomad
96
+ export NOMAD_HOST=<your-host-label> # add to ~/.zshrc or ~/.bashrc
97
+ $ nomad pull
89
98
  ```
90
99
 
91
- Then the everyday loop:
100
+ Then the everyday loop on any host:
92
101
 
93
102
  ```bash
94
103
  $ nomad doctor # confirm setup
@@ -96,37 +105,33 @@ $ nomad pull # apply config to ~/.claude/
96
105
  $ nomad push # publish local changes (sessions, settings)
97
106
  ```
98
107
 
99
- First-host bootstrap and the safe-migration sequence for a populated `~/.claude/` are in
100
- [Setup](#setup) and [Migrating an existing ~/.claude/](#migrating-an-existing-claude).
108
+ Full walkthrough and the safe-migration sequence for a populated `~/.claude/` are in [Setup](#setup)
109
+ and [Migrating an existing ~/.claude/](#migrating-an-existing-claude).
101
110
 
102
- ## How it works (two-repo model)
111
+ ## How it works
103
112
 
104
- **claude-nomad** is a **tool**, not a config store. You maintain a separate **private** repo that
105
- holds your actual config (`CLAUDE.md`, agents, skills, settings overrides, session transcripts). The
106
- tool's source and your config end up coexisting in one working tree on each host.
113
+ **claude-nomad** is a **tool**, not a config store. You install the CLI globally
114
+ (`npm i -g claude-nomad`) and keep a separate **private** Git repo that holds only your config:
115
+ `CLAUDE.md`, agents, skills, settings, session transcripts. No tool source code lives in that repo.
107
116
 
108
117
  ```text
109
- public funkadelic/claude-nomad your private <your-username>/claude-nomad
110
- ├── src/ (the CLI) ├── src/ (copy of the CLI)
111
- ├── package.json ├── package.json
112
- └── ... ├── ...
113
- ├── shared/ (your config, synced)
114
- │ ├── CLAUDE.md
115
- │ ├── agents/
116
- │ ├── skills/
117
- │ ├── commands/
118
- ├── rules/
119
- ├── hooks/
120
- │ ├── settings.base.json
121
- │ └── projects/
122
- ├── hosts/<hostname>.json
123
- └── path-map.json
118
+ your private <your-username>/claude-nomad-config
119
+ ├── shared/ (your config, synced to every host)
120
+ ├── CLAUDE.md
121
+ ├── agents/
122
+ ├── skills/
123
+ │ ├── commands/
124
+ │ ├── rules/
125
+ │ ├── hooks/
126
+ │ ├── settings.base.json
127
+ └── projects/
128
+ ├── hosts/<hostname>.json
129
+ └── path-map.json
124
130
  ```
125
131
 
126
- You bootstrap once by mirror-pushing this public tool repo into a fresh private repo of your own
127
- (see [Setup](#setup)), then layer your config on top. Every host afterward installs the CLI
128
- (`npm i -g claude-nomad`), clones your private repo to `~/claude-nomad/`, and runs `nomad pull` to
129
- sync.
132
+ `nomad init` creates this repo for you (via `gh`) and scaffolds the directory structure in one step.
133
+ Every host after the first installs the CLI, clones your private data repo to `~/claude-nomad/`, and
134
+ runs `nomad pull` to sync.
130
135
 
131
136
  By default the CLI operates on `~/claude-nomad/` (see `REPO_HOME` in `src/config.ts`). Developers
132
137
  working from an alternate checkout can `export NOMAD_REPO=/path/to/repo` to point the CLI at their
@@ -138,8 +143,6 @@ so a clobbered dotfile variable does not break the CLI.
138
143
 
139
144
  ```text
140
145
  ~/claude-nomad/
141
- ├── src/ # the CLI (came from the public tool repo)
142
- ├── scripts/ # helper scripts you add
143
146
  ├── shared/ # synced to every machine
144
147
  │ ├── CLAUDE.md
145
148
  │ ├── settings.base.json # baseline settings
@@ -156,8 +159,7 @@ so a clobbered dotfile variable does not break the CLI.
156
159
  │ ├── <your-mac>.json # patches merged over settings.base.json
157
160
  │ ├── <your-wsl-host>.json
158
161
  │ └── <your-nuc>.json
159
- ├── path-map.json # logical project -> per-host absolute path
160
- └── package.json, ... (tool metadata)
162
+ └── path-map.json # logical project -> per-host absolute path
161
163
  ```
162
164
 
163
165
  ## What gets synced vs. not
@@ -350,9 +352,9 @@ Results on `your-other-host`: opus 4.8, the local Ollama env var, plus the share
350
352
 
351
353
  `nomad doctor` warns when `settings.json` carries a top-level key it does not recognize (a cue that
352
354
  Claude Code added a setting). The recognized set is kept current against Claude Code's published
353
- settings schema by a weekly automated PR in the public repo, so a periodic `nomad update` is what
354
- keeps that warning quiet on your hosts. To check your own `settings.json` against the live schema on
355
- demand, run `nomad doctor --check-schema`.
355
+ settings schema by a weekly automated PR in the public repo, so a periodic `nomad update` (to get
356
+ the latest CLI) is what keeps that warning quiet on your hosts. To check your own `settings.json`
357
+ against the live schema on demand, run `nomad doctor --check-schema`.
356
358
 
357
359
  ## What does NOT sync (deliberate trade-offs)
358
360
 
@@ -385,100 +387,57 @@ Read these before adopting so you opt in with eyes open.
385
387
  - [`gitleaks`](https://github.com/gitleaks/gitleaks) (required for `nomad push`, which exits with an
386
388
  error if it is not on PATH; `nomad doctor` also checks it against the pinned 8.30.x and warns when
387
389
  it is absent or mismatched)
388
- - A **private** GitHub repo (or any Git remote you control)
390
+ - `gh` ([GitHub CLI](https://cli.github.com/)), required by `nomad init` to create and wire the
391
+ private sync repo. When `gh` is missing or unauthenticated, `nomad init` exits with a FATAL and
392
+ shows install / `gh auth login` guidance. On hosts where the private repo is already set up (all
393
+ subsequent hosts), `gh` is only needed by `nomad doctor`'s Actions-drift check and auto-disable;
394
+ pull and push work without it.
389
395
 
390
- **Optional, but recommended:**
396
+ **Optional:**
391
397
 
392
- - `gh` ([GitHub CLI](https://cli.github.com/)), used only by `nomad init` to auto-disable Actions on
393
- the private repo; if it is missing or unauthenticated, init prints a manual fallback tip and
394
- continues. `nomad doctor` reports its presence in the Version Checks section.
395
- - [curl](https://curl.se/), used by the version/update check (the `nomad doctor` latest-release line
396
- and the post-`nomad update` check) and by `nomad doctor --check-schema`; it degrades silently when
397
- curl is absent or offline, so the rest of the CLI works without it. `nomad doctor` reports its
398
- presence in the Version Checks section.
398
+ - [curl](https://curl.se/), used by the version-staleness check (`nomad doctor` latest-release line)
399
+ and by `nomad doctor --check-schema`; it degrades silently when curl is absent or offline, so the
400
+ rest of the CLI works without it. `nomad doctor` reports its presence in the Version Checks
401
+ section.
399
402
 
400
403
  ## Setup
401
404
 
402
- **Why not just fork?** GitHub doesn't let you flip a public fork to private, and your config
403
- (especially session transcripts) must stay private. So the bootstrap is a one-time mirror-push into
404
- a fresh private repo, not a fork.
405
-
406
405
  ### Privacy by default
407
406
 
408
- When you mirror-push the tool into your repo, you copy its automation along with its code: the
409
- `.github/workflows/` directory holds the public project's own CI (running its test suite, linting,
410
- secret and code scanning, release tagging, and npm publishing). That CI is meant for the public
411
- project, not your config; if it ran on your private mirror, a job could echo transcript contents
412
- into build logs. So your mirror gets two independent layers of defense against that, both applied
413
- automatically:
407
+ Your private sync repo must stay private. Session transcripts contain the full text of your
408
+ conversations. `nomad init` disables Actions on the new repo as soon as it is created, via the
409
+ GitHub API call `gh api -X PUT repos/<owner>/<repo>/actions/permissions -F enabled=false`. What this
410
+ means for you: CI workflows (which could echo transcript content into build logs) are turned off on
411
+ your private data repo automatically; you do not need to remember to do it.
414
412
 
415
- 1. **The workflows are written to skip private repos.** Each one carries the run condition
416
- `${{ !github.event.repository.private }}` (in plain terms: "run only when this repo is NOT
417
- private"), so even with Actions enabled the jobs do not run on your mirror.
418
- 2. **`nomad init` turns Actions off for the whole repo** on first run, via the GitHub API call
419
- `gh api -X PUT repos/<owner>/<repo>/actions/permissions -F enabled=false`. This needs the `gh`
420
- CLI installed and authed; if it is missing or unauthed, init logs a manual fallback tip and
421
- continues.
422
-
423
- Pass `--keep-actions` to either form of init to skip step 2 (for example, when your org already
424
- enforces an Actions policy upstream).
413
+ Pass `--keep-actions` to skip the disable step (for example, when your org already enforces an
414
+ Actions policy).
425
415
 
426
416
  <!-- prettier-ignore -->
427
417
  > [!WARNING]
428
- > If you ever flip the mirror to public, both protections evaporate: CI starts firing on
429
- > every `nomad push` against `main`, and your session transcripts (which include conversation
430
- > content) become world-readable. **Keep it private.**
418
+ > If you ever make the repo public, your session transcripts (which include conversation content)
419
+ > become world-readable. **Keep it private.**
431
420
 
432
- ### Bootstrap
421
+ ### First host
433
422
 
434
- Steps 1-2 are once-ever across all hosts; steps 3-4 repeat on every host:
423
+ `nomad init` creates the private repo via `gh`, wires it as `origin`, disables Actions, scaffolds
424
+ the directory layout, and pushes. The `gh` CLI must be installed and authenticated before you run
425
+ it.
435
426
 
436
427
  ```bash
437
- # 1. Create the private repo (or use the GitHub UI). Once, ever.
438
- $ gh repo create <your-username>/claude-nomad --private
439
-
440
- # 2. Copy the public tool into your private repo. A bare clone followed by a
441
- # mirror push makes a complete, independent copy (every branch and tag) with
442
- # no fork link back to upstream, which is what lets you keep it private. Once, ever.
443
- $ git clone --bare git@github.com:funkadelic/claude-nomad.git /tmp/claude-nomad.git # download a full copy
444
- $ cd /tmp/claude-nomad.git
445
- $ git push --mirror git@github.com:<your-username>/claude-nomad.git # upload it to your private repo
446
- $ cd .. && rm -rf /tmp/claude-nomad.git
447
-
448
- # 3. Install the CLI globally and clone your private copy. Repeat on every host.
428
+ # Install the CLI.
449
429
  $ npm i -g claude-nomad
450
- $ git clone git@github.com:<your-username>/claude-nomad.git ~/claude-nomad
451
-
452
- # 4. Add a stable host label to your shell rc (~/.zshrc or ~/.bashrc). Repeat on every host.
453
- export NOMAD_HOST=<your-host-label> # any short, stable label; nomad reads this instead of os.hostname()
454
- ```
455
-
456
- `npm i -g claude-nomad` puts a `nomad` binary on your PATH. The binary runs the `src/nomad.ts`
457
- source directly under Node's built-in type-stripping. What this means for you: there is no compile
458
- step, no extra transpiler to install, and nothing is fetched from the network the first time you run
459
- `nomad`, so the first run works offline. (The Node version floor and the `engine-strict` caveat are
460
- in [Requirements](#requirements).)
461
-
462
- On every additional host you repeat only steps 3-4; steps 1-2 are already done, since your private
463
- repo lives on the remote from step 2.
464
-
465
- `NOMAD_HOST` overrides `os.hostname()`, which returns noisy values like `WINDOWS-I5NT6OH` on WSL or
466
- `<name>.local` on macOS. Pick a clean label per machine (e.g., `wsl-laptop`, `macbook`,
467
- `homelab-nuc`). `nomad doctor` reports the resolved host so you can confirm.
468
-
469
- ### Initialize the repo layout
470
-
471
- First host only; subsequent hosts just clone and `nomad pull`. Both forms below auto-disable Actions
472
- on a detected private GitHub mirror as described in [Privacy by default](#privacy-by-default). Pick
473
- one:
474
430
 
475
- ```bash
476
- # Fresh start: scaffold an empty shared/, hosts/, path-map.json skeleton.
431
+ # Create the private sync repo and scaffold it. You will be prompted for a
432
+ # repo name (default: claude-nomad-config). Pass --repo to skip the prompt.
477
433
  $ nomad init
434
+ # or non-interactively:
435
+ $ nomad init --repo my-config
478
436
 
479
- # Already have ~/.claude/ populated on this host? Capture it as the
480
- # starting point. Stages shared/ and writes hosts/<NOMAD_HOST>.json from
481
- # your current ~/.claude/settings.json. Does NOT touch the originals.
437
+ # If ~/.claude/ is already populated on this host, capture it as the starting
438
+ # point instead of an empty scaffold. Stages shared/ and writes
439
+ # hosts/<NOMAD_HOST>.json from your current ~/.claude/settings.json.
440
+ # Does NOT touch the originals.
482
441
  $ nomad init --snapshot
483
442
  ```
484
443
 
@@ -487,6 +446,16 @@ safe no-op (it errors out naming the offender). `nomad pull` against an unscaffo
487
446
  with `FATAL: repo not initialized; run 'nomad init' to scaffold` instead of silently leaving a
488
447
  half-state.
489
448
 
449
+ Add a stable host label to your shell rc, then reload it:
450
+
451
+ ```bash
452
+ export NOMAD_HOST=<your-host-label> # add to ~/.zshrc or ~/.bashrc
453
+ ```
454
+
455
+ `NOMAD_HOST` overrides `os.hostname()`, which returns noisy values like `WINDOWS-I5NT6OH` on WSL or
456
+ `<name>.local` on macOS. Pick a clean label per machine (e.g., `wsl-laptop`, `macbook`,
457
+ `homelab-nuc`). `nomad doctor` reports the resolved host so you can confirm.
458
+
490
459
  Edit `path-map.json` to add your logical projects (see [Path remapping](#path-remapping)), then:
491
460
 
492
461
  ```bash
@@ -505,6 +474,28 @@ If the destination host already has populated `~/.claude/{CLAUDE.md, agents/, ..
505
474
  `nomad pull` will refuse to overwrite real files. See
506
475
  [Migrating an existing ~/.claude/](#migrating-an-existing-claude) for the safe migration flow.
507
476
 
477
+ ### Each additional host
478
+
479
+ ```bash
480
+ # Install the CLI.
481
+ $ npm i -g claude-nomad
482
+
483
+ # Clone your private data repo.
484
+ $ gh repo clone <your-username>/claude-nomad-config ~/claude-nomad
485
+ # or with plain git:
486
+ $ git clone git@github.com:<your-username>/claude-nomad-config.git ~/claude-nomad
487
+
488
+ # Add to ~/.zshrc or ~/.bashrc, then reload.
489
+ export NOMAD_HOST=<your-host-label>
490
+
491
+ $ nomad pull # apply config to ~/.claude/
492
+ ```
493
+
494
+ `npm i -g claude-nomad` puts a `nomad` binary on your PATH. What this means for you: there is no
495
+ compile step, no extra transpiler to install, and nothing is fetched from the network the first time
496
+ you run `nomad`, so the first run works offline. (The Node version floor and the `engine-strict`
497
+ caveat are in [Requirements](#requirements).)
498
+
508
499
  ## Migrating an existing ~/.claude/
509
500
 
510
501
  If a host already has real files at `~/.claude/{CLAUDE.md, agents/, skills/, ...}` and you want to
@@ -536,88 +527,29 @@ Prefer an explicit tarball rollback and a confirmation prompt before any deletio
536
527
  equivalent under `scripts/`: tar the `SHARED_LINKS` entries under `~/.claude/` first, copy into
537
528
  `shared/`, prompt, then `nomad pull`. The auto-move path above is the recommended default.
538
529
 
539
- ## Upgrading the tool
530
+ ## Upgrading the CLI
540
531
 
541
- Two different things can fall behind, and they update independently:
542
-
543
- - **The `nomad` CLI binary** (what runs when you type `nomad`). If you installed it with
544
- `npm i -g claude-nomad`, upgrade it with `npm update -g claude-nomad`. This refreshes only the
545
- binary on your PATH; it does not touch anything inside your private `~/claude-nomad/` repo.
546
- - **The synced tool files inside your private repo:** `src/`, `.gitleaks.toml` (the secret-scan
547
- allowlist), and the `.github/workflows/` privacy gating. These were copied from the public repo at
548
- bootstrap and then froze, so `npm update -g` does not refresh them. `nomad update`, run from
549
- `~/claude-nomad/`, is what pulls newer versions of these files in. Topology-aware: detects vanilla
550
- vs fork remotes, pulls or merges upstream, and re-runs `npm install` when `package-lock.json`
551
- shifted.
552
-
553
- Most people who followed the Quickstart need both: `npm update -g` for the binary, and an occasional
554
- `nomad update` for the repo files (notably to receive `.gitleaks.toml` allowlist changes and any
555
- update to the privacy gating itself). The mirror-push bootstrap leaves your repo with `origin` on
556
- your private mirror and no `upstream` remote; that becomes the "fork" topology `nomad update`
557
- expects once you add the upstream remote (the one-time `git remote add upstream ...` step is below).
558
-
559
- Your private repo is not a fork, so GitHub's "Sync fork" UI doesn't apply. The shortcut on a
560
- source-checkout host is:
532
+ `nomad update` updates the `nomad` binary from npm:
561
533
 
562
534
  ```bash
563
- $ cd ~/claude-nomad
564
535
  $ nomad update
565
536
  ```
566
537
 
567
- `nomad update` detects which layout your `~/claude-nomad/` uses and does the right thing:
568
-
569
- - **vanilla** (`origin` points at the public repo): `git pull --ff-only origin main`.
570
- - **fork** (`upstream` points at the public repo, `origin` points at your private mirror):
571
- `git fetch upstream`, then (before merging) commit any whitelisted `shared/extras/` content that
572
- is still untracked locally so an overlap with upstream becomes a normal file merge instead of an
573
- untracked-overwrite abort, `git merge upstream/main`, then prompt before pushing the merge to
574
- `origin/main`. Pass `--push-origin` to skip the prompt. When the merge is a no-op (HEAD unchanged,
575
- nothing new to push) the prompt is skipped entirely and `nomad update` logs
576
- `already in sync with origin/main`.
577
-
578
- Pre-flight checks run before any mutation: `REPO_HOME` exists, the topology resolves to `vanilla` or
579
- `fork`, the current branch is `main`, the working tree is clean (override with `--force`), and
580
- `--push-origin` is rejected on vanilla topology.
581
-
582
- After the merge or pull, `nomad update` re-runs `npm install` only when `package-lock.json` actually
583
- shifted, commits the regenerated `package-lock.json` (fork topology) if the reinstall changed it,
584
- then invokes `nomad doctor`. The trailing version-check is non-fatal: `✓` when local matches the
585
- latest release, `⚠︎` when behind, an informational `ℹ︎ ... ahead of latest release` line when ahead
586
- (e.g. a `-dev` build between releases), and silent on network failures.
587
-
588
- Common cases:
589
-
590
- ```bash
591
- $ nomad update # the usual path
592
- $ nomad update --dry-run # detect topology + pre-flight, print would-be git commands only
593
- $ nomad update --push-origin # fork topology: push merge to origin/main without prompting
594
- $ nomad update --force # proceed past a dirty working tree
595
- ```
596
-
597
- One-time setup if you're running a fork layout and don't have the `upstream` remote yet:
598
-
599
- ```bash
600
- $ git remote add upstream git@github.com:funkadelic/claude-nomad.git
601
- ```
602
-
603
- To pin to a specific release (`vX.Y.Z`, tagged by release-please) instead of tracking `main`, fetch
604
- tags from the public repo and check out the tag (detached HEAD). On vanilla topology that's
605
- `origin`; on fork topology that's `upstream` (the private mirror at `origin` does not accumulate
606
- upstream release tags). Example: `git fetch upstream --tags && git switch --detach vX.Y.Z`
607
- (substitute `origin` for vanilla; use `git checkout vX.Y.Z` on older Git).
538
+ What this means for you: it runs `npm update -g claude-nomad` and refreshes the binary on your PATH.
539
+ It does NOT pull your sync data; run `nomad pull` separately when you want to apply remote changes
540
+ to this host.
608
541
 
609
- If you installed an earlier version via `./install.sh` and a shell alias (the pre-npm path), your
610
- existing alias keeps working unchanged. Run `npm i -g claude-nomad` whenever you're ready to switch
611
- to the global binary, confirm `nomad --version` resolves to the npm install (`which nomad` should
612
- point under your npm prefix's `bin/`), then delete the alias line from your shell rc.
542
+ `nomad doctor` reports when your local install is behind the latest npm release:
543
+ `⚠︎ claude-nomad: <local> -> <latest> (run nomad update)`.
613
544
 
614
545
  ## Commands
615
546
 
616
547
  | Command | Description |
617
548
  | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
618
- | `nomad init` | Scaffold empty `shared/`, `hosts/`, `path-map.json` on a fresh clone. Refuses to clobber existing scaffold. Auto-disables Actions on a detected private GitHub mirror (see [Privacy by default](#privacy-by-default)). |
549
+ | `nomad init` | Create a private GitHub repo via `gh`, wire it as `origin`, disable Actions, scaffold `shared/`, `hosts/`, `path-map.json`, and push. Prompts for a repo name (default: `claude-nomad-config`). `gh` must be installed and authenticated; exits with FATAL otherwise. Refuses to clobber existing scaffold. See [Privacy by default](#privacy-by-default). |
550
+ | `nomad init --repo <name>` | Non-interactive: use `<name>` as the private repo name without prompting. Useful in scripts. |
619
551
  | `nomad init --snapshot` | Overlay current host's `~/.claude/` into `shared/` and write `~/.claude/settings.json` verbatim into `hosts/<NOMAD_HOST>.json`. Originals not modified. Same auto-disable behavior as `nomad init`. |
620
- | `nomad init --keep-actions` | Skip the auto-disable. Combinable with `--snapshot`. Use when an upstream org policy already governs Actions, or you intentionally want CI on the private mirror. |
552
+ | `nomad init --keep-actions` | Skip the Actions-disable step. Combinable with `--snapshot` and `--repo`. Use when an org policy already governs Actions, or you intentionally want CI on the private repo. |
621
553
  | `nomad pull` | `git pull --rebase --autostash`, apply symlinks, regenerate `settings.json`, remap session paths, and pull opted-in per-project extras. Errors out if scaffold missing. |
622
554
  | `nomad pull --dry-run` | Network-aware preview: acquire lock + `git pull --rebase`, print planned changes (symlink moves, `settings.json` diff, transcript overwrites), exit without writing. |
623
555
  | `nomad diff` | Offline, lockless twin of `pull --dry-run`. No network, no lock. Works against the current local repo state. |
@@ -630,7 +562,7 @@ point under your npm prefix's `bin/`), then delete the alias line from your shel
630
562
  | `nomad redact <session-id>` | Rewrite the secret span in the local source transcript for a session, backed up to `~/.cache/claude-nomad/backup/`. Refuses to touch a session that was modified recently (potential active session). Safe to re-run. See [`nomad redact <session-id>`](#nomad-redact-session-id). |
631
563
  | `nomad redact --rule <id>` | Limit redaction to findings of one gitleaks rule id only. |
632
564
  | `nomad redact --dry-run` | Show what `nomad redact` would change without writing anything. |
633
- | `nomad update` | Topology-aware upgrade to the latest upstream. Flags: `--dry-run`, `--force`, `--push-origin`. See [Upgrading the tool](#upgrading-the-tool). |
565
+ | `nomad update` | Update the `nomad` CLI binary from npm (`npm update -g claude-nomad`). Does NOT pull your sync data; run `nomad pull` separately for that. See [Upgrading the CLI](#upgrading-the-cli). |
634
566
  | `nomad doctor` | Read-only health check. Each line carries a status glyph (`✓` pass, `✗` fail, `⚠︎` warn); any `✗` sets `process.exitCode = 1` (`⚠︎` does not). Includes an offline-tolerant release-version staleness check, a Hook targets check that fails (`✗`, exit 1) when `settings.json` references a hook command whose script under `~/.claude/` is missing on this host, plus two `⚠︎`-only drift checks: gitleaks version drift and, on a private GitHub mirror, re-enabled Actions. |
635
567
  | `nomad doctor --resume-cmd <id>` | Print a host-local `cd ... && claude --resume <id>` line for a session (see [Cross-OS resume](#cross-os-resume)). |
636
568
  | `nomad doctor --check-shared` | Read-only gitleaks preflight: stages the session transcripts a `push` would publish into a temp tree and scans them, failing (`✗`, exit 1) per affected session with rotate-and-scrub guidance. Skips with a `⚠︎` when gitleaks is not on PATH. See [Recovery flow: gitleaks FATAL on a session JSONL](#recovery-flow-gitleaks-fatal-on-a-session-jsonl). |
@@ -852,7 +784,8 @@ Two branches from here:
852
784
 
853
785
  2. **False positive.** Add an allowlist regex to `.gitleaks.toml` at the repo root that matches the
854
786
  noise pattern but not real-secret formats, commit it, then re-run `nomad push`. The new allowlist
855
- propagates to deploy hosts via `nomad update`.
787
+ propagates to other hosts when they run `nomad update` (CLI upgrade) or when you push the updated
788
+ file to your data repo.
856
789
 
857
790
  `nomad drop-session` only acts on the staged tree of `~/claude-nomad/`. Active Claude Code sessions
858
791
  writing to the local file are not disturbed.
@@ -929,17 +862,64 @@ regexes = [
929
862
  ]
930
863
  ```
931
864
 
932
- File location: `.gitleaks.toml` at the public repo root (alongside `package.json`). At runtime both
933
- `probeGitleaks` (in `src/push-checks.ts`) and `runGitleaksScan` (in `src/push-gitleaks.ts`)
934
- conditionally pass `--config <REPO_HOME>/.gitleaks.toml` when the file exists. Hosts that have not
935
- yet run `nomad update` (or fresh clones predating the allowlist) fall back silently to the default
936
- gitleaks ruleset; there is no warning. Run `nomad update` to receive the latest allowlist.
865
+ File location: `.gitleaks.toml` ships bundled with the CLI binary. At runtime both `probeGitleaks`
866
+ (in `src/push-checks.ts`) and `runGitleaksScan` (in `src/push-gitleaks.ts`) try
867
+ `<REPO_HOME>/.gitleaks.toml` first and fall back to the package-bundled copy when the repo-level
868
+ file is absent. So when you have no repo-level copy the allowlist tracks the installed binary, and
869
+ running `nomad update` (to get the latest CLI) is enough to receive allowlist updates. If you do
870
+ place a `<REPO_HOME>/.gitleaks.toml`, it takes precedence and `nomad update` will not change it; you
871
+ maintain that file yourself.
872
+
873
+ #### Customizing the allowlist with an overlay
874
+
875
+ What this means for you: if you only want to allow a couple of extra patterns of your own (say, an
876
+ internal tool that emits a structured token that keeps tripping the scan), you do not have to copy
877
+ the whole bundled allowlist into your sync repo and keep it in step by hand. Instead, drop a small
878
+ `<REPO_HOME>/.gitleaks.overlay.toml` containing only your extra `[[allowlists]]` tables (and
879
+ optionally `[[rules]]`). nomad layers your entries on top of the bundled allowlist at scan time, so
880
+ the shipped Sonar / gitleaks / npm-audit / coverage noise allows stay in effect, the gitleaks
881
+ default ruleset stays in effect, and your additions are appended to all of them.
882
+
883
+ Why this is better than a full `.gitleaks.toml`: a full repo-level `.gitleaks.toml` replaces the
884
+ bundled allowlist outright, so the shipped noise allows are lost and `nomad update` can no longer
885
+ refresh them (you own that file). The overlay is additive instead: it never drops the bundled base,
886
+ and because the base still ships with the CLI, `nomad update` keeps the base current while your
887
+ overlay rides on top.
888
+
889
+ How it works, briefly: on `nomad push`, when the overlay is present, nomad generates a throwaway
890
+ config that extends the bundled `.gitleaks.toml` (which itself extends the gitleaks default),
891
+ appends your overlay body, scans with that combined config, then deletes the throwaway file. The
892
+ merge is gitleaks' own `[extend]` append, so your allowlist entries add to the shipped and default
893
+ ones rather than replacing them.
894
+
895
+ Two rules to keep in mind:
896
+
897
+ - Your overlay must NOT contain its own `[extend]` block. nomad writes the `[extend]` line for you;
898
+ if the overlay includes one, the push aborts with a clear error rather than scanning with a config
899
+ you did not intend.
900
+ - If you keep BOTH a full `<REPO_HOME>/.gitleaks.toml` AND an overlay, the full `.gitleaks.toml`
901
+ wins and the overlay is ignored (a full repo toml means you have taken complete manual control).
902
+ Pick one approach: the overlay for additive tweaks, or a full `.gitleaks.toml` for total control.
903
+
904
+ Example `<REPO_HOME>/.gitleaks.overlay.toml` (note: no `[extend]` block):
905
+
906
+ ```toml
907
+ [[allowlists]]
908
+ description = "my-org: internal build-token noise"
909
+ regexes = [
910
+ '''BUILDTOK-[A-Za-z0-9]{24}''',
911
+ ]
912
+ ```
913
+
914
+ The overlay file is push-allowed (it is an exact-name entry in `PUSH_ALLOWED_STATIC` in
915
+ `src/config.ts`, alongside `.gitleaksignore`), so you can commit `.gitleaks.overlay.toml` to your
916
+ sync repo and it travels to your other hosts on the next `nomad pull`.
937
917
 
938
918
  Editing: amend `.gitleaks.toml` in this repo, open a PR, and merge to `main`. Use TOML literal
939
919
  strings (triple single quotes, `'''regex'''`) for new regex entries so backslashes do not need
940
920
  escaping. Verify the new pattern does not match real-secret formats (`ghp_<36>`, `sk_live_*`,
941
- `xoxb-*`, `AKIA[A-Z0-9]{16}`, etc.) before merging. The propagation path is the same as any other
942
- repo update: `nomad update` on each host pulls the new file in.
921
+ `xoxb-*`, `AKIA[A-Z0-9]{16}`, etc.) before merging. The allowlist ships with the binary, so
922
+ `nomad update` on each host picks up the new file.
943
923
 
944
924
  ## Cross-OS resume
945
925