Skip to content

Instantly share code, notes, and snippets.

@ben-vargas
Created October 7, 2025 16:54
Show Gist options
  • Select an option

  • Save ben-vargas/1ea000a3e6681d3e7bce689d665fe100 to your computer and use it in GitHub Desktop.

Select an option

Save ben-vargas/1ea000a3e6681d3e7bce689d665fe100 to your computer and use it in GitHub Desktop.
Consistent Usage Windows for Claude Code and Codex CLI

Running AI CLI Tools with Cron for 5-Hour Usage Windows

The Challenge

Anthropic and OpenAI enforce a 5-hour usage window - once I make my first request, I have 5 hours fixed usage before the window expires. This meant:

  • ❌ Unpredictable availability - window could expire mid-project or at random times during the work day
  • ❌ Frustration when usage windows expire at unopportune times
  • ❌ Consistent work schedule and usage window planning

The Solution

I created a cron-based wrapper script that automatically activates both Claude Code and Codex on a predictable schedule (7 AM, 12 PM, 5 PM, 10 PM). Now:

  • ✅ 5-hour windows start at predictable times throughout the day
  • ✅ I can plan around "new usage window" consistently at 7am, 12pm, 5pm, 10pm throughout the day
  • ✅ Logging for confirming execution/start of windows
  • ✅ Zero manual intervention needed

This guide shares my setup so you can implement a similar approach that fits your work style.

The Wrapper Script

Create /home/code/bin/ai-cli-cron:

#!/bin/bash
# AI CLI tools wrapper for scheduled activation of usage windows
# Outputs JSON for machine-readable logs with human-friendly summaries

# ============================================================================
# CONFIGURATION - Update these values for your environment
# ============================================================================

# System paths
USER_HOME="/home/code"
NVM_DIR="$USER_HOME/.nvm"
NODE_VERSION="v22.18.0"

# Tool locations
CLAUDE_BIN="$USER_HOME/.local/bin/claude"
CODEX_BIN="$USER_HOME/.local/bin/codex"

# Timezone (see: timedatectl list-timezones)
TIMEZONE="America/Denver"

# MCP server API keys (replace with your actual keys, optional and only if seeing MCP errors)
FIRECRAWL_API_KEY="your-firecrawl-api-key-here"
OPENMEMORY_API_KEY="your-openmemory-api-key-here"

# Health check prompt
HEALTH_CHECK_PROMPT="Respond with 'Ready' if operational"

# ============================================================================
# ENVIRONMENT SETUP
# ============================================================================

export HOME="$USER_HOME"
export NVM_DIR="$NVM_DIR"
export PATH="$NVM_DIR/versions/node/$NODE_VERSION/bin:$USER_HOME/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export TZ="$TIMEZONE"
export FIRECRAWL_API_KEY
export OPENMEMORY_API_KEY
export CLIENT_NAME="claude-code"

# ============================================================================
# LOGGING HELPER
# ============================================================================

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# Parse Claude JSON response and extract key info
parse_claude_json() {
    local json="$1"
    local result=$(echo "$json" | jq -r '.result // "ERROR"')
    local duration=$(echo "$json" | jq -r '.duration_ms // 0')
    local cost=$(echo "$json" | jq -r '.total_cost_usd // 0')
    echo "result=$result, duration=${duration}ms, cost=\$${cost}"
}

# Parse Codex JSONL response and extract key info
parse_codex_jsonl() {
    local jsonl="$1"
    local last_line=$(echo "$jsonl" | tail -1)
    local type=$(echo "$last_line" | jq -r '.type // "unknown"')

    if [[ "$type" == "turn.completed" ]]; then
        local tokens=$(echo "$last_line" | jq -r '.usage.input_tokens // 0')
        echo "status=completed, tokens=${tokens}"
    elif [[ "$type" == "turn.failed" ]]; then
        echo "status=failed"
    else
        echo "status=unknown"
    fi
}

# ============================================================================
# TOOL EXECUTION
# ============================================================================

# Run Claude
log "Claude job started"
CLAUDE_OUTPUT=$("$CLAUDE_BIN" --output-format json -p "$HEALTH_CHECK_PROMPT" 2>&1)
CLAUDE_EXIT=$?
echo "$CLAUDE_OUTPUT"
log "Claude job completed (exit: $CLAUDE_EXIT) - $(parse_claude_json "$CLAUDE_OUTPUT")"
echo ""

