@osolmaz/pi-workflows

Workflow engine, JSON control-flow tool, and live terminal viewer for the pi coding agent

Packages

Package details

extension

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

$ pi install npm:@osolmaz/pi-workflows
Package
@osolmaz/pi-workflows
Version
0.2.0
Published
Jul 30, 2026
Downloads
487/mo · 487/wk
Author
osolmaz
License
MIT
Types
extension
Size
902.4 KB
Dependencies
1 dependency · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./src/extension/index.ts"
  ]
}

Security note

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

README

pi-workflows

pi-workflows is a workflow extension for the pi coding agent. It lets you define multi-step agent workflows as TypeScript graphs, trigger them at any point in a pi conversation with /workflow, and watch them run live in a standalone terminal viewer.

The workflow model is a port of openclaw/acpx flows into pi itself. Agent steps run inside your current pi conversation, so the model keeps everything it already knows from the discussion. The model completes each step by calling a JSON workflow tool, which gives the engine structured, validated output to route on.

Install

pi install npm:@osolmaz/pi-workflows

You can also install directly from GitHub:

pi install git:github.com/osolmaz/pi-workflows

Or try the npm package without installing it:

pi -e npm:@osolmaz/pi-workflows

Install the interactive terminal viewer separately from crates.io. The crate is named pi-workflows; its command is piw:

cargo install pi-workflows
piw

The npm package also includes the simpler pi-workflows snapshot viewer. To link that command from a clone, run npm install && npm run build && npm link, or run it in place with npx tsx src/viewer/cli.ts.

Quick start

Put a workflow file in .pi/workflows/ (project) or ~/.pi/agent/workflows/ (global):

// .pi/workflows/echo.workflow.ts
import { agent, defineWorkflow } from "@osolmaz/pi-workflows";

export default defineWorkflow({
  name: "echo",
  presentationPrompt: "Give the user the concise reply from the workflow result.",
  startAt: "reply",
  nodes: {
    reply: agent({
      prompt: ({ input }) => `Answer concisely: ${(input as { task?: string }).task}`,
      expectedOutput: `{ "reply": "your concise answer" }`,
    }),
  },
  edges: [],
});

Then, from any pi conversation:

/workflow echo summarize this repository

/workflow with no arguments lists discovered workflows. /workflow pause lets the current step finish and then holds the run before the next node — useful when you want to interject in the conversation mid-workflow — and /workflow resume continues it. Pressing escape to interrupt a turn pauses the workflow automatically, so the run never nudges the model while you have taken the conversation back; /workflow resume re-delivers the pending step prompt. /workflow cancel stops the active run; if the last run already ended (for example parked at a checkpoint), it clears the leftover widget instead. Trailing text becomes { task: "..." }; pass arbitrary input with --input-json {"key": "value"}. The names cancel, list, pause, and resume are reserved and rejected as workflow names.

While a run is on screen, the footer status bar shows a compact wf <name> [status] <node> indicator alongside the widget.

presentationPrompt is optional. When present, pi-workflows uses it after the structured run ends to request one normal, human-readable assistant response. Workflows without it remain silent after their final structured output, which keeps shell-only and machine-consumed workflows model-free.

Because the workflow runs in your current conversation, you can have a long discussion first and then trigger a workflow that builds on it. The elegant-solution example does exactly that. It asks the model for the most elegant long-term production-ready solution to the problem you discussed, then for the holy grail, then whether the two are the same (y/n). On y it routes straight into implementation, and on n it asks the model to reconcile the gap and pauses at a checkpoint for you to decide. In either case, its presentationPrompt turns the final structured result into a plain assistant response.

Watching a run

Runs persist to ~/.pi/agent/workflows/runs/ as they execute. The viewer tails that directory and re-renders on every state change:

pi-workflows view          # interactive picker, live updates
pi-workflows view <runId>  # jump straight to one run
pi-workflows runs          # plain list of recent runs
pi-workflows view --once   # print a snapshot and exit (good for scripts)

The run detail view draws the workflow as a boxed graph, like the acpx replay viewer. Every card has a centered step-name header and a divider above its structured metadata. Border characters keep the graph background, the body surface begins inside the border, and the header interior uses a separate surface. Node type, status, attempts, and timing use compact symbol rows; start and terminal markers sit outside the card. Node types have distinct semantic colors, active cards use a heavy border, branches carry their case labels, the taken path is highlighted, and loops route through a gutter on the right back into their target from above. ←/→ scrubs backwards and forwards through the recorded steps and re-derives every node's status as of that step, with the selected step's full output shown below; scrubbing to the end snaps back to following the run live.

The Rust piw viewer under tui/ adds a Catppuccin interface, selectable themes, centered active-node following, draggable browser and inspector sizes, detailed trace and conversation inspection, temporal replay, and reconnecting remote viewing. Full cards have one fixed graph-wide size, so streaming, selection, timer ticks, and replay never move nodes or edges. Live conversation capture shows text, thinking, tool calls, and tool execution as they happen, then reconciles settled messages to verbatim Pi entries. See the piw guide.

  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
  ┃            review            ┃
  ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
  ┃ ● agent            ◐ running ┃
  ┃ ↻ 2                    ◷ 12s ┃
  ┃ ◇ clean                     ┃
  ┃ ◇ issues_found              ┃
  ┃ … reviewing implementation  ┃
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Inside pi, a widget above the editor shows the same boxed graph while a workflow is running, windowed around the active node when it is taller than pi's widget budget. Scroll the window with shift+↑ / shift+↓; it snaps back to following the active node whenever the workflow advances a step.

Node types

A workflow is a graph of named nodes with exactly one entry point. Each node finishes with a JSON output, and edges decide what runs next.

An agent node sends a prompt into the pi conversation and waits for the model to submit its output through the workflow tool. A compute node runs a pure TypeScript function. An action node performs a side effect, either a TypeScript function (action({ run })) or a runtime-owned shell command (shell({ exec, parse })). A checkpoint node ends the run in a waiting state so a human can pick it up. On top of agent, the decision helper asks the model to pick from a fixed set of choices and validates the answer, and decisionEdge routes on the result with compile-time case checking.

See docs/workflows.md for the full authoring reference and docs/run-bundles.md for the on-disk run format.

Examples

The examples/workflows/ directory mirrors the acpx example set. Copy any of them into .pi/workflows/ to use them:

  • echo is the smallest possible workflow, one agent step.
  • branch classifies a task with a decision and routes to either a continue lane or a clarification checkpoint.
  • shell runs a runtime-owned shell command and parses its output, with no agent step at all.
  • two-turn chains three agent steps that build on each other's outputs in the same conversation.
  • elegant-solution is the mid-conversation trigger described above.
  • autoimplement runs an implement, verify, review loop where the review decision routes issues_found back to a fix step until it comes back clean, bounded by maxSteps.
  • autoresearch runs an iterative feature-search loop in the style of karpathy/autoresearch: setup creates a frozen evaluation harness, one editable feature file, and a journal; each loop iteration runs one generation of experiments and journals every result; an assess decision keeps looping until a kept result plateaus or a diverse generation all fails, then conclusions are written before the winner is promoted out of the loop directory.

License

MIT