DEV Community

Cover image for Snapshot your terminal state, restore it after a crash — Claude Code sessions included
Daksh Gargas
Daksh Gargas

Posted on

Snapshot your terminal state, restore it after a crash — Claude Code sessions included

The problem

Last time I built claude-sessions: one picker to resume any Claude Code session across projects. But a machine crash takes more than Claude with it — it takes your whole terminal layout. Which tabs were open, which directory each one was in, which ones had a Claude session running.

macOS window restoration exists, but it fails this job twice:

  • It restores windows, not processes — your claude sessions don't come back.
  • It can't tell a crash from a quit — close your terminal on purpose for a fresh start, and it cheerfully brings back all the old tabs anyway.

The solution

A snapshot daemon plus an explicit restore — no auto-restore, ever.

Every 5 minutes, a launchd job walks every iTerm window and tab via AppleScript, and for each tab resolves the tty → the processes on it (ps) → the working directory (lsof -d cwd) → and whether a claude process is running there, including which session: from its --resume argument, from the transcript file it holds open, or from the newest transcript for that directory. The result is a small JSON file.

Snapshots accumulate as history — the last 100 distinct states, consecutive duplicates skipped, and an empty terminal never overwrites anything.

After a crash:

claude-sessions --restore-crash   # newest snapshot
claude-sessions --restore-pick    # or: fzf through history, pick the right one
Enter fullscreen mode Exit fullscreen mode

Every tab comes back — as tabs of the window you're standing in — cd'd to its old directory, with claude --resume <session-id> rerun wherever Claude was live. Intentional quit? Just don't run it. You are the crash detector; that's the feature.

Three gotchas that cost me an evening

  1. launchd can't read iCloud paths. If your script lives in iCloud Drive (mine syncs via ~/.claude/bin → iCloud), the timer dies with Operation not permitted — macOS TCC silently denies background jobs access to ~/Library/Mobile Documents. Point the plist at a local copy.
  2. A single snapshot file is a footgun. After a crash you reopen a near-empty terminal, the timer fires, and your pre-crash snapshot is gone — replaced by three sad tabs. That's why history, not one file.
  3. Don't restore the original window grouping. My first version faithfully recreated every window — including a junk window the snapshot had captured. Flattening everything into tabs of the current window is what you actually want.

Script, launchd template, and docs:

GitHub logo DenverLifeSciences / claude-sessions

Cross-project session picker for Claude Code CLI — fzf over all your sessions, Enter resumes each in a new tab

claude-sessions

A cross-project session picker for Claude Code CLI.

Your machine crashes with seven Claude Code sessions open across five repos. claude --resume only lists sessions for the directory you run it from — so recovery means remembering every repo you were in, cd-ing into each one, and picking from a list, seven times.

claude-sessions instead lists every session from every project in one fzf picker, newest first. Hit Enter and the session opens in a new iTerm tab (or tmux window) running claude --resume <id> in the right directory — while the picker stays open for the next one.

enter   open session in a new tab (picker stays open)
ctrl-a  hide/show agent-teammate sessions (⛭)
ctrl-r  refresh the list
esc     quit

Each line shows the session's age, project directory, git branch (when not main/master), and its opening message. A preview pane shows the last few user/assistant exchanges of the…

Top comments (0)