Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions ANALYSIS_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Analysis: Supporting Gemini & GitHub Copilot in Ralph

## 1. Current Architecture vs. Required Architecture

Currently, **Ralph is a Supervisor**, not an Agent.
* **Ralph's Job:** It watches `claude-code`, ensures it doesn't get stuck, manages its rate limits, and decides when to stop.
* **Claude Code's Job:** It acts as the **Agent**. It reads files, thinks, decides to edit a file, runs `sed`/`git`, checks the result, and iterates.

**The "Agent Gap":**
Standard CLIs for Gemini (`gemini`) and GitHub Copilot (`gh copilot`) are primarily **Text-In/Text-Out** interfaces. They generate code snippets or explanations but **do not** natively execute tools (like writing files or running tests) in a continuous loop on your terminal.

To support them, Ralph cannot just "wrap" them. Ralph must **become the Agent Runtime**.

## 2. Transformation Required

To support non-agentic CLIs (like Gemini/Copilot), Ralph must expand its responsibilities:

| Feature | Current (Claude Code) | Proposed (Gemini/Copilot) |
| :--- | :--- | :--- |
| **Thinking** | Claude Code | Gemini / Copilot |
| **Tool Execution** | **Claude Code** | **Ralph (New)** |
| **Loop Management** | Ralph | Ralph |
| **State/Memory** | Claude Code | Ralph (New) |

## 3. Key Components to Build

### A. Provider Abstraction Layer (`lib/providers/`)
We need a standard interface for interacting with different AIs.
* `init_session()`: Start a conversation.
* `send_message(prompt, context)`: Send user input + file context.
* `parse_response(output)`: Extract text content AND **Tool Calls**.

### B. Tool Execution Engine (`lib/tool_executor.sh`)
Since Gemini/Copilot won't edit files themselves, Ralph must do it.
* **Protocol:** Define a format for the AI to request actions (e.g., XML tags like `<write_file path="...">content</write_file>` or JSON function calls).
* **Executor:** A script that parses these requests and runs:
* `write_file`: create/update files.
* `run_command`: execute bash commands (with safety checks).
* `read_file`: read file content to feed back to the AI.

### C. Prompt Engineering (`templates/prompts/`)
* **Claude:** Uses its built-in system prompt.
* **Gemini/Copilot:** We must inject a **System Prompt** that teaches them:
* "You are an autonomous coding agent."
* "You have access to these tools: read_file, write_file..."
* "To use a tool, output this specific format..."

## 4. Implementation Steps

1. **Refactor `ralph_loop.sh`**:
* Replace direct `claude` calls with `provider.send_message`.
* Add a check: Does the provider handle tools?
* **Yes (Claude):** Do nothing (current behavior).
* **No (Gemini):** Parse output -> Run `ToolExecutor` -> Feed result back to `provider.send_message`.

2. **Create Provider Adapters**:
* `lib/providers/claude.sh`: Wraps existing logic.
* `lib/providers/gemini.sh`: Wraps `gemini-cli` (or API curl calls), handles JSON parsing.
* `lib/providers/copilot.sh`: Wraps `gh copilot suggest` (more complex due to interactive nature, might need `expect` or API usage).

3. **Build the Runtime**:
* Implement `lib/tool_executor.sh`.
* Implement "Tool Feedback Loop" in `ralph_loop.sh`.

## 5. Conclusion
Making Ralph compatible with Gemini/Copilot is a **major architectural upgrade**. It moves Ralph from being a "Process Manager" to being a "ReAct (Reason+Act) Agent Framework".
50 changes: 13 additions & 37 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,27 @@ See [README.md](README.md) for version info, changelog, and user documentation.

## Core Architecture

The system consists of four main bash scripts and a modular library system:
The system consists of a modular provider architecture and a core loop:

### Main Scripts

