23:58 · somewhere in your terminal

While you sleep,
the work continues.

When Claude Code, Codex, or any of your AI coding agents hits the 5-hour or weekly usage limit, the session just… stops. unsnooze tracks every limit-stopped session across all your projects and wakes each one — in tmux or Zellij — the moment the limit resets. Even if your laptop slept through it.

$ npm install -g unsnooze && unsnooze setup
v1.13MITNode ≥ 20tmux · ZellijmacOS · Linux · WSLzero telemetry
tmux · unsnooze

the problem

Every other tool solves a slice.

Overnight and long-running agent work dies at the 5-hour or weekly cap — one pane retried, one CLI covered, or your session abandoned for a different provider mid-thought. unsnooze guards all of it and always brings back the same session, same context, same conversation.

Capabilityunsnoozeclaude-auto-retryautoclaudehydra
Multi-CLI (Claude · Codex · Grok · Qwen · Kimi · OpenCode · Antigravity)partial
GUI sessions (VS Code extension, desktop apps)
Waits for reset & resumes the same session
All sessions at once (shared ledger + one daemon)
Revives sessions whose pane or process is gone
Survives laptop sleep & weekly-scale waitspartialpartial
Settings + first-run wizard

one night · how it works

Anatomy of a night shift

unsnooze is a scheduler that presses your keys — nothing more. It records the stop, waits out the reset, and resumes the same session. Here is one night, minute by minute.

The wall

Mid-refactor, the banner lands: “You've hit your usage limit · resets 3am.” The session stops. Without unsnooze, this is where the night ends.

The ledger

Claude's StopFailure hook — or a pane scrape for CLIs that fire no event — records the stop in ~/.unsnooze/state.json: agent, session id, working directory, pane, reset time. The reset is parsed from the banner text, DST-safe. Unparseable banners are never guessed at — unsnooze re-probes until a real time appears.

The wait

A singleton daemon checks wall-clock against the target epoch every 30 seconds — no long timers to break. A laptop that slept through the reset fires on the next tick. A weekly limit is just a bigger epoch.

The wake

Pane still alive? The resume message is typed in — only after proving the pane is yours and your CLI is foreground and idle. Pane gone? A fresh multiplexer pane runsclaude --resume <id>. Either way it's the same session, same context, same conversation.

The verification

After every wake, the pane is re-captured. If the limit banner came back — reset time misparsed, limit not actually lifted — unsnooze reschedules from the fresh banner, capped at five attempts. It never hammers.

The morning

You wake up; the work didn't stop. unsnooze status shows every session that hit a wall, when it woke, and what it's doing now.

who it watches

Seven CLIs, one ledger

Terminal sessions are watched through the shell wrapper and your multiplexer; GUI sessions through the files they already write. One shared ledger, one daemon, every project at once.

Claude Code stable

Dual-channel: the StopFailure hook (authoritative, carries the session id) plus pane scraping for banners and the interactive limit menu — always answered with “Stop and wait for limit to reset,” never a blind Enter. Dead sessions revive via claude --resume.

Codex CLI stable

Detects the exact banner strings from the Codex source and parses every reset format — “try again at 3:51 PM,” absolute dates, “in 4 days 20 hours.” Dead sessions revive via codex resume.

GUI surfaces daemon

Sessions in Claude Code's VS Code extension, the ChatGPT desktop app, and Claude desktop have no pane to scrape — so the daemon tails the session files those surfaces already write. Codex rollouts even carry the exact epoch reset time. At reset, the session revives in a multiplexer pane; it's the same session file, so the conversation stays visible in the GUI's own history.

experimentalOff by default — enable per agent in unsnooze setup. Hit a banner one missed? unsnooze report captures are how these get good.
grokClaude-compatible hooks (including StopFailure) work today; banner patterns stay generic until xAI documents the strings.
qwenHook plus verbatim quota scrapes. Qwen never prints a reset time — 5-hour fallback, self-corrected on verify.
kimiAnchors on the red 429 line kimi stops with. Resume ids verified on disk first — kimi silently starts a new session for unknown ids.
opencodeRetries limits itself, forever — so unsnooze never touches a live pane. Its job: reviving sessions whose process died mid-wait.
agyAntigravity, the Gemini-CLI successor. Parses “Refreshes in 6 days…” as the weekly cap; treats 503 capacity errors as transient, not a limit.

trust · before you install

It presses your keys.
Here's the contract.

A tool that types into your terminal at 3am has to earn that. unsnooze is a scheduler, not an auto-approver — it never changes how your agent handles permissions.

