Each Ghostty tab remembers its Claude Code session. After a crash, every tab auto-resumes its exact previous conversation.
$ ccr restore-all
✓ 6E4B5E8D… → 04098887… /Users/me/onc9-systems
✓ 2E99AE11… → 2ba800e1… /Users/me/onc9-systems
✓ ABCB70F0… → e7c1b559… /Users/me/Projects/osis
...
ccr: dispatched 50 resume command(s)
If you run many parallel Claude Code sessions in Ghostty (one per task / branch / idea), a Ghostty crash leaves you with restored tabs but no memory of which session was in which tab. Title-based recovery doesn't work because Claude Code overwrites the title on every spinner tick. cwd-based recovery doesn't work when you have 20 sessions in the same project directory.
Ghostty assigns every terminal surface a stable UUID that survives window-save-state (verified in SurfaceView_AppKit.swift — the UUID round-trips through Codable/NSCoder). That UUID is queryable via Ghostty's AppleScript API.
ccr binds surface_uuid → claude_session_id in a small registry on disk. When Ghostty restores tabs, the UUIDs come back unchanged — ccr looks each one up and dispatches claude --resume <id> into the matching tab via AppleScript.
user runs `claude` in a Ghostty tab
│
▼
┌───────────── SessionStart hook ──────────────┐
│ AppleScripts Ghostty for the focused tab's │
│ surface UUID, writes binding: │
│ ~/.ccr/registry.json │
│ { "<surface_uuid>": { │
│ "session_id": "<uuid>", │
│ "cwd": "/path", │
│ "started_at": "..." }} │
└──────────────────────────────────────────────┘
│
── time passes, Ghostty crashes ──
│
▼
┌──────── window-save-state restore ───────────┐
│ tabs come back with the same surface │
│ UUIDs (Ghostty preserves them through │
│ NSCoder secure-coding) │
└──────────────────────────────────────────────┘
│
▼
┌───────────── ccr restore-all ────────────────┐
│ single AppleScript loops over registry, │
│ types `claude --resume <id>` into each │
│ live tab whose UUID matches │
└──────────────────────────────────────────────┘
| Operation | Trigger |
|---|---|
| Create | claude start fires SessionStart hook → writes binding |
| Read | ccr, ccr ls, ccr state, ccr pick, ccr restore-all |
| Update | ccr adopt (retroactively bind), or running claude again in same tab overwrites |
| Delete | Automatic on tab close (lazy: at every mutating ccr command). Plus ccr unbind (this tab) and ccr clear (stale JSONL bindings). Bindings persist when Claude exits but the tab stays open — type ccr to resume. |
git clone https://github.com/andresCamp/ccr.git
cd ccr
./install.shThe installer:
- Copies
bin/ccrto~/.ccr/bin/ccrand symlinks~/.local/bin/ccr - Copies
hooks/session-start.shto~/.ccr/hooks/ - Idempotently registers the SessionStart hook in
~/.claude/settings.json - Triggers Ghostty's Automation permission prompt (you'll need to grant it once)
Make sure ~/.local/bin is on your PATH.
ccr resume the session bound to this tab
ccr help list all commands
ccr state full registry + Ghostty live-state table
ccr ls compact list of bindings
ccr ls . bindings for current cwd only
ccr pick fzf picker over sessions in current cwd
ccr adopt bind current tab → most recent session in cwd
ccr restore-all bulk recovery: resume all bound sessions in their tabs
ccr <uuid|prefix> resume a specific session id (must be unambiguous)
ccr unbind remove this tab's binding
ccr clear remove bindings whose session JSONL has been deleted
ccr --version
- Ghostty crashes / you force-quit.
- Reopen Ghostty —
window-save-staterestores your tabs with the same surface UUIDs (cwds intact, shells fresh). - From any tab:
ccr restore-all. - Every tab whose UUID is in the registry receives
claude --resume <id>typed into it. They all spin up in parallel, in their original positions.
| OS | macOS (Ghostty's window-save-state and AppleScript are macOS-only currently) |
| Ghostty | 1.3.0+ (AppleScript object model added in 1.3) |
| Python | 3.10+ |
jq |
required (brew install jq) |
fzf |
optional, for ccr pick (brew install fzf) |
| Claude Code | any recent version |
- macOS only. Ghostty's window-save-state restoration is macOS-specific. Linux support depends on Ghostty's roadmap.
- AppleScript permission required. First use prompts. Cached after grant.
- tmux nested inside Ghostty. Auto-binding doesn't work because the surface's reported cwd is tmux's launch dir, not the pane's. Use
ccr adoptmanually. - Splits.
focused terminal of selected tab of front windowreturns the focused split — correct in current Ghostty. - Race conditions. Concurrent
claudeinvocations are serialized viaflockon~/.ccr/registry.lock, so bindings can't be lost.
./uninstall.shRemoves the binary symlink, unregisters the hook from settings.json, and (with confirmation) removes ~/.ccr/.
Claude Code Resume. Three letters, one keystroke per character, no shift.
MIT — see LICENSE.