ralph-coder 0.2.1

An agentic code generation CLI powered by multiple LLM backends
Documentation
# Ralph — Project Status

**Date:** 2026-03-23
**Build status:** ✅ Compiles clean, 75 tests passing, 0 failures
**Rust edition:** 2021
**Rust toolchain:** 1.87.0 (note: `home` crate pinned to 0.5.11 — see Cargo.lock)

---

## What Is Built

Ralph is a fully functional agentic code generation CLI written in Rust. It runs an observe → plan → execute → verify loop against a user-specified codebase, using one of four LLM backends (code present for all four; see notes below).

---

## Project Layout

```
ralph/
├── Cargo.toml
├── Cargo.lock
├── README.md
├── docs/
│   ├── specs.md           — full technical specification
│   ├── project-plan.md    — 11-milestone build plan
│   └── status.md          — this file
├── src/
│   ├── main.rs            — CLI entry point, interactive mode, subcommands
│   ├── lib.rs             — re-exports all modules publicly (for integration tests)
│   ├── errors.rs          — RalphError enum, Result<T> type alias
│   ├── config.rs          — Config struct, loads ~/.ralph/config.toml + .ralph.toml
│   ├── context.rs         — WorkspaceContext (file tree + git status for system prompt)
│   ├── prompts.rs         — system_prompt(), compaction_prompt()
│   ├── output.rs          — Printer, Phase, ConfirmResult, colored terminal + JSON output
│   ├── guardrails.rs      — path traversal, blocked commands, secret detection
│   ├── loop_runner.rs     — the Ralph Loop (observe→plan→execute→verify)
│   ├── session.rs         — session persistence, resume, log_event()
│   ├── compaction.rs      — tiktoken-rs token counting, LLM-driven context compaction
│   ├── checkpoint.rs      — checkpoint create/list/find/revert
│   ├── providers/
│   │   ├── mod.rs         — LlmProvider trait, Message/ToolDef/LlmResponse types, with_retry()
│   │   ├── anthropic.rs   — Anthropic Claude API (code present)
│   │   ├── openai.rs      — OpenAI API (code present; also used by DeepSeek via base_url override)
│   │   ├── deepseek.rs    — DeepSeek (wraps OpenAiProvider with different base URL + model)
│   │   └── gemini.rs      — Google Gemini API (code present)
│   └── tools/
│       ├── mod.rs         — ToolRegistry, tool_defs(), all tool dispatch
│       ├── file_tools.rs  — read_file, write_file, edit_file, delete_file, list_dir,
│       │                    load_files (glob), explain_code (project analysis)
│       ├── shell_tools.rs — run_command (sh -c, captures stdout+stderr+exit code)
│       ├── search_tools.rs— search_available(), search_web (Brave/SerpAPI), search_codebase
│       └── meta_tools.rs  — ask_user, declare_done, declare_failed
└── tests/
    ├── test_file_tools.rs          (9 tests)
    ├── test_guardrails.rs          (9 tests)
    ├── test_session.rs             (6 tests)
    ├── test_checkpoint.rs          (7 tests)
    ├── test_checkpoint_revert.rs   (2 tests)
    ├── test_compaction.rs          (5 tests)
    ├── test_search_tools.rs        (5 tests)
    ├── test_shell_tools.rs         (4 tests)
    ├── test_session_log.rs         (1 test)
    ├── test_token_counting.rs      (7 tests)
    ├── test_search_availability.rs (10 tests)
    └── test_load_explain.rs        (9 tests)
```

---

## Dependencies (Cargo.toml)

| Crate | Purpose |
|---|---|
| `clap` (derive, env) | CLI argument parsing and subcommands |
| `tokio` (full) | Async runtime |
| `reqwest` (json, stream) | HTTP client for all LLM APIs |
| `serde` / `serde_json` | Serialisation for sessions, configs, API payloads |
| `toml` | Config file parsing |
| `async-trait` | Async methods on `LlmProvider` trait |
| `ctrlc` | Ctrl+C handler — saves session on interrupt |
| `rustyline` | Readline (history, arrow keys) for interactive mode |
| `thiserror` / `anyhow` | Error types |
| `colored` | Terminal colours |
| `indicatif` | Spinner during LLM API calls |
| `ignore` | Gitignore-aware directory walking |
| `similar` | Unified diffs for `print_diff()` |
| `regex` | `search_codebase` and guardrail patterns |
| `uuid` | Session IDs and Gemini tool-call IDs |
| `dirs` | Home directory resolution |
| `chrono` (serde) | Timestamps in sessions and logs |
| `tiktoken-rs` | BPE token counting for context compaction |

Dev deps: `tempfile`, `mockito`, `tokio`

---

## Active Provider

| Flag | Env var | Default model | Context window |
|---|---|---|---|
| `--deepseek` | `DEEPSEEK_API_KEY` | `deepseek-chat` | 64,000 |

Search gating (`src/tools/search_tools.rs`): enabled when `BRAVE_API_KEY` or `SERP_API_KEY` is set; provider-agnostic.

> **Note:** Provider code for `--anthropic`, `--openai`, and `--gemini` is present in `src/providers/` but those providers are not documented here while rate-limit issues are being resolved.

---

## CLI Interface

