pi-queue-steer-factory
Visible steering, follow-up, and session-control queues for Pi and Pi Fabric.
Package details
Install pi-queue-steer-factory from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-queue-steer-factory- Package
pi-queue-steer-factory- Version
0.15.0- Published
- Sep 5, 2026
- Downloads
- 2,159/mo · 2,159/wk
- Author
- monotykamary
- License
- MIT
- Types
- extension
- Size
- 1.1 MB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"index.ts"
],
"image": "https://raw.githubusercontent.com/monotykamary/pi-queue-steer-factory/main/assets/pi-queue-steer-demo.gif"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-queue-steer-factory
A visible steering, follow-up, and session-control timeline for Pi, with acknowledged /fabric prewalk barriers.
Queue instructions while the agent works. Every row keeps its Pi delivery timing — blue steering at a turn boundary, yellow follow-up after a run — inside one first-in, first-out execution outline. Follow-ups are root rows that start runs; steering is indented beneath the run it will join.
Move into any row to edit it. The selected row becomes the live Pi editor, with its cursor, wrapping, paste handling, autocomplete and custom-editor behaviour intact.
Demo

Install
Install the queue and Pi Fabric from npm:
pi install npm:pi-queue-steer-factory
pi install npm:pi-fabric
Pin the current releases when you want reproducible installs:
pi install npm:pi-queue-steer-factory@0.15.0
pi install npm:pi-fabric@0.64.0
The GitHub package is also installable directly:
pi install git:github.com/monotykamary/pi-queue-steer-factory@v0.15.0
Then start a new Pi session or run /reload.
Controls
The extension follows your configured Pi action bindings. These are the default keys on macOS terminals:
| Context | Key | Action |
|---|---|---|
| Agent working | Enter |
Append visible steering; it runs at its next safe turn boundary after earlier rows |
| Agent working | Option+Enter |
Append a visible follow-up; it runs after earlier rows and the active run |
| Queue visible | Option+Up |
Select the most recently queued row |
| Editing a row | Option+Up |
Keep the current draft and move to the previous visual row |
| Editing a row | Option+Down |
Keep the current draft and move to the next visual row; mirrors the configured Option+Up action |
| Editing a row | Type normally | Edit directly inside the selected row |
| Editing a row | Option+X |
Mark the selected row for removal; save deletes it, a second press restores it |
| Editing a row | Option+Right |
Indent the row into steering for the run above; its timeline position does not change |
| Editing a row | Option+Left |
Outdent the row into a follow-up that starts the next run; its timeline position does not change |
| Editing a row | Option+T |
Toggle the same depth as a fallback when a terminal cannot distinguish Option+Arrow |
| Editing a row | Option+P |
Pause or resume the selected row where it sits; a paused row stops dispatch at its position until resumed |
| Editing a row | Option+Shift+Up / Option+Shift+Down |
Reorder the selected row within its lane; positions apply immediately and roll back on Escape |
| Editing a row | Enter or Option+Enter |
Save all row edits, including explicit indent/outdent drafts |
| Editing a row | Escape |
Cancel the session and roll back all unsaved row edits |
| Empty composer, follow-up at the timeline head | Enter |
Promote that next follow-up to steering now |
| Queue paused after an abort | Enter |
Resume from the next timeline row |
| Queue paused after a run error | Enter |
Resume manually; a recovered run (built-in retry, auto-compact, pi-retry) releases the queue first |
| Queue restored after resume | Enter |
Send the next queued row; Option+Up edits it first |
| Agent stopped | Option+Enter |
Queue a message, skill/template, or control command (/compact, /reload, /new, /model, /thinking, /fabric prewalk) visibly and paused |
| Agent working, queue visible | Escape |
Abort the run and pause the entire delivery plan |
| Agent working | /pause |
Pause the run once every in-flight tool call finishes; tool work is never killed mid-execution |
| Any state | Option+W |
Toggle a peer settle gate row (pick a peer or all peers) |
| Peer gate waiting | Escape |
Cancel the wait and pause the gate row |
Option+Left/Right, Option+W, Option+X, Option+T, Option+P and Option+Shift+Up/Down are fixed shortcuts. The other controls use Pi’s configured action bindings, and the next-row key mirrors whatever app.message.dequeue is bound to — rebind that action to ctrl+up and next-row becomes ctrl+down. A dequeue binding with no up twin keeps Option+Down. The physical arrow sequences change depth only while editing queued rows; Option+B/F remain editor word navigation. A terminal that encodes Option+Arrow as those indistinguishable word-navigation bytes can use Option+T instead. Terminals outside macOS may label Option as Alt.
Execution outline
The indentation is deliberately one-way:
• current run
↳ steer current work
○ queued run A
↳ steer inside A
○ queued run B
A yellow follow-up is a root because it starts a run. Any blue steering rows immediately after it are children because they enter that run at turn boundaries. Leading steering rows sit under an implicit current run (or next run while idle). A follow-up after steering always outdents to start another run; queues are therefore never shown as children of steers. There are exactly two depths, so Option+Right on steering and Option+Left on a follow-up are no-ops. The keys preview real depth changes in place, and saving changes only delivery timing—not FIFO position, text, images, or row identity.
Pausing at a tool boundary
Interrupting a run mid-tool (Escape) kills the executing tool outright. /pause is the graceful alternative: with tool calls in flight it holds fire until every one of them finishes and then stops the run at that tool boundary, so long bash jobs and edits always complete and their results stay in the transcript. With no tool call executing it stops the LLM call immediately, and with the agent idle it simply parks the visible queues. A paused queue stays put until an explicit Enter on the empty composer, and /pause during compaction reports back instead of cutting summarization.
Pausing a single row
The whole-queue pause stops everything; sometimes the agent should keep working until it reaches one specific row. While editing the queue (Option+Up), press Option+P to pause or resume the selected row where it sits. A paused row is a dispatch barrier: earlier rows still send on their normal boundaries, and once the paused row reaches the timeline head delivery stops there — rows behind it never jump ahead, regardless of lane — until you select it again and press Option+P to resume. The pause belongs to the row like its lane does: it survives a save, persists across restart and resume, and an unsaved toggle rolls back with the rest of the editing session on Escape. A drain skips paused rows and leaves them parked.
Peer settle gates
When another Pi Fabric session is running in the same project, queue the edit work and hold it behind a gate until that peer settles. Press Option+W (or queue /fabric await [LABEL] by hand) to add a gate row: with one live peer it targets that peer directly, with several it offers an all-peers default plus per-peer cards (● PQS-1 · gpt-5.4 · running · started 6m ago, with Linear-style project labels that are never reused). Rows queued behind the gate dispatch as soon as every watched peer has settled and stayed quiet briefly; peers that leave the mesh count as settled. Escape cancels an active wait and pauses the gate row; Option+W again removes a queued gate. Requires pi-fabric 0.64.0 or newer.
Delivery semantics
The extension keeps Pi’s 2 delivery classes inside one ordered timeline:
- steering at the timeline head reaches the current run at Pi’s next safe turn boundary
- a follow-up at the head waits until the current run finishes
- later rows never overtake the head because they use the other delivery class
- yellow follow-ups stay at the root and blue steering rows indent beneath the run they will join
- reordered rows keep their stable IDs, text drafts and attachments
- reordering waits while an indent/outdent draft is pending; saving the depth change keeps the row in its existing timeline slot
- Pi’s
one-at-a-timeandallsettings still apply per lane, but anallbatch stops at the next lane switch, command, or paused row
This makes both directions composable. Queueing follow-up A → steer A → follow-up B starts A after the current run, injects the steering into A at its next turn boundary, then starts B only after that run settles. While an agent is working, enter that sequence with Option+Enter, Enter, then Option+Enter; queueing steering before a follow-up keeps the inverse relationship just as strictly.
The extension hands the timeline head back to Pi only when that row's delivery boundary arrives. Rows remain visible and editable before that point, and Pi records delivered rows as normal user messages. Queue ownership is TUI-only; RPC, JSON and print-mode input pass through unchanged.
Run errors and retries
A run that ends in an error (including context overflow) pauses the queue instead of de-queueing the next row into the failed session. Recovery gets the queue first: Pi's built-in retry and overflow auto-compaction, or an external retry loop such as pi-retry that re-prompts once the agent goes idle. The pause lifts automatically at the first healthy assistant tail — or when an overflow compact-and-retry cycle concludes — and the parked rows then flow in order. A threshold compaction triggered merely by context size after a failed run is housekeeping, not recovery, and leaves the queue parked. If nothing recovers the run (no retry installed, retries exhausted, or compact-and-retry itself failed), the rows stay parked until Enter sends the next one. Aborting during recovery keeps the pause; an aborted tail never counts as recovery.
Queueing while stopped
With the agent stopped, Enter keeps Pi's normal immediate send. Option+Enter instead places the submission as a yellow follow-up root, paused — including skill and prompt-template invocations and the supported /compact [instructions], /reload, /new, /model [target], /thinking [level], exact /fabric prewalk, and /fabric await [label] controls. Press Enter on the empty composer to execute the next row, or Option+Up to edit it first.
A plain Enter still runs every command immediately. With Option+Enter, other Pi built-ins, other extension commands, unknown slash input and ! bash keep passing straight through.
Prompt templates and Agent Skills
Queued /do-less this code, /skill:bro and /bro rows stay short and editable, then expand when delivered — while the agent works they queue through steering or follow-up input, and while stopped Option+Enter parks them paused like any message. /bro is shorthand for /skill:bro unless a built-in, prompt or extension already uses that name. Template arguments and images are preserved; unknown slash input remains ordinary text.
Arbitrary commands are intentionally not replayed. The supported command rows have explicit completion signals; an unsupported queued extension command pauses until you edit or remove it.
Command rows
Text-only rows matching /compact [instructions], /reload, /new, /model [target], /thinking [level], exact /fabric prewalk, or /fabric await [label] are command rows. A row with image attachments remains a normal message even if its text matches a command, so attachments are never discarded. Command rows execute the control operation instead of becoming LLM messages:
Option+Enterwhile the agent works queues a command in normal follow-up order; while stopped it parks every command row paused,/compactand/reloadincluded- lane timing is uniform: a steered command row executes at the next turn boundary — mid-run, exactly as if typed there — and a queued (follow-up) command row runs when the run settles; rows behind an executing command wait for it
/model provider/modelresolves an exact available model; bare or non-exact/modelopens a filtered picker, and cancellation or authentication failure restores and pauses the row/thinking levelsets Pi's thinking level through the clamped public API; bare/thinkingopens the level picker, and an unknown level or a cancelled picker restores and pauses the row- exact
/fabric prewalkwaits for Pi Fabric to acknowledge that prewalk is armed before the next row can run; it requires Pi Fabric 0.62.7 or newer /fabric await [label]holds the tail until every watched peer session on the project mesh settles (a quiet window after its last observed run) or leaves the mesh; it requires Pi Fabric 0.64.0 or newer. Controls and watch state show on the row (waiting for PQS-1 (running))/newstarts a fresh session and transfers its committed tail to that replacement runtime without adding rows to either transcript; the tail continues automatically, while reopening a persisted queue still starts paused. Pi resolves a fresh session's model from the shared saved default (the last model any session persisted) or the first scoped model, so the handoff pins the outgoing session's model back onto the replacement session before the tail runs — or warns and continues if that model is no longer available/reloadruns Pi’s built-in reload; committed trailing rows retain their IDs, lanes, attachments and pause state across the runtime swap- a queued
/compactruns at settle via Pi’s public compaction API and trailing rows resume when compaction finishes; steered, it fires at the next turn boundary and aborts the in-flight run on purpose — a start failure restores and pauses the command row /reloadqueued as a follow-up runs at settle, sidestepping Pi's built-in busy wait warning; steered, it fires at the next turn boundary and Pi's own busy handling applies- plain
Enteron/compactor/newwhile the agent works parks it as a steer row instead of firing instantly: it runs at the next turn boundary — after the turn's in-flight tool results land — and the extension owns the abort tail; from idle, both still start immediately and a started compaction holds visible rows until it settles - ordinary messages submitted during compaction remain in Pi's native queue and can run before extension-owned command rows after compaction finishes
- stopped
Option+Enterparks/compactand/reloadpaused like every other control row; they run only on an explicit empty-composerEnter - unsupported command forms, including
/fabric prewalk <task>, are not control rows; queue exact/fabric prewalkand the task as separate rows - command rows show a
⚙marker and keep the same pause, edit, reorder and snapshot semantics as messages
Factory pipelines
A linear Factory run is just an observed queue of controls followed by work:
/new
/model openai/gpt-5.4
/fabric prewalk
Implement the queued task
Queue each line with Option+Enter, then press Enter on the empty composer. The dispatcher waits for session replacement, model selection and prewalk arming before advancing; later controls wait for Pi's agent_settled idle boundary. A failed or cancelled control remains at the front and pauses the whole tail, so it can be edited or retried without reordering.
Fabric remains the execution plane inside the task: it can launch durable or recursive agents, steer them, and create isolated worktrees. This extension owns only the visible deterministic queue and its observation boundaries; it does not introduce another agent loop.
Draining the queue
/queue-drain empties both lanes into the run as a single combined message. Row texts join in exact global timeline order, expanding prompt templates and skills as they go, with every row's image attachments appended in the same order.
- during a run, the combined message reaches Pi as one steering message
- while stopped, the combined message starts a new run directly
- a mid-turn drain lands inside the in-flight call's context when the turn has not responded yet, or as the next steering turn once it has — either way the transcript records the combined message exactly once
- command rows are not messages:
/compact,/reload,/new,/model,/thinking, and/fabric prewalkstay queued and execute at their lane's dispatch boundary - an active row-editing session refuses the drain, so rows are never pulled away mid-draft
- a synchronous hand-off failure restores every row, in order, and pauses the queue
Editing semantics
Option+Upstarts at the row you queued most recentlyOption+UpandOption+Downthen move through the visible timeline- saving never changes a row’s lane implicitly;
Option+Rightindents to steering andOption+Leftoutdents to follow-up - depth changes preview in place and commit without changing the row’s global timeline position;
Option+Tremains a toggle fallback Option+Xmarks the selected row for removal; save deletes it, andEscapeor a secondOption+Xrestores it- a selected row becomes the real editor without a nested composer frame
- one editing session can hold drafts for several rows
Escaperestores every row from the session snapshot, including removal marks and indent/outdent drafts- saving an empty text-only row removes it
- image-only rows survive text clearing;
Option+Xremoves them - an unrelated composer draft is stashed and restored when editing ends
A touched head row is pinned until you save or cancel. In one-at-a-time mode, later rows do not block the head. In all mode, editing any row in the contiguous dispatchable head segment holds that batch at active-run delivery boundaries; rows beyond a lane switch, command, or paused row are not part of it.
Abort and recovery
Aborting a run pauses the entire visible delivery plan. This prevents a follow-up from starting immediately after the abort.
Press Enter on the empty composer to resume; the same keypress sends rows queued while stopped. A synchronous handoff or preflight failure returns the affected batch to the global timeline head.
Committed rows also survive quitting and resuming Pi. On shutdown the extension records the queue in the session file as an invisible custom entry that stays out of the transcript and out of the model context. Committed row saves and accepted deliveries immediately supersede that entry with the remaining queue—or an empty tombstone—so edited-away or already-sent rows cannot return after a later restart. Reopening that session restores the remaining rows paused: nothing sends until you press Enter on the empty composer. A /reload runtime swap still carries committed rows and pause state through a short in-process handoff. Edit drafts stay session-local and never persist; ordinary /new and forks start clean, while queued /new intentionally transfers its committed tail.
Public API limits
Pi’s public sendUserMessage API is fire-and-forget. The extension restores synchronous message-dispatch failures and preflight/expansion failures without reordering; once the call returns, it retires the accepted row and checkpoints the remaining queue. Pi does not expose later asynchronous input rejection. Inferring rejection from queue timing could duplicate a delayed successful handoff, so the extension does not do that.
Queued /model uses Pi's awaited model API. Queued /new runs through an internal extension-command adapter because newSession() is intentionally available only in command contexts. Queued /fabric prewalk uses Pi Fabric's versioned host-local request/ack protocol. /reload remains the one supported control exposed only through the TUI editor's void submit callback, so Pi cannot acknowledge or reject that submit back to the extension.
If an all-mode head segment stays pinned until the agent settles, saving from idle starts the new run with the timeline head, then delivers the remaining rows in that segment at the next native boundary. The public API has no atomic idle-to-native-queue batch operation, so this restart cannot be one native batch. Draining sidesteps that limit by composing its combined message client-side, so one send carries every row.
Resume persistence
Queuing a row does not send it. When Pi shuts down cleanly — /quit, Ctrl+C, Ctrl+D, or a session switch — the extension records the committed queue as a custom session entry (pi-queue-steer:queue), invisible in the transcript and excluded from the model context. Successful deliveries and committed row saves append a superseding snapshot immediately; if they empty the queue, they append a tombstone instead. When the same session is reopened (pi -c, pi -r, pi --session, /resume), only the rows in the newest snapshot come back, in FIFO order with their IDs, lanes, image attachments and command rows intact — and the queue is parked paused. Press Enter on the empty composer to send the next row, or Option+Up to edit it first.
Rows normally belong to the session they were queued in. /fork and an ordinary immediate /new start with an empty queue. A queued /new is the explicit exception: it retires the old session snapshot, transfers its tail in process, and continues that tail in the replacement session. Older snapshots superseded by later ones stay in session files but are never restored, and a session can only be resumed at all if Pi wrote it: sessions without an assistant response are not persisted by Pi, and a hard kill skips the shutdown hook.
Editor composition
pi-queue-steer-factory wraps the active Pi editor. It does not replace Pi’s input model.
For display, it extracts the live editor’s text and cursor from the editor frame. It then places that content inside the selected queue row. Autocomplete remains below the edited text.
The extension composes with custom editors including raw-paste and pi-session-hud.
Extension interop
The queue publishes its state for peer extensions on the shared pi.events bus:
- Event —
queue-steer:state, emitted on every change to{ pending, paused, blocked }.pendingcounts all rows still held by the queue (both lanes, including paused and edit-held rows),pausedmeans dispatch is paused,blockedmeans a control row (/compact,/model,/thinking,/new,/reload,/fabric prewalk) is executing. - Mirror — the same snapshot lives on
globalThis.__tmustierPiQueueSteerStatefor synchronous reads, immune to extension load order, and survives/reloadruntime swaps.
Consumers: pi-ledger ≥ 0.6.0 holds back its no-credit engagement wizard while pending > 0, so a parked backlog no longer triggers the billing prompt, and re-offers it once the backlog drains without starting a run.
Development
npm install
npm run ci
./test/tui-evidence.sh /tmp/pi-queue-tui-evidence
pi -e ./index.ts
The automated suite covers delivery, editing, command rows, resource expansion, recovery, images, editor composition, repeated reloads, real retry ordering, real manual compaction success/failure and real automatic overflow compaction. The tmux harness exercises the same paths through Pi's real TUI, including actual runtime reloads and native post-compaction input.
The Pi package ranges are intentionally unpinned. The full suite and real-TUI harness are verified against the current resolved Pi release; see the validation record for exact commands and evidence.
Security
Pi extensions run with the same system permissions as Pi. Review extension source before installing a third-party package.
Licence
MIT. See LICENSE.
This project draws on Cursor’s queue interaction. It is not affiliated with Cursor or Anysphere.