Types only after proving the pane is yours
Every keystroke requires identity (an ownership stamp or a process-id + birth-time lease — pane ids get recycled, so a mismatch vetoes) and liveness (your agent is still running there, foreground, not mid-stream). Ownership unprovable → it opens a fresh session instead of typing.
Never a blind Enter
Claude's limit menu is located and read before any key is sent; unsnooze computes the exact moves to “Stop and wait for limit to reset.” Unreadable menu → it presses nothing. It will never select “Upgrade your plan.”
No bypass flags, ever
No --dangerously-skip-permissions, no auto-trust, no auto-approve, no touching MCP config. Whatever your agent does after resuming is governed by its own permission model — the same as if you'd typed the message yourself.
See before you trust
unsnooze preview is a true dry-run: it prints exactly what would be typed, where, and why — or what's holding it back — and sends nothing. It shares its decision code with the real dispatcher, so it cannot drift from what dispatch actually does.
Nearly zero network, zero telemetry
One daily version check to the npm registry (nothing identifying; off with one setting), and push notifications only if you configure an ntfy topic. All state stays local under ~/.unsnooze.
Reversible install
The settings hook and rc-file wrappers are backed up first;unsnooze uninstall removes every change. Releases are published to npm with provenance.

Honest limits: unsnooze does inject keystrokes into your live terminal on your behalf, and it does not sandbox your agent or defend against prompt injection — that's your agent's job. The full threat model and residual risks live in SECURITY.md.

01:30 · if you're up anyway

A dashboard for the small hours

unsnooze dashboard is a full-screen terminal UI — status, usage forecast, sessions, install doctor, live logs, queued prompts, and your whole ssh fleet, with mouse support: click tabs and rows, wheel-scroll the logs. Pipes, CI, and --json stay plain. Try the tabs.

unsnooze dashboard
 unsnooze z z z                                          daemon: running

  claude  f3a1…  ~/work/payments      snoozed   wakes 3:00 am (in 2h 41m)   ctx ~152k tok
  claude  9d07…  ~/oss/unsnooze       resumed   woke 8:01 pm · verified
  codex   8c42…  ~/work/ingest        snoozed   wakes 3:00 am (in 2h 41m)
  qwen    1be3…  ~/exp/agents         paused    held by workspaceGuard · resume-now to review

  2 snoozed · 1 resumed · 1 paused — attach: tmux attach -t unsnooze-resumed

m toggles mouse mode · ? help overlay · q quits — works down to an 80×24 terminal

20:41 · before the wall

Know the wall before you hit it

Recovery is only half the job. unsnooze usage forecasts your burn rate and time-to-limit so you can /compact, pause, or switch models before a stop — see it live in the usage tab of the dashboard above, or --json it into your own tooling.

Every figure carries its provenance

(exact) from local data or the opt-in statusline shim, (calibrated from N stops) learned from your own recorded limit stops, or (estimated) while calibrating. Never a bare percentage.

ETA as a band, not a lie

Time-to-wall is cross-checked against known reset times and shown as a range — never a false-precision minute, never a blind now-plus-five-hours.

Account-wide, not per-pane

Burn sums all active sessions and subagents — the limit is per-account. Idle gaps over 5 minutes are excluded so a quiet hour doesn't hide a fast burn.

03:00 · the reset moment

Don't just resume — queue what's next

Resuming snoozed work is half the night. unsnooze prompt add pre-writes the next task per project: the moment the limit resets, a brand-new agent session opens in that directory and your prompt is typed as its first message. Manage the queue from the dashboard's prompts tab, or entirely from the CLI.

unsnooze prompt
$ unsnooze prompt add --project ~/work/payments "run the full test suite and fix any failures"
prompts: queued p-3f9a1c2e for claude in ~/work/payments — delivering after the reset

  03:00:07 queue    new claude window in ~/work/payments · typing queued prompt
  03:00:29 verify   pane healthy · no banner · delivered ▶ p-3f9a1c2e consumed

One-shot, and verified

Each prompt is delivered exactly once — a fresh window running the agent in that project, prompt typed only after the input box is provably idle. If the limit turns out to still be active, the entry re-queues using the banner's own reset time and the window is closed; after repeated failures it gives up loudly.

Anchored, never guessed

Due-ness comes from real signals — the statusline's exact resets_at or your recorded limit stops. No signal means no limit to wait out: it delivers on the next daemon tick and prompt add says so up front. --at 9pm / --at +2h30m schedule an exact time instead.

Fleet-wide

--host gpu-box queues on any registered machine over your own ssh; that host's daemon delivers under its own gates and reports back in unsnooze fleet. Hosts opt out with remoteQueue off.

04:15 · the details that hold

Guards for everything that can go wrong

Every guard is a config key — set once in unsnooze setup, change any time with unsnooze config set, override per environment with UNSNOOZE_* vars.

Pre-wall warnings usageWarndefault: notify

The daemon warns at 80% and 95% of the window's quota, and at 30 and 10 minutes to the wall at your current pace — deduped once per window. It may suggest /compact so the eventual wake is cheap; it never auto-types it.

Cold-cache wake guard contextGuarddefault: inform

Waking after hours means the provider's prompt cache is long gone — the first message re-reads the entire context at full price. unsnooze estimates the size (ctx ~152k tok) and notifies you, or holds sessions above a threshold for a manual decision.

Stale-workspace guard workspaceGuarddefault: inform

The repo's HEAD and dirty state are fingerprinted at stop time. If anything changed before the wake, the resumed agent is told to re-read before acting — or the session is held and resume-now shows the diff first.

Notifications, your way notifyChanneldefault: auto

OS toasts (macOS, Linux, native Windows toasts from WSL), terminal OSC banners in iTerm2 / kitty / WezTerm / Ghostty, a plain BEL, or ntfy push to your phone — for limit hit, resumed, and gave-up.

Menu answering, on your terms menuAutoAnswerdefault: true

Claude's limit menu is driven only when it can be read exactly; turn it off and unsnooze is watch-only. autoResume off goes further: track everything, type nothing.

Tidy panes, eventually reapResumeddefault: false

unsnooze reap closes finished panes and empty sessions — dry-run by default, auto-reap strictly opt-in after seven idle days.

the toolbox

You mostly just run claude

Day to day nothing changes — the shell wrapper watches claude,codex and friends automatically. The rest of the CLI is for looking around.

unsnooze statustracked sessions, reset countdowns, context sizes — or a live dashboard on a TTYunsnooze dashboardfull-screen TUI: status, usage, sessions, doctor, logs, fleet, prompts — with mouse supportunsnooze usageaccount burn & time-to-limit forecast (--json for scripts)unsnooze previewdry-run: what would happen right now, and why — nothing is typedunsnooze resume-nowdon't wait for the reset time (--all for everything)unsnooze cancelstop tracking a sessionunsnooze message <id>per-session wake message (--clear to reset)unsnooze prompt add "…"queue a one-shot prompt: at the limit reset a new session opens in the project and types it (--at, --now, --host)unsnooze sessionslist unsnooze-owned multiplexer sessions and panesunsnooze hosts add <name>register another machine over ssh — key or password auth (hosts test pre-flights it)unsnooze fleetevery registered host's sessions in one view; resume/cancel remotely from the dashboard's fleet tabunsnooze reapclose finished panes and empty sessions — dry-run by defaultunsnooze doctorinstall health check, with --fixunsnooze logs -fwhat unsnooze has been doing, liveunsnooze reportcapture a pane to report an undetected banner — how experimental adapters get goodunsnooze updateupdate unsnooze itself (a daily registry check tells you when)unsnooze uninstallremove every change it made (--purge for state too)

asked at 4am

Questions people actually ask

Does this get around the rate limit?

No. unsnooze waits for the reset exactly like you would, resumes once, and verifies the limit actually lifted. It replaces the 4am alarm, not the limit.

What if my laptop was asleep or the terminal was closed?

Reset times are stored as absolute timestamps and checked every 30 seconds, so a laptop that slept through the reset resumes on the next tick — and dead panes are reopened by session id in a fresh multiplexer pane.

Why did resuming a big session eat so much quota?

Prompt-cache expiry, not unsnooze. After hours stopped at a limit the provider's cache is long gone, so the first wake message — unsnooze's or a hand-typed “continue,” identical cost — re-reads the entire context at full price. The contextGuard setting estimates the size before waking and can notify you or hold the session for a manual decision. /compact before the wall is what actually helps.

What if the repo changed while a session slept?

unsnooze fingerprints the workspace (HEAD plus uncommitted state) at stop time and re-checks at wake. By default the session resumes with a heads-up in the wake message;workspaceGuard=pause holds it until you review the diff.

Does it work on Windows?

Via WSL — which is where the agent CLIs live on Windows anyway. Everything works as on Linux, and desktop notifications arrive as native Windows toasts through PowerShell. Native Windows without WSL isn't supported: with no tmux or Zellij there's no pane to watch, and unsnooze says so instead of breaking your CLI.

What does it need?

Node ≥ 20 and tmux ≥ 3.2 or Zellij, on macOS, Linux, or Windows via WSL. Wrappers install into ~/.zshrc / ~/.bashrc; everything is reversible with unsnooze uninstall.