About | prd.json format | Install/update Copilot CLI | ralph.sh (looped runner) | ralph-once.sh (single run) | Demo
Also available for WordPress: soderlind/ralph-wp
Ralph is a small runner around GitHub Copilot CLI (standalone) inspired by the“Ralph Wiggum” technique: run a coding agent from a clean slate, over and over, until a stop condition is met.
The core idea:
- Run the agent in a finite bash loop (e.g. 10 iterations)
- Each iteration: implement exactly one scoped feature, then commit
- Append a short progress report to
progress.txtafter each run - Keep CI green by running checks/tests every iteration
- Use a PRD-style checklist (here:
plans/prd.jsonwithpasses: false/true) so the agent knows what to do next and when it’s done - Stop early when the agent outputs
<promise>COMPLETE</promise>
References:
- Thread: https://x.com/mattpocockuk/status/2007924876548637089
- Video: Ship working code while you sleep with the Ralph Wiggum technique | Matt Pocock
You’ll find two helper scripts:
ralph.sh— runs Copilot in a loop for N iterations (stops early if Copilot prints<promise>COMPLETE</promise>).ralph-once.sh— runs Copilot exactly once (useful for quick testing / dry-runs).
You should adjust the prompt/instructions in the scripts to suit your project and workflow.
Here’s an example of what running MODEL=claude-opus-4.5 ./ralph-once.sh might look like:
ralph-some.mp4
.
├── plans/
│ └── prd.json
├── progress.txt
├── ralph.sh
└── ralph-once.sh
See the plans/ folder for more context.
plans/prd.json is a JSON array where each entry is a “work item”, “acceptance test” or “user story”:
[
{
"category": "functional",
"description": "User can send a message and see it appear in the conversation",
"steps": [
"Open the chat app and navigate to a conversation",
"Type a message in the composer",
"Click Send (or press Enter)",
"Verify the message appears in the message list"
],
"passes": false
}
]category: typically"functional"or"ui"(you can add more if you want).description: one-line requirement / behavior.steps: human-readable steps to verify.passes: boolean; set totruewhen complete.
Copilot is instructed to:
- pick the highest-priority item (it decides),
- implement only one feature per run,
- run
pnpm typecheckandpnpm test, - update
plans/prd.json, - append notes to
progress.txt, - commit changes.
copilot --version
# or
copilot -vHomebrew (macOS/Linux)
brew update
brew upgrade copilotnpm
npm i -g @github/copilotWinGet (Windows)
winget upgrade GitHub.CopilotTip: If you’re not sure how you installed it, run
which copilot(macOS/Linux) orwhere copilot(Windows) to see where it’s coming from.
Force an error to print allowed models (quick check)
copilot --model not-a-real-model -p "hi"You can also list/select models in interactive mode:
copilotThen inside the Copilot prompt:
/model
copilot --model gpt-5.2 -p "Hello"All scripts read a MODEL environment variable and default to gpt-5.2 if not set:
MODEL="${MODEL:-gpt-5.2}"Run with a specific model like this:
MODEL=claude-opus-4.5 ./ralph-once.sh- Runs Copilot up to N iterations
- Captures Copilot output each time
- Stops early if output contains:
<promise>COMPLETE</promise>
./ralph.sh 10The prompt includes:
@plans/prd.json@progress.txt
…plus instructions to implement one feature, run checks, update files, and commit.
- Runs Copilot exactly once with the same instructions as the loop script.
./ralph-once.shCopilot CLI supports tool permission flags like:
--allow-tool 'write'(file edits)--allow-tool 'shell(git)'/--deny-tool 'shell(git push)'--allow-all-tools(broad auto-approval; use with care)
The scripts in this bundle:
- enable non-interactive execution with
--allow-all-tools - explicitly deny dangerous commands like
rmandgit push
Adjust these to match your comfort level and CI/CD setup.
- Put work items in
plans/prd.json - Run one iteration to validate your setup:
./ralph-once.sh
- Run multiple iterations:
./ralph.sh 20
- Review
progress.txtfor a running log of changes and next steps.
Run Ralph in an isolated sandbox using a git worktree so you can delete everything afterwards.
- Clone this repo and
cdinto it:
git clone https://github.com/soderlind/ralph
cd ralph- From the repo root, create a worktree on a new branch:
ROOT_DIR="$PWD"
git worktree add "$ROOT_DIR/../ralph-demo" -b ralph-demo
cd "$ROOT_DIR/../ralph-demo"- (Optional) Confirm Copilot CLI is available:
copilot --version- Run one iteration to validate everything works end-to-end:
./ralph-once.sh- Run multiple iterations (adjust the number as needed):
./ralph.sh 10- Inspect what happened:
git --no-pager log --oneline --decorate -n 20
cat progress.txt- Clean up (removes the worktree folder and deletes the demo branch):
# IMPORTANT: run worktree commands against the same repo you created the worktree from.
# Using `git -C "$ROOT_DIR" ...` avoids relying on `cd -` (which can change across shells).
cd "$ROOT_DIR"
git -C "$ROOT_DIR" worktree list
git -C "$ROOT_DIR" worktree remove "$ROOT_DIR/../ralph-demo" || true
# If you deleted the folder manually, prune stale worktree metadata then re-check:
# git -C "$ROOT_DIR" worktree prune
# git -C "$ROOT_DIR" worktree list
git -C "$ROOT_DIR" branch -D ralph-demo- Prompt in scripts: Matt Pocock
MIT — see LICENSE.