fix(cursor-agent): enable headless CLI dispatch end-to-end (-p --trust --approve-mcps --force + Windows .cmd shim resolution)#2631
Conversation
Restores the ability for ``specify workflow run`` to dispatch the cursor-agent CLI, complementing the existing in-IDE skill flow. Without this fix, ``specify workflow run speckit --input integration=cursor-agent ...`` fails with a misleading ``CLI not found or not installed`` error even when the CLI is installed (since cursor-agent had ``requires_cli=False`` and an unset ``build_exec_args``). The cursor-agent CLI (>= 2026.05.16) supports headless execution via ``-p`` (print mode with full tool access including write/shell) and ``--trust`` (bypass Workspace Trust prompt). Without ``--trust`` the CLI exits non-zero in non-TTY contexts (verified locally). Changes to ``src/specify_cli/integrations/cursor_agent/__init__.py``: * ``config.requires_cli``: ``False`` -> ``True`` * ``config.install_url``: ``None`` -> Cursor CLI docs URL * Override ``build_exec_args()`` to emit ``[cursor-agent, -p, --trust, <prompt>, ...]`` with optional ``--model`` and ``--output-format json`` flags, mirroring the shape used by ``claude``/``codex``/``gemini``. Tests: * 34 existing cursor-agent tests still pass. * 6 new tests in ``TestCursorAgentCliDispatch`` pin ``requires_cli``, ``install_url``, and the exact argv shape (default, text-output, with-model, and the hyphenated skill invocation form ``/speckit-<name>``). * Full repo: 1085 / 1085 passed, no regressions. Fixes github#2629 Co-authored-by: Cursor <cursoragent@cursor.com>
On Windows, ``shutil.which`` honors ``PATHEXT`` and locates wrappers like ``cursor-agent.cmd`` and ``codex.cmd``, but Python's ``subprocess.run`` calls ``CreateProcess`` which does **not** consult ``PATHEXT`` and therefore fails with ``WinError 2`` on a bare argv like ``[cursor-agent, ...]``. Resolve ``exec_args[0]`` via ``shutil.which`` in ``IntegrationBase.dispatch_command`` so ``.cmd``/``.bat`` shims work transparently. On POSIX this is a no-op for absolute paths and a harmless lookup otherwise. Verified locally on Windows 10 + cursor-agent 2026.05.16: without this fix, ``specify workflow run speckit --input integration=cursor-agent`` fails with ``FileNotFoundError`` even after the cursor-agent integration starts producing valid exec args (per the prior commit on this branch). Tests: * New: 2 cursor-agent tests pin the shim-resolution + passthrough behavior (``test_dispatch_command_resolves_cmd_shim_for_subprocess`` and ``test_dispatch_command_passthrough_when_shutil_which_finds_nothing``). * Updated: ``tests/test_workflows.py::TestCommandStep::test_dispatch_with_mock_cli`` was mocking ``shutil.which`` only at the ``command`` step level and not at the ``base`` level, which made it environment-sensitive (fails locally when the real ``claude`` CLI is on PATH). Added the matching base-level patch and updated the argv-assertion to reflect the resolved path. ``test_dispatch_failure_returns_failed_status`` gets the same patch for consistency. * Full repo: 2867 passed, 0 regression from this PR. The 12 remaining pre-existing failures are unrelated Windows ``symlink`` privilege failures (``WinError 1314``) on a non-admin Windows runner. Co-authored-by: Cursor <cursoragent@cursor.com>
…l access The previous commit (1c55988) wired up ``-p --trust`` so the CLI launches in headless mode without the Workspace Trust prompt, but that alone is not enough to let ``specify workflow run`` drive a real speckit feature end-to-end with cursor-agent on Windows. Two more flags are required: * ``--approve-mcps``: without it, every MCP server configured in ``.cursor/mcp.json`` stays ``not loaded (needs approval)``, and any tool call against them is silently dropped. We hit this immediately trying to read a DingTalk PRD from a remote MCP server during the ``/speckit-specify`` step. * ``--force``: without it, the agent halts on the first tool-call approval prompt (the tool call gets rejected and the workflow exits non-zero with a misleading message). With ``--force`` cursor-agent matches the implicit "trusted environment" semantics that ``claude -p`` and ``codex --exec`` already have by default -- which is the right semantics for an unattended ``specify workflow run`` invocation. Verified end-to-end on Windows 10 + cursor-agent 2026.05.16-0338208: * ``cursor-agent -p --trust --approve-mcps --force --output-format text`` + a ``/speckit-specify`` prompt that included a DingTalk URL produced a full spec.md (31.5 KB) plus checklists/requirements.md in ~10.7 min, reading the source PRD through the ``dingtalk-doc`` remote MCP server, deciding the ``specs/`` subpath itself, and updating ``.specify/feature.json`` and ``specs/menu-dictionary.md`` along the way -- no human-in-the-loop, no source PRD ever touched the filesystem. * Without ``--approve-mcps`` the same prompt errors with the tool call rejected message; without ``--force`` the agent stops at the first non-MCP tool call. Tests: * ``test_build_exec_args_*`` updated to pin the new four-flag prefix. * New ``test_build_exec_args_contains_mandatory_headless_flags`` asserts the four flags are always present together. * ``test_dispatch_command_resolves_cmd_shim_for_subprocess`` updated to match the new argv layout. * All 43 cursor-agent tests pass; no other tests touched. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Enables specify workflow run to dispatch the cursor-agent integration via its CLI in a fully headless mode (including Windows .cmd/.bat shim support), aligning Cursor’s workflow-runner behavior with existing CLI-driven integrations.
Changes:
- Add explicit headless CLI argv construction for
cursor-agent(-p --trust --approve-mcps --force, plus optional--model/ JSON output). - Fix Windows CLI dispatch by resolving
.cmd/.batshims viashutil.which()before callingsubprocess.run(). - Update and add tests to pin the new behavior and make workflow dispatch tests environment-independent.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/integrations/cursor_agent/__init__.py |
Adds cursor-agent CLI dispatch support and config updates (install URL / requires_cli). |
src/specify_cli/integrations/base.py |
Resolves executable via shutil.which() prior to subprocess.run() for Windows shim compatibility. |
tests/integrations/test_integration_cursor_agent.py |
Adds coverage for cursor-agent CLI argv shape and .cmd shim resolution behavior. |
tests/test_workflows.py |
Updates command-step tests to patch shutil.which at the new resolution layer and assert resolved executable path. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 4/4 changed files
- Comments generated: 1
| config = { | ||
| "name": "Cursor", | ||
| "folder": ".cursor/", | ||
| "commands_subdir": "skills", | ||
| "install_url": None, | ||
| "requires_cli": False, | ||
| "install_url": "https://docs.cursor.com/en/cli/overview", | ||
| "requires_cli": True, | ||
| } |
mnriem
left a comment
There was a problem hiding this comment.
Please address Copilot feedback
| i = get_integration("cursor-agent") | ||
| url = i.config.get("install_url") | ||
| assert url is not None | ||
| assert "cursor.com" in url |
|
Please address code scanning resulsts |
…) instead of requires_cli Co-authored-by: Cursor <cursoragent@cursor.com>
…thout requires_cli Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks for the review! I've addressed both points in ff07cc0 + 422b250:
|
|
Please address Copilot feedback |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
This autofix suggestion was applied. |
Summary
Enable
specify workflow runto dispatch thecursor-agentCLI headlessly end-to-end on Windows, matching the way it already dispatchesclaude/codex/gemini. Existing in-IDE skill flow (via.cursor/skills/speckit-*/SKILL.md) is preserved exactly — this PR only adds the CLI dispatch path on top.Fixes #2629.
Problem
Before this PR,
specify workflow run speckit --input integration=cursor-agent ...fails immediately with:even when
cursor-agentis installed and healthy (cursor-agent --versionreturns2026.05.16-0338208).Three separate gaps each independently block the headless path; this PR fixes all three.
Three commits, three gaps
1)
1c55988 fix(cursor-agent): enable CLI dispatch via -p --trust headless modeCursorAgentIntegrationhadrequires_cli: Falseand did not overridebuild_exec_args, soSkillsIntegration.build_exec_argsreturnedNone, the dispatcher inworkflows/steps/command/__init__.py::_try_dispatchreturnedNone, and the misleading CLI-missing error was surfaced. The CLI is actually fully headless-capable:-p / --print— print mode, "Has access to all tools, including write and shell"--output-format text | json | stream-json--model <name>--trust— "Trust the current workspace without prompting (only works with --print/headless mode)"Without
--trust, the CLI exits non-zero with⚠ Workspace Trust Required.This commit:
config["requires_cli"]:False→Trueconfig["install_url"]:None→https://docs.cursor.com/en/cli/overviewbuild_exec_argsto emit[self.key, "-p", "--trust", prompt, …].2)
454d3d0 fix(integrations): resolve .cmd/.bat shims before subprocess.runAfter commit (1), the dispatcher still failed on Windows with
FileNotFoundError [WinError 2]. Root cause:shutil.which("cursor-agent")returnscursor-agent.CMD(honoringPATHEXT).subprocess.run(["cursor-agent", ...])callsCreateProcess, which does not consultPATHEXT, so the bare command name fails to launch.The same bug affects every
.cmd/.bat-wrapped CLI on Windows (verified forcodexas well; only.exeCLIs likeclaudewere unaffected).Fix in
IntegrationBase.dispatch_command: resolveexec_args[0]viashutil.whichbeforesubprocess.run. No-op on POSIX (noPATHEXTto consult); transparent fix on Windows.3)
595bcab fix(cursor-agent): inject --approve-mcps --force for headless MCP/tool accessAfter commits (1)+(2),
subprocess.runsucceeds and cursor-agent starts, but the first MCP call (e.g. to read a DingTalk PRD) fails withThe tool call was rejected. I can't retrieve the document name without permission to call the tool.Two more flags are needed:--approve-mcps— without it, every MCP server staysnot loaded (needs approval)and tool calls are silently dropped.--force— without it, the agent halts on the first tool-call approval prompt; the workflow returns a misleading non-zero exit. With it, cursor-agent matches the implicit "trusted environment" semantics thatclaude -pandcodex --execalready have by default — the right semantics for an unattendedspecify workflow run.Final argv after all three commits:
End-to-end verification (real DingTalk PRD, just a URL as input)
Repro on Windows 10 + cursor-agent
2026.05.16-0338208:Two independent runs (
df8c4b9b,388d3c15) both reach thereview-specgate:(Both runs above were eventually cancelled by
Ctrl+Cwhile the gate step was just starting, which surfaced an[Errno 22] Invalid argumentfrom the interruptedsubprocess.run. That was an artifact of my cancellation, not a gate-step bug —review-specis agatestep and its designed terminal status ispaused. Independent of this PR either way.)Backward compatibility
.cursor/skills/speckit-*/SKILL.md,--skillsdefault) — unchanged.multi_install_safe,context_file,registrar_config,options()— unchanged.shutil.whichlookup is a no-op for already-absolute exec_args[0], and the--approve-mcps --forceflags exist on Linux/macOS cursor-agent identically.catalog.json,workflow.yml, templates — untouched.claude,codex,gemini) — theirbuild_exec_argspaths are not touched; the only shared change isbase.py::dispatch_commandadding theshutil.whichshim resolution, which only takes effect whenshutil.whichreturns a different path than the input (i.e..cmd/.batshim case on Windows).Testing
requires_cli is Trueandinstall_urlpoints at cursor.com[cursor-agent, -p, --trust, --approve-mcps, --force, …](default + text-output + with-model variants)/speckit-plannot/speckit.plan).cmdshim resolution: subprocess receives fullcursor-agent.CMDpath, not barecursor-agentshutil.whichreturnsNonetests/test_workflows.py::TestCommandStep::test_dispatch_with_mock_cliandtest_dispatch_failure_returns_failed_statusupdated to patchshutil.whichat thebase.pylayer (was previously only patching the dispatcher layer, which made the test environment-sensitive — passed on CI with noclaudeon PATH, failed locally withclaudeinstalled).symlinkprivilege issues (WinError 1314) requiring elevated runner — not introduced or affected by this PR.Out of scope (deliberately)
"CLI not found or not installed. Install the CLI tool."error message itself (it conflates 5 distinct dispatch failure modes into one). Worth a separate PR to split per-failure-mode messages — happy to do that as a follow-up if maintainers want.cursor-agentto bundledspeckitworkflow'srequires.integrations.anylist (currently["copilot", "claude", "gemini"]). Not strictly required —specify workflow rundoes not enforce that list strictly today — but worth a follow-up sospecify workflow info speckitis consistent with the actual support matrix.docs/reference/integrations.mdpage for cursor-agent (none exists today; happy to add if wanted).speckit.git.featurestep tospeckitworkflow.yml (current workflow doesn't auto-create feature branches; that's by design but worth surfacing as an opt-in for teams that want branch-per-feature).