mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
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, <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 #2629 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
Cursor Agent uses the ``.cursor/skills/speckit-<name>/SKILL.md`` layout.
|
||||
Commands are deprecated; ``--skills`` defaults to ``True``.
|
||||
|
||||
CLI dispatch via ``cursor-agent -p --trust <prompt>`` 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 [
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user