```
ralph [OPTIONS] [PROMPT]

# Interactive mode (no prompt — opens readline chat loop)
ralph --deepseek

# Single-shot mode
ralph --deepseek "your task here"

# Common options
--model/-m <model>      Override default model
--workspace/-w <path>   Target directory (default: current dir)
--max-turns <n>         Agentic loop iteration limit
--dry-run               Show planned actions without executing
--no-confirm            Skip all confirmation prompts
--resume [session-id]   Resume last (or specific) session
--new-session           Force a fresh session
--checkpoint <name>     Create a named checkpoint before starting
--auto-checkpoint       Auto-checkpoint before destructive operations
--context <file>        Inject extra context from a file
--output terminal|json  Output format (default: terminal)
--verbose               Show full LLM interactions

# Subcommands
ralph sessions list
ralph sessions show <id>
ralph sessions clean --older-than <days>
ralph checkpoint list [--session <id>]
ralph checkpoint create <name> [--session <id>]
ralph checkpoint revert <name-or-number> [--session <id>] [--files-only]
ralph checkpoint show <name-or-number> [--session <id>]
```

---

## Tools Available to the LLM

| Tool | Always on | Description |
|---|---|---|
| `read_file` || Read a single file |
| `list_dir` || List directory (gitignore-aware, depth 4) |
| `load_files` || Load multiple files by glob pattern (e.g. `src/**/*.rs`) |
| `explain_code` || Analyze project structure, detect type, summarise source files |
| `write_file` || Create or overwrite a file |
| `edit_file` || Exact-string replace in a file |
| `delete_file` || Delete a file |
| `run_command` || Run arbitrary shell command (needs user confirmation once) |
| `run_test` || Run test suite (whitelisted, no confirmation) |
| `run_build` || Run build (whitelisted, no confirmation) |
| `search_codebase` || Regex search across workspace files |
| `ask_user` || Ask user a clarifying question |
| `declare_done` || Signal task complete |
| `declare_failed` || Signal task cannot be completed |
| `search_web` | Gated | Brave or SerpAPI (only when a search key is set) |

---

## Key Features

### Interactive Mode
When launched without a prompt (`ralph --deepseek`), Ralph opens a readline chat loop. Each task runs the full agentic loop; session context accumulates across tasks. `exit`/`quit`/Ctrl+D to leave. Ctrl+C cancels the current task without exiting.

### Session Persistence
Sessions saved to `~/.ralph/sessions/<session-id>/`. Auto-resume on startup (prompts user). Human-readable log at `~/.ralph/logs/YYYY-MM-DD_<session-id>.log`.

### Context Compaction
When messages exceed 80% of the provider's context window (token-counted via tiktoken-rs cl100k_base), the LLM summarises the conversation. Archive saved to `compacted.json` in the session dir.

### Checkpoints
Named snapshots of file state + conversation. `revert` deletes post-checkpoint files and restores snapshots. Always creates a `before-revert-<timestamp>` safety checkpoint before reverting. Requires explicit confirmation (not bypassable with `--no-confirm`).

### Guardrails
- Path traversal blocked (`..` or absolute paths outside workspace)
- Blocked commands: `rm -rf`, `sudo`, `curl | bash`, `wget | sh`
- Secret detection in file writes: API keys (`sk-`), AWS keys (`AKIA`), private key headers
- Soft guards: confirm overwrites, confirm deletes, confirm `run_command` once per session

---

## Configuration Files

**Global:** `~/.ralph/config.toml`
**Workspace:** `.ralph.toml` (in project root, takes precedence)

```toml
[defaults]
provider = "deepseek"
confirm_overwrites = true

[providers.deepseek]
model = "deepseek-chat"

[search]
brave_api_key_env = "BRAVE_API_KEY"
serp_api_key_env  = "SERP_API_KEY"

[session]
auto_resume = true
clean_after_days = 30

[compaction]
enabled = true
threshold_pct = 80
keep_recent_turns = 3

[checkpoints]
auto_checkpoint_before_destructive = false
```

---

## Data Storage

```
~/.ralph/
  config.toml
  sessions/
    <session-id>/
      meta.json          — session metadata
      messages.json      — full message history
      compacted.json     — compaction archives
      checkpoints/
        <n>_<name>/
          meta.json
          files/         — file snapshots
  logs/
    YYYY-MM-DD_<session-id>.log
```

---

## Known Quirks / Notes

- **`home` crate pinned to 0.5.11** in Cargo.lock — `rustyline 14` pulls in `home 0.5.12` which requires rustc 1.88, but the project is on 1.87. The pin in Cargo.lock keeps it working. If you upgrade rustc to 1.88+ the pin can be removed.
- **DeepSeek** reuses `OpenAiProvider` with `base_url = "https://api.deepseek.com/v1/chat/completions"` and `model = "deepseek-chat"`.
- All integration tests use `tempfile::TempDir` — no files are written to the real project during test runs.

---

## How to Resume Work

```bash
cd /Users/anand-air/projects/ralph

# Verify everything still compiles and tests pass
cargo test

# Build release binary
cargo build --release

# Install system-wide (--locked is required on Rust < 1.88)
cargo install --path . --locked

# Run interactively
./target/release/ralph --deepseek
```