claude-hopper 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/cli.js +7147 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nate
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# claude-hopper
|
|
2
|
+
|
|
3
|
+
Manage multiple isolated Claude Code profiles — personal, work, contractor, whatever — and sync the non-secret parts across machines via git.
|
|
4
|
+
|
|
5
|
+
Each profile is a fully independent copy of Claude Code config. Different OAuth, different settings, different agents, different skills. One alias per profile (`claude-personal`, `claude-lazer`, ...) drops you into the right one.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
Before installing, make sure these are available on each machine you'll use:
|
|
12
|
+
|
|
13
|
+
| Tool | Why | Install |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| Node.js ≥ 20 | Runtime for the CLI | `brew install node` |
|
|
16
|
+
| `git` | Sync transport | usually pre-installed; `brew install git` |
|
|
17
|
+
| [GitHub CLI](https://cli.github.com) (`gh`) | Easiest way to auth git over HTTPS | `brew install gh` |
|
|
18
|
+
| [Claude Code](https://claude.com/claude-code) | The thing being managed | follow official install instructions |
|
|
19
|
+
| A private git repo for sync | Holds your synced profile config | create an **empty** repo on GitHub/GitLab — don't add a README, .gitignore, or LICENSE; `claude-hopper` will populate it |
|
|
20
|
+
|
|
21
|
+
Authenticate `git` for the sync remote (one-time per machine):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gh auth login # GitHub.com → HTTPS → "Login with a web browser"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This sets up a credential helper so subsequent `git clone`/`push`/`pull` against private repos works without prompts. If you prefer SSH, set up an SSH key and add it to GitHub instead — `claude-hopper` will use whichever URL form you pass to `--remote`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g claude-hopper
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Both `claude-hopper` and the short alias `chp` land on your PATH.
|
|
38
|
+
|
|
39
|
+
> **Building from source** (only needed if you're contributing): clone the repo, `bun install`, `bun run build`, `npm i -g .`.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Quickstart — first machine
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 1. Bootstrap hopper, pointing at your (empty) sync repo
|
|
47
|
+
claude-hopper init --remote https://github.com/<you>/<sync-repo>.git
|
|
48
|
+
|
|
49
|
+
# 2. Add profiles (one per Anthropic identity you want to keep separate)
|
|
50
|
+
claude-hopper profile add personal --seed canonical # copy from ~/.claude
|
|
51
|
+
claude-hopper profile add work --seed empty # start fresh
|
|
52
|
+
|
|
53
|
+
# 3. Reload your shell so the new aliases work
|
|
54
|
+
source ~/.zshrc
|
|
55
|
+
|
|
56
|
+
# 4. Sign in to each profile
|
|
57
|
+
# IMPORTANT: sign out of claude.ai in your browser between accounts, or
|
|
58
|
+
# use a separate browser / incognito window — otherwise OAuth will
|
|
59
|
+
# auto-authorize whichever account is currently signed in.
|
|
60
|
+
claude-personal # OAuth, then quit
|
|
61
|
+
claude-work
|
|
62
|
+
|
|
63
|
+
# 5. Push the synced parts to your remote
|
|
64
|
+
claude-hopper sync push
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Quickstart — second (or third, or N-th) machine
|
|
70
|
+
|
|
71
|
+
After repeating the **Prerequisites** and **Install** sections on the new machine:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 1. Bootstrap — clones the sync repo into ~/.claude-hopper/
|
|
75
|
+
# and installs shell aliases for every profile.
|
|
76
|
+
claude-hopper init --remote https://github.com/<you>/<sync-repo>.git
|
|
77
|
+
|
|
78
|
+
# 2. Reload your shell
|
|
79
|
+
source ~/.zshrc
|
|
80
|
+
|
|
81
|
+
# 3. Verify
|
|
82
|
+
claude-hopper profile list # all profiles present, status: needs-auth
|
|
83
|
+
claude-hopper doctor # all ✓ except auth warnings
|
|
84
|
+
|
|
85
|
+
# 4. Authenticate each profile on this machine
|
|
86
|
+
# (same browser-session gotcha as above — be careful)
|
|
87
|
+
claude-personal
|
|
88
|
+
claude-work
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
That's it. From here, ongoing sync is `claude-hopper sync push` on the machine that changed something, `claude-hopper sync pull` on the others.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## How profiles work
|
|
96
|
+
|
|
97
|
+
Every profile lives at `~/.claude-hopper/profiles/<name>/` and is a complete, standalone Claude Code config dir:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
~/.claude-hopper/profiles/personal/
|
|
101
|
+
settings.json
|
|
102
|
+
CLAUDE.md
|
|
103
|
+
agents/
|
|
104
|
+
skills/
|
|
105
|
+
hooks/
|
|
106
|
+
plugins/
|
|
107
|
+
keybindings.json
|
|
108
|
+
statusline.sh
|
|
109
|
+
mcp.json
|
|
110
|
+
.credentials.json ← OAuth token, never synced
|
|
111
|
+
projects/ ← session history, never synced
|
|
112
|
+
todos/ ← never synced
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The `claude-<name>` alias just sets `CLAUDE_CONFIG_DIR` and execs Claude Code. There's no wrapper, no daemon, no magic.
|
|
116
|
+
|
|
117
|
+
**Important trade-off:** because each profile is a full copy, **changes to one profile do not propagate to the others.** If you tweak a skill or add a hook in `personal`, the others won't see it. This is intentional: full isolation is what makes profiles safe across separate Anthropic accounts. A future `rebroadcast` command may help propagate common config; for now, copy manually.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Cross-machine sync
|
|
122
|
+
|
|
123
|
+
Sync uses your own git remote (GitHub, GitLab, self-hosted — whatever).
|
|
124
|
+
|
|
125
|
+
**What's synced:** `config.json`, `profiles/*/settings.json`, `profiles/*/CLAUDE.md`, `profiles/*/agents/`, `profiles/*/skills/`, `profiles/*/hooks/`, `profiles/*/plugins/` (config only, not the marketplace checkouts), `profiles/*/keybindings.json`, `profiles/*/statusline.sh`, `profiles/*/mcp.json`.
|
|
126
|
+
|
|
127
|
+
**What's NOT synced:**
|
|
128
|
+
|
|
129
|
+
| Path | Why |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `.credentials.json` | OAuth token — secret |
|
|
132
|
+
| `.claude.json` | Claude Code's per-machine runtime state |
|
|
133
|
+
| `settings.local.json` | Per-machine permission overrides |
|
|
134
|
+
| `projects/`, `todos/`, `session-env/` | Local session history |
|
|
135
|
+
| `plugins/known_marketplaces.json`, `plugins/marketplaces/`, `plugins/installed/` | Per-machine plugin install paths |
|
|
136
|
+
| `paste-cache/` | Clipboard cache |
|
|
137
|
+
| `statsig/`, `cache/`, `locks/`, `shell-snapshots/`, `*.log` | Per-machine runtime state |
|
|
138
|
+
|
|
139
|
+
You'll need to authenticate each profile on each machine — `claude-hopper` does not handle OAuth.
|
|
140
|
+
|
|
141
|
+
Sync is **explicit**: `claude-hopper sync push`, `claude-hopper sync pull`, `claude-hopper sync status`. Pull is fast-forward only; if histories diverge, resolve manually with `cd ~/.claude-hopper && git pull`.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Command reference
|
|
146
|
+
|
|
147
|
+
| Command | What it does |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `claude-hopper init [--remote <url>] [--no-sync] [--yes]` | Bootstrap on this machine. With `--remote`, clones an existing sync repo or initializes a new one. |
|
|
150
|
+
| `claude-hopper profile add <name> [--seed canonical\|empty\|clone:<name>]` | Create a new profile. |
|
|
151
|
+
| `claude-hopper profile list` | List profiles with health/auth status. |
|
|
152
|
+
| `claude-hopper profile remove <name> [--keep-files]` | Remove a profile (and its alias). |
|
|
153
|
+
| `claude-hopper profile clone <source> <new>` | Duplicate an existing profile, minus secrets. |
|
|
154
|
+
| `claude-hopper profile alias-install <name>` | (Re)install the shell alias for a profile. |
|
|
155
|
+
| `claude-hopper profile alias-remove <name>` | Remove just the shell alias. |
|
|
156
|
+
| `claude-hopper run <name> [...args]` | Launch Claude Code with the profile (or use the `claude-<name>` alias). |
|
|
157
|
+
| `claude-hopper doctor [--repair] [--profile <name>]` | Health-check profiles. `--repair` fixes what it can. |
|
|
158
|
+
| `claude-hopper sync push [--message <m>] [--force]` | Stage, commit, push. Doctor-gated unless `--force`. |
|
|
159
|
+
| `claude-hopper sync pull [--discard] [--no-repair]` | Fast-forward pull, then `doctor --repair` to fix local aliases. |
|
|
160
|
+
| `claude-hopper sync status` | Show ahead/behind, uncommitted files, last push/pull. |
|
|
161
|
+
| `claude-hopper uninstall` | Remove `~/.claude-hopper` and all aliases. Leaves `~/.claude` untouched. |
|
|
162
|
+
|
|
163
|
+
Most commands accept `--json` for machine-readable output. `run` accepts any args and passes them through to `claude` (e.g. `claude-hopper run personal --resume`).
|
|
164
|
+
|
|
165
|
+
Profile-name prefix matching is supported: `claude-hopper run p` is fine if it's unambiguous.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Troubleshooting
|
|
170
|
+
|
|
171
|
+
**`init --remote ...` errors with `Cannot reach git remote: ... Permission denied`** — auth isn't set up for that remote. Easiest fix: `gh auth login` and use the `https://` form of the URL. For SSH URLs, set up an SSH key (`ssh-keygen -t ed25519` then add it to GitHub via `gh ssh-key add ~/.ssh/id_ed25519.pub` or the web UI).
|
|
172
|
+
|
|
173
|
+
**`profile list` shows `No profiles yet` after `init --remote`** — the clone didn't happen. As of v0.1.1 this should now fail loudly during `init` (see above), but if you're on an older build, `claude-hopper uninstall && claude-hopper init --remote <url>` after fixing auth will get you sorted.
|
|
174
|
+
|
|
175
|
+
**`doctor` says `Directory exists ✗ Missing`** — run `claude-hopper doctor --repair` to recreate empty profile dirs (Claude Code will populate them on next launch).
|
|
176
|
+
|
|
177
|
+
**`doctor` says `Alias in ~/.zshrc ✗ missing`** — `claude-hopper profile alias-install <name>`, then `source ~/.zshrc`.
|
|
178
|
+
|
|
179
|
+
**`doctor` shows `No foreign absolute paths (config/code) ✗`** — one of your tracked config files contains a path like `/Users/someone-else/...`. Open the listed file and replace with a portable reference (e.g. `~/...`). This is the single most common cross-machine sync failure mode, and the doctor is built to catch it before the bad data hits your remote.
|
|
180
|
+
|
|
181
|
+
**`doctor` shows `No foreign absolute paths (docs) ⚠`** — a markdown file in a skill contains an example path like `/Users/me/...`. This is a warning, not a failure; push will still succeed. If you want it clean, edit the doc to use a placeholder like `<your-home>/...`.
|
|
182
|
+
|
|
183
|
+
**Aliases don't exist after `init` or `profile add`** — you need to `source ~/.zshrc` (or `~/.bashrc`, `~/.config/fish/config.fish`) to pick them up in the current shell. New terminals will pick them up automatically.
|
|
184
|
+
|
|
185
|
+
**`sync pull` says `Pull failed (not a fast-forward)`** — histories diverged. `cd ~/.claude-hopper && git pull` (or rebase/merge), then re-run `claude-hopper doctor --repair`.
|
|
186
|
+
|
|
187
|
+
**OAuth keeps grabbing the wrong account** — sign out of claude.ai in your browser first, or use an incognito window when authenticating a fresh profile. The `claude` CLI uses your browser's active session.
|
|
188
|
+
|
|
189
|
+
**`run` says `Could not find the claude executable`** — install [Claude Code](https://claude.com/claude-code), or set `CLAUDE_HOPPER_CLAUDE_BIN=/path/to/claude` if it's at a non-standard location.
|
|
190
|
+
|
|
191
|
+
**Want a stack trace?** Set `CLAUDE_HOPPER_DEBUG=1` and re-run.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Comparison to alternatives
|
|
196
|
+
|
|
197
|
+
**[jean-claude](https://github.com/anthropics/jean-claude)** stores absolute paths in its registry and pollutes `~/.claude/` with metadata, both of which break cross-machine sync. claude-hopper was built specifically to not do those things: every path is `~`-prefixed or profile-relative, and hopper data lives at `~/.claude-hopper/`, never inside `~/.claude/`. If you've been bitten by jean-claude on a second machine, that's the bug we don't repeat.
|
|
198
|
+
|
|
199
|
+
**[aimux](https://github.com/aimux/aimux)** is broader (multi-tool: Claude, Codex, Gemini). claude-hopper is Claude-only and treats that as a feature — fewer moving parts, no abstractions over per-tool quirks.
|
|
200
|
+
|
|
201
|
+
**[claude-swap](https://github.com/anthropics/claude-swap)** focuses on quota juggling between accounts with usage tracking. claude-hopper doesn't track usage; it's about long-lived, fully-isolated identities. If you want a usage dashboard, use claude-swap; if you want stable per-identity config that syncs across machines, use claude-hopper.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## How it works (one screen)
|
|
206
|
+
|
|
207
|
+
1. `claude-hopper init --remote <url>` creates `~/.claude-hopper/`. If the remote already has content, it clones into the hopper dir (so all profiles materialize immediately). If the remote is empty, it `git init`s and adds origin. Either way, it detects your shell, writes the `.gitignore` and sync-repo README templates, and runs `doctor --repair` to install aliases for any profiles that came from the clone.
|
|
208
|
+
2. `profile add <name>` makes `~/.claude-hopper/profiles/<name>/`, seeds it from your chosen source (`~/.claude`, another profile, or empty), and writes a marker-fenced alias into your shell rc file:
|
|
209
|
+
```bash
|
|
210
|
+
# >>> claude-hopper: personal >>>
|
|
211
|
+
alias claude-personal='CLAUDE_CONFIG_DIR="$HOME/.claude-hopper/profiles/personal" command claude'
|
|
212
|
+
# <<< claude-hopper: personal <<<
|
|
213
|
+
```
|
|
214
|
+
3. The alias uses `$HOME`, never a hard-coded absolute path. The marker comments let `alias-remove` and `uninstall` clean up cleanly years later.
|
|
215
|
+
4. `sync push` commits everything except secrets and per-machine state (see [the exclusion list](#cross-machine-sync)) and pushes. `sync pull` fast-forwards and then runs `doctor --repair` to (re)install aliases for any profiles that appeared in the pull.
|
|
216
|
+
|
|
217
|
+
The whole thing is a few hundred lines of TypeScript. No daemons, no caches, no background processes.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Roadmap
|
|
222
|
+
|
|
223
|
+
Out of scope for v1:
|
|
224
|
+
|
|
225
|
+
- Codex CLI and Gemini CLI support
|
|
226
|
+
- Per-machine config overrides
|
|
227
|
+
- A `rebroadcast` command for propagating changes across all profiles
|
|
228
|
+
- Usage tracking (5h / 7d limits per profile)
|
|
229
|
+
- Conflict resolution beyond fast-forward
|
|
230
|
+
- TUI mode
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## License
|
|
235
|
+
|
|
236
|
+
MIT
|