claude-nextsession

Schedule a set of instructions to run automatically once your Claude Code usage session resets — no need to babysit the clock or remember to come back.

Install

git clone https://github.com/pcx-wave/claude-nextsession.git && mkdir -p ~/.claude/skills && ln -sf "$(pwd)/claude-nextsession" ~/.claude/skills/claude-nextsession

That's a symlink, not a copy — git pull inside the cloned repo keeps the skill current, no reinstall. One manual step remains: wiring the rate-limit cache into your statusLine command, covered in Setup below — it's the only thing this depends on outside the repo itself.

Usage

This is a Claude Code skill — you talk to it, you don't run Python yourself.

Before you walk away:

postpone this to next session: fix the failing tests in src/

or invoke it explicitly:

/claude-nextsession Fix the failing tests in src/

Claude schedules the run as a tool call and tells you when it'll fire — it figures out exactly when your current 5-hour usage window resets and schedules a headless claude -p run for that moment.

When you're back:

what did the scheduled task do?

Claude reads this project's report and continues from whatever's left outstanding, or hands you the one-line command to reopen that exact session. See SKILL.md for the precise instructions Claude follows in both directions.

(The raw python3 postpone.py commands still work directly — see Direct CLI below — but that path is for cron jobs and scripting, not day-to-day use.)

Why

Claude Code Pro/Max sessions have a rolling usage window. When you hit it mid-task, you either wait around watching the clock, or you forget and lose the moment your quota comes back. This tool lets you hand off a task and walk away — it fires on its own the instant you're unblocked again.

Walking away is only half of it, though. Coming back to a task that ran without you is its own problem: the run is over, its reasoning is gone, and if you postponed work in three repos they all fired at the same reset. So --resume is scoped to the project you're standing in, leads with what's still outstanding, and ends with the exact claude --resume <id> for that run — never a list of sessions to guess between.

How it works

Claude Code already knows exactly when your session resets — it's the same number behind the session 42% (60m) indicator in your status bar (rate_limits.five_hour.resets_at, a Unix timestamp, delivered to your statusLine command on every turn). This tool doesn't call any API; it just reads that value from a small local cache.

  1. Your statusLine command writes rate_limits to ~/.claude/cache/rate-limits.json on every turn (see Setup below — this one small addition is the only external dependency).
  2. postpone.py reads rate_limits.five_hour.resets_at from that cache.
  3. It schedules templates/delayed_task.sh via systemd-run --user --on-calendar, which runs claude -p "$INSTRUCTIONS" at that exact time.
  4. When the run finishes, it writes a report keyed to that project — including the run's own session_id — so --resume can find the right one later and point you back at the exact session.

Because it reads Claude Code's own subscription-session state instead of querying the Anthropic API, there's no API key, no network call, and no confusion with unrelated pay-per-token rate limits.

Setup

1. No Python dependencies — standard library only.

2. Wire the rate-limit cache into your statusLine command. Claude Code invokes your configured statusLine command (see statusLine in ~/.claude/settings.json) with a JSON payload on stdin that includes rate_limits. Add this wherever your script parses that payload:

import json, os, time

rl = d.get("rate_limits") or {}  # `d` = the parsed stdin JSON
if rl:
    cache_path = os.path.expanduser("~/.claude/cache/rate-limits.json")
    os.makedirs(os.path.dirname(cache_path), exist_ok=True)
    with open(cache_path, "w") as f:
        json.dump({"rate_limits": rl, "captured_at": int(time.time())}, f)

If you don't have a custom statusLine command yet, you need one purely to receive this payload each turn — see Claude Code's statusline docs for the minimal setup.

3. Confirm systemd-run is available:

systemctl --user status   # should show a running instance

(Any modern Linux desktop/server with systemd has this — nothing to install.)

4. Confirm claude is in $PATH.

That's the whole setup — no .env, no API key, no extra packages.

Direct CLI (cron, scripting, no Claude Code involved)

# Basic — reads the reset time from the local cache
python3 postpone.py --instructions "Your instructions here"

# Run in a specific project directory
python3 postpone.py -i "Check system" -w /path/to/project

# Dry run — see what would be scheduled without actually scheduling it
python3 postpone.py -i "Check system" --dry-run

# Override the detected reset time (for testing)
python3 postpone.py -i "Test" -t "2026-07-26T15:30:00Z"

# Catch up on this project's most recent run
python3 postpone.py --resume

# ...or the most recent run from any project
python3 postpone.py --resume --global

Permissions

The scheduled task runs headless — nobody is there to answer a permission prompt. Without an explicit flag, claude -p silently declines any Read/Edit on an existing file and exits 0, so the log looks like a success even though nothing happened. --permissions controls this:

Value Effect
scoped (default) --allowedTools "Read Edit Write Bash Glob Grep" — covers most coding tasks, nothing beyond that
bypass --dangerously-skip-permissions — no restrictions at all
python3 postpone.py -i "..." --permissions bypass

Pick bypass deliberately for tasks that need it — not as a default, since nothing reviews what it does before it runs.

Picking up where it left off

Every run writes a report to ~/.claude/cache/claude-nextsession/reports/ (outside this repo — it's public, and reports can contain private task content). Catch up after starting a new session with:

python3 postpone.py --resume

It only shows reports for the project you're standing in. If you postpone tasks in several repos, they all fire at the same reset — scoping by working directory is what keeps them from getting mixed up. Add --global to see the most recent report from any project instead. (--last-report still works and behaves identically.)

The report leads with a Next steps section and ends with a one-line resume command:

# claude-nextsession report — SUCCESS

## Next steps

- [x] JWT issue/verify + middleware landed
- [ ] refresh-token rotation - blocked on a TTL decision
- next: wire logout to token revocation

...

## Resume

cd /path/to/your-project && claude --resume 4f2a91c8-77de-4f0b-9c1e-aa0912345678

That resume line reopens the exact headless session that did the work, with its full transcript — no session picker, no guessing which entry belongs to which project.

The "Next steps" section comes from the run itself: every scheduled instruction gets a short epilogue appended asking Claude to close with a ## NEXT STEPS section (completed / not completed and why / what's next), which is then lifted to the top of the report. If the run omits it, the report is simply built without it.

Below that sits the full picture: status (SUCCESS / PARTIAL (N denied) / FAILED / CRASHED), what was asked, what Claude actually did, any permission denials, and a git diff --stat of what changed — built from claude -p --output-format json (is_error, permission_denials, cost, turns) plus a real git diff of the workdir. The full JSON and patch are saved alongside the summary for anyone who wants the raw detail.

Inspecting a scheduled task

systemctl --user status claudepostpone-<timestamp>-<pid>
journalctl --user -u claudepostpone-<timestamp>-<pid>

# or just tail the log the script itself writes
tail -f /tmp/claudepostpone_*.log

# or the structured report, once it's done
python3 postpone.py --resume

Requirements

  • Python 3.10+ (standard library only)
  • systemd-run + a working systemd user session (systemctl --user)
  • claude CLI available in $PATH at execution time
  • A statusLine command that persists rate_limits as described above

Files

  • postpone.py — main script
  • report.py — builds and reads execution reports
  • templates/delayed_task.sh — generated-script template; invokes claude -p "$INSTRUCTIONS" --output-format json in the captured working directory and logs to /tmp/claudepostpone_<timestamp>.log
  • SKILL.md — Claude Code skill definition (frontmatter + full reference)

License

MIT