From 7f436a467b669a98bfa3e408bf307d989930cbe6 Mon Sep 17 00:00:00 2001 From: Corey Cauble Date: Fri, 9 Jan 2026 12:34:57 -0800 Subject: [PATCH 1/3] Implement reset time parsing for auto-continue Added functionality to parse and handle reset time for auto-continue based on agent response when Limit is reached for Claude Code SDK --- agent.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/agent.py b/agent.py index e4d0de4..d86bc37 100644 --- a/agent.py +++ b/agent.py @@ -7,17 +7,20 @@ Core agent interaction functions for running autonomous coding sessions. import asyncio import io +import re import sys +from datetime import datetime, timedelta from pathlib import Path from typing import Optional +from zoneinfo import ZoneInfo from claude_agent_sdk import ClaudeSDKClient # Fix Windows console encoding for Unicode characters (emoji, etc.) # Without this, print() crashes when Claude outputs emoji like ✅ if sys.platform == "win32": - sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') - sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace") from client import create_client from progress import has_features, print_progress_summary, print_session_header @@ -195,9 +198,60 @@ async def run_autonomous_agent( # Handle status if status == "continue": - print(f"\nAgent will auto-continue in {AUTO_CONTINUE_DELAY_SECONDS}s...") + delay_seconds = AUTO_CONTINUE_DELAY_SECONDS + target_time_str = None + if response.lower().strip().startswith("limit reached"): + print("Agent indicated limit reached.") + + # Try to parse reset time from response + match = re.search( + r"resets (\d+)(?::(\d+))?(am|pm) \(([^)]+)\)", + response, + re.IGNORECASE, + ) + if match: + hour = int(match.group(1)) + minute = int(match.group(2)) if match.group(2) else 0 + period = match.group(3).lower() + tz_name = match.group(4) + + # Convert to 24-hour format + if period == "pm" and hour != 12: + hour += 12 + elif period == "am" and hour == 12: + hour = 0 + + try: + tz = ZoneInfo(tz_name) + now = datetime.now(tz) + target = now.replace( + hour=hour, minute=minute, second=0, microsecond=0 + ) + + # If target time has already passed today, wait until tomorrow + if target <= now: + target += timedelta(days=1) + + delta = target - now + delay_seconds = delta.total_seconds() + target_time_str = target.strftime("%B %d, %Y at %I:%M %p %Z") + + except Exception as e: + print(f"Error parsing reset time: {e}, using default delay") + + if target_time_str: + print( + f"\nAgent will auto-continue in {delay_seconds:.0f}s ({target_time_str})...", + flush=True, + ) + else: + print( + f"\nAgent will auto-continue in {delay_seconds:.0f}s...", flush=True + ) + print_progress_summary(project_dir) - await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS) + sys.stdout.flush() + await asyncio.sleep(delay_seconds) elif status == "error": print("\nSession encountered an error") From 2b2e28a2c5e7670181b3d13259827352b0162ae1 Mon Sep 17 00:00:00 2001 From: Corey Cauble Date: Fri, 9 Jan 2026 14:30:34 -0800 Subject: [PATCH 2/3] Enhance limit reached message for better context and display in the UI --- agent.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agent.py b/agent.py index d86bc37..19f1560 100644 --- a/agent.py +++ b/agent.py @@ -200,8 +200,9 @@ async def run_autonomous_agent( if status == "continue": delay_seconds = AUTO_CONTINUE_DELAY_SECONDS target_time_str = None + if response.lower().strip().startswith("limit reached"): - print("Agent indicated limit reached.") + print("Claude Agent SDK indicated limit reached.") # Try to parse reset time from response match = re.search( @@ -241,7 +242,7 @@ async def run_autonomous_agent( if target_time_str: print( - f"\nAgent will auto-continue in {delay_seconds:.0f}s ({target_time_str})...", + f"\nClaude Code Limit Reached. Agent will auto-continue in {delay_seconds:.0f}s ({target_time_str})...", flush=True, ) else: @@ -249,8 +250,8 @@ async def run_autonomous_agent( f"\nAgent will auto-continue in {delay_seconds:.0f}s...", flush=True ) + sys.stdout.flush() # this should allow the pause to be displayed before sleeping print_progress_summary(project_dir) - sys.stdout.flush() await asyncio.sleep(delay_seconds) elif status == "error": From 9c07dd72db9698aed236f11e8da041cebda794e8 Mon Sep 17 00:00:00 2001 From: Corey Cauble Date: Fri, 9 Jan 2026 14:35:20 -0800 Subject: [PATCH 3/3] Fixed issues requested by coderabbitai Applied Fixes More flexible string matching: Changed from response.lower().strip().startswith("limit reached") to "limit reached" in response.lower() to handle cases where the message has prefix text or variations in whitespace. Improved regex pattern: Updated to r"(?i)\bresets(?:\s+at)?\s+(\d+)(?::(\d+))?\s*(am|pm)\s*\(([^)]+)\)" which now handles: Optional "at" after "resets" (e.g., "resets at 3pm" or "resets 3pm") Flexible whitespace around components Word boundaries to prevent partial matches Timezone sanitization: Added .strip() to tz_name = match.group(4).strip() to remove any leading/trailing whitespace that could cause ZoneInfo() to fail. Safety clamp: Added delay_seconds = min(delta.total_seconds(), 24 * 60 * 60) to ensure the delay never exceeds 24 hours, preventing the agent from being stuck waiting for extremely long periods. --- agent.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/agent.py b/agent.py index 19f1560..50edc46 100644 --- a/agent.py +++ b/agent.py @@ -201,20 +201,19 @@ async def run_autonomous_agent( delay_seconds = AUTO_CONTINUE_DELAY_SECONDS target_time_str = None - if response.lower().strip().startswith("limit reached"): + if "limit reached" in response.lower(): print("Claude Agent SDK indicated limit reached.") # Try to parse reset time from response match = re.search( - r"resets (\d+)(?::(\d+))?(am|pm) \(([^)]+)\)", + r"(?i)\bresets(?:\s+at)?\s+(\d+)(?::(\d+))?\s*(am|pm)\s*\(([^)]+)\)", response, - re.IGNORECASE, ) if match: hour = int(match.group(1)) minute = int(match.group(2)) if match.group(2) else 0 period = match.group(3).lower() - tz_name = match.group(4) + tz_name = match.group(4).strip() # Convert to 24-hour format if period == "pm" and hour != 12: @@ -234,7 +233,9 @@ async def run_autonomous_agent( target += timedelta(days=1) delta = target - now - delay_seconds = delta.total_seconds() + delay_seconds = min( + delta.total_seconds(), 24 * 60 * 60 + ) # Clamp to 24 hours max target_time_str = target.strftime("%B %d, %Y at %I:%M %p %Z") except Exception as e: