Skip to content

Stage 2: Copilot integration — proof of concept with shared template primitives#2035

Merged
mnriem merged 24 commits intogithub:mainfrom
mnriem:issue-1924-stage-2
Mar 31, 2026
Merged

Stage 2: Copilot integration — proof of concept with shared template primitives#2035
mnriem merged 24 commits intogithub:mainfrom
mnriem:issue-1924-stage-2

Conversation

@mnriem
Copy link
Copy Markdown
Collaborator

@mnriem mnriem commented Mar 31, 2026

Stage 2 of #1924 — migrates Copilot as the first integration to validate the architecture. Byte-for-byte identical output to the old release script path.

What's in this PR

src/specify_cli/integrations/base.py — Shared 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() — replaces {SCRIPT}, {ARGS}, __AGENT__, strips frontmatter script blocks, rewrites paths

src/specify_cli/integrations/copilot/ — CopilotIntegration

  • .agent.md commands, companion .prompt.md files, .vscode/settings.json merge
  • Self-contained update-context.sh/.ps1 scripts (staged for Stage 7 activation)

src/specify_cli/__init__.py — CLI wiring

  • --integration flag (mutually exclusive with --ai)
  • --ai copilot auto-promotes to integration path with migration nudge
  • integration.json with integration key + script paths
  • _install_shared_infra() with speckit.manifest.json tracking

tests/integrations/ — 76 tests across 6 files

  • test_base.py, test_manifest.py, test_registry.py, test_copilot.py, test_cli.py
  • Complete 37-file inventory tests for both sh and ps variants

Verification

  • Byte-for-byte parity with old release script output verified
  • End-to-end specify init . --integration copilot validated
  • --ai claude --offline (old path) confirmed working
  • All 971 tests pass (76 new + 895 existing)

mnriem added 12 commits March 31, 2026 11:06
- 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 github#1924
- 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 github#1924
…red 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 github#1924
Cleaner naming — the shared infrastructure (scripts, templates)
belongs to spec-kit itself, not to any specific integration.
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.
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.
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).
Validates every single file (37 total) produced by
specify init --integration copilot --script sh --no-git.
Validates all 37 files produced by --script ps variant, including
.specify/scripts/powershell/ instead of bash.
- 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.
File inventories are copilot-specific. test_cli.py now only tests
CLI flag mechanics (mutual exclusivity, unknown rejection, auto-promote).
Copilot AI review requested due to automatic review settings March 31, 2026 17:04
@mnriem mnriem mentioned this pull request Mar 31, 2026
17 tasks
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Stage 2 of the “Agents → Integrations” migration, introducing shared integration scaffolding primitives and a first migrated integration (Copilot) wired into specify init via a new --integration flag, with comprehensive integration-focused tests.

Changes:

  • Added shared integration primitives (shared template discovery, command install helpers, template processing) and automatic built-in integration registration.
  • Implemented CopilotIntegration (agent + prompt file generation, VS Code settings handling, integration-specific update-context scripts).
  • Wired specify init to support --integration (mutually exclusive with --ai) and added integration test coverage + full file-inventory assertions.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/specify_cli/integrations/base.py Adds shared template discovery + granular setup primitives and template processing.
src/specify_cli/integrations/__init__.py Registers built-in integrations (Copilot) at import time.
src/specify_cli/integrations/copilot/__init__.py Implements Copilot integration install flow (agents/prompts/settings/scripts).
src/specify_cli/integrations/copilot/scripts/update-context.sh Adds Copilot-specific context updater script (staged for later activation).
src/specify_cli/integrations/copilot/scripts/update-context.ps1 PowerShell variant of the staged context updater.
src/specify_cli/__init__.py Adds --integration CLI routing, integration scaffolding path, and shared infra install helper.
tests/integrations/conftest.py Adds a stub integration used across integration unit tests.
tests/integrations/test_base.py Tests new base primitives and default setup behavior.
tests/integrations/test_registry.py Tests integration registry registration and lookup semantics.
tests/integrations/test_manifest.py Refocuses manifest tests to manifest-only behavior.
tests/integrations/test_copilot.py Validates CopilotIntegration behavior + full file inventories for sh/ps.
tests/integrations/test_cli.py CLI-level tests for --integration and --ai copilot auto-promotion.
tests/integrations/__init__.py Adds package marker for integration tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

mnriem added 2 commits March 31, 2026 12:13
- _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)
Copilot AI review requested due to automatic review settings March 31, 2026 17:15
User now sees the exact settings they should add manually.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

mnriem added 2 commits March 31, 2026 12:48
- 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/
Only write files that don't already exist — preserves any user
modifications to shared scripts (common.sh etc.) and templates.
Copilot AI review requested due to automatic review settings March 31, 2026 17:49
Lists all shared scripts and templates that were not copied because
they already existed in the project.
Verifies that _install_shared_infra() preserves user-modified scripts
and templates while still installing missing ones.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (3)

tests/integrations/test_copilot.py:225

  • Same as the sh inventory: str(p.relative_to(project)) is OS-dependent. Use .as_posix() for deterministic path strings.
        assert result.exit_code == 0
        actual = sorted(str(p.relative_to(project)) for p in project.rglob("*") if p.is_file())
        expected = sorted([

tests/integrations/test_manifest.py:123

  • test_force_removes_modified no longer asserts that the modified file was actually deleted. Without that check, a regression where force=True still skips modified files would pass. Add an assertion that tmp_path / 'f.txt' does not exist after uninstall(force=True).
    tests/integrations/test_manifest.py:148
  • test_cleans_empty_parent_dirs only asserts that a/ is removed, but not the intermediate directories (a/b and a/b/c). Since the uninstall code attempts to rmdir parents repeatedly, keeping the deeper assertions would better validate directory cleanup behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…est 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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mnriem
Copy link
Copy Markdown
Collaborator Author

mnriem commented Mar 31, 2026

@copilot apply changes based on the comments in this thread

…isite

- 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)
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- _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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

… 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.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mnriem mnriem merged commit 3899dcc into github:main Mar 31, 2026
12 checks passed
@mnriem mnriem deleted the issue-1924-stage-2 branch March 31, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants