@koltmcbride/pi-loop
A pi coding-agent extension that runs a prompt repeatedly on a timer, pi event, or self-paced (Claude Code-style /loop)
Package details
Install @koltmcbride/pi-loop from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@koltmcbride/pi-loop- Package
@koltmcbride/pi-loop- Version
0.2.0- Published
- May 30, 2026
- Downloads
- 1,494/mo · 65/wk
- Author
- koltmcbride
- License
- MIT
- Types
- extension
- Size
- 51.4 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./loop.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-loop
A pi extension that runs a prompt repeatedly — on a fixed timer, when a pi event fires, or at the agent's own pace. Modelled on Claude Code's /loop.
Overview
Schedule a prompt to run repeatedly inside pi. An interval is parsed at the command layer into a cron schedule and run by a self-re-arming timer, so the loop keeps going on its own — the model is never responsible for keeping it alive. A self-paced mode is kept for "let the model decide when there's nothing left to do."
What changed in 0.2.0
Earlier versions had a single self-paced mode: the model had to call a wakeup tool at the end of every turn or the loop ended. That meant a turn ending on a summary silently killed the loop, and an interval like 15m written in the prompt was never actually a cadence — just text.
0.2.0 makes a parsed interval authoritative and timer-driven, adds event and hybrid triggers, multiple concurrent loops, and persistence that restores unexpired loops on resume. Self-paced remains as a second mode.
Features
- Fixed-interval loops —
/loop 15m <prompt>parses the interval into cron and runs it on a self-re-arming timer. Continuation is the default. - Self-paced loops —
/loop <prompt>(no interval) fires once, then continues only if the model callsschedule_loop_wakeup. It may end the loop by not calling it. - Event & hybrid triggers — fire on a pi event (e.g.
tool_execution_end,turn_end,monitor:done) instead of polling, or combine cron + event with debounce. - Multiple loops — run several at once; manage with
LoopCreate/LoopList/LoopDeleteor/loop list. - Persistence — loops are stored under
.pi/loopsand restored, if unexpired, on--resume/--continue. - Safety caps — per-loop
maxFiresand an automatic 7-day expiry; jittered fire times avoid API stampedes. - Read-only mode — restrict a loop's fires to read/inspection tools.
- Live status — a footer indicator and widget list active loops with next-fire countdowns.
Installation
pi install npm:@koltmcbride/pi-loop
# or
pi install git:github.com/kolt-mcb/pi-loop@v0.2.0
Verify it's loaded with pi list.
Quick start
/loop 5m check if the deployment finished and report what happened
Fixed 5-minute loop. Runs until you stop it, 7 days pass, or it hits a fire cap.
/loop check whether CI passed and address review comments
Self-paced: the model works, then decides whether to continue via schedule_loop_wakeup (and at what delay).
/loop stop # stop all active loops
/loop stop 3 # stop loop #3
/loop list # show / manage active loops
Usage
/loop command
| Input | Behaviour |
|---|---|
/loop 15m <prompt> |
Fixed-interval (cron) loop. Interval may also trail: <prompt> every 2 hours. |
/loop 0 9 * * 1-5 <prompt> |
Full 5-field cron schedule. |
/loop <prompt> |
Self-paced loop (model-driven cadence). |
/loop list |
List/manage active loops. |
/loop stop [id] |
Stop all loops, or one by id. |
Intervals use s / m / h / d. Sub-minute rounds up to one minute (cron's floor); odd intervals like 7m snap to the nearest clean cron step and the loop tells you what it picked.
Tools (for the agent)
| Tool | What it does |
|---|---|
LoopCreate |
Schedule a loop on a cron timer, a pi event, or a hybrid of both. Supports recurring, readOnly, maxFires, filter. |
LoopList |
List loops with ids, triggers, fire counts, next-fire times. |
LoopDelete |
Delete a loop, or action="pause" to keep it without firing. |
schedule_loop_wakeup |
Continue the active self-paced loop; optional delaySeconds. Omit to end it. |
Trigger types: cron (5m, 1h, 0 9 * * 1-5), event (any pi event-bus channel; lifecycle events tool_execution_start/end, turn_start/end, agent_start/end, message_end are bridged through), or hybrid (both, debounced).
Behaviour notes
- Fires land between turns. Each fire is delivered as a follow-up message, so it waits for the current turn to finish. A recurring fire is skipped while a message is already queued, so ticks never stack.
- Takeover. Typing while a self-paced loop is waiting ends it (you took over). Fixed and event loops keep running across your messages until you
/loop stopthem. - No catch-up. If fires were missed while busy, the loop fires once when idle, not once per missed interval.
Configuration
| Variable | Effect | Default |
|---|---|---|
PI_LOOP |
off disables persistence (in-memory only); an absolute or relative path sets a custom store file |
.pi/loops/loops-<sessionId>.json |
Constants at the top of loop.ts / src/: status tick interval, default hybrid debounce, and the bridged lifecycle event list. Caps: 25 active loops, 7-day expiry.
Development
npm install
npm run typecheck # tsc --noEmit
npm test # node:test via tsx — covers parsing, cron, jitter
Source layout:
| File | Responsibility |
|---|---|
src/types.ts |
Loop/trigger types. |
src/loop-parse.ts |
parseInterval, extractInterval, cron math, jitter (pure, tested). |
src/store.ts |
Loop registry + JSON persistence. |
src/scheduler.ts |
Self-re-arming cron timers. |
src/triggers.ts |
Event/hybrid subscriptions + debounce. |
loop.ts |
Entry: command, tools, fire→message bridge, status widget, lifecycle. |