1. **ralph_loop.sh** - The main autonomous loop that executes Claude Code repeatedly
1. **ralph_loop.sh** - The main autonomous loop that executes the selected AI provider
2. **ralph_monitor.sh** - Live monitoring dashboard for tracking loop status
3. **setup.sh** - Project initialization script for new Ralph projects
4. **create_files.sh** - Bootstrap script that creates the entire Ralph system
5. **ralph_import.sh** - PRD/specification import tool that converts documents to Ralph format
- Uses modern Claude Code CLI with `--output-format json` for structured responses
- Implements `detect_response_format()` and `parse_conversion_response()` for JSON parsing
- Backward compatible with older CLI versions (automatic text fallback)
6. **ralph_enable.sh** - Interactive wizard for enabling Ralph in existing projects
- Multi-step wizard with environment detection, task source selection, configuration
- Imports tasks from beads, GitHub Issues, or PRD documents
- Generates `.ralphrc` project configuration file
7. **ralph_enable_ci.sh** - Non-interactive version for CI/automation
- Same functionality as interactive version with CLI flags
- JSON output mode for machine parsing
- Exit codes: 0 (success), 1 (error), 2 (already enabled)
4. **ralph_import.sh** - PRD/specification import tool (uses Claude)
5. **ralph_enable.sh** - Interactive wizard for enabling Ralph in existing projects (includes Provider selection)

### Library Components (lib/)

The system uses a modular architecture with reusable components in the `lib/` directory:

1. **lib/circuit_breaker.sh** - Circuit breaker pattern implementation
- Prevents runaway loops by detecting stagnation
- Three states: CLOSED (normal), HALF_OPEN (monitoring), OPEN (halted)
- Configurable thresholds for no-progress and error detection
- Automatic state transitions and recovery

2. **lib/response_analyzer.sh** - Intelligent response analysis
- Analyzes Claude Code output for completion signals
- **JSON output format detection and parsing** (with text fallback)
- Supports both flat JSON format and Claude CLI format (`result`, `sessionId`, `metadata`)
- Extracts structured fields: status, exit_signal, work_type, files_modified
- **Session management**: `store_session_id()`, `get_last_session_id()`, `should_resume_session()`
- Automatic session persistence to `.ralph/.claude_session_id` file with 24-hour expiration
- Session lifecycle: `get_session_id()`, `reset_session()`, `log_session_transition()`, `init_session_tracking()`
- Session history tracked in `.ralph/.ralph_session_history` (last 50 transitions)
- Session auto-reset on: circuit breaker open, manual interrupt, project completion
- Detects test-only loops and stuck error patterns
- Two-stage error filtering to eliminate false positives
- Multi-line error matching for accurate stuck loop detection
- Confidence scoring for exit decisions
1. **lib/providers/base.sh** - Provider loader and abstraction layer
2. **lib/providers/claude.sh** - Claude Code CLI integration
3. **lib/providers/gemini.sh** - Google Gemini CLI integration (Agent Mode)
4. **lib/providers/copilot.sh** - GitHub Copilot CLI integration (Agent Mode)
5. **lib/session_manager.sh** - Centralized session lifecycle management
6. **lib/circuit_breaker.sh** - Circuit breaker pattern implementation
7. **lib/response_analyzer.sh** - Intelligent response analysis (shared across providers)
8. **lib/tool_executor.sh** - Generic tool execution runtime for non-agentic providers (Legacy/Future)
9. **lib/date_utils.sh** & **lib/timeout_utils.sh** - Cross-platform utilities

3. **lib/date_utils.sh** - Cross-platform date utilities
- ISO timestamp generation for logging
Expand Down
65 changes: 65 additions & 0 deletions IMPLEMENTATION_PLAN_MULTI_PROVIDER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Implementation Plan: Multi-Provider Support

This plan outlines the steps to transform Ralph into a multi-provider agent framework.

## Phase 1: Modularization (The "Socket")
**Goal:** Abstract the hardcoded `claude` commands into a plugin system.

- [ ] **Create `lib/providers/` directory.**
- [ ] **Create `lib/providers/base.sh`:** Define the interface (functions `provider_init`, `provider_chat`, `provider_parse`).
- [ ] **Create `lib/providers/claude.sh`:** Move current `claude` CLI logic here.
- [ ] **Update `ralph_loop.sh`**:
- Load the selected provider script based on `RALPH_PROVIDER` env var.
- Replace `execute_claude_code` with generic `execute_provider_loop`.

