My simplified Ralph loop setup for Claude Code
Three files to autonomous coding. A simplified Ralph loop setup that works across any project with Claude Code.
I let Claude Code build nine features for a new project while I worked on other things. When I checked back, nine user stories were complete. Tests passing. Commits clean. Progress documented.
This isn't magic. It's a pattern called Ralph, and I've simplified it down to three files that work across any project.
'Who' is Ralph?
Ralph is a bash loop. That's it. The concept comes from [Geoffrey Huntley](https://ghuntley. com/ralph/), who named it after Ralph Wiggum from The Simpsons. The idea is beautifully simple: run an AI coding agent repeatedly until the work is done.
Each iteration spawns a fresh Claude instance with no memory of previous runs. And here's the magic: the AI's work persists in files, Git commits, a progress log, and a learnings file. Each new iteration reads what the last one did and picks up where it left off.
The pattern works because AI agents are deterministic enough to make progress, and persistent enough (through files) to accumulate that progress over time.
Ryan Carson built a [full implementation](https://github. com/snarktank/ralph) with scripts, skills, and flowcharts. But I wanted something far more simple. Something I could set up once and use in any project without copying files around.
So I reduced it to three files and made it a Claude Code plugin. I've [published it on GitHub](https://github. com/mischasigtermans/ralph).
Installation
I've packaged Ralph as a Claude Code plugin. Install it through Claude Code's plugin system, or copy the files manually to your ~/. claude/ directory (user-level) or . claude/ in your project (project-level).
How it works
Ralph uses a . ralph/ directory in each project containing three files:
- stories. json holds your user stories (what to build)
- progress. txt tracks implementation details per iteration
- learnings. txt accumulates patterns and gotchas across runs
The setup consists of three parts: a skill that creates these files, a prompt that tells Claude what to do each iteration, and a bash script that runs the loop.
The skill: planning without implementing
When you run /ralph in Claude Code, it reads your codebase, asks clarifying questions, and generates user stories. The critical rule:
**CRITICAL: NEVER start implementing within this session.**
Claude will try to be helpful and start coding. Don't let it. The skill explicitly forbids implementation because that burns context on coding instead of planning. The actual work happens in the Ralph loop, where each iteration gets a fresh context and full attention on one story.
The skill also handles re-initialization. If . ralph/ already exists, it asks whether you want to add stories or replace them. Learnings can persist across resets.
[View the full skill on GitHub →](https://github. com/mischasigtermans/ralph/blob/main/skills/ralph/SKILL. md)
The prompt: one story per iteration
Each iteration receives instructions to:
- Read the stories and learnings files
- Pick the highest priority incomplete story
- Implement it completely
- Run tests
- Commit if tests pass
- Mark the story as done
- Record any patterns discovered
The stop condition is simple. When all stories are complete:
<promise>COMPLETE</promise>
The bash script watches for this signal and exits the loop.
[View the full prompt on GitHub →](https://github. com/mischasigtermans/ralph/blob/main/scripts/ralph-prompt. md)
The loop: spawn, check, repeat
The bash script is the orchestrator. It validates the setup, then loops:
for i in $(seq 1 $MAX_ITERATIONS); do
# Check remaining stories
incomplete=$(jq '[. stories[] | select(. passes == false)] | length'. ralph/stories. json)
if [ "$incomplete" -eq 0 ]; then
echo "✓ All stories complete!"
exit 0
fi
# Run Claude with the prompt
OUTPUT=$(claude --dangerously-skip-permissions --print "$PROMPT_CONTENT")
# Check for completion signal
if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
echo "✓ Ralph completed all tasks!"
exit 0
fi
done
It shows a spinner while Claude works, displays which story is next, and continues until everything is done or max iterations is reached.
[View the full script on GitHub →](https://github. com/mischasigtermans/ralph/blob/main/scripts/ralph. sh)
Using Ralph
Step 1: Initialize
Open Claude Code in your project and run /ralph. Describe what you want to build. Claude generates stories like this:
{
"project": "Tiller",
"description": "Managed hosting platform for personal AI infrastructure",
"stories": [
{
"id": "TILLER-001",
"title": "Cloud provider credentials storage",
"description": "Create database structure for storing cloud provider API credentials.",
"acceptance_criteria": [
"CloudCredential model with provider enum",
"API token encrypted using Laravel Crypt",
"Feature test verifies encryption at rest"
],
"priority": 1,
"passes": false
}
]
}
Step 2: Run the loop
Exit Claude Code and run ralph or ralph 10 from your terminal. The number is max iterations, defaults to 20.
Watch the spinner. See commits roll in. Go grab coffee. Wrap up another project. Go to bed. Wake up to a working feature.
Step 3: Review
When Ralph finishes, check:
- Git log for commits
. ralph/progress. txtfor what was implemented. ralph/learnings. txtfor patterns discovered. ralph/stories. jsonfor completion status
What I learned
Story sizing matters most
If a story is too big, Ralph wastes iterations trying and failing. The sweet spot is one database change, one component, one service class. Things that fit in a single context window.
Rule of thumb: If you can't describe the change in 2-3 sentences, split it.
The learnings file is gold
After six iterations on Tiller (a project I'm working on), my . ralph/learnings. txt looks like this:
# Tiller Learnings
## Codebase Patterns
Laravel 12 uses `casts()` method instead of `$casts` property
Use `'api_token' => 'encrypted'` cast for automatic encryption
Feature tests (not Unit tests) are required when using Laravel facades
## Gotchas
Http:: fake URL patterns need wildcards to match query params
Use sodium_crypto_sign_keypair() for ED25519 SSH key generation
Every future iteration reads this first. Knowledge compounds.
When to intervene
Ralph works best on greenfield features with clear acceptance criteria. It struggles when:
- A story needs context it can't discover from files
- The same error repeats across iterations (split the story)
- Tests require manual setup or external services
When I see the same story fail twice, I jump in, split it, and restart.
Try it yourself
Install the plugin, run /ralph on your next feature, then run ralph from your terminal to start the loop.
Ralph won't replace thinking about what to build. But once you know what you want, it can do the building while you do something else. Nine features while I worked on other things. That's the pitch.
Hi, I'm Mischa. I've been Shipping products and building ventures for over a decade. First exit at 25, second at 30. Now Partner & CPO at Ryde Ventures, an AI venture studio in Amsterdam. Currently shipping Stagent and Onoma. Based in Hong Kong. I write about what I learn along the way.
Keep reading: Joining Ryde Ventures as partner and chief product officer.