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. 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 "...".

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.0.tar.gz (56.3 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.0-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: duet_cli-0.2.0.tar.gz
  • Upload date:
  • Size: 56.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for duet_cli-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e260c709b03029033992d4f2b6910f276441323d9001991445f165db8910eeaa
MD5 2c285d2aeda2345d7372111b70c40fb7
BLAKE2b-256 58305d72010a90dfe0e0cd4b7e6a493c34f94adef23eb9916a0271b30d03601d

See more details on using hashes here.

File details

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

File metadata

  • Download URL: duet_cli-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for duet_cli-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c7bed5d58e75a41a5b8b88bb4d467c2db36ac2a51cdc7eb5107616723d155eb7
MD5 083861624747b360ded8997d73f2aa15
BLAKE2b-256 9ad69a61ec7dc67b6b0a0e23e4122bd04103ca8d3e8e4a93d52948f5bcd1c77a

See more details on using hashes here.

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