Wrap the Claude Code CLI in tmux so long agentic tasks survive rate-limit hits, SSH disconnects, and overnight runs — without you babysitting the reset clock.
English | 中文 | Français | Русский
git clone https://github.com/zhihuiyuze/vibe-coding-auto-resume.git ~/dev/claude-auto-continue
cd ~/dev/claude-auto-continue
./install.sh
sudo apt install tmux # only if missing — installer tells you
source ~/.bashrcThe installer is idempotent. It symlinks vibe-run, vibe-status, vibe-session-capture into ~/.local/bin/, drops a vibe shell function into ~/.bashrc, and appends a tmux config snippet. It never touches ~/.claude/ and never uses sudo on your behalf.
cd ~/dev/<your-project>
vibe work # cd here + open a named tmux session
vibe run # this replaces `claude` — same flags, same UINow use Claude normally. When the 5-hour block is exhausted, vibe run notices, sleeps until the reset, and re-launches the same session UUID automatically. If Anthropic's new interactive modal appears (What do you want to do? 1. Stop and wait …), the wrapper picks the safe "Stop and wait" option for you.
To leave the session running and come back later: Ctrl+b d. To get back in: vibe work.
If you remember the session UUID (from ~/.claude/projects/, or copied out of a previous run's log):
cd ~/dev/<your-project>
vibe work
vibe run --resume <session-uuid>If you don't remember it but it's the most recent session in that project:
vibe work
vibe run --mode continue # same as `claude --continue`, with auto-resume on rate-limit hitTo browse what's there with timestamps and a snippet of each session's last user message:
vibe history # 10 most recent for the current cwd
vibe history --limit 0 # all of them
vibe history --json # for piping into jq / scriptsOutput looks like:
2026-05-25 12:34:01 43 msgs e482a7e9-6685-4fd2-bafa-3b86c8adaf21 fix the modal handler when claude exits
2026-05-24 18:22:11 127 msgs 45abc163-1883-4c2f-ab21-b97a36bb0332 refactor the L3 provider abstraction
Pick the UUID you want and vibe run --resume <uuid>.
The tmux session keeps running even after SSH dies — your claude process is owned by tmux, not by your shell. Step-by-step recovery:
ssh you@server # 1. reconnect
tmux ls # 2. is your vibe-* session still alive?
# expected: e.g. "vibe-default: 1 windows (...)"
vibe work # 3. re-attach (or `vibe work <name>` if you used a name)
# you land exactly where you left offOnce re-attached, scroll up to see what happened during your absence: Ctrl+b [, then PageUp / arrows, q to exit scrollback. If a rate limit hit while you were gone, the wrapper already handled it — you'll see the [vibe-run] Sleeping … until … and resume entries.
Peek without attaching (e.g. from another machine, just checking status):
ssh you@server "tmux ls" # what's alive
ssh you@server "tmux capture-pane -t vibe-default -p | tail -50" # last 50 pane lines
ssh you@server "vibe status" # current block usageIf tmux ls says no server running — the host rebooted, or tmux was OOM-killed. The tmux session is gone, but Claude's JSONL history isn't. Use Scenario 2 above (vibe run --resume <uuid> or vibe run --mode continue) to pick up where you left off.
vibe ls shows every vibe-* tmux session with its current cwd, attached state, and a ← here mark when the cwd matches yours:
$ vibe ls
vibe-boldfox /home/u/dev/projectA ← here [attached]
vibe-feature-x /home/u/dev/projectA ← here
vibe-quietowl /home/u/dev/scratch
vibe work without arguments now uses this discovery before defaulting to the cwd-hash name:
- 0 matches → creates a new session (cwd-hash name).
- 1 match → attaches directly, no prompt.
- N matches → interactive picker (
1..Nto pick,nfor a new one).
Explicit vibe work <name> skips discovery — the name always wins.
Naming tip: for projects you'll touch repeatedly, give the session an explicit name (vibe work projectA) instead of letting the cwd hash pick. It survives path changes (renames, symlinks) and is recognizable in vibe ls output.
L1 (JSONL parsing) and L2 (tmux pane regex) cover the common rate-limit shapes with zero external calls. To handle TUI wording changes and edge cases — and to extract reset times the regex misses — opt into L3:
echo 'DEEPSEEK_API_KEY=sk-...' >> ~/.config/vibe/env # chmod 600, created by installer
chmod 600 ~/.config/vibe/env
source ~/.bashrcSupported providers: DeepSeek (cheapest, ~$0.05/block), Anthropic Claude Haiku, OpenAI gpt-4o-mini, Ollama (local, [untested] — needs GPU validation).
Privacy: with L3 on, the last ~30 pane lines (conversation tail + visible file previews) are sent to your chosen provider for a single classification call per limit event. Basic secret redaction (sk-*, Bearer *, *_SECRET=*, long base64) is on by default but is not a guarantee. Decline the opt-in (or vibe run --no-l3) to stay fully local.
When claude exits, vibe run runs three checks in order:
- L1 sums
message.usage.{input,output,cache_read}_tokensacross the current project's JSONL files to know how much of the 5-hour block is burned and when it resets. - L2 runs
tmux capture-paneand greps the tail for verbatim TUI strings (5-hour limit reached ∙ resets …,weekly limit reached,Approaching 5-hour limit, plus the new interactive modal "Stop and wait for limit to reset"). It extracts the reset clock-time. - L3 (opt-in) sends the same tail to an LLM and gets back
{status, reset_time, idle, modal_open}for the cases L2 can't parse.
When claude exits cleanly or with a real error (crash, MCP failure, /exit), the wrapper exits with the same code — it does not blindly retry. Auto-resume fires only when a rate-limit signal is positively detected. See docs/architecture.md and docs/design/001-three-layer-detection.md for the full rationale.
vibe run [...args]
--resume <uuid> resume a specific session (used for all cycles)
--threshold <0..1> opt-in soft cap (default off — burn the block)
--max-cycles <n> resume cycles per invocation (0 = unlimited, default 1)
--mode auto|session-id|continue
--provider deepseek|claude|openai|ollama
--no-l3 force L1+L2 only
--dangerously-skip-permissions
-p "prompt"
... any other flag passes straight to claude
| Variable | Default | Purpose |
|---|---|---|
CC_LLM_PROVIDER |
auto-detect | deepseek / claude / openai / ollama / none |
CC_USAGE_THRESHOLD |
unset (off) | Opt-in soft cap (e.g. 0.80) to reserve interactive budget |
CC_RESUME_MODE |
auto |
auto / session-id (strict UUID) / continue |
CC_RESUME_MAX_CYCLES |
1 |
0 = unlimited resume cycles per invocation |
CC_SLEEP_PAD |
60 |
Seconds added to reset time before re-launching |
Less-common knobs (CC_LLM_REDACT, CC_PANE_TAIL_LINES, CC_MODAL_POLL_INTERVAL, …) are documented at the top of bin/vibe-run.
vibe work <name> creates an isolated tmux session + state dir. Use it to run multiple Claude tasks side by side:
vibe work feature-a # tmux session "vibe-feature-a"
# Ctrl+b d, then in another shell:
vibe work bugfix # tmux session "vibe-bugfix", separate session UUID cachevibe work without a name uses a deterministic-random name derived from the cwd hash, so coming back to the same project always lands on the same session.
Read AGENTS.md first — it's the single entry point for contributors. This project uses a spec-first workflow: every feature begins as a design doc in docs/design/00X-<name>.md (template) reviewed before code is written.
If Claude Code ever shows a rate-limit or modal message we don't match, paste it verbatim into tests/fixtures/<name>.txt and open a PR.
MIT.