github / github/spec-kit

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

Abierto
#2,416 3 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
137k
Forks
12.3k
Merge medio
2 d 7 h
PR fusionados (30 d)
155

Descripción

## 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:

```python
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:

```python
# 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](https://github.com/github/spec-kit/issues/2409#issuecomment-4344752659)*

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.