Cap-aware tmux scheduler & auto-resume for Claude Code — schedule prompts and keep sessions running through the 5-hour usage limit.
Project description
⏻ claudekeeper
Schedule prompts for Claude Code, and keep your sessions running through the 5‑hour usage cap.
claudekeeper is a tiny background daemon that watches your Claude Code sessions running in tmux and does two things your usage limit won't let you do on your own:
- ⏰ Schedule — pre‑type a prompt in a pane and have claudekeeper press Enter at a wall‑clock time (
2am,now + 30min, …). Kick off long runs while you sleep. - ♻️ Auto‑resume — when a session hits the 5‑hour usage cap, claudekeeper waits out the reset and continues it automatically, so work doesn't stall while you're away.
And because it does both, it does the thing neither half can alone:
🧠 Cap‑aware deferral — a prompt scheduled for
2amis never fired into a capped pane and lost. claudekeeper holds it until the cap resets, then submits it.
It's dependency‑free Python driven entirely through the tmux CLI, so it runs anywhere tmux does — Linux boxes, HPC login nodes, macOS.
🚀 Install
One‑liner (recommended):
curl -fsSL https://raw.githubusercontent.com/PursuitOfDataScience/claudekeeper/main/install.sh | sh
Drops claudekeeper in ~/.local/share/claudekeeper and a launcher at ~/.local/bin/claudekeeper. The only runtime requirements are python3 ≥ 3.8 and tmux — both already on almost any machine you'd run Claude Code on. No pip, no virtualenv, nothing to compile.
Or via a Python package manager:
uv tool install claudekeeper # or: pipx install claudekeeper
Why not npm i -g?
npm installs Node programs; claudekeeper is Python. The Python equivalent of npm's global one‑liner is uv tool install / pipx install above, or the curl | sh script (which needs nothing but python3). If ~/.local/bin isn't on your PATH, the installer tells you how to add it.
⚡ Quickstart
claudekeeper setup # 1. one-time: start the background daemon
# 2. Open Claude Code in a tmux session, type your prompt, DON'T hit enter — then:
claudekeeper schedule mysession 2am # claudekeeper presses Enter at 2am
claudekeeper schedule mysession "now + 30min" --prompt "run the full test suite"
# 3. (optional) auto-resume after the usage cap:
claudekeeper watch on
claudekeeper status # the daemon, your jobs, your sessions
claudekeeper logs -f # watch what it does, live
Run claudekeeper with no arguments any time for a refresher — it even lists the Claude sessions you have running right now.
🧭 The mental model (read this once)
claudekeeper has two moving parts:
- A background daemon (
claudekeeper setupinstalls it). It's the thing that actually presses keys. Scheduled jobs only fire while the daemon is running — the one gotcha, andclaudekeeper schedulewarns you if it isn't. - A job list + settings you manage from the CLI (
schedule,ls,rm,watch on/off). The daemon reads these live.
You pre‑type your prompt into the Claude Code input box and leave it there; claudekeeper submits it for you (a single Enter) at the right moment. With --prompt "...", claudekeeper types the text for you instead.
📖 Commands
| Command | What it does |
|---|---|
claudekeeper setup |
Install + start the daemon, then print personalized next steps |
claudekeeper schedule <target> <time> [--prompt T] [--daily] [--force] |
Arm a keypress. <target> is a tmux session or session:win.pane. |
claudekeeper ls |
List scheduled jobs (with ETA and any deferral) |
claudekeeper rm <id|all> |
Cancel a job |
claudekeeper watch on|off|status |
Toggle cap auto‑resume (off by default) |
claudekeeper status |
Daemon state, jobs, detected Claude panes |
claudekeeper logs [-f] [-n N] |
View / follow activity |
claudekeeper service install|uninstall|start|stop|status |
Manage the daemon directly |
claudekeeper daemon |
Run the loop in the foreground (what the service runs) |
claudekeeper doctor |
Check python, tmux, service manager, paths |
Time formats: 2am, 3pm, 9:30am, 14:30, 22:00, now + 5min, +10m, in 90s, noon, midnight, teatime.
🛡️ Safety
claudekeeper automates keystrokes into your terminals. It's built fail‑safe, but be aware:
- Auto‑resume is opt‑in.
watchis off until you runclaudekeeper watch on. Out of the box claudekeeper only fires prompts you explicitly schedule. - A cap is only a cap if the pane shows a real reset time. Auto‑resume ignores API errors, scrollback, and mere chatter about limits — it acts only on a parseable "resets H:MM am/pm". Each cap is resumed once, and only re‑arms after the pane is seen working again, so it can never loop and drain your budget.
- A blind Enter lands on whatever has focus. If a pane is sitting on a tool‑permission prompt whose default is destructive, an Enter would accept it. Schedule against panes you've left at the prompt with your text staged.
- It's node‑local. The daemon watches the tmux server on the machine it runs on; run it on the same host as your sessions.
⚙️ Configuration
Settings live in ~/.local/state/claudekeeper/config.json (override the dir with CLAUDEKEEPER_HOME). Toggle the common one with claudekeeper watch on/off; edit the file for the rest (msg, poll, buffer, stagger, match_command, only, exclude, …). The daemon re‑reads it every loop, so changes take effect without a restart, and out‑of‑range or wrong‑type values are clamped to safe defaults rather than crashing the daemon. Set CLAUDEKEEPER_TMUX to point at a specific tmux binary.
Keep
CLAUDEKEEPER_HOMEon a local filesystem. claudekeeper serializes its state withflock, which is only reliable on local disks — on some network filesystems (NFS) cross‑host locking is a no‑op. claudekeeper is node‑local anyway.
🔬 How it works
A single loop, every poll seconds:
- list tmux panes whose command is
claude, capture each one's text; - if
watchis on, detect real caps and schedule a one‑time resume at reset +buffer(staggered across sessions); - fire any due scheduled jobs — deferring any whose target pane is currently capped.
Scheduled jobs are persisted before the keypress (at‑most‑once — a crash never double‑fires a prompt into Claude) and are never lost to a momentary tmux/session outage. Handled caps persist across restarts, so a restart never re‑fires an already‑handled cap. Only one daemon runs per CLAUDEKEEPER_HOME (single‑instance lock).
🧪 Development
git clone https://github.com/PursuitOfDataScience/claudekeeper && cd claudekeeper
PYTHONPATH=src python3 -m claudekeeper doctor
for f in tests/test_*.py; do PYTHONPATH=src python3 "$f"; done # unit tests (no deps)
⚖️ Disclaimer
claudekeeper is an independent, community‑built tool. It is not affiliated with, endorsed by, or sponsored by Anthropic. "Claude" and "Claude Code" are trademarks of Anthropic, PBC — used here only to describe what the tool works with. claudekeeper just automates the tmux CLI around Claude Code sessions you run yourself.
📄 License
MIT © 2026 PursuitOfDataScience
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claudekeeper-0.1.1.tar.gz.
File metadata
- Download URL: claudekeeper-0.1.1.tar.gz
- Upload date:
- Size: 120.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1a2f018ea5510260bca7ae5062f2f7e4424dab6d6c6f43884003190bfc0cec7
|
|
| MD5 |
6ea294fe862e136ade3dae2a85a6cf20
|
|
| BLAKE2b-256 |
bf251462618c5c6feb34cb3779778e27d1c16fedaa0b08a9d9767cf3be48aea1
|
Provenance
The following attestation bundles were made for claudekeeper-0.1.1.tar.gz:
Publisher:
publish.yml on PursuitOfDataScience/claudekeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claudekeeper-0.1.1.tar.gz -
Subject digest:
e1a2f018ea5510260bca7ae5062f2f7e4424dab6d6c6f43884003190bfc0cec7 - Sigstore transparency entry: 2195048253
- Sigstore integration time:
-
Permalink:
PursuitOfDataScience/claudekeeper@b7e4b67040e903482836f06546f508adb6f7c5fa -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/PursuitOfDataScience
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b7e4b67040e903482836f06546f508adb6f7c5fa -
Trigger Event:
release
-
Statement type:
File details
Details for the file claudekeeper-0.1.1-py3-none-any.whl.
File metadata
- Download URL: claudekeeper-0.1.1-py3-none-any.whl
- Upload date:
- Size: 27.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ab27acd804def113f5f07aa3da37063fd76c0dd44b2bfae1c66cffc2f13f0a8
|
|
| MD5 |
8e33149d111d389fb52c322eda39066e
|
|
| BLAKE2b-256 |
77d257b95e9a2c35c9c1d36ff13c74e11801b174a6cb2e52fe2c52c8d2cb828d
|
Provenance
The following attestation bundles were made for claudekeeper-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on PursuitOfDataScience/claudekeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claudekeeper-0.1.1-py3-none-any.whl -
Subject digest:
3ab27acd804def113f5f07aa3da37063fd76c0dd44b2bfae1c66cffc2f13f0a8 - Sigstore transparency entry: 2195048325
- Sigstore integration time:
-
Permalink:
PursuitOfDataScience/claudekeeper@b7e4b67040e903482836f06546f508adb6f7c5fa -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/PursuitOfDataScience
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b7e4b67040e903482836f06546f508adb6f7c5fa -
Trigger Event:
release
-
Statement type: