combo

A combo is several moves that land as one. So is a workflow here: subtasks run apart, and come back as a single Result.

combo is a small TypeScript library for writing pi subagents and composing them into workflows: orchestrator, fan-out, chain, coding/review loop, and flows, task graphs written down, the full delivery among them. Subagents run in-process, through pi’s SDK, so a subagent is an object with a lifetime you control rather than a process you parse.

Four things it promises:

  1. In-process subagents, isolated and composable in TypeScript.

  2. An explicit lifetime: disposable, or persistent across a workflow. The caller decides, never the library.

  3. A live view of the work, in herdr if it is running and in pi’s TUI otherwise, with no change to the calling code.

  4. Everything measured and exportable: time and tokens per subagent, plus a readable HTML and replayable JSONL export of a whole run.

The first hour is Quickstart: installing, one disposable subagent, one that remembers, a workflow, and the same thing from inside pi. It is the only page that opens with a first example - everything here builds on it rather than restating it.

The tutorials are the other way in: twelve sittings in front of pi, each around one problem agents have today, each run on this repository with the frames it drew.

Where the line is drawn

Agents and flows are data; our code decides what runs next. An agent is Markdown with frontmatter, a flow is YAML and Markdown built from a closed set of nodes, and a workflow is TypeScript combinators, for what a file cannot say. A model produces values, never the next node, and an agent never writes a flow.

A prompt is not a permission boundary. A subagent that must not write gets no write tool - asking it nicely has been tried, and it edited the repository anyway. The agents produce text; this library performs the act.

Design decisions has the rest, each with the reason it was taken - and the reversals, with theirs.

Start here

Quickstart

The first subagent, the first workflow, the first build.

Quickstart
Tutorials

Twelve sittings in front of pi, one problem each, every one run on this repository.

Tutorials
Agents

Defining an agent in Markdown: frontmatter, tools, scopes.

Agents
Lifetime

The central choice: disposable or persistent, and who closes what.

Lifetime
Workflows

The combinators, and the options they all share.

Workflows
Flows

A task graph written in Markdown, next to your agents, checked whole before it runs.

Flows
Deliver a change

/run build: plan, pair, check, audit, with nobody asked anything.

Deliver a change
Walk a chain by hand

One step at a time, with the session passive between two of them.

Walk a chain by hand
Worktrees

A working copy each, so two subagents can write at once.

Worktrees
Swarms

Several members on one job, a board between them, and nobody dividing it.

Swarms

Watching and measuring

  • Display - reporters, herdr splits, the pi TUI widget.

  • Measurements - what Usage counts, and what it refuses to guess.

  • Export - runs/<timestamp>/, HTML, JSONL, usage.json.

  • Experiments - one workflow, M models, N repetitions, one table.

Using it from pi

  • Extension - the subagent tool, /run, /interview, /herdr.

Reference

  • Cheatsheet - commands, flags, keys, agent and flow syntax, the API, on one page.

  • API reference - every public export, generated from the source.

  • Flows - each shipped flow, drawn from its file.

  • Examples - one runnable script per shape.

Development

  • Development - tests, typechecking, conventions, how the docs stay honest.

  • Design decisions - why the library is shaped this way, and what was reversed.

In one page

npm install @ai-for-dev/combo       # the library
pi install npm:@ai-for-dev/combo    # the same package, loaded into pi
One subagent, then two of them arguing until they agree
import { findAgent, loadAgents, loop, run, saysWord } from "@ai-for-dev/combo";

const agents = loadAgents();
const scout = findAgent(agents, "scout");

const result = await run(scout, "Find the authentication code");
result.ok;                      // a model failure is a Result, never a throw

const review = await loop({
	steps: [findAgent(agents, "coder"), findAgent(agents, "reviewer")],
	input: "Implement the parser",
	until: (step) => saysWord(step.output, "LGTM"),
	lifetime: "workflow",       // the reviewer remembers what it already said
	timeoutMs: 300_000,         // no default: pi's agent loop has no step cap
});
review.converged;               // reaching the cap is not success