pi-loop-mode
v2.5.3
Published
Unattended loop mode for pi — separate goal/prepare/run phases with per-phase model selection, endless by default, objective goal function (--check) with score/regression tracking, error retry with backoff, layered anti-repetition defenses (near-duplicate
Maintainers
Readme
pi-loop-mode
Unattended loop mode for pi — endless, self-correcting agent iterations for multi-day runs.
Full documentation with examples: DOCUMENTATION.md (deutsche Übersetzung: DOCUMENTATION_de.md)
Pi package for an unattended loop mode: give the agent a goal and it works on it in small iterations until you stop it — designed for multi-day runs where a product keeps growing (features, tests, bug fixes, refactoring, docs). Works with commercial and open-source models alike.
Preview
Click the preview image to open the short MP4 demo. The same video is also exposed as pi.video metadata for the pi.dev package gallery.
What it does
- Endless by default: no iteration cap. The loop runs until
/loop stop. Set--max Nfor an optional cap,--until-doneif completion should actually stop the loop. - Objective goal function (
--check "CMD"): a shell command run after every iteration. Exit code 0 = criteria met (verified completion in--until-donemode — the check decides, not the model's claim). OptionalSCORE: <n>output enables progress tracking: score improvements count as real progress, score regressions trigger an immediate "find and fix the regression" prompt. Essential against models that claim success without delivering. - Error resilience: model/provider errors (crashes, timeouts, rate limits, empty responses) never end the loop. It retries with exponential backoff (5s → 5min) and tells the model to re-establish its bearings and continue.
- Garbage/repetition detection: repeated assistant answers (exact fingerprints and near-duplicates via trigram similarity with numbers masked, alternating A-B-A-B patterns), repeated tool results/errors, repeated questions, and narration-only turns (3 turns without a single tool call) are all detected. Instead of pausing, the loop injects rotating recovery strategies (switch subtask, write PROGRESS.md, run tests and fix one failure, review diffs, list alternative approaches) with escalating delays — important for weaker open-source models that loop on the same output.
- Degenerate-generation kill switch: if one sentence dominates a response, one word repeats 16× consecutively, or a short phrase (up to four words) repeats 8× consecutively in visible output or provider thinking/reasoning, the stream is aborted live, the stored message is truncated, and already-poisoned context is sanitized before every LLM call. The abort enters automatic stuck recovery instead of pausing the loop.
- Escalation ladder on persistent stuck: rotating strategy → anti-repetition sampling penalties (
frequency_penalty/presence_penalty+ higher temperature for 3 turns, OpenAI-compatible APIs only) → hard reset with banned openings → one rescue turn by a stronger model (--rescue-model M, after 3 consecutive stuck interventions) → automatic context compaction (after 5). - Backlog-driven improvement mode: after
LOOP_DONEin endless mode the loop works off anIMPROVEMENTS.mdbacklog — concrete checklist items with file paths and acceptance criteria; vague items ("add support for other platforms") are forbidden. - Bounded per-iteration JSONL log (
.pi-loop-log.jsonl, 5 MiB + one.1backup) with/loop stats: event distribution, interventions, productive iterations/hour, score trend — compare models and goal phrasings without unbounded disk growth. - No-progress audit: if no concrete file/system change happens for 8 iterations, the loop demands a tangible artifact (file change, passing test, fixed bug).
- Never waits for a human:
LOOP_BLOCKED:does not pause the loop. The model is told to make a documented assumption (ASSUMPTIONS.md) and keep going. - Done ≠ stop (endless mode): when the model reports
LOOP_DONE:, the loop continues with improvement work — new features, more tests, bug fixes, refactoring, performance, docs. - Auto-resume: loop state is persisted in the session; after a pi restart/reload an active loop resumes automatically after 3 seconds.
- Short iterations: every loop prompt tells the model to do one concrete progress batch and keep output under 1,200 characters, so context stays small and normal compaction works.
- Operator control:
Esc(abort) pauses the loop;/loop resumecontinues it;/loop finishsoft-stops it (current iteration completes, then no new turn);/loop stopstops it immediately;/loop endclears state.
Recommended workflow: separate goal, preparation, and execution
/loop goal <goal + flags> # 1. set the goal — starts nothing
/loop prepare --model anthropic/claude-opus-4-6 # 2. strong model writes GOAL.md + check script (ends with GOAL_READY:)
/loop run --model vllm-omega/qwen3-coder-30b --rescue-model anthropic/claude-sonnet-4-5
# # 3. cheap/local model works the loop against the spec;
# # the rescue model takes over for one cleanup turn when stuckPlanning and execution are decoupled: an expensive model writes a precise spec once (GOAL.md, reviewable/editable before the run), a cheap or local model executes it for days. The run model is persisted and restored on auto-resume. Switch models mid-run with /loop stop + /loop resume --model M.
Commands
Pi slash commands only match the first word, so this package registers a single /loop command with subcommands:
| Command | Description |
|---------|-------------|
| /loop goal <goal> [flags] | Set goal + config without starting. /loop goal shows it. |
| /loop prepare [--model M] [--file F] | Have a (strong) model write the goal spec (GOAL.md) + check script. |
| /loop run [--model M] | Start the loop, optionally with a different model. |
| /loop start <goal> | Shortcut: goal + run in one step (endless loop). |
| /loop start <goal. Done when: criteria> | Optional explicit completion criteria. |
| /loop start <goal> --max 50 | Optional iteration cap (pauses at N). |
| /loop start <goal> --delay 30 | Wait 30s between iterations. |
| /loop start <goal> --check "npm test" | Objective goal check after every iteration. |
| /loop start <goal> --check "./check.sh" --check-timeout 300 | Check with custom timeout (default 120s). |
| /loop start <goal> --until-done | Stop on verified completion (check passes; without check: LOOP_DONE:). |
| /loop start <goal> --rescue-model M | Stronger model takes over for one cleanup turn after 3 consecutive stuck interventions. |
| /loop resume [--max N] [--model M] [--rescue-model M] | Continue a stopped/paused loop, optionally switching models. |
| /loop status | Show loop state, errors, interventions, iterations. |
| /loop stats | Summarize the per-iteration JSONL log: events, interventions, iterations/hour, score trend. |
| /loop finish | Soft stop: finish the current iteration, then stop (state preserved). Alias: /loop soft-stop. |
| /loop stop | Hard stop: abort the in-flight turn immediately and preserve state. |
| /loop end | End the loop and clear state. |
| /loop help | Show command help. |
Additionally, the /loop-examples [focus] prompt template inspects the current project and proposes ready-to-paste /loop commands (features, bug fixing, tests, refactoring, docs) — including model-split setups for weaker/local models. Focus values: features, bugs, tests, refactor, docs, quality.
Example — a 5-day product-building run:
/loop start Build a complete REST API for task management in ./taskapi: CRUD, auth, SQLite, tests, docs. Keep improving it: more features, better tests, bug fixes, refactoring.Then stop it whenever you want with /loop stop.
Example — with objective goal function:
/loop start Build a REST API in ./taskapi. Done when: all tests pass --check "cd taskapi && npm test" --until-doneWriting a goal check script
The check command is the loop's fitness function. Exit 0 = done criteria met; print SCORE: <n> (higher = better) for progress/regression tracking:
#!/usr/bin/env bash
# check.sh — example goal function
cd taskapi || { echo "SCORE: 0"; exit 1; }
npm run build >/dev/null 2>&1 || { echo "SCORE: 0"; exit 1; } # build must work
passed=$(npm test 2>/dev/null | grep -oE '[0-9]+ passing' | grep -oE '[0-9]+' || echo 0)
echo "SCORE: $passed"
[ "$passed" -ge 50 ] && exit 0 || exit 1 # done when 50+ tests passScore ideas: number of passing tests, test coverage, implemented endpoints, - lint warning count. The score appears in every loop prompt and in the status bar, and drives regression detection.
How failures are handled
| Situation | Behavior |
|-----------|----------|
| Model/provider error (crash, rate limit, timeout) | Retry with exponential backoff, then "recover" prompt; never gives up. |
| Model repeats the same answer/tool call/question | Stuck intervention with rotating strategies and escalating delay. |
| Model repeats a near-identical answer (≥ 80 % trigram similarity, numbers masked) | Also a stuck intervention. |
| Model answers with narration only (3 turns without any tool call) | Also a stuck intervention. |
| One sentence, word, or short phrase repeats mechanically within one response | Stream aborted mid-generation (sentences ≥ 6×, words ≥ 16× consecutively, phrases ≥ 8× consecutively); stored message truncated; automatic stuck recovery continues the loop. |
| 3 consecutive stuck interventions | Hard reset (banned openings, turn must start with a tool call) — or a one-turn takeover by the --rescue-model, if configured. |
| 5 consecutive stuck interventions | Automatic context compaction (repetitive filler explicitly excluded from the summary). |
| Any stuck intervention | Additionally: anti-repetition sampling penalties for the next 3 turns (OpenAI-compatible APIs). |
| Model produces analysis but no changes for 8 iterations | Audit prompt demanding a tangible artifact. |
| Model says LOOP_DONE: (endless mode) | Loop continues with backlog-driven improvement work (IMPROVEMENTS.md). |
| Model says LOOP_DONE: but goal check fails (--until-done) | Loop rejects the claim and demands fixing what the check reports. |
| Goal check score drops | Immediate regression prompt: inspect diffs, fix what broke. |
| Goal check passes (--until-done) | Verified completion — loop stops. |
| Goal check command itself fails to run | Warning shown; loop continues without blocking. |
| Model says LOOP_BLOCKED: | Loop instructs it to assume, document in ASSUMPTIONS.md, and continue. |
| Operator presses Esc | Loop pauses; /loop resume continues. |
| /loop finish (soft stop) | Current iteration completes, then the loop stops without a new turn; state preserved for /loop resume. Survives restarts (finalized on session start instead of auto-resuming). |
| pi restart / reload | Active loop auto-resumes after 3s, restoring the stored loop model. |
| Loop model unavailable after restart | Warning; loop continues with the current model. |
| --max N reached | Loop pauses; /loop resume continues (uncapped if exhausted). |
Install
pi install npm:pi-loop-modeOr from a local checkout:
pi install /path/to/pi-loop-modeThen start or reload pi and run /loop help.
Security note: pi packages run with full system access, and this package is built for unattended operation — the model works for hours without supervision. Use a dedicated directory/repo, ideally a VM or container, and keep production systems out of reach.
Files
pi-loop-mode/
├── package.json
├── README.md # This file
├── DOCUMENTATION.md # Full documentation with examples (English)
├── DOCUMENTATION_de.md # German translation
├── CHANGELOG.md
├── GALLERY.md # pi.dev gallery asset notes
├── LICENSE # GNU AGPL v3.0 only
├── assets/ # pi.dev preview video + poster image
├── extensions/index.ts # Pi command/event orchestration
├── src/ # Repetition, parsing, state, checks, and bounded logging
├── skills/loop-skill/SKILL.md # Behavior rules for the model in loop mode
├── prompts/loop-prompt.md # Prompt template
└── prompts/loop-examples.md # /loop-examples — project-specific loop recipes (features, bugs, tests, …)Notes
- Loop state is persisted with
pi.appendEntry()and does not add large payloads to model context. - The loop deliberately allows Pi's normal compaction behavior; short per-turn output keeps multi-day runs compactable.
- Recommended for long runs: work in a git repo so the model can commit incrementally, and tell it so in the goal.
- The bounded per-iteration logs
.pi-loop-log.jsonland.pi-loop-log.jsonl.1are written to the working directory — add both to.gitignoreif you don't want them committed. - "Continue" prompts are slightly varied per iteration; identical prompts encourage identical (repetitive) answers from weaker models.

