Architecture
Architecture
Section titled “Architecture”ralph is designed to be simple, composable, and tool-agnostic. This document explains how it works under the hood.
High-Level Overview
Section titled “High-Level Overview”┌─────────────────────────────────────────────────────────────────┐│ ralph │├─────────────────────────────────────────────────────────────────┤│ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ Config │──▶│ Loop │──▶│ Adapter │──▶│ Complete │ ││ │ Loader │ │ Engine │ │ Runner │ │ Checker │ ││ └──────────┘ └────┬─────┘ └──────────┘ └──────────┘ ││ │ ││ ▼ ││ ┌──────────┐ ││ │ Hooks │ ││ │ Executor │ ││ └──────────┘ ││ │└─────────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ AI Tool (External) ││ ││ ┌────────┐ ┌────────┐ ┌────────┐ ││ │ Claude │ │OpenCode│ │ Gemini │ ││ │ Code │ │ │ │ (dev) │ ││ └────────┘ └────────┘ └────────┘ ││ │└─────────────────────────────────────────────────────────────────┘Core Components
Section titled “Core Components”Config Loader
Section titled “Config Loader”Loads and validates configuration from multiple sources.
Priority (lowest to highest):1. Default values2. ~/.ralph/config.toml (user config)3. .ralph/config.toml (project config)4. CLI argumentsLoop Engine
Section titled “Loop Engine”The heart of ralph. Manages the iteration lifecycle.
// Simplified loop logicasync function runLoop(config: Config): Promise<Result> { let iteration = 0;
while (iteration < config.maxIterations) { iteration++;
// Pre-iteration hooks await runHooks('ralph_loop_start', { iteration });
// Run the AI tool const result = await runAdapter(config.adapter, config.prompt);
// Post-iteration hooks await runHooks('ralph_loop_end', { iteration });
// Check for completion marker if (result.output.includes('<promise>COMPLETE</promise>')) { await runHooks('ralph_complete', { iteration }); return { success: true, iterations: iteration }; } }
await runHooks('ralph_max_iterations', { iteration }); return { success: false, reason: 'maxIterations' };}Adapter Runner
Section titled “Adapter Runner”Abstracts the interface to different AI tools.
interface Adapter { name: string; supportedFormats: OutputFormat[]; buildArgs(prompt: string): string[]; checkAvailability(): boolean;}Each supported tool has an adapter:
ClaudeAdapter— for Claude CodeOpenCodeAdapter— for OpenCodeGeminiAdapter— for Gemini (under development)
Completion Checker
Section titled “Completion Checker”Evaluates if the task is complete after each iteration.
function checkCompletion(output: string): boolean { return output.includes('<promise>COMPLETE</promise>');}Hooks Executor
Section titled “Hooks Executor”Runs shell commands at key points in the lifecycle.
type HookPoint = | 'ralph_start' // Before first iteration | 'ralph_loop_start' // Before each iteration | 'ralph_loop_end' // After each iteration | 'ralph_complete' // When task completes | 'ralph_max_iterations'; // When limit reachedData Flow
Section titled “Data Flow”1. Initialization
Section titled “1. Initialization”CLI args → Config Loader → Validated Config │ ├── Load prompt file ├── Initialize adapter └── Run ralph_start hook2. Each Iteration
Section titled “2. Each Iteration”Loop Engine │ ├── 1. ralph_loop_start hook │ ├── 2. Adapter Runner │ │ │ ├── Start AI process │ ├── Stream output │ ├── Track tokens │ └── Wait for exit │ ├── 3. ralph_loop_end hook │ ├── 4. Completion Checker │ │ │ └── Check for <promise>COMPLETE</promise> │ └── 5. Decision: Continue or Exit3. Completion
Section titled “3. Completion”Completion marker found │ ├── Run ralph_complete hook ├── Display summary (tokens, cost, iterations) └── Exit with code 0Adapters
Section titled “Adapters”Claude Adapter
Section titled “Claude Adapter”class ClaudeAdapter implements Adapter { name = 'claude'; supportedFormats = ['stream-json', 'text'];
buildArgs(prompt: string): string[] { return [ '--permission-mode', 'acceptEdits', '--prompt', prompt ]; }}OpenCode Adapter
Section titled “OpenCode Adapter”class OpenCodeAdapter implements Adapter { name = 'opencode'; supportedFormats = ['opencode-json', 'text'];
buildArgs(prompt: string): string[] { return ['run', '--prompt', prompt]; }}Project Structure
Section titled “Project Structure”project/├── .ralph/│ └── config.toml # Configuration└── .plans/ ├── prd.json # Feature requirements ├── PROMPT.md # System prompt └── progress.txt # Learning logConfiguration Schema
Section titled “Configuration Schema”interface Config { adapter: 'claude' | 'opencode' | 'gemini'; plansDir: string; maxIterations: number; verbose: boolean; tui: boolean; showUsage: boolean; hooks: { ralph_start?: string; ralph_loop_start?: string; ralph_loop_end?: string; ralph_complete?: string; ralph_max_iterations?: string; };}