GitHub - shnksi/optimize-ai-limits

GitHub

6 min read Original article ↗

AI Limit Timer

Automatically maximize your token usage on Anthropic's AI coding tools by ensuring you never lose time to rate-limit reset windows.

Why This Exists

Codex CLI and Claude Code both enforce rolling rate limits: a 5-hour window and a weekly window. When you hit a limit, the timer counts down to a reset — but the fresh allocation only begins when you actually send your next prompt. If your 5-hour window resets at 6:00 AM and you don't open your terminal until 9:00 AM, your NEXT rest window won't be until 2pm. With this tool active, your next window will be available at 11am.

This tool fixes that. It:

  1. Reads exact reset timestamps from Codex session logs and Claude's stream output
  2. Sends a minimal keep-alive prompt ("Reply with exactly OK.") right after each reset, so your next window starts immediately
  3. Self-schedules via macOS launchd to wake up at the next reset time — no cron jobs, no always-on process

The result: your rate-limit windows overlap seamlessly. You get the maximum available token budget without ever having to think about reset timing.

How It Works

┌──────────────────────────────────────────────────────┐
│                     ai-limit-timer                   │
│                                                      │
│  1. Check current reset times                        │
│     ├─ Codex: read ~/.codex/sessions/**/*.jsonl      │
│     └─ Claude: parse stream-json output              │
│                                                      │
│  2. If any window has reset → send "OK" prompt       │
│     ├─ Codex: codex exec "Reply with exactly OK."    │
│     └─ Claude: claude -p "Reply with exactly OK."    │
│                                                      │
│  3. Compute next reset time (earliest across both)   │
│                                                      │
│  4. Write launchd plist → sleep until then + 60s     │
│                                                      │
│  5. launchd wakes us → go to step 1                  │
└──────────────────────────────────────────────────────┘

After a single npm run run, the timer runs itself indefinitely via launchd. Each cycle writes a new plist with the next wake time and re-registers it — no persistent process, no cron job.

If your Mac is off or asleep when a scheduled reset passes, the job runs automatically on next login (RunAtLoad is enabled). It detects that windows have already reset, sends the keep-alive prompts, and resumes the normal scheduling chain.

Data Sources

Provider Source Accuracy
Codex ~/.codex/sessions/**/*.jsonl — token_count payloads with rate_limits.primary.resets_at and rate_limits.secondary.resets_at Exact — reads Codex's own persisted timestamps
Claude claude -p --output-format stream-json — rate_limit_event objects with resetsAt and overageResetsAt Best-effort — if stream-json doesn't contain rate-limit events, falls back to now + 5h for the primary window and preserves the previous weekly reset

Requirements

Setup

1. Clone and configure

git clone https://github.com/shnksi/ai-limit-timer.git
cd ai-limit-timer

Copy the example config and edit it:

cp ai-limit-timer.config.example.json ai-limit-timer.config.json

2. Set your workspace directories

Edit ai-limit-timer.config.json. The critical setting is workspaceDir for each provider — this is the directory the CLI runs in when sending the keep-alive prompt.

{
  "codex": {
    "workspaceDir": "/path/to/any/project"
  },
  "claude": {
    "workspaceDir": "/path/to/a/trusted/project"
  }
}

Important: For Claude, use a directory you've already opened and trusted in interactive mode. Otherwise the trust prompt will block the automated run.

3. First run

This will:

  • Check current reset times for both providers
  • Send a keep-alive prompt if any window has already reset
  • Save state to ./var/state.json
  • Install a launchd job to wake up at the next reset time

4. Verify

You should see output like:

Codex: session-log
  5h reset: 3/17/2026, 10:05:20 AM
  weekly reset: 3/18/2026, 11:39:57 PM
Claude: stream-json
  5h reset: 3/17/2026, 7:00:00 AM
  weekly reset: 4/1/2026, 12:00:00 AM
Next run: 3/17/2026, 7:01:00 AM

From here, the timer manages itself. It will wake up after each reset, send the keep-alive prompt, and schedule the next run.

Commands

Command Description
npm run run Execute one cycle: check resets, prime if due, schedule next run
npm run status Show current reset times and next scheduled run
npm run status -- --json Output status as JSON
npm run install Install/update the launchd daemon from saved state
npm run uninstall Remove the launchd daemon
npm test Run parser unit tests

Configuration

All settings in ai-limit-timer.config.json. Only workspaceDir is required — everything else has sensible defaults.

Disabling a provider

Set "enabled": false to skip either provider entirely:

{
  "codex": { "enabled": false },
  "claude": { "enabled": true, "workspaceDir": "/my/project" }
}

File Structure

ai-limit-timer/
├── src/
│   ├── ai-limit-timer.mjs    # Main entrypoint: CLI, scheduling, orchestration
│   └── parsers.mjs            # Extracts reset timestamps from Codex/Claude output
├── test/
│   └── parsers.test.mjs       # Unit tests for parsers
├── var/                        # Runtime data (gitignored)
│   ├── state.json              # Persisted reset times and next run
│   ├── logs/                   # Stdout/stderr from each keep-alive prompt
│   └── transcripts/            # Raw Claude usage captures
├── ai-limit-timer.config.example.json
├── ai-limit-timer.config.json  # Your local config (gitignored)
├── package.json
└── LICENSE

Troubleshooting

Codex shows "prime-fallback" instead of "session-log" Codex doesn't always include rate-limit timestamps in session logs for small prompts. The timer still works — it estimates now + 5h and schedules accordingly. You'll get exact timestamps again once you use Codex interactively and it writes rate_limits data to its session logs.

Claude prime fails or times out

  • Ensure workspaceDir points to a directory you've previously trusted in Claude Code's interactive mode.
  • Check ./var/logs/claude-prime-*.log for the full stdout/stderr.

"launchd scheduling requires a local macOS user session" The tool must run as a logged-in user, not as root or via SSH without a GUI session. launchd user agents require a GUI login context.

Next run time seems wrong The timer picks the earliest reset across all providers and adds runDelayAfterResetSeconds (default: 60s). Check npm run status -- --json to see exact timestamps and verify which provider is driving the schedule.

Want to see what happened on the last run? Check ./var/launchd.stdout.log and ./var/launchd.stderr.log for the launchd-triggered output, or browse ./var/logs/ for per-provider execution logs.

How the Fallback Works

When exact rate-limit data isn't available — Claude's stream-json doesn't contain a rate_limit_event, or Codex's session log has rate_limits: null for small prompts — the timer uses a conservative fallback for that provider:

  • 5-hour window: estimated as now + 5 hours
  • Weekly window: preserved from the previous known value if it hasn't expired, otherwise estimated as now + 7 days

This means the timer will always schedule a next run, even with incomplete data. The worst case is waking up slightly early and sending an extra keep-alive prompt — which is harmless.

License

MIT