Ralph without Docker: opencode & pi coding agent (Linux + Windows)
Ralph is a technique for running AI coding agents in a loop: you run the same prompt repeatedly, the AI picks its own tasks from a PRD, commits after each feature, and you come back later to working code.
Adapted from Getting Started with Ralph (AI Hero). The original uses Claude Code + Docker Desktop on Linux. This version swaps in two open-source agents — opencode and pi coding agent — drops Docker entirely, and covers Linux and Windows. Shared concepts appear once; each OS/agent combo gets its own section with only what's different.
What is Ralph?
Ralph is a technique for running AI coding agents in a loop: you run the same prompt repeatedly, the AI picks its own tasks from a PRD, commits after each feature, and you come back later to working code. The loop stays the same no matter which agent or OS you use:
| Piece | Purpose |
|---|---|
PRD.md |
Defines the end state — a checklist, list of tasks, or prose the agent can pull individual tasks from |
progress.txt |
Tracks what's done between runs |
| One prompt | "Find the next task, implement it, commit" — the loop body |
| Completion sigil | <promise>COMPLETE</promise> — printed when the PRD is finished so the loop can stop |
For tips on task sizing, prioritization, and feedback loops, see the 11 tips for AI coding with Ralph.
What changes without Docker
Docker Desktop sandboxes gave you three things you now own yourself:
- No sandbox.
docker sandbox run claudeisolated the agent. opencode and pi run directly with your user's permissions — they can install packages, run servers, and touch any file you can. Start in a disposable VM or a scratch folder, run the once-script a few times first, and review commits before going fully AFK. - Git identity is on you. Sandboxes auto-injected git config for commit attribution. Without
Docker, set it once:
git config --global user.name "You" git config --global user.email "you@example.com" - Credentials are yours to manage. No Docker volume — each agent stores auth in its own config directory. For scripted (headless) runs, prefer exporting a provider API key in your shell.
Step 1 — Write the PRD (identical for all four combos)
Ask the agent for a plan, iterate until you're happy, then save it as PRD.md:
- opencode: the TUI has a plan mode — press
Tabto switch modes, iterate, then tell it to save the plan toPRD.md. - pi: no built-in plan mode (by design). Ask pi interactively to draft a plan, iterate in chat, then have it write
PRD.md.
Create the empty progress file:
touch progress.txt
The PRD can be any format (markdown checklist, JSON, prose). What matters: clear scope, and tasks the agent can pull out one at a time.
Step 2 — Install & authenticate (pick your combo)
| # | Combo | Install | Auth |
|---|---|---|---|
| A | Linux + opencode | curl -fsSL https://opencode.ai/install \| bash or npm install -g opencode-ai |
opencode → /connect → pick provider, or export ANTHROPIC_API_KEY=... |
| B | Linux + pi | npm install -g --ignore-scripts @earendil-works/pi-coding-agent (or curl -fsSL https://pi.dev/install.sh \| sh) |
pi → /login → pick provider, or export ANTHROPIC_API_KEY=... |
| C | Windows + opencode | scoop install opencode or choco install opencode (Node optional: npm i -g opencode-ai) |
opencode → /connect, or set ANTHROPIC_API_KEY env var |
| D | Windows + pi | npm install -g --ignore-scripts @earendil-works/pi-coding-agent (needs Node.js) + Git for Windows — pi requires a bash shell |
pi → /login, or export ANTHROPIC_API_KEY=... in Git Bash |
Auth done in the TUI persists for later headless runs; an exported API key works everywhere including scripts.
A. Linux + opencode (no Docker)
Human-in-the-loop run — ralph-once.sh (watch it, run it again):
#!/bin/bash
opencode run -p --auto "@PRD.md @progress.txt
1. Read the PRD and progress file.
2. Find the next incomplete task and implement it.
3. Commit your changes.
4. Update progress.txt with what you did.
ONLY DO ONE TASK AT A TIME."
chmod +x ralph-once.sh
./ralph-once.sh
--autoauto-approves permission requests so the loop never stalls (omit it for a stricter run).-p/--printformats output cleanly;@fileattaches files to the prompt.
AFK loop — afk-ralph.sh (run with a cap: ./afk-ralph.sh 20):
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 <iterations>"
exit 1
fi
for ((i=1; i<=$1; i++)); do
result=$(opencode run -p --auto "@PRD.md @progress.txt
1. Find the highest-priority task and implement it.
2. Run your tests and type checks.
3. Update the PRD with what was done.
4. Append your progress to progress.txt.
5. Commit your changes.
ONLY WORK ON A SINGLE TASK.
If the PRD is complete, output <promise>COMPLETE</promise>.")
echo "$result"
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
echo "PRD complete after $i iterations."
exit 0
fi
done
set -e exits on errors, $1 caps iterations (runaway-cost protection), and the sigil check
stops the loop early when the PRD is done.
B. Linux + pi coding agent (no Docker)
Human-in-the-loop run — ralph-once.sh:
#!/bin/bash
pi -p "@PRD.md @progress.txt
1. Read the PRD and progress file.
2. Find the next incomplete task and implement it.
3. Commit your changes.
4. Update progress.txt with what you did.
ONLY DO ONE TASK AT A TIME."
chmod +x ralph-once.sh
./ralph-once.sh
- pi has no permission popups by design — tool calls just execute.
-pis print (headless) mode;@fileattaches files. Use--tools read,write,edit,bash(the default set) or a narrower allowlist if you want extra safety without a sandbox.
AFK loop — afk-ralph.sh:
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 <iterations>"
exit 1
fi
for ((i=1; i<=$1; i++)); do
result=$(pi -p "@PRD.md @progress.txt
1. Find the highest-priority task and implement it.
2. Run your tests and type checks.
3. Update the PRD with what was done.
4. Append your progress to progress.txt.
5. Commit your changes.
ONLY WORK ON A SINGLE TASK.
If the PRD is complete, output <promise>COMPLETE</promise>.")
echo "$result"
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
echo "PRD complete after $i iterations."
exit 0
fi
done
C. Windows + opencode (no Docker)
Runs fine from PowerShell (scripts below) or cmd. Install with scoop install opencode
or choco install opencode, run opencode once, /connect, and pick a provider.
Human-in-the-loop run — ralph-once.ps1:
# Run once, watch it, run again.
opencode run -p --auto "@PRD.md @progress.txt
1. Read the PRD and progress file.
2. Find the next incomplete task and implement it.
3. Commit your changes.
4. Update progress.txt with what you did.
ONLY DO ONE TASK AT A TIME."
Set-ExecutionPolicy -Scope Process Bypass # if scripts are blocked
.\ralph-once.ps1
AFK loop — afk-ralph.ps1 (run with .\afk-ralph.ps1 20):
param([int]$Iterations)
if (-not $Iterations) { Write-Host "Usage: .\afk-ralph.ps1 <iterations>"; exit 1 }
for ($i = 1; $i -le $Iterations; $i++) {
$result = opencode run -p --auto "@PRD.md @progress.txt
1. Find the highest-priority task and implement it.
2. Run your tests and type checks.
3. Update the PRD with what was done.
4. Append your progress to progress.txt.
5. Commit your changes.
ONLY WORK ON A SINGLE TASK.
If the PRD is complete, output <promise>COMPLETE</promise>."
Write-Output $result
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ($result -match "<promise>COMPLETE</promise>") {
Write-Host "PRD complete after $i iterations."
exit 0
}
}
$result collects opencode's stdout, $LASTEXITCODE propagates failures, and -match detects
the completion sigil.
D. Windows + pi coding agent (no Docker)
pi requires a bash shell on Windows — install Git for Windows
(pi auto-detects C:\Program Files\Git\bin\bash.exe). Everything below is bash run from a
Git Bash terminal. Install pi, then authenticate (pi → /login, or export ANTHROPIC_API_KEY
in the Git Bash session).
Human-in-the-loop run — ralph-once.sh (in Git Bash):
#!/bin/bash
pi -p "@PRD.md @progress.txt
1. Read the PRD and progress file.
2. Find the next incomplete task and implement it.
3. Commit your changes.
4. Update progress.txt with what you did.
ONLY DO ONE TASK AT A TIME."
chmod +x ralph-once.sh
./ralph-once.sh
AFK loop — afk-ralph.sh:
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 <iterations>"
exit 1
fi
for ((i=1; i<=$1; i++)); do
result=$(pi -p "@PRD.md @progress.txt
1. Find the highest-priority task and implement it.
2. Run your tests and type checks.
3. Update the PRD with what was done.
4. Append your progress to progress.txt.
5. Commit your changes.
ONLY WORK ON A SINGLE TASK.
If the PRD is complete, output <promise>COMPLETE</promise>.")
echo "$result"
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
echo "PRD complete after $i iterations."
exit 0
fi
done
Same as the Linux+pi variant — the only difference is where the shell comes from (Git Bash),
so these files work verbatim on both. If pi ever says it can't find a shell, point it at Git Bash
via shellPath in ~/.pi/agent/settings.json:
{ "shellPath": "C:\\Program Files\\Git\\bin\\bash.exe" }
Customizing the loop
Ralph is just a loop, so it's endlessly swappable:
- Swap the task source. Instead of a local PRD, pull from GitHub Issues, Linear, or beads — the agent still picks what to work on.
- Change the output. Instead of committing to main, create a branch + PR per iteration to triage a backlog.
- Different loop types: test coverage (write tests until coverage hits target), linting
(fix errors one by one), duplication (refactor
jscpdclones), or entropy (clean up code smells). - Per-agent extras: pi can load skills/extensions that gate tools or checkpoint git commits; opencode supports custom agent modes and permissions config. Both work fine inside the same loop.
Original article: Getting Started with Ralph · Tips: 11 tips for AI coding with Ralph
Comments
No comments yet.