hatcheck 0.2.0__py3-none-win_amd64.whl
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.
- hatcheck-0.2.0.data/scripts/cl.exe +0 -0
- hatcheck-0.2.0.data/scripts/claude-k.exe +0 -0
- hatcheck-0.2.0.data/scripts/claude-z.exe +0 -0
- hatcheck-0.2.0.data/scripts/cx.exe +0 -0
- hatcheck-0.2.0.data/scripts/hat.exe +0 -0
- hatcheck-0.2.0.data/scripts/hatcheck.exe +0 -0
- hatcheck-0.2.0.dist-info/METADATA +841 -0
- hatcheck-0.2.0.dist-info/RECORD +11 -0
- hatcheck-0.2.0.dist-info/WHEEL +4 -0
- hatcheck-0.2.0.dist-info/licenses/LICENSE +21 -0
- hatcheck-0.2.0.dist-info/sboms/hatcheck.cyclonedx.json +3982 -0
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hatcheck
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Operating System :: MacOS
|
|
8
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Classifier: Programming Language :: Rust
|
|
11
|
+
Classifier: Topic :: Software Development
|
|
12
|
+
Classifier: Topic :: Utilities
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Summary: Claude Code and Codex accounts on one computer, each kept apart: recent sessions, plan usage, resume commands and moves across all of them.
|
|
15
|
+
Keywords: claude,claude-code,codex,cli,profiles,sessions
|
|
16
|
+
Home-Page: https://hatcheck.dev
|
|
17
|
+
Author: Yi Wang
|
|
18
|
+
License-Expression: MIT
|
|
19
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
|
+
Project-URL: Homepage, https://github.com/hatcheck/hatcheck
|
|
21
|
+
Project-URL: Issues, https://github.com/hatcheck/hatcheck/issues
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<picture>
|
|
25
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/hatcheck/hatcheck/main/site/logo-wordmark-dark.svg">
|
|
26
|
+
<img src="https://raw.githubusercontent.com/hatcheck/hatcheck/main/site/logo-wordmark.svg" alt="Hatcheck" width="360">
|
|
27
|
+
</picture>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
# Hatcheck
|
|
31
|
+
|
|
32
|
+
Several Claude accounts on one computer, each kept apart: isolated Claude Desktop instances on
|
|
33
|
+
macOS, and Claude Code CLI profiles, with recent sessions, plan usage and resume commands across all
|
|
34
|
+
of them. OpenAI's Codex CLI gets the same treatment, and sessions can be carried between the two.
|
|
35
|
+
|
|
36
|
+
*Unofficial: not made by, affiliated with or endorsed by Anthropic or OpenAI. Claude, Claude Code and
|
|
37
|
+
Claude Desktop are trademarks of Anthropic; Codex and ChatGPT are trademarks of OpenAI.*
|
|
38
|
+
|
|
39
|
+
Run **two (or more) fully isolated Claude Desktop instances** side by side on macOS — e.g. one
|
|
40
|
+
signed into your **personal** account and one into your **work** account — each with its own
|
|
41
|
+
**color-coded Dock launcher** that **focuses the existing window instead of spawning duplicates**.
|
|
42
|
+
|
|
43
|
+
This builds on Philipp Stracker's excellent
|
|
44
|
+
[`--user-data-dir` approach](https://gist.github.com/stracker-phil/9f84927a556632c7f9cc06663b534f14)
|
|
45
|
+
([write-up](https://philippstracker.com/multiple-claude-instances/)) and adds two things:
|
|
46
|
+
|
|
47
|
+
1. **Recolored icons** so you can tell the launchers apart at a glance (blue work / red personal / etc.).
|
|
48
|
+
2. A **smart, self-contained launcher** that fixes the "every click opens another window" problem.
|
|
49
|
+
|
|
50
|
+
It also has **command-line tools for Claude Code and [Codex](https://github.com/openai/codex)**, for
|
|
51
|
+
keeping one profile per account: `cl` and `cx` run Claude Code and Codex with a profile, and
|
|
52
|
+
`hatcheck` lists the recent sessions in all of them, with the command that resumes each one and
|
|
53
|
+
what's left of each plan. `hatcheck usage` shows the plans, `hatcheck best` says which account to use
|
|
54
|
+
now, `hatcheck where` finds a session, `hatcheck open` resumes one in a new terminal, and
|
|
55
|
+
`hatcheck move` carries one over to another profile, or converts it between Claude Code and Codex.
|
|
56
|
+
`hatcheck bind` ties a folder to a profile, so `cl` and `cx` pick it there, and `--json` gives the
|
|
57
|
+
sessions and plans to scripts and status bars. `claude-z` and `claude-k` run Claude Code on Z.ai's
|
|
58
|
+
GLM or Kimi's models instead, in any profile. See
|
|
59
|
+
[Claude Code and Codex CLI profiles](#claude-code-and-codex-cli-profiles).
|
|
60
|
+
|
|
61
|
+
| | macOS | Linux | Windows |
|
|
62
|
+
|---------------------------------|:-----:|:-----:|:-------:|
|
|
63
|
+
| Claude Desktop instances | yes | no official Desktop app | [planned](#roadmap) |
|
|
64
|
+
| Claude Code and Codex CLI tools | yes | yes | yes |
|
|
65
|
+
|
|
66
|
+
> ## ⭐ Recommended: give *every* account its own isolated instance
|
|
67
|
+
>
|
|
68
|
+
> The most reliable setup is to create a **separate, dedicated instance for each account —
|
|
69
|
+
> including your "personal" one — and stop using Claude's built-in default profile for day-to-day
|
|
70
|
+
> use.** Don't try to make one launcher target the shared default profile.
|
|
71
|
+
>
|
|
72
|
+
> Why: Claude has **no per-profile single-instance lock**, so two processes can run on one profile
|
|
73
|
+
> at once — and **only the *first* instance on a profile can use its login database. Every
|
|
74
|
+
> additional instance on that same profile shows up logged out.** If a "personal" launcher points
|
|
75
|
+
> at the default profile (which the stock Claude icon also uses), you end up with duplicate,
|
|
76
|
+
> logged-out windows. Giving each account its own dir guarantees **one profile → one instance →
|
|
77
|
+
> always logged in**, and makes the focus-don't-duplicate logic rock-solid.
|
|
78
|
+
>
|
|
79
|
+
> Already logged into the default profile and don't want to re-auth? See
|
|
80
|
+
> [Copying an existing login into an instance](docs/desktop-instances.md#copying-an-existing-login-into-an-instance) — you can copy your current
|
|
81
|
+
> profile into the new dedicated dir.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## How it works
|
|
86
|
+
|
|
87
|
+
Claude Desktop is an Electron app. Electron's `--user-data-dir` flag points the app at a
|
|
88
|
+
completely separate profile folder (config, sessions, **auth tokens**, MCP servers, Cowork data):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
open -n -a "/Applications/Claude.app" --args --user-data-dir="$HOME/.claude-instances/work"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A tiny macOS `.app` "wrapper" just runs that command, so it shows up in Launchpad/Dock like a
|
|
95
|
+
normal app. Each wrapper stores its instance name in `Contents/MacOS/config.sh` and runs
|
|
96
|
+
`Contents/MacOS/claude-launcher`.
|
|
97
|
+
|
|
98
|
+
### The duplicate problem (and the fix)
|
|
99
|
+
|
|
100
|
+
The naive launcher uses `open -n` — the `-n` flag means *"open a NEW instance, even if one is
|
|
101
|
+
already running."* Claude has **no per-profile single-instance lock**, so **every click spawns
|
|
102
|
+
another process** against the same profile dir. That stacks up duplicate windows and risks
|
|
103
|
+
corrupting the shared profile.
|
|
104
|
+
|
|
105
|
+
The **smart launcher** in this repo fixes it. On click it inspects *that specific instance*
|
|
106
|
+
(matched by `--user-data-dir`; for the default instance, the main process that has *no*
|
|
107
|
+
`--user-data-dir`) and does one of three things:
|
|
108
|
+
|
|
109
|
+
- **Running with a window** → focuses that process's window by PID via System Events. No duplicate.
|
|
110
|
+
- **Running but windowless** → opens a fresh window. Claude keeps its main process alive after you
|
|
111
|
+
close the window, and a windowless instance *cannot* be re-shown per-instance (all instances share
|
|
112
|
+
one bundle id, so `open -a` / "activate" can't target it). We deliberately **do not kill** the
|
|
113
|
+
windowless process: it is often windowless precisely because it's still doing background work
|
|
114
|
+
(e.g. a chat that's "thinking"), and killing it would lose that work. The leftover windowless
|
|
115
|
+
process is harmless and goes away when you fully **Quit (⌘Q)** Claude.
|
|
116
|
+
- **Not running** → launches fresh with `open -n`.
|
|
117
|
+
|
|
118
|
+
How "has a window?" is detected: a live window shows up as one or more `--type=renderer` processes
|
|
119
|
+
for that profile; closing the window drops that count to 0.
|
|
120
|
+
|
|
121
|
+
So "always click the launcher you want" reliably **focuses-or-launches** the right instance, even
|
|
122
|
+
after you've closed its window.
|
|
123
|
+
|
|
124
|
+
> **Tip:** to keep the focus-don't-relaunch behavior working, **close Claude with ⌘W** (close
|
|
125
|
+
> window, app keeps running) when you want it to stay live, and **⌘Q** when you truly want it gone.
|
|
126
|
+
|
|
127
|
+
### What the colors do (and don't do)
|
|
128
|
+
|
|
129
|
+
The recolored icon lives on the **launcher you click**. Because all instances share the one
|
|
130
|
+
`/Applications/Claude.app` binary, **while running they all show the same stock Dock icon and the
|
|
131
|
+
name "Claude"** — the color only marks the launcher. The reliable in-app tell is the signed-in
|
|
132
|
+
account. (Truly distinct *running* Dock icons would require a full standalone copy of Claude.app.)
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Claude Desktop instances (macOS)
|
|
137
|
+
|
|
138
|
+
`hatcheck desktop` makes them: each instance gets its own data folder, `~/.claude-instances/<name>`,
|
|
139
|
+
and a launcher in `/Applications` with Claude's icon in a colour you pick. The launcher brings a
|
|
140
|
+
running instance's window forward instead of starting a second one on the same folder.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
hatcheck desktop new work Work --hue 190 # Claude Work.app, blue
|
|
144
|
+
hatcheck desktop new personal Personal --hue -10 # Claude Personal.app, red-orange
|
|
145
|
+
hatcheck desktop open work # or open it from Launchpad or the Dock
|
|
146
|
+
hatcheck desktop # the instances, and which are running
|
|
147
|
+
hatcheck desktop remove work # its launcher; --data deletes its data folder too
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Sign each one in to its account the first time it opens. The first time a launcher brings a window
|
|
151
|
+
forward, macOS asks whether it may control System Events; allow it, once per launcher. `--hue`
|
|
152
|
+
turns the icon's colours by that many degrees (about 110 is green, 190 blue, 240 purple) and
|
|
153
|
+
`--saturation` scales them. hatcheck recolours the icon itself, with macOS's `iconutil`, and a
|
|
154
|
+
launcher it can't recolour keeps Claude's own icon. The name `default` makes a launcher for
|
|
155
|
+
Claude's built-in data folder, which is only safe while nothing else runs on that folder, the stock
|
|
156
|
+
Claude icon included. [docs/desktop-instances.md](docs/desktop-instances.md) has the rest: what
|
|
157
|
+
the launcher does, and how to copy a login you already have into an instance.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Claude Code and Codex CLI profiles
|
|
162
|
+
|
|
163
|
+
Claude Code (the `claude` CLI) keeps its settings, history and session transcripts in `~/.claude`
|
|
164
|
+
(plus `~/.claude.json`), and its login in the macOS Keychain, or on Linux and Windows in
|
|
165
|
+
`~/.claude/.credentials.json`. Point `CLAUDE_CONFIG_DIR` at another folder and you get a separate
|
|
166
|
+
profile with its own login, so you can keep one per account:
|
|
167
|
+
`~/.claude-work`, `~/.claude-personal`, and so on. These are separate from the Desktop instances
|
|
168
|
+
above, though sessions from Desktop's Code tab can be filed in them too (the session list below
|
|
169
|
+
hides those unless you pass `--all`). Codex works the same way with `CODEX_HOME`: see
|
|
170
|
+
[`cx`](#cx-run-codex-with-a-home-per-account).
|
|
171
|
+
|
|
172
|
+
### Install
|
|
173
|
+
|
|
174
|
+
One binary per platform, with no Python needed. Each of these installs `hatcheck`, `hat`, `cl`,
|
|
175
|
+
`cx`, `claude-z` and `claude-k`:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
curl -LsSf https://github.com/hatcheck/hatcheck/releases/latest/download/hatcheck-installer.sh | sh
|
|
179
|
+
brew install hatcheck/tap/hatcheck # Homebrew, on macOS and Linux
|
|
180
|
+
uv tool install hatcheck # or: pipx install hatcheck
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
On Windows, in PowerShell:
|
|
184
|
+
|
|
185
|
+
```powershell
|
|
186
|
+
powershell -ExecutionPolicy Bypass -c "irm https://github.com/hatcheck/hatcheck/releases/latest/download/hatcheck-installer.ps1 | iex"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The installer scripts put the commands in `~/.local/bin` and add that folder to your `PATH`; set
|
|
190
|
+
`HATCHECK_NO_MODIFY_PATH=1` first to keep your shell's startup files as they are. Each release, made
|
|
191
|
+
from a tag such as `v0.2.0`, has them and the binaries for each platform, and PyPI gets the same
|
|
192
|
+
binaries as wheels. The release builds start after 0.1.0, which is the Python version.
|
|
193
|
+
|
|
194
|
+
From a clone, on macOS and Linux, with Rust 1.90 or newer ([rustup.rs](https://rustup.rs)):
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
./install.sh # build the commands and link them into ~/.local/bin
|
|
198
|
+
./install.sh --bin ~/bin # or into another folder on your PATH
|
|
199
|
+
./install.sh --uninstall # remove the links again
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The links point at the build in `target/release`, so after a `git pull`, run `./install.sh` again
|
|
203
|
+
to rebuild them. The installer doesn't edit your shell startup files: if the folder isn't on your
|
|
204
|
+
`PATH`, it prints the line to add. A file that already has one of these names is left alone unless
|
|
205
|
+
you pass `--force`, except the links it made before, to the Python `hatcheck` and the `sh` launchers,
|
|
206
|
+
and the copies of `claude-z` and `claude-k` that the [claude-z](https://github.com/yiwang/claude-z)
|
|
207
|
+
installers made, which these replace. `hat` is installed as its short name too, unless another
|
|
208
|
+
command on your `PATH` already has that name.
|
|
209
|
+
|
|
210
|
+
Before the rename to Hatcheck these were separate commands: `claude-recent-sessions` (now
|
|
211
|
+
`hatcheck`), `claude-usage`, `claude-where`, `claude-move-session` and `claude-provider` (now
|
|
212
|
+
`hatcheck usage`, `where`, `move` and `provider`). The installers remove the links to them.
|
|
213
|
+
|
|
214
|
+
On Windows, in PowerShell:
|
|
215
|
+
|
|
216
|
+
```powershell
|
|
217
|
+
.\install.ps1 # build the commands and add .cmd launchers for them to ~\.local\bin
|
|
218
|
+
.\install.ps1 -Bin D:\bin # or to another folder on your PATH
|
|
219
|
+
.\install.ps1 -Uninstall # remove them again
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The launchers run the build, so after a `git pull`, run it again. They work from PowerShell and
|
|
223
|
+
`cmd` alike, whatever the script execution policy. If PowerShell won't run `install.ps1`, start it
|
|
224
|
+
with `powershell -ExecutionPolicy Bypass -File install.ps1`. On Windows, `hatcheck` prints `cd` and
|
|
225
|
+
resume commands for PowerShell. Paste them into PowerShell, not `cmd`, because `cmd` doesn't treat
|
|
226
|
+
the trailing `# usage` as a comment. Inside a Visual Studio developer prompt, the MSVC compiler is
|
|
227
|
+
also called `cl`, and whichever comes first on `PATH` wins.
|
|
228
|
+
|
|
229
|
+
#### Or as shell functions
|
|
230
|
+
|
|
231
|
+
`cl` and `cx` are small enough to keep as shell functions instead. Add this to `~/.zshrc` (or
|
|
232
|
+
`~/.bashrc`):
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# cl work --resume <id> == CLAUDE_CONFIG_DIR=~/.claude-work claude --resume <id>
|
|
236
|
+
# cl -k work ... runs the installed claude-k instead (-z: claude-z)
|
|
237
|
+
cl() {
|
|
238
|
+
local run=claude
|
|
239
|
+
case $1 in -z|-k) run=claude$1; shift ;; esac
|
|
240
|
+
[ -n "$1" ] || { echo "usage: cl [-z|-k] <profile> [claude args...] (uses ~/.claude-<profile>)" >&2; return 2; }
|
|
241
|
+
CLAUDE_CONFIG_DIR="$HOME/.claude-$1" "$run" "${@:2}"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# cx work resume <id> == CODEX_HOME=~/.codex-work codex resume <id>
|
|
245
|
+
cx() {
|
|
246
|
+
[ -n "$1" ] || { echo "usage: cx <profile> [codex args...] (uses ~/.codex-<profile>)" >&2; return 2; }
|
|
247
|
+
mkdir -p "$HOME/.codex-$1" && CODEX_HOME="$HOME/.codex-$1" codex "${@:2}"
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
A shell function takes precedence over an installed command with the same name, and both do the
|
|
253
|
+
same thing. `hatcheck` needs the install; without it, `find ~/.claude* ~/.codex* -name "*3f1c2a9e*"`
|
|
254
|
+
finds a session's files.
|
|
255
|
+
|
|
256
|
+
### `cl`: run Claude Code with a profile
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
cl work # Claude Code with ~/.claude-work (sign in on first run)
|
|
260
|
+
cl work --resume <session-id> # resume one of that profile's sessions
|
|
261
|
+
cl personal --continue # any claude option works
|
|
262
|
+
cl -k work --resume <session-id> # that session on Kimi; -z for Z.ai (see below)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Plain `claude` keeps using `~/.claude`. Claude Code names each profile's Keychain entry after its
|
|
266
|
+
`CLAUDE_CONFIG_DIR` value, so keep one spelling of each path; `cl` always uses `~/.claude-<name>`.
|
|
267
|
+
In a folder tied to a profile with [`hatcheck bind`](#hatcheck-bind-tie-a-folder-to-a-profile), the
|
|
268
|
+
name can be left out: `cl --resume <session-id>` there uses that profile.
|
|
269
|
+
|
|
270
|
+
### `cx`: run Codex with a home per account
|
|
271
|
+
|
|
272
|
+
[Codex](https://github.com/openai/codex) keeps its login, config and sessions in `~/.codex`, and
|
|
273
|
+
`CODEX_HOME` points it at another folder, so you can keep one home per ChatGPT account the same way:
|
|
274
|
+
`~/.codex-work`, `~/.codex-personal`, and so on.
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
cx work login # sign in once, with the work account
|
|
278
|
+
cx work # Codex with ~/.codex-work
|
|
279
|
+
cx work resume <session-id> # resume one of that home's sessions
|
|
280
|
+
cx personal exec "run the tests" # any codex command works
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
- `cx` makes the folder on first use, since Codex won't start with a `CODEX_HOME` that doesn't exist.
|
|
284
|
+
- A new home starts without your `~/.codex/config.toml` (model, trusted folders, MCP servers); copy
|
|
285
|
+
it over if you want the same settings.
|
|
286
|
+
- Plain `codex` keeps using `~/.codex`.
|
|
287
|
+
- In a folder tied to a home with [`hatcheck bind`](#hatcheck-bind-tie-a-folder-to-a-profile), the name
|
|
288
|
+
can be left out: `cx resume <session-id>` there uses that home. A Codex command such as `resume`
|
|
289
|
+
counts as a home's name only when `~/.codex-resume` exists; anywhere else, `cx resume …` says it
|
|
290
|
+
needs a home rather than making `~/.codex-resume`.
|
|
291
|
+
|
|
292
|
+
### `claude-z`, `claude-k`: Claude Code on Z.ai or Kimi
|
|
293
|
+
|
|
294
|
+
`claude-z` and `claude-k` start Claude Code on [Z.ai](https://z.ai)'s GLM models or
|
|
295
|
+
[Kimi](https://www.kimi.com/code)'s, with your plan there, instead of on Anthropic's. `cl -z <name>` and
|
|
296
|
+
`cl -k <name>` do the same in a profile, so a session can switch between Claude and those models and
|
|
297
|
+
stay in its profile:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
claude-k reconfig # once: your Kimi API key and model (claude-z reconfig for Z.ai)
|
|
301
|
+
claude-k # Claude Code on Kimi, with ~/.claude
|
|
302
|
+
cl -k work --resume <session-id> # carry on a work-profile session on Kimi…
|
|
303
|
+
cl work --resume <session-id> # …and later back on the work account's Claude plan
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- Setup asks for the API key (from [z.ai](https://z.ai/manage-apikey/apikey-list) or the
|
|
307
|
+
[Kimi Code Console](https://www.kimi.com/code)), the model, and an optional cap on output tokens.
|
|
308
|
+
It saves them in `~/.config/claude-z/config` or `~/.config/claude-k/config` (`%APPDATA%\claude-z\config`
|
|
309
|
+
on Windows), the files the [claude-z](https://github.com/yiwang/claude-z) launchers use, so a setup
|
|
310
|
+
made with those works as is. `hatcheck provider` shows what's set up.
|
|
311
|
+
- Models: on Z.ai `glm-5.3[1m]` (the default, with `glm-5.3-flash` for quick tasks), `glm-5.3-flash`
|
|
312
|
+
or `glm-4.7`; on Kimi `k3[1m]` (the default), `k3`, `kimi-for-coding` or
|
|
313
|
+
`kimi-for-coding-highspeed`. `[1m]` turns on the 1M-token context window.
|
|
314
|
+
- The key reaches Claude Code as `ANTHROPIC_AUTH_TOKEN`, which it always uses ahead of the profile's
|
|
315
|
+
claude.ai login and never asks about, and `ANTHROPIC_API_KEY` is cleared. Claude Code asks once per
|
|
316
|
+
profile whether to use an `ANTHROPIC_API_KEY`, with No preselected, and after a No it sends the
|
|
317
|
+
profile's claude.ai login token to the provider instead.
|
|
318
|
+
- The model is pinned with `ANTHROPIC_MODEL`, so a `"model"` in the profile's `settings.json`, such
|
|
319
|
+
as `"fable"`, can't send a Claude model name to Z.ai or Kimi.
|
|
320
|
+
- Resuming a session on another provider sends it the whole conversation so far, so check that's
|
|
321
|
+
allowed before doing it with a work account's sessions.
|
|
322
|
+
- `hatcheck usage` and the comments in `hatcheck` show the Claude and ChatGPT plans, not Z.ai's or
|
|
323
|
+
Kimi's.
|
|
324
|
+
|
|
325
|
+
Coming from the claude-z repo: `./install.sh` replaces the copies its installers put in
|
|
326
|
+
`~/.local/bin`, and your config stays. On Windows, delete `%LOCALAPPDATA%\claude-z` (and `\claude-k`)
|
|
327
|
+
and take it off your `PATH` by hand, but keep `%APPDATA%\claude-z`, which holds your key; that repo's
|
|
328
|
+
uninstallers delete both. The permission mode its installers asked for isn't used (the old launchers
|
|
329
|
+
only ever applied `dangerously-skip-permissions`): set `permissions.defaultMode` in the profile's
|
|
330
|
+
`settings.json`, or pass `--permission-mode`. OpenRouter (`claude-o`) isn't included.
|
|
331
|
+
|
|
332
|
+
### `hatcheck where`: find a session
|
|
333
|
+
|
|
334
|
+
```console
|
|
335
|
+
$ hatcheck where 3f1c2a9e
|
|
336
|
+
/Users/me/.claude-work/projects/-Users-me-code-api/3f1c2a9e-8b7d-4c1e-9f2a-6d5e4c3b2a10.jsonl
|
|
337
|
+
$ hatcheck where 019a8c1e
|
|
338
|
+
/Users/me/.codex/sessions/2026/10/14/rollout-2026-10-14T09-12-33-019a8c1e-7d3f-7b21-9c4e-2f1a0b9c8d7e.jsonl
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The profile in the path tells you how to resume it (`claude`, or `cl work` here), and the folder
|
|
342
|
+
under `projects/` is the directory the session ran in, with every non-alphanumeric character turned
|
|
343
|
+
into `-`. A session with subagents or large tool results also has a data folder of the same name,
|
|
344
|
+
which is listed too. Codex files its sessions by the date they started, archived ones under
|
|
345
|
+
`archived_sessions/`; resume one with `codex resume <id>`, or `cx <name> resume <id>` for a
|
|
346
|
+
`~/.codex-<name>` home.
|
|
347
|
+
|
|
348
|
+
### `hatcheck`: what to resume
|
|
349
|
+
|
|
350
|
+
`hat` is the short name: `hat`, `hat usage`, `hat move …` and so on work the same. `hat check` is the name
|
|
351
|
+
in two words, so it's `hat` again: `hat check` lists sessions and `hat check usage` is `hat usage`.
|
|
352
|
+
|
|
353
|
+
Lists the Claude Code sessions from every `~/.claude*` profile, and the Codex sessions from every
|
|
354
|
+
`~/.codex*` home, that were active in the last 24 hours or are still open, grouped by folder and
|
|
355
|
+
newest first. Run from a folder that has sessions, it lists
|
|
356
|
+
just that folder's (`--all-folders` lists them all). Each one comes with the command that resumes it
|
|
357
|
+
and what's left of that profile's plan:
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
$ hatcheck
|
|
361
|
+
Claude Code and Codex CLI sessions active in the last 24h or open now (by folder, newest first; % = plan left, ↻ = hours/days until reset)
|
|
362
|
+
cd ~/code/api
|
|
363
|
+
4m Fix flaky login test · main · ● open in pid 48213 (busy)
|
|
364
|
+
cl work --resume 3f1c2a9e-8b7d-4c1e-9f2a-6d5e4c3b2a10 # 5h 100% █████ Fable 48% ███▍░░░ ↻████▎░░ 4d6h
|
|
365
|
+
40m Add retries to the uploader · codex · main
|
|
366
|
+
cx work resume 019a8c1e-7d3f-7b21-9c4e-2f1a0b9c8d7e # 5h 80% ████░ ↻███▏░ 3h10m week 64% ████▌░░ ↻█▊░░░░░ 1d18h
|
|
367
|
+
1h Refactor the auth middleware · main · on k3
|
|
368
|
+
cl -k work --resume 2d4f6a8c-1e3b-4d5f-9a7c-0b2d4e6f8a1c
|
|
369
|
+
3h Add rate limiting to the login endpoint · feat/rate-limit
|
|
370
|
+
claude --resume 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d # 5h 62% ███▏░ ↻██▋░░ 2h41m week 91% ██████▍ ↻███▏░░░ 3d2h
|
|
371
|
+
cd ~/code/blog
|
|
372
|
+
20h Draft post on Rust lifetimes · main
|
|
373
|
+
cl personal --resume c47e2b1d-0a9f-4e8d-b7c6-5d4e3f2a1b0c # 5h ? ····· week 97% ██████▊ ↻█████▏░ 5d2h
|
|
374
|
+
cd ~/notes
|
|
375
|
+
6h Plan next sprint · main
|
|
376
|
+
cl work --resume 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e # 5h 100% █████ Fable 48% ███▍░░░ ↻████▎░░ 4d6h
|
|
377
|
+
9h Write release notes · main
|
|
378
|
+
cl work --resume 7e8f9a0b-1c2d-4e3f-8a4b-5c6d7e8f9a0b
|
|
379
|
+
Profiles: ~/.claude (1), ~/.claude-personal (1), ~/.claude-work (4)
|
|
380
|
+
Usage from Claude Code's last snapshot (token expired; run claude in this profile to renew it): ~/.claude-personal 9h ago
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
- Run the folder's `cd` line, then the session's command. Sessions still open in a terminal are
|
|
384
|
+
marked `● open`, so you don't resume one twice.
|
|
385
|
+
- The comment after each command shows each plan limit: the 5-hour session (`5h`), the weekly limit
|
|
386
|
+
(`week`) and per-model weekly limits such as `Fable`, which are named in violet, with their bar
|
|
387
|
+
drawn in it, so they stand apart from `week`. Each one has the % left and a bar of it, then `↻`, a bar of the time left until
|
|
388
|
+
it resets and that time. Bars have a block per hour of the 5-hour window and per day of a weekly
|
|
389
|
+
one, as in `↻████▎░░ 4d6h`.
|
|
390
|
+
- Time bars are coloured by urgency: blue with most of the window to go, then gold and orange, and
|
|
391
|
+
red as the reset nears.
|
|
392
|
+
- A plan bar shorter than its time bar means that limit is being used up faster than its window runs
|
|
393
|
+
out, like `Fable` above: 48% left with more than half the week to go. `?` means the limit has
|
|
394
|
+
reset since the saved snapshot it came from.
|
|
395
|
+
- A command without the comment, like the second one in `~/notes`, uses the same account as the
|
|
396
|
+
command above it, even through another profile, so its usage is the same.
|
|
397
|
+
- A session whose newest reply came from Z.ai or Kimi shows that model, like `on k3` above, and its
|
|
398
|
+
command resumes it there (`claude-k` for `~/.claude`). Use `cl work` instead to go back to Claude;
|
|
399
|
+
its comment is still the profile's Claude plan.
|
|
400
|
+
- Codex sessions are marked `codex` and resume with `codex resume <id>`, or `cx <name> resume <id>`
|
|
401
|
+
for a `~/.codex-<name>` home. Their comment shows the ChatGPT plan's Codex limits (see
|
|
402
|
+
`hatcheck usage` below). Sessions from the Codex app, its IDE extension and `codex exec` are hidden
|
|
403
|
+
like Desktop ones, and Codex's own approval reviews aren't listed.
|
|
404
|
+
- Since the usage is a shell comment, pasting the whole line runs just the command (zsh needs
|
|
405
|
+
`setopt interactivecomments`).
|
|
406
|
+
- Usage is fetched live, one request per account. When that fails (expired token, rate limit), Claude
|
|
407
|
+
Code's last saved snapshot is used instead and named at the bottom with its age.
|
|
408
|
+
- A session that ran in a folder [bound](#hatcheck-bind-tie-a-folder-to-a-profile) to another
|
|
409
|
+
profile is marked `⚠ folder bound to ~/.claude-work`, so it's easy to spot and `hatcheck move`.
|
|
410
|
+
- What it reads from each session file is cached, in `sessions.json` in `~/Library/Caches/hatcheck`
|
|
411
|
+
on macOS (`%LOCALAPPDATA%\hatcheck\Cache` on Windows, `~/.cache/hatcheck` elsewhere, or
|
|
412
|
+
`$HATCHECK_CACHE_DIR`). A repeat run reads only what was added to a session since, so a session
|
|
413
|
+
that's still being written costs little however long it is. Deleting the folder is always safe.
|
|
414
|
+
|
|
415
|
+
| Option | What it does |
|
|
416
|
+
|--------------|-------------------------------------------------------------------------------|
|
|
417
|
+
| `HOURS` | how far back to look (default 24); takes `36`, `1.5`, `12h` or `3d` |
|
|
418
|
+
| `-n N` | show only the newest N sessions per folder and count the rest |
|
|
419
|
+
| `--all` | also list sessions from Claude Desktop's Code tab, the SDK, the Codex app, its IDE extension and `codex exec` |
|
|
420
|
+
| `--all-folders` | list every folder, even when run from one that has sessions |
|
|
421
|
+
| `--full` | print `CLAUDE_CONFIG_DIR=~/.claude-<name> claude …` instead of `cl`, and `CODEX_HOME=… codex …` instead of `cx` |
|
|
422
|
+
| `--cached` | take usage from Claude Code's and Codex's saved snapshots, without calling the APIs |
|
|
423
|
+
| `--no-usage` | leave plan usage out |
|
|
424
|
+
| `--json` | print the sessions, and the accounts behind them, as JSON ([below](#--json-for-scripts-status-bars-and-apps)) |
|
|
425
|
+
| `-e`, `--email` | with `--json`, include each account's email address |
|
|
426
|
+
|
|
427
|
+
### `hatcheck usage`: plan usage for every profile
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
$ hatcheck usage
|
|
431
|
+
Plan usage by profile (% left, then time left until reset)
|
|
432
|
+
Sorted by Fable: those with some left first, the soonest reset on top; then those out of it, the soonest back on top.
|
|
433
|
+
~/.claude-work team max 5x · live
|
|
434
|
+
session (5h) 100% ██████████
|
|
435
|
+
week (Fable) 48% ████▊░░░░░ ██████▏░░░ resets Sun 8:20pm (in 4d6h)
|
|
436
|
+
~/.claude pro · live
|
|
437
|
+
session (5h) 62% ██████▎░░░ █████▍░░░░ resets 5:01pm (in 2h41m)
|
|
438
|
+
week (all models) 91% █████████▏ ████▍░░░░░ resets Sat 4:20pm (in 3d2h)
|
|
439
|
+
~/.claude-personal pro · cached 9h00m ago (token expired; run claude in this profile to renew it)
|
|
440
|
+
session (5h) ? ·········· reset 10:20am, after this snapshot
|
|
441
|
+
week (all models) 97% █████████▊ ███████▎░░ resets Mon 4:20pm (in 5d2h)
|
|
442
|
+
~/.codex-work ChatGPT plus · live
|
|
443
|
+
session (5h) 80% ████████░░ ██████▍░░░ resets 5:30pm (in 3h10m)
|
|
444
|
+
week (all models) 64% ██████▍░░░ ██▌░░░░░░░ resets Fri 8:20am (in 1d18h)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
- Accounts are sorted by their Fable limit. First come those with some Fable left, the soonest
|
|
448
|
+
Fable reset on top: what's left there goes unused once it resets, so that's the one to use first.
|
|
449
|
+
Next come those out of Fable, or out of the weekly limit for all models, which Fable counts against
|
|
450
|
+
too, the soonest back on top. Then come accounts without a Fable limit, such as Pro plans, and
|
|
451
|
+
Codex homes. A snapshot saved before its Fable limit reset counts as full until the reset after.
|
|
452
|
+
- Each limit has two bars: what's left of it, then how much of its window, 5 hours or a week, is
|
|
453
|
+
left before it resets. In a terminal the second goes from blue with most of the window to go,
|
|
454
|
+
through gold and orange, to red as the reset nears. When the first bar is shorter than the
|
|
455
|
+
second, as for `Fable` here, the limit is being used up faster than its window runs out. A limit
|
|
456
|
+
without a reset time, like an unused 5-hour session, has no time bar.
|
|
457
|
+
- It reads each profile's OAuth token from the macOS Keychain, or from the profile's
|
|
458
|
+
`.credentials.json` on Linux and Windows, and asks the same endpoint as Claude Code's `/usage`. The token is only read, never refreshed: a refresh rotates it and would sign Claude Code
|
|
459
|
+
out of that profile.
|
|
460
|
+
- When a token has expired or the endpoint rate-limits the account, Claude Code's last saved snapshot
|
|
461
|
+
is shown with its age. Run `claude` (or `cl <name>`) in that profile to renew the token.
|
|
462
|
+
- Profiles signed in to the same account are shown together. Per-model limits such as
|
|
463
|
+
`week (Fable)` are named in violet, with their bar drawn in it, to tell them from `week (all
|
|
464
|
+
models)`; the % keeps the severity's colour. Quota fields the server
|
|
465
|
+
sends that Claude Code doesn't display are listed dimmed under their raw names.
|
|
466
|
+
- Codex homes show their ChatGPT plan's Codex limits: the 5-hour and weekly windows (a free plan has
|
|
467
|
+
one 30-day window instead), then limits for single models such as `GPT-5.3-Codex-Spark`, named in
|
|
468
|
+
violet like `Fable`. The access token in the home's `auth.json` is sent as it is to the endpoint
|
|
469
|
+
Codex's `/status` uses, and never refreshed. When it has expired, or the request fails, the newest
|
|
470
|
+
limits Codex recorded in that home's session files are shown, with their age. Codex doesn't send a
|
|
471
|
+
severity, so a limit turns yellow from 75% used and red from 90%. Homes signed in to the same
|
|
472
|
+
account are shown together.
|
|
473
|
+
- `--cached` skips the APIs. `-e` / `--email` adds each account's email address, which is hidden by
|
|
474
|
+
default so the output is safe to share. `--json` prints the accounts as data
|
|
475
|
+
([below](#--json-for-scripts-status-bars-and-apps)).
|
|
476
|
+
|
|
477
|
+
### `hatcheck move`: carry on a session with another profile
|
|
478
|
+
|
|
479
|
+
When one account runs low, continue a session on another profile, or in the other agent. It copies
|
|
480
|
+
the session by default; `--no-keep` deletes the original once the copy is checked, which moves it.
|
|
481
|
+
|
|
482
|
+
```
|
|
483
|
+
$ hatcheck move 9a8b7c6d work
|
|
484
|
+
Copied session 9a8b7c6d "Add rate limiting to the login endpoint" from ~/.claude to ~/.claude-work:
|
|
485
|
+
transcript 53.0 KB
|
|
486
|
+
subagents and tool output 3 files
|
|
487
|
+
/rewind file snapshots 4 files
|
|
488
|
+
plan quiet-river-plan.md
|
|
489
|
+
Paths in the conversation to these files now point to the copies.
|
|
490
|
+
Kept the original in ~/.claude; --no-keep deletes it once the copy is checked.
|
|
491
|
+
Resume it with:
|
|
492
|
+
cd ~/code/api
|
|
493
|
+
cl work --resume 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d
|
|
494
|
+
Resume it now in ~/.claude-work? [y/N]
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
- It copies the transcript, the folder of subagent transcripts and saved tool output, the `/rewind`
|
|
498
|
+
file snapshots and the plan file, keeping their timestamps, and checks every copied file. Paths in
|
|
499
|
+
the conversation that point at those files are changed to point at the copies, except inside
|
|
500
|
+
Claude's thinking, whose signatures would stop matching.
|
|
501
|
+
- A session that's open in a terminal is refused: quit it first, so it stops changing.
|
|
502
|
+
`hatcheck` marks those `● open`.
|
|
503
|
+
- Resume it with the printed command, from the directory it ran in, or answer `y` when it asks and
|
|
504
|
+
it starts Claude Code there with the new profile. It only asks in a terminal, so scripts never
|
|
505
|
+
wait on it. The conversation comes across whole; the account and its plan limits, settings,
|
|
506
|
+
permissions, MCP servers, plugins and project memory are the new profile's. If the new account
|
|
507
|
+
rejects the signatures on earlier thinking, Claude Code drops those thinking blocks and carries
|
|
508
|
+
on.
|
|
509
|
+
- Plan files stay in the old profile even with `--no-keep`, since sessions can share one.
|
|
510
|
+
- If the session's last reply came from Z.ai or Kimi, it also prints the `cl -z` or `cl -k` command
|
|
511
|
+
that carries on there instead of on the new profile's Claude plan.
|
|
512
|
+
|
|
513
|
+
#### When the profile already has a copy
|
|
514
|
+
|
|
515
|
+
A session copied to another profile and carried on there can be moved back: the copy it left
|
|
516
|
+
behind is an older version of it. The two copies are compared line by line:
|
|
517
|
+
|
|
518
|
+
- **The copy there is older**, because the session went on from it: it's brought up to date, and
|
|
519
|
+
each item shows `(updated an older copy)`. Nothing is lost, since the copy being moved starts with
|
|
520
|
+
all of it.
|
|
521
|
+
- **The copy there is newer**, because the session went on there: nothing is copied. With
|
|
522
|
+
`--no-keep`, the older copy here is deleted.
|
|
523
|
+
- **Both went on** after the copy was made: nothing is copied, since that would lose the prompts
|
|
524
|
+
only the copy there has. It says when they split and how many prompts each has had since.
|
|
525
|
+
`--fork` brings this one in as a new session with its own ID and keeps both, as Claude Code's
|
|
526
|
+
`claude --resume <id> --fork-session` and `codex fork` do. Or delete the copy there first.
|
|
527
|
+
|
|
528
|
+
```
|
|
529
|
+
$ hatcheck move 9a8b7c6d default
|
|
530
|
+
Session 9a8b7c6d went on in both ~/.claude-work and ~/.claude after Oct 15 01:40: 3 new prompts in ~/.claude-work, 1 in ~/.claude. Copying this one over would lose what's only in ~/.claude, so nothing was copied.
|
|
531
|
+
To keep both, bring this one in as a new session with its own ID:
|
|
532
|
+
hatcheck move 9a8b7c6d default --fork
|
|
533
|
+
Or delete the copy in ~/.claude first, to replace it:
|
|
534
|
+
~/.claude/projects/-Users-me-code-api/9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d.jsonl
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
A fork changes the session's ID in its records and gives it its own folders for subagents, saved
|
|
538
|
+
tool output and `/rewind` snapshots. Its message IDs stay the same, as in Claude Code's own fork.
|
|
539
|
+
Resuming a fork, a round trip and a copy that went on in both profiles were tested with the real
|
|
540
|
+
`claude` (2.1.301).
|
|
541
|
+
|
|
542
|
+
When more than one profile has the session, you're asked which copy to take. The one used last
|
|
543
|
+
comes first and is the default, and each other one says how it stands to it:
|
|
544
|
+
|
|
545
|
+
```
|
|
546
|
+
$ hatcheck move 4215508b hp-alan
|
|
547
|
+
Session 4215508b is in 3 profiles:
|
|
548
|
+
1. ~/.claude-hp-aria Oct 17 14:02 312 prompts used last
|
|
549
|
+
2. ~/.claude-hp-yi Oct 16 09:11 298 prompts 14 prompts behind 1
|
|
550
|
+
3. ~/.claude-hp-aiqing Oct 12 18:40 201 prompts went its own way after Oct 11 20:15: 2 prompts of its own
|
|
551
|
+
Which one? [1]
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
A number, a profile's name or Enter picks one. When the output isn't a terminal, as in a script or
|
|
555
|
+
with `--json`, the copy used last is taken and a line says so; `--from` always decides.
|
|
556
|
+
|
|
557
|
+
#### With Codex
|
|
558
|
+
|
|
559
|
+
`codex` is `~/.codex` and `codex:<name>` is `~/.codex-<name>`, as a place to copy to or `--from`:
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
$ hatcheck move 019a8c1e work
|
|
563
|
+
Converted Codex session 019a8c1e "Add retries to the uploader" from ~/.codex-work into a Claude Code session in ~/.claude-work:
|
|
564
|
+
conversation 5 of your messages, with the replies
|
|
565
|
+
tool calls 191, as text with their output shortened
|
|
566
|
+
left out Codex's reasoning, images, goals and subagent sessions
|
|
567
|
+
new session 6f6749d7-0f59-4683-997c-a3595421b93f (241.9 KB)
|
|
568
|
+
The original stays in ~/.codex-work.
|
|
569
|
+
Resume it with:
|
|
570
|
+
cd ~/code/api
|
|
571
|
+
cl work --resume 6f6749d7-0f59-4683-997c-a3595421b93f
|
|
572
|
+
Resume it now in ~/.claude-work? [y/N]
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
- Between Codex homes signed in to the same ChatGPT account, the session file is copied as it is,
|
|
576
|
+
with its ID and its name, and `--no-keep` works as for Claude Code. A copy already there is
|
|
577
|
+
compared the same way, and `--fork` gives the copy a new ID in its file name, its
|
|
578
|
+
`session_meta` and its events.
|
|
579
|
+
- Between Claude Code and Codex (`hatcheck move 3f1c2a9e codex:work` goes the other way), and
|
|
580
|
+
between Codex homes on different accounts, the session is converted into a new one with a new ID,
|
|
581
|
+
and the original is always kept. Codex encrypts its reasoning and its summaries of compacted
|
|
582
|
+
context for the account that wrote them, so a copy can't take them to another account.
|
|
583
|
+
- Your messages and the replies come across as text, and each tool call as a block with its input
|
|
584
|
+
and the start of its output, in the format Codex's own `/import` uses for Claude Code sessions
|
|
585
|
+
(`[external_agent_tool_call: Bash] … [/external_agent_tool_call]`). A note at the start tells the
|
|
586
|
+
model where the session came from. Claude's thinking and Codex's reasoning are left out, and so are
|
|
587
|
+
images, plans, goals, `/rewind` snapshots and subagent transcripts.
|
|
588
|
+
- The whole conversation before a compaction is carried over, not its summary. Past about 100,000
|
|
589
|
+
tokens the oldest turns after the first are left out, so it fits the new agent's context window.
|
|
590
|
+
- Converted sessions were tested by resuming them with the real `claude` (2.1.301) and `codex`
|
|
591
|
+
(0.155.1), which sent the whole converted history to the model and carried on in the same
|
|
592
|
+
session. Codex's resume picker lists them by their Claude Code title.
|
|
593
|
+
- Codex can import Claude Code sessions itself (`/import` in Codex), but only from `~/.claude` and
|
|
594
|
+
the last 30 days. Claude Code's `claude import codex` brings over Codex's instructions, agents and
|
|
595
|
+
hooks, not sessions.
|
|
596
|
+
|
|
597
|
+
| Option | What it does |
|
|
598
|
+
|------------------|------------------------------------------------------------------------------|
|
|
599
|
+
| `SESSION` | the session ID, or the start of it |
|
|
600
|
+
| `PROFILE` | where it goes: `NAME` for `~/.claude-NAME`, `default` for `~/.claude`, `codex` for `~/.codex`, `codex:NAME` for `~/.codex-NAME`, or a path |
|
|
601
|
+
| `--from PROFILE` | the profile or home to take it from, when more than one has it; without it you're asked, with the one used last as the default, and a script or `--json` gets that one |
|
|
602
|
+
| `--no-keep` | delete the original once the copy is checked, which moves it (not when converting) |
|
|
603
|
+
| `--fork` | copy it in as a new session with its own ID, leaving a copy already there as it is (a converted session always gets one) |
|
|
604
|
+
| `--dry-run` | show what would be copied, converted and deleted, and change nothing |
|
|
605
|
+
|
|
606
|
+
### `hatcheck open`: resume a session in a new terminal
|
|
607
|
+
|
|
608
|
+
```console
|
|
609
|
+
$ hatcheck open 3f1c2a9e # a new terminal window in ~/code/api, running cl work --resume …
|
|
610
|
+
$ hatcheck open # the newest session started from this folder, in any profile or home
|
|
611
|
+
$ hatcheck open 3f1c2a9e --here # in this terminal instead
|
|
612
|
+
$ hatcheck open 3f1c2a9e --print # the two commands, for scripts
|
|
613
|
+
cd ~/code/api
|
|
614
|
+
cl work --resume 3f1c2a9e-8b7d-4c1e-9f2a-6d5e4c3b2a10
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
- It finds the session by its ID or the start of it, in every Claude Code profile and Codex home.
|
|
618
|
+
When more than one profile has a copy, it takes the one used last; `--from <profile>` picks one.
|
|
619
|
+
- The command is the one `hatcheck` prints: `cl` or `claude`, `cx` or `codex`, and `cl -z`/`cl -k` for a
|
|
620
|
+
session whose last reply came from Z.ai or Kimi. Without `cl` or `cx` on the `PATH` it's the full
|
|
621
|
+
form (`CLAUDE_CONFIG_DIR=… claude …`), as `--full` gives.
|
|
622
|
+
- A session that's open in a terminal is refused, so it isn't resumed twice.
|
|
623
|
+
- The terminal:
|
|
624
|
+
- **macOS:** Terminal, or the one `hatcheck` runs in when that's iTerm2, Ghostty or WezTerm. Terminal
|
|
625
|
+
and iTerm2 are driven with AppleScript, which macOS asks you to allow once.
|
|
626
|
+
- **Windows:** Windows Terminal (`wt`), otherwise a new PowerShell window.
|
|
627
|
+
- **Linux:** `$TERMINAL`, otherwise `x-terminal-emulator`, GNOME Terminal or Konsole.
|
|
628
|
+
- `--terminal NAME`, or `HATCHECK_TERMINAL`, picks another: `terminal`, `iterm`, `ghostty`,
|
|
629
|
+
`wezterm`, `wt`, `powershell`, `gnome-terminal`, `konsole`, `kitty`, or any terminal that takes
|
|
630
|
+
`-e`. A shell stays open in the folder after the session ends.
|
|
631
|
+
- If the terminal can't be opened, it prints the two commands to run instead.
|
|
632
|
+
|
|
633
|
+
### `hatcheck best`: which account to use now
|
|
634
|
+
|
|
635
|
+
```console
|
|
636
|
+
$ hatcheck best
|
|
637
|
+
Claude Code, best first (sorted by Fable, as hatcheck usage sorts)
|
|
638
|
+
1. ~/.claude-work Fable 31% left, resets Tue 12:00pm · 5h 80% → cl work
|
|
639
|
+
2. ~/.claude-team Fable 100% left, resets Thu 1:00am · 5h 100% → cl team
|
|
640
|
+
3. ~/.claude-personal Fable 0% left, back Sat 4:59pm · 5h 100% → cl personal
|
|
641
|
+
4. ~/.claude week 64% · 5h 100% → claude
|
|
642
|
+
Codex, best first
|
|
643
|
+
1. ~/.codex month 100% → codex
|
|
644
|
+
Advice only: nothing is started, moved or switched. hatcheck best --for <session> says where to carry a session.
|
|
645
|
+
$ hatcheck best --for 9a8b7c6d
|
|
646
|
+
Session 9a8b7c6d "Add rate limiting to the login endpoint" is in ~/.claude-personal: Fable 0% left, back Sat 4:59pm · 5h 100%.
|
|
647
|
+
Carry it to ~/.claude-work: Fable 31% left, resets Tue 12:00pm · 5h 80%.
|
|
648
|
+
hatcheck move 9a8b7c6d work --from personal
|
|
649
|
+
It prints the command that resumes it there, and offers to run it.
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
- Claude Code accounts come in the order `hatcheck usage` sorts them: those with Fable left first,
|
|
653
|
+
the soonest Fable reset on top; then those out of Fable, or out of the weekly limit for all models,
|
|
654
|
+
the soonest back on top; then accounts without a Fable limit, by the limit with the least left.
|
|
655
|
+
Codex homes are ranked by the limit with the least left.
|
|
656
|
+
- `--for <session>` picks the best other account of the session's agent that has room now, and
|
|
657
|
+
prints the [`hatcheck move`](#hatcheck-move-carry-on-a-session-with-another-profile) command. If
|
|
658
|
+
the session's own account is the best, it says so and prints the resume command.
|
|
659
|
+
- The numbers are the ones `hatcheck usage` shows, live when it can be, with a snapshot's age
|
|
660
|
+
otherwise; `--cached` skips the APIs.
|
|
661
|
+
- **Advice only.** It runs when you ask, prints commands, and never starts, moves or switches
|
|
662
|
+
anything. Using several accounts to get around one plan's limits may break Anthropic's or OpenAI's
|
|
663
|
+
terms; this is for choosing between accounts you may use for the work at hand, such as your own
|
|
664
|
+
and your company's.
|
|
665
|
+
|
|
666
|
+
### `hatcheck bind`: tie a folder to a profile
|
|
667
|
+
|
|
668
|
+
With a work and a personal account, it's easy to start `claude` in a work repo out of habit. The
|
|
669
|
+
session then runs on the personal account and is filed in the personal profile. A binding says
|
|
670
|
+
which profile a folder's work belongs to:
|
|
671
|
+
|
|
672
|
+
```console
|
|
673
|
+
$ cd ~/code/api
|
|
674
|
+
$ hatcheck bind work # this folder and everything under it: ~/.claude-work, ~/.codex-work
|
|
675
|
+
Bound ~/code/api and everything under it: Claude Code ~/.claude-work · Codex ~/.codex-work.
|
|
676
|
+
Here, cl --resume <id> means cl work --resume <id> and cx resume <id> means cx work resume <id>.
|
|
677
|
+
$ hatcheck bind work --codex personal # a different Codex home
|
|
678
|
+
$ hatcheck bind # what this folder is bound to
|
|
679
|
+
$ hatcheck bind --list # every bound folder
|
|
680
|
+
$ hatcheck unbind # untie it
|
|
681
|
+
$ hatcheck bind --hook # warn in Claude Code and Codex (once; hatcheck unbind --hook undoes it)
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
- Bindings live in your own config, `~/.config/hatcheck/bindings.json`
|
|
685
|
+
(`%APPDATA%\hatcheck\bindings.json` on Windows), not in the repo, so profile names don't end up in
|
|
686
|
+
git. The deepest bound folder wins, and folders are compared once symlinks are resolved.
|
|
687
|
+
- `default` binds `~/.claude` and `~/.codex`. The Codex home is the one named like the profile when
|
|
688
|
+
there is one; `--codex <name>` picks another, and `--codex` alone binds just the Codex home.
|
|
689
|
+
- `cl` and `cx` use the binding when no profile name is given: `cl --resume <id>`, `cl`,
|
|
690
|
+
`cx resume <id>`. A name given still wins, and outside bound folders nothing changes.
|
|
691
|
+
- `hatcheck` marks sessions that ran in a bound folder under another profile:
|
|
692
|
+
`⚠ folder bound to ~/.claude-work`.
|
|
693
|
+
- `--hook` adds a `SessionStart` hook, `hatcheck bind --check`, to every profile's `settings.json`
|
|
694
|
+
and every Codex home's `hooks.json`. When a session starts in a bound folder under another
|
|
695
|
+
profile, it shows a warning and nothing is blocked:
|
|
696
|
+
`hatcheck: ~/code/api is bound to ~/.claude-work, but this session runs in ~/.claude-personal. Quit
|
|
697
|
+
and run cl work here instead.` For a resumed session it gives the `hatcheck move` command instead.
|
|
698
|
+
- **Claude Code:** tested with the real `claude` (2.1.301). It shows the warning in the interactive
|
|
699
|
+
UI; `claude -p` doesn't display it.
|
|
700
|
+
- **Codex:** Codex runs a new hook only once you trust it with `/hooks` in `codex`, once per home.
|
|
701
|
+
Tested with `codex exec` (0.163): it runs the hook from the home's `hooks.json`, with the folder,
|
|
702
|
+
the session and `CODEX_HOME`, but `codex exec` doesn't print hook messages. Codex's docs say its
|
|
703
|
+
interactive UI shows them as a warning; that part wasn't tested here.
|
|
704
|
+
|
|
705
|
+
### `--json`: for scripts, status bars and apps
|
|
706
|
+
|
|
707
|
+
`hatcheck --json` and `hatcheck usage --json` print what the text shows, as data:
|
|
708
|
+
|
|
709
|
+
```console
|
|
710
|
+
$ hatcheck --json
|
|
711
|
+
{
|
|
712
|
+
"version": 1,
|
|
713
|
+
"generated_at": "2026-10-15T22:41:07Z",
|
|
714
|
+
"hours": 24,
|
|
715
|
+
"this_folder_only": false,
|
|
716
|
+
"sessions": [
|
|
717
|
+
{
|
|
718
|
+
"agent": "claude",
|
|
719
|
+
"id": "3f1c2a9e-8b7d-4c1e-9f2a-6d5e4c3b2a10",
|
|
720
|
+
"profile": "/Users/me/.claude-work",
|
|
721
|
+
"account": 0,
|
|
722
|
+
"title": "Fix flaky login test",
|
|
723
|
+
"cwd": "/Users/me/code/api",
|
|
724
|
+
"last_active": "2026-10-15T22:37:02Z",
|
|
725
|
+
"entrypoint": "cli",
|
|
726
|
+
"branch": "main",
|
|
727
|
+
"model": "claude-fable-5-1",
|
|
728
|
+
"open": {"pid": 48213, "status": "busy"},
|
|
729
|
+
"continued_in": null,
|
|
730
|
+
"bound_to": null,
|
|
731
|
+
"resume": "cl work --resume 3f1c2a9e-8b7d-4c1e-9f2a-6d5e4c3b2a10"
|
|
732
|
+
}
|
|
733
|
+
],
|
|
734
|
+
"accounts": [
|
|
735
|
+
{
|
|
736
|
+
"agent": "claude",
|
|
737
|
+
"profiles": ["/Users/me/.claude-work"],
|
|
738
|
+
"plan": "team max 5x",
|
|
739
|
+
"source": "live",
|
|
740
|
+
"saved_at": null,
|
|
741
|
+
"why": null,
|
|
742
|
+
"limits": [
|
|
743
|
+
{"name": "session (5h)", "short": "5h", "kind": "session", "used_percent": 20, "left_percent": 80,
|
|
744
|
+
"resets_at": "2026-10-16T01:30:00Z", "window_seconds": 18000, "severity": "normal", "per_model": false}
|
|
745
|
+
],
|
|
746
|
+
"extra": null,
|
|
747
|
+
"fable": {"left": true, "resets_at": "2026-10-20T16:00:00Z"}
|
|
748
|
+
}
|
|
749
|
+
],
|
|
750
|
+
"hidden": {"Desktop": 3},
|
|
751
|
+
"other_folders": 0,
|
|
752
|
+
"older_not_listed": {}
|
|
753
|
+
}
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
- **The same choices as the text:** the time window, `--all`, `--all-folders` and `-n` pick the same
|
|
757
|
+
sessions in the same order, and `resume` is the command the text prints. `account` is the
|
|
758
|
+
session's place in `accounts`, which holds the accounts behind the listed sessions (none with
|
|
759
|
+
`--no-usage`). `hatcheck usage --json` gives every account, in its Fable order, with `sorted_by`.
|
|
760
|
+
- **Stable:** absolute paths rather than `~`, times in UTC (ISO 8601), no colours. `version` goes up
|
|
761
|
+
only when a field changes meaning or goes away; new fields can appear at any time.
|
|
762
|
+
- `left_percent` is `null` for a snapshot's limit that has reset since it was saved. `fable` is
|
|
763
|
+
`{"left": true, "resets_at": …}`, `{"left": false, "back_at": …}`, or `null` without a Fable limit.
|
|
764
|
+
`source` is `live`, `cached` or `none`, with `why` when it isn't live.
|
|
765
|
+
- Errors go to stderr with a non-zero exit, so stdout is always valid JSON. Email addresses only with
|
|
766
|
+
`--email`.
|
|
767
|
+
|
|
768
|
+
## The desktop app
|
|
769
|
+
|
|
770
|
+
A menu-bar app (system tray on Windows and Linux) built on the CLI. It shows what's left of every
|
|
771
|
+
plan, sorted by Fable, with bars and reset times, and the last day's sessions with **Resume**,
|
|
772
|
+
which opens a terminal in the session's folder, and **Copy**. It also has buttons for the Claude
|
|
773
|
+
Desktop instances and notifies you when a limit runs low or resets.
|
|
774
|
+
|
|
775
|
+
Its main window browses every session, with search, filters and a read-only preview of the
|
|
776
|
+
conversation. From there you can resume a session, copy its command, or move it to another
|
|
777
|
+
profile after a dry run. The window also lists profiles and homes, signs in to a new one in a
|
|
778
|
+
terminal, and makes Desktop instances.
|
|
779
|
+
|
|
780
|
+
It ships the CLI inside itself, so it needs no Python. It never handles a login, and live usage is
|
|
781
|
+
off until you turn it on. See [desktop/README.md](desktop/README.md) to build it; releases come
|
|
782
|
+
from `.github/workflows/desktop.yml` for macOS, Windows and Linux. The plan behind it is
|
|
783
|
+
[the proposal](docs/proposals/desktop-app.md).
|
|
784
|
+
|
|
785
|
+
## Files in this repo
|
|
786
|
+
|
|
787
|
+
- **`crates/hatcheck/`**: hatcheck in Rust, the commands and the launchers. Its `src/` has a file per
|
|
788
|
+
subcommand (`sessions.rs`, `usage.rs`, `move_cmd.rs`, `open_cmd.rs`, `desktop.rs` and the rest),
|
|
789
|
+
`launch.rs` for `cl`, `cx`, `claude-z` and `claude-k`, and `src/bin/` for those binaries and `hat`.
|
|
790
|
+
It was ported from a Python version with the same output; see [docs/rust-port.md](docs/rust-port.md).
|
|
791
|
+
- **`install.sh`** / **`install.ps1`**: build the commands from a clone and put them on your `PATH`
|
|
792
|
+
(`~/.local/bin` by default).
|
|
793
|
+
- **`pyproject.toml`**: the PyPI package, the binaries as wheels built with maturin.
|
|
794
|
+
**`dist-workspace.toml`**: the release builds, installers and Homebrew formula, made by cargo-dist.
|
|
795
|
+
**`packaging/`**: the app's Homebrew cask, and the winget manifests for the app and the CLI.
|
|
796
|
+
- **`desktop/`**: the desktop app: Tauri, with the CLI inside it ([its README](desktop/README.md)).
|
|
797
|
+
- **`site/`**: the landing page, one self-contained file, served at [hatcheck.dev](https://hatcheck.dev)
|
|
798
|
+
from Cloudflare as static assets; `site/deploy.sh` ships it. The logo lives there too:
|
|
799
|
+
`logo.svg` and `logo-dark.svg` (the mark), `logo-wordmark.svg` and `logo-wordmark-dark.svg`, `favicon.svg`.
|
|
800
|
+
- **`docs/proposals/`**: the plans these came from: [the CLI features](docs/proposals/cli-features.md)
|
|
801
|
+
and [the desktop app](docs/proposals/desktop-app.md).
|
|
802
|
+
- **`tests/`**: the command tests, which run the binary against throwaway home folders:
|
|
803
|
+
`python3 -m unittest discover -s tests`, which builds it with cargo first, and the crate's own
|
|
804
|
+
tests with `cargo test -p hatcheck`. They never read your real profiles, Keychain or the network.
|
|
805
|
+
GitHub Actions runs both on macOS, Linux and Windows (`.github/workflows/tests.yml`).
|
|
806
|
+
|
|
807
|
+
## Roadmap
|
|
808
|
+
|
|
809
|
+
- **Claude Desktop instances on Windows.** Claude Desktop is an Electron app there too, so a
|
|
810
|
+
separate `--user-data-dir` per account should work. A Windows version would create a shortcut per
|
|
811
|
+
instance with its own icon, and a launcher that brings a running instance forward instead of
|
|
812
|
+
opening a duplicate. It needs testing on a Windows machine first.
|
|
813
|
+
- **Linux.** There's no official Claude Desktop for Linux, so only the CLI tools apply there.
|
|
814
|
+
- **Releases:** the CLI's 0.1.0 is on PyPI, as Python; the next release ships the Rust binaries to
|
|
815
|
+
PyPI, GitHub, the Homebrew tap and winget, then the app's installers, once the release workflows
|
|
816
|
+
have their accounts and secrets (the workflows list them).
|
|
817
|
+
- **Signing:** a Developer ID for the macOS app, and a certificate for the Windows one.
|
|
818
|
+
|
|
819
|
+
## Credits
|
|
820
|
+
|
|
821
|
+
- **Claude Desktop instances:** this repo started as a fork of
|
|
822
|
+
[Aaron Oxborrow](https://github.com/aoxborrow)'s
|
|
823
|
+
[gist](https://gist.github.com/aoxborrow/5a3a0ac0aa37819cc19439b70a552221), whose scripts made
|
|
824
|
+
colour-coded launchers for isolated instances. On 16 October 2026 his scripts and his sections of
|
|
825
|
+
this README were replaced by a rewrite, `hatcheck desktop`, so none of his code or text remains.
|
|
826
|
+
The idea both implement, running Claude Desktop with `--user-data-dir`, is from
|
|
827
|
+
[Philipp Stracker's gist](https://gist.github.com/stracker-phil/9f84927a556632c7f9cc06663b534f14)
|
|
828
|
+
([write-up](https://philippstracker.com/multiple-claude-instances/)), itself a cleaned-up version
|
|
829
|
+
of [weidwonder/claude-desktop-multi-instance](https://github.com/weidwonder/claude-desktop-multi-instance).
|
|
830
|
+
- **Claude Code and Codex CLI tools:** `hatcheck`, `cl`, `cx`, the installers and the tests are by
|
|
831
|
+
[Yi Wang](https://github.com/yiwang).
|
|
832
|
+
- **Z.ai and Kimi:** the provider settings in `crates/hatcheck/src/provider.rs` (endpoints, model slots, context
|
|
833
|
+
windows and the config files) come from [claude-z](https://github.com/sam-fakhreddine/claude-z) by
|
|
834
|
+
Kerim ([lettt325](https://github.com/lettt325)), MIT-licensed, and Yi Wang's
|
|
835
|
+
[fork](https://github.com/yiwang/claude-z) of it, which added Kimi.
|
|
836
|
+
|
|
837
|
+
## License
|
|
838
|
+
|
|
839
|
+
MIT, see [LICENSE](LICENSE). It covers everything in this repo: the CLI, the desktop app, the site
|
|
840
|
+
and the docs.
|
|
841
|
+
|