mirror of
https://github.com/github/spec-kit.git
synced 2026-08-15 14:02:04 +02:00
* Stage 5: Skills, Generic & Option-Driven Integrations (#1924) Add SkillsIntegration base class and migrate codex, kimi, agy, and generic to the integration system. Integrations: - SkillsIntegration(IntegrationBase) in base.py — creates speckit-<name>/SKILL.md layout matching release ZIP output byte-for-byte - CodexIntegration — .agents/skills/, --skills default=True - KimiIntegration — .kimi/skills/, --skills + --migrate-legacy options, dotted→hyphenated skill directory migration - AgyIntegration — .agent/skills/, skills-only (commands deprecated v1.20.5) - GenericIntegration — user-specified --commands-dir, MarkdownIntegration - All four have update-context.sh/.ps1 scripts - All four registered in INTEGRATION_REGISTRY CLI changes: - --ai <agent> auto-promotes to integration path for all registered agents - Interactive agent selection also auto-promotes (bug fix) - --ai-skills and --ai-commands-dir show deprecation notices on integration path - Next-steps display shows correct skill invocation syntax for skills integrations - agy added to CommandRegistrar.AGENT_CONFIGS Tests: - test_integration_base_skills.py — reusable mixin with setup, frontmatter, directory structure, scripts, CLI auto-promote, and complete file inventory (sh+ps) tests - Per-agent test files: test_integration_{codex,kimi,agy,generic}.py - Kimi legacy migration tests, generic --commands-dir validation - Registry updated with Stage 5 keys - Removed 9 dead-mock tests, moved 4 integration tests to proper locations - Fixed all bare project-name tests to use tmp_path - Fixed 6 pre-existing ANSI escape code test failures in test_extensions.py and test_presets.py 1524 tests pass, 0 failures. * fix: remove unused variable flagged by ruff (F841) * fix: address PR review — integration-type-aware deprecation messages and early generic validation - --ai-skills deprecation message now distinguishes SkillsIntegration ("skills are the default") from command-based integrations ("has no effect") - --ai-commands-dir validation for generic runs even when auto-promoted, giving clear CLI error instead of late ValueError from setup() - Resolves review comments from #2052 * fix: address PR review round 2 - Remove unused SKILL_DESCRIPTIONS dict from base.py (dead code after switching to template descriptions for ZIP parity) - Narrow YAML parse catch from Exception to yaml.YAMLError - Remove unused shutil import from test_integration_kimi.py - Remove unused _REGISTRAR_EXEMPT class attr from test_registry.py - Reword --ai-commands-dir deprecation to be actionable - Update generic validation error to mention both --ai and --integration * fix: address PR review round 3 - Clarify parsed_options forwarding is intentional (all options passed, integrations decide what to use) - Extract _strip_ansi() helper in test_extensions.py and test_presets.py - Remove unused pytest import (test_cli.py), unused locals (test_integration_base_skills.py) - Reword --ai-commands-dir deprecation to be actionable without referencing the not-yet-implemented --integration-options * fix: address PR review round 4 - Reorder kimi migration: run super().setup() first so hyphenated targets exist, then migrate dotted dirs (prevents user content loss) - Move _strip_ansi() to shared tests/conftest.py, import from there in test_extensions.py, test_presets.py, test_ai_skills.py - Remove now-unused re imports from all three test files * fix: address PR review round 5 - Use write_bytes() for LF-only newlines (no CRLF on Windows) - Add --integration-options CLI parameter — raw string passed through to the integration via opts['raw_options']; the integration owns parsing of its own options - GenericIntegration.setup() reads --commands-dir from raw_options when not in parsed_options (supports --integration-options="...") - Skip early --ai-commands-dir validation when --integration-options is provided (integration validates in its own setup()) - Remove parse_integration_options from core — integrations parse their own options * fix: address PR review round 6 - GenericIntegration is now stateless: removed self._commands_dir instance state, overrides setup() directly to compute destination from parsed_options/raw_options on the stack - commands_dest() raises by design (stateless singleton) - _quote() in SkillsIntegration now escapes backslashes and double quotes to produce valid YAML even with special characters * fix: address PR review round 7 - Support --commands-dir=value form in raw_options parsing (not just --commands-dir value with space separator) - Normalize CRLF to LF in write_file_and_record() before encoding - Persist ai_skills=True in init-options.json when using a SkillsIntegration, so extensions/presets emit SKILL.md overrides correctly even without explicit --ai-skills flag
106 lines
3.7 KiB
Python
106 lines
3.7 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 .codex import CodexIntegration
|
|
from .codebuddy import CodebuddyIntegration
|
|
from .copilot import CopilotIntegration
|
|
from .cursor_agent import CursorAgentIntegration
|
|
from .gemini import GeminiIntegration
|
|
from .generic import GenericIntegration
|
|
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(CodexIntegration())
|
|
_register(CodebuddyIntegration())
|
|
_register(CopilotIntegration())
|
|
_register(CursorAgentIntegration())
|
|
_register(GeminiIntegration())
|
|
_register(GenericIntegration())
|
|
_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()
|