# 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)
| `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
| `--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
# 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
| `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
```