@lnilluv/pi-ralph-loop
Pi-native ralph loop — autonomous coding iterations with mid-turn supervision
Package details
Install @lnilluv/pi-ralph-loop from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@lnilluv/pi-ralph-loop- Package
@lnilluv/pi-ralph-loop- Version
1.6.1- Published
- May 4, 2026
- Downloads
- 2,725/mo · 483/wk
- Author
- lnilluv
- License
- MIT
- Types
- extension, skill
- Size
- 351.8 KB
- Dependencies
- 3 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
],
"skills": [
"./skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-ralph-loop
Autonomous coding loops for pi.
Describe what you want done. The loop runs your agent, re-reads the task, feeds fresh command output every iteration, and stops when the work is finished — or when you tell it to stop.
/ralph "fix the flaky auth tests"
Why loops
A single agent run can fix a bug. But the real leverage is sustained, autonomous work — campaigns that run for hours, making progress one commit at a time while you do something else.
| Without a loop | With a loop |
|---|---|
| Run an agent once, hope it finishes | Re-run until the work is done |
| Copy-paste test output back into chat | Commands feed fresh evidence each iteration |
| Watch the terminal and Ctrl+C when bored | Completion gating stops when the goal is met |
| One long context that gets stale | Fresh context every iteration |
| No guardrails — agent can push to main or delete secrets | Block commands, protect files, confine paths |
People use ralph loops for:
| Task | How the loop helps |
|---|---|
| Grow test coverage | Run the suite each iteration, only commit when coverage increases |
| Fix flaky tests | Run tests, find failures, fix, verify, repeat |
| Migrate a codebase | Transform one module per iteration, keep the build green |
| Write documentation | Check for doc build warnings, fix them, commit |
| Security audit | Scan for vulnerabilities, fix them, verify |
| Deep research | Write findings to files, iterate until the report is complete |
Install
pi install npm:@lnilluv/pi-ralph-loop
For Pi agents and automation
/ralph is a Pi slash command, not a shell executable. Do not run /ralph ... with bash; bash will correctly say No such file or directory.
If the user intended a bare /ralph ... command but it reaches the assistant as normal chat, the command was not intercepted by Pi. Treat that as an extension loading problem, not as a request to manually simulate the loop. Ask the user to run pi install npm:@lnilluv/pi-ralph-loop, then /reload or restart Pi, and retry the slash command.
Useful checks (pi list is authoritative; npm list -g only checks global npm installs):
pi list | grep '@lnilluv/pi-ralph-loop'
npm list -g @lnilluv/pi-ralph-loop --depth=0
pi --help lists CLI flags, not extension slash commands, so pi --help | grep ralph is not a valid install check.
From an assistant turn, either:
- prepare a task folder with
RALPH.md, then tell the user to run/ralph --path ./task, or - when a noninteractive smoke test is explicitly wanted, run Pi itself with the slash command as the prompt:
pi -p "/ralph --path ./task"
RALPH.md frontmatter should use snake_case keys. Common camelCase aliases are accepted for compatibility with LLM-authored drafts, but new files should prefer max_iterations, inter_iteration_delay, completion_promise, completion_gate, required_outputs, stop_on_error, guardrails.block_commands, and guardrails.protected_files.
Quick start
From plain language
Draft and run in one command:
/ralph "fix the failing auth tests"
Draft only:
/ralph-draft "fix the failing auth tests"
The extension creates a RALPH.md draft and shows it for review. Edit, start, or cancel.
With an existing task folder
/ralph --path ./my-task --arg owner="Ada"
From a scaffold
/ralph-scaffold my-task
Creates my-task/RALPH.md with a starter template — edit it, then run with /ralph --path my-task.
What a run looks like
▶ Ralph loop started: my-task (max 20 iterations)
── Iteration 1 ──
Commands: 2 ran (tests, verify)
✗ auth/login.test.ts — 2 failures
✓ Iteration 1 completed (48.2s)
── Iteration 2 ──
Commands: 2 ran
✓ All tests passing
✓ Iteration 2 completed (23.1s)
Ralph loop complete: completion promise matched on iteration 2 (71s total)
The task folder
my-task/
├── RALPH.md ← the prompt (required)
├── check-coverage.sh ← helper script (optional)
├── testing-conventions.md ← reference doc (optional)
├── RALPH_PROGRESS.md ← rolling memory (auto-managed)
├── .ralph-runner/ ← live run state (auto-managed)
│ ├── status.json
│ ├── iterations.jsonl
│ ├── events.jsonl
│ └── transcripts/
└── .ralph-runner-archive/ ← archived run state (auto-managed)
└── <ISO>/
Put scripts, reference docs, and data files alongside RALPH.md. The agent can read them every iteration. RALPH_PROGRESS.md is injected as rolling memory — the loop reads and writes it between iterations. Archived runs move .ralph-runner/ into .ralph-runner-archive/<ISO>/.
RALPH.md format
YAML header (configuration) + Markdown body (the prompt). The header uses snake_case keys. Common camelCase aliases are accepted for compatibility, but new RALPH.md files should use the documented snake_case form.
---
args:
- owner
commands:
- name: tests
run: npm test
timeout: 60
acceptance: true
- name: verify
run: ./scripts/verify.sh
timeout: 60
acceptance: true
max_iterations: 20
timeout: 120
completion_promise: DONE
completion_gate: required
required_outputs:
- AUTH_FIXES.md
stop_on_error: false
guardrails:
block_commands:
- 'git\s+push'
protected_files:
- '.env*'
- 'policy:secret-bearing-paths'
---
Fix the failing auth tests for {{ args.owner }}.
## Current test results
{{ commands.tests }}
## Verification
{{ commands.verify }}
Stop with <promise>DONE</promise> only when all tests pass, AUTH_FIXES.md exists, and OPEN_QUESTIONS.md has no remaining P0/P1 items.
Frontmatter reference
| YAML key | Type | Default | Description |
|---|---|---|---|
commands |
CommandDef[] | [] |
Shell commands run each iteration. Each: name, run, timeout (1–3600s, default 60; must not exceed top-level timeout), optional acceptance: true |
args |
string[] | [] |
Declared runtime parameters for --arg name=value |
max_iterations |
integer | 50 |
1–50 |
inter_iteration_delay |
integer | 0 |
Seconds between iterations |
items_per_iteration |
integer | — | Pacing cap for each iteration. Valid values: 1–20 |
reflect_every |
integer | — | Reflection cadence. Valid values: 2–20 |
timeout |
integer | 300 |
1–3600 seconds per iteration |
completion_promise |
string | — | Done marker. Single line, no <> or line breaks |
completion_gate |
required | optional | disabled |
required when completion_promise is set |
Controls whether the promise, required outputs, and OPEN_QUESTIONS.md readiness block stopping |
required_outputs |
string[] | [] |
Relative file paths that must exist for early stop |
stop_on_error |
boolean | true |
false continues past RPC errors and timeouts |
guardrails.block_commands |
string[] | [] |
Default shell blocklist. Matching bash commands are blocked |
guardrails.protected_files |
string[] | [] |
Glob patterns + policy:secret-bearing-paths |
guardrails.shell_policy |
object | — | Optional shell allowlist. Use only when you want to permit specific bash commands; mode: allowlist requires allow |
Pacing controls
Use these to slow the loop down or add periodic self-checks:
items_per_iteration: 3
reflect_every: 4
items_per_iteration adds a short constraint section on every iteration. reflect_every adds a reflection request on iterations 4, 8, 12, ...
Body placeholders
| Placeholder | Resolves to |
|---|---|
{{ commands.NAME }} |
Output of the named command |
{{ args.NAME }} |
Value of the named runtime arg |
{{ ralph.iteration }} |
Current iteration number |
{{ ralph.name }} |
Task directory basename |
{{ ralph.max_iterations }} |
Current max iterations |
Commands starting with ./ run from the task directory. Others run from the project root. Blocked commands produce [blocked by guardrail: PATTERN]. Timed-out commands produce [timed out after Ns]. Non-zero exits are recorded as error. Ralph records each command outcome as ok, blocked, timeout, or error in durable iteration metadata with a bounded output preview. Command output included in prompts/transcripts is capped with a truncation marker and byte count.
Goal continuation audits
Every Ralph iteration now includes goal-continuation steering: the agent sees elapsed time and a completion-audit checklist. It should map the original prompt to concrete deliverables and inspect real artifacts/tests/status. If completion_promise is configured, the agent should emit it only when the evidence covers every requirement; otherwise, it should keep making verified progress until normal loop termination or operator stop.
Commands
| Command | What it does |
|---|---|
/ralph [path-or-task] |
Start or draft+start a loop |
/ralph-draft [path-or-task] |
Create or edit a draft without starting |
/ralph-list |
List active loops |
/ralph-status [path] |
Show durable status and the latest iteration summary |
/ralph-resume <path> |
Start a new run from an existing RALPH.md |
/ralph-archive <path> |
Move .ralph-runner/ into .ralph-runner-archive/<ISO>/ |
/ralph-stop [task folder or RALPH.md] |
Finish current iteration, then stop |
/ralph-cancel [task folder or RALPH.md] |
Kill the current iteration immediately |
/ralph-scaffold [--preset <name>] <name-or-path> |
Create a starter RALPH.md template |
/ralph-logs [<task folder or RALPH.md>] [--path <task folder or RALPH.md>] [--dest <dir>] |
Export run artifacts to a directory |
Ralph runs each iteration in a child pi --mode rpc process. The child explicitly loads the Ralph extension but disables normal Pi extension discovery, so unrelated local extensions or MCP gateways do not slow or alter loop startup.
Argument passing
--arg name=value is only valid with --path to an existing RALPH.md:
/ralph --path ./my-task --arg owner="Ada" --arg env=staging
/ralph-draft, /ralph-stop [task folder or RALPH.md], and /ralph-cancel [task folder or RALPH.md] reject --arg. Names must match ^\w[\w-]*$ and be declared in args.
Stopping
| Action | Behavior |
|---|---|
/ralph-stop [task folder or RALPH.md] |
Finish current iteration, then stop |
/ralph-cancel [task folder or RALPH.md] |
Kill the current iteration immediately |
| Completion promise + gate | Stop when the promise is matched; required gates also wait for required_outputs, OPEN_QUESTIONS.md readiness, and successful acceptance: true reruns |
| Max iterations reached | Stop after the last iteration |
| No progress for all iterations | Stop with no-progress-exhaustion |
Completion gating
completion_gate controls how strictly the loop treats completion promises. Commands marked acceptance: true still provide normal pre-iteration evidence, and when a required gate is otherwise ready after a completion promise, Ralph reruns those acceptance commands before stopping. Any acceptance outcome other than ok blocks completion and is recorded in iteration metadata/events.
| Mode | Behavior |
|---|---|
required |
Default when completion_promise is set. The loop waits for the promise, every file in required_outputs, and an OPEN_QUESTIONS.md that is ready to stop (no remaining P0/P1 items). |
optional |
The prompt still reminds the agent about outputs and OPEN_QUESTIONS.md readiness, but the loop may stop once the promise is emitted. |
disabled |
The loop skips completion-gate reminders and checks. |
In optional and disabled mode, complete means the promise was matched; those modes do not block on required_outputs or OPEN_QUESTIONS.md readiness.
When the gate is required, completion still needs all conditions:
- The agent emits
<promise>DONE</promise>(or whatever marker you set) - Every file in
required_outputsexists on disk OPEN_QUESTIONS.mdis ready to stop, meaning it has no remaining P0/P1 items- Every
commands[].acceptance: truererun exits with outcomeok
If the promise is seen but files, OPEN_QUESTIONS.md, or acceptance commands are not ready, the loop continues — the next iteration gets a rejection notice telling the agent what still needs to be fixed.
RALPH_PROGRESS.md is injected as rolling memory (max 4096 chars) and excluded from the required_outputs gate.
Guardrails
Block commands
Regex patterns matched against the full bash command. If any pattern matches, the command is blocked:
guardrails:
block_commands:
- 'git\s+push'
- 'rm\s+-rf\s+/'
Protect files
Glob patterns matched against file paths. Blocks write and edit tool calls:
guardrails:
protected_files:
- '.env*'
- '*.pem'
- 'policy:secret-bearing-paths'
policy:secret-bearing-paths is a built-in policy that blocks .aws/, .ssh/, secrets/, .npmrc, .pem, .key, and other secret-bearing paths.
Shell allowlist
Use shell_policy only when you want to allow a narrow set of bash commands. The allowlist is checked before block_commands. If a command does not match any allow regex, it is blocked with [blocked by guardrail: shell_policy.allowlist].
guardrails:
shell_policy:
mode: allowlist
allow:
- '^npm test$'
- '^npm run lint$'
You can omit shell_policy entirely unless you need an allowlist.
Common patterns
Minimal loop
---
max_iterations: 10
---
Read TODO.md and implement the next task. Commit when done.
Self-healing with test feedback
---
commands:
- name: tests
run: npm test
timeout: 60
max_iterations: 20
completion_promise: DONE
---
{{ commands.tests }}
Fix failing tests before starting new work.
Read TODO.md and implement the next task.
Stop with <promise>DONE</promise> when all tests pass and OPEN_QUESTIONS.md has no remaining P0/P1 items.
Parameterized multi-env loop
---
args:
- env
- focus
commands:
- name: tests
run: npm test -- --env={{ args.env }}
timeout: 120
max_iterations: 15
guardrails:
protected_files:
- 'policy:secret-bearing-paths'
---
Environment: {{ args.env }}
Focus: {{ args.focus }}
{{ commands.tests }}
Run: /ralph --path my-task --arg env=staging --arg focus="auth"
Incremental migration
---
commands:
- name: build
run: npm run build
timeout: 60
- name: tests
run: npm test
timeout: 120
required_outputs:
- MIGRATION_NOTES.md
stop_on_error: false
max_iterations: 30
completion_promise: DONE
---
Migrate one module per iteration from the legacy API to the new one.
Build output:
{{ commands.build }}
Test results:
{{ commands.tests }}
Stop with <promise>DONE</promise> when MIGRATION_NOTES.md exists, all tests pass, and OPEN_QUESTIONS.md has no remaining P0/P1 items.
Run state
.ralph-runner/ is auto-created in the task directory. Everything the loop needs to resume, inspect, or export:
| File | Purpose |
|---|---|
status.json |
Current loop state (status, iteration, guardrails, timing) |
iterations.jsonl |
Append-only iteration records |
events.jsonl |
Append-only runner events (progress, gates, starts, finishes) |
transcripts/ |
Per-iteration markdown transcripts |
active-loops/ |
Registry of running loops (pruned after 30 minutes) |
Log export
/ralph-logs copies status.json, iterations.jsonl, events.jsonl, and transcripts/ to a destination directory. Use a positional task path or --path <task folder or RALPH.md>; use --dest <dir> to choose the export directory. Short aliases -p and -d are also supported. Skips symlinks and excludes control files. Default destination: ./ralph-logs-<ISO-timestamp>.
Termination statuses
| Status | Meaning |
|---|---|
complete |
Completion promise matched; required gates also passed when configured |
max-iterations |
Reached max_iterations without completion |
no-progress-exhaustion |
No durable progress in any iteration |
stopped |
/ralph-stop observed |
timeout |
An iteration exceeded the timeout limit |
error |
Structural failure (parse error, missing file) |
cancelled |
/ralph-cancel observed |
Draft workflow
/ralph-draft and /ralph without a path produce a draft:
- Task text is classified as
analysis,fix,migration, orgeneral - A deterministic draft is generated from repo signals (package manager, test/lint commands)
- If an authenticated model is available, the draft may be strengthened by LLM review
- The draft is presented for interactive review — edit, start, or cancel
- Guardrails and
required_outputsfrom the baseline are preserved during strengthening
Drafts include a metadata comment (<!-- pi-ralph-loop: ... -->) used for re-validation on edits.
Scaffold
/ralph-scaffold [--preset <name>] <name-or-path> creates a starter template:
---
max_iterations: 10
timeout: 120
commands: []
completion_promise: DONE
completion_gate: optional
---
# {{ ralph.name }}
Describe the task here.
## Evidence
Use {{ commands.* }} outputs as evidence.
## Completion
Stop with <promise>DONE</promise> when finished.
Bundled presets:
fix-testsmigrationresearch-reportsecurity-audit
Use /ralph-scaffold --preset fix-tests my-task to start from one of the bundled templates. Quoted paths are supported, for example /ralph-scaffold --preset migration "feature/new task".
Refuses to overwrite an existing RALPH.md or write outside the current working directory.
Agent skills
pi-ralph-loop ships two skills that pi auto-discovers when the package is installed:
| Skill | When it activates | What it teaches |
|---|---|---|
ralph-loop |
Starting or configuring a loop | When to loop vs. single-session, prompt structure, guardrails, completion gating, common mistakes |
ralph-draft |
Creating a RALPH.md from plain language | Task classification, project detection, frontmatter generation, guardrail selection |
The ralph-loop skill includes detailed references:
- Prompt patterns — annotated examples for self-healing, migration, research, security, and evidence-driven loops
- Config cookbook — copy-paste frontmatter recipes for common scenarios
License
MIT