Ask one question to multiple local AI coding CLIs in parallel and collect their answers.
Project description
MOA - Mixture of Agents
Ask one question to multiple local AI coding CLIs in parallel and collect their answers. MOA detects which agent CLIs you have installed (Claude Code, Codex, agy, opencode), fans your prompt out to them, and streams each answer back the moment that agent finishes. Optionally, it can synthesize the answers into a single unified response.
It's a drop-in, batteries-included replacement for hand-rolling parallel claude -p / codex exec / opencode run calls (or a "peer review" agent skill): one command, clean attributed output, made to be called by a human or by another agent.
The package is named moa-cli but installs the command moa.
uv tool install moa-cli
moa ask "Is Postgres or SQLite better for a desktop app?"
Or run it once without installing:
uvx --from moa-cli moa ask "Review this plan."
Why
A single model gives you one perspective. Asking three frontier models the same question - and seeing where they agree, diverge, or contradict - is a fast, cheap way to pressure-test an answer. MOA makes that a one-liner using the CLIs you already pay for, with no API keys of its own.
Usage
moa doctor # show which agent CLIs are installed
moa ask "Should this feature use SQLite?" # ask the top 3 installed agents
moa ask -n 2 "..." # ask only the top 2 (priority order)
moa ask -p claude -p agy "..." # pin specific agents
moa ask -x claude "..." # drop an agent (e.g. exclude the caller's own model)
moa ask -m claude=sonnet "..." # override which model a tool uses
moa ask --synth "..." # also merge the answers into one
moa ask --json "..." # machine-readable JSONL (for agents/pipes)
git diff | moa ask -f - "Review this diff." # read the prompt from stdin
How agents are selected
-n/--num (default 3) picks the first N installed agents from a popularity-ordered priority list:
claude -> codex -> agy -> opencode
So moa ask -n 3 on a machine with all four installed asks Claude, Codex, and agy. Use -p/--provider (repeatable) to pin an exact set and ignore -n.
Use -x/--exclude (repeatable) to drop one or more agents from the run. Exclusion is applied before -n takes the first N, and it also drops excluded names from an explicit -p set. It is off by default. The motivating case: an agent (e.g. Claude Code) calls moa for other opinions; moa ask -x claude makes sure one "peer" isn't just the caller's own model. So moa ask -n 3 -x claude asks Codex, agy, and opencode.
Choosing models
Each tool ships with a reasonable default model, but you can override which model any tool uses with -m/--model PROVIDER=MODEL (repeatable). Only the providers you name change; the rest keep their defaults.
moa ask -m claude=sonnet -m agy="Gemini 3.1 Pro (Low)" "..."
The model-string format differs per tool and is passed through verbatim (the tool's own CLI validates it):
| Provider | Default | -m format |
|---|---|---|
claude |
opus |
short id, e.g. claude=sonnet |
codex |
gpt-5.5 |
model id, e.g. codex=gpt-5.5 |
agy |
Gemini 3.1 Pro (High) |
exact display name, e.g. agy="Gemini 3.1 Pro (Low)" |
opencode |
(tool's authed default) | provider/model slug, e.g. opencode=anthropic/claude-sonnet-4 |
opencode has no built-in default; without an override it omits -m and lets opencode pick. Pass -m opencode=provider/model to pin one.
Output
- stdout carries only content: each agent's answer as a Markdown block (
## claude (opus) - OK - 3.5s), flushed the instant that agent finishes, then the synthesis block if--synthis set. - stderr carries progress and selection notes (
Asking claude, codex ...), so piping stdout stays clean. --jsonemits one JSON object per line (JSONL): a{"type": "response", ...}record per agent as it completes, then a{"type": "synthesis", ...}record. Ideal when another agent calls MOA and parses the result.
Synthesis
--synth runs one more pass that merges the collected answers into a single, unified answer. The synthesizer is chosen with --synthesizer:
auto(default) - the highest-priority agent that ran (deterministic)random- pick one of the agents that ran, at random- a provider name (
claude,codex,agy,opencode)
Attribution policy
The human (or agent) reading MOA's output always gets correct attribution: every response block shows the real provider name. There is no human-facing anonymization toggle.
The synthesizer is a different story. To stop it picking favourites by brand, it always receives the proposer answers anonymized as "Response A / B / C" and order-shuffled. This is always-on internal behaviour, not a flag. The synthesized answer itself is brand-agnostic prose, and the A/B/C labels never leak into stdout, stderr, or the JSON.
Supported agents
| Provider | CLI | Invocation |
|---|---|---|
claude |
claude |
claude --model opus -p PROMPT |
codex |
codex |
codex exec -m gpt-5.5 --skip-git-repo-check PROMPT |
agy |
agy |
agy --model "Gemini 3.1 Pro (High)" -p PROMPT |
opencode |
opencode |
opencode run PROMPT |
Adding a new agent is a single entry in the PROVIDERS table in src/moa_cli/cli.py (executable, default model, command builder); it then participates in detection, -n selection, and synthesis automatically.
Development
uv sync
uv run pytest
uv run ruff check src tests
MIT licensed.
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 moa_cli-0.1.0.tar.gz.
File metadata
- Download URL: moa_cli-0.1.0.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7d7ff5f580ecae9b7eae4e87fa406af2e366e53e8929d0bdf8e1ae80555696a
|
|
| MD5 |
5471e6f074e6a2fa9ed225a3528607a4
|
|
| BLAKE2b-256 |
326a28e0ea587b8cce4f7fa50f11631dab4d46d833920a7743b29f5654dc9d17
|
File details
Details for the file moa_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: moa_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3f99990698fe6ed274838dd7b23f4b16dd88a40d1a7f8153174364915e8c0af
|
|
| MD5 |
3134520a1b10fd29d3d078cb01da17a1
|
|
| BLAKE2b-256 |
67ce3c5e292b9b5503b716ecdf0f8332f8f9099a7e3c956de6c67a95eefcf454
|