Ralph for Claude Code: A Shell Wrapper That Decides When to Stop
Autonomous AI development loop for Claude Code with intelligent exit detection
At a glance
- What is it?
- Ralph runs Claude Code in a loop against a task list and uses a dual-condition exit gate to decide when the work is done. It is a Bash tool that trades autonomy for a specific, checkable stopping rule, and its own release notes show how often that rule has needed fixing.
- Who is it for?
- Adopt Ralph if you already work from a markdown task file or PRD and you want Claude Code to iterate without you retyping the prompt, and if you are willing to read the loop log. Do not adopt it if you expect unattended correctness or if your project has no written task list for the loop to consume.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 60 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Ralph solves is the stopping condition, not the prompting
Driving Claude Code manually is not hard. The hard part is deciding when to stop. A person types a prompt, reads the diff, types a follow-up, and repeats until they are satisfied. That loop does not scale past a few turns, and it does not run while you sleep. Ralph is an implementation, in the README's words, of Geoffrey Huntley's technique for Claude Code, which the README describes as continuous autonomous development cycles. The tool wraps the claude command in a Bash loop and adds machinery whose only job is to answer one question: has the work finished, or has the loop merely gone quiet? The intended user is someone who already keeps a written task list, a PRD or a markdown checklist, and wants an agent to work through it. It is not a chat interface and it is not an IDE plugin. It is a global command, described in the README as install once, use everywhere, that you invoke from a project directory.
The dual-condition exit gate and what happens when it misfires
The central mechanism is what the README calls a dual-condition exit gate: the loop stops only when both completion indicators are present and Claude emits an explicit EXIT_SIGNAL. One signal is not enough. That is a deliberate choice against the obvious alternative, which is to stop when the model says the task is complete. The README's own changelog is the best evidence for why. Version 0.9.9 fixed a premature exit bug by requiring the explicit EXIT_SIGNAL. Version 0.11.1 fixed premature exit after exactly five loops in JSON output mode, and states that completion_indicators now only accumulate when EXIT_SIGNAL is true. Version 0.11.4 fixed an override case where STATUS: COMPLETE with EXIT_SIGNAL: false now continues working. Read together, those entries describe a stopping rule that took several releases to settle, and they are the reason a safety circuit breaker exists: the README mentions a force exit after five consecutive completion indicators. Around the exit gate sit the other guards. Rate limiting defaults to 100 calls per hour with an hourly reset and is configurable. The circuit breaker uses what the README calls advanced error detection to prevent runaway loops. The response analyzer does semantic analysis with two-stage error filtering, and multi-line error matching is used for stuck loop detection. Output parsing supports JSON with automatic fallback to text, which matters because the two formats expose different fields.
Getting it running: setup, .ralphrc, and the flags that matter
The README documents ralph-setup and ralph-enable as the two entry points. ralph-setup creates a .ralphrc file with a consistent set of tool permissions, and version 0.11.2 notes that both ralph-setup and ralph-enable now write identical .ralphrc configurations, with ALLOWED_TOOLS defaulting to include Edit, Bash(npm *), and Bash(pytest). ralph-enable is an interactive wizard added in 0.11.0, with five phases: environment detection, task source selection, configuration, file generation, and verification. It auto-detects project type (TypeScript, Python, Rust, Go) and framework (Next.js, FastAPI, Django), and can import tasks from beads, GitHub Issues, or PRD documents. ralph-enable-ci is the non-interactive equivalent for automation. Task import also has a dedicated command: ralph-import --github-issue, with metadata filters for labels, title, assignee, milestone, and state, plus first, interactive, or priority selection and a --dry-run preview. For the loop itself, the flags worth knowing are --output-format, --allowed-tools, --no-continue, --resume, --live, --dry-run, --notify, and --backup with --rollback. Two of these are not conveniences. --dry-run simulates loops without API calls, and --backup creates automatic git backup branches that --rollback can restore. If you are evaluating Ralph on a real repository, those two are the ones to use first. Session handling also changed: 0.11.4 replaced --continue with --resume <session_id> to stop session hijacking, and sessions expire after a configurable timeout, 24 hours by default.
The .ralph/ directory move and what it costs to upgrade
Version 0.10.0 was a breaking change: all Ralph-specific files moved into a .ralph/ subfolder so the project root keeps only src/, README.md, and user files. The README states that a ralph-migrate command was added for upgrading existing projects. That is the honest shape of the maintenance cost here. This is a tool under active development with no retrieved releases, and the changelog shows a steady cadence of correctness fixes rather than feature additions: bash 3.x compatibility in 0.11.5 after a lowercase substitution broke, checkbox regex in 0.11.4 so date entries like [2026-01-29] stop being counted as checkboxes, cross-platform date commands for macOS with Homebrew coreutils, and a fix in 0.11.4 for ralph-import hanging indefinitely. Each of those is a case where the loop was running but the bookkeeping around it was wrong. Upgrading means reading the changelog for behaviour changes to the exit gate, not just for new flags. The licence is MIT, which is permissive and places few obligations on how you redistribute or modify the scripts. That is a statement about the licence identifier, not legal advice; if you vendor Ralph into a commercial product, read the LICENSE file yourself.
Where Ralph is the wrong tool
Ralph assumes it can tell progress from noise, and the changelog shows the places where it could not. Version 0.11.4 fixed progress detection so that git commits within a loop count as progress. Before that fix, an agent that was committing work could still be judged stuck. That is the failure mode to keep in mind: a loop that is doing real work but not emitting the tokens the analyzer expects. The inverse is worse. A loop that emits completion-shaped text without doing the work will satisfy the indicators, which is exactly why the exit gate requires EXIT_SIGNAL as a second condition, and why a circuit breaker forces exit after five consecutive completion indicators. Neither guard verifies that the code is correct. If your task cannot be expressed as a checklist or a PRD, Ralph has nothing to iterate against. If your work needs a human decision at each step, the loop is the wrong shape. And if you cannot read a Bash log to tell a real completion from a false one, the safeguards are not a substitute for that reading. The README does not make correctness claims about the code the agent produces, and nothing in the material suggests it should.
Alternatives and the actual difference in approach
The natural comparison is a CI job that runs a linter, a test suite, or a coding agent once per commit. That approach is event-driven: a push triggers a bounded run, and the run ends when the command exits. Ralph is time-and-state-driven: it keeps invoking Claude Code until its own signals say to stop, with rate limiting and a circuit breaker as the outer bounds. The difference matters in two ways. A CI job cannot decide to keep working, so it never needs an exit gate and never risks a runaway loop. Ralph can, which is why it carries a response analyzer, a circuit breaker, and a 100-call-per-hour default limit. The other comparison is the raw claude CLI in a shell for loop you write yourself. That is what Ralph is, plus session continuity via --resume, log rotation at 10MB keeping four archived files, metrics in JSON Lines per loop read by ralph-stats, desktop notifications via --notify, and the .ralphrc permission file. If you only need a few turns, the hand-rolled loop is smaller and has no upgrade surface. If you need the loop to survive a night and leave a readable record of why it stopped, those additions are the product.
What to verify before you let it run unattended
Start with ralph --dry-run in a repository that has a task file, and confirm that the loop reads the tasks you expect it to read. Then run one bounded session with a low MAX_CALLS_PER_HOUR and --live so you can watch Claude Code's output as it happens rather than reconstructing it later. Take a --backup branch before the first real run so --rollback is available. After the run, check two things in the log: whether the exit came from an EXIT_SIGNAL or from the circuit breaker, and whether git commits inside the loop were counted as progress. Those are the two mechanisms with the longest history of fixes in this changelog, and they are the ones that determine whether a stopped loop means finished work or a misread. The ralph-stats command reads per-loop metrics in JSON Lines, which is the cheapest way to compare runs. If your project has no markdown task list and no PRD, write one first. Ralph has no way to invent the stopping condition for you.
Editorial conclusion
Adopt Ralph if you already work from a markdown task file or PRD and you want Claude Code to iterate without you retyping the prompt, and if you are willing to read the loop log. Do not adopt it if you expect unattended correctness or if your project has no written task list for the loop to consume. Before trusting it, run ralph --dry-run on your own repository, then one bounded loop with a low MAX_CALLS_PER_HOUR, and inspect whether the run stopped because of a real EXIT_SIGNAL or because the circuit breaker fired.
Community notes