pi-epicflow

Pi skill + prompt + agents for shipping multi-feature work autonomously: decompose a design.md into a DAG of small features, each on its own git worktree+branch off a long-lived epic branch, squash-merged back, then opened as a single PR. Halt only when t

Packages

Package details

skillprompt

Install pi-epicflow from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-epicflow
Package
pi-epicflow
Version
0.14.2
Published
Jun 9, 2026
Downloads
not available
Author
shankar029
License
MIT
Types
skill, prompt
Size
1 MB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "skills": [
    "./skills"
  ],
  "prompts": [
    "./prompts"
  ]
}

Security note

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

README

pi-epicflow

Ship multi-feature work as one clean PR. A pi extension that decomposes a design.md into a DAG of small features, runs each on its own git worktree + short-lived branch, squash-merges back into a long-lived epic branch, and opens a single reviewable PR to main when the whole epic is done. Halts only when truly blocked.

v0.5 adds a hybrid planning architecture: every feature writes a binding plan before any code edit, and features tagged needs_planner: true get a dedicated feature-planner subagent pass that produces a contract plan.md. Spikes (kind: spike) are first-class features whose deliverable is a decision artifact in deviations.md, not code — use them to resolve open questions before they corrupt downstream features.

smoke license: MIT pi >= 0.74


What is this, and what does it need?

pi-epicflow is a plugin for pi, an open-source terminal coding agent. It is not a standalone CLI or an IDE extension — you run pi in your repo, and this package adds two skills (epic-feature-workflow, project-memory), 9 slash commands, 11 sub-agent personas, and 9 CLI scripts that pi knows how to invoke.

If you've never used pi:

  1. Install pi first — see pi.dev (one-line installer; works on macOS, Linux, WSL, Windows).
  2. Then install this extensionpi install npm:pi-epicflow@^0.14 (full quickstart below).
  3. Use it via picd your-repo && pi, then run a slash command like /project-init or /epic-design.

If you've used Claude Code, Aider, or Cursor: pi is in the same family (a terminal agent that edits your repo). pi-epicflow is the layer on top that turns pi from "smart pair-programmer" into "autonomous shipper of multi-feature epics with a persistent project brain."

Without pi installed, nothing in this package does anything.



The problem this solves

When you ask an AI coding agent to ship a non-trivial change end-to-end — 5–20 features, multiple files, real tests — the day-1 workflow falls apart:

  • Context budget. Implementing a dozen features in one context window burns tokens, slows every turn, and makes the agent fragile to long-tail errors. Each new line of code makes every prior line more expensive to re-read.
  • Unreviewable PRs. A 5,000-line single-PR change-set is a rubber-stamp request, not a review. You spot the problems three weeks later in prod.
  • No checkpoint. When a test breaks at hour 3, or the agent misinterprets a spec, or your laptop runs out of battery, the run restarts from zero. There's no "resume from feature 7".
  • Silent scope drift. Without an explicit per-feature scope, the agent helpfully edits files no one asked about. The reviewer can't separate intentional changes from drift.
  • No memory across runs. Mistakes the agent made on epic N — the ambiguous spec, the misunderstood acceptance criterion, the test-fixture trap — get made again on epic N+1 because nothing fed back into the agent's instructions.

pi-epicflow is the workflow + tooling that fixes all five. The shape is straightforward: human + agent co-design → agent decomposes into a DAG of 20–60-minute features → each feature runs on its own git worktree → squash-merges into a long-lived epic branch → single PR to main when the whole epic completes. Halts are explicit, deviations are logged, and a lessons.md grows after every epic so the agent literally gets better at decomposing the next one.

How it works — the mental model

  design.md  ────────────────────┐
  (you write this, in pi or alone)  │
                                    │
                                    ▼
                          ┌───────────────┐
                          │ /epic-design    │    pi co-authors design.md
                          │ (in-place)      │    in `.pi/epics/<id>/`
                          └────────┬────────┘    ingests BRDs first,
                                   │             gist→approve→write
                                   ▼
                          ┌───────────────┐
                          │ /epic-review-   │   (optional) unbiased
                          │   design        │    critic sub-agent pass
                          └────────┬────────┘    before decomposition
                                   │
                                   ▼
                          ┌───────────────┐
                          │ /epic-decompose │    pi proposes 3–7 features
                          │ (one turn)      │    with deps, scope, ACs
                          └────────┬────────┘    you approve once
                                   │
                                   ▼
           decomposition.yaml (committed)
                                   │
                                   ▼
                          ┌───────────────┐
                          │ /epic-run-auto  │   orchestrator loop
                          └────────┬────────┘
                                   │
              for each ready feature in DAG order:
                                   │
      ┌────────────────┬───────┴───────┬───────────────────┐
      ▼                  ▼               ▼                      ▼
  worktree+branch    [feature-planner]  feature-worker         feature-reviewer
  spawned            subagent (only     subagent (fresh        subagent (fresh
  off epic branch    when needs_planner context, reads plan.md context, validates
                     :true; writes      if present, mandatory  plan-vs-impl +
                     plan.md)           plan section in §4)    AC + scope)
                                                              │
                                                              ▼
                                                       squash-merge into
                                                       epic branch, delete
                                                       branch+wt, archive
                                                       feature, deviations.md
                                   │
                                   ▼
                          all features merged?
                                   │
                                   ▼
                          ┌───────────────┐
                          │ epic-reviewer  │    final pre-PR pass over
                          │ subagent       │    the cumulative diff
                          └────────┬───────┘
                                   ▼
                          ┌───────────────┐
                          │ pi-epic-complete│   rebase epic onto main,
                          │                 │   distill deviations →
                          └────────┬────────┘   global lessons.md,
                                   ▼                push, open ONE PR
                          single PR → main
                          (one clean, reviewable diff)

