Edit Flow
You are creating and editing pi-flows flows and agents directly in this session. Two tools do the writing (both validate before writing and return diagnostics on failure):
flow_agents—op: "list"returns the agent catalog;op: "write"validates and writes an agent.mdto.pi/flows/agents/<name>.md(filename derived from the agent's frontmattername).flow_write—namespace(defaultcustom),name,content. Validates and writes a flow to.pi/flows/flows/<namespace>/<name>.yaml, which auto-registers as the/<namespace>:<name>command.
These tools derive their write locations from the discovery convention — there is no raw path. Writing to a name that already exists overwrites it (that is how you edit).
The edit-flow tools are off by default. They are active only when
flows.editFlow: trueis set in.pi/settings.json(project, when trusted) or~/.pi/agent/settings.json(global). Ifflow_agents/flow_writeare not available, tell the user to enable that setting and restart the session.
Workflow
- Clarify what the user wants the flow to do. Ask if it is unclear.
- Call
flow_agentswithop: "list"to see existing agents and theirinputs,outputs, andsource_type. Reusebuilt-in/localagents where they fit. - For each role not already covered, author a purpose-built agent with
flow_agentsop: "write". Do not repurpose infrastructure agents (flow-decision,project-context-reader) for unrelated tasks. - Author the flow with
flow_write. Wire every declared input. Fix any validation diagnostics and retry. - Tell the user the resulting command name (
/<namespace>:<name>).
Editing an existing flow/agent = read the current file, change it, then write it back with the same tool and the same name/namespace.
Agent files (.md)
YAML frontmatter + Markdown body (the system prompt).
---
name: code-reviewer
description: Reviews source code for quality and correctness
model: @coding
thinking: medium
tools: read, grep, find
inputs:
- research_context
outputs:
- name: findings
description: Categorized issues found
- name: verdict
description: "pass" or "fail"
access:
read:
- "src/**"
write:
- "src/**"
bash:
deny:
- "rm -rf *"
card:
label: "Reviewer"
metric: "default"
architect:
use_when: "User wants code reviewed for quality"
produces: "A findings report and a pass/fail verdict"
depends_on: "An implementer must have produced code"
domain: "review"
---
# System Prompt
You are a code reviewer. Task: ${{task}}
Context: ${{input.research_context}}
| Field | Required | Notes |
|---|---|---|
name |
Yes | Unique. Filename = <name>.md. Referenced as agent: <name> in steps. |
description |
Yes | One line. Shown in catalog. |
model |
Yes | See Model references. |
tools |
Yes | Comma-separated. Guard blocks anything not listed. Standard: read, write, edit, grep, find, ls, bash, ask_user, skill_read. |
thinking |
No | off/minimal/low/medium/high/xhigh. Overrides any :level suffix in model. |
skills |
No | Comma-separated skill names injected into the prompt. |
inputs |
No | Names → ${{input.NAME}} in the prompt. Flow step must wire each one. |
outputs |
No | Names (or {name, description} objects). Become finish parameters and ${{result.STEP.NAME}} downstream. |
interactive |
No | true allows mid-task UI prompts. Default false. |
output |
No | Output file hint (display only). |
access |
No | read/write glob allowlists; bash.deny command patterns. * = segment, ** = any depth. |
card |
No | label, metric (default/files/tests/custom), role. |
architect |
No | use_when/produces/depends_on/domain metadata surfaced in flow_agents op: list. |
Model references
The model: field accepts three forms. Prefer @role. Use the other two when a specific model is required regardless of role config, or when the user explicitly asks for a non-role model.
| Form | Example | When |
|---|---|---|
@role (preferred) |
model: @coding |
Default. Resolves via the active role→model map (/roles). Built-in roles: @planning, @coding, @fast, @architect. |
provider/model[:thinking] |
model: anthropic/claude-haiku-4-5:high |
A specific provider+model is required; optional :thinking suffix sets the thinking level. |
bare model-id |
model: claude-haiku-4-5 |
A specific model id with no provider qualifier; thinking comes from the thinking: field or none. |
A thinking: field always overrides any :thinking suffix in model.
Flow files (.yaml)
name: my-flow # REQUIRED
description: What it does # REQUIRED
max_concurrent: 3 # optional (default 4)
task_required: true # optional — prompt for task if invoked with no args
task_prompt: "Task:" # optional
steps:
- id: research
agent: code-reviewer
task: Review ${{task}}
name is the frontmatter name; the command name comes from the on-disk location (namespace/name you pass to flow_write). Every step needs a unique id. Step type is usually inferred from which fields are present; set type: explicitly when ambiguous.
Step types
- agent — dispatch an agent.
- id: impl agent: implementer task: Implement ${{task}} blockedBy: [research] inputs: ctx: ${{result.research.summary}} on_complete: verify # optional cross-segment jump on_error: handler # optional - fork — user (or
agent:in autonomous mode) picks a branch.
Branch keys must match- id: choose type: fork question: Which strategy? options: [Fast, Full] branches: { Fast: fast-impl, Full: full-impl } agent: flow-decision # autonomous-mode decider allowCustom: false multiSelect: falseoptionsexactly. - conditional — presence/absence of a result field.
- id: has-gaps type: conditional check: research.artifacts # stepId.field present: gap-filler absent: finalize - agent-decision — agent calls
finish({ branch })to choose.- id: complexity type: agent-decision agent: analyzer task: Simple or complex? branches: { simple: quick, complex: thorough } - agent-loop-decision — loop back or exit.
- id: should-fix type: agent-loop-decision agent: flow-decision task: Iteration ${{loop.should-fix.iteration}}/${{loop.should-fix.max}}. ${{result.verify.summary}} loop_target: fixer # jump back exit_target: done # continue forward max_iterations: 3 - flow-ref — delegate to another flow file.
- id: sub type: flow-ref path: "project/changes/*/exec.yaml" # glob ok on_complete: verify
Template variables
Expanded in task, inputs values, and question. Not validated — a typo silently becomes empty string.
${{task}}— the user task.${{input.NAME}}— input wired into this step.${{result.STEP_ID.status|summary|artifacts|files|fullOutput|OUTPUTNAME}}—STEP_IDis the stepid, not the agent name.${{loop.STEP_ID.iteration|max}}— loop counters.
Wire data between steps via inputs: (agent declares names in frontmatter, step supplies values). Prefix an input value with file:// to inject file content verbatim; that file's producer step must be in blockedBy.
Write locations (discovery)
| Content | Tool | Lands at |
|---|---|---|
| Agent | flow_agents op: write |
.pi/flows/agents/<name>.md |
| Flow | flow_write |
.pi/flows/flows/<namespace>/<name>.yaml → /<namespace>:<name> |
Project-local definitions (.pi/flows/) override package and built-in ones.
Fixing validation errors
Both tools validate before writing. On failure they return { written: false, diagnostics: [...] } and write nothing. Read each diagnostic's message and suggestion, fix the content, and call the tool again. Common cases:
- Missing required field (
name,description,model,toolsfor agents;name,descriptionfor flows) → add it. - "Agent not in catalog" → the flow references an agent that does not exist. Create it with
flow_agentsop: write, then retryflow_write. - Unwired declared input → add the missing key to the step's
inputs:block. - Unknown tool in
tools:→ use a valid tool name (see the standard list above) or an extension-registered tool name. - Invalid YAML → fix indentation/quoting; re-validate.