Stage 3: Standard markdown integrations — 19 agents migrated to plugin architecture#2038
Stage 3: Standard markdown integrations — 19 agents migrated to plugin architecture#2038mnriem merged 8 commits intogithub:mainfrom
Conversation
…n architecture
Migrate all standard markdown integrations to self-contained subpackages
under integrations/. Each subclasses MarkdownIntegration with config-only
overrides (~10 lines per __init__.py).
Integrations migrated (19):
claude, qwen, opencode, junie, kilocode, auggie, roo, codebuddy,
qodercli, amp, shai, bob, trae, pi, iflow, kiro-cli, windsurf,
vibe, cursor-agent
Changes:
- Create integrations/<key>/ subpackage with __init__.py and scripts/
(update-context.sh, update-context.ps1) for each integration
- Register all 19 in INTEGRATION_REGISTRY (20 total with copilot)
- MarkdownIntegration.setup() processes templates (replaces {SCRIPT},
{ARGS}, __AGENT__; strips frontmatter blocks; rewrites paths)
- Extract install_scripts() to IntegrationBase; refactor copilot to use it
- Generalize --ai auto-promote from copilot-only to registry-driven:
any integration registered in INTEGRATION_REGISTRY auto-promotes.
Unregistered agents (gemini, tabnine, codex, kimi, agy, generic)
continue through the legacy --ai path unchanged.
- Fix cursor/cursor-agent key mismatch in CommandRegistrar.AGENT_CONFIGS
- Add missing vibe entry to CommandRegistrar.AGENT_CONFIGS
- Update kiro alias test to reflect auto-promote behavior
Testing:
- Per-agent test files (test_integration_<agent>.py) with shared mixin
- 1316 tests passing, 0 failures
- Complete file inventory tests for both sh and ps variants
- Byte-for-byte validated against v0.4.3 release packages (684 files)
There was a problem hiding this comment.
Pull request overview
Migrates 19 “standard markdown” agents to the new plugin-based integrations/ architecture by introducing one subpackage per integration and registering them in INTEGRATION_REGISTRY, with corresponding tests validating registry completeness, registrar alignment, and per-integration file inventories.
Changes:
- Added 19 new
MarkdownIntegrationsubclasses undersrc/specify_cli/integrations/<key>/and registered them inINTEGRATION_REGISTRY. - Refactored script installation into
IntegrationBase.install_scripts()and updatedMarkdownIntegration.setup()to process templates + install per-integration scripts. - Expanded/updated test suite with a reusable markdown integration test mixin and per-integration test modules; updated
--aiauto-promote behavior tests.
Reviewed changes
Copilot reviewed 84 out of 85 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_ai_skills.py | Updates kiro alias test to assert --ai auto-promotes to --integration and files are created. |
| tests/integrations/test_registry.py | Adds registry completeness checks and registrar key alignment assertions (cursor-agent/vibe). |
| tests/integrations/test_integration_windsurf.py | Adds Windsurf integration test via shared markdown mixin. |
| tests/integrations/test_integration_vibe.py | Adds Vibe integration test via shared markdown mixin. |
| tests/integrations/test_integration_trae.py | Adds Trae integration test via shared markdown mixin. |
| tests/integrations/test_integration_shai.py | Adds Shai integration test via shared markdown mixin. |
| tests/integrations/test_integration_roo.py | Adds Roo integration test via shared markdown mixin. |
| tests/integrations/test_integration_qwen.py | Adds Qwen integration test via shared markdown mixin. |
| tests/integrations/test_integration_qodercli.py | Adds Qoder CLI integration test via shared markdown mixin. |
| tests/integrations/test_integration_pi.py | Adds Pi integration test via shared markdown mixin. |
| tests/integrations/test_integration_opencode.py | Adds Opencode integration test via shared markdown mixin. |
| tests/integrations/test_integration_kiro_cli.py | Adds Kiro CLI integration test via shared markdown mixin. |
| tests/integrations/test_integration_kilocode.py | Adds Kilocode integration test via shared markdown mixin. |
| tests/integrations/test_integration_junie.py | Adds Junie integration test via shared markdown mixin. |
| tests/integrations/test_integration_iflow.py | Adds iFlow integration test via shared markdown mixin. |
| tests/integrations/test_integration_cursor_agent.py | Adds Cursor Agent integration test via shared markdown mixin. |
| tests/integrations/test_integration_copilot.py | Adds comprehensive Copilot integration tests (setup, manifests, inventories, uninstall behavior). |
| tests/integrations/test_integration_codebuddy.py | Adds CodeBuddy integration test via shared markdown mixin. |
| tests/integrations/test_integration_claude.py | Adds Claude integration test via shared markdown mixin. |
| tests/integrations/test_integration_bob.py | Adds Bob integration test via shared markdown mixin. |
| tests/integrations/test_integration_base_markdown.py | Introduces shared markdown integration test mixin + inventory expectations. |
| tests/integrations/test_integration_auggie.py | Adds Auggie integration test via shared markdown mixin. |
| tests/integrations/test_integration_amp.py | Adds Amp integration test via shared markdown mixin. |
| src/specify_cli/integrations/windsurf/scripts/update-context.sh | Adds Windsurf wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/windsurf/scripts/update-context.ps1 | Adds Windsurf PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/windsurf/init.py | Adds Windsurf integration config-only subclass. |
| src/specify_cli/integrations/vibe/scripts/update-context.sh | Adds Vibe wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/vibe/scripts/update-context.ps1 | Adds Vibe PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/vibe/init.py | Adds Vibe integration config-only subclass. |
| src/specify_cli/integrations/trae/scripts/update-context.sh | Adds Trae wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/trae/scripts/update-context.ps1 | Adds Trae PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/trae/init.py | Adds Trae integration config-only subclass. |
| src/specify_cli/integrations/shai/scripts/update-context.sh | Adds Shai wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/shai/scripts/update-context.ps1 | Adds Shai PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/shai/init.py | Adds Shai integration config-only subclass. |
| src/specify_cli/integrations/roo/scripts/update-context.sh | Adds Roo wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/roo/scripts/update-context.ps1 | Adds Roo PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/roo/init.py | Adds Roo integration config-only subclass. |
| src/specify_cli/integrations/qwen/scripts/update-context.sh | Adds Qwen wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/qwen/scripts/update-context.ps1 | Adds Qwen PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/qwen/init.py | Adds Qwen integration config-only subclass. |
| src/specify_cli/integrations/qodercli/scripts/update-context.sh | Adds Qoder CLI wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/qodercli/scripts/update-context.ps1 | Adds Qoder CLI PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/qodercli/init.py | Adds Qoder CLI integration config-only subclass. |
| src/specify_cli/integrations/pi/scripts/update-context.sh | Adds Pi wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/pi/scripts/update-context.ps1 | Adds Pi PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/pi/init.py | Adds Pi integration config-only subclass. |
| src/specify_cli/integrations/opencode/scripts/update-context.sh | Adds Opencode wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/opencode/scripts/update-context.ps1 | Adds Opencode PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/opencode/init.py | Adds Opencode integration config-only subclass. |
| src/specify_cli/integrations/kiro-cli/scripts/update-context.sh | Adds Kiro CLI wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/kiro-cli/scripts/update-context.ps1 | Adds Kiro CLI PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/kiro-cli/init.py | Adds Kiro CLI integration config-only subclass. |
| src/specify_cli/integrations/kilocode/scripts/update-context.sh | Adds Kilocode wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/kilocode/scripts/update-context.ps1 | Adds Kilocode PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/kilocode/init.py | Adds Kilocode integration config-only subclass. |
| src/specify_cli/integrations/junie/scripts/update-context.sh | Adds Junie wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/junie/scripts/update-context.ps1 | Adds Junie PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/junie/init.py | Adds Junie integration config-only subclass. |
| src/specify_cli/integrations/iflow/scripts/update-context.sh | Adds iFlow wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/iflow/scripts/update-context.ps1 | Adds iFlow PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/iflow/init.py | Adds iFlow integration config-only subclass. |
| src/specify_cli/integrations/cursor-agent/scripts/update-context.sh | Adds Cursor Agent wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/cursor-agent/scripts/update-context.ps1 | Adds Cursor Agent PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/cursor-agent/init.py | Adds Cursor Agent integration config-only subclass. |
| src/specify_cli/integrations/copilot/init.py | Refactors Copilot integration to use install_scripts() helper. |
| src/specify_cli/integrations/codebuddy/scripts/update-context.sh | Adds CodeBuddy wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/codebuddy/scripts/update-context.ps1 | Adds CodeBuddy PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/codebuddy/init.py | Adds CodeBuddy integration config-only subclass. |
| src/specify_cli/integrations/claude/scripts/update-context.sh | Adds Claude wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/claude/scripts/update-context.ps1 | Adds Claude PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/claude/init.py | Adds Claude integration config-only subclass. |
| src/specify_cli/integrations/bob/scripts/update-context.sh | Adds Bob wrapper script delegating to shared update-agent-context. |
| src/specify_cli/integrations/bob/scripts/update-context.ps1 | Adds Bob PowerShell wrapper delegating to shared update-agent-context. |
| src/specify_cli/integrations/bob/init.py | Adds Bob integration config-only subclass. |
| src/specify_cli/integrations/base.py | Adds install_scripts() and implements MarkdownIntegration.setup() template processing + script install. |
| src/specify_cli/integrations/init.py | Registers all Stage 3 integrations in INTEGRATION_REGISTRY (including hyphenated keys via importlib). |
| src/specify_cli/agents.py | Fixes cursor key mismatch (cursor-agent) and adds missing vibe registrar entry. |
| src/specify_cli/init.py | Generalizes --ai auto-promote behavior to any registered integration (not just copilot). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix repo root fallback in all 20 update-context.sh scripts: walk up from script location to find .specify/ instead of falling back to pwd - Fix repo root fallback in all 20 update-context.ps1 scripts: walk up from script location to find .specify/ instead of falling back to $PWD - Add assertions to test_setup_writes_to_correct_directory: verify expected_dir exists and all command files reside under it
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 86 out of 87 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/specify_cli/integrations/copilot/scripts/update-context.ps1
Outdated
Show resolved
Hide resolved
src/specify_cli/integrations/windsurf/scripts/update-context.sh
Outdated
Show resolved
Hide resolved
In monorepos the git toplevel may differ from the project root that contains .specify/. The previous fix still preferred git rev-parse over the walk-up result. Bash scripts (20): prefer the discovered _root when it contains .specify/; only accept git root if it also contains .specify/. PowerShell scripts (20): validate git root contains .specify/ before using it; fall back to walking up from script directory otherwise.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 86 out of 87 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
With $ErrorActionPreference = 'Stop', an unguarded git rev-parse throws a terminating CommandNotFoundException when git is not installed, preventing the .specify walk-up fallback from running. Wrap the git call in try/catch across all 20 update-context.ps1 scripts so the fallback works reliably without git.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 86 out of 87 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Rename kiro-cli → kiro_cli and cursor-agent → cursor_agent so the packages can be imported with normal Python syntax instead of importlib. The user-facing integration key (IntegrationBase.key) stays hyphenated to match the actual CLI tool / binary name. Also reorganize _register_builtins(): imports and registrations are now grouped alphabetically with clear section comments.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 86 out of 87 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace the duplicated regex-based path rewriting in MarkdownIntegration.process_template() with a call to the shared CommandRegistrar._rewrite_project_relative_paths() implementation. This ensures extension-local paths are preserved and boundary rules stay consistent across the codebase.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 86 out of 87 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
src/specify_cli/integrations/windsurf/scripts/update-context.ps1:1
- The wrapper invokes the shared PowerShell script but doesn’t explicitly propagate its exit code, and it builds the path via string interpolation with
/. For better Windows robustness and consistent failure signaling, build the path withJoin-Path(or equivalent) and explicitlyexitwith the invoked script’s status so CI/automation can reliably detect failures.
tests/integrations/test_integration_base_markdown.py:1 os.access(..., os.X_OK)is platform-dependent (notably on Windows, where executable bits may not behave like POSIX). If the test suite runs on Windows, this assertion is likely to fail even if the script was installed correctly. Consider making this assertion conditional on a POSIX platform, or assert a different invariant cross-platform (e.g., file exists + shebang present) and keep the executable-bit check only where it’s meaningful.
tests/test_ai_skills.py:1- This test asserts on a specific CLI tip substring (
--integration kiro-cli) which may change with formatting (Rich styling), wording, or localization, making it more brittle than necessary. Since the next assertion already validates the effective behavior (files created under.kiro/prompts), consider either removing the output assertion or relaxing it to a less presentation-dependent check.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Rename CommandRegistrar._rewrite_project_relative_paths() to rewrite_project_relative_paths() (drop leading underscore) so integrations can call it without reaching into a private method across subsystem boundaries. Addresses PR review feedback: github#2038 (comment)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 87 out of 88 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Parametrize across ALL_INTEGRATION_KEYS instead of only checking cursor-agent and vibe. Keeps a separate negative test for the stale 'cursor' shorthand. Addresses PR review feedback: github#2038 (comment)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 87 out of 88 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Migrate all 19 standard markdown integrations to self-contained subpackages under
integrations/. Each subclassesMarkdownIntegrationwith config-only overrides (~10 lines per__init__.py).Integrations migrated (19)
claude, qwen, opencode, junie, kilocode, auggie, roo, codebuddy, qodercli, amp, shai, bob, trae, pi, iflow, kiro-cli, windsurf, vibe, cursor-agent
Changes
integrations/<key>/subpackage with__init__.pyandscripts/(update-context.sh,update-context.ps1) for each integrationINTEGRATION_REGISTRY(20 total with copilot)MarkdownIntegration.setup()processes templates (replaces{SCRIPT},{ARGS},__AGENT__; strips frontmatter blocks; rewrites paths)install_scripts()toIntegrationBase; refactor copilot to use it--aiauto-promote from copilot-only to registry-driven: any integration registered inINTEGRATION_REGISTRYauto-promotes. Unregistered agents (gemini, tabnine, codex, kimi, agy, generic) continue through the legacy--aipath unchanged.cursor/cursor-agentkey mismatch inCommandRegistrar.AGENT_CONFIGSvibeentry toCommandRegistrar.AGENT_CONFIGSTesting
test_integration_<agent>.py) with shared mixin (test_integration_base_markdown.py)--script shand--script psvariantsWheel size
214 KB — contains all 20 integrations, templates, scripts, and the CLI. Compared to 3+ MB of release ZIPs (52 files), this is a ~93% reduction.
Part of #1924