From 0a126256e0a35c9c0edcb2acf53065f1a7a2aa6e Mon Sep 17 00:00:00 2001 From: Ali jawwad <33836051+jawwad-ali@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:01:00 +0500 Subject: [PATCH] feat: add Firebender integration (Android Studio / IntelliJ) (#3077) * feat: add Firebender integration (Android Studio / IntelliJ) Firebender (https://firebender.com/) is an AI coding agent for Android Studio and IntelliJ. It reads project-local custom slash commands from .firebender/commands/*.mdc and project rules from .firebender/rules/*.mdc. Add a FirebenderIntegration (MarkdownIntegration) that installs the speckit command templates as .mdc command files and writes the managed context section into .firebender/rules/specify-rules.mdc. command_filename is overridden so init-time commands also use the .mdc extension Firebender requires. Register it in the integration registry, add the catalog entry and docs row, and add an integration test covering the .mdc command output. Closes #1548 Co-Authored-By: Claude Opus 4.8 (1M context) * feat: address review - bump catalog updated_at and list firebender as multi-install safe Bump the catalog top-level updated_at to reflect the new entry, and add firebender (with its .firebender/commands + .firebender/rules/specify-rules.mdc isolation paths) to the 'currently declared multi-install safe integrations' table in the docs. Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- docs/reference/integrations.md | 2 + integrations/catalog.json | 11 ++++- src/specify_cli/integrations/__init__.py | 2 + .../integrations/firebender/__init__.py | 33 ++++++++++++++ .../test_integration_firebender.py | 45 +++++++++++++++++++ tests/integrations/test_registry.py | 2 +- 6 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/specify_cli/integrations/firebender/__init__.py create mode 100644 tests/integrations/test_integration_firebender.py diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md index 1fe4a5364..a04e9db1d 100644 --- a/docs/reference/integrations.md +++ b/docs/reference/integrations.md @@ -15,6 +15,7 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify | [Codex CLI](https://github.com/openai/codex) | `codex` | Skills-based integration; installs skills into `.agents/skills` and invokes them as `$speckit-` | | [Cursor](https://cursor.sh/) | `cursor-agent` | | | [Devin for Terminal](https://cli.devin.ai/docs) | `devin` | Skills-based integration; installs skills into `.devin/skills/` and invokes them as `/speckit-` | +| [Firebender](https://firebender.com/) | `firebender` | IDE-based agent for Android Studio / IntelliJ | | [Forge](https://forgecode.dev/) | `forge` | | | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | | | [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | | @@ -185,6 +186,7 @@ The currently declared multi-install safe integrations are: | `codebuddy` | `.codebuddy/commands`, `CODEBUDDY.md` | | `codex` | `.agents/skills`, `AGENTS.md` | | `cursor-agent` | `.cursor/skills`, `.cursor/rules/specify-rules.mdc` | +| `firebender` | `.firebender/commands`, `.firebender/rules/specify-rules.mdc` | | `gemini` | `.gemini/commands`, `GEMINI.md` | | `iflow` | `.iflow/commands`, `IFLOW.md` | | `junie` | `.junie/commands`, `.junie/AGENTS.md` | diff --git a/integrations/catalog.json b/integrations/catalog.json index f89af37d5..5e6862ec1 100644 --- a/integrations/catalog.json +++ b/integrations/catalog.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "updated_at": "2026-06-02T00:00:00Z", + "updated_at": "2026-06-22T00:00:00Z", "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/integrations/catalog.json", "integrations": { "claude": { @@ -102,6 +102,15 @@ "repository": "https://github.com/github/spec-kit", "tags": ["cli"] }, + "firebender": { + "id": "firebender", + "name": "Firebender", + "version": "1.0.0", + "description": "Firebender IDE integration for Android Studio / IntelliJ", + "author": "spec-kit-core", + "repository": "https://github.com/github/spec-kit", + "tags": ["ide"] + }, "forge": { "id": "forge", "name": "Forge", diff --git a/src/specify_cli/integrations/__init__.py b/src/specify_cli/integrations/__init__.py index a81d70554..fe09468a7 100644 --- a/src/specify_cli/integrations/__init__.py +++ b/src/specify_cli/integrations/__init__.py @@ -58,6 +58,7 @@ def _register_builtins() -> None: from .copilot import CopilotIntegration from .cursor_agent import CursorAgentIntegration from .devin import DevinIntegration + from .firebender import FirebenderIntegration from .forge import ForgeIntegration from .gemini import GeminiIntegration from .generic import GenericIntegration @@ -95,6 +96,7 @@ def _register_builtins() -> None: _register(CopilotIntegration()) _register(CursorAgentIntegration()) _register(DevinIntegration()) + _register(FirebenderIntegration()) _register(ForgeIntegration()) _register(GeminiIntegration()) _register(GenericIntegration()) diff --git a/src/specify_cli/integrations/firebender/__init__.py b/src/specify_cli/integrations/firebender/__init__.py new file mode 100644 index 000000000..b49140b1f --- /dev/null +++ b/src/specify_cli/integrations/firebender/__init__.py @@ -0,0 +1,33 @@ +"""Firebender IDE integration. + +Firebender (https://firebender.com/) is an AI coding agent for Android Studio +and IntelliJ. It reads project-local custom slash commands from +``.firebender/commands/*.mdc`` and project rules from ``.firebender/rules/*.mdc``, +so Spec Kit installs its command templates as ``.mdc`` command files and writes +the managed context section into a ``.firebender/rules/`` rule file. +""" + +from ..base import MarkdownIntegration + + +class FirebenderIntegration(MarkdownIntegration): + key = "firebender" + config = { + "name": "Firebender", + "folder": ".firebender/", + "commands_subdir": "commands", + "install_url": "https://firebender.com/", + "requires_cli": False, + } + registrar_config = { + "dir": ".firebender/commands", + "format": "markdown", + "args": "$ARGUMENTS", + "extension": ".mdc", + } + context_file = ".firebender/rules/specify-rules.mdc" + multi_install_safe = True + + def command_filename(self, template_name: str) -> str: + """Firebender reads custom slash commands from ``.firebender/commands/*.mdc``.""" + return f"speckit.{template_name}.mdc" diff --git a/tests/integrations/test_integration_firebender.py b/tests/integrations/test_integration_firebender.py new file mode 100644 index 000000000..b42d2fbf9 --- /dev/null +++ b/tests/integrations/test_integration_firebender.py @@ -0,0 +1,45 @@ +"""Tests for FirebenderIntegration.""" + +from specify_cli.integrations import get_integration +from specify_cli.integrations.manifest import IntegrationManifest + +from .test_integration_base_markdown import MarkdownIntegrationTests + + +class TestFirebenderIntegration(MarkdownIntegrationTests): + KEY = "firebender" + FOLDER = ".firebender/" + COMMANDS_SUBDIR = "commands" + REGISTRAR_DIR = ".firebender/commands" + CONTEXT_FILE = ".firebender/rules/specify-rules.mdc" + + # Firebender reads custom slash commands from ``.firebender/commands/*.mdc``, + # so this integration uses the ``.mdc`` extension instead of the ``.md`` + # default the base mixin assumes. Override the two extension-specific tests. + def test_registrar_config(self): + i = get_integration(self.KEY) + assert i.registrar_config["dir"] == self.REGISTRAR_DIR + assert i.registrar_config["format"] == "markdown" + assert i.registrar_config["args"] == "$ARGUMENTS" + assert i.registrar_config["extension"] == ".mdc" + + def test_setup_creates_files(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + assert len(created) > 0 + cmd_files = [f for f in created if "scripts" not in f.parts] + for f in cmd_files: + assert f.exists() + assert f.name.startswith("speckit.") + assert f.name.endswith(".mdc") + + def _expected_files(self, script_variant: str) -> list[str]: + # Firebender emits ``.mdc`` command files, so remap the base mixin's + # ``.md`` expectations for files under this integration's command dir. + cmd_dir = get_integration(self.KEY).registrar_config["dir"] + prefix = cmd_dir + "/" + return sorted( + f[:-3] + ".mdc" if f.startswith(prefix) and f.endswith(".md") else f + for f in super()._expected_files(script_variant) + ) diff --git a/tests/integrations/test_registry.py b/tests/integrations/test_registry.py index 7582bd671..0110e19ec 100644 --- a/tests/integrations/test_registry.py +++ b/tests/integrations/test_registry.py @@ -23,7 +23,7 @@ ALL_INTEGRATION_KEYS = [ # Stage 3 — standard markdown integrations "claude", "qwen", "opencode", "junie", "kilocode", "auggie", "roo", "rovodev", "codebuddy", "qodercli", "amp", "shai", "bob", "trae", - "pi", "iflow", "kiro-cli", "windsurf", "vibe", "cursor-agent", + "pi", "iflow", "kiro-cli", "windsurf", "vibe", "cursor-agent", "firebender", # Stage 4 — TOML integrations "gemini", "tabnine", # Stage 5 — skills, generic & option-driven integrations