fix(workflows): dispatch prompt steps via the resolved executable (#3793)

PromptStep._try_dispatch runs `subprocess.run(exec_args, ...)` with an
UNRESOLVED argv[0] -- a bare name like `claude`. On Windows subprocess.run calls
CreateProcess, which does not consult PATHEXT, so an agent CLI installed as a
`.cmd`/`.bat` shim (the usual npm layout) raises FileNotFoundError [WinError 2].
That OSError is swallowed by the method's `except OSError: return None`, and
execute() then reports "CLI not found or not installed" -- even though the
step's own preflight `shutil.which(...)` two lines earlier just found it.

The sibling path does not have this bug: IntegrationBase.dispatch_command (used
by the `command` step) resolves argv[0] through shutil.which first, added in
8e5643d for exactly this reason. Same machine, same integration, CLI present as
a .cmd shim:

    type: prompt  -> failed    "integration 'claude' CLI not found or not installed."
    type: command -> completed

Primitive confirmation: bare `subprocess.run(["fakeagent"])` raises
[WinError 2] while `subprocess.run([shutil.which("fakeagent")])` runs fine.

Reuse the path the preflight already resolved (`fallback_cli_path`) instead of
calling which() again, so the shim is executed. On POSIX it is the same
executable, so behaviour is unchanged there.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali jawwad
2026-07-29 17:44:04 +05:00
committed by GitHub
parent f04a36a629
commit d99170fb5e
2 changed files with 46 additions and 0 deletions

View File

@@ -169,6 +169,17 @@ class PromptStep(StepBase):
if not exec_args:
return None
# Windows: ``subprocess.run`` calls ``CreateProcess``, which does not
# consult ``PATHEXT``, so a bare command name like ``claude`` installed
# as ``claude.cmd`` (the usual npm shim layout) fails with
# ``WinError 2``. That OSError is swallowed below and reported as "CLI
# not found or not installed" -- even though the preflight above just
# found it. Reuse the already-resolved path so the shim is executed,
# mirroring ``IntegrationBase.dispatch_command``, which the ``command``
# step already goes through. On POSIX this is the same executable.
if fallback_cli_path:
exec_args = [fallback_cli_path, *exec_args[1:]]
import subprocess
project_root = (