* 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