From 3dad624e5d25369f2922ef9e53e36ee2e36276e3 Mon Sep 17 00:00:00 2001 From: Ali jawwad <33836051+jawwad-ali@users.noreply.github.com> Date: Wed, 29 Jul 2026 03:14:31 +0500 Subject: [PATCH] fix(integrations): render hyphenated /speckit- for Droid (always-slash agent) (#3688) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(integrations): render hyphenated /speckit- for Droid (always-slash agent) DroidIntegration is an always-skills agent: it installs commands as .factory/skills/speckit-/SKILL.md and its build_command_invocation returns the hyphenated /speckit-. But "droid" was missing from every _invocation_style set, so is_slash_skills_agent("droid", True) returned False and both HookExecutor._render_hook_invocation and `specify init` next-steps fell through to the dotted /speckit. form — a command Droid never registers. Add "droid" to ALWAYS_SLASH_AGENTS, matching its always-skills siblings grok/trae/zed/devin (each added there by their own integration PR; droid's #3587 omitted it). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 (1M context) * test(integrations): assert Droid is ALWAYS-slash (disabled case too) Address review: the test only covered ai_skills=True, which would also pass if Droid were miscategorized as CONDITIONAL_SLASH. Add the ai_skills=False assertion — True there is what distinguishes an ALWAYS_SLASH agent from a conditional one. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- src/specify_cli/_invocation_style.py | 2 +- tests/integrations/test_integration_droid.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/specify_cli/_invocation_style.py b/src/specify_cli/_invocation_style.py index 30b42eb08..29018e863 100644 --- a/src/specify_cli/_invocation_style.py +++ b/src/specify_cli/_invocation_style.py @@ -12,7 +12,7 @@ from __future__ import annotations DOLLAR_SKILLS_AGENTS: frozenset[str] = frozenset({"codex", "zcode"}) # Agents that always render /speckit-, regardless of ai_skills. -ALWAYS_SLASH_AGENTS: frozenset[str] = frozenset({"devin", "grok", "trae", "zed"}) +ALWAYS_SLASH_AGENTS: frozenset[str] = frozenset({"devin", "droid", "grok", "trae", "zed"}) # Agents that render /speckit- only when ai_skills is enabled. CONDITIONAL_SLASH_AGENTS: frozenset[str] = frozenset( diff --git a/tests/integrations/test_integration_droid.py b/tests/integrations/test_integration_droid.py index 851a80c5a..09848af93 100644 --- a/tests/integrations/test_integration_droid.py +++ b/tests/integrations/test_integration_droid.py @@ -43,6 +43,20 @@ class TestDroidIntegration(SkillsIntegrationTests): i = get_integration(self.KEY) assert i.multi_install_safe is True + def test_is_slash_skills_agent(self): + """Droid is an always-skills agent whose commands install as + /speckit-, so is_slash_skills_agent must report True — otherwise + hook invocations and the init next-steps panel render the dotted + /speckit. form Droid never registers (mirrors grok/trae/zed/devin).""" + from specify_cli._invocation_style import is_slash_skills_agent + + # True in BOTH the enabled and disabled cases: Droid is *always* slash, + # not conditional. The disabled case is what distinguishes an + # ALWAYS_SLASH agent from a CONDITIONAL_SLASH one (which would be False + # when ai_skills is disabled). + assert is_slash_skills_agent("droid", True) is True + assert is_slash_skills_agent("droid", False) is True + def test_install_url_points_to_factory(self): i = get_integration(self.KEY) url = i.config.get("install_url")