refactor: split god files into focused modules (phase 4, steps 2-4)

Split 3 large files into 11 focused sub-modules via composition:

ralph-tracker.ts (3,868 → ~2,400 LOC):
- ralph-plan-tracker.ts: plan task tracking, checkpoints, history
- ralph-fix-plan-watcher.ts: @fix_plan.md file watching
- ralph-stall-detector.ts: iteration stall detection
- ralph-status-parser.ts: RALPH_STATUS block parsing, circuit breaker

respawn-controller.ts (3,611 → ~3,200 LOC):
- respawn-patterns.ts: pure pattern detection functions
- respawn-adaptive-timing.ts: adaptive timing with percentile calc
- respawn-metrics.ts: cycle metrics tracking + aggregation
- respawn-health.ts: pure health scoring functions

session.ts (2,418 → ~1,800 LOC):
- session-cli-builder.ts: CLI argument construction
- session-auto-ops.ts: auto-compact/clear automation
- session-task-cache.ts: task description LRU cache

All external APIs preserved via delegation. Events forwarded
through parent classes. Zero behavioral changes — all 436 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-03-01 04:04:35 +01:00
co-authored by Claude Opus 4.6
parent 8d2d51e8f0
commit e7a9cbe442
16 changed files with 4429 additions and 2937 deletions
+131
View File
@@ -0,0 +1,131 @@
/**
* @fileoverview Pure utility functions for terminal pattern detection in respawn controller.
*
* Extracted from respawn-controller.ts for modularity. These are stateless functions
* and constants used to detect completion messages, working patterns, and token counts
* in terminal output.
*
* @module respawn-patterns
*/
import { TOKEN_PATTERN } from './utils/index.js';
// ========== Constants ==========
/**
* Pattern to detect completion messages from Claude Code.
* Requires "Worked for" prefix to avoid false positives from bare time durations
* in regular text (e.g., "wait for 5s", "run for 2m").
*
* Matches: "Worked for 2m 46s", "Worked for 46s", "Worked for 1h 2m 3s"
* Does NOT match: "wait for 5s", "run for 2m", "for 3s the system..."
*/
const COMPLETION_TIME_PATTERN = /\bWorked\s+for\s+\d+[hms](\s*\d+[hms])*/i;
/**
* Patterns indicating Claude is ready for input (legacy fallback).
* Used as secondary signals, not primary detection.
*/
export const PROMPT_PATTERNS = [
'❯', // Standard prompt
'\u276f', // Unicode variant
'⏵', // Claude Code prompt variant
];
/**
* Patterns indicating Claude is actively working.
* When detected, resets all idle detection timers.
* Note: ✻ and ✽ removed - they appear in completion messages too.
*/
export const WORKING_PATTERNS = [
'Thinking',
'Writing',
'Reading',
'Running',
'Searching',
'Editing',
'Creating',
'Deleting',
'Analyzing',
'Executing',
'Synthesizing',
'Brewing', // Claude's processing indicators
'Compiling',
'Building',
'Installing',
'Fetching',
'Downloading',
'Processing',
'Generating',
'Loading',
'Starting',
'Updating',
'Checking',
'Validating',
'Testing',
'Formatting',
'Linting',
'⠋',
'⠙',
'⠹',
'⠸',
'⠼',
'⠴',
'⠦',
'⠧',
'⠇',
'⠏', // Spinner chars
'◐',
'◓',
'◑',
'◒', // Alternative spinners
'⣾',
'⣽',
'⣻',
'⢿',
'⡿',
'⣟',
'⣯',
'⣷', // Braille spinners
];
/**
* Check if data contains a completion message pattern.
* Matches "Worked for Xh Xm Xs" time duration patterns.
*
* @param data - Raw terminal output data
* @returns True if completion message pattern is found
*/
export function isCompletionMessage(data: string): boolean {
return COMPLETION_TIME_PATTERN.test(data);
}
/**
* Check if a rolling window of terminal output contains working patterns.
* The rolling window catches patterns split across chunks (e.g., "Thin" + "king").
*
* @param window - Rolling window of recent terminal output (already includes current data)
* @returns True if any working pattern is found in the window
*/
export function hasWorkingPattern(window: string): boolean {
return WORKING_PATTERNS.some((pattern) => window.includes(pattern));
}
/**
* Extract token count from data if present.
* Parses patterns like "123.4k tokens" or "1.5M tokens".
*
* @param data - Raw terminal output data
* @returns Parsed token count, or null if no token pattern found
*/
export function extractTokenCount(data: string): number | null {
const match = data.match(TOKEN_PATTERN);
if (!match) return null;
let count = parseFloat(match[1]);
const suffix = match[2]?.toLowerCase();
if (suffix === 'k') count *= 1000;
else if (suffix === 'm') count *= 1000000;
return Math.round(count);
}