mirror of
https://github.com/github/spec-kit.git
synced 2026-08-15 14:02:04 +02:00
* feat(integrations): add Devin for Terminal skills-based integration - Register DevinIntegration as a SkillsIntegration with .devin/skills/ layout - Add catalog entry, docs row, and supported-agents listing - Display /speckit-<command> hyphen syntax in init "Next Steps" panel (matches Claude/Cursor/Copilot skills mode, since Devin invokes skills by directory name) Closes #2346 * fix(devin): implement -p non-interactive dispatch; clarify skills comment Addresses Copilot review on PR #2364: - Override build_exec_args() in DevinIntegration to emit 'devin -p <prompt> [--model X]' for non-interactive text dispatch (verified Devin CLI supports -p / --print). Returns None when output_json=True since Devin has no structured-output flag, so CommandStep workflows that require JSON cleanly raise NotImplementedError instead of crashing on an unknown CLI flag. requires_cli=True is retained for tool detection. - Extend the skills-integrations enumeration comment in specify_cli/__init__.py to include copilot and devin so the comment matches the code below it. * fix(devin): always return exec args; document plain-text stdout Addresses third Copilot review comment on PR #2364. Returning None from build_exec_args() when output_json=True incorrectly used the codebase's IDE-only sentinel: workflow CommandStep checks 'impl.build_exec_args("test") is None' to detect non-dispatchable integrations (test_workflows.py exercises this with WindsurfIntegration). The previous implementation made Devin appear non-dispatchable to all command steps even though it runs fine via 'devin -p'. Always return the args list. When output_json is requested, Devin is still dispatched and returns plain-text stdout instead of structured JSON; the docstring documents this explicitly. * docs(devin): include claude in skills-integrations enumeration comment Addresses Copilot review on PR #2364: the comment listing skills integrations omitted Claude, which is also a SkillsIntegration subclass. Updated to keep the comment accurate for future readers. * test(devin): add build_exec_args regression tests; bump catalog updated_at Addresses Copilot review on PR #2364, per @mnriem's request to 'address the Copilot feedback, especially the testing ask': - tests/integrations/test_integration_devin.py: add TestDevinBuildExecArgs with three regression assertions: * build_exec_args returns args (not the None IDE-only sentinel) * --output-format is never emitted, regardless of output_json * --model flag is passed through correctly - integrations/catalog.json: bump top-level updated_at to reflect the Devin entry addition so downstream catalog consumers can detect the change reliably.
113 lines
3.9 KiB
Python
113 lines
3.9 KiB
Python
"""Integration registry for AI coding assistants.
|
|
|
|
Each integration is a self-contained subpackage that handles setup/teardown
|
|
for a specific AI assistant (Copilot, Claude, Gemini, etc.).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from .base import IntegrationBase
|
|
|
|
# Maps integration key → IntegrationBase instance.
|
|
# Populated by later stages as integrations are migrated.
|
|
INTEGRATION_REGISTRY: dict[str, IntegrationBase] = {}
|
|
|
|
|
|
def _register(integration: IntegrationBase) -> None:
|
|
"""Register an integration instance in the global registry.
|
|
|
|
Raises ``ValueError`` for falsy keys and ``KeyError`` for duplicates.
|
|
"""
|
|
key = integration.key
|
|
if not key:
|
|
raise ValueError("Cannot register integration with an empty key.")
|
|
if key in INTEGRATION_REGISTRY:
|
|
raise KeyError(f"Integration with key {key!r} is already registered.")
|
|
INTEGRATION_REGISTRY[key] = integration
|
|
|
|
|
|
def get_integration(key: str) -> IntegrationBase | None:
|
|
"""Return the integration for *key*, or ``None`` if not registered."""
|
|
return INTEGRATION_REGISTRY.get(key)
|
|
|
|
|
|
# -- Register built-in integrations --------------------------------------
|
|
|
|
|
|
def _register_builtins() -> None:
|
|
"""Register all built-in integrations.
|
|
|
|
Package directories use Python-safe identifiers (e.g. ``kiro_cli``,
|
|
``cursor_agent``). The user-facing integration key stored in
|
|
``IntegrationBase.key`` stays hyphenated (``"kiro-cli"``,
|
|
``"cursor-agent"``) to match the actual CLI tool / binary name that
|
|
users install and invoke.
|
|
"""
|
|
# -- Imports (alphabetical) -------------------------------------------
|
|
from .agy import AgyIntegration
|
|
from .amp import AmpIntegration
|
|
from .auggie import AuggieIntegration
|
|
from .bob import BobIntegration
|
|
from .claude import ClaudeIntegration
|
|
from .codebuddy import CodebuddyIntegration
|
|
from .codex import CodexIntegration
|
|
from .copilot import CopilotIntegration
|
|
from .cursor_agent import CursorAgentIntegration
|
|
from .devin import DevinIntegration
|
|
from .forge import ForgeIntegration
|
|
from .gemini import GeminiIntegration
|
|
from .generic import GenericIntegration
|
|
from .goose import GooseIntegration
|
|
from .iflow import IflowIntegration
|
|
from .junie import JunieIntegration
|
|
from .kilocode import KilocodeIntegration
|
|
from .kimi import KimiIntegration
|
|
from .kiro_cli import KiroCliIntegration
|
|
from .opencode import OpencodeIntegration
|
|
from .pi import PiIntegration
|
|
from .qodercli import QodercliIntegration
|
|
from .qwen import QwenIntegration
|
|
from .roo import RooIntegration
|
|
from .shai import ShaiIntegration
|
|
from .tabnine import TabnineIntegration
|
|
from .trae import TraeIntegration
|
|
from .vibe import VibeIntegration
|
|
from .windsurf import WindsurfIntegration
|
|
|
|
# -- Registration (alphabetical) --------------------------------------
|
|
_register(AgyIntegration())
|
|
_register(AmpIntegration())
|
|
_register(AuggieIntegration())
|
|
_register(BobIntegration())
|
|
_register(ClaudeIntegration())
|
|
_register(CodebuddyIntegration())
|
|
_register(CodexIntegration())
|
|
_register(CopilotIntegration())
|
|
_register(CursorAgentIntegration())
|
|
_register(DevinIntegration())
|
|
_register(ForgeIntegration())
|
|
_register(GeminiIntegration())
|
|
_register(GenericIntegration())
|
|
_register(GooseIntegration())
|
|
_register(IflowIntegration())
|
|
_register(JunieIntegration())
|
|
_register(KilocodeIntegration())
|
|
_register(KimiIntegration())
|
|
_register(KiroCliIntegration())
|
|
_register(OpencodeIntegration())
|
|
_register(PiIntegration())
|
|
_register(QodercliIntegration())
|
|
_register(QwenIntegration())
|
|
_register(RooIntegration())
|
|
_register(ShaiIntegration())
|
|
_register(TabnineIntegration())
|
|
_register(TraeIntegration())
|
|
_register(VibeIntegration())
|
|
_register(WindsurfIntegration())
|
|
|
|
|
|
_register_builtins()
|