## Phase 2: The Agent Runtime (The "Brain")
**Goal:** Enable Ralph to execute tools for "dumb" LLMs.

- [ ] **Design Tool Protocol:** Define how LLMs should request actions (e.g., Markdown code blocks or XML).
- Example:
```xml
<tool name="write_file">
<path>src/main.py</path>
<content>print("hello")</content>
</tool>
```
- [ ] **Create `lib/tool_executor.sh`:**
- Function `extract_tool_calls(llm_output)`
- Function `execute_tool(name, args)`
- Safety checks (prevent `rm -rf /`).
- [ ] **Create `templates/system_prompts/generic_agent.md`:**
- A master prompt that explains available tools and the output format to the LLM.

## Phase 3: Gemini Adapter
**Goal:** Connect Google Gemini.

- [ ] **Prerequisite:** Install `gemini` CLI or use `curl` with API Key.
- [ ] **Create `lib/providers/gemini.sh`:**
- Implement `provider_chat`:
- Construct payload with `templates/system_prompts/generic_agent.md` + User Prompt + History.
- Call API.
- Implement `provider_parse`:
- Extract text.
- Detect if tool calls are present.

## Phase 4: GitHub Copilot Adapter
**Goal:** Connect GitHub Copilot.

- [ ] **Investigation:** Determine best CLI entry point (`gh copilot` vs raw API).
- [ ] **Create `lib/providers/copilot.sh`:**
- Similar adaptation as Gemini.
- *Note:* Copilot often refuses "system prompts" in CLI. May require "User" role spoofing.

## Phase 5: Configuration Update
**Goal:** User-friendly switching.

- [ ] **Update `ralph-setup` / `ralph-enable`:**
- Ask "Which AI provider do you want to use?"
- Generate `.ralphrc` with `RALPH_PROVIDER=gemini`.
- [ ] **Update `.ralphrc` template:** Add provider configuration sections.

## Estimated Effort
- **Phase 1:** 2 days (Refactoring)
- **Phase 2:** 3-4 days (Security & Logic)
- **Phase 3:** 1-2 days (Integration)
- **Total:** ~1-2 weeks for a robust MVP.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,26 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t

## Project Status

**Version**: v0.11.4 - Active Development
**Version**: v0.12.0 - Multi-Provider Support
**Core Features**: Working and tested
**Test Coverage**: 484 tests, 100% pass rate
**Providers**: Claude Code (Anthropic), Google Gemini, GitHub Copilot

### What's Working Now
- **Multi-Provider Support**: Switch between Claude, Gemini, and Copilot
- **Agentic Loops for all providers**: Native support for agentic CLIs
- Autonomous development loops with intelligent exit detection
- **Dual-condition exit gate**: Requires BOTH completion indicators AND explicit EXIT_SIGNAL
- Rate limiting with hourly reset (100 calls/hour, configurable)
- Circuit breaker with advanced error detection (prevents runaway loops)
- Response analyzer with semantic understanding and two-stage error filtering
- **JSON output format support with automatic fallback to text parsing**
- **Session continuity with `--resume` flag for context preservation (no session hijacking)**
- **Session expiration with configurable timeout (default: 24 hours)**
- **Session continuity with `--resume` flag for context preservation**
- **Modern CLI flags: `--output-format`, `--allowed-tools`, `--no-continue`**
- **Interactive project enablement with `ralph-enable` wizard**
- **`.ralphrc` configuration file for project settings**
- **Live streaming output with `--live` flag for real-time Claude Code visibility**
- **Live streaming output with `--live` flag (Claude only)**
- Multi-line error matching for accurate stuck loop detection
- 5-hour API limit handling with user prompts
- 5-hour API limit handling with user prompts (Claude only)
- tmux integration for live monitoring
- PRD import functionality
- **CI/CD pipeline with GitHub Actions**
Expand Down
9 changes: 8 additions & 1 deletion lib/enable_core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -662,13 +662,15 @@ FIXPLANEOF
# $1 (project_name) - Project name
# $2 (project_type) - Project type
# $3 (task_sources) - Task sources (local, beads, github)
# $4 (provider) - AI Provider (claude, gemini, copilot)
#
# Outputs to stdout
#
generate_ralphrc() {
local project_name="${1:-$(basename "$(pwd)")}"
local project_type="${2:-unknown}"
local task_sources="${3:-local}"
local provider="${4:-claude}"

cat << RALPHRCEOF
# .ralphrc - Ralph project configuration
Expand All @@ -679,6 +681,10 @@ generate_ralphrc() {
PROJECT_NAME="${project_name}"
PROJECT_TYPE="${project_type}"

# AI Provider Configuration
# Options: claude, gemini, copilot
RALPH_PROVIDER="${provider}"

# Loop settings
MAX_CALLS_PER_HOUR=100
CLAUDE_TIMEOUT_MINUTES=15
Expand Down Expand Up @@ -729,6 +735,7 @@ enable_ralph_in_directory() {
local project_name="${ENABLE_PROJECT_NAME:-}"
local project_type="${ENABLE_PROJECT_TYPE:-}"
local task_content="${ENABLE_TASK_CONTENT:-}"
local provider="${ENABLE_PROVIDER:-claude}"

# Check existing state (use || true to prevent set -e from exiting)
check_existing_ralph || true
Expand Down Expand Up @@ -789,7 +796,7 @@ enable_ralph_in_directory() {

# Generate .ralphrc
local ralphrc_content
ralphrc_content=$(generate_ralphrc "$project_name" "$DETECTED_PROJECT_TYPE" "$task_sources")
ralphrc_content=$(generate_ralphrc "$project_name" "$DETECTED_PROJECT_TYPE" "$task_sources" "$provider")
safe_create_file ".ralphrc" "$ralphrc_content"

enable_log "SUCCESS" "Ralph enabled successfully!"
Expand Down
52 changes: 52 additions & 0 deletions lib/providers/base.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash
# Base Provider Loader for Ralph

# Load the configured provider
load_provider() {
local raw_provider="${RALPH_PROVIDER:-claude}"

# Sanitize and validate provider name to prevent path traversal
if [[ ! "$raw_provider" =~ ^[a-zA-Z0-9_-]+$ ]]; then
log_status "ERROR" "Invalid AI provider name: $raw_provider (only alphanumeric, underscores, and hyphens allowed)"
exit 1
fi

local provider_name="$raw_provider"
local provider_script="$RALPH_HOME/lib/providers/${provider_name}.sh"

# Fallback to local path if RALPH_HOME not set or script not found
if [[ ! -f "$provider_script" ]]; then
provider_script="$(dirname "${BASH_SOURCE[0]}")/${provider_name}.sh"
fi

if [[ -f "$provider_script" ]]; then
source "$provider_script"
log_status "INFO" "Loaded AI provider: $provider_name"
else
log_status "ERROR" "AI provider script not found: $provider_script"
exit 1
fi
}

# Helper to build loop context shared by all providers
build_loop_context() {
local loop_count=$1
local context="Loop #$loop_count. "

if [[ -f "$RALPH_DIR/fix_plan.md" ]]; then
local incomplete_tasks=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || true)
context+="Remaining tasks: ${incomplete_tasks:-0}. "
fi

if [[ -f "$RALPH_DIR/.circuit_breaker_state" ]]; then
local cb_state=$(jq -r '.state // "UNKNOWN"' "$RALPH_DIR/.circuit_breaker_state" 2>/dev/null)
[[ "$cb_state" != "CLOSED" && -n "$cb_state" && "$cb_state" != "null" ]] && context+="Circuit breaker: $cb_state. "
fi

if [[ -f "$RALPH_DIR/.response_analysis" ]]; then
local prev_summary=$(jq -r '.analysis.work_summary // ""' "$RALPH_DIR/.response_analysis" 2>/dev/null | head -c 200)
[[ -n "$prev_summary" && "$prev_summary" != "null" ]] && context+="Previous: $prev_summary"
fi

echo "${context:0:500}"
}
Loading
Loading