coding-agent-cli 0.1.0__tar.gz → 0.2.0__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.
- coding_agent_cli-0.2.0/.release-please-manifest.json +3 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/AGENTS.md +9 -6
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/CHANGELOG.md +16 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/CONTRIBUTING.md +8 -0
- coding_agent_cli-0.2.0/PKG-INFO +290 -0
- coding_agent_cli-0.2.0/README.md +267 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/.handover.md +69 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/DESIGN.md +421 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/PLAN.md +333 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-01/journal.md +166 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-01/notes.md +164 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-01/prompt.md +70 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-02/journal.md +130 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-02/notes.md +118 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-02/prompt.md +72 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-03/journal.md +121 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-03/notes.md +153 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-03/prompt.md +69 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-04/journal.md +96 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-04/notes.md +148 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-04/prompt.md +73 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-05/journal.md +76 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-05/notes.md +76 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-05/prompt.md +59 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-06/journal.md +88 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-06/notes.md +110 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-06/prompt.md +59 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-07/journal.md +86 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-07/notes.md +78 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-07/prompt.md +59 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-08/journal.md +104 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-08/notes.md +112 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-08/prompt.md +59 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-09/journal.md +50 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-09/notes.md +33 -0
- coding_agent_cli-0.2.0/docs/plans/coding-agent-cli-v2/phase-09/prompt.md +37 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/pyproject.toml +2 -2
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/release-please-config.json +1 -0
- coding_agent_cli-0.2.0/scripts/record_fixtures.py +847 -0
- coding_agent_cli-0.2.0/scripts/scrub_fixtures.py +264 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/__init__.py +21 -7
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/_args.py +28 -8
- coding_agent_cli-0.2.0/src/coding_agent_cli/_classify.py +261 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/_drivers/_common.py +21 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/_drivers/agy.py +316 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/_drivers/claude.py +449 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/_drivers/codex.py +412 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/_drivers/pi.py +230 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/_platform.py +71 -1
- coding_agent_cli-0.2.0/src/coding_agent_cli/_transport.py +520 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/api.py +190 -0
- coding_agent_cli-0.2.0/src/coding_agent_cli/driver.py +308 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/events.py +50 -9
- coding_agent_cli-0.2.0/src/coding_agent_cli/registry.py +43 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/types.py +45 -5
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/both_flood.py +12 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/line_flood.py +4 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/oversized_then_valid.py +6 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/print_then_exit.py +3 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/silent_then_output.py +7 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/slow_callback.py +7 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/slow_trickle.py +11 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/stubborn_descendant.py +15 -0
- coding_agent_cli-0.2.0/tests/fixtures/fake_cli/two_messages.py +4 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/FINDINGS.md +110 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/cancel.meta.json +24 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/cancel.stderr.txt +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/cancel.stdout.jsonl +4 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/edit.meta.json +25 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/edit.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/edit.stdout.jsonl +16 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/invalid_model.meta.json +24 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/invalid_model.stderr.txt +20 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/invalid_model.stdout.jsonl +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/plain.meta.json +24 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/plain.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/plain.stdout.jsonl +5 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/reasoning.meta.json +26 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/reasoning.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/reasoning.stdout.jsonl +5 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/resume.meta.json +25 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/resume.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/resume.stdout.jsonl +6 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/shell.meta.json +25 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/shell.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/shell.stdout.jsonl +15 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/stdin_stream_json.meta.json +25 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/stdin_stream_json.stderr.txt +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/agy/1.5.9/stdin_stream_json.stdout.jsonl +2 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/auth_failure.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/auth_failure.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/auth_failure.stdout.jsonl +4 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/budget.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/budget.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/budget.stdout.jsonl +46 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/cancel.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/cancel.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/cancel.stdout.jsonl +22 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/edit.meta.json +31 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/edit.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/edit.stdout.jsonl +78 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/invalid_model.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/invalid_model.stderr.txt +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/invalid_model.stdout.jsonl +4 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/max_turns.meta.json +33 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/max_turns.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/max_turns.stdout.jsonl +38 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/no_verbose.meta.json +25 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/no_verbose.stderr.txt +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/no_verbose.stdout.jsonl +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/plain.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/plain.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/plain.stdout.jsonl +11 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/reasoning.meta.json +29 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/reasoning.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/reasoning.stdout.jsonl +11 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/resume.meta.json +29 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/resume.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/resume.stdout.jsonl +11 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/shell.meta.json +31 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/shell.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/claude/2.1.296/shell.stdout.jsonl +34 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/auth_failure.meta.json +17 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/auth_failure.stderr.txt +8 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/auth_failure.stdout.jsonl +14 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/cancel.meta.json +19 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/cancel.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/cancel.stdout.jsonl +5 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/edit.meta.json +19 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/edit.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/edit.stdout.jsonl +11 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/invalid_model.meta.json +19 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/invalid_model.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/invalid_model.stdout.jsonl +7 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/plain.meta.json +17 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/plain.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/plain.stdout.jsonl +6 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/reasoning.meta.json +19 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/reasoning.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/reasoning.stdout.jsonl +6 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/resume.meta.json +19 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/resume.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/resume.stdout.jsonl +6 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/shell.meta.json +19 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/shell.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/codex/0.169.0/shell.stdout.jsonl +9 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/auth_failure.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/auth_failure.stderr.txt +5 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/auth_failure.stdout.jsonl +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/cancel.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/cancel.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/cancel.stdout.jsonl +30 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/edit.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/edit.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/edit.stdout.jsonl +92 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/invalid_model.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/invalid_model.stderr.txt +1 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/invalid_model.stdout.jsonl +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/no_p.meta.json +26 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/no_p.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/no_p.stdout.jsonl +16 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/plain.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/plain.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/plain.stdout.jsonl +16 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/reasoning.meta.json +29 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/reasoning.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/reasoning.stdout.jsonl +15 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/resume.meta.json +29 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/resume.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/resume.stdout.jsonl +14 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/shell.meta.json +27 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/shell.stderr.txt +0 -0
- coding_agent_cli-0.2.0/tests/fixtures/recorded/pi/1.5.2/shell.stdout.jsonl +40 -0
- coding_agent_cli-0.2.0/tests/procs.py +53 -0
- coding_agent_cli-0.2.0/tests/replay.py +110 -0
- coding_agent_cli-0.2.0/tests/test_agy_driver.py +507 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/test_api_contract.py +57 -3
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/test_args.py +13 -2
- coding_agent_cli-0.2.0/tests/test_classify.py +330 -0
- coding_agent_cli-0.2.0/tests/test_claude_driver.py +571 -0
- coding_agent_cli-0.2.0/tests/test_codex_driver.py +598 -0
- coding_agent_cli-0.2.0/tests/test_driver_base.py +312 -0
- coding_agent_cli-0.2.0/tests/test_events.py +87 -0
- coding_agent_cli-0.2.0/tests/test_fixture_privacy.py +313 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/test_live.py +6 -7
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/test_packaging.py +5 -1
- coding_agent_cli-0.2.0/tests/test_pi_driver.py +435 -0
- coding_agent_cli-0.2.0/tests/test_readme.py +34 -0
- coding_agent_cli-0.2.0/tests/test_record_fixtures.py +300 -0
- coding_agent_cli-0.2.0/tests/test_registry.py +80 -0
- coding_agent_cli-0.2.0/tests/test_replay.py +88 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/test_stream.py +34 -2
- coding_agent_cli-0.2.0/tests/test_transport.py +772 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/test_types.py +86 -7
- coding_agent_cli-0.2.0/tests/toy_driver.py +47 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/uv.lock +2 -2
- coding_agent_cli-0.1.0/.release-please-manifest.json +0 -3
- coding_agent_cli-0.1.0/PKG-INFO +0 -217
- coding_agent_cli-0.1.0/README.md +0 -194
- coding_agent_cli-0.1.0/src/coding_agent_cli/_classify.py +0 -170
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/_common.py +0 -330
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/_usage.py +0 -77
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/agy.py +0 -340
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/claude.py +0 -304
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/codex.py +0 -333
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/codex_rollout.py +0 -439
- coding_agent_cli-0.1.0/src/coding_agent_cli/_drivers/pi.py +0 -333
- coding_agent_cli-0.1.0/src/coding_agent_cli/_process.py +0 -338
- coding_agent_cli-0.1.0/src/coding_agent_cli/api.py +0 -190
- coding_agent_cli-0.1.0/src/coding_agent_cli/registry.py +0 -67
- coding_agent_cli-0.1.0/tests/cli_stub.py +0 -144
- coding_agent_cli-0.1.0/tests/fixtures/usage/agy.jsonl +0 -6
- coding_agent_cli-0.1.0/tests/fixtures/usage/claude.jsonl +0 -12
- coding_agent_cli-0.1.0/tests/fixtures/usage/codex.jsonl +0 -7
- coding_agent_cli-0.1.0/tests/fixtures/usage/pi.jsonl +0 -55
- coding_agent_cli-0.1.0/tests/test_agy_driver.py +0 -723
- coding_agent_cli-0.1.0/tests/test_classify.py +0 -88
- coding_agent_cli-0.1.0/tests/test_claude_driver.py +0 -735
- coding_agent_cli-0.1.0/tests/test_codex_driver.py +0 -692
- coding_agent_cli-0.1.0/tests/test_codex_rollout.py +0 -610
- coding_agent_cli-0.1.0/tests/test_events.py +0 -68
- coding_agent_cli-0.1.0/tests/test_pi_driver.py +0 -746
- coding_agent_cli-0.1.0/tests/test_process.py +0 -394
- coding_agent_cli-0.1.0/tests/test_registry.py +0 -55
- coding_agent_cli-0.1.0/tests/test_usage.py +0 -87
- coding_agent_cli-0.1.0/tests/toy_driver.py +0 -68
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.editorconfig +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/dependabot.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/workflows/ci.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/workflows/pr-title.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.github/workflows/release.yml +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/.gitignore +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/CLAUDE.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/CODE_OF_CONDUCT.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/LICENSE +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/SECURITY.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/docs/plans/coding-agent-cli/.handover.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/docs/plans/coding-agent-cli/DESIGN.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/docs/plans/coding-agent-cli/PLAN.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/docs/plans/coding-agent-cli/journal-phase4.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/docs/plans/coding-agent-cli/journal-phase5.md +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/_drivers/__init__.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/src/coding_agent_cli/py.typed +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/__init__.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/conftest.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/echo_stdin.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/emit_both.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/exit_now.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/exit_status.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/fake_cli.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/grandchild.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/long_line.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/many_lines.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/non_utf8.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/sleeper.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/fixtures/fake_cli/spawn_and_sleep.py +0 -0
- {coding_agent_cli-0.1.0 → coding_agent_cli-0.2.0}/tests/launchers.py +0 -0
|
@@ -11,13 +11,16 @@ Guidance for AI coding agents working in this repository. Humans should read [CO
|
|
|
11
11
|
| Path | Contents |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `src/coding_agent_cli/api.py` | Public entry points: `run`, `stream`, `run_sync`, `check` |
|
|
14
|
-
| `src/coding_agent_cli/types.py`, `events.py` | Public frozen dataclasses |
|
|
15
|
-
| `src/coding_agent_cli/
|
|
16
|
-
| `src/coding_agent_cli/
|
|
14
|
+
| `src/coding_agent_cli/types.py`, `events.py` | Public frozen dataclasses and typed events |
|
|
15
|
+
| `src/coding_agent_cli/driver.py` | Public `CliDriver`, `Launch`, `Parser`, and driver helpers |
|
|
16
|
+
| `src/coding_agent_cli/registry.py` | Built-in registration and `DriverRegistry` |
|
|
17
|
+
| `src/coding_agent_cli/_transport.py`, `_platform.py` | Subprocess transport and process-tree cleanup |
|
|
17
18
|
| `src/coding_agent_cli/_args.py`, `_classify.py` | Argument validation and outcome classification |
|
|
18
|
-
| `src/coding_agent_cli/_drivers/` | Built-in drivers and shared
|
|
19
|
-
| `tests/fixtures/fake_cli/` | Fake CLIs
|
|
20
|
-
| `
|
|
19
|
+
| `src/coding_agent_cli/_drivers/` | Built-in drivers and shared helpers |
|
|
20
|
+
| `tests/fixtures/fake_cli/`, `tests/fixtures/recorded/` | Fake CLIs and sanitized recorded fixtures |
|
|
21
|
+
| `tests/replay.py` | Recorded fixture replay helpers |
|
|
22
|
+
| `scripts/record_fixtures.py` | Developer fixture recording script, not shipped in the wheel |
|
|
23
|
+
| `docs/plans/coding-agent-cli-v2/` | Design, plan, and phase journals |
|
|
21
24
|
|
|
22
25
|
## Commands
|
|
23
26
|
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0](https://github.com/sitien173/coding-agent-cli/compare/v0.1.0...v0.2.0) (2026-11-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* rebuild on public CliDriver API with recorded CLI fixtures
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* rebuild on public CliDriver API with recorded CLI fixtures ([3a5de96](https://github.com/sitien173/coding-agent-cli/commit/3a5de960c13d4da99791d02f3ef28cfc61671713))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **scripts:** narrow recorder process-group kill so pyright passes on Windows ([b1dd2a0](https://github.com/sitien173/coding-agent-cli/commit/b1dd2a00d098220c316945091fc568f7dfb63da3))
|
|
18
|
+
|
|
3
19
|
## 0.1.0
|
|
4
20
|
|
|
5
21
|
First release. Extracted from the CLI-driver layer of Refract.
|
|
@@ -59,8 +59,16 @@ uv run pytest -m live -rP
|
|
|
59
59
|
|
|
60
60
|
- Every behavior change comes with a test. Write the failing test first when you can.
|
|
61
61
|
- Tests use the fake CLIs in `tests/fixtures/fake_cli`. Unit tests never call a real coding-agent CLI.
|
|
62
|
+
- Recorded sanitized CLI fixtures live in `tests/fixtures/recorded/`; `tests/replay.py` provides replay helpers. `scripts/record_fixtures.py` records new fixtures and is a developer script, not part of the wheel.
|
|
62
63
|
- A new driver needs argv tests proving it adds no permission flag for any option combination.
|
|
63
64
|
|
|
65
|
+
### Module map
|
|
66
|
+
|
|
67
|
+
- `src/coding_agent_cli/driver.py` defines the public `CliDriver` extension API, `Launch`, and `Parser`.
|
|
68
|
+
- `src/coding_agent_cli/_transport.py` supervises subprocesses; `_classify.py` maps run facts to outcomes.
|
|
69
|
+
- `src/coding_agent_cli/_drivers/` contains the built-in CLI drivers and shared helpers.
|
|
70
|
+
- `src/coding_agent_cli/registry.py` registers built-in drivers and manages per-call registries.
|
|
71
|
+
|
|
64
72
|
### Adding a driver
|
|
65
73
|
|
|
66
74
|
1. Subclass `CliDriver` in `src/coding_agent_cli/_drivers/`. Follow `claude.py` or `pi.py`.
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: coding-agent-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Run coding-agent CLIs from Python, stream typed events, and get a classified result.
|
|
5
|
+
Project-URL: Homepage, https://github.com/sitien173/coding-agent-cli
|
|
6
|
+
Project-URL: Issues, https://github.com/sitien173/coding-agent-cli/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/sitien173/coding-agent-cli/blob/main/CHANGELOG.md
|
|
8
|
+
Author: sitien173
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: asyncio,claude,cli,codex,coding-agent,subprocess
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# coding-agent-cli
|
|
25
|
+
|
|
26
|
+
[](https://github.com/sitien173/coding-agent-cli/actions/workflows/ci.yml)
|
|
27
|
+
[](https://pypi.org/project/coding-agent-cli/)
|
|
28
|
+
[](https://pypi.org/project/coding-agent-cli/)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
|
|
31
|
+
Run coding-agent CLIs from Python, stream typed events, and get a classified result.
|
|
32
|
+
|
|
33
|
+
Built-in drivers: `claude`, `codex`, `pi`, and `agy`. Python 3.12 or later on Linux, macOS, and Windows. No runtime dependencies. The CLIs themselves are not bundled; install and authenticate each CLI you use.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install coding-agent-cli
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import asyncio
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
from coding_agent_cli import Request, run
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def main() -> None:
|
|
51
|
+
result = await run(Request(driver="claude", prompt="Summarize README.md", cwd=Path.cwd()))
|
|
52
|
+
print(result.outcome, result.text)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
asyncio.run(main())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API
|
|
59
|
+
|
|
60
|
+
The public entry points have these signatures:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
async def run(
|
|
64
|
+
req: Request,
|
|
65
|
+
*,
|
|
66
|
+
on_event: Callable[[Event], Awaitable[None] | None] | None = None,
|
|
67
|
+
cancel: asyncio.Event | None = None,
|
|
68
|
+
on_spawn: Callable[[int], Awaitable[None] | None] | None = None,
|
|
69
|
+
registry: DriverRegistry | None = None,
|
|
70
|
+
) -> Result: ...
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def stream(
|
|
74
|
+
req: Request,
|
|
75
|
+
*,
|
|
76
|
+
cancel: asyncio.Event | None = None,
|
|
77
|
+
on_spawn: Callable[[int], Awaitable[None] | None] | None = None,
|
|
78
|
+
registry: DriverRegistry | None = None,
|
|
79
|
+
) -> EventStream: ...
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run_sync(
|
|
83
|
+
req: Request,
|
|
84
|
+
*,
|
|
85
|
+
on_event: Callable[[Event], None] | None = None,
|
|
86
|
+
cancel: threading.Event | None = None,
|
|
87
|
+
registry: DriverRegistry | None = None,
|
|
88
|
+
) -> Result: ...
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def check(
|
|
92
|
+
driver: str,
|
|
93
|
+
*,
|
|
94
|
+
executable: str | None = None,
|
|
95
|
+
env: Mapping[str, str] | None = None,
|
|
96
|
+
refresh: bool = False,
|
|
97
|
+
registry: DriverRegistry | None = None,
|
|
98
|
+
) -> Capabilities: ...
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`run` and `stream` are asynchronous. `run` accepts a sync or async event callback. `stream` is an async iterator; use it as an async context manager so leaving the block closes the run. Callback exceptions propagate after process cleanup. Unknown driver names raise `KeyError`.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import asyncio
|
|
105
|
+
from pathlib import Path
|
|
106
|
+
|
|
107
|
+
from coding_agent_cli import MessageCompleted, Request, RunCompleted, TextDelta, stream
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def main() -> None:
|
|
111
|
+
req = Request(driver="codex", prompt="List the tests", cwd=Path.cwd())
|
|
112
|
+
async with stream(req) as events:
|
|
113
|
+
async for event in events:
|
|
114
|
+
if isinstance(event, TextDelta):
|
|
115
|
+
print(event.text, end="")
|
|
116
|
+
elif isinstance(event, MessageCompleted):
|
|
117
|
+
print()
|
|
118
|
+
elif isinstance(event, RunCompleted):
|
|
119
|
+
print(event.result.outcome)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
asyncio.run(main())
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`run_sync` is for synchronous applications and raises `RuntimeError` inside an active event loop. Its `threading.Event` cancellation signal can be set from another thread:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
import threading
|
|
129
|
+
from pathlib import Path
|
|
130
|
+
|
|
131
|
+
from coding_agent_cli import Request, run_sync
|
|
132
|
+
|
|
133
|
+
cancel = threading.Event()
|
|
134
|
+
request = Request(driver="pi", prompt="Inspect this project", cwd=Path.cwd())
|
|
135
|
+
# Pass cancel to run_sync and call cancel.set() from another thread to stop the run.
|
|
136
|
+
result = run_sync(request, cancel=cancel)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`check(driver, ...)` probes `--version` and `--help` and returns `Capabilities(available, executable, version, structured_streaming)`. Successful probe results are cached by executable path and modification time. `run` does not probe first.
|
|
140
|
+
|
|
141
|
+
## Request and Result
|
|
142
|
+
|
|
143
|
+
`Request` is frozen and keyword-only:
|
|
144
|
+
|
|
145
|
+
| Field | Type | Meaning |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `driver` | `str` | Registered driver name: `claude`, `codex`, `pi`, or `agy`. |
|
|
148
|
+
| `prompt` | `str` | Prompt text. |
|
|
149
|
+
| `cwd` | `Path` | Working directory for the CLI. |
|
|
150
|
+
| `session_id` | `str \| None` | Resume a CLI session when supported. |
|
|
151
|
+
| `model` | `str \| None` | Portable model option when supported. |
|
|
152
|
+
| `reasoning` | `str \| None` | Portable reasoning or effort option when supported. |
|
|
153
|
+
| `system_prompt` | `str \| None` | Portable system prompt when supported. |
|
|
154
|
+
| `args` | `tuple[str, ...]` | Additional CLI arguments passed through unchanged. Each driver decides their position; some drivers append the prompt or session arguments after them. |
|
|
155
|
+
| `env` | `Mapping[str, str]` | Environment overlay on the inherited environment. |
|
|
156
|
+
| `timeout_s` | `float \| None` | Positive finite wall-clock limit, or `None` for no limit. |
|
|
157
|
+
| `idle_timeout_s` | `float \| None` | Positive finite idle limit, reset by process output, or `None` for no limit. |
|
|
158
|
+
| `raw_events` | `bool` | Emit parsed JSON stdout lines as `RawEvent` events. |
|
|
159
|
+
| `executable` | `str \| None` | Executable path or name overriding the driver's default. |
|
|
160
|
+
|
|
161
|
+
`Result` is frozen and keyword-only. Its fields are `outcome: Outcome`, `text: str`, `session_id: str | None`, `model: str | None`, `usage: Usage`, `cost_usd: float | None`, `duration_s: float`, `num_turns: int | None`, `stop_reason: str | None`, `returncode: int | None`, `error: Diagnostic | None`, and `warnings: tuple[Diagnostic, ...]`. `text` is the parser's reported final text when the CLI provides one, otherwise the accumulated text of the last assistant message. It is never the concatenation of every assistant turn. Unknown reported values remain `None`. Codex does not report a model, so its `Result.model` is `None`.
|
|
162
|
+
|
|
163
|
+
## Events
|
|
164
|
+
|
|
165
|
+
Each event has an increasing `sequence` and optional CLI-native `entity_id`; the `entity_id` is `None` when the CLI provides no native identifier. Events have no `schema_version` field. The module constant `SCHEMA_VERSION` is `2`. A run that returns a `Result` emits one final `RunCompleted` after cleanup. If cleanup misses its deadline, the result carries a `cleanup_incomplete` warning and termination is not confirmed. If `on_event` or `on_spawn` raises, or the calling task is cancelled, the library cleans up and re-raises without emitting `RunCompleted`.
|
|
166
|
+
|
|
167
|
+
| Event | `kind` | Additional fields |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `SessionIdentified` | `session.identified` | `session_id` |
|
|
170
|
+
| `MessageStarted` | `assistant.message.started` | none |
|
|
171
|
+
| `TextDelta` | `assistant.text.delta` | `text` |
|
|
172
|
+
| `ReasoningDelta` | `assistant.reasoning.delta` | `text` |
|
|
173
|
+
| `MessageCompleted` | `assistant.message.completed` | none |
|
|
174
|
+
| `ToolStarted` | `tool.started` | `tool`, `activity` (`command` or `tool_call`), `input` |
|
|
175
|
+
| `ToolCompleted` | `tool.completed` | `tool`, `status` (`completed`, `error`, or `unknown`), `output` |
|
|
176
|
+
| `FileChanged` | `file.changed` | `path`, `change` (`added`, `modified`, `deleted`, or `unknown`), `evidence` (`reported` or `inferred`) |
|
|
177
|
+
| `Notice` | `notice` | `code`, `message` |
|
|
178
|
+
| `UsageUpdated` | `usage.updated` | `usage` |
|
|
179
|
+
| `RawEvent` | `raw` | `stream`, `data` |
|
|
180
|
+
| `RunCompleted` | `run.completed` | `result` |
|
|
181
|
+
|
|
182
|
+
## Outcomes and errors
|
|
183
|
+
|
|
184
|
+
| `Outcome` | Meaning |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `success` | The CLI completed successfully. |
|
|
187
|
+
| `retryable` | The run may succeed later, for example after a timeout, service error, nonzero exit, or missing output. |
|
|
188
|
+
| `limit_reached` | A CLI-reported turn or budget limit stopped the run. |
|
|
189
|
+
| `agent_fatal` | The CLI cannot run here, such as when it is missing or authentication fails. |
|
|
190
|
+
| `request_fatal` | The request is invalid or unsupported. |
|
|
191
|
+
| `cancelled` | The caller cancelled the run. |
|
|
192
|
+
|
|
193
|
+
`ErrorCode` values are `bad_cwd`, `invalid_args`, `unsupported_option`, `invalid_prompt`, `missing_cli`, `spawn_failed`, `auth`, `invalid_model`, `context_overflow`, `service_error`, `max_turns`, `budget`, `cancelled`, `timeout`, `idle_timeout`, `stdin_failed`, `execution_error`, `parser_error`, `no_output`, `no_session_id`, `line_too_long`, and `cleanup_incomplete`. `Result.error` is a `Diagnostic(code, message)` when the run has a primary error; `Result.warnings` contains non-fatal diagnostics.
|
|
194
|
+
|
|
195
|
+
`retryable` means possibly transient, not safe to replay. The agent may already have edited files, run commands, or spent tokens. Inspect the working tree before sending the same prompt again.
|
|
196
|
+
|
|
197
|
+
## CLI arguments and permissions
|
|
198
|
+
|
|
199
|
+
Drivers do not add permission, approval, or sandbox-bypass flags. Callers pass any CLI-specific permission flags through `Request.args`:
|
|
200
|
+
|
|
201
|
+
| CLI | Example flag |
|
|
202
|
+
|---|---|
|
|
203
|
+
| Claude Code | `--permission-mode bypassPermissions` |
|
|
204
|
+
| Codex | `--dangerously-bypass-approvals-and-sandbox` |
|
|
205
|
+
| Pi | No automatic permission flag; `--approve` trusts project-local files and is not a tool-permission bypass. |
|
|
206
|
+
| Agy | `--dangerously-skip-permissions` |
|
|
207
|
+
|
|
208
|
+
Only use unattended permission flags in an environment you are prepared to lose. Portable `model`, `reasoning`, and `system_prompt` options are supported selectively; unsupported options and conflicts with `args` are classified as request errors.
|
|
209
|
+
|
|
210
|
+
## Custom drivers
|
|
211
|
+
|
|
212
|
+
Subclass `CliDriver` and implement `launch` and `parser`. `Launch` describes argv, optional stdin, and files to read back after the run. `Parser` converts JSON or text lines into events and final `Facts`:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from collections.abc import Iterable, Mapping
|
|
216
|
+
from pathlib import Path
|
|
217
|
+
from typing import Any, Literal
|
|
218
|
+
|
|
219
|
+
from coding_agent_cli import CliDriver, DriverRegistry, Event, Facts, Launch, Parser, Request
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ExampleParser:
|
|
223
|
+
def feed_json(self, obj: dict[str, Any]) -> Iterable[Event]:
|
|
224
|
+
return ()
|
|
225
|
+
|
|
226
|
+
def feed_text(self, line: str, *, stream: Literal["stdout", "stderr"]) -> Iterable[Event]:
|
|
227
|
+
return ()
|
|
228
|
+
|
|
229
|
+
def finish(self, read_back: Mapping[Path, bytes]) -> Facts:
|
|
230
|
+
return Facts()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class ExampleDriver(CliDriver):
|
|
234
|
+
name = "example"
|
|
235
|
+
executable = "example-cli"
|
|
236
|
+
|
|
237
|
+
def launch(self, req: Request, exe: Path, tmp: Path) -> Launch:
|
|
238
|
+
return Launch(argv=(str(exe), "--prompt", req.prompt))
|
|
239
|
+
|
|
240
|
+
def parser(self, req: Request) -> Parser:
|
|
241
|
+
return ExampleParser()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
registry = DriverRegistry()
|
|
245
|
+
registry.register("example", ExampleDriver())
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`CliDriver` owns process supervision, sequencing, classification, and cleanup. A driver must not add permission flags on behalf of a caller.
|
|
249
|
+
|
|
250
|
+
## Migrating from 0.1
|
|
251
|
+
|
|
252
|
+
The v2 API has no compatibility layer.
|
|
253
|
+
|
|
254
|
+
- **Renamed:** `Driver` is now `CliDriver`, an abstract base class. Implement `launch()` and `parser()` instead of `check()` and `run()`. `check()` is now a module function taking a driver name and optional `executable`, `env`, `refresh`, and `registry` keywords.
|
|
255
|
+
- **Removed:** `EventHandler` and `SpawnHandler` public aliases. Their callback types appear directly in the `run`, `stream`, and `run_sync` signatures. `Driver` is no longer exported; use `CliDriver`.
|
|
256
|
+
- **Added public names:** `CliDriver`, `ErrorCode`, `Facts`, `FileChanged`, `JsonValue`, `Launch`, `Notice`, `Parser`, `RawEvent`, `RequestError`, and `ReasoningDelta`.
|
|
257
|
+
- **Outcomes and errors:** `Outcome.LIMIT_REACHED` is new for turn and budget limits, which 0.1 reported as retryable. `Diagnostic.code` is an `ErrorCode` instead of a free-form `str`.
|
|
258
|
+
- **Fields added:** `ToolCompleted.tool` and `ToolCompleted.output`; `Result.cost_usd`, `Result.duration_s`, `Result.num_turns`, and `Result.stop_reason`; `Capabilities.version`.
|
|
259
|
+
- **Event schema:** `schema_version` is no longer stored on each event. Import the `SCHEMA_VERSION` module constant instead. `entity_id` contains the CLI-native identifier or `None` when unavailable.
|
|
260
|
+
- **Result behavior:** `Result.text` is the parser's final text when reported, otherwise the last assistant message, rather than the concatenation of all assistant messages. Codex reports `Result.model` as `None`.
|
|
261
|
+
- **Removed behavior:** codex rollout-file parsing is removed; Codex output is parsed from its structured stream.
|
|
262
|
+
- **Registry:** `DriverRegistry.register` accepts only `CliDriver` instances; structurally compatible objects are rejected.
|
|
263
|
+
- **Callbacks and cancel:** `run` accepts synchronous or awaitable `on_event` and `on_spawn` callbacks. `run_sync` now accepts `on_event` and a `threading.Event` for `cancel`.
|
|
264
|
+
- **Request:** `idle_timeout_s` and `raw_events` are available in addition to the 0.1 request fields.
|
|
265
|
+
|
|
266
|
+
## Known limits
|
|
267
|
+
|
|
268
|
+
- On Windows, Job Object assignment happens immediately after process spawn. In the short window before assignment, the process can exit or spawn descendants; descendants created in that window are outside the job and can survive process-tree cleanup. If creating, configuring, or assigning the Job Object fails, the library silently falls back to `taskkill /T`, which cannot reach a descendant whose parent already exited; no `cleanup_incomplete` warning is reported in that case.
|
|
269
|
+
- Agy receives the prompt in argv, which is limited to 100 KiB by the driver.
|
|
270
|
+
- Agy output that mixes structured JSON events and plain text on stdout is unsupported.
|
|
271
|
+
- Set `idle_timeout_s` longer than the longest tool command, because a quiet but still-running command can reach the idle timeout.
|
|
272
|
+
|
|
273
|
+
## Contributing
|
|
274
|
+
|
|
275
|
+
Contributions are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the coding standard, and the pull request process. Report security problems privately as described in [SECURITY.md](SECURITY.md). Everyone taking part follows the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
uv sync
|
|
279
|
+
uv run ruff format --check && uv run ruff check
|
|
280
|
+
uv run pyright
|
|
281
|
+
uv run pytest
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
[MIT](LICENSE)
|
|
287
|
+
|
|
288
|
+
## Provenance
|
|
289
|
+
|
|
290
|
+
This library is extracted from the CLI-driver layer of [Refract](https://github.com/sitien173/refract), a multi-model coordination tool. Its drivers, process handling, and many of its tests are ported from Refract under the MIT license.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# coding-agent-cli
|
|
2
|
+
|
|
3
|
+
[](https://github.com/sitien173/coding-agent-cli/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/coding-agent-cli/)
|
|
5
|
+
[](https://pypi.org/project/coding-agent-cli/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
Run coding-agent CLIs from Python, stream typed events, and get a classified result.
|
|
9
|
+
|
|
10
|
+
Built-in drivers: `claude`, `codex`, `pi`, and `agy`. Python 3.12 or later on Linux, macOS, and Windows. No runtime dependencies. The CLIs themselves are not bundled; install and authenticate each CLI you use.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install coding-agent-cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import asyncio
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from coding_agent_cli import Request, run
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def main() -> None:
|
|
28
|
+
result = await run(Request(driver="claude", prompt="Summarize README.md", cwd=Path.cwd()))
|
|
29
|
+
print(result.outcome, result.text)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
asyncio.run(main())
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
The public entry points have these signatures:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
async def run(
|
|
41
|
+
req: Request,
|
|
42
|
+
*,
|
|
43
|
+
on_event: Callable[[Event], Awaitable[None] | None] | None = None,
|
|
44
|
+
cancel: asyncio.Event | None = None,
|
|
45
|
+
on_spawn: Callable[[int], Awaitable[None] | None] | None = None,
|
|
46
|
+
registry: DriverRegistry | None = None,
|
|
47
|
+
) -> Result: ...
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def stream(
|
|
51
|
+
req: Request,
|
|
52
|
+
*,
|
|
53
|
+
cancel: asyncio.Event | None = None,
|
|
54
|
+
on_spawn: Callable[[int], Awaitable[None] | None] | None = None,
|
|
55
|
+
registry: DriverRegistry | None = None,
|
|
56
|
+
) -> EventStream: ...
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_sync(
|
|
60
|
+
req: Request,
|
|
61
|
+
*,
|
|
62
|
+
on_event: Callable[[Event], None] | None = None,
|
|
63
|
+
cancel: threading.Event | None = None,
|
|
64
|
+
registry: DriverRegistry | None = None,
|
|
65
|
+
) -> Result: ...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def check(
|
|
69
|
+
driver: str,
|
|
70
|
+
*,
|
|
71
|
+
executable: str | None = None,
|
|
72
|
+
env: Mapping[str, str] | None = None,
|
|
73
|
+
refresh: bool = False,
|
|
74
|
+
registry: DriverRegistry | None = None,
|
|
75
|
+
) -> Capabilities: ...
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`run` and `stream` are asynchronous. `run` accepts a sync or async event callback. `stream` is an async iterator; use it as an async context manager so leaving the block closes the run. Callback exceptions propagate after process cleanup. Unknown driver names raise `KeyError`.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import asyncio
|
|
82
|
+
from pathlib import Path
|
|
83
|
+
|
|
84
|
+
from coding_agent_cli import MessageCompleted, Request, RunCompleted, TextDelta, stream
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def main() -> None:
|
|
88
|
+
req = Request(driver="codex", prompt="List the tests", cwd=Path.cwd())
|
|
89
|
+
async with stream(req) as events:
|
|
90
|
+
async for event in events:
|
|
91
|
+
if isinstance(event, TextDelta):
|
|
92
|
+
print(event.text, end="")
|
|
93
|
+
elif isinstance(event, MessageCompleted):
|
|
94
|
+
print()
|
|
95
|
+
elif isinstance(event, RunCompleted):
|
|
96
|
+
print(event.result.outcome)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
asyncio.run(main())
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`run_sync` is for synchronous applications and raises `RuntimeError` inside an active event loop. Its `threading.Event` cancellation signal can be set from another thread:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
import threading
|
|
106
|
+
from pathlib import Path
|
|
107
|
+
|
|
108
|
+
from coding_agent_cli import Request, run_sync
|
|
109
|
+
|
|
110
|
+
cancel = threading.Event()
|
|
111
|
+
request = Request(driver="pi", prompt="Inspect this project", cwd=Path.cwd())
|
|
112
|
+
# Pass cancel to run_sync and call cancel.set() from another thread to stop the run.
|
|
113
|
+
result = run_sync(request, cancel=cancel)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`check(driver, ...)` probes `--version` and `--help` and returns `Capabilities(available, executable, version, structured_streaming)`. Successful probe results are cached by executable path and modification time. `run` does not probe first.
|
|
117
|
+
|
|
118
|
+
## Request and Result
|
|
119
|
+
|
|
120
|
+
`Request` is frozen and keyword-only:
|
|
121
|
+
|
|
122
|
+
| Field | Type | Meaning |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `driver` | `str` | Registered driver name: `claude`, `codex`, `pi`, or `agy`. |
|
|
125
|
+
| `prompt` | `str` | Prompt text. |
|
|
126
|
+
| `cwd` | `Path` | Working directory for the CLI. |
|
|
127
|
+
| `session_id` | `str \| None` | Resume a CLI session when supported. |
|
|
128
|
+
| `model` | `str \| None` | Portable model option when supported. |
|
|
129
|
+
| `reasoning` | `str \| None` | Portable reasoning or effort option when supported. |
|
|
130
|
+
| `system_prompt` | `str \| None` | Portable system prompt when supported. |
|
|
131
|
+
| `args` | `tuple[str, ...]` | Additional CLI arguments passed through unchanged. Each driver decides their position; some drivers append the prompt or session arguments after them. |
|
|
132
|
+
| `env` | `Mapping[str, str]` | Environment overlay on the inherited environment. |
|
|
133
|
+
| `timeout_s` | `float \| None` | Positive finite wall-clock limit, or `None` for no limit. |
|
|
134
|
+
| `idle_timeout_s` | `float \| None` | Positive finite idle limit, reset by process output, or `None` for no limit. |
|
|
135
|
+
| `raw_events` | `bool` | Emit parsed JSON stdout lines as `RawEvent` events. |
|
|
136
|
+
| `executable` | `str \| None` | Executable path or name overriding the driver's default. |
|
|
137
|
+
|
|
138
|
+
`Result` is frozen and keyword-only. Its fields are `outcome: Outcome`, `text: str`, `session_id: str | None`, `model: str | None`, `usage: Usage`, `cost_usd: float | None`, `duration_s: float`, `num_turns: int | None`, `stop_reason: str | None`, `returncode: int | None`, `error: Diagnostic | None`, and `warnings: tuple[Diagnostic, ...]`. `text` is the parser's reported final text when the CLI provides one, otherwise the accumulated text of the last assistant message. It is never the concatenation of every assistant turn. Unknown reported values remain `None`. Codex does not report a model, so its `Result.model` is `None`.
|
|
139
|
+
|
|
140
|
+
## Events
|
|
141
|
+
|
|
142
|
+
Each event has an increasing `sequence` and optional CLI-native `entity_id`; the `entity_id` is `None` when the CLI provides no native identifier. Events have no `schema_version` field. The module constant `SCHEMA_VERSION` is `2`. A run that returns a `Result` emits one final `RunCompleted` after cleanup. If cleanup misses its deadline, the result carries a `cleanup_incomplete` warning and termination is not confirmed. If `on_event` or `on_spawn` raises, or the calling task is cancelled, the library cleans up and re-raises without emitting `RunCompleted`.
|
|
143
|
+
|
|
144
|
+
| Event | `kind` | Additional fields |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| `SessionIdentified` | `session.identified` | `session_id` |
|
|
147
|
+
| `MessageStarted` | `assistant.message.started` | none |
|
|
148
|
+
| `TextDelta` | `assistant.text.delta` | `text` |
|
|
149
|
+
| `ReasoningDelta` | `assistant.reasoning.delta` | `text` |
|
|
150
|
+
| `MessageCompleted` | `assistant.message.completed` | none |
|
|
151
|
+
| `ToolStarted` | `tool.started` | `tool`, `activity` (`command` or `tool_call`), `input` |
|
|
152
|
+
| `ToolCompleted` | `tool.completed` | `tool`, `status` (`completed`, `error`, or `unknown`), `output` |
|
|
153
|
+
| `FileChanged` | `file.changed` | `path`, `change` (`added`, `modified`, `deleted`, or `unknown`), `evidence` (`reported` or `inferred`) |
|
|
154
|
+
| `Notice` | `notice` | `code`, `message` |
|
|
155
|
+
| `UsageUpdated` | `usage.updated` | `usage` |
|
|
156
|
+
| `RawEvent` | `raw` | `stream`, `data` |
|
|
157
|
+
| `RunCompleted` | `run.completed` | `result` |
|
|
158
|
+
|
|
159
|
+
## Outcomes and errors
|
|
160
|
+
|
|
161
|
+
| `Outcome` | Meaning |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `success` | The CLI completed successfully. |
|
|
164
|
+
| `retryable` | The run may succeed later, for example after a timeout, service error, nonzero exit, or missing output. |
|
|
165
|
+
| `limit_reached` | A CLI-reported turn or budget limit stopped the run. |
|
|
166
|
+
| `agent_fatal` | The CLI cannot run here, such as when it is missing or authentication fails. |
|
|
167
|
+
| `request_fatal` | The request is invalid or unsupported. |
|
|
168
|
+
| `cancelled` | The caller cancelled the run. |
|
|
169
|
+
|
|
170
|
+
`ErrorCode` values are `bad_cwd`, `invalid_args`, `unsupported_option`, `invalid_prompt`, `missing_cli`, `spawn_failed`, `auth`, `invalid_model`, `context_overflow`, `service_error`, `max_turns`, `budget`, `cancelled`, `timeout`, `idle_timeout`, `stdin_failed`, `execution_error`, `parser_error`, `no_output`, `no_session_id`, `line_too_long`, and `cleanup_incomplete`. `Result.error` is a `Diagnostic(code, message)` when the run has a primary error; `Result.warnings` contains non-fatal diagnostics.
|
|
171
|
+
|
|
172
|
+
`retryable` means possibly transient, not safe to replay. The agent may already have edited files, run commands, or spent tokens. Inspect the working tree before sending the same prompt again.
|
|
173
|
+
|
|
174
|
+
## CLI arguments and permissions
|
|
175
|
+
|
|
176
|
+
Drivers do not add permission, approval, or sandbox-bypass flags. Callers pass any CLI-specific permission flags through `Request.args`:
|
|
177
|
+
|
|
178
|
+
| CLI | Example flag |
|
|
179
|
+
|---|---|
|
|
180
|
+
| Claude Code | `--permission-mode bypassPermissions` |
|
|
181
|
+
| Codex | `--dangerously-bypass-approvals-and-sandbox` |
|
|
182
|
+
| Pi | No automatic permission flag; `--approve` trusts project-local files and is not a tool-permission bypass. |
|
|
183
|
+
| Agy | `--dangerously-skip-permissions` |
|
|
184
|
+
|
|
185
|
+
Only use unattended permission flags in an environment you are prepared to lose. Portable `model`, `reasoning`, and `system_prompt` options are supported selectively; unsupported options and conflicts with `args` are classified as request errors.
|
|
186
|
+
|
|
187
|
+
## Custom drivers
|
|
188
|
+
|
|
189
|
+
Subclass `CliDriver` and implement `launch` and `parser`. `Launch` describes argv, optional stdin, and files to read back after the run. `Parser` converts JSON or text lines into events and final `Facts`:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from collections.abc import Iterable, Mapping
|
|
193
|
+
from pathlib import Path
|
|
194
|
+
from typing import Any, Literal
|
|
195
|
+
|
|
196
|
+
from coding_agent_cli import CliDriver, DriverRegistry, Event, Facts, Launch, Parser, Request
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ExampleParser:
|
|
200
|
+
def feed_json(self, obj: dict[str, Any]) -> Iterable[Event]:
|
|
201
|
+
return ()
|
|
202
|
+
|
|
203
|
+
def feed_text(self, line: str, *, stream: Literal["stdout", "stderr"]) -> Iterable[Event]:
|
|
204
|
+
return ()
|
|
205
|
+
|
|
206
|
+
def finish(self, read_back: Mapping[Path, bytes]) -> Facts:
|
|
207
|
+
return Facts()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class ExampleDriver(CliDriver):
|
|
211
|
+
name = "example"
|
|
212
|
+
executable = "example-cli"
|
|
213
|
+
|
|
214
|
+
def launch(self, req: Request, exe: Path, tmp: Path) -> Launch:
|
|
215
|
+
return Launch(argv=(str(exe), "--prompt", req.prompt))
|
|
216
|
+
|
|
217
|
+
def parser(self, req: Request) -> Parser:
|
|
218
|
+
return ExampleParser()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
registry = DriverRegistry()
|
|
222
|
+
registry.register("example", ExampleDriver())
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`CliDriver` owns process supervision, sequencing, classification, and cleanup. A driver must not add permission flags on behalf of a caller.
|
|
226
|
+
|
|
227
|
+
## Migrating from 0.1
|
|
228
|
+
|
|
229
|
+
The v2 API has no compatibility layer.
|
|
230
|
+
|
|
231
|
+
- **Renamed:** `Driver` is now `CliDriver`, an abstract base class. Implement `launch()` and `parser()` instead of `check()` and `run()`. `check()` is now a module function taking a driver name and optional `executable`, `env`, `refresh`, and `registry` keywords.
|
|
232
|
+
- **Removed:** `EventHandler` and `SpawnHandler` public aliases. Their callback types appear directly in the `run`, `stream`, and `run_sync` signatures. `Driver` is no longer exported; use `CliDriver`.
|
|
233
|
+
- **Added public names:** `CliDriver`, `ErrorCode`, `Facts`, `FileChanged`, `JsonValue`, `Launch`, `Notice`, `Parser`, `RawEvent`, `RequestError`, and `ReasoningDelta`.
|
|
234
|
+
- **Outcomes and errors:** `Outcome.LIMIT_REACHED` is new for turn and budget limits, which 0.1 reported as retryable. `Diagnostic.code` is an `ErrorCode` instead of a free-form `str`.
|
|
235
|
+
- **Fields added:** `ToolCompleted.tool` and `ToolCompleted.output`; `Result.cost_usd`, `Result.duration_s`, `Result.num_turns`, and `Result.stop_reason`; `Capabilities.version`.
|
|
236
|
+
- **Event schema:** `schema_version` is no longer stored on each event. Import the `SCHEMA_VERSION` module constant instead. `entity_id` contains the CLI-native identifier or `None` when unavailable.
|
|
237
|
+
- **Result behavior:** `Result.text` is the parser's final text when reported, otherwise the last assistant message, rather than the concatenation of all assistant messages. Codex reports `Result.model` as `None`.
|
|
238
|
+
- **Removed behavior:** codex rollout-file parsing is removed; Codex output is parsed from its structured stream.
|
|
239
|
+
- **Registry:** `DriverRegistry.register` accepts only `CliDriver` instances; structurally compatible objects are rejected.
|
|
240
|
+
- **Callbacks and cancel:** `run` accepts synchronous or awaitable `on_event` and `on_spawn` callbacks. `run_sync` now accepts `on_event` and a `threading.Event` for `cancel`.
|
|
241
|
+
- **Request:** `idle_timeout_s` and `raw_events` are available in addition to the 0.1 request fields.
|
|
242
|
+
|
|
243
|
+
## Known limits
|
|
244
|
+
|
|
245
|
+
- On Windows, Job Object assignment happens immediately after process spawn. In the short window before assignment, the process can exit or spawn descendants; descendants created in that window are outside the job and can survive process-tree cleanup. If creating, configuring, or assigning the Job Object fails, the library silently falls back to `taskkill /T`, which cannot reach a descendant whose parent already exited; no `cleanup_incomplete` warning is reported in that case.
|
|
246
|
+
- Agy receives the prompt in argv, which is limited to 100 KiB by the driver.
|
|
247
|
+
- Agy output that mixes structured JSON events and plain text on stdout is unsupported.
|
|
248
|
+
- Set `idle_timeout_s` longer than the longest tool command, because a quiet but still-running command can reach the idle timeout.
|
|
249
|
+
|
|
250
|
+
## Contributing
|
|
251
|
+
|
|
252
|
+
Contributions are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the coding standard, and the pull request process. Report security problems privately as described in [SECURITY.md](SECURITY.md). Everyone taking part follows the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
uv sync
|
|
256
|
+
uv run ruff format --check && uv run ruff check
|
|
257
|
+
uv run pyright
|
|
258
|
+
uv run pytest
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
[MIT](LICENSE)
|
|
264
|
+
|
|
265
|
+
## Provenance
|
|
266
|
+
|
|
267
|
+
This library is extracted from the CLI-driver layer of [Refract](https://github.com/sitien173/refract), a multi-model coordination tool. Its drivers, process handling, and many of its tests are ported from Refract under the MIT license.
|