From 0e787b336688b7aa8ec6234d22e0bd6698d978ae Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Thu, 23 Jul 2026 23:02:47 +0500 Subject: [PATCH 1/2] fix(integrations): render hyphenated /speckit- for Droid (always-slash agent) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/specify_cli/_invocation_style.py | 2 +- tests/integrations/test_integration_droid.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/specify_cli/_invocation_style.py b/src/specify_cli/_invocation_style.py index 2b5f189488..5390010142 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", "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 851a80c5aa..2907b13abd 100644 --- a/tests/integrations/test_integration_droid.py +++ b/tests/integrations/test_integration_droid.py @@ -43,6 +43,15 @@ def test_multi_install_safe_is_true(self): 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 + + assert is_slash_skills_agent("droid", True) is True + def test_install_url_points_to_factory(self): i = get_integration(self.KEY) url = i.config.get("install_url") From 30142444c758a7cb98c0b7473af377cfe69d3ccb Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Fri, 24 Jul 2026 12:49:03 +0500 Subject: [PATCH 2/2] test(integrations): assert Droid is ALWAYS-slash (disabled case too) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- tests/integrations/test_integration_droid.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/integrations/test_integration_droid.py b/tests/integrations/test_integration_droid.py index 2907b13abd..09848af930 100644 --- a/tests/integrations/test_integration_droid.py +++ b/tests/integrations/test_integration_droid.py @@ -50,7 +50,12 @@ def test_is_slash_skills_agent(self): /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)