# Run Codex
log "Codex job started"
CODEX_OUTPUT=$("$CODEX_BIN" exec --skip-git-repo-check --json "$HEALTH_CHECK_PROMPT" 2>&1)
CODEX_EXIT=$?
echo "$CODEX_OUTPUT"
log "Codex job completed (exit: $CODEX_EXIT) - $(parse_codex_jsonl "$CODEX_OUTPUT")"
echo ""

Make it executable:

chmod +x /home/code/bin/ai-cli-cron

Key Features

  1. Organized Configuration: All customizable settings are grouped at the top:

    • System paths and tool locations
    • Timezone settings
    • API keys
    • Health check prompt
  2. Clean Environment Setup: Cron runs with minimal environment variables. The script explicitly sets:

    • PATH to include Node.js, local binaries, and system paths
    • HOME, NVM_DIR for proper tool resolution
    • TZ for consistent timezone handling across all timestamp operations
    • MCP server API keys (optional)
  3. JSON Output with Summaries:

    • Both CLI tools output machine-readable JSON
    • Claude uses --output-format json for structured response data
    • Codex uses --json for JSONL (JSON Lines) event streaming
    • Helper functions parse JSON and display human-friendly summaries (status, duration, cost, tokens)
  4. Timestamp Logging: Every operation is logged with consistent timestamps in your configured timezone

  5. Health Checks: Simple "Ready" prompts verify both tools are operational

  6. Exit Code Tracking: Captures and logs exit codes to verify successful execution

  7. Dependency: Requires jq for JSON parsing (install: sudo apt install jq)

Cron Setup

My Schedule (Recommended for 5-Hour Windows)

I run the script 4 times per day at 5-hour intervals to maximize coverage throughout my working hours:

0 7,12,17,22 * * * /home/code/bin/ai-cli-cron >> /home/code/logs/ai-cli-cron.log 2>&1

Schedule breakdown:

  • 7:00 AM - Morning window (covers 7 AM - 12 PM work session)
  • 12:00 PM - Midday window (covers 12 PM - 5 PM work session)
  • 5:00 PM - Evening window (covers 5 PM - 10 PM work session)
  • 10:00 PM - Late night window (covers 10 PM - 3 AM if needed)

Why this works:

  • Each activation starts a new 5-hour usage window
  • 5-hour spacing ensures minimal overlap while maximizing availability
  • Covers typical work hours (7 AM - 10 PM) plus late night if needed
  • Predictable schedule - morning window > after-lunch window > evening window

Setting Up Your Cron

Edit your crontab:

crontab -e

Add the line above, adjusting times to match your work schedule. The format is:

minute hour * * * command

Tips for customizing:

  • Adjust times to start 30-60 minutes before you typically work
  • Use comma-separated hours (e.g., 7,12,17,22) for specific times
  • Ensure at least 5 hours between runs to establish new 5h session

Create Log Directory

mkdir -p /home/code/logs

Log Rotation (Optional)

To prevent logs from growing indefinitely, create /etc/logrotate.d/ai-cli-cron:

/home/code/logs/ai-cli-cron.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
}

Log Output Format

The script now outputs JSON for each tool, followed by a human-readable summary line. Example:

[2025-10-07 10:26:06] Claude job started
{"type":"result","subtype":"success","is_error":false,"duration_ms":2123,"result":"Ready","total_cost_usd":0.007160099999999999,...}
[2025-10-07 10:26:19] Claude job completed (exit: 0) - result=Ready, duration=2123ms, cost=$0.00716

[2025-10-07 10:26:19] Codex job started
{"type":"thread.started","thread_id":"0199bf7e-c1b1-7861-85b2-110c05bb9706"}
{"type":"turn.started"}
{"type":"item.completed","item":{"id":"item_0","type":"reasoning","text":"**Preparing initial response**"}}
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"Ready"}}
{"type":"turn.completed","usage":{"input_tokens":2937,"cached_input_tokens":2048,"output_tokens":7}}
[2025-10-07 10:26:22] Codex job completed (exit: 0) - status=completed, tokens=2937

Benefits:

  • JSON lines can be parsed by log analysis tools
  • Summary lines provide quick status at a glance
  • Full event data preserved for debugging

Monitoring Your Setup

With my schedule running 4 times daily (7 AM, 12 PM, 5 PM, 10 PM), you should see 8 job completions per day (4 Claude + 4 Codex).

Quick Health Check

View the most recent runs:

tail -50 /home/code/logs/ai-cli-cron.log | grep "job completed"

Expected output (shows both tools succeeded):

