Automatically resume Claude Code sessions after tmux-resurrect restore.
Step 1. Install the plugin (TPM):
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'MadAppGang/tmux-claude-continuity'Press prefix + I to install.
Step 2. Register the hooks in ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_session_start.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_stop.sh"
}
]
}
]
}
}Done. Next time tmux-resurrect restores your sessions, Claude Code resumes where you left off.
You run Claude Code in a tmux pane. You save your tmux session with tmux-resurrect, then restore it — maybe after a reboot or a tmux kill-server. tmux-resurrect recreates your pane layout and reruns the shell command. But Claude Code starts a brand-new session, not the one you were in. Your conversation context is gone.
You can recover manually with claude --resume <uuid>, but first you have to find the right UUID — and if you had Claude running in several panes, you have to match each UUID to the correct pane.
tmux-claude-continuity does that bookkeeping for you.
Every time Claude Code starts a session (new, resumed, cleared, or compacted), its SessionStart hook writes the session UUID to a file named after the pane's position: ~/.config/tmux-claude/panes/<session>-<window>-<pane>.session-id.
If you named your session with /title, the custom title is stored on line 2 of the sidecar (for display purposes), but the UUID on line 1 is always used for --resume — ensuring reliable direct resume rather than fuzzy search. The Stop hook keeps both values updated after every turn.
After tmux-resurrect restores, a post-restore hook reads those files and sends claude --resume <token> to each pane that was running Claude.
Claude Code starts in pane work:1.0
└── SessionStart hook fires
└── writes ~/.config/tmux-claude/panes/work-1-0.session-id
(line 1: session UUID, line 2: custom title if set)
User types /title bugfix-sentry
└── Stop hook fires after the turn
└── updates work-1-0.session-id → line 1: UUID, line 2: "bugfix-sentry"
You restore tmux with tmux-resurrect
└── post_restore.sh fires after all panes are recreated
├── reads the resurrect save file
├── finds panes that were running claude
└── sends claude --resume <uuid> to each one
Each pane is keyed by session_name-window_index-pane_index (e.g. work-1-0). tmux-resurrect recreates panes at exactly these positions, so the key written before a save matches the pane address after restore. The ephemeral %N numeric pane ID — which changes every session — is never used.
- tmux-resurrect
- jq
- Claude Code >= 2.0
Optional: tmux-continuum for automatic periodic saves.
Add to ~/.tmux.conf in this order — tmux-resurrect must load before this plugin:
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum' # optional
set -g @plugin 'MadAppGang/tmux-claude-continuity'Press prefix + I to install.
git clone https://github.com/MadAppGang/tmux-claude-continuity \
~/.tmux/plugins/tmux-claude-continuityAdd to ~/.tmux.conf after the tmux-resurrect line:
run-shell ~/.tmux/plugins/tmux-claude-continuity/tmux-claude-continuity.tmuxThe hooks go in ~/.claude/settings.json, not ~/.tmux.conf. This is the step most users miss.
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_session_start.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_stop.sh"
}
]
}
]
}
}SessionStart captures the session on startup. Stop keeps the resume token updated after every turn — required for /title changes to take effect without restarting.
If you already have a hooks key in settings.json, add SessionStart and Stop alongside your existing hooks.
# Where per-pane session ID files are stored
# Default: ~/.config/tmux-claude/panes
set -g @claude-continuity-panes-dir "$HOME/.config/tmux-claude/panes"
# Seconds to wait after restore before sending keys
# Increase if your shell init is slow
# Default: 1
set -g @claude-continuity-restore-delay "1"
# Command used to launch Claude Code
# Use a shell alias if it already includes your preferred flags
# Default: claude
set -g @claude-continuity-claude-cmd "claude"Check whether the sidecar files exist:
ls ~/.config/tmux-claude/panes/
# Expected: work-1-0.session-id work-2-0.session-id ...Files appear after Claude Code's first API response in each pane (that is when SessionStart fires). An empty directory means the hook is not running. Verify ~/.claude/settings.json contains the SessionStart entry shown above, then restart Claude Code.
This happens when no sidecar file exists for a pane — for example, a pane that never ran Claude before or a new pane added after the last save. The plugin falls back to claude <flags>, starting a fresh session.
The sidecar file updates on every SessionStart event (startup, resume, clear, compact), so it always reflects the most recent session in that pane. If you see a wrong session resumed, the save was taken before a session switch; save again with prefix + C-s to capture the current state.
If Claude starts before your shell finishes sourcing rc files, increase the delay:
set -g @claude-continuity-restore-delay "3"MIT — MadAppGang