diff --git a/docs/reference/integrations.md b/docs/reference/integrations.md index a790389774..8bb248a3e5 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` | | @@ -184,6 +185,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 33c6ddd931..c80b7e9755 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 07d3cc1a6d..cf78daaf74 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 @@ -94,6 +95,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 0000000000..b49140b1f8 --- /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 0000000000..b42d2fbf9d --- /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 7582bd6717..0110e19ec7 100644 --- a/tests/integrations/test_registry.py +++ b/tests/integrations/test_registry.py @@ -23,7 +23,7 @@ # 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