[2025-10-07 07:00:16] Claude job completed (exit: 0) - result=Ready, duration=2123ms, cost=$0.00716
[2025-10-07 07:00:19] Codex job completed (exit: 0) - status=completed, tokens=2937

Watch Live Execution

Monitor in real-time (useful during troubleshooting):

tail -f /home/code/logs/ai-cli-cron.log

Press Ctrl+C to exit.

Check Today's Activity

See all runs from today:

grep "$(date '+%Y-%m-%d')" /home/code/logs/ai-cli-cron.log | grep "job completed"

Verify Cron is Running

Check that your cron job is active:

# List your cron jobs
crontab -l

# Check cron service status
systemctl status cron

# View recent cron executions in system logs
grep "ai-cli-cron" /var/log/syslog | tail -20

If jobs aren't running, check:

  1. Cron service is active: sudo systemctl start cron
  2. Script is executable: chmod +x /home/code/bin/ai-cli-cron
  3. Log directory exists: mkdir -p /home/code/logs

Troubleshooting

Script Doesn't Run

  1. Check cron service: systemctl status cron
  2. Check crontab syntax: crontab -l
  3. Verify script permissions: ls -l /home/code/bin/ai-cli-cron
  4. Check cron logs: grep ai-cli-cron /var/log/syslog

Environment Issues

If tools can't be found:

  1. Verify paths in script match your installation:
    which claude
    which codex
    which node
  2. Update PATH variable in script accordingly

Timezone Problems

Ensure your system timezone matches the script:

timedatectl  # Check system timezone
export TZ=America/Denver  # Test in shell first

Understanding the 5-Hour Window Strategy

Some AI providers (like the one I use for Codex) enforce 5-hour usage windows where you can only make requests within a 5-hour period after your first request. This creates a problem: if you start working at unpredictable times, your usage window might expire during important work.

My solution: Run the script on a predictable schedule to pre-activate these windows at optimal times.

How My Schedule Works

By running at 7 AM, 12 PM, 5 PM, and 10 PM, I ensure:

  1. Coverage during work hours: No matter when I start working, a window is active or will activate soon
  2. Predictable availability: I know exactly when each 5-hour window starts and ends
  3. No wasted activations: 5-hour spacing means each run starts a fresh window without overlap
  4. Late-night flexibility: The 10 PM run covers unexpected late-night work sessions

Visual timeline:

7 AM activation  → covers 7 AM - 12 PM
12 PM activation → covers 12 PM - 5 PM
5 PM activation  → covers 5 PM - 10 PM
10 PM activation → covers 10 PM - 3 AM

This approach transforms an unpredictable constraint into a reliable, automated system.

Customizing for Your Setup

Before First Run

Edit the configuration section at the top of /home/code/bin/ai-cli-cron:

  1. Verify paths:

    which claude  # Should match CLAUDE_BIN
    which codex   # Should match CODEX_BIN
    which node    # Check Node version matches NODE_VERSION
  2. Update these variables:

    • USER_HOME - Your home directory (default: /home/code)
    • NODE_VERSION - Your Node.js version (find with: node --version)
    • TIMEZONE - Your timezone (list: timedatectl list-timezones)
    • FIRECRAWL_API_KEY, OPENMEMORY_API_KEY - Your actual API keys
  3. Adjust the schedule in your crontab to match your work hours:

    crontab -e
    # Change 7,12,17,22 to your preferred hours

Adding More Tools

To add additional AI CLI tools, insert in the "TOOL EXECUTION" section:

# Run YourTool
log "YourTool job started"
TOOL_OUTPUT=$("$YOUR_TOOL_BIN" --json "health check prompt" 2>&1)
TOOL_EXIT=$?
echo "$TOOL_OUTPUT"
log "YourTool job completed (exit: $TOOL_EXIT)"
echo ""

Optional: Email Notifications

Add to your crontab (requires mail configured):

MAILTO=[email protected]
0 7,12,17,22 * * * /home/code/bin/ai-cli-cron >> /home/code/logs/ai-cli-cron.log 2>&1

Benefits

Predictable usage windows starting at optimal times

Automated health checks verify tools are operational

Machine-readable output - JSON/JSONL format for log analysis

Human-friendly summaries - quick status overview without parsing JSON

Comprehensive logging with consistent timezone-aware timestamps

Cost and token tracking - automatically extracted and logged

No manual intervention needed

Easy to customize - all configuration at the top of the script

Clean and maintainable - streamlined, professional code

Multiple tools managed in one script

Shareable - clearly documented configuration section


Note: Adjust paths, timezones, and schedules to match your specific setup and workflow needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment