cckeep logo

Your session is still running.
Your phone doesn't know.

Claude Code Remote Control retries for about 31 seconds and then gives up for good. cckeep notices, and re-arms the session — without ever typing into one that's busy.

$npm i -g cckeep && cckeep enable

Zero dependencies · zero cloud · zero telemetry — it reads your tmux panes and nothing leaves the machine.

cckeep detecting a dead Remote Control link and re-arming it
Remote Control's entire reconnect budget
5 attempts · 1 / 2 / 4 / 8 / 16 s31 s, then closed for good

Thirty-one seconds is the whole budget.

Every one of these outlasts it. The link closes, and the documented recovery is to walk back to your desk and type /remote-control.

💤The lid

You close the laptop to move rooms. It reconnects for 31 seconds, then stops trying.

📶The handoff

Wi-Fi to LTE, office to café, VPN reconnecting. Longer than half a minute, and it's over.

🛗The elevator

Two floors of no signal costs you the session you were about to check on.

🪢The wedge

Sometimes it just sits in “reconnecting” forever — issue #34255, open since March, 99 👍, no fix.

It will not type into a session that's working.

A watchdog that types into your terminal on a timer is a liability unless it's certain the moment is safe. Every rule below is enforced and tested.

⏸️
Never during a turn

The pane is captured twice, two seconds apart. A running turn animates a spinner and a token counter — identical captures mean nothing is happening, different ones mean hands off.

🚧
Never into a dialog

Permission prompts turn Enter into a selection. If a selection marker is on screen, the pass is skipped entirely.

📱
Never into your panel

/remote-control opens a status panel with a QR code. cckeep presses Enter there only when it opened the panel itself.

🔕
Never a session you turned off

Only panes seen connected at least once are ever chased. Disable Remote Control deliberately and it stays off.

🧊
Never in a tight loop

One action per pane per five minutes, so a genuinely broken link can't turn into a stream of keystrokes.

🔁
Re-checked at the last moment

The decision is made from one capture, then re-verified after the wait. Reconnected in between? Dialog appeared? Nothing is sent.

--dry-run prints exactly what it would do and sends nothing. Run it for a day before you trust it.

What it watches for.

State on screenWhat it meansWhat cckeep does
/rc activeconnectedremembers the pane, nothing else
/rc reconnectinginside the 31-second budgetwaits — this usually resolves on its own
/rc reconnecting · 2 minwedged (#34255)cycles the bridge: opens the panel, disconnects, reconnects
Remote Control disconnectedgave upre-arms immediately
no indicator, pane had one beforenotification scrolled awayre-arms after 4 quiet checks
no indicator, pane never had onenot your setupnothing, ever

One command away.

npm i -g cckeep       # install globally — an npx path would vanish
cckeep enable        # launchd on macOS, systemd user timer on Linux
cckeep               # what it sees right now — changes nothing
cckeep watch         # run in the foreground instead of scheduling
cckeep doctor        # tmux, panes, scheduler, paths
cckeep logs          # what it has done
cckeep disable       # remove the background job
One requirement: Claude Code has to run inside tmux.

A session started in a bare terminal can't be reached from another process — there's no channel to type into, so no tool can help it. Restarting the process isn't an alternative: that ends the conversation, which is the thing you were trying to save.

The README has a drop-in shell function that wraps interactive launches only, so claude update and claude -p keep behaving normally.

The best outcome is that this becomes unnecessary.

cckeep re-arms a connection. It can't raise Claude Code's retry budget — that's a constant inside a closed-source binary, and only Anthropic can change it. If #34255 gets fixed, the right move is to archive this repo. Until then, a 👍 over there is worth more than a ⭐ here.

Star history

Star history chart