Skip to content

Instantly share code, notes, and snippets.

@ds17f
Created February 20, 2026 06:20
Show Gist options
  • Select an option

  • Save ds17f/f4dd09c230122ac5e6875fce9cd1920a to your computer and use it in GitHub Desktop.

Select an option

Save ds17f/f4dd09c230122ac5e6875fce9cd1920a to your computer and use it in GitHub Desktop.
Schedule claude --resume sessions to run overnight in tmux panes. Sleep until a target time, then launch each session as an interactive Claude Code instance.
#!/usr/bin/env bash
set -euo pipefail
# claude-resume — Schedule claude --resume sessions to run overnight in tmux panes
usage() {
cat <<'EOF'
Usage: claude-resume <time> <sessions> [options]
Arguments:
<time> Target time to start (e.g., 2:00am, 14:00, now)
<sessions> Comma-separated session names/search terms
Options:
--prompt "…" Override default prompt (default: "execute the plan")
--model M Override model (e.g., sonnet)
--help Show this help
Sessions run as interactive claude instances in tmux panes. After they
finish processing the prompt, they stay open for you to interact with.
Requires tmux.
Examples:
claude-resume 2:00am fix-android-auto,fix-dupe-qr
claude-resume now test-session
claude-resume 2:00am s1,s2 --prompt "implement step 1"
claude-resume 2:00am s1,s2 --model sonnet
EOF
exit 0
}
die() { echo "error: $*" >&2; exit 1; }
# --- Parse arguments ---
[[ $# -lt 1 ]] && usage
[[ "$1" == "--help" || "$1" == "-h" ]] && usage
[[ $# -lt 2 ]] && die "missing <sessions> argument"
TARGET_TIME="$1"
SESSION_LIST="$2"
shift 2
PROMPT="execute the plan"
MODEL=""
while [[ $# -gt 0 ]]; do
case "$1" in
--prompt) [[ $# -lt 2 ]] && die "--prompt requires a value"; PROMPT="$2"; shift 2 ;;
--model) [[ $# -lt 2 ]] && die "--model requires a value"; MODEL="$2"; shift 2 ;;
--help|-h) usage ;;
*) die "unknown option: $1" ;;
esac
done
# --- Parse sessions ---
IFS=',' read -ra SESSIONS <<< "$SESSION_LIST"
[[ ${#SESSIONS[@]} -eq 0 ]] && die "no sessions specified"
# --- Require tmux ---
[[ -z "${TMUX:-}" ]] && die "not in a tmux session. Run this from inside tmux."
# --- Compute sleep duration ---
RUN_ID=$(date +%Y%m%d-%H%M%S)
LOG_DIR="$HOME/.claude/resume-logs/$RUN_ID"
mkdir -p "$LOG_DIR"
if [[ "$TARGET_TIME" == "now" ]]; then
SLEEP_SECONDS=0
WAKE_DISPLAY="now"
else
TARGET_EPOCH=$(date -d "$TARGET_TIME" +%s 2>/dev/null) \
|| die "cannot parse time: '$TARGET_TIME' (try 2:00am, 14:00, etc.)"
NOW_EPOCH=$(date +%s)
SLEEP_SECONDS=$(( TARGET_EPOCH - NOW_EPOCH ))
if [[ $SLEEP_SECONDS -lt 0 ]]; then
TARGET_EPOCH=$(date -d "tomorrow $TARGET_TIME" +%s 2>/dev/null) \
|| die "cannot parse time: 'tomorrow $TARGET_TIME'"
SLEEP_SECONDS=$(( TARGET_EPOCH - NOW_EPOCH ))
fi
WAKE_DISPLAY=$(date -d "@$TARGET_EPOCH" "+%-I:%M %p")
fi
format_duration() {
local secs=$1
local hours=$(( secs / 3600 ))
local mins=$(( (secs % 3600) / 60 ))
if [[ $hours -gt 0 ]]; then
echo "${hours}h ${mins}m"
else
echo "${mins}m"
fi
}
# --- Print confirmation ---
echo ""
if [[ "$TARGET_TIME" == "now" ]]; then
echo "claude-resume starting immediately"
else
echo "claude-resume scheduled for $WAKE_DISPLAY (in $(format_duration $SLEEP_SECONDS))"
fi
echo ""
echo "Sessions (${#SESSIONS[@]}):"
for i in "${!SESSIONS[@]}"; do
echo " $((i+1)). ${SESSIONS[$i]}"
done
echo ""
echo "Prompt: \"$PROMPT\""
[[ -n "$MODEL" ]] && echo "Model: $MODEL"
echo "Logs: $LOG_DIR/"
echo ""
# --- Sleep ---
if [[ $SLEEP_SECONDS -gt 0 ]]; then
echo "Press Ctrl+C to cancel."
echo "Sleeping..."
sleep "$SLEEP_SECONDS"
echo ""
echo "Waking up at $(date "+%-I:%M %p")..."
echo ""
fi
# --- Launch tmux panes ---
WINDOW_NAME="claude-resume"
tmux new-window -n "$WINDOW_NAME"
BASE_PANE=$(tmux display-message -p -t "$WINDOW_NAME" '#{pane_id}')
PANE_IDS=("$BASE_PANE")
for i in "${!SESSIONS[@]}"; do
[[ $i -eq 0 ]] && continue
NEW_PANE=$(tmux split-window -t "$WINDOW_NAME" -P -F '#{pane_id}')
PANE_IDS+=("$NEW_PANE")
tmux select-layout -t "$WINDOW_NAME" tiled
done
echo "Launching ${#SESSIONS[@]} session(s)..."
for i in "${!SESSIONS[@]}"; do
session="${SESSIONS[$i]}"
log_file="$LOG_DIR/${session}.log"
pane="${PANE_IDS[$i]}"
cmd="claude --resume '${session}' --dangerously-skip-permissions"
[[ -n "$MODEL" ]] && cmd+=" --model $MODEL"
cmd+=" '${PROMPT}'"
# Wrap in script(1) for logging
full_cmd="script -q -c \"$cmd\" '${log_file}'"
tmux send-keys -t "$pane" "$full_cmd" Enter
# Stagger API calls slightly
if [[ $i -lt $(( ${#SESSIONS[@]} - 1 )) ]]; then
sleep 2
fi
done
echo ""
echo "All sessions launched in tmux window '$WINDOW_NAME'."
echo ""
echo " tmux select-window -t $WINDOW_NAME"
echo ""
echo "Logs: $LOG_DIR/"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment