feat(agents): add VS Code Copilot SDD multi-mode support (backend)#505
Closed
Snakeblack wants to merge 5 commits into
Closed
feat(agents): add VS Code Copilot SDD multi-mode support (backend)#505Snakeblack wants to merge 5 commits into
Snakeblack wants to merge 5 commits into
Conversation
Enable per-phase sub-agents for VS Code Copilot via .agent.md files,
matching the multi-mode capability already shipped for OpenCode, Cursor
and Kiro.
- VS Code adapter now reports SupportsSubAgents() == true and exposes
SubAgentsDir(homeDir) == ~/.copilot/agents/ plus EmbeddedSubAgentsDir()
== "vscode/agents".
- 10 embedded .agent.md templates under internal/assets/vscode/agents/
(one per SDD phase, sdd-init through sdd-onboard). Templates omit the
model field on the default set so Copilot uses the user's default
model.
- New profile generator GenerateVSCodeProfileFiles(profile, agentsDir)
([]string, error) reads templates, resolves {{VSC_MODEL}} via a
provider/model → Copilot display name table (vscModelEntries) and
writes suffixed files atomically.
- inject.go step 2c writes named-profile files for VS Code Copilot
when SDD mode is multi and a non-default profile is configured;
step-3c handles the default unsuffixed set unchanged.
- RemoveVSCodeProfileAgents(agentsDir, profileName) removes the 10
suffixed phase files for a named profile; default profile rejected,
non-gentle-ai files preserved.
- Post-injection verification extended to recognize the .agent.md
extension and validate sdd-apply.agent.md plus sdd-verify.agent.md
are non-empty.
TUI integration (welcome menu entry, profile create/edit screens, VS
Code-specific model picker) lands in a separate PR.
This was referenced May 11, 2026
Add two regression tests that explicitly invoke Inject() twice with
identical inputs and assert:
1. The second run reports InjectionResult.Changed = false.
2. The file inventory in ~/.copilot/agents/ is unchanged (same set,
same modification times) — proving filemerge.WriteFileAtomic's
content-equality short-circuit holds across the full VS Code
injection path.
Coverage:
- Default profile path (step 3c only, 10 files).
- Named profile path (step 2c writes 10 suffixed files on top of
step 3c's 10 defaults — total 20 files).
These tests blind the recent fix at inject.go:456 (changed = changed
|| len(profileFiles) > 0) against future regressions where a
contributor might re-introduce the unconditional Changed=true that
broke the idempotency contract.
Closed
2 tasks
The vscode-sdd-profiles-research.md doc was a working/exploration artifact useful during design but does not belong in the shipped codebase. The architectural decisions it captured are reflected in proposal/spec/design/tasks (kept in engram and locally in openspec/changes/, which is gitignored).
Snakeblack
added a commit
to Snakeblack/gentle-ai
that referenced
this pull request
May 11, 2026
…ates agents VS Code Copilot scans both \`.agent.md\` (its native format) and Claude \`.md\` agent files in parallel. When a user installs SDD multi-mode for both VS Code Copilot and Claude Code through the gentle-ai wizard, the 8 sub-agent phases that both adapters ship (sdd-apply, sdd-archive, sdd-design, sdd-explore, sdd-propose, sdd-spec, sdd-tasks, sdd-verify) appear twice in the VS Code Agent customizations panel. This is not a bug — each agent file is correct and functional in its own host — but it looks broken to a user who sees \"sdd-verify\" listed twice and assumes gentle-ai wrote duplicates. The case was traced down in PR Gentleman-Programming#505 manual testing. Changes: - New ScreenSDDDuplicateAgentsWarning with render function that lists exactly the 8 phases that duplicate (sdd-init and sdd-onboard are excluded because the Claude adapter does not ship them as sub-agents). - shouldWarnAboutDuplicateAgents() returns true when SDD is selected AND both AgentVSCodeCopilot and AgentClaudeCode are in the agent set. Extensible: add other Claude-format adapters to the check as they are introduced. - Wired into the ScreenSDDMode handler: when SDDModeMulti is chosen and the warning condition holds, the warning screen is shown before the normal post-SDDMode flow (ModelPicker / StrictTDD / etc.). - Extracted advanceFromSDDModeSelection() helper so both the SDDMode handler (when no warning is required) and the warning's \"Continue anyway\" path can share the same forward navigation logic. - Warning screen offers two options: \"Continue anyway\" resumes the normal flow; \"Back to adapter selection\" returns to ScreenSDDMode so the user can unselect one adapter. Test coverage: - TestShouldWarnAboutDuplicateAgents (7 subtests) covers the detection helper across all relevant adapter combinations. - TestSDDMode_TriggersDuplicateAgentsWarning verifies that selecting multi-mode with the offending combination routes to the warning. - TestSDDMode_NoWarningWhenNotDuplicating verifies that a benign combination skips the warning. - TestSDDDuplicateAgentsWarning_ContinueAdvances and TestSDDDuplicateAgentsWarning_BackReturnsToSDDMode cover both branches of the warning handler. - TestRenderSDDDuplicateAgentsWarning_ListsExpectedPhases pins the rendered phase list to the contract so a future contributor cannot silently drop or add a phase.
Without an orchestrator template, SDD multi-mode on VS Code Copilot
relies on the default chat agent inferring the phase sequence from
sub-agent description fields alone. Weak Copilot models routinely
skip phases or reorder them, breaking the SDD contract.
This change adds an explicit orchestrator template that mirrors the
pattern OpenCode, Claude Code, and Kiro already use:
- New internal/assets/vscode/agents/sdd-orchestrator.agent.md
embedded template with tools: ['agent'], an agents: whitelist of
the 10 phases, user-invocable: true, and a body that documents
the strict explore → propose → spec → design → tasks → apply →
verify → archive sequence plus the init / onboard utility flows.
- {{VSC_PROFILE_SUFFIX}} placeholder so the same template serves
both the default (unsuffixed) install and named profiles where
every phase reference is suffixed with -{name}.
- vscode.OrchestratorPhase constant + generateOrchestratorAgent()
helper that renders the orchestrator inline for named profiles
(separate from the embedded template path used by 3c).
- GenerateVSCodeProfileFiles now writes 11 files per profile
(orchestrator + 10 phases). RemoveVSCodeProfileAgents already
matched the suffix pattern, so it cleans up the orchestrator
automatically.
- inject.go step 3c resolves {{VSC_PROFILE_SUFFIX}} to empty for
the default set; post-check conditionally validates the
orchestrator file only when the adapter ships one (VS Code does,
Claude Code does not — Claude uses CLAUDE.md as the root
orchestrator prompt instead of a separate agent file).
Tests:
- TestGenerateAgentFile_Orchestrator_DefaultProfile_HasAllRequiredFields
pins the YAML contract (tools, agents, user-invocable, etc.).
- TestGenerateAgentFile_Orchestrator_NamedProfile_SuffixesAgentNames
proves the agents: whitelist AND body references get suffixed in
lockstep — without that, the orchestrator would dispatch to
nonexistent agents.
- TestGenerateAgentFile_Orchestrator_OrchestratorModelAssignment
covers three model resolution paths (omit / known / fallback).
- TestGenerateVSCodeProfileFiles_IncludesOrchestrator regression
guard against the previous 10-file design.
- TestRemoveVSCodeProfileAgents_AlsoRemovesOrchestrator regression
guard against the orchestrator lingering after delete.
- Existing idempotency tests updated for the new counts (11 default,
22 with one named profile).
Docs:
- docs/prd-vscode-profiles.md captures the full product spec in
English, mirroring the format of docs/prd-opencode-profiles.md.
Covers problem statement (June 2026 AI Credits transition +
enterprise lock-in), vision, scope, detailed requirements,
technical design, UX flows, edge cases, and open questions.
…templates
Embedded .agent.md templates may contain CRLF line endings on Windows.
The sentinel removal (e.g. model: {{VSC_MODEL}}\n → empty) only
matched LF, silently leaving raw {{VSC_MODEL}} in the deployed files.
VS Code Copilot's YAML parser then rejects the invalid model value.
Normalize \r\n → \n immediately after reading the embedded content,
before any sentinel replacement runs.
2 tasks
Contributor
|
Heads-up: this PR is now showing a merge conflict against Could you rebase against current git fetch upstream main
git rebase upstream/main
# resolve any conflicts
git push --force-with-leaseOnce it's green again I can review for merge. Sorry for the drift — it's the cost of the cleanup velocity. Thanks for sticking with it. |
Author
|
Closing this as superseded by the newer VS Code Copilot SDD subagents stack:
This older PR used the previous named-profile / |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔗 Linked Issue
Closes #504
🏷️ PR Type
type:feature— New feature (non-breaking change that adds functionality)📝 Summary
.agent.mdtemplates so SDD multi-mode now works on VS Code Copilot out of the box (10 default agents written to~/.copilot/agents/).GenerateVSCodeProfileFiles/RemoveVSCodeProfileAgentsplus aprovider/model → "Display Name (copilot)"mapping table (vscModelEntries) so named profiles can land per-phase models in Copilot's frontmatter.inject.gostep 2c) and post-injection verification to recognize the.agent.mdextension, without touching OpenCode/Cursor/Kiro paths.Context for prioritization (from the linked issue): GitHub Copilot's billing shifts to AI Credits (usage-based) in June — that change makes per-phase model assignment a real cost lever for the enterprise cohort that's already standardized on Copilot + VS Code. This PR is the backend foundation; TUI integration follows in a separate PR.
📂 Changes
internal/agents/vscode/adapter.goSupportsSubAgents() = true, newSubAgentsDir,EmbeddedSubAgentsDir,VSCModelIDdelegateinternal/agents/vscode/adapter_test.goSubAgentsDir(macOS/Linux/Windows)internal/agents/vscode/vscode_profiles.govscModelEntriestable,VSCodeModelID,GenerateAgentFile,GenerateVSCodeProfileFiles,RemoveVSCodeProfileAgents,SDDPhasesinternal/agents/vscode/vscode_profiles_test.gointernal/assets/vscode/agents/*.agent.mdsdd-initthroughsdd-onboard) with{{VSC_MODEL}}sentinelinternal/assets/assets.govscode/directory treeinternal/assets/assets_test.gointernal/components/sdd/inject.go.agent.mdextension support, post-check extended tosdd-apply.agent.md+sdd-verify.agent.mdinternal/components/sdd/vscode_inject_test.godocs/vscode-sdd-profiles-research.md🧪 Test Plan
Unit Tests
go test ./...Manual E2E (verified locally)
go install ./cmd/gentle-aigentle-ai→ Start installation → select VS Code Copilot → choose SDD multi-mode → finish~/.copilot/agents/contains the 10sdd-*.agent.mdfilesgo test ./...) — 0 failures,go vetcleancd e2e && ./docker-test.sh) — not run locally (Docker on Windows); CI will validate🤖 Automated Checks
.agent.mdtemplates (markdown content, not logic) + research doc. Requestingsize:exception— the Go production+test code alone is ~400 LOC and well within the spirit of the budget. Splitting templates into a separate PR would only churn assets and delay the unified review.Closes #504status:approvedstatus:needs-review— submitter has no maintainer permissions on the upstream repotype:*Labeltype:featureapplied by a maintainer✅ Contributor Checklist
size:exception; rationale in the table abovetype:featurelabel — needs maintainer to apply (no permissions from submitter)go test ./...)docs/vscode-sdd-profiles-research.md)Co-Authored-Bytrailers💬 Notes for Reviewers
kiroModelResolver/claudeModelResolver) —vscModelResolveris the third instance of the same shape. Step 2c is cleanly isolated behindadapter.Agent() == model.AgentVSCodeCopilot; OpenCode / Cursor / Kiro code paths are untouched.vscModelEntriesis significant:gpt-4o-minimust precedegpt-4oandgpt-4.1-minimust precedegpt-4.1(substring matching). Comment invscode_profiles.godocuments this plus the latent risk for a futureclaude-sonnet-4-5.gentle-aiwith no changes does not flipInjectionResult.Changedtotrue(recently fixed atinject.go:456).t.TempDir()for transient paths — no hardcoded/tmp.vscModelEntrieslist, no OpenCode model cache). Named-profile generation is fully implemented in the backend and reachable via the Go API today — the TUI just doesn't expose it yet.