- Remove hard-coded command count (9) and command set from test_directory_structure
- Use forge.list_command_templates() to derive expected commands
- Test now auto-syncs when core command templates are added/removed
- Prevents test breakage when template set changes
- All 11 tests pass
- Add replacement of $ARGUMENTS to {{parameters}} after template processing
- Use arg_placeholder from config (Copilot's cleaner approach)
- Remove unused 'import re' from _apply_forge_transformations()
- Enhance tests to verify $ARGUMENTS replacement works correctly
- All 11 tests pass
Fixes template processing to ensure Forge receives user-supplied parameters correctly.
- Create ForgeIntegration class with custom processing for {{parameters}}, handoffs stripping, and name injection
- Add update-context scripts (bash and PowerShell) for Forge
- Register Forge in integration registry
- Update AGENTS.md with Forge documentation and special processing requirements section
- Add comprehensive test suite (11 tests, all passing)
Closes migration from release packaging to Python-based scaffolding for Forge agent.
* Stage 3: Standard markdown integrations — 19 agents migrated to plugin 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)
* Address PR review: fix repo root detection and no-op test
- 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
* Fix REPO_ROOT priority: prefer .specify walk-up over git root
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.
* Guard git call with try/catch in PowerShell scripts
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.
* Rename hyphenated package dirs to valid Python identifiers
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.
* Reuse CommandRegistrar path rewriting in process_template()
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.
* Promote _rewrite_project_relative_paths to public API
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:
https://github.com/github/spec-kit/pull/2038#discussion_r3022105627
* Broaden TestRegistrarKeyAlignment to cover all integration keys
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:
https://github.com/github/spec-kit/pull/2038#discussion_r3022269032
* feat: Stage 2a — CopilotIntegration with shared template primitives
- base.py: added granular primitives (shared_commands_dir,
shared_templates_dir, list_command_templates, command_filename,
commands_dest, copy_command_to_directory, record_file_in_manifest,
write_file_and_record, process_template)
- CopilotIntegration: uses primitives to produce .agent.md commands,
companion .prompt.md files, and .vscode/settings.json
- Verified byte-for-byte parity with old release script output
- Copilot auto-registered in INTEGRATION_REGISTRY
- 70 tests (22 new: base primitives + copilot integration)
Part of #1924
* feat: Stage 2b — --integration flag, routing, agent.json, shared infra
- Added --integration flag to init() (mutually exclusive with --ai)
- --ai copilot auto-promotes to integration path with migration nudge
- Integration setup writes .specify/agent.json with integration key
- _install_shared_infra() copies scripts and templates to .specify/
- init-options.json records 'integration' key when used
- 4 new CLI tests: mutual exclusivity, unknown rejection, copilot
end-to-end, auto-promote (74 total integration tests)
Part of #1924
* feat: Stage 2 completion — integration scripts, integration.json, shared manifest
- Added copilot/scripts/update-context.sh and .ps1 (thin wrappers
that delegate to the shared update-agent-context script)
- CopilotIntegration.setup() installs integration scripts to
.specify/integrations/copilot/scripts/
- Renamed agent.json → integration.json with script paths
- _install_shared_infra() now tracks files in
integration-shared.manifest.json
- Updated tests: scripts installed, integration.json has script paths,
shared manifest recorded (74 tests)
Part of #1924
* refactor: rename shared manifest to speckit.manifest.json
Cleaner naming — the shared infrastructure (scripts, templates)
belongs to spec-kit itself, not to any specific integration.
* fix: copilot update-context scripts reflect target architecture
Scripts now source shared functions (via SPECKIT_SOURCE_ONLY=1) and
call update_agent_file directly with .github/copilot-instructions.md,
rather than delegating back to the shared case statement.
* fix: simplify copilot scripts — dispatcher sources common functions
Integration scripts now contain only copilot-specific logic (target
path + agent name). The dispatcher is responsible for sourcing shared
functions before calling the integration script.
* fix: copilot update-context scripts are self-contained implementations
These scripts ARE the implementation — the dispatcher calls them.
They source common.sh + update-agent-context functions, gather
feature/plan data, then call update_agent_file with the copilot
target path (.github/copilot-instructions.md).
* docs: add Stage 7 activation note to copilot update-context scripts
* test: add complete file inventory test for copilot integration
Validates every single file (37 total) produced by
specify init --integration copilot --script sh --no-git.
* test: add PowerShell file inventory test for copilot integration
Validates all 37 files produced by --script ps variant, including
.specify/scripts/powershell/ instead of bash.
* refactor: split test_integrations.py into tests/integrations/ directory
- test_base.py: IntegrationOption, IntegrationBase, MarkdownIntegration, primitives
- test_manifest.py: IntegrationManifest, path traversal, persistence, validation
- test_registry.py: INTEGRATION_REGISTRY
- test_copilot.py: CopilotIntegration unit tests
- test_cli.py: --integration flag, auto-promote, file inventories (sh + ps)
- conftest.py: shared StubIntegration helper
76 integration tests + 48 consistency tests = 124 total, all passing.
* refactor: move file inventory tests from test_cli to test_copilot
File inventories are copilot-specific. test_cli.py now only tests
CLI flag mechanics (mutual exclusivity, unknown rejection, auto-promote).
* fix: skip JSONC merge to preserve user settings, fix docstring
- _merge_vscode_settings() now returns early (skips merge) when
existing settings.json can't be parsed (e.g. JSONC with comments),
instead of overwriting with empty settings
- Updated _install_shared_infra() docstring to match implementation
(scripts + templates, speckit.manifest.json)
* fix: warn user when JSONC settings merge is skipped
* fix: show template content when JSONC merge is skipped
User now sees the exact settings they should add manually.
* fix: document process_template requirement, merge scripts without rmtree
- base.py setup() docstring now explicitly states raw copy behavior
and directs to CopilotIntegration for process_template example
- _install_shared_infra() uses merge/overwrite instead of rmtree to
preserve user-added files under .specify/scripts/
* fix: don't overwrite pre-existing shared scripts or templates
Only write files that don't already exist — preserves any user
modifications to shared scripts (common.sh etc.) and templates.
* fix: warn user about skipped pre-existing shared files
Lists all shared scripts and templates that were not copied because
they already existed in the project.
* test: add test for shared infra skip behavior on pre-existing files
Verifies that _install_shared_infra() preserves user-modified scripts
and templates while still installing missing ones.
* fix: address review — containment check, deterministic prompts, manifest accuracy
- CopilotIntegration.setup() adds dest containment check (relative_to)
- Companion prompts generated from templates list, not directory glob
- _install_shared_infra() only records files actually copied (not pre-existing)
- VS Code settings tests made unconditional (assert template exists)
- Inventory tests use .as_posix() for cross-platform paths
* fix: correct PS1 function names, document SPECKIT_SOURCE_ONLY prerequisite
- Fixed Get-FeaturePaths → Get-FeaturePathsEnv, Read-PlanData → Parse-PlanData
- Documented that shared scripts must guard Main with SPECKIT_SOURCE_ONLY
before these integration scripts can be activated (Stage 7)
* fix: add dict type check for settings merge, simplify PS1 to subprocess
- _merge_vscode_settings() skips merge with warning if parsed JSON
is not a dict (array, null, etc.)
- PS1 update-context.ps1 uses & invocation instead of dot-sourcing
since the shared script runs Main unconditionally
* fix: skip-write on no-op merge, bash subprocess, dynamic integration list
- _merge_vscode_settings() only writes when keys were actually added
- update-context.sh uses exec subprocess like PS1 version
- Unknown integration error lists available integrations dynamically
* fix: align path rewriting with release script, add .specify/.specify/ fix
Path rewrite regex matches the release script's rewrite_paths()
exactly (verified byte-identical output). Added .specify/.specify/
double-prefix fix for additional safety.