Automatically hand off a Codex task before a usage limit and resume it in the same session after the limit resets.
Uses Codex's PreToolUse, PostToolUse, Stop, and SessionStart hooks, with no
third-party Python dependencies.
At the end of a turn, the Stop hook checks the session's locally recorded usage. When the configured threshold is reached, it asks Codex to write a HANDOFF.md summary and stop. A detached helper then schedules a continuation through codex queue after the reset. The SessionStart hook can re-arm a pending continuation after a restart.
The PreToolUse and PostToolUse hooks run the same check at every tool call. When
the threshold is reached, they inject the handoff instruction once per window. This
gives a long turn an earlier warning, before its next turn boundary.
By default, the tool monitors the 5-hour usage window at 85%. It can also monitor the weekly window. Optional reset credits can bypass the handoff when a redemption succeeds.
GitHub renders this Mermaid diagram directly in the README. The normal path requires no reset credits and never spends them unless the user explicitly opts in.
flowchart TD
A["Codex activity"] --> B{"Hook event"}
B -- "PreToolUse / PostToolUse" --> C{"Threshold reached<br/>and quota available?"}
C -- Yes --> D["Inject HANDOFF instructions<br/>once per quota window"]
C -- No --> E["Continue normally"]
D --> E
B -- Stop --> F["Check usage<br/>Try optional reset credit"]
F --> G{"Limit still reached?"}
G -- No --> E
G -- Yes --> H{"Quota exhausted?"}
H -- No --> I["Request HANDOFF.md<br/>via Stop block if needed"]
H -- Yes --> J["Schedule auto-resume"]
I --> J
B -- SessionStart --> K["Check for updates<br/>Re-arm pending resume"]
K -. "If pending" .-> L
J --> L["Wait for reset + buffer"]
L --> M{"Session active?"}
M -- Yes --> N["Skip auto-resume"]
M -- No --> O["codex queue<br/>Same session"]
O --> P["Resume task<br/>Read HANDOFF.md<br/>or reconstruct context"]
style D fill:#e8f1ff,stroke:#5684bd,color:#172b45
style I fill:#fff3e1,stroke:#cb963f,color:#47351a
style J fill:#e7f5ee,stroke:#60a686,color:#193e2e
style P fill:#e7f5ee,stroke:#60a686,color:#193e2e
The tool hooks check usage only when a tool runs, and the Stop hook checks at a turn boundary. Reaching the threshold cannot interrupt a model response already in flight, and the handoff instruction is not a guarantee that Codex will write the file. If Codex is already exhausted, the hook can schedule recovery without asking the model to write a report. If the machine is off at reset, the helper cannot deliver the message until Codex runs again and SessionStart re-arms it. Resume is optional, and existing user activity prevents an automatic interruption.
- Codex CLI 0.160 or later
- Python 3.10 or later
- Windows, Linux, or macOS (tested by CI)
Automatic message delivery uses Codex's app-server and queue support. Check your CLI version with codex --version.
Choose one method. Installing both the plugin and the standalone hooks will register duplicate hooks.
codex plugin marketplace add IsmailAzzouz/codex-auto-startstop
codex plugin add codex-auto-startstop@codex-auto-startstopRestart Codex, review the hook commands and explicitly approve them in the
interactive hook review flow. Installing the plugin does not grant execution
permission. Verify the installation with codex plugin list. If the desktop app
does not show an approval prompt, use the interactive CLI to review pending hooks.
git clone https://github.com/IsmailAzzouz/codex-auto-startstop.git
python3 codex-auto-startstop/codex_auto_startstop.py installOn Windows PowerShell, use the Python launcher instead:
git clone https://github.com/IsmailAzzouz/codex-auto-startstop.git
py -3 .\codex-auto-startstop\codex_auto_startstop.py installThe installer copies the script to ~/.codex/auto-startstop/, creates a default configuration, seeds the editable handoff prompt (see Handoff prompt), and registers the hooks in ~/.codex/hooks.json. It never edits config.toml to trust hooks automatically. Open interactive Codex, inspect the registered commands, and explicitly approve each one before expecting them to run. An untrusted hook will be skipped.
For standalone installs, commands point to ~/.codex/auto-startstop/codex_auto_startstop.py with stop, session-start, or checkpoint. On Windows they use Windows-safe double quotes. Plugin hooks use python3 and ${PLUGIN_ROOT} on Linux/macOS, and the official commandWindows override (py -3 and %PLUGIN_ROOT%) on Windows. Each handler has a descriptive statusMessage.
Some Codex versions display "Hook 1", "Hook 2", or "unknown" rather than a custom title. Codex currently lacks a supported per-hook display-name field: statusMessage helps in expanded details but does not override every client's row title. Identify hooks by their event, source, and command. See the upstream naming limitation.
Before trusting a command, inspect the file it executes. Do not use --dangerously-bypass-hook-trust as an installation step.
Configuration is read from ~/.codex/auto-startstop/config.json. The standalone installer creates this file; plugin installations use built-in defaults until you create it.
| Option | Default | Description |
|---|---|---|
trigger |
"5h" |
Handoff trigger: 5h, weekly, or both |
stop_percent |
85.0 |
5-hour usage threshold (%) |
stop_percent_weekly |
90.0 |
Weekly usage threshold (%) |
resume_buffer_sec |
90 |
Delay in seconds after the reset |
resume |
true |
Enable automatic continuation |
handoff_file |
"HANDOFF.md" |
Handoff filename in the session working directory |
notify_desktop |
true |
Enable desktop notifications when supported |
check_updates |
true |
Check GitHub for a newer release at session start |
update_check_hours |
24 |
Hours between update checks |
update_url |
latest release page | Page used by the check; override for a fork |
use_reset_credits |
false |
Allow automatic redemption of reset credits |
reset_credit_on |
"weekly" |
Window eligible to trigger redemption: 5h, weekly, or both |
codex_bin |
"codex" |
Codex executable path |
python |
Current interpreter | Python executable used for the helper |
For example, to monitor both usage windows, set:
{
"trigger": "both"
}The tool checks thresholds at turn boundaries, so a handoff is not guaranteed before an active turn exhausts the available usage.
The prompt that asks Codex to wrap up lives in its own markdown file, so you can reword it without touching the script or the JSON config:
- Plugin installs read
prompts/handoff.mdfrom the plugin folder. - Standalone installs seed
~/.codex/auto-startstop/handoff-prompt.mdon first install; edit that copy.
At send time the Stop hook fills in {window}, {percent}, {minutes_left}, {reset_local}, {handoff_file}, and {handoff_path}. Keep {handoff_file} so Codex knows which file to write. For compatibility, a stop_message value in config.json still overrides the file.
The prompt file is optional and never fatal: if it is missing, empty, unreadable, larger than 64 KiB, or not valid UTF-8, the built-in prompt is used instead. A UTF-8 BOM and Windows line endings are normalised, and a literal { or } in your markdown (a JSON or code sample) does not stop the placeholders from being filled in.
At session start, the tool asks GitHub for the newest release tag. When a newer version exists, it shows the update command once for that version. The check does not block the session when the network is down or the URL is unreachable.
- Plugin install:
codex plugin marketplace upgrade codex-auto-startstop - Standalone install: update your checkout, then run
python3 codex_auto_startstop.py install - Codex asks you to approve the hooks again after an update. Hook trust is bound to the script content, so this prompt is expected.
- The check makes one HTTPS request to the release page, at most once every
update_check_hours. The result is stored in~/.codex/auto-startstop/update.json. - The request must stay on the configured host over HTTPS. The tool refuses a redirect to another host or to plain HTTP.
- Set
check_updatestofalseto disable the check.
If your Codex account has usage-limit reset credits, setting use_reset_credits to true permits the tool to redeem one automatically when a selected window reaches its threshold. A successful redemption skips the handoff and wait; if redemption is unavailable or fails, the normal handoff flow applies.
reset_credit_on selects which window can trigger a redemption attempt. It is "weekly" by default. With reset credits enabled, that window is monitored even if it is not selected by trigger, so a failed attempt can still fall back to a handoff.
Enabling this option can spend real credits from your account without further confirmation when the selected window reaches its threshold. It is disabled by default (false), and the installer does not enable it. Only opt in if you accept this automatic consumption.
Run these commands from a repository checkout, or replace the script path with your installed copy:
python3 codex_auto_startstop.py status
python3 codex_auto_startstop.py status --thread <session-id>
python3 codex_auto_startstop.py creditsOn Windows, replace python3 with py -3 and / with \ where a path is shown.
For standalone installations, the script is at ~/.codex/auto-startstop/codex_auto_startstop.py.
Runtime state and logs are stored in ~/.codex/auto-startstop/. To inspect the log:
tail -f ~/.codex/auto-startstop/auto-startstop.logEvery normal Stop run records the observed quota and threshold, for example:
stop: below threshold session=<id> trigger=5h 5h=41.0% used (threshold=85.0%) (...)
no readable rate-limit data is reserved for a missing or incompatible quota snapshot. It is not used when a valid quota is simply below the threshold.
For a plugin install, the runtime state remains under ~/.codex/auto-startstop/, but the script lives in the plugin directory. To run status without a source checkout:
script="$(find ~/.codex -type f -name codex_auto_startstop.py -path '*codex-auto-startstop*' -print -quit)"
python3 "$script" status --thread <session-uuid>- The
PreToolUseandPostToolUsehooks run only when a tool runs. A turn with no tool calls reaches only theStophook. - The
Stophook runs at turn boundaries; it cannot interrupt a turn that has already reached the usage limit. - The handoff is an instruction to Codex, not a guarantee that
HANDOFF.mdwill be written. If it is missing, the continuation message asks Codex to reconstruct the task status. - If the machine is off when a reset occurs, the helper cannot run. The
SessionStarthook can re-arm pending work when the session is opened again. - For a weekly reset several days away, the detached helper remains alive until the scheduled continuation (unless the machine or process stops); there is no eight-hour cutoff.
- If there is user activity after the reset, automatic continuation is skipped to avoid interrupting ongoing work.
- Queued continuation depends on the Codex CLI's app-server and message queue behavior.
For a plugin installation:
codex plugin remove codex-auto-startstop@codex-auto-startstopFor a standalone installation:
python3 ~/.codex/auto-startstop/codex_auto_startstop.py uninstallOn Windows PowerShell:
py -3 $HOME\.codex\auto-startstop\codex_auto_startstop.py uninstallAdd --purge to the standalone uninstall command to remove runtime state. This does not automatically remove previously trusted hook entries from Codex's configuration. Entries written by versions before 1.0.1 are not revoked automatically; review them manually if necessary.
python3 tests/synthetic_test.pyThe synthetic suite tests threshold decisions, per-window deduplication, resume scheduling, activity checks, reset-credit decisions, and platform-specific command generation without consuming real account credits. GitHub Actions runs it on Windows, Linux, and macOS with Python 3.10 and 3.13. The release-shell tests run on Linux and macOS because release publication is a Bash workflow.
Reset-credit redemption is tested with a simulated app-server; it has not been verified by spending a real credit as part of the test suite.
All pull requests and pushes to main run the six CI test jobs (Windows, Linux, and macOS with Python 3.10 and 3.13). They cover the hooks, installation safety, and release-version logic; Bash-only release simulations run on Linux and macOS. A failed or cancelled test job prevents publication. Forked pull requests run with read-only permissions and never publish releases.
For each successful, new push to main, the release job:
- Selects the next
vMAJOR.MINOR.PATCHversion. A manually committed higher version (for example1.2.0) is respected; otherwise, the patch version increments. - Synchronizes the version in
codex_auto_startstop.pyand.codex-plugin/plugin.json. When a bump is needed, it makes a version-onlychore(release)commit onmainwith[skip ci]. - Creates an annotated Git tag and publishes a GitHub Release with automatically generated release notes. The source ZIP and tarball are automatically provided by GitHub.
No personal access token or third-party publishing service is necessary: only the publish job receives GITHUB_TOKEN with contents: write. The workflow serializes releases, never overwrites an existing version tag, and can be rerun without creating a duplicate. If another commit arrives before an older run can publish, the newer CI run becomes responsible for the latest code, rather than releasing an untested commit.
The current workflow uses a small version-only commit on main to keep the published source and plugin versions consistent. If you enable branch protection that blocks GitHub Actions from pushing to main, this step will fail safely; allow this narrow automation or migrate to a release-PR approach. Release publication does not run for a PR or before it is merged.
Releases: latest version · CI and release workflow.
MIT. See LICENSE.