2026-08-28 · Updated 2026-08-28 · 10 min read

Use the Pi coding agent with YYLO

Run Pi as YYLO's documented first-party agent service — tree-structured sessions and Pi's deliberate no-subagent stance mapped onto bounded dispatch, one Pi per task worktree, execution envelopes, and merge admission.

By Juno AI INC · pi · interoperability · yylo

Pi introduces itself as "a terminal-based coding agent" whose rule is "Pi is a minimal agent harness. Adapt Pi to your workflows, not the other way around" — and it means both halves. The core stays small, your history is a tree you can re-enter at any point, and the features other agents bake in are left to extensions you choose. What minimalism does not settle is everything around the run: which unit of work this Pi invocation is for, how several Pi instances fan out without colliding, and how a finished run proves itself once its terminal closes. YYLO is the layer that answers those. It dispatches Pi as a documented first-party service, bounds every invocation, gives each task its own worktree, and gates landing behind recorded evidence.

One sentence carries the whole combination: Pi keeps the conversation; YYLO keeps the consequences — what ran, on which base, with which result, and whether it was admitted into shared history. And unlike the Kiro and OpenCode combinations, no verification gate hangs over this seam: pi sits in YYLO's documented service table today, so every command below is wiring you can run this afternoon.

What Pi already owns

Checked against pi.dev and its published documentation on 2026-08-28, and all of it stays Pi's regardless of what runs above it:

  • A harness that stays small on purpose. The documentation opens by calling Pi "a minimal terminal coding harness" that is "designed to stay small at the core while being extended through TypeScript extensions, skills, prompt templates, themes, and pi packages" (documentation). Its source lives on GitHub under an MIT license.
  • Tree-structured sessions. "Pi saves conversations as sessions so you can continue work, branch from earlier turns, and revisit previous paths"; "Each session is a JSONL file with a tree structure", and /tree "lets you jump to any previous point and continue from there without creating a new file" (sessions). /export writes HTML; /share uploads "as private GitHub gist with shareable HTML link".
  • Four execution modes. The interactive TUI; print mode with pi -p and --mode json for structured event streams; an RPC protocol over stdin/stdout; and an SDK for embedding Pi in your own application (modes).
  • Extensibility as the product stance. "Customize Pi with extensions, skills, prompt templates, and themes", bundle them as packages shared "via npm or git", install them with pi install npm:@foo/pi-tools — or take the site's advice: "Ask Pi to build it for you. Or install a package that does it your way." against a gallery of "50+ examples".
  • Many providers, switched live. "15+ providers, hundreds of models", with the instruction to "Switch models mid-session with /model or Ctrl+L" (pi.dev).
  • Deliberate non-features. "Pi ships with powerful defaults but skips features like sub-agents and plan mode." The site lists what it did not build and where each gap is meant to be filled — the stance the next two sections build on.

Where the agent ends and the control plane begins is a boundary worth knowing precisely; the harness guide draws it for any agent product, Pi included.

Branch the session, branch the repository

The combination pairs two branching systems, and they branch different things. Pi branches the *conversation*: /tree explores alternatives inside one session file, while /fork and /clone move to a new one — the sessions documentation's own rule is "Use /tree when you want to keep alternatives together. Use /fork or /clone when you want a separate session file" (sessions).

YYLO builds its session commands directly on that second mechanism. Its clone flow "uses Pi native --fork, so every clone receives a dedicated Pi session id that can be continued independently" (YYLO documentation); every branch is a real Pi session you can reopen later. Scoping is per shell, so two terminals never silently share a continuation: yy branches lists this shell's named branches, yy switch C makes one active, yy cc continues it, and yy continue-scope --json reports the scope's status — running, finished, not_found, or error — as script-facing truth. A missing Pi session fails right there, directing you to an explicit --resume <session-id> or a new run, never to routing into another scope.

YYLO's other branching system is the *repository*: yy task start TASK_ID returns a dedicated worktree per unit of work, and the README's name for it is "the safety boundary". The mapping is now exact. Session branches compare approaches to the same change; task worktrees isolate different changes to the same repository. They compose cleanly — clone a Pi session inside a task worktree to try two designs, and never point two designs at one checkout:

sh
# inside the worktree yy task start returned
ypl 'draft the config loader'            # yy pi --live
yy clone C 'explore the streaming design' # a new Pi session via native --fork
yy branches                               # named branches for this shell
yy switch C 'continue the streaming design'
yy cc 'continue the main design'          # yylo continue
yy continue-scope --json                  # status for scripts

The subagent question, answered by orchestration

Search for Pi subagents and the project's own answer is a refusal, not an omission. Under what the site did not build: "No sub-agents" — "Spawn Pi instances via tmux, or build your own with extensions, or install a package that does it your way." MCP gets the same posture: "Build CLI tools with READMEs (see Skills), or build an extension that adds MCP support" (pi.dev). For a harness committed to staying small, that refusal is coherent. But it leaves a real job open: when several Pi instances should work at once, something must decide which instance works on what, in what order, and with what blast radius.

That something is the orchestration layer, and it is precisely what YYLO contributes. Readiness is declared as ledger data — the task schema's blocked_by[], surfaced by yy ledger ready — and the parallel runner spawns one bounded Pi invocation per task, each in its own worktree, under an explicit concurrency cap:

sh
yy ledger ready                          # tasks with no unmet blockers
./.juno_task/scripts/parallel_runner.sh --kanban T1,T2 -s pi --parallel 2

The refusal then turns into an advantage. Each Pi remains a full, inspectable session — a tree you can re-enter with /tree after the fact — while coordination lives in the ledger where every surface reads the same answer, not inside any agent where only that agent can see it. Everything the safe parallel Kanban execution guide teaches about quotas, isolation, and dependencies carries over to Pi instances untouched, choosing between the runners covers when to reach for each, and the parallel runner reference documents the flags.

The runner contract: yy pi, live mode, and bounds

This is the seam the sibling guides have to gate, and it ships ungated here. YYLO's documented -s/--subagent table lists the services claude, codex, gemini, pi, and cursor, with the summary "Switch between Claude, Codex, Gemini, Pi, or Cursor with one flag" (YYLO documentation); the documentation page itself demonstrates yylo -b shell -s pi -i 1 for a first bounded run, and yylo init --task "Your task description" --subagent pi registers Pi from the start.

The quick forms matter because you will type them often: yy pi 'your task' dispatches headless — YYLO's Pi service wraps the CLI in JSON event mode by default — and ypl expands to yy pi --live when you want Pi's interactive TUI while keeping YYLO's iteration hooks and statistics. Live mode is validated as Pi-only and auto-exits on non-aborted agent_end only, so pressing Esc to steer keeps the session open. The bounds sit on YYLO's side of the contract: -i/--max-iterations caps each invocation, --on-hourly-limit wait|raise decides what quota exhaustion does, and yy loop sequences Pi against other steps with iteration, run, or shell continuity:

sh
ypl '/skill:ralph-loop Fix the selected ready task' -i 1
yy pi -m :luna --thinking high -p 'Audit the parser edge cases' -i 3
yy loop -n 3 \
  --step 'yy pi "Implement the next increment"' \
  --step 'yy cc "Inspect and improve your work"' \
  --step 'npm test'

Model selection is Pi-shaped: shorthands like :gpt (Sol), :luna, and :mini (Terra) expand to provider/model pairs, yy pi set-default-model :api-codex pins a project default, and --thinking scales reasoning effort on models that support it. Two onboarding details finish the picture: yylo auth import-codex translates Codex CLI credentials into Pi's auth store, and YYLO auto-provisions .pi/skills/ with kanban-workflow, ralph-loop, plan-kanban-tasks, and understand-project — which is why the /skill:... prompt above resolves at all. First runs belong inside the standard the bounded-loop guide teaches.

From session evidence to merge admission

Pi's native records answer conversational questions — which branch said what, exported with /export, shared with /share. YYLO adds two evidence layers above them, one per question automation actually asks.

For *what did this run really do*, there is a machine-checkable envelope: yy pi --execution-envelope --model PROVIDER/MODEL PROMPT returns exactly one juno_execution_envelope.v1 JSON object on stdout, and its "provider/model/session/version/cost fields come from marked backend terminal evidence, not assistant text" — a missing cost stays unavailable, and a reported zero means zero (YYLO documentation). Trust the envelope, not the model's own summary of itself.

For *did this work land*, there is admission. yy ledger update T1 --response "Config loader shipped" --commit 9f3e21a records the outcome and where it settled; the read-only yy task preflight TASK_ID pass tells you what closure still demands while fixing it is cheap; and yy task finish TASK_ID hands the committed tip to the serialized merge queue, so finishes that coincide still enter the target one after another. The task-truth guide owns that record's design and auditable agent workflows the evidence pattern around it. When workflows drive Pi themselves, typed managed_agent steps delegate fresh configured-default yy pi execution to a dedicated runner and keep hash-bound receipts — step digest, session, response hash, terminal result — so the receipt, never the prompt, is the enforcement boundary (workflow runner reference).

Standing context, steering, and staying portable

Pi loads project instructions at startup "from ~/.pi/agent/, parent directories, and the current directory" (pi.dev) — so one AGENTS.md in a task worktree steers both your interactive ypl session and every YYLO-dispatched run against that worktree, and the auto-provisioned skills work identically in both. The task record stays episodic beside that standing context: this unit's intent, its recorded response, its commit. Steering belongs to Pi — "Enter sends a steering message (delivered after current tool, interrupts remaining tools)" — and consequences belong to the ledger; each layer holds its half, and blurring them costs you both.

One caution before you wire anything irreversible: a steerable agent, prompts able to carry substitution commands, plus a control plane dispatching both put three shell-executing surfaces on the same machine — the guardrails collected under prompt and shell-context safety come first. And notice that this combination is unusually easy to leave: Pi already switches models mid-session across 15+ providers, and YYLO treats the harness itself as a one-flag swap — how harness switching works — while the portability audit covers the wider question. Cheap to leave is also safe to try, for one task first. The Cursor, Kiro, and OpenCode guides walk the neighboring compositions.

One small task, run end to end

Nothing convinces like a loop you can watch complete. Pick a change trivial enough to discard, install both products, admit it as one task, and put Pi to work inside the boundary:

sh
npm install -g @yylo/cli
# Pi installs separately, per its own documentation
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
yylo init --task "Describe one verifiable outcome" --subagent pi
yy task start TASK_ID
ypl 'Implement this task with focused checks, then commit' -i 1
yy task preflight TASK_ID
yy task finish TASK_ID

Aim the first iteration at a single verifiable outcome, with validation scoped to the code you changed; if it stalls, yy branches and /tree recover the conversation while yy task status recovers the work. The steady state that falls out is the division this guide promised: Pi holding the session — minimal, steerable, branchable — and YYLO holding everything the session cannot promise itself: the task's identity and readiness, its worktree, its envelope, and its admission. Every Pi statement above was verified against pi.dev and its documentation on 2026-08-28; every YYLO statement against the committed README, service source, and documentation. Both products move quickly — re-verify after either changes.