@maheidem/pi-loop

Pi extension: /loop — a generic recurring-prompt loop (Claude Code /loop style) delivered as in-session steer custom messages. Skills (e.g. shepherd) are consumers of /loop, not part of it.

Packages

Package details

extension

Install @maheidem/pi-loop from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@maheidem/pi-loop
Package
@maheidem/pi-loop
Version
0.7.1
Published
Sep 10, 2026
Downloads
251/mo · 251/wk
Author
marcos-heidemann
License
MIT
Types
extension
Size
49 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

loop — /loop for pi

Run any prompt on a schedule inside the current session. Modeled on Claude Code's /loop (CronCreate): the interval and the prompt are yours; the loop just keeps injecting the prompt into this conversation every X.

The loop knows nothing about what the prompt says. It is a generic mechanism. Specializations — like the bundled shepherd skill (skills/shepherd/) — are consumers of /loop, not part of it.

Usage

/loop                                               # interactive dashboard
/loop 5m check the deploy and tell me what happened
/loop 30s run the test suite and fix failures       # fast loop (min 15s)
/loop 2h <prompt>                                    # slow loop
/loop status                                         # show active loop
/loop stop                                           # cancel

The bare command opens a responsive dashboard in TUI mode. It shows the current schedule, live countdown, delivery mode, prompt preview, and safe start/edit/stop actions. In print/RPC/JSON modes, the bare command reports status instead. The nested forms remain the stable scripting interface and are taught by argument autocomplete.

Interval formats: 30s, 5m, 2h, 1d (minimum 15s). Omit the interval for the default 10m. A trailing every 5m clause also works: /loop check CI every 5m.

Each tick arrives as a loop-tick custom message — the prompt verbatim, with a one-line provenance header so the model knows it is a scheduled message, not a fresh user request — rendered in the TUI as a collapsible card (↻ loop tick #7 · check the deploy…):

[loop tick #7 · scheduled by /loop, not typed by the user]
check the deploy and tell me what happened

How it works

One mechanism: an in-session timer (index.ts). Each tick is a single pi.sendMessage({ customType: "loop-tick", content, display: true, details }, { deliverAs: "steer", triggerTurn: true }) call — steer injects into a busy agent and wakes an idle one in the same call, so there is no idle check to race. Catch-up: on agent_settled, if a tick's fire time passed while the agent was busy, it fires immediately (at most one catch-up tick, no backlog). The timer dies with the pi process, the same lifetime a Claude Code in-session cron has. (An earlier version also spawned an external RPC runner; it was removed — old state entries carrying a runnerPid are still read, the field is ignored.)

State & resume

Loop state persists as a loop custom entry in the session JSONL (pi.appendEntry). On /resume, session_start re-arms the timer — CC's "restored on --resume" semantics. /loop stop appends a stopped: true entry. (Entries written by earlier versions under the old entry type, or carrying a legacy runnerPid, are still read.)

Safety & limits

  • One active loop per session (/loop while armed → stop first).
  • Session-scoped by lifetime (delivery is a single in-process call — no child processes, nothing to orphan). No wall-clock expiry.

The shepherd skill (a consumer of /loop)

The bundled skills/shepherd/ skill (installed at ~/.pi/agent/skills/shepherd) is the pi port of the Claude Code shepherd plugin: it pins /loop to a fixed 10-minute cadence with the shepherd-role prompt (watch, act, unblock, understand problems). It's a separate thing from the loop — the loop is generic; the skill just builds a specific prompt and hands it to /loop.

/skill:shepherd <goal to shepherd>

Differences vs Claude Code /loop

Claude Code pi /loop
Scheduler built-in CronCreate (in-process, session-scoped) in-session timer, single steer delivery
Survives model stall yes (cron fires between turns) yes (steer injects into a busy turn)
Survives restart no (restored on --resume) no (re-armed on /resume via session entry)
Interval cron granularity, 1m–7d any ≥15s, no expiry
Dynamic interval yes (model picks delay) no — fixed interval only
Jitter yes (deterministic offset) no

Development

cd custom-extensions/loop
npm install
npm run typecheck
npm test            # domain + dashboard contracts + real pi E2E (~30s)

Install as a pi package (~/.pi/agent/settings.jsonpackages):

"/Users/maheidem/Documents/dev/pi-coder-management/custom-extensions/loop"

then /reload in pi. The skill is in skills/shepherd/SKILL.md (source of truth) and installed to ~/.pi/agent/skills/shepherd/ (copy after edits).

Files

  • index.ts — the extension: /loop commands, timer, steer delivery, catch-up, tick renderer
  • state.ts — pure helpers: interval parsing, tick provenance line, state
  • ui/loop-panel.ts — Pi-free dashboard view model
  • ui/settings-panel.ts — vendored workbench panel interaction primitive
  • skills/shepherd/SKILL.md — the shepherd skill (a consumer of /loop)