Skip to main content

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 agent-relay         # 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:

agent-relay new "a todo-list CLI in Rust with JSON persistence" -d ./todo-cli

What happens:

  1. plan — an agent drafts a build plan
  2. review loop — a reviewer critiques it; the plan is revised and re-reviewed until VERDICT: APPROVED or the loop cap (default 3) is hit
  3. ⏸ you approve — the final plan is printed; answer y to build
  4. build — the project is created in ./todo-cli

Useful options:

agent-relay new "..." -d ./app --agent codex          # use Codex to plan+review+build
agent-relay new "..." -d ./app --agent claude --build-agent codex   # mix agents
agent-relay new "..." -d ./app --max-iterations 5     # allow more review rounds
agent-relay new "..." -d ./app --dry-run              # preview the commands
agent-relay new "..." -d ./app --yes                  # CI: skip the approval prompt

For full control over steps, agents, and gates, write a pipeline (below) and use agent-relay run.

Use

# list available agents
agent-relay agents

# run the example pipeline
agent-relay run "Add a --json flag to the CLI" -p examples/pipeline.yaml

# preview commands without running anything
agent-relay run "..." -p examples/pipeline.yaml --dry-run

# resume an interrupted run
agent-relay run --resume -p examples/pipeline.yaml

# CI mode: auto-approve every gate
agent-relay run "..." -p pipeline.yaml --yes

# override the loop cap for this run (beats max_iterations in the YAML)
agent-relay 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)
agent-relay run -p examples/self-hosted/pipeline.yaml --dry-run

# run it for real
agent-relay 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: APPROVED or VERDICT: 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 output as an input of the first step, so the next pass sees the last review.
  • If the cap is reached without approval, the run logs 🛑 hit max_iterations and 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:

  1. No code — use the generic adapter with a command: template (above).

  2. 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.adapters entry 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

relaypipe-0.1.0.tar.gz (44.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

relaypipe-0.1.0-py3-none-any.whl (20.7 kB view details)

Uploaded Python 3

File details

Details for the file relaypipe-0.1.0.tar.gz.

File metadata

  • Download URL: relaypipe-0.1.0.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

Hashes for relaypipe-0.1.0.tar.gz
Algorithm Hash digest
SHA256 70c427b57336f64af460d1467105d18563cd19a18657ee49f42b0c1dfdb5d43d
MD5 11ede9c2a1482f33de6548bbb058226c
BLAKE2b-256 bae759923dd0a919453b922d3871fc0f18b83d381a7f03c6f8cce55ed9baf694

See more details on using hashes here.

Provenance

The following attestation bundles were made for relaypipe-0.1.0.tar.gz:

Publisher: python-publish.yml on yuzhiquan/agent-relay

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file relaypipe-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: relaypipe-0.1.0-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

Hashes for relaypipe-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f8b4a948de42478b411682d048198df169172fa1bf82f276ffd73f2bf566c07b
MD5 d096249fef12ee0dab69153ab5c318c1
BLAKE2b-256 15584be18b77394840219d0df004759b9370ee4cc070ef8b2475c52593e2e538

See more details on using hashes here.

Provenance

The following attestation bundles were made for relaypipe-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on yuzhiquan/agent-relay

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page