duet-cli 0.2.8__tar.gz → 0.2.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {duet_cli-0.2.8/duet_cli.egg-info → duet_cli-0.2.10}/PKG-INFO +45 -9
- {duet_cli-0.2.8 → duet_cli-0.2.10}/README.md +44 -8
- {duet_cli-0.2.8 → duet_cli-0.2.10}/duet.py +1289 -274
- {duet_cli-0.2.8 → duet_cli-0.2.10/duet_cli.egg-info}/PKG-INFO +45 -9
- {duet_cli-0.2.8 → duet_cli-0.2.10}/duet_cli.egg-info/SOURCES.txt +1 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/pyproject.toml +4 -1
- {duet_cli-0.2.8 → duet_cli-0.2.10}/tests/test_bump_release_version.py +18 -23
- duet_cli-0.2.10/tests/test_control_plane.py +830 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/tests/test_duet.py +193 -2
- {duet_cli-0.2.8 → duet_cli-0.2.10}/LICENSE +0 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/duet_cli.egg-info/dependency_links.txt +0 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/duet_cli.egg-info/entry_points.txt +0 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/duet_cli.egg-info/requires.txt +0 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/duet_cli.egg-info/top_level.txt +0 -0
- {duet_cli-0.2.8 → duet_cli-0.2.10}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: duet-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
4
4
|
Summary: Two CLI agents in conversation. One Python file. Stdlib only.
|
|
5
5
|
Author: Volkan Altan
|
|
6
6
|
License-Expression: MIT
|
|
@@ -47,7 +47,9 @@ Plain `/duet` runs Claude Code's real `/review`, then loops Codex and Claude in
|
|
|
47
47
|
a worktree until they converge. Pass any upstream command as the kickoff:
|
|
48
48
|
`/duet 'npm test 2>&1' --turns 4`. The plugin shells out to the `duet` CLI, so
|
|
49
49
|
install that first (see below) and make sure `command -v duet` passes in Claude
|
|
50
|
-
Code's shell.
|
|
50
|
+
Code's shell. Custom topology flags such as `--no-worktree` and
|
|
51
|
+
`--allow-worktree-fallback` replace the plugin defaults without emitting
|
|
52
|
+
conflicting CLI flags. Full guide:
|
|
51
53
|
[docs/CLAUDE_CODE_PLUGIN.md](https://github.com/volkan/duet/blob/main/docs/CLAUDE_CODE_PLUGIN.md).
|
|
52
54
|
If Claude Code disambiguates plugin commands with namespaces, `/duet:duet` is
|
|
53
55
|
the same command.
|
|
@@ -108,8 +110,10 @@ Then invoke `/duet` in the OpenCode TUI (or `opencode run --command duet "..."`
|
|
|
108
110
|
non-interactively). Like the other plugins it shells out to the `duet` CLI, so
|
|
109
111
|
install that first and make sure `command -v duet` passes in OpenCode's shell.
|
|
110
112
|
The command runs on OpenCode's `build` agent; plain `/duet` runs the same
|
|
111
|
-
`claude -p /review` kickoff, and
|
|
112
|
-
any command.
|
|
113
|
+
`claude -p /review --model sonnet` kickoff, and
|
|
114
|
+
`/duet 'npm test 2>&1' --turns 4` seeds from any command. Custom worktree
|
|
115
|
+
overrides are constructed conditionally, matching the Claude and Codex entry
|
|
116
|
+
points. Full guide:
|
|
113
117
|
[docs/OPENCODE_PLUGIN.md](https://github.com/volkan/duet/blob/main/docs/OPENCODE_PLUGIN.md).
|
|
114
118
|
(duet can also drive OpenCode as a backend — `--partner opencode:coder` — so
|
|
115
119
|
OpenCode can be one of the two looped agents too.)
|
|
@@ -155,9 +159,28 @@ duet --task "Review the latest commit; fix only what the reviewer requests." \
|
|
|
155
159
|
`/review`, a test run, or any command:
|
|
156
160
|
|
|
157
161
|
```bash
|
|
158
|
-
duet --
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
duet --recipe review --cwd ~/workspace/project
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The review recipe uses recap mode, `claude:reviewer` + `codex:coder`, six
|
|
166
|
+
turns, `claude -p /review --model sonnet`, and strict worktree isolation.
|
|
167
|
+
Claude agents use the stable `sonnet` alias unless an explicit slot model
|
|
168
|
+
overrides it. The recipe also sets
|
|
169
|
+
`--on-turn-timeout continue`, so when the coder turn times out the reviewer
|
|
170
|
+
still gets the timeout block plus the worktree diff to review instead of the
|
|
171
|
+
run dying reviewless. Explicit flags override recipe values.
|
|
172
|
+
|
|
173
|
+
**Deterministic automation** — discover the run without parsing terminal prose,
|
|
174
|
+
then poll the curated status schema:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
control_dir=$(mktemp -d)
|
|
178
|
+
duet --recipe review --run-info-file "$control_dir/run.json" &
|
|
179
|
+
duet_pid=$!
|
|
180
|
+
while [ ! -s "$control_dir/run.json" ]; do sleep 0.1; done
|
|
181
|
+
run_dir=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["run_dir"])' "$control_dir/run.json")
|
|
182
|
+
duet --status "$run_dir" --json
|
|
183
|
+
wait "$duet_pid"
|
|
161
184
|
```
|
|
162
185
|
|
|
163
186
|
**Deep planner, fast coder** — Claude plans at high effort while Codex coder
|
|
@@ -202,18 +225,31 @@ other.
|
|
|
202
225
|
To converge, an agent must include an `LGTM rationale:` explaining why the work
|
|
203
226
|
is done, followed by the sentinel `<<<LGTM>>>` on its own line — a bare
|
|
204
227
|
sentinel is ignored, and **both** agents must agree in back-to-back turns. The
|
|
205
|
-
loop also stops on `--turns`, a per-turn timeout, or Ctrl-C.
|
|
228
|
+
loop also stops on `--turns`, a per-turn timeout, or Ctrl-C. With
|
|
229
|
+
`--on-turn-timeout continue` (the review recipe's default) a single turn
|
|
230
|
+
timeout is handed to the partner instead of ending the run; two consecutive
|
|
231
|
+
timeouts still stop. Without `--timeout`, the per-turn budget scales with the
|
|
232
|
+
reasoning level (900s → 1800s at `xhigh`/`max`). After a normal
|
|
206
233
|
stop, duet opens a `force>` prompt so you can push another round.
|
|
207
234
|
|
|
208
235
|
Every run writes a directory with `transcript.md`, `state.json`, per-turn
|
|
209
236
|
stderr logs, and the `wt/` worktree when `--worktree` is on. Inspect a run with
|
|
210
|
-
`duet --status <run-id
|
|
237
|
+
`duet --status <run-id> --json` (or omit `--json` for the human view), list runs
|
|
238
|
+
with `duet --list`, and start a fresh run
|
|
211
239
|
from saved state with `duet --continue <run> --task "next thing"`.
|
|
212
240
|
|
|
213
241
|
- **Backends:** `claude`, `codex`, `gemini`, `copilot`, `opencode`
|
|
214
242
|
- **Roles:** `planner`, `coder`, `reviewer`, `triage-reviewer`, or a custom one
|
|
215
243
|
- **Reasoning:** `--reasoning minimal|low|medium|high|xhigh|max`
|
|
216
244
|
|
|
245
|
+
## Limits / future
|
|
246
|
+
|
|
247
|
+
Timeout continuation applies only to automatic loop turns. Seed extraction
|
|
248
|
+
and forced turns still stop on timeout. A final-turn timeout also stops because
|
|
249
|
+
no partner turn remains, and two consecutive timeouts stop the run. Poll
|
|
250
|
+
`duet --status <run-id> --json` to watch the current budget instead of assuming
|
|
251
|
+
a deep-reasoning turn is stuck.
|
|
252
|
+
|
|
217
253
|
## Documentation
|
|
218
254
|
|
|
219
255
|
[docs/USAGE.md](https://github.com/volkan/duet/blob/main/docs/USAGE.md) is the
|
|
@@ -26,7 +26,9 @@ Plain `/duet` runs Claude Code's real `/review`, then loops Codex and Claude in
|
|
|
26
26
|
a worktree until they converge. Pass any upstream command as the kickoff:
|
|
27
27
|
`/duet 'npm test 2>&1' --turns 4`. The plugin shells out to the `duet` CLI, so
|
|
28
28
|
install that first (see below) and make sure `command -v duet` passes in Claude
|
|
29
|
-
Code's shell.
|
|
29
|
+
Code's shell. Custom topology flags such as `--no-worktree` and
|
|
30
|
+
`--allow-worktree-fallback` replace the plugin defaults without emitting
|
|
31
|
+
conflicting CLI flags. Full guide:
|
|
30
32
|
[docs/CLAUDE_CODE_PLUGIN.md](https://github.com/volkan/duet/blob/main/docs/CLAUDE_CODE_PLUGIN.md).
|
|
31
33
|
If Claude Code disambiguates plugin commands with namespaces, `/duet:duet` is
|
|
32
34
|
the same command.
|
|
@@ -87,8 +89,10 @@ Then invoke `/duet` in the OpenCode TUI (or `opencode run --command duet "..."`
|
|
|
87
89
|
non-interactively). Like the other plugins it shells out to the `duet` CLI, so
|
|
88
90
|
install that first and make sure `command -v duet` passes in OpenCode's shell.
|
|
89
91
|
The command runs on OpenCode's `build` agent; plain `/duet` runs the same
|
|
90
|
-
`claude -p /review` kickoff, and
|
|
91
|
-
any command.
|
|
92
|
+
`claude -p /review --model sonnet` kickoff, and
|
|
93
|
+
`/duet 'npm test 2>&1' --turns 4` seeds from any command. Custom worktree
|
|
94
|
+
overrides are constructed conditionally, matching the Claude and Codex entry
|
|
95
|
+
points. Full guide:
|
|
92
96
|
[docs/OPENCODE_PLUGIN.md](https://github.com/volkan/duet/blob/main/docs/OPENCODE_PLUGIN.md).
|
|
93
97
|
(duet can also drive OpenCode as a backend — `--partner opencode:coder` — so
|
|
94
98
|
OpenCode can be one of the two looped agents too.)
|
|
@@ -134,9 +138,28 @@ duet --task "Review the latest commit; fix only what the reviewer requests." \
|
|
|
134
138
|
`/review`, a test run, or any command:
|
|
135
139
|
|
|
136
140
|
```bash
|
|
137
|
-
duet --
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
duet --recipe review --cwd ~/workspace/project
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The review recipe uses recap mode, `claude:reviewer` + `codex:coder`, six
|
|
145
|
+
turns, `claude -p /review --model sonnet`, and strict worktree isolation.
|
|
146
|
+
Claude agents use the stable `sonnet` alias unless an explicit slot model
|
|
147
|
+
overrides it. The recipe also sets
|
|
148
|
+
`--on-turn-timeout continue`, so when the coder turn times out the reviewer
|
|
149
|
+
still gets the timeout block plus the worktree diff to review instead of the
|
|
150
|
+
run dying reviewless. Explicit flags override recipe values.
|
|
151
|
+
|
|
152
|
+
**Deterministic automation** — discover the run without parsing terminal prose,
|
|
153
|
+
then poll the curated status schema:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
control_dir=$(mktemp -d)
|
|
157
|
+
duet --recipe review --run-info-file "$control_dir/run.json" &
|
|
158
|
+
duet_pid=$!
|
|
159
|
+
while [ ! -s "$control_dir/run.json" ]; do sleep 0.1; done
|
|
160
|
+
run_dir=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["run_dir"])' "$control_dir/run.json")
|
|
161
|
+
duet --status "$run_dir" --json
|
|
162
|
+
wait "$duet_pid"
|
|
140
163
|
```
|
|
141
164
|
|
|
142
165
|
**Deep planner, fast coder** — Claude plans at high effort while Codex coder
|
|
@@ -181,18 +204,31 @@ other.
|
|
|
181
204
|
To converge, an agent must include an `LGTM rationale:` explaining why the work
|
|
182
205
|
is done, followed by the sentinel `<<<LGTM>>>` on its own line — a bare
|
|
183
206
|
sentinel is ignored, and **both** agents must agree in back-to-back turns. The
|
|
184
|
-
loop also stops on `--turns`, a per-turn timeout, or Ctrl-C.
|
|
207
|
+
loop also stops on `--turns`, a per-turn timeout, or Ctrl-C. With
|
|
208
|
+
`--on-turn-timeout continue` (the review recipe's default) a single turn
|
|
209
|
+
timeout is handed to the partner instead of ending the run; two consecutive
|
|
210
|
+
timeouts still stop. Without `--timeout`, the per-turn budget scales with the
|
|
211
|
+
reasoning level (900s → 1800s at `xhigh`/`max`). After a normal
|
|
185
212
|
stop, duet opens a `force>` prompt so you can push another round.
|
|
186
213
|
|
|
187
214
|
Every run writes a directory with `transcript.md`, `state.json`, per-turn
|
|
188
215
|
stderr logs, and the `wt/` worktree when `--worktree` is on. Inspect a run with
|
|
189
|
-
`duet --status <run-id
|
|
216
|
+
`duet --status <run-id> --json` (or omit `--json` for the human view), list runs
|
|
217
|
+
with `duet --list`, and start a fresh run
|
|
190
218
|
from saved state with `duet --continue <run> --task "next thing"`.
|
|
191
219
|
|
|
192
220
|
- **Backends:** `claude`, `codex`, `gemini`, `copilot`, `opencode`
|
|
193
221
|
- **Roles:** `planner`, `coder`, `reviewer`, `triage-reviewer`, or a custom one
|
|
194
222
|
- **Reasoning:** `--reasoning minimal|low|medium|high|xhigh|max`
|
|
195
223
|
|
|
224
|
+
## Limits / future
|
|
225
|
+
|
|
226
|
+
Timeout continuation applies only to automatic loop turns. Seed extraction
|
|
227
|
+
and forced turns still stop on timeout. A final-turn timeout also stops because
|
|
228
|
+
no partner turn remains, and two consecutive timeouts stop the run. Poll
|
|
229
|
+
`duet --status <run-id> --json` to watch the current budget instead of assuming
|
|
230
|
+
a deep-reasoning turn is stuck.
|
|
231
|
+
|
|
196
232
|
## Documentation
|
|
197
233
|
|
|
198
234
|
[docs/USAGE.md](https://github.com/volkan/duet/blob/main/docs/USAGE.md) is the
|