feat: OpenCode runner support#396
Conversation
Add QwenRunner as a fourth CLI backend alongside Claude Code, Bob Shell, and Codex. Qwen Code's CLI flags mirror Claude Code (--append-system-prompt, -p, --model), making it the simplest runner addition. Key details: - Auth: warns if DASHSCOPE_API_KEY/QWEN_API_KEY missing (non-blocking) - Headless: uses --yolo and --output-format text - Dry-run: FACTORY_QWEN_DRY_RUN=1 returns stub responses - Env: strips VIRTUAL_ENV from subprocess environment - 22 tests covering command construction, auth, dry-run, env, and errors Signed-off-by: Luke Inglis <lukeinglis21@yahoo.com>
Signed-off-by: Luke Inglis <lukeinglis21@yahoo.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #396 +/- ##
==========================================
+ Coverage 87.31% 87.43% +0.12%
==========================================
Files 61 62 +1
Lines 9339 9472 +133
==========================================
+ Hits 8154 8282 +128
- Misses 1185 1190 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
xukai92
left a comment
There was a problem hiding this comment.
Clean addition that follows the existing Claude/Codex runner pattern with dry-run + auth-warn parity and good test coverage. One small contract bug noted below — happy to approve once gated (or as a quick follow-up PR).
[P2] dangerously_skip_permissions is silently ignored in QwenRunner.headless
factory/runners/qwen.py around the headless method:
dangerously_skip_permissions: bool = True,
...
cmd = [
"qwen",
"--append-system-prompt", prompt,
"-p", task,
"--yolo",
"--output-format", "text",
]The signature accepts dangerously_skip_permissions: bool = True to match the Runner protocol used by Claude/Codex/Bob, but --yolo is appended unconditionally. interactive_run in the same file gates this flag correctly, and the sibling runners all gate their equivalents. A caller that explicitly passes dangerously_skip_permissions=False to disable auto-approval will still get --yolo, bypassing all permission prompts.
The default is True so current call sites are unaffected, but the contract is broken for any future caller.
Suggested fix:
cmd = [
"qwen",
"--append-system-prompt", prompt,
"-p", task,
"--output-format", "text",
]
if dangerously_skip_permissions:
cmd.append("--yolo")Worth adding a dangerously_skip_permissions=False test in the headless suite — there's already an equivalent for interactive_run.
- Add factory/runners/opencode.py implementing the Runner protocol - Uses 'opencode run' subcommand pattern with prompt prepended to task - --dangerously-skip-permissions gated on parameter (not unconditional) - Dry-run mode via FACTORY_OPENCODE_DRY_RUN=1 - Register 'opencode' in RunnerName and _RUNNERS registry - Update CLAUDE.md with OpenCode runner docs and config profile - Tests in tests/test_opencode_runner.py (20 tests) - Remove factory/runners/qwen.py and tests/test_qwen_runner.py Signed-off-by: Luke Inglis <linglis@redhat.com> Signed-off-by: Luke Inglis <lukeinglis21@yahoo.com>
|
Thanks for the review @xukai92. Heads up: this PR has been significantly reworked since your review. Scope change: Replaced the Qwen Code runner with an OpenCode runner (opencode.ai). The runner pattern and test structure remain the same, but the underlying CLI mapping is different. Your P2 is addressed: --dangerously-skip-permissions is now gated on the parameter in both headless() and interactive_run(), matching how ClaudeRunner handles it. A dangerously_skip_permissions=False test is included for both methods. Key implementation details:
Files changed: factory/runners/opencode.py (new), tests/test_opencode_runner.py (new, 20 tests passing), factory/runners/init.py, CLAUDE.md. Old qwen.py and test_qwen_runner.py are deleted. Ready for re-review when you get a chance. |
Summary
Closes #394
Usage
factory ceo /path --runner opencode factory agent researcher --task '...' --runner opencode FACTORY_RUNNER=opencode factory ceo /pathConfig profile:
Test plan