Chain any AI coding CLIs (Claude, Codex, Gemini, Aider, ...) into an approve-once pipeline.
Project description
agent-relay
Chain any AI coding CLIs into an approve-once pipeline.
English | 中文
You give a target. One agent plans, another reviews the plan, another implements it — automatically, with no copy-pasting between them. The only time it stops for you is the final gate, right before anything is committed.
target ─▶ ┌──────┐ PLAN.md ┌────────┐ review.md ┌──────────┐ ⏸ you
│ plan │ ──────────▶ │ review │ ──────────▶ │ implement│ ─────▶ commit?
└──────┘ └────────┘ └──────────┘
(claude) (codex) (codex)
Each arrow is an automatic hand-off; files on disk are the medium. Swap any box for a different agent by changing one line of YAML.
Why
Running a multi-agent workflow by hand means copy-pasting one tool's output into the next, over and over. Add a third agent and the transfers multiply. agent-relay makes the hand-offs automatic and gates only the decision that actually needs a human.
Supported agents
Built-in adapters: claude (Claude Code), codex (OpenAI Codex), gemini (Gemini CLI), aider, and generic — wrap any CLI from YAML with no code:
- name: implement
agent: generic
command: ["mytool", "run", "--prompt", "{prompt}", "--out", "{output}"]
Placeholders: {prompt} {output} {workspace} {model} {role}.
Install
pipx install relaypipe # once published
# or, from source:
pip install -e ".[dev]"
Quick start: idea → project (zero config)
The fastest path. Give an idea and a folder; agent-relay plans, loops the plan through review until it's solid, shows you the final plan, and — once you approve — builds the project into that folder. No YAML, no prompt files:
relaypipe new "a todo-list CLI in Rust with JSON persistence" -d ./todo-cli
What happens:
- plan — an agent drafts a build plan
- review loop — a reviewer critiques it; the plan is revised and re-reviewed
until
VERDICT: APPROVEDor the loop cap (default 3) is hit - ⏸ you approve — the final plan is printed; answer
yto build - build — the project is created in
./todo-cli
Useful options:
relaypipe new "..." -d ./app --agent codex # use Codex to plan+review+build
relaypipe new "..." -d ./app --agent claude --build-agent codex # mix agents
relaypipe new "..." -d ./app --max-iterations 5 # allow more review rounds
relaypipe new "..." -d ./app --dry-run # preview the commands
relaypipe new "..." -d ./app --yes # CI: skip the approval prompt
For full control over steps, agents, and gates, write a pipeline (below) and use
relaypipe run.
Use
# list available agents
relaypipe agents
# run the example pipeline
relaypipe run "Add a --json flag to the CLI" -p examples/pipeline.yaml
# preview commands without running anything
relaypipe run "..." -p examples/pipeline.yaml --dry-run
# resume an interrupted run
relaypipe run --resume -p examples/pipeline.yaml
# CI mode: auto-approve every gate
relaypipe run "..." -p pipeline.yaml --yes
# override the loop cap for this run (beats max_iterations in the YAML)
relaypipe run "..." -p pipeline.yaml --max-iterations 5
Example: agent-relay improving itself
The repo ships a self-hosted pipeline that points agent-relay at its own
codebase. It reads this project's PLAN.md, has one agent review a
roadmap item (agent-relay init), and has another implement it — pausing once
for your approval before you commit:
# preview the chain (reads PLAN.md, no API calls)
relaypipe run -p examples/self-hosted/pipeline.yaml --dry-run
# run it for real
relaypipe run -p examples/self-hosted/pipeline.yaml
The goal is baked into the prompts, so no positional target is needed — the
plan file is the input. This is the best worked example of the
"review an existing plan → implement" flow: see
examples/self-hosted/.
Pipeline config
name: plan-review-implement
workspace: .
steps:
- name: plan
agent: claude
role: plan # plan | review | implement | generic
prompt_file: prompts/plan.md
output: PLAN.md # what this agent writes
- name: review-plan
agent: codex
role: review
inputs: [PLAN.md] # files fed into the prompt ({inputs})
output: .agent-relay/plan-review.md
- name: implement
agent: codex
role: implement
inputs: [PLAN.md, .agent-relay/plan-review.md]
approve_after: true # the single human gate
Review loops (iterate up to a cap)
One plan→review pass is rarely enough. A loops: block repeats a contiguous
group of steps until a reviewer approves — or a hard iteration cap is hit, so it
never spins forever:
steps:
- name: plan
agent: claude
role: plan
inputs: [PLAN.md, .agent-relay/review.md] # sees last round's feedback
output: PLAN.md
- name: review
agent: codex
role: review
inputs: [PLAN.md]
output: .agent-relay/review.md
- name: implement
agent: codex
role: implement
inputs: [PLAN.md]
approve_after: true
loops:
- name: plan-review
steps: [plan, review] # must be contiguous & in pipeline order
until_step: review # whose output holds the verdict
max_iterations: 3 # the cap (override per-run with --max-iterations)
approved_marker: "VERDICT: APPROVED"
How it stops:
- The review prompt is told to end its output with
VERDICT: APPROVEDorVERDICT: NEEDS_WORK. The runner reads the verdict step's output: approved → exit the loop early; otherwise → re-run the group. - The feedback path is the normal file hand-off: list the review's
outputas aninputof the first step, so the next pass sees the last review. - If the cap is reached without approval, the run logs
🛑 hit max_iterationsand proceeds to the next step anyway (the human gate still backstops it).
The self-hosted example uses exactly this — see
examples/self-hosted/pipeline.yaml.
Adding a new agent
Two ways:
-
No code — use the
genericadapter with acommand:template (above). -
An adapter class — ~20 lines:
from agent_relay.adapters.base import Agent, AgentContext, register @register class MyToolAgent(Agent): name = "mytool" binary = "mytool" def build_command(self, ctx: AgentContext) -> list[str]: cmd = [self.binary, "--prompt", ctx.prompt] if ctx.output_file: cmd += ["--out", str(ctx.output_file)] return cmd
Ship it as a separate package by registering the
agent_relay.adaptersentry point — no fork needed.
Development
pip install -e ".[dev]"
pytest # tests use mocked agents — no API keys needed
ruff check .
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file relaypipe-0.1.1.tar.gz.
File metadata
- Download URL: relaypipe-0.1.1.tar.gz
- Upload date:
- Size: 44.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a61d57d88b94edc0921771a300c939bb92e08421e4a97c24b93827c8df378f33
|
|
| MD5 |
0322a5223cc3adea241f11a99ec245fe
|
|
| BLAKE2b-256 |
6343d6b59ff80f2d9a873d5619e4bb607e532a7742a32c5b1f0c439a9f5b9862
|
Provenance
The following attestation bundles were made for relaypipe-0.1.1.tar.gz:
Publisher:
python-publish.yml on yuzhiquan/agent-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
relaypipe-0.1.1.tar.gz -
Subject digest:
a61d57d88b94edc0921771a300c939bb92e08421e4a97c24b93827c8df378f33 - Sigstore transparency entry: 1795120224
- Sigstore integration time:
-
Permalink:
yuzhiquan/agent-relay@765fb6b65772516a393005b0ca61219ad763f831 -
Branch / Tag:
refs/tags/v0.0.4 - Owner: https://github.com/yuzhiquan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@765fb6b65772516a393005b0ca61219ad763f831 -
Trigger Event:
release
-
Statement type:
File details
Details for the file relaypipe-0.1.1-py3-none-any.whl.
File metadata
- Download URL: relaypipe-0.1.1-py3-none-any.whl
- Upload date:
- Size: 20.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
008b0fb5328f21daf117b657a3cad0e9e4816e0a4528e8700d9ea02a841da9b2
|
|
| MD5 |
0d87d2e841bb7bcb8b653b904403fe11
|
|
| BLAKE2b-256 |
0c82927d1ebcab15a3c3098c38275d29b492859b66c999ef4b83d6f7d3e5dba2
|
Provenance
The following attestation bundles were made for relaypipe-0.1.1-py3-none-any.whl:
Publisher:
python-publish.yml on yuzhiquan/agent-relay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
relaypipe-0.1.1-py3-none-any.whl -
Subject digest:
008b0fb5328f21daf117b657a3cad0e9e4816e0a4528e8700d9ea02a841da9b2 - Sigstore transparency entry: 1795120353
- Sigstore integration time:
-
Permalink:
yuzhiquan/agent-relay@765fb6b65772516a393005b0ca61219ad763f831 -
Branch / Tag:
refs/tags/v0.0.4 - Owner: https://github.com/yuzhiquan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@765fb6b65772516a393005b0ca61219ad763f831 -
Trigger Event:
release
-
Statement type: