From 1c55988637e5d29943dbd22148843d32c72cf1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=B8=80?= Date: Tue, 19 May 2026 14:40:18 +0800 Subject: [PATCH] fix(cursor-agent): enable CLI dispatch via ``-p --trust`` headless mode 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, , ...]`` 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-``). * Full repo: 1085 / 1085 passed, no regressions. Fixes #2629 Co-authored-by: Cursor --- .../integrations/cursor_agent/__init__.py | 31 ++++++++++- .../test_integration_cursor_agent.py | 52 +++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/specify_cli/integrations/cursor_agent/__init__.py b/src/specify_cli/integrations/cursor_agent/__init__.py index 70af454ce..7196c7fd0 100644 --- a/src/specify_cli/integrations/cursor_agent/__init__.py +++ b/src/specify_cli/integrations/cursor_agent/__init__.py @@ -2,6 +2,10 @@ Cursor Agent uses the ``.cursor/skills/speckit-/SKILL.md`` layout. Commands are deprecated; ``--skills`` defaults to ``True``. + +CLI dispatch via ``cursor-agent -p --trust `` is supported so +``specify workflow run`` can drive cursor-agent headlessly, in addition +to the existing in-IDE skill flow. """ from __future__ import annotations @@ -15,8 +19,8 @@ class CursorAgentIntegration(SkillsIntegration): "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, } registrar_config = { "dir": ".cursor/skills", @@ -28,6 +32,29 @@ class CursorAgentIntegration(SkillsIntegration): context_file = ".cursor/rules/specify-rules.mdc" multi_install_safe = True + def build_exec_args( + self, + prompt: str, + *, + model: str | None = None, + output_json: bool = True, + ) -> list[str] | None: + """Build CLI arguments for non-interactive ``cursor-agent`` execution. + + Uses ``-p`` (print/headless mode, which gives access to all + tools including write and shell) plus ``--trust`` (bypass the + Workspace Trust prompt — mandatory for headless execution; the + CLI exits non-zero without it). + """ + if not self.config or not self.config.get("requires_cli"): + return None + args = [self.key, "-p", "--trust", prompt] + if model: + args.extend(["--model", model]) + if output_json: + args.extend(["--output-format", "json"]) + return args + @classmethod def options(cls) -> list[IntegrationOption]: return [ diff --git a/tests/integrations/test_integration_cursor_agent.py b/tests/integrations/test_integration_cursor_agent.py index 352a0475b..5c8e518c4 100644 --- a/tests/integrations/test_integration_cursor_agent.py +++ b/tests/integrations/test_integration_cursor_agent.py @@ -106,3 +106,55 @@ class TestCursorAgentAutoPromote: assert result.exit_code == 0, f"init --ai cursor-agent failed: {result.output}" assert (target / ".cursor" / "skills" / "speckit-plan" / "SKILL.md").exists() + +class TestCursorAgentCliDispatch: + """Verify the CLI dispatch path for cursor-agent (issue #2629). + + The ``cursor-agent`` CLI supports headless execution via ``-p`` (with + full tool access including write/shell) and requires ``--trust`` to + bypass the Workspace Trust prompt. These tests pin the exact argv + shape that the workflow runner will use. + """ + + def test_requires_cli_is_true(self): + i = get_integration("cursor-agent") + assert i.config.get("requires_cli") is True + + def test_install_url_is_set(self): + i = get_integration("cursor-agent") + url = i.config.get("install_url") + assert url is not None + assert "cursor.com" in url + + def test_build_exec_args_default_includes_trust_and_json(self): + i = get_integration("cursor-agent") + args = i.build_exec_args("/speckit-specify some-feature") + assert args == [ + "cursor-agent", "-p", "--trust", + "/speckit-specify some-feature", + "--output-format", "json", + ] + + def test_build_exec_args_text_output_omits_format(self): + i = get_integration("cursor-agent") + args = i.build_exec_args("/speckit-plan", output_json=False) + assert args == [ + "cursor-agent", "-p", "--trust", "/speckit-plan", + ] + + def test_build_exec_args_with_model(self): + i = get_integration("cursor-agent") + args = i.build_exec_args( + "/speckit-specify", model="sonnet-4-thinking", output_json=False + ) + assert args == [ + "cursor-agent", "-p", "--trust", "/speckit-specify", + "--model", "sonnet-4-thinking", + ] + + def test_build_command_invocation_uses_hyphenated_skill_name(self): + """SkillsIntegration: /speckit-plan (not /speckit.plan).""" + i = get_integration("cursor-agent") + assert i.build_command_invocation("speckit.plan", "feature-x") == "/speckit-plan feature-x" + assert i.build_command_invocation("plan") == "/speckit-plan" +