Skip to content

[Bug]: Systematic build_exec_args() audit — CLI dispatch flags unverified for 11+ integrations #2416

@markuswondrak

Description

@markuswondrak

Scope extends beyond opencode — systematic build_exec_args() audit

While investigating the opencode dispatch bug, I audited all 28 integrations and found that the root cause is not specific to opencode — it's a systematic architectural weakness in how build_exec_args() is inherited.

Current state

Only 4 out of 15 CLI-capable integrations have verified, correct build_exec_args() implementations:

Integration Base class CLI invocation Status
Claude SkillsIntegration claude -p "prompt" --model X --output-format json ✅ Verified (-p is documented)
Gemini TomlIntegration gemini -p "prompt" -m X --output-format json ✅ Verified (-p and -m are documented)
Codex SkillsIntegration codex exec "prompt" --model X --json ✅ Custom override with exec subcommand
Copilot IntegrationBase copilot -p "prompt" --agent X --yolo --model X --output-format json ✅ Custom override with dynamic --yolo

The remaining 11 integrations with requires_cli: True all inherit build_exec_args() from their base class without verification that the CLI actually supports the generated flags:

Integration Inherited invocation Risk
OpenCode opencode -p "prompt" --model X --output-format json Confirmed broken — should be opencode run "prompt" -m X
Amp amp -p "prompt" --model X --output-format json ⚠️ Unverified
Auggie auggie -p "prompt" --model X --output-format json ⚠️ Unverified
CodeBuddy codebuddy -p "prompt" --model X --output-format json ⚠️ Unverified
Forge forge -p "prompt" --model X --output-format json ⚠️ Unverified
iFlow iflow -p "prompt" --model X --output-format json ⚠️ Unverified
Junie junie -p "prompt" --model X --output-format json ⚠️ Unverified
Kiro CLI kiro-cli -p "prompt" --model X --output-format json ⚠️ Unverified
Pi pi -p "prompt" --model X --output-format json ⚠️ Unverified
QoderCLI qodercli -p "prompt" --model X --output-format json ⚠️ Unverified
Qwen qwen -p "prompt" --model X --output-format json ⚠️ Unverified

Additionally, 3 SkillsIntegration-based CLIs (Kimi, Shai, Vibe) and 1 TomlIntegration-based CLI (Tabnine) inherit the same -p pattern without verification.

There's also a data inconsistency: Goose has requires_cli: True but inherits from YamlIntegration, which doesn't override build_exec_args() — so it returns None, meaning CLI dispatch silently fails despite claiming CLI support.

Root cause

MarkdownIntegration.build_exec_args() hardcodes three assumptions that don't hold for all CLIs:

  1. Prompt mechanism: -p flag (works for Claude/Gemini) vs. run/exec subcommand (opencode/codex) vs. other formats
  2. Model flag: --model long form (most) vs. -m short form (Gemini, opencode) vs. not supported
  3. JSON output: --output-format json (most) vs. --json (Codex) vs. -f json (opencode) vs. not supported

Every deviation requires a full method override, which is easy to forget (as the opencode bug shows).

Proposed fix: Declarative batch-mode attributes

Instead of requiring each integration to override build_exec_args(), introduce class-level attributes on IntegrationBase that make the common variations declarative:

class IntegrationBase(ABC):
    exec_mode: str = "flag"           # "flag" | "subcommand" | "none"
    exec_prompt_flag: str = "-p"      # flag used when exec_mode="flag"
    exec_subcommand: str = ""          # subcommand when exec_mode="subcommand"
    exec_model_flag: str = "--model"   # "--model" or "-m"
    exec_json_args: tuple[str, ...] = ("--output-format", "json")  # or ("--json",) or ()

Then build_exec_args() on IntegrationBase uses these attributes, and most integrations need zero method overrides:

# OpencodeIntegration — just attributes, no method override
class OpencodeIntegration(MarkdownIntegration):
    key = "opencode"
    exec_mode = "subcommand"
    exec_subcommand = "run"
    exec_model_flag = "-m"
    exec_json_args = ()
    ...

# CodexIntegration — just attributes, no method override
class CodexIntegration(SkillsIntegration):
    key = "codex"
    exec_mode = "subcommand"
    exec_subcommand = "exec"
    exec_json_args = ("--json",)
    ...

Complex cases like Copilot (dynamic --yolo flag) still override build_exec_args() directly.

This makes CLI invocation patterns visible at a glance in the integration class, eliminates an entire class of bugs, and reduces boilerplate from ~15 lines per override to ~4 lines of attributes.


Originally posted as a comment on #2409

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions