Skip to content

feat: restore claude code sessions via --resume <session-id>#571

Open
manueltarouca wants to merge 2 commits into
tmux-plugins:masterfrom
manueltarouca:feature/restore-claude-code-sessions
Open

feat: restore claude code sessions via --resume <session-id>#571
manueltarouca wants to merge 2 commits into
tmux-plugins:masterfrom
manueltarouca:feature/restore-claude-code-sessions

Conversation

@manueltarouca

@manueltarouca manueltarouca commented May 29, 2026

Copy link
Copy Markdown

What

claude re-launch starts a fresh conversation. Resuming requires claude --resume <session-id>. Adds two paths to make tmux-resurrect resurrect claude panes correctly:

  1. Basic mode (zero claude-side install): a restore strategy that picks the most recent session for the pane's saved cwd from ~/.claude/projects/ and emits the matching --resume command.
  2. Per-pane mode (recommended for multiple parallel sessions in the same cwd): a SessionStart hook plus claude-aware save strategy that capture session identity per pane at save time.

How

Basic mode. Claude Code stores sessions at ~/.claude/projects/<encoded-cwd>/<uuid>.jsonl, where the encoded cwd is the absolute path with / replaced by -. strategies/claude_default.sh reproduces that encoding, picks the newest *.jsonl by mtime, strips the extension to recover the UUID, and emits claude --resume <uuid>. Falls back to the originally captured command when no session exists.

Per-pane mode. Basic mode collapses parallel same-cwd sessions to one. To map each pane to its own session:

  • extras/claude/track-session.sh: a SessionStart hook installed in ~/.claude/settings.json. At session start it reads session_id and cwd from the hook payload and writes ~/.claude/runtime/<claude-pid>.json. Garbage-collects sidecars older than 7 days. Requires python3.
  • save_command_strategies/claude_aware.sh: drop-in replacement for the default ps save strategy. For non-claude commands it behaves identically to ps.sh. For claude panes it looks up the sidecar by claude PID and rewrites the captured command to claude --resume <uuid>, so the resurrect record stores explicit session identity per pane.

strategies/claude_default.sh detects an existing --resume/--continue in the captured command and passes it through unchanged, so per-pane mode survives restore.

Configuration

Opt-in. Not added to the default process list.

Basic mode:

set -g @resurrect-processes 'claude'
set -g @resurrect-strategy-claude 'default'

Per-pane mode (in addition):

set -g @resurrect-save-command-strategy 'claude_aware'

Plus the SessionStart hook in ~/.claude/settings.json pointing at extras/claude/track-session.sh. Setup walked through in docs/restoring_claude_code_sessions.md.

Caveats

  • Per-pane mode requires the hook to have fired. Sessions started before installation fall back to basic mode.
  • The hook keys the sidecar by $PPID. Wrapper scripts between claude and the hook can throw this off.
  • The / to - encoding tracks Claude Code's current on-disk layout. A future Claude Code release that changes the scheme breaks basic mode (per-pane mode is unaffected).

Upstream

The right long-term fix is for Claude Code to expose the running session ID externally (process env or runtime state file). Filed at anthropics/claude-code#63758. Once that lands, the hook becomes unnecessary.

Testing

  • claude running in a pane, save (prefix + Ctrl-s), kill tmux server, restart, restore (prefix + Ctrl-r): pane comes back attached to the prior session (basic mode, verified).
  • Fresh project dir with no ~/.claude/projects/<encoded>/ entry: strategy emits bare claude.
  • Original command claude --dangerously-skip-permissions with no prior session: flags preserved on fallback.
  • Restore strategy passthrough for explicit --resume <id>, --continue, and combined-with-flags forms: verified.
  • Per-pane mode end-to-end (hook fires, sidecar written, save strategy reads): logic verified standalone. Full reboot cycle not yet exercised.

`claude` re-launch starts a fresh conversation. Resuming requires
`claude --resume <session-id>`. Adds a restore strategy that picks the
most recent session for the pane's saved cwd under
`~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` (encoded cwd is the
absolute path with `/` replaced by `-`), and emits the matching
`--resume` command. Falls back to the originally captured command when
no session exists for the cwd.

Opt-in. Not added to the default process list.

    set -g @resurrect-processes 'claude'
    set -g @resurrect-strategy-claude 'default'
Basic mode picks "newest .jsonl in cwd by mtime" at restore time, which
collapses parallel sessions to one when multiple panes ran `claude` in
the same directory.

Adds two pieces that together capture per-pane session identity at save
time:

- extras/claude/track-session.sh: SessionStart hook that records
  {sessionId, cwd, ppid} to ~/.claude/runtime/<pid>.json at session
  start. Garbage-collects sidecars older than 7 days.
- save_command_strategies/claude_aware.sh: drop-in replacement for the
  default `ps` save strategy. Behaves like ps.sh for every command,
  diverges only for `claude` panes: looks up the sidecar by claude PID
  and rewrites the captured command to `claude --resume <uuid>`.

strategies/claude_default.sh now detects an explicit --resume/--continue
in the captured command and passes it through unchanged, so the
per-pane path is honored at restore. Falls back to newest-mtime when no
explicit flag was captured.

Enable per-pane mode by installing the hook in ~/.claude/settings.json
and:

    set -g @resurrect-save-command-strategy 'claude_aware'

Long-term, the right fix lives upstream in Claude Code (expose session
ID via process env or runtime file). Tracked at
anthropics/claude-code#63758.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant