Skip to content

amanjaiman/sleeperagent

Repository files navigation

sleeperagent

CI Release Go Reference License: MIT

Your usage limit resets. Your agent automatically continues.

When a coding agent hits a 5-hour or weekly usage limit it hard-stops until you manually type "continue." If that reset lands while you're asleep, the task sits dead for hours. sleeperagent runs the agent in a session it can watch, detects the limit, waits for the reset, and re-prompts it automatically — then hands the live session back the moment you show up.

12:58:03 watching. take over any time with: tmux attach -t feature-x
12:58:31 usage limit detected
12:58:31 reset at Sat 17:00 EDT (source=clock, confidence=high); waiting 4h2m29s
17:01:00 reset reached; resuming
17:01:01 injected resume prompt (attempt 1): "Usage limit reset. Continue with the prior task."
17:01:03 resume confirmed; back to running
  • Cross-agent — Claude Code and Codex out of the box; any CLI agent via config-driven patterns. It only waits for legitimate resets — no quota circumvention.
  • Cross-platform — native on Linux, macOS, and Windows (no WSL required).
  • Graceful handoff — hotkeys, detach/stop, auto-detach when you attach, Ctrl-C that detaches rather than kills.
  • Local-LLM reprompt (optional) — a local Ollama model writes a context-aware continuation instruction from the transcript + git diff, validated before use. Falls back to a static prompt on any doubt.
  • Operable & safestatus, desktop + webhook notifications, a parse command to tune patterns, and unattended tool calls off by default (--yolo opts in).

See docs/SPEC.md for the full design rationale.


Install

Prebuilt binary — download for your OS/arch from the Releases page.

Put it on your PATH:

./sleeperagent install

If the install directory is not already on PATH, the command prints the exact setx PATH or export PATH line to run, plus a reminder to open a new shell. On macOS/Linux, install also tries to add that PATH update to your shell profile automatically (zsh, bash, and sh; other shells get the printed line only). Pass --no-profile to skip that and just print the line yourself.

Staying up to date — release builds check GitHub for a newer version when you start a run from a real terminal (at most once a day) and offer to install it; answer Y and the binary replaces itself, taking effect on the next start. You can also update explicitly any time:

sleeperagent update          # or --check to only report

Disable the startup check with check = false under [update] in config.toml, or SLEEPERAGENT_NO_UPDATE_CHECK=1.

With the Go toolchain:

go install github.com/amanjaiman/sleeperagent/cmd/sleeperagent@latest

From source (requires Go 1.23+):

git clone https://github.com/amanjaiman/sleeperagent && cd sleeperagent
make build      # -> ./sleeperagent (version stamped from git)
make check      # gofmt + go vet + unit tests

Platform support

sleeperagent needs a "session backend" — something it can run the agent inside, read output from, and type into.

OS Default backend Setup Handoff
Linux / macOS tmux when available, otherwise pty optional tmux (apt install tmux / brew install tmux) for full handoff full with tmux; reduced with pty
Linux / macOS pty (--backend pty) none reduced — agent is bound to the supervisor
Windows pty (ConPTY) none (Windows 10 1809+) reduced — agent is bound to the supervisor

Optional extras: a local Ollama for --reprompt; notify-send (Linux) / osascript (macOS) for desktop notifications (Windows uses a toast).


Quick start

# Launch claude, watch it, and auto-resume when the limit resets
sleeperagent run --agent claude --name mytask

--agent picks the adapter (how to detect the limit and drive the agent); by default it also launches that adapter's own command, so the claude adapter just runs claude. You only need a trailing -- <command…> to launch something different — your own flags, a wrapper, or another binary (see Examples). Run sleeperagent with no arguments for the built-in help.

On every platform, run from a real terminal drops you straight into the live agent: prompt it and use it exactly as if you'd launched it directly, while sleeperagent watches in the background and auto-resumes after a limit reset. On the tmux backend, detach your view with the tmux prefix + d (watchdog keeps running), or pass --detached to skip attaching entirely.


Ways to use it

  • sleeperagent run — the default. Launches the agent, watches it in your terminal, detaches with a hotkey, and takes over the moment you're back. Check on it from any other shell with sleeperagent status.
  • sleeperagent attach-existing — you already started the agent yourself in tmux and want sleeperagent to pick up watching it without restarting anything. From a real terminal this also drops you into the live session, same as run.

Commands

Command Description
run [flags] [-- <cmd…>] Launch an agent and watch it. The main mode. The trailing -- <cmd…> is optional — omit it to use the adapter's default command.
attach-existing --target T [flags] Watch an agent already running in a tmux session (also the crash-recovery path).
status [--name N] Report each instance's state, reset countdown, and prompt preview.
logs --name N [--follow] Print (or -f/--follow to tail) an instance's supervisor log.
detach --name N Stop watching; keep the session (tmux) running. If the supervisor already exited, cleans up its stale record.
stop --name N [--kill] Stop watching; --kill also terminates the session. If the supervisor already exited, cleans up its stale record.
rm --name N [--force] / rm --all Remove a stale/ended instance record (e.g. after the agent exited). --all prunes every record with no running supervisor.
agents [--config P] List configured adapters and validate that their patterns compile.
parse --agent A "text…" Test a captured limit string against an agent's patterns and show the resolved reset.
install [--dir DIR] [--force] [--no-profile] Copy this binary to a PATH directory.
update [--check] Update to the latest GitHub release (checksum-verified); --check only reports.
version Print the build version.

run flags

Flag Description
--agent Adapter to use: claude (default) or codex.
--name Instance / tmux session name (default sleeperagent-<agent>).
--prompt Static resume prompt to inject on reset.
--reprompt Local-LLM reprompt, e.g. ollama:llama3.1 (falls back to static).
--backend tmux or pty. Unix defaults to tmux when it is available, otherwise pty; Windows defaults to pty.
--detached tmux backend: don't attach this terminal to the session; watch from the console with the d/q/k hotkeys instead.
--yolo Append the agent's skip-permissions flag (DANGEROUS — unattended, no prompts).
--auto-answer-prompts Answer interactive prompts with the first/default option so the run doesn't stall while you're away (default: on; pass =false to disable).
--webhook POST notifications to this URL as JSON.
--no-notify Disable desktop notifications.
--config Path to config.toml (default: OS config dir).

Examples

# Codex with a custom static resume prompt
sleeperagent run --agent codex --prompt "Continue; run the tests after."

# Let a local model write the continuation instruction each reset
sleeperagent run --agent claude --reprompt ollama:llama3.1

# Check on running instances from any other shell (works on all platforms)
sleeperagent status

# Custom launch command — same Claude adapter, but your own flags / wrapper / binary
sleeperagent run --agent claude -- claude --model opus --add-dir ../shared-lib

# Watch an agent you started yourself in tmux
sleeperagent attach-existing --agent claude --target mywork:0.1

# Same, but for a Codex session running in another tmux pane
sleeperagent attach-existing --agent codex --target mywork:0.2

# Validate a limit pattern against text you copied from your real CLI
sleeperagent parse --agent claude "5-hour limit reached ∙ resets 2pm"

Taking over

sleeperagent is built to get out of your way. How handoff works depends on the backend:

tmux backend (Linux/macOS): the agent lives in a tmux session that outlives the supervisor, so nothing is lost when you take over. Install tmux (brew install tmux on macOS) or pass --backend tmux if you specifically need this behavior.

  • You start attached: run from a terminal puts you inside the session immediately — prompt the agent as usual while the watchdog monitors. Detach the view with the tmux prefix + d (default Ctrl-b d); the watchdog keeps running and you get its console log back with the d/q/k hotkeys active. (Runs from inside an existing tmux session skip the auto-attach, since tmux refuses nested attaches.)
  • sleeperagent detach --name X from any other shell stops watching (the session keeps running). If you're still inside the view, sleeperagent tells you via the tmux status line and waits for you to detach rather than yanking your terminal.
  • Auto-detach: a tmux attach after you detach your initial view — or a second client attaching while your view is up — is treated as a takeover, and sleeperagent steps aside so you don't both type.
  • Reattach anytime with tmux attach -t <name>.
  • --detached mode: the pre-0.4 console view — supervisor logs in your terminal with hotkeys d/q detach, k kill (with a y confirm); Ctrl-C detaches, never kills.

pty / ConPTY backend (default on Windows, automatic Unix fallback when tmux is missing): the agent is a child of the supervisor, so it can't be handed back interactively. detach gives the terminal back to you until the agent exits; stop --kill ends the agent. Use the tmux backend if you need full handoff.


Configuration

Built-in defaults already cover Claude Code and Codex. To override timings or the limit patterns (e.g. after an agent CLI changes its wording), copy config.example.toml to your OS config dir — no reinstall needed:

OS Config file State / logs
Linux ~/.config/sleeperagent/config.toml ~/.local/state/sleeperagent/
macOS ~/Library/Application Support/sleeperagent/config.toml ~/.local/state/sleeperagent/
Windows %AppData%\sleeperagent\config.toml %AppData%\sleeperagent\state\

sleeperagent status reads the per-instance state file, so it works from any shell; a * next to a state means the supervisor process is no longer running.

Limit patterns are Go regexes with a named group for the reset time, resolved most-reliable-first:

  • (?P<ts>…) — an explicit unix timestamp (most reliable)
  • (?P<time>…) — a clock time (2pm, 6:34 AM)
  • (?P<dur>…) — a relative duration (in 2h30m, in 45 minutes)

If none parse, sleeperagent assumes a 5-hour window and flags it low-confidence. Use sleeperagent parse to check a pattern against real output, and sleeperagent agents to validate your config. Adding a new agent is usually just a new [agents.<name>] block — no code.


Local-LLM reprompt (optional)

By default sleeperagent injects a fixed string on reset. With --reprompt ollama:<model> it instead asks a local model to write the next instruction: it reads the tail of the agent's transcript plus git diff --stat / git log in the agent's cwd, sends a fixed meta-prompt to Ollama, and validates the reply (non-empty, under max_prompt_chars, clears the denylist) before injecting.

It's purely additive and safe-by-construction: if Ollama is unreachable, the output is empty/over-long/denylisted, or there's no context, it falls back to the static prompt so the session still resumes. Everything stays local — no transcript leaves your machine. Tune it under [reprompt] in the config (model, base_url, max_prompt_chars, tail_messages, denylist); base_url also honors $OLLAMA_HOST.

Notifications

Desktop notifications are on by default (best effort; --no-notify to disable) and fire when a limit is hit, when the agent resumes, and on detach. Add --webhook <url> to also POST each event as JSON ({title, body, time}).


Safety

sleeperagent waits for legitimate resets; it does not bypass limits.

Resuming unattended runs tool calls with no human in the loop, so by default the agent keeps its normal permission prompts — sleeperagent does not pass --dangerously-skip-permissions / full-auto for you. That's an explicit opt-in via --yolo, which bypasses permission prompts entirely; use it only when you understand the risk. Separately, --auto-answer-prompts defaults to on: it doesn't remove any prompts, but if one comes up while you're away, it answers with the first/default option — including ones that approve tool calls — rather than letting the run stall. Choosing to run an agent unattended already means accepting it can act without you each cycle; pass --auto-answer-prompts=false if you'd rather it stall on unexpected prompts instead. LLM-generated prompts are length-capped and denylist-checked before injection.

How it works

The reset time isn't exposed in any file or API, so detection is output-scraping with config-driven regexes that fail loud when formats change. The supervisor only ever reads the agent's pane and writes to it — it never owns the terminal — which is what makes clean handoff possible.

RUNNING --limit detected--> LIMITED --reset parsed--> WAITING
   ^                                                     |
   |                                              reset reached
   +----------- resume confirmed -- RESUMING <-----------+

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. Adding an agent is usually just a config block; reporting a limit-string that stopped matching helps keep the built-in defaults current. The codebase is a small set of internal/ packages behind the CLI in cmd/sleeperagent; run make check before a PR.

License

MIT © Aman Jaiman

About

Resume Claude Code / Codex automatically when usage limits reset - and step aside the moment you take over. Cross-platform (Linux/macOS/Windows).

Resources

License

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors