Two CLI agents in conversation. One Python file. Stdlib only.
Project description
duet
Two CLI agents in conversation. One Python file. Stdlib only.
duet runs two command-line coding agents in alternating turns until they
agree. By default that is Claude and Codex; Gemini and Copilot are also
supported, and you can pair two agents from the same backend. One agent plans
or reviews while the other implements; each keeps its own session memory across
turns, and every run leaves a transcript you can inspect.
Use it three ways
1. Inside Claude Code — /duet
The fastest path if you already live in Claude Code.
/plugin marketplace add volkan/duet
/plugin install duet@volkan-duet
/duet
Plain /duet runs Claude Code's real /review, then loops Codex and Claude in
a worktree until they converge. Pass any upstream command as the kickoff:
/duet 'npm test 2>&1' --turns 4. The plugin shells out to the duet CLI, so
install that first (see below) and make sure command -v duet passes in Claude
Code's shell. Full guide:
docs/CLAUDE_CODE_PLUGIN.md.
If Claude Code disambiguates plugin commands with namespaces, /duet:duet is
the same command.
Autonomous handoff example:
While the handoff runs, Claude Code shows the shell in auto mode and exposes
the exact duet command under shell details:
Copy-ready version:
/loop /goal Create a temporary todo.md from the plan above and the remaining
tasks in todo_codex.md.
1. Use /duet:duet with max reasoning to confirm the plan.
2. After the plan is confirmed, implement it.
3. Once the first implementation is done, use /duet:duet with max reasoning
for code review.
4. Use /duet:duet with max reasoning to review the second plan, then implement
it.
5. When the process is complete and all checks are green, merge the approved
changes.
P.S. I will not be around, so handle decisions without me. If you need another
opinion, use /duet:duet to discuss it with Codex.
2. Inside Codex — $duet
codex plugin marketplace add volkan/duet
codex plugin add duet@volkan-duet
Start a new Codex thread and invoke $duet, or just ask Codex to use duet in
plain language. Like the Claude Code plugin, the skill shells out to the duet
CLI, so install that first (see below) and make sure command -v duet passes in
Codex's shell. Full guide:
docs/CODEX_PLUGIN.md.
3. From the terminal — duet
pipx install duet-cli # recommended; the command it installs is `duet`
duet --task "Fix the failing test" --cwd ~/code/myrepo
pipx is the recommended install. Two other persistent options put duet on
PATH the same way:
uv tool install duet-cli
python3 -m pip install --user duet-cli
The PyPI package is duet-cli (bare duet on PyPI is Google's async library).
Add the [yaml] extra for --config foo.yaml support — pipx install 'duet-cli[yaml]', uv tool install 'duet-cli[yaml]', or python3 -m pip install --user 'duet-cli[yaml]'. One-shot, no install:
uvx --from duet-cli duet --task "..." — note this is ephemeral and does not put
duet on PATH, so the /duet and $duet plugins need a persistent install
(pipx install duet-cli, uv tool install duet-cli,
python3 -m pip install --user duet-cli, or make install) instead.
Examples
Each command teaches one capability. The partner agent speaks first.
Review loop — Codex reviews at max effort, Claude applies only the fixes Codex asks for, in an isolated worktree:
duet --task "Review the latest commit; fix only what the reviewer requests." \
--lead claude:coder --partner codex:reviewer \
--reasoning max --worktree --worktree-for lead --turns 6
Seed from another tool's output — drive the loop from Claude Code's real
/review, a test run, or any command:
duet --task-from-cmd 'claude -p /review' \
--lead claude:reviewer --partner codex:coder \
--worktree --recap --cwd ~/workspace/project --turns 6
Deep planner, fast coder — Claude plans at high effort while Codex coder turns drop to low for latency:
duet --reasoning high --codex-fast \
--task "Fix the issue" --cwd ~/workspace/project
Verify gate — a convergence proposal only counts if make test exits 0;
any failure feeds back into the next turn:
duet --task "Fix the issue" \
--lead claude:coder --partner codex:reviewer \
--verify-cmd 'make test' --worktree --worktree-for lead
Resume a plan — plan with Codex in its own session, then hand the session
id to duet; Codex implements with the plan in context while Claude reviews
(--resume-claude <id> does the inverse):
duet --resume-codex <codex-session-id> --worktree --reasoning max \
--task "Implement the plan from your Codex planning session."
Reusable configs ship under examples/ — pr-review.yaml (deep review of
HEAD) and codex-test-fix.yaml (Codex planner diagnoses failing checks, Codex
coder fixes them). Run one with duet --config examples/pr-review.yaml.
How it works
Each agent keeps its own conversation memory across turns (Claude via
--resume, Codex via codex exec resume, Gemini and Copilot via their JSON
session ids). On each turn duet sends one agent's latest reply to the other.
To converge, an agent must include an LGTM rationale: explaining why the work
is done, followed by the sentinel <<<LGTM>>> on its own line — a bare
sentinel is ignored, and both agents must agree in back-to-back turns. The
loop also stops on --turns, a per-turn timeout, or Ctrl-C. After a normal
stop, duet opens a force> prompt so you can push another round.
Every run writes a directory with transcript.md, state.json, per-turn
stderr logs, and the wt/ worktree when --worktree is on. Inspect a run with
duet --status <run-id>, list runs with duet --list, and start a fresh run
from saved state with duet --continue <run> --task "next thing".
- Backends:
claude,codex,gemini,copilot - Roles:
planner,coder,reviewer,triage-reviewer, or a custom one - Reasoning:
--reasoning minimal|low|medium|high|xhigh|max
Documentation
docs/USAGE.md is the
full reference: every flag, reasoning levels, session memory, output layout,
--status / --continue, the force prompt, Codex sandbox and network rules,
and worktree mode.
Contributing
Contributor guidance is in CLAUDE.md; Codex entry notes are in AGENTS.md. CI runs on every PR and is advisory until marked required — see .github/BRANCH_PROTECTION.md.
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
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 duet_cli-0.2.5.tar.gz.
File metadata
- Download URL: duet_cli-0.2.5.tar.gz
- Upload date:
- Size: 58.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03b665da094a92a5ba025fcac2e4d708a9b9632dbb9fe0e5bb86c92813df2ddb
|
|
| MD5 |
a2ddd40fd724e87c693464ab53d33cad
|
|
| BLAKE2b-256 |
3d8a69cdf3f5dfa80bae6b71f1da1afcc8d9f1dffebec5aedfee0a3f62c79bd6
|
Provenance
The following attestation bundles were made for duet_cli-0.2.5.tar.gz:
Publisher:
release.yml on volkan/duet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duet_cli-0.2.5.tar.gz -
Subject digest:
03b665da094a92a5ba025fcac2e4d708a9b9632dbb9fe0e5bb86c92813df2ddb - Sigstore transparency entry: 1819680509
- Sigstore integration time:
-
Permalink:
volkan/duet@c4c830d6178c814f09611f336a43649f90e5915d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/volkan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c4c830d6178c814f09611f336a43649f90e5915d -
Trigger Event:
push
-
Statement type:
File details
Details for the file duet_cli-0.2.5-py3-none-any.whl.
File metadata
- Download URL: duet_cli-0.2.5-py3-none-any.whl
- Upload date:
- Size: 46.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7117b6c01fe0751ef4b0d46221ca035ec8bfb7dd98abf8dd0bd3696a77907cfa
|
|
| MD5 |
00eb7d552865f827a9aa4e6fdb1b9d5c
|
|
| BLAKE2b-256 |
bfd51ec305fc0de7edb346557831ca7234dd56123e7a4fbadbb720c9c51c378b
|
Provenance
The following attestation bundles were made for duet_cli-0.2.5-py3-none-any.whl:
Publisher:
release.yml on volkan/duet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duet_cli-0.2.5-py3-none-any.whl -
Subject digest:
7117b6c01fe0751ef4b0d46221ca035ec8bfb7dd98abf8dd0bd3696a77907cfa - Sigstore transparency entry: 1819680539
- Sigstore integration time:
-
Permalink:
volkan/duet@c4c830d6178c814f09611f336a43649f90e5915d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/volkan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c4c830d6178c814f09611f336a43649f90e5915d -
Trigger Event:
push
-
Statement type: