Skip to content

fix(integrations): dispatch Forge via prompt flag only - #4668

Open
hmohammadi wants to merge 1 commit into
github:mainfrom
hmohammadi:fix/4666-forge-prompt-flags
Open

hmohammadi wants to merge 1 commit into
github:mainfrom
hmohammadi:fix/4666-forge-prompt-flags

Conversation

@hmohammadi

Copy link
Copy Markdown
Contributor

Description

Fixes #4666. Part of the audit in #2416.

ForgeIntegration never overrode build_exec_args(), so it inherited the generic implementation from MarkdownIntegration (base.py), which emits -p <prompt> --model <model> --output-format json. Forge accepts -p/--prompt, but the other two flags do not exist in its CLI, so every workflow command:/prompt: step targeting Forge aborted at argument parsing before the agent ran:

$ forge -p "hello" --model gpt-4o --output-format json
error: unexpected argument '--model' found

Usage: forge.exe --prompt <PROMPT>
$ echo $?
2

Unlike the Amp case (#4581), both inherited extras are invalid, so both dispatch paths broke:

  • stream=False (output_json=True) appends --output-format json → exit 2.
  • stream=True (the default) drops --output-format but still appends --model <model> whenever a model is configured → exit 2.

The only surviving combination was stream=True with no model, which reduces to plain forge -p <prompt>.

This keeps -p and drops the two invalid flags:

$ uv run python -c "from specify_cli.integrations import get_integration; print(get_integration('forge').build_exec_args('/speckit-plan add OAuth', model='gpt-4o', output_json=True))"
['forge', '-p', '/speckit-plan add OAuth']

Verified against forge --help (v2.13.21):

Flag previously sent In the Forge CLI? Notes
-p <prompt> Yes kept as-is; also accepts the /speckit-… slash invocations
--model <model> No no model flag exists (see below)
--output-format json No no top-level equivalent; --porcelain is subcommand-only

On model: it is deliberately dropped rather than remapped. Forge exposes no model-selection flag — model choice is a persisted setting (forge config set model <provider> <model>, or interactive forge select model). --agent <AGENT> takes an agent ID, not a model identifier, so forwarding the caller's model onto it would silently select the wrong thing. This is the one judgement call in the diff; happy to change it if maintainers prefer a different mapping.

On flag order: SPECKIT_INTEGRATION_FORGE_EXTRA_ARGS is applied before -p, matching the opencode / goose / codex ordering. Confirmed Forge parses its global flags ahead of the prompt flag — forge --verbose -p "x" --bogus rejects only --bogus.

Same fix shape as the existing one-off overrides for Amp (#4581), opencode (#2409) and goose (#3781); follows the AmpIntegration.build_exec_args() pattern.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

Added 4 regression tests to tests/integrations/test_integration_forge.py, which previously had no build_exec_args coverage at all.

Red-green verified. With the source change reverted but the tests kept, all 4 fail; with the fix restored, all 4 pass:

# fix stashed, tests kept
4 failed, 26 deselected

# fix restored
4 passed, 26 deselected

Worth noting one of these caught a real trap during development: a first draft of the "uses -p" test used output_json=False with no model, which is exactly the one combination that already passes on main. It was rewritten to assert the real dispatch shape (model set, output_json=True).

End-to-end against the real Forge CLI (npx -y @antinomyhq/forge@latest, v2.13.21) on Windows 11:

# before — main's argv
$ forge -p "/speckit-plan add OAuth" --model gpt-4o --output-format json
error: unexpected argument '--model' found          (exit 2)

# after — this branch's argv, with a deliberately bogus probe flag to stop
# before execution rather than at parsing
$ forge -p "/speckit-plan add OAuth" --zz-probe-stop
error: unexpected argument '--zz-probe-stop' found

Only the probe flag is rejected, confirming the generated argv itself parses clean.

Targeted suites on this branch:

  • tests/integrations/test_integration_forge.py — 30 passed
  • tests/integrations/test_extra_args.py, test_base.py, test_integration_base_markdown.py (the shared machinery this override calls into) plus tests/test_agent_config_consistency.py — 177 passed, 1 skipped
  • tests/integrations — 17 failed, 2634 passed, 14 skipped. All 17 failures are symlink tests that abort in fixture setup with OSError: [WinError 1314] A required privilege is not held by the client, because this Windows account has no symlink-creation privilege. None of them is a Forge test, and collection is 2661 on main versus 2665 here — exactly the four tests added, both figures measured. CI is the authority on pass/fail rather than my machine.

ruff reports the same 10 pre-existing findings on these two files before and after the change; this diff introduces none.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (fill in the disclosure below)

AI disclosure: Agent/tool: Claude Code (interactive CLI session). Model: Claude Opus 5, 1M-token context window, high reasoning effort. Mode: human-supervised, not autonomous — I reviewed the diff and the tests line by line, approved each step, and ran the quoted commands on my own machine. Extent: code generation (the build_exec_args override and the four regression tests) plus drafting of this description, the linked issue #4666, and any comments I post on this PR. The Forge flag surface was verified against forge --help from the installed binary (@antinomyhq/forge v2.13.21) and against the upstream clap Cli struct, not from model recall.

Fixes github#4666.

`ForgeIntegration` never overrode `build_exec_args()`, so it inherited
`MarkdownIntegration`'s generic `-p <prompt> --model <model>
--output-format json`. Forge accepts `-p/--prompt`, but `--model` and
`--output-format` do not exist in its CLI, so every workflow
`command:`/`prompt:` step targeting Forge aborted at argument parsing
with `error: unexpected argument '--model' found` (exit 2) before the
agent ever ran.

Unlike the Amp case, both inherited extras are invalid, so both dispatch
paths broke: `stream=False` appends `--output-format json`, while
`stream=True` still appends `--model` whenever a model is configured.
The only surviving combination was `stream=True` with no model, which
reduces to plain `forge -p <prompt>`.

`model` is deliberately dropped rather than remapped: Forge exposes no
model-selection flag. Model choice is a persisted setting (`forge config
set model`), and `--agent` takes an agent ID, not a model identifier, so
forwarding the caller's model onto it would silently select the wrong
thing. `output_json` is dropped for the same reason: Forge's
machine-readable `--porcelain` exists only on certain subcommands, not
on the top-level prompt invocation.

Extra args from `SPECKIT_INTEGRATION_FORGE_EXTRA_ARGS` are applied
before `-p`, matching the opencode / goose / codex ordering; Forge
parses its global flags ahead of the prompt flag.

Same fix shape as the one-off overrides for Amp (github#4581), opencode
(github#2409) and goose (github#3781). Part of the audit in github#2416.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hmohammadi
hmohammadi requested a review from mnriem as a code owner September 22, 2026 09:41
@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Sep 22, 2026
@hmohammadi

Copy link
Copy Markdown
Contributor Author

Follow-up on the Testing section: I now have a Linux environment for this repo, so the Windows caveat above is resolved. Full suite on this branch:

8359 passed, 17 skipped, 0 failed

Ubuntu 26.04.1 LTS, Python 3.14.4, PowerShell 7.6.6, uv run pytest against 5bf5cb0 with a clean working tree.

The failures reported earlier were symlink tests aborting in fixture setup with OSError: [WinError 1314] A required privilege is not held by the client — my Windows account lacks symlink-creation privilege. That was an artifact of the machine, not of this change, and it does not occur here.

This comment was drafted with Claude Code (Claude Opus 5); I ran the suite and verified the numbers on my own machine.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Forge dispatch sends --model/--output-format — neither exists in the Forge CLI

2 participants