Skip to main content

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.

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        # the command it installs is `duet`
duet --task "Fix the failing test" --cwd ~/code/myrepo

The PyPI package is duet-cli (bare duet on PyPI is Google's async library), so pipx/uvx isolation is recommended. Add 'duet-cli[yaml]' if you want --config foo.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 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

duet_cli-0.2.1.tar.gz (56.4 kB view details)

Uploaded Source

Built Distribution

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

duet_cli-0.2.1-py3-none-any.whl (45.8 kB view details)

Uploaded Python 3

File details

Details for the file duet_cli-0.2.1.tar.gz.

File metadata

  • Download URL: duet_cli-0.2.1.tar.gz
  • Upload date:
  • Size: 56.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for duet_cli-0.2.1.tar.gz
Algorithm Hash digest
SHA256 418ff8ba6ca8a01b5a47ff250a3760a19fc7f9f188b22c10074d2fb496dfa7c6
MD5 af32f44e2a8e0bd9a13d574a9fe59d38
BLAKE2b-256 95fcfcd123c9d4f32d59503077d222698ccc8d3a8acbf14d11b8cf6ba127f9a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for duet_cli-0.2.1.tar.gz:

Publisher: release.yml on volkan/duet

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

File details

Details for the file duet_cli-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: duet_cli-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 45.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for duet_cli-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 de8437de9bc0b84438329bb8549e0e78e232cbcf1d83bd3e65c4fba659333deb
MD5 02d24a45057d704fa6827a7af7f083d5
BLAKE2b-256 58aa8f84cb44cf2ed2a842b3503db751479bf8db8c0d587ec96f97495e36669c

See more details on using hashes here.

Provenance

The following attestation bundles were made for duet_cli-0.2.1-py3-none-any.whl:

Publisher: release.yml on volkan/duet

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