All skills
Skillintermediate

ralph-adapters

CLI backend integrations for various AI tools.

Claude Code Knowledge Pack7/10/2026

Overview

ralph-adapters

CLI backend integrations for various AI tools.

Overview

ralph-adapters provides:

  • Backend definitions for Claude, Kiro, Gemini, and more
  • PTY-based execution for real-time output
  • Stream handlers for different output modes
  • Auto-detection of available backends

Supported Backends

BackendCLIStatus
Claude CodeclaudeFull support
KirokiroFull support
Gemini CLIgeminiFull support
CodexcodexFull support
AmpampFull support
Copilot CLIcopilotFull support
OpenCodeopencodeFull support

Key Components

CliBackend

Backend definition.

pub struct CliBackend {
    pub name: String,
    pub command: String,
    pub prompt_mode: PromptMode,
    pub output_format: OutputFormat,
}

pub enum PromptMode {
    Arg,    // cli -p "prompt"
    Stdin,  // echo "prompt" | cli
}

pub enum OutputFormat {
    Text,
    Ndjson,
    Custom(Box<dyn Parser>),
}

Built-in backends:

use ralph_adapters::backends;

let claude = backends::claude();
let kiro = backends::kiro();
let gemini = backends::gemini();

Auto-Detection

Detect available backends.

use ralph_adapters::auto_detect;

// Get first available backend
let backend = auto_detect::detect()?;

// Get all available backends
let backends = auto_detect::detect_all();

// Check specific backend
let available = auto_detect::is_available("claude");

Detection order:

  1. Claude
  2. Kiro
  3. Gemini
  4. Codex
  5. Amp
  6. Copilot
  7. OpenCode

PtyExecutor

PTY-based execution for real-time output.

use ralph_adapters::pty_executor::PtyExecutor;

let executor = PtyExecutor::new();

// Execute with stream handler
let result = executor.execute(
    &backend,
    &prompt,
    Box::new(ConsoleStreamHandler::new()),
).await?;

StreamHandler

Handle output from backends.

pub trait StreamHandler: Send {
    fn on_output(&mut self, chunk: &str);
    fn on_complete(&mut self);
    fn on_error(&mut self, error: &str);
}

Built-in handlers:

use ralph_adapters::stream_handler::*;

// Console output (plain)
let handler = ConsoleStreamHandler::new();

// Pretty output (formatted)
let handler = PrettyStreamHandler::new();

// TUI mode
let handler = TuiStreamHandler::new(tx);

// Quiet (CI mode)
let handler = QuietStreamHandler::new();

Claude Stream Parser

Parse Claude's NDJSON streaming output.

use ralph_adapters::claude_stream::ClaudeStreamParser;

let parser = ClaudeStreamParser::new();

// Parse chunk
let events = parser.parse_chunk(chunk)?;

for event in events {
    match event {
        ClaudeEvent::Text(text) => println!("{}", text),
        ClaudeEvent::ToolCall(call) => println!("Tool: {}", call.name),
        ClaudeEvent::ToolResult(result) => println!("Result: {}", result),
        ClaudeEvent::Complete => break,
    }
}

Custom Backends

Create custom backend definitions.

use ralph_adapters::{CliBackend, PromptMode, OutputFormat};

let my_backend = CliBackend {
    name: "my-ai".to_string(),
    command: "my-ai-cli".to_string(),
    prompt_mode: PromptMode::Arg,
    output_format: OutputFormat::Text,
};

Custom Stream Handlers

Implement the StreamHandler trait.

use ralph_adapters::StreamHandler;

struct MyHandler {
    buffer: String,
}

impl StreamHandler for MyHandler {
    fn on_output(&mut self, chunk: &str) {
        self.buffer.push_str(chunk);
        // Custom processing
    }

    fn on_complete(&mut self) {
        println!("Done: {}", self.buffer);
    }

    fn on_error(&mut self, error: &str) {
        eprintln!("Error: {}", error);
    }
}

Error Types

pub enum AdapterError {
    BackendNotFound(String),
    ExecutionError(String),
    ParseError(String),
    IoError(std::io::Error),
}

Feature Flags

FlagDescription
defaultAll backends
claudeClaude support only
kiroKiro support only

Example: Execute Backend

use ralph_adapters::{backends, PtyExecutor, ConsoleStreamHandler};

#[tokio::main]
async fn main() -> Result<()> {
    // Get Claude backend
    let backend = backends::claude();

    // Create executor
    let executor = PtyExecutor::new();

    // Execute with prompt
    let result = executor.execute(
        &backend,
        "Write a hello world function",
        Box::new(ConsoleStreamHandler::new()),
    ).await?;

    println!("Exit code: {}", result.exit_code);
    println!("Output: {}", result.output);

    Ok(())
}