Five keys to why this scales where naive "agent in one big context" doesn't:

  1. Each feature gets a fresh subagent context. A worker that spends 80 KB of tokens implementing F03 doesn't pollute the orchestrator's context or F04's worker's context. The orchestrator's own context grows only by the size of each feature's 1 KB worker-report.
  2. Each feature gets its own git worktree. No git stash dance. No branch-switching mid-implementation. Workers can in principle run in parallel; today they're serialized for atomic squash-merge into the epic branch.
  3. The decomposition is YAML, not chat. Once approved, it's the contract. Any departure goes into deviations.md with a reason. Reviewable. Diffable. Version-controlled.
  4. Plan before code, always. Every worker fills a structured Plan section in feature.md §4 (files-to-touch, AC interpretations, ambiguities, anti-scope) before the first edit. Features tagged needs_planner: true additionally get a dedicated feature-planner subagent pass that produces a binding plan.md — the worker treats it as a contract, and the reviewer checks plan-vs-impl alignment. Surfaces ambiguity at planning time (cheap) instead of implementation time (expensive).
  5. Halts, not guesses. When the agent is unsure (planner can't resolve, test fails 3x, merge conflict, ambiguous spec), it writes a halt report with the exact resume command and stops. A bad guess at hour 3 wastes hours; a halt loses minutes.

Deeper rationale lives in docs/design.md.

Two pillars

pi-epicflow ships two complementary pillars. Use either, or both.

Pillar 1 — Epic workflow (what this README has been about so far)

Multi-feature deliverables. design.md → DAG of small features → worktree-per-feature → squash-merge → single PR. The three-command flow (pi-epic-init, /epic-decompose, /epic-run-auto) and the worked example below cover it.

Pillar 2 — Project memory (new in v0.13, expanded in v0.14)

Persistent, file-based project brain that pi reads on entry and writes to autonomously — no slash commands required for routine writes. Solves the everyday pi pain that the epic workflow doesn't touch:

  • Decisions made on Tuesday evaporating by Wednesday.
  • Out-of-scope items getting forgotten the moment the session ends.
  • Pi re-asking the same question across sessions.
  • Pi shipping TODO: implement later stubs.
  • Pi drifting from "fix the auth bug" into a user-model refactor nobody asked for.
  • (v0.14) Re-stating the same personal/team rules in every repo's conventions.md.

Nine artifacts in .pi/project/ (six core + three Phase 2 from v0.14):

File Purpose Since
index.md Always-loaded router with progressive-disclosure "Read for X" table (≤150 lines) v0.13 (rewritten v0.14)
charter.md Goal, non-goals, quality bar, owner persona v0.13
conventions.md Always/never rules — includes hard anti-stub rule v0.13
decisions.md ADR-lite log of choices and alternatives v0.13
backlog.md Deferred work, each entry tagged with revisit-trigger v0.13
sessions.md Per-session log: goal, status, summary, linked DEC/BL ids v0.13
gotchas.md Standalone gotcha log (was a section in decisions.md). G-NNN ids. v0.14
questions.md Tracked open questions until resolution. Q-NNN ids; resolution writes DEC-NNN with resolves: Q-NNN. v0.14
modules/<name>.md Optional per-module card (purpose, public surface, dependencies, gotchas). User-authored from _template.md. v0.14

Plus one optional cross-repo overlay at ~/.pi/global-memory/ (v0.14):

~/.pi/global-memory/      # opt-in; one per user account, not per repo
├── index.md             # routing for the overlay
├── charter.md           # personal/team identity (optional)
├── conventions.md       # cross-repo always/never rules (GC-NNN)
└── decisions.md         # cross-repo defaults (GD-NNN), e.g. "ruff+uv for all Python"

Loaded after per-repo .pi/project/ on session entry. Per-repo always wins on conflict. Trigger phrases for global writes are explicit ("globally always X" / "across all my repos" / "in every project of mine"); bare "always do X" still fires the per-repo trigger. See DEC-006.

Five mechanics make it work:

  1. Stated session goal. Pi asks for the goal on the first non-trivial turn (proposing one if it can infer). The goal becomes a guardrail — off-goal turns prompt "park or pivot?". Pi proactively proposes closing the session on goal achievement.
  2. Trigger-driven writes. Pi watches for "defer", "decided", "always do X", "out of scope", "open question" and writes the entry the moment the phrase fires — not at session end. Survives crashed sessions (the ASSUME INTERRUPTION operating principle from v0.13.2).
  3. End-of-task sweep. Before any "done" report, pi re-scans for missed triggers and diffs code-changed vs brain-recorded.
  4. Append-only with supersedes. Nothing is edited or deleted. Reversals are new entries pointing to what they replace. Capacity caps trigger user-confirmed archive rollovers, never silent deletes — stable ids never recycle across archives (v0.14).
  5. Steward + specialists. The main session stays small and on-goal. Substantive work is delegated to custom epicflow-* sub-agent personas with bounded budgets and strict output contracts.

Six custom sub-agent personas (replace the generic pi-subagents ones, which drift and time out):

  • epicflow-scout — read-only repo recon, ≤30 reads
  • epicflow-researcher — web research, ≤4 queries, citations required (with curl fallback when pi-web-access isn't installed)
  • epicflow-worker — bounded impl (≤5 files), anti-stub self-check
  • epicflow-reviewer — 7-check diff review, anti-stub grep
  • epicflow-oracle — top-3-risks architectural critique
  • epicflow-stewardbrain-only maintenance with strict write-allowlist (.pi/project/ and ~/.pi/global-memory/ only). Refuses any code / test / git / config edit. Delegation target for unattended /project-review sweeps across multiple repos. (v0.14)

Five slash commands (mostly optional):

/project-init           # one-shot scaffold of .pi/project/ for this repo
/project-init-global    # one-shot scaffold of ~/.pi/global-memory/ (v0.14, run once per user)
/project-onboard        # optional warm-up summary at session start
/project-review         # periodic audit + ripe-backlog surfacing; A-0..A-8 checks
/session-end            # manual force-close (pi normally proposes this)

Day-to-day, you invoke none of these except the two one-shot inits. The project-memory skill is autoloaded in any repo with .pi/project/ and handles reads / writes / goal-tracking transparently.

Capacity & rollover (v0.14). Each artifact has a soft cap; when exceeded, /project-review recommends a manual archive split (e.g. git mv decisions.md decisions-archive-2026.md, start a fresh live file, log a row in index.md). Caps: decisions 500/2 yr, backlog 200/180 d open, sessions 150/1 yr, gotchas 200/2 yr, questions 50 open + 200 resolved / 1 yr open. Conventions and charter are uncapped. Stable ids never recycle.

See:


Two modes (for the epic workflow)

  • Auto mode (default, recommended) — the three slash commands above. /epic-decompose and /epic-run-auto drive the whole pipeline, delegating each feature to a feature-worker subagent and each pre-merge review to a feature-reviewer subagent. Requires pi-subagents.
  • Manual mode — you call the pi-* CLI scripts from your shell or from a regular pi session, doing the implementation yourself or in pi's main context. Same scripts, same state on disk, same halt codes — just no subagent delegation. Useful when subagents aren't available, when you want to inspect every step, or when one of the features genuinely needs a human.

Mix freely: start in auto mode, drop to manual for a tricky feature, hop back into auto. The on-disk state in .pi/epics/<id>/ is the single source of truth and doesn't care which mode wrote it.

A worked example: shipping todoq (a tiny Python CLI)

Given a design.md that specifies a four-command todo CLI (add, list, done, rm) with JSON-backed storage, here's the full transcript:

# 1. Bootstrap the epic from your shell.
$ cd ~/code/todoq && git checkout main && git pull
$ pi-epic-init todoq --from /tmp/todoq-design.md
[epic-workflow] created branch: epic/todoq
[epic-workflow] seeded design.md from /tmp/todoq-design.md

# 2. Open pi in the repo.
$ pi

Then in pi:

you ▸ /epic-decompose
pi  ▸ [reads design.md + lessons.md + epic-config.yaml]
      [proposes the YAML below + ASCII dep-graph]
      F01 storage          ──┐
      F02 add-and-list      ├── F03 done-and-rm
      F01 storage          ──┘
      Looks good, or want changes?
you ▸ approved, write it and commit
pi  ▸ [detects pyproject.toml → proposes test_cmd: python -m pytest -q]
      [writes decomposition.yaml + epic-config.yaml]
      [runs pi-epic-validate-decomposition → OK, 3 features, DAG valid]
      [git commit -m "decomp: 0001-todoq into F01..F03"]
      decomposition.yaml committed as 49a2071.
      next: /epic-run-auto

you ▸ /epic-run-auto
pi  ▸ ─── EPIC STATUS ───
      epic: 0001-todoq  branch: epic/todoq
      phase: spawning F01
      ...
      ───────────────────
      [spawns feature-worker on ../todoq-F01]
      [worker implements + tests → pytest -q green]
      [spawns feature-reviewer → APPROVE]
      [pi-feature-complete F01 → squash-merge into epic/todoq]
      ─── EPIC STATUS ───
      phase: spawning F02
      done: F01 ✓
      ...

…repeating for F02 and F03, finishing with an epic-wide review pass and a push of epic/todoq. The PR step opens gh pr create if gh is on your PATH; otherwise it prints the command for you to run.

Total wall-clock for 3 small features: ~15 minutes. Two STATUS heartbeats per feature, zero attention required between them.

Want something bigger? The 12-feature minikv validation epic (an in-memory KV server with snapshots, expiry, and an admin CLI) ran end-to-end in ~87 minutes — 7-level DAG, four features touching the same server.js, zero stalls, zero merge conflicts, 119/119 tests green.

When NOT to use this

  • Single-PR changes. If the work fits in one PR, just open it. The workflow's overhead pays off starting at ~3 features.
  • Cross-repo work. One epic = one repo. For polyrepo changes, run parallel epics and coordinate via PRs.
  • Multi-user concurrent. Two humans on one epic branch is not supported — there's no locking on .pi/epics/<id>/. Coordinate via PR review or work on different epics.
  • Throwaway exploration. Spelunking, prototyping, "what if we tried…" — just run pi without ceremony. Use this when you've decided to ship.

Table of contents


Install

From GitHub (recommended for v0.x)

# globally (writes to ~/.pi/agent/settings.json)
pi install git:github.com/shankar029/pi-epicflow

# or pin to a tag
pi install git:github.com/shankar029/pi-epicflow@v0.1.0

# or project-scope only (writes to ./.pi/settings.json)
pi install -l git:github.com/shankar029/pi-epicflow

What the install does

  1. Registers the skill (epic-feature-workflow) and prompt (/epic-run-auto) so pi loads them in any session.
  2. Runs install/postinstall.mjs which:
    • Copies feature-worker.md, feature-reviewer.md, and feature-planner.md into ~/.pi/agent/agents/ so pi-subagents can discover them.
    • Symlinks pi-epic-* and pi-feature-* from the package into ~/.local/bin so you can call them from any shell.
    • Auto-installs npm:pi-subagents (required for auto mode) and npm:pi-intercom (recommended) at the same scope you installed pi-epicflow — so the first /epic-run-auto doesn't have to stop and install them mid-run. Already-installed packages are detected and skipped. Set PI_EPICFLOW_NO_AUTOINSTALL_DEPS=1 to skip this step.
  3. Prints a hint if ~/.local/bin isn't on your PATH yet.

All side-effects are idempotent and defensive — they never clobber your local edits; on conflict they write *.new siblings and warn. If the auto-install of pi-subagents/pi-intercom fails (offline, registry down, etc.) the pi-epicflow install itself still succeeds and prints the exact manual command.

Verify:

which pi-epic-init pi-feature-start pi-feature-complete   # all should resolve
ls ~/.pi/agent/agents/feature-*                            # 3 files: worker, reviewer, planner
pi list | grep -E 'pi-subagents|pi-intercom'               # both should appear

Skipping the auto-install of pi-subagents / pi-intercom

Auto mode needs pi-subagents; manual mode does not. If you only ever intend to drive the workflow from your shell (no /epic-run-auto), you can opt out of the auto-install:

PI_EPICFLOW_NO_AUTOINSTALL_DEPS=1 pi install git:github.com/shankar029/pi-epicflow

You can install them later by hand whenever you want auto mode:

pi install npm:pi-subagents
pi install npm:pi-intercom   # nicer in-chat prompts; optional but recommended

Quickstart — the three-command flow

From your repo, on the branch you want to PR into:

Using git worktree? If your repo uses a bare-clone + worktrees layout (e.g. created via mkrepo.sh), cd into the worktree subdir first — the outer bare-clone dir isn't a working tree. See docs/git-worktrees.md for the full walkthrough.

# 1. Bootstrap the epic. Creates the epic branch + journal folder.
pi-epic-init my-feature --from /path/to/design.md

# 2. Open pi in the repo.
pi

Then in the pi chat:

/epic-design             # pi co-authors design.md in-place (ingests BRDs first, gist→approve→write)
/epic-review-design      # (optional) unbiased critic sub-agent pass
/epic-decompose          # pi proposes features, you approve, pi writes + validates + commits
/epic-run-auto           # pi ships every feature, runs the reviewer, opens the PR

That's the whole workflow. Four commands (three if you skip the critic), no file paths to memorize.

/epic-run-auto is self-bootstrapping: if you skip /epic-decompose, it'll run that flow first automatically. Pass --no-bootstrap to force a halt instead if you'd rather decompose by hand.

If you want to drive the loop manually (no subagents, no auto-mode), the manual-mode section below shows the bare shell commands behind each step.


Quickstart — manual mode

Drive the workflow by hand from your shell. Best when you want pi in the loop only for parts of the work, or for environments where pi-subagents isn't available.

# 0. one-time per repo: stand on the branch you want to PR into
cd ~/code/myrepo
git checkout main && git pull

# 1. write a design.md somewhere (in or out of the repo) describing the goal
$EDITOR /tmp/my-design.md

# 2. initialize an epic (creates an epic/<slug> branch + .pi/epics/<id>/ tree)
pi-epic-init my-feature --from /tmp/my-design.md --title "Add X to Y"

# 3. write decomposition.yaml — list the features, deps, scope_files, ACs
$EDITOR .pi/epics/0001-my-feature/decomposition.yaml
pi-epic-validate-decomposition
git add .pi/ && git commit -m "decomp"

# 4. loop: start, implement (anywhere, in any way), complete
while next=$(pi-epic-next-feature); [ "$next" != "DONE" ] && [[ "$next" != HALT:* ]]; do
  pi-feature-start "$next"                      # creates feat/<slug>/<fid>-... worktree
  # … implement under the printed worktree path, commit on the feature branch …
  pi-feature-complete "$next"                   # runs tests, squash-merges, archives
done

# 5. wrap up
pi-epic-complete                                # rebases, distills lessons, opens PR

pi-epic-status will give you a one-screen view of where the epic is at any point.


Quickstart — auto mode (deep-dive)

The three-command flow above is auto mode. This section explains what each slash command actually does so you can debug if anything goes sideways.

After pi install npm:pi-subagents:

cd ~/code/myrepo
git checkout main && git pull
$EDITOR /tmp/my-design.md
pi-epic-init my-feature --from /tmp/my-design.md

# fresh pi session in the same dir
pi
# inside pi:
/epic-decompose                  # propose + approve + commit decomposition.yaml
/epic-run-auto                   # run the loop

What you'll see in chat:

─── EPIC STATUS ───
epic:    0001-my-feature  branch: epic/my-feature
phase:   spawning F02
done:    F01 ✓
ready:   F03, F04 (waiting)
last:    F01 merged (APPROVE, 1 worker run, 1 review cycle)
budget:  features merged 1/4, deviations 0, halts 0
───────────────────

…repeating for every transition. On any blocker, you get a clearly-marked ─── EPIC HALTED ─── block with a halt-report path and an exact resume command.

Optional flags after /epic-run-auto:

  • --max-features=N — stop after N features merged this run (useful for iterative review).
  • --no-reviewer — skip the per-feature feature-reviewer pass.
  • --no-bootstrap — halt instead of auto-running /epic-decompose when decomposition.yaml is empty.

Optional flags after /epic-decompose:

  • --features=N — ask pi to aim for N features (default 3–7).
  • --auto-commit — skip the "commit this?" prompt at the end.

What gets created on disk

your-repo/
├── .pi/
│   ├── STATE.md                                # pointer: active epic + active feature
│   └── epics/
│       └── 0001-<slug>/                        # one folder per epic, numbered
│           ├── meta.yaml                       # status, branch, links, timestamps
│           ├── design.md                       # the input you wrote
│           ├── decomposition.yaml              # features + deps + ACs
│           ├── epic-config.yaml                # test_cmd, budgets, worktree pattern
│           ├── deviations.md                   # append-only log of design departures
│           ├── lessons-candidate.md            # distilled by pi-epic-complete
│           ├── run-log.jsonl                   # one line per event (gitignored)
│           ├── halt-<UTC>.md                   # only on halt (gitignored auto-commit)
│           ├── epic-review.md                  # final pre-PR review (auto mode)
│           └── features/
│               ├── <fid>-<slug>/               # active feature folder
│               │   ├── meta.yaml
│               │   ├── feature.md
│               │   ├── worker-report.md        # auto mode (gitignored)
│               │   └── review-report.md        # auto mode (gitignored)
│               └── done/<fid>-<slug>/          # archived after squash-merge
└── ../<repo>-<fid>/                            # per-feature git worktree

Branches:

  • epic/<slug> — long-lived; receives squash-merges; pushed when the epic completes.
  • feat/<epic-slug>/<fid>-<slug> — short-lived; deleted by pi-feature-complete after squash-merge.

Scripts

Script Job
pi-epic-init <slug> [--from <design-file>] [--base <branch>] [--no-planner] Create the epic folder + branch + STATE.md. --base overrides the parent branch (default: repo's default branch); recorded in meta.yaml and used by pi-epic-complete as the PR target. --no-planner disables the feature-planner subagent for every feature in this epic (disable_planner: true in meta.yaml). Auto-commits a .gitignore for pi runtime state.
pi-epic-validate-decomposition Sanity-check decomposition.yaml: no cycles, no unknown deps, IDs unique, scope_files unique, etc.
pi-epic-next-feature Print the next feature id to work on. Prefers any in-progress feature (resume) over the lowest-numbered ready one. Prints DONE when all merged, HALT:<reason> on DAG corruption.
pi-feature-start <fid> Create the feature worktree + branch, write feature.md / meta.yaml, auto-commit any pending .pi/epics/<id>/ edits (except halt-*.md) on the epic branch, advance epic status: design → in-progress on first call.
pi-feature-complete <fid> [--skip-tests] Run the epic's test_cmd on the feature branch, squash-merge it into the epic branch, delete the feature branch + worktree, archive the feature folder to features/done/.
pi-epic-status One-screen overview: epic, branch, done/in-flight/ready, deviations count, halts.
pi-epic-complete [--no-pr] [--draft] Rebase the epic branch onto the latest default branch, run the full test suite, distill deviations.mdlessons-candidate.md → global lessons.md, push, and (if gh is available) open the PR. Archive the epic to .pi/epics/done/.

All scripts share _common.sh (yaml read/write, slugify, git helpers) and each prints actionable error messages on failure.


Halt codes

The orchestrator (and the manual workflow, via the scripts) halts rather than guess when:

Code Trigger Operator action
H1 Tests failing after retries, or post-merge tests red Inspect; fix or clarify ACs; resume
H2 Dirty working tree outside .pi/epics/<id>/ Commit/stash/revert outside the journal scope; resume
H3 pi-epic-next-feature returned an unknown feature id (decomposition drift) Reconcile decomposition.yaml vs features/; resume
H4 Feature failed review 3+ times Inspect review reports; rewrite scope or AC; resume
H5 Environment fatal — disk full, git corrupt, missing toolchain Fix the host; resume
H6 Merge conflict on squash-merge into epic branch Resolve on epic branch (pi-feature-complete --skip-tests); resume
H7 Subagent stalled past the §STALL HANDLING budget (auto mode only) Inspect last forensics; decide manual takeover or respawn
H9 feature-planner subagent returned BLOCKED (unresolvable ambiguity, missing call sites, contradictory AC) Read plan.md + planner-report.md; fix decomposition AC or run a spike; resume

Every halt writes .pi/epics/<id>/halt-<UTC>.md with the failing step, the worker/review reports involved, what the human needs to decide, and the exact resume command.


FAQ

Q: How is this different from just opening a feature branch and asking pi to implement it? The single-branch approach works for changes that fit in one context window (~one PR's worth). pi-epicflow buys you (a) context isolation per feature via subagents, (b) resumability — you can crash, switch machines, or come back tomorrow and /epic-run-auto again, (c) review-friendly history — the epic branch is one squash-commit per feature, not one bag of agent intermediates, and (d) cross-epic learning via lessons.md. Below ~3 features it's not worth the ceremony; above ~5 it pays for itself many times over.

Q: Do I have to use auto mode? No. Manual mode is a first-class path — same scripts, same on-disk state, same halt codes, you're just the one calling pi-feature-start / pi-feature-complete from your shell. Use auto mode when you want to walk away; use manual mode when you want a human in the loop on every feature.

Q: Does pi-epicflow work with git worktree? Yes — it uses worktrees automatically. Every feature gets its own per-feature worktree (<repo>-F01-<slug>/) created by pi-feature-start and removed by pi-feature-complete. You don't manage these directly. If your own checkout uses a bare-clone + worktrees layout (e.g. via mkrepo.sh), see docs/git-worktrees.md for the two valid setups (reuse default-branch worktree vs dedicated epic worktree) and the common gotchas (not in a git repository from the outer bare dir, seeding an epic from a non-default branch, etc.).

Q: What if pi proposes a bad decomposition? You iterate. /epic-decompose shows the YAML in chat and asks for feedback before writing to disk. You can say things like "merge F03 and F04", "split F02 into model + CLI", "F05 doesn't really depend on F03". Pi revises and re-presents. Only on your explicit approval does it write + validate + commit. Then /epic-run-auto is bound by that contract — any departure goes into deviations.md with a reason.

Q: What is the feature-planner subagent and when does it run? A pre-implementation pass that produces a binding plan.md for features flagged needs_planner: true in decomposition.yaml. The planner reads the design, the decomposition entry, any reference_paths: (POC code, prior-art docs), and the repo — then writes files-to-touch, AC interpretations with literal expected behavior, ambiguities, and anti-scope. The worker treats plan.md as a contract; the reviewer checks plan-vs-impl alignment. Triggered by a 7-item checklist in /epic-decompose (any 2 of: unverified-callsites, format-sensitive-ac, scope-crosses-modules, deep-dep-chain, large-estimate, many-acs, cross-cutting-verb). Threshold tunable via PI_EPICFLOW_PLANNER_THRESHOLD. Disable per-feature with needs_planner: false, or per-epic with pi-epic-init --no-planner.

Q: What is a spike? A feature whose deliverable is a decision, not code. Used when an open question blocks 2+ downstream features ("CRC32 vs xxhash?", "which seam do we decorate?", "hand-roll parser or use lark?"). Declared in decomposition.yaml as kind: spike with S<NN> ids that share a counter with features. Spike workers investigate (read code, prototype, benchmark), then write a structured Decision / Evidence / Impact entry in deviations.md. pi-feature-complete skips tests for spikes and squash-merges the decision artifact into the epic branch. feature-reviewer runs in spike-mode and checks decision quality, not test results. Spikes are capped at 8 estimated hours.

Q: What if a feature's tests fail? The feature-worker retries up to 3 times with different strategies. After that it returns BLOCKED. The orchestrator escalates: spawns a fresh worker once, and if still BLOCKED, halts with H1 and writes .pi/epics/<id>/halt-<UTC>.md with the failing tests, the worker's notes, and the exact resume command. You fix the underlying issue (bad AC, misunderstood spec, flaky test) and re-run /epic-run-auto — it picks up exactly where it left off.

Q: Can I run features in parallel? Not today. The orchestrator serializes feature execution so that pi-feature-complete's squash-merge into the epic branch is atomic. Worktrees support parallel execution structurally — lifting the serialization requires a per-feature lock around the merge, which is on the roadmap.

Q: What happens if I close pi mid-run? Nothing breaks. All state is on disk in .pi/epics/<id>/. Open pi again, type /epic-run-auto, and it picks up from the next ready feature. If a worker was mid-flight when you closed, the in-progress feature's worktree is still there — the dispatcher prefers in-progress features over starting new ones (lesson L-010), so the same feature resumes.

Q: Does this work on Windows? WSL only. The scripts assume Unix paths, symlinks, and standard git worktree. Native Windows is untested and likely broken.

Q: Does it work with pi -p / non-interactive sessions? Manual mode does — the pi-* scripts are pure CLI. Auto mode is built around a chat session because the orchestrator posts STATUS heartbeats and handles stall notices. You can run /epic-run-auto non-interactively but you lose the visibility into what's happening; not recommended.

Q: How do I update?

pi update git:github.com/shankar029/pi-epicflow

Restart any open pi session to pick up new prompts/skills (they're loaded at session start). The bin symlinks update automatically.

Q: Does this lock me in? No. Everything pi-epicflow produces is plain git — branches, worktrees, squash commits, and a .pi/ directory of YAML/markdown state. If you stop using it tomorrow, nothing in your repo breaks. You can also drive the entire workflow manually from your shell with no pi at all.


Design

The deeper design rationale (worktree topology, why squash-merge, the deviation log, lesson distillation, the orchestrator state machine, etc.) is in docs/design.md.

The orchestrator's behavior contract is in prompts/epic-run-auto.md — read this if you're writing your own orchestrator or want to understand exactly what /epic-run-auto does.

The skill's instructions to pi are in skills/epic-feature-workflow/SKILL.md.


Lessons

skills/epic-feature-workflow/lessons.md is an append-only log of empirical rules discovered during real runs (e.g. "decomposition AC should name the error class when it says 'throws'", "halt files must not ride the auto-commit train", "prefer in-progress over ready in the dispatcher to avoid leaking worktrees"). New lessons get appended each time pi-epic-complete distills deviations.md from a finished epic.

L-001 through L-052 are documented today (v0.8.1 added L-050 — derive install lists from repo contents, never from a hardcoded parallel list (postinstall.mjs's hardcoded 3-agent list shipped a broken v0.7.0 L-043 gate for four releases until v0.8.0 real-app verification caught it); L-051 — workers should contact_supervisor before installing dependencies that touch shared metadata files (package-lock.json, Cargo.lock, etc.), not just self-report deviations after the fact (under parallel mode this pattern is a real H6 risk); L-052 — pi-epic-complete must tolerate "no origin remote" for offline + sample workflows; v0.8.0 added L-048 — in-process orchestrator queue beats IPC for single-host parallelism (no flock, no lock-dir, coordination lives in the orchestrator's loop variables) and L-049 — conflict pre-check from declared scope_files is the cheap mechanical guard against parallel-merge collisions (data is already in the decomposition; false positives serialize when they could've paralleled; false negatives reduce to existing scope-discipline rule); v0.7.3 added L-047 — heuristics must be verified on a real app, not just smoke fixtures; real-app verification on a Vite+React TODO sample surfaced two compounding L-045 bugs synthetic fixtures could not have caught (App.tsx missing from the ts_react shell list AND wiring targets buried behind config files in the hint); release-checklist now requires a real-app verification pass for any heuristic-shaped feature; v0.7.2 added L-046 — detect + suggest, not detect + install; epic-config.yaml gains a required_toolchain block ({name, min_version, validate_cmd, install_hint} entries) and pi-epic-validate-decomposition runs each validate_cmd and refuses to validate if any fails, emitting the install_hint verbatim for the operator to run; pi-epicflow intentionally does NOT auto-install (security/portability/state-pollution/concern-boundary rationale spelled out in L-046); .mise.toml / .tool-versions defer to the toolchain manager; --skip-toolchain-check bypass; v0.7.1 added L-045 — integration shells are part of the work, not part of the toolchain; ~58 deviations across harmony + gen-ui all shared the same shape (worker built the new thing but forgot to wire it into the host app); pi-epic-validate-decomposition now refuses decompositions where AC contains trigger verbs (wire/register/integrate/etc.) without a language-appropriate integration shell in scope_files; --skip-shell-check bypass; L-044 was distilled the same day v0.7.0 shipped — public-facing surface lags code by default; codified in docs/RELEASE-CHECKLIST.md so future releases bump the site, the README lesson pointer, and any other landing surface as a hard gate; v0.7.0 added L-043 — per-feature reviewers are blind to cross-feature bugs by design; harmony's stale-lockfile B1 and gen-ui's MapHarmonyAgent no-op stub motivated the feature-epic-reviewer agent + pi-epic-complete epic-review gate; v0.6.3 added L-042 — framework epics need explicit verification features (sample apps, conformance harnesses) in the original decomposition, motivating pi-epic-extend as a first-class verb; v0.6.2 added L-036/L-037/L-038/L-039/L-040/L-041 from the harmony + gen-ui retrospectives; v0.6.1 added L-035 — defer the parallel-dispatch orchestrator, ship pi-epic-status --ready as the manual-parallelism aid; v0.6.0 added L-032/L-033/L-034 from the obra/superpowers review and the v0.6 verification-gate + soft-halt release; v0.5.2 added L-028/L-029/L-030/L-031 from the gen-ui epic decomposition review). Contributions of new lessons via PR are welcome and encouraged.


Uninstall

pi remove git:github.com/shankar029/pi-epicflow

This removes the package from pi's settings. The postinstall side-effects (symlinks under ~/.local/bin/, agent files in ~/.pi/agent/agents/) are not automatically reverted — they may be hand-edited and the install script is deliberately non-destructive. Remove them by hand if desired:

rm ~/.local/bin/pi-epic-* ~/.local/bin/pi-feature-*
rm ~/.pi/agent/agents/feature-worker.md ~/.pi/agent/agents/feature-reviewer.md ~/.pi/agent/agents/feature-planner.md

Compatibility

  • pi ≥ 0.74 (uses the packages settings schema and the standard skill loader).
  • node ≥ 18 (postinstall script uses node:fs, ESM, top-level await).
  • git ≥ 2.20 (git worktree).
  • python3 ≥ 3.8 (YAML helpers + validation; stdlib only).
  • Shell:
    • Linux / macOS: bash ≥ 4.x. Scripts in skills/epic-feature-workflow/scripts/.
    • Windows: PowerShell ≥ 5.1 (preinstalled). PowerShell-native siblings in skills/epic-feature-workflow/scripts-win/. WSL is not required and not recommended for .NET / NuGet / ADO feed workflows. The postinstall detects process.platform === "win32" and installs pi-<name>.cmd shims into BIN_DST that exec the .ps1 with -ExecutionPolicy Bypass, so corporate Restricted policies are not a blocker. Status: v0.12.0 ships Phase 1 (init + doctor); see PLAN-v0.12.0.md for phase rollout.
  • OS: Linux ✅, macOS ✅ (best-effort — same bash scripts), Windows ✅ (native PowerShell, as of v0.12.0).
  • Auto mode additionally requires pi-subagents ≥ 0.24. pi-intercom is optional but improves the in-chat prompts.

Windows-specific setup

  1. Install Git for Windows (≥ 2.20) — ships with the git binary.
  2. Install Python ≥ 3.8 from https://www.python.org/downloads/windows/ (the Windows Store stub is auto-detected and skipped; install the real interpreter).
  3. Recommended one-time git config for the per-feature worktrees:
    git config --global core.longpaths true
    
    Also enable Windows long-path support system-wide via HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled = 1 (or Group Policy: Computer Configuration → Administrative Templates → System → Filesystem → Enable Win32 long paths).
  4. Run pi install git:github.com/shankar029/pi-epicflow from any PowerShell or cmd.exe. The postinstall picks the Windows scripts automatically.
  5. Verify with pi-epicflow-doctor — it runs Windows-specific checks (PowerShell version, ExecutionPolicy, core.longpaths, etc.) and prints actionable hints for anything missing.

License

MIT. See LICENSE.