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:
In-process subagents, isolated and composable in TypeScript.
An explicit lifetime: disposable, or persistent across a workflow. The caller decides, never the library.
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.
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¶
The first subagent, the first workflow, the first build.
Twelve sittings in front of pi, one problem each, every one run on this repository.
Defining an agent in Markdown: frontmatter, tools, scopes.
The central choice: disposable or persistent, and who closes what.
The combinators, and the options they all share.
A task graph written in Markdown, next to your agents, checked whole before it runs.
/run build: plan, pair, check, audit, with nobody asked anything.
One step at a time, with the session passive between two of them.
A working copy each, so two subagents can write at once.
Several members on one job, a board between them, and nobody dividing it.
Watching and measuring¶
Display - reporters, herdr splits, the pi TUI widget.
Measurements - what
Usagecounts, 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
subagenttool,/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
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