Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ No configuration is required. You should feel like you never quit tmux.

It even (optionally)
[restores vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)!
It also restores [Claude Code](https://claude.ai/claude-code) and
[Codex CLI](https://github.com/openai/codex) sessions automatically.

Automatic restoring and continuous saving of tmux env is also possible with
[tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) plugin.
Expand Down Expand Up @@ -92,7 +94,7 @@ You should now be able to use the plugin.
- [Changing the default key bindings](docs/custom_key_bindings.md).
- [Setting up hooks on save & restore](docs/hooks.md).
- Only a conservative list of programs is restored by default:<br/>
`vi vim nvim emacs man less more tail top htop irssi weechat mutt`.<br/>
`vi vim nvim emacs man less more tail top htop irssi weechat mutt claude codex`.<br/>
[Restoring programs doc](docs/restoring_programs.md) explains how to restore
additional programs.
- [Change a directory](docs/save_dir.md) where `tmux-resurrect` saves tmux
Expand Down
2 changes: 2 additions & 0 deletions resurrect.tmux
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ set_restore_bindings() {
set_default_strategies() {
tmux set-option -gq "${restore_process_strategy_option}irb" "default_strategy"
tmux set-option -gq "${restore_process_strategy_option}mosh-client" "default_strategy"
tmux set-option -gq "${restore_process_strategy_option}claude" "session"
tmux set-option -gq "${restore_process_strategy_option}codex" "session"
}

set_script_path_options() {
Expand Down
2 changes: 1 addition & 1 deletion scripts/variables.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ restore_path_option="@resurrect-restore-script-path"

# default processes that are restored
default_proc_list_option="@resurrect-default-processes"
default_proc_list='vi vim view nvim emacs man less more tail top htop irssi weechat mutt'
default_proc_list='vi vim view nvim emacs man less more tail top htop irssi weechat mutt claude codex'

# User defined processes that are restored
# 'false' - nothing is restored
Expand Down
22 changes: 22 additions & 0 deletions strategies/claude_session.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

# "claude session strategy"
#
# Restores Claude Code CLI sessions using the --continue flag,
# which resumes the most recent conversation in the working directory.

ORIGINAL_COMMAND="$1"
DIRECTORY="$2"

original_command_contains_resume_flag() {
[[ "$ORIGINAL_COMMAND" =~ (--continue|--resume) ]]
}

main() {
if original_command_contains_resume_flag; then
echo "$ORIGINAL_COMMAND"
else
echo "$ORIGINAL_COMMAND --continue"
fi
}
main
22 changes: 22 additions & 0 deletions strategies/codex_session.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

# "codex session strategy"
#
# Restores OpenAI Codex CLI sessions using the `resume --last` subcommand,
# which resumes the most recent conversation in the working directory.

ORIGINAL_COMMAND="$1"
DIRECTORY="$2"

original_command_contains_resume() {
[[ "$ORIGINAL_COMMAND" =~ (^|[[:space:]])(resume|fork)([[:space:]]|$) ]]
}

main() {
if original_command_contains_resume; then
echo "$ORIGINAL_COMMAND"
else
echo "codex resume --last"
fi
}
main
128 changes: 128 additions & 0 deletions tests/test_claude_strategy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env bash

# Tests for Claude Code session restore support:
# - claude_session.sh strategy script behavior
# - claude in default_proc_list (variables.sh)
# - claude strategy registration (resurrect.tmux)

CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$CURRENT_DIR/.."
STRATEGY="$ROOT_DIR/strategies/claude_session.sh"
VARIABLES="$ROOT_DIR/scripts/variables.sh"
RESURRECT_TMUX="$ROOT_DIR/resurrect.tmux"

pass_count=0
fail_count=0

pass() {
local desc="$1"
echo " PASS: $desc"
((pass_count++))
}

fail() {
local desc="$1"
local detail="$2"
echo " FAIL: $desc"
echo " $detail"
((fail_count++))
}

assert_strategy_output() {
local desc="$1"
local input_command="$2"
local input_dir="$3"
local expected="$4"
local actual
actual="$($STRATEGY "$input_command" "$input_dir")"
if [ "$actual" = "$expected" ]; then
pass "$desc"
else
fail "$desc" "expected '$expected', got '$actual'"
fi
}

# ---------- Strategy script tests ----------

echo "Strategy: claude_session.sh"

assert_strategy_output \
"plain 'claude' appends --continue" \
"claude" "/some/dir" \
"claude --continue"

assert_strategy_output \
"'claude --continue' is returned as-is (no duplication)" \
"claude --continue" "/some/dir" \
"claude --continue"

assert_strategy_output \
"'claude --resume <id>' is preserved" \
"claude --resume abc123" "/some/dir" \
"claude --resume abc123"

assert_strategy_output \
"'claude --model opus' preserves flags and appends --continue" \
"claude --model opus" "/some/dir" \
"claude --model opus --continue"

assert_strategy_output \
"multiple flags are preserved with --continue appended" \
"claude --verbose --model sonnet" "/some/dir" \
"claude --verbose --model sonnet --continue"

assert_strategy_output \
"--continue with other flags is returned as-is" \
"claude --model opus --continue" "/some/dir" \
"claude --model opus --continue"

assert_strategy_output \
"--resume with other flags is preserved" \
"claude --resume abc123 --verbose --model opus" "/some/dir" \
"claude --resume abc123 --verbose --model opus"

assert_strategy_output \
"flags without resume/continue get --continue appended" \
"claude --debug --allowedTools Bash Edit" "/some/dir" \
"claude --debug --allowedTools Bash Edit --continue"

# ---------- Wiring tests ----------

echo ""
echo "Wiring: variables.sh"

if grep -q "'.*claude.*'" "$VARIABLES"; then
pass "claude is in default_proc_list"
else
fail "claude is in default_proc_list" "not found in $VARIABLES"
fi

echo ""
echo "Wiring: resurrect.tmux"

if grep -q 'claude.*"session"' "$RESURRECT_TMUX"; then
pass "claude strategy registered as 'session'"
else
fail "claude strategy registered as 'session'" "not found in $RESURRECT_TMUX"
fi

# ---------- Strategy file sanity ----------

echo ""
echo "Sanity: strategy file"

if [ -x "$STRATEGY" ]; then
pass "claude_session.sh is executable"
else
fail "claude_session.sh is executable" "file is not executable"
fi

# ---------- Summary ----------

echo ""
total=$((pass_count + fail_count))
echo "Results: $pass_count/$total passed, $fail_count failed"

if [ "$fail_count" -gt 0 ]; then
exit 1
fi
133 changes: 133 additions & 0 deletions tests/test_codex_strategy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env bash

# Tests for Codex CLI session restore support:
# - codex_session.sh strategy script behavior
# - codex in default_proc_list (variables.sh)
# - codex strategy registration (resurrect.tmux)

CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$CURRENT_DIR/.."
STRATEGY="$ROOT_DIR/strategies/codex_session.sh"
VARIABLES="$ROOT_DIR/scripts/variables.sh"
RESURRECT_TMUX="$ROOT_DIR/resurrect.tmux"

pass_count=0
fail_count=0

pass() {
local desc="$1"
echo " PASS: $desc"
((pass_count++))
}

fail() {
local desc="$1"
local detail="$2"
echo " FAIL: $desc"
echo " $detail"
((fail_count++))
}

assert_strategy_output() {
local desc="$1"
local input_command="$2"
local input_dir="$3"
local expected="$4"
local actual
actual="$($STRATEGY "$input_command" "$input_dir")"
if [ "$actual" = "$expected" ]; then
pass "$desc"
else
fail "$desc" "expected '$expected', got '$actual'"
fi
}

# ---------- Strategy script tests ----------

echo "Strategy: codex_session.sh"

assert_strategy_output \
"plain 'codex' restores with resume --last" \
"codex" "/some/dir" \
"codex resume --last"

assert_strategy_output \
"'codex' with a prompt restores with resume --last" \
"codex fix the bug" "/some/dir" \
"codex resume --last"

assert_strategy_output \
"'codex resume' is returned as-is" \
"codex resume" "/some/dir" \
"codex resume"

assert_strategy_output \
"'codex resume --last' is returned as-is" \
"codex resume --last" "/some/dir" \
"codex resume --last"

assert_strategy_output \
"'codex resume <id>' is preserved" \
"codex resume abc123" "/some/dir" \
"codex resume abc123"

assert_strategy_output \
"'codex fork <id>' is preserved" \
"codex fork abc123" "/some/dir" \
"codex fork abc123"

assert_strategy_output \
"'codex --model gpt-5' restores with resume --last" \
"codex --model gpt-5" "/some/dir" \
"codex resume --last"

assert_strategy_output \
"'codex --full-auto' restores with resume --last" \
"codex --full-auto fix everything" "/some/dir" \
"codex resume --last"

assert_strategy_output \
"'codex resume' with flags is preserved" \
"codex resume --last --all" "/some/dir" \
"codex resume --last --all"

# ---------- Wiring tests ----------

echo ""
echo "Wiring: variables.sh"

if grep -q "'.*codex.*'" "$VARIABLES"; then
pass "codex is in default_proc_list"
else
fail "codex is in default_proc_list" "not found in $VARIABLES"
fi

echo ""
echo "Wiring: resurrect.tmux"

if grep -q 'codex.*"session"' "$RESURRECT_TMUX"; then
pass "codex strategy registered as 'session'"
else
fail "codex strategy registered as 'session'" "not found in $RESURRECT_TMUX"
fi

# ---------- Strategy file sanity ----------

echo ""
echo "Sanity: strategy file"

if [ -x "$STRATEGY" ]; then
pass "codex_session.sh is executable"
else
fail "codex_session.sh is executable" "file is not executable"
fi

# ---------- Summary ----------

echo ""
total=$((pass_count + fail_count))
echo "Results: $pass_count/$total passed, $fail_count failed"

if [ "$fail_count" -gt 0 ]; then
exit 1
fi