Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5450d27
feat(vscode): add native SDD agents
Snakeblack May 29, 2026
b78bd1d
test(assets): register vscode agent files in expected list
Snakeblack Jun 29, 2026
f7ed70b
fix(uninstall): filter by opRemoveFile in VS Code uninstall test
Snakeblack Jul 1, 2026
41f4f50
style(vscode): add language identifier to dependency-graph code block
Snakeblack Jul 1, 2026
034c7a4
feat(vscode): agrega asignaciones de modelo
Snakeblack Jun 1, 2026
d51c5e0
fix(app,cli): add VSCodeModelAssignments merge and empty override checks
Snakeblack Jun 29, 2026
2dab40b
fix(vscode): endurece instalación de agentes
Snakeblack Jun 2, 2026
a247af2
fix(sync,sdd): resolve CodeRabbit review comments on PR 731
Snakeblack Jun 30, 2026
8ff7569
fix(sdd): require both delimiters before removing legacy preflight lines
Snakeblack Jul 1, 2026
47ff860
fix(sdd): validate every legacy preflight block independently
Snakeblack Jul 1, 2026
e3eba36
fix(sdd): remove duplicate containsString test helper
Snakeblack Jul 1, 2026
90553ef
fix(sdd,uninstall): add backup and restore support for Claude interna…
Snakeblack Jun 29, 2026
cabc090
fix(vscode): resolve rebase conflicts on hardening branch and fix eng…
Snakeblack Jun 30, 2026
b198fae
fix(vscode,tui): resolve all CodeRabbit review comments on backup/res…
Snakeblack Jun 30, 2026
d220e9c
fix(vscode,uninstall,tui): address coderabbit review on atomic backup…
Snakeblack Jun 30, 2026
5d70857
fix(vscode,cli,sdd): resolve remaining CodeRabbit comments on backup …
Snakeblack Jun 30, 2026
51763da
fix(vscode): resolve CodeRabbit review comments on backup restore and…
Snakeblack Jul 1, 2026
28d0885
fix(uninstall): abort on unexpected backup or read failures
Snakeblack Jul 1, 2026
dd1d17c
feat(vscode): agrega TUI y docs finales
Snakeblack Jun 2, 2026
1e3acc9
fix(sdd,tui,docs): resolve VS Code Copilot model catalog I/O, quoting…
Snakeblack Jun 29, 2026
0bc1015
fix(vscode,tui): resolve all CodeRabbit review comments on backup/res…
Snakeblack Jun 30, 2026
acdbd08
fix(tui): resolve model picker panic on profiles and update goldens
Snakeblack Jun 30, 2026
41eb985
merge: sync with origin/main and resolve conflicts in docs/intended-u…
Snakeblack Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Gentle-AI is NOT an AI agent installer. Most agents are easy to install. It is a
| **Kilo Code** | Full (multi-mode overlay) | OpenCode-compatible config in `~/.config/kilo` |
| **Gemini CLI** | Full (experimental) | Custom agents in `~/.gemini/agents/` |
| **Cursor** | Full (native subagents) | 10 SDD agents in `~/.cursor/agents/` |
| **VS Code Copilot** | Full (runSubagent) | Parallel execution |
| **VS Code Copilot** | Full (native subagents) | `~/.copilot/agents` SDD agents with model fallback |
| **Codex** | Solo-agent | CLI-native, TOML config |
| **Windsurf** | Solo-agent | Plan Mode, Code Mode, native workflows |
| **Antigravity** | Solo-agent + Mission Control | Built-in Browser/Terminal sub-agents |
Expand Down Expand Up @@ -190,6 +190,14 @@ After creating a profile, open OpenCode and press **Tab** to switch between `gen

**Full guide**: [OpenCode SDD Profiles](docs/opencode-profiles.md)

### VS Code Copilot SDD Agents

Gentle-AI installs three VS Code layers: global instructions/rules in the VS Code user prompts folder (e.g. `%APPDATA%/Code/User/prompts/gentle-ai.instructions.md` on Windows, `~/Library/Application Support/Code/User/prompts/gentle-ai.instructions.md` on macOS, or `~/.config/Code/User/prompts/gentle-ai.instructions.md` on Linux), native custom agents in `~/.copilot/agents`, and SDD skills/shared conventions in `~/.copilot/skills`.

You can assign GitHub Copilot models per coordinator or SDD phase from the TUI. If a model cache is missing, invalid, or unvalidated, generated agents omit `model` and inherit the parent chat model instead of failing sync.

If VS Code also discovers Gentle-AI's Claude-format internal agents, sync marks those managed `sdd-*` and `jd-*` files `user-invocable: false` so the visible picker stays focused on `sdd-orchestrator`.

### Engram (Persistent Memory)

Your AI agent automatically remembers decisions, bugs, and context across sessions. You don't need to do anything -- but when you do:
Expand Down
30 changes: 18 additions & 12 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
| Kilo Code | `kilocode` | Yes | Yes | Full (multi-mode overlay) | No | Yes | `~/.config/kilo` |
| Gemini CLI | `gemini-cli` | Yes | Yes | Full (experimental) | No | No | `~/.gemini` |
| Cursor | `cursor` | Yes | Yes | Full (native subagents) | No | No | `~/.cursor` |
| VS Code Copilot | `vscode-copilot` | Yes | Yes | Full (runSubagent) | No | No | `~/.copilot` + VS Code User profile |
| VS Code Copilot | `vscode-copilot` | Yes | Yes | Full (native subagents) | No | No | `~/.copilot` + VS Code User profile |
| Codex | `codex` | Yes | Yes | Solo-agent (multi-agent opt-in, experimental) | No | No | `~/.codex` |
| Windsurf | `windsurf` | Yes (native) | Yes | Solo-agent | No | No | `~/.codeium/windsurf` |
| Antigravity | `antigravity` | Yes (native) | Yes | Solo-agent + Mission Control | No | No | `~/.gemini/antigravity` |
Expand Down Expand Up @@ -72,17 +72,19 @@ Kiro uses native custom agents in `~/.kiro/agents/`. `gentle-ai` writes phase ag

## SDD Mode Support

| Feature | Claude Code | OpenCode | Kilo Code | Gemini CLI | Cursor | VS Code Copilot | Codex | Windsurf | Antigravity | Kiro IDE | Qwen Code | OpenClaw | Trae | Pi | Hermes |
| ---------------- | :---------: | :------: | :-------: | :--------: | :----: | :-------------: | :---: | :------: | :---------: | :------: | :-------: | :------: | :--: | :-----: | :----: |
| SDD orchestrator | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Single-mode SDD | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Multi-mode SDD | — | Yes | Yes | — | — | | — | — | — | Yes\* | — | — | — | Yes\*\* | — |
| Feature | Claude Code | OpenCode | Kilo Code | Gemini CLI | Cursor | VS Code Copilot | Codex | Windsurf | Antigravity | Kiro IDE | Qwen Code | OpenClaw | Trae | Pi | Hermes |
| ---------------- | :---------: | :------: | :-------: | :--------: | :----: | :-------------: | :---: | :------: | :---------: | :------: | :-------: | :------: | :--: | :-------: | :----: |
| SDD orchestrator | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Single-mode SDD | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Multi-mode SDD | — | Yes | Yes | — | — | Yes\* | — | — | — | Yes\*\* | — | — | — | Yes\*\*\* | — |

**Multi-mode** (assigning different AI models to each SDD phase) is supported by **OpenCode** and **Kilo Code** through the OpenCode-compatible multi-mode overlay, and by **Kiro IDE** through native subagent `model:` frontmatter. All other agents run in **single-mode** — the orchestrator manages everything using whatever model the agent is already running.
**Multi-mode** (assigning different AI models to each SDD phase) is supported by **OpenCode** and **Kilo Code** through the OpenCode-compatible multi-mode overlay, by **VS Code Copilot** through native `.agent.md` `model` frontmatter, and by **Kiro IDE** through native subagent `model:` frontmatter. All other agents run in **single-mode** — the orchestrator manages everything using whatever model the agent is already running.

> \* **Kiro multi-mode** assigns models per phase through `KiroModelAssignments` (configured via _Configure Models → Configure Kiro models_ in the TUI). The selected Kiro alias (`auto|opus|sonnet|haiku|minimax|glm|deepseek|qwen`) is resolved to a Kiro-native model ID and stamped into each `~/.kiro/agents/sdd-{phase}.md` at sync time.
> \* **VS Code Copilot multi-mode** assigns GitHub Copilot models per coordinator or phase through `VSCodeModelAssignments` (configured via _Configure Models → Configure VS Code Copilot SDD models_ in the TUI). The picker uses dynamic `github-copilot` cache data; missing, invalid, or unvalidated assignments omit `model` so the agent inherits the parent chat model.

> \*\* **Pi multi-mode** is owned by the Pi packages. `gentle-pi` installs SDD agent and chain assets into `.pi/agents/` and `.pi/chains/`; model overrides live in those Pi-managed files or chain steps.
> \*\* **Kiro multi-mode** assigns models per phase through `KiroModelAssignments` (configured via _Configure Models → Configure Kiro models_ in the TUI). The selected Kiro alias (`auto|opus|sonnet|haiku|minimax|glm|deepseek|qwen`) is resolved to a Kiro-native model ID and stamped into each `~/.kiro/agents/sdd-{phase}.md` at sync time.

> \*\*\* **Pi multi-mode** is owned by the Pi packages. `gentle-pi` installs SDD agent and chain assets into `.pi/agents/` and `.pi/chains/`; model overrides live in those Pi-managed files or chain steps.

---

Expand Down Expand Up @@ -128,10 +130,14 @@ Kiro uses native custom agents in `~/.kiro/agents/`. `gentle-ai` writes phase ag

### VS Code Copilot

- Uses the `runSubagent` tool with support for parallel execution
- Skills at `~/.copilot/skills/`
- System prompt at `Code/User/prompts/gentle-ai.instructions.md`
- Native custom agents at `~/.copilot/agents/*.agent.md`: one user-invocable `sdd-orchestrator` coordinator plus hidden SDD phase agents
- Delegates through VS Code Copilot's native sub-agent / `runSubagent` flow
- Skills and shared SDD conventions at `~/.copilot/skills/`
- Global instructions/rules at `Code/User/prompts/gentle-ai.instructions.md`
- MCP config at `Code/User/mcp.json`
- TUI model assignments use dynamic `github-copilot` models and render optional `.agent.md` frontmatter `model`
- Missing, invalid, or unvalidated model assignments omit `model` and inherit the parent chat model; named/suffixed VS Code profiles are not generated
- VS Code may also discover Claude-format agents in `~/.claude/agents`; Gentle-AI marks its managed internal `sdd-*` and `jd-*` Claude agents `user-invocable: false` so the picker stays focused on `sdd-orchestrator`

### Codex

Expand Down
3 changes: 3 additions & 0 deletions docs/intended-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Support depends on the agent:
| ----- | -------------------- |
| **OpenCode** | SDD Profiles generate `gentle-orchestrator` plus phase sub-agents in `opencode.json` |
| **Kilo Code** | OpenCode-compatible SDD profile overlay in `~/.config/kilo` |
| **VS Code Copilot** | Use _Configure Models → Configure VS Code Copilot SDD models_ in the TUI. Gentle-AI writes a managed coordinator/phase assignment map into native `~/.copilot/agents/*.agent.md` files. |
| **Kiro IDE** | Native phase agents with per-agent `model:` frontmatter |
| **Pi** | Owned by `gentle-pi` through Pi-managed agents, chains, and model overrides |
| **Others** | Single-mode SDD; one active model handles all phases |
Expand All @@ -81,6 +82,8 @@ If you want OpenCode profiles:

You can create multiple profiles (e.g., "cheap" for experimentation, "premium" for production) and switch between them freely.

For VS Code Copilot, the picker uses dynamic `github-copilot` model data. If the cache is missing, the selected model is invalid, or the cost tier cannot be validated, generated agents omit `model` and inherit the parent chat model instead of failing sync. Gentle-AI does not generate named/suffixed VS Code profiles.

If you prefer a **runtime profile manager** that keeps profiles outside `opencode.json`, gentle-ai supports that too. During sync, OpenCode can auto-detect external profile files under `~/.config/opencode/profiles/*.json` and switch to a safer compatibility path that preserves the active `gentle-orchestrator` prompt instead of overwriting it.

**Full step-by-step guide**: [OpenCode SDD Profiles](opencode-profiles.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Release artifacts are produced by CI, but Windows users should install through S
| OpenCode | `%USERPROFILE%\.config\opencode\` |
| Gemini CLI | `%USERPROFILE%\.gemini\` |
| Cursor | `%USERPROFILE%\.cursor\` |
| VS Code Copilot | `%APPDATA%\Code\User\` (settings, MCP, prompts) + `%USERPROFILE%\.copilot\` (skills) |
| VS Code Copilot | `%APPDATA%\Code\User\` (settings, MCP, prompts) + `%USERPROFILE%\.copilot\agents\` (native SDD agents) + `%USERPROFILE%\.copilot\skills\` (skills) |
| Codex | `%USERPROFILE%\.codex\` |
| Windsurf | `%USERPROFILE%\.codeium\windsurf\` (skills, MCP, rules) + `%APPDATA%\Windsurf\User\` (settings) |
| Kimi | `%USERPROFILE%\.kimi\` (includes `config.toml`, system prompt, agents, MCP) |
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/mattn/go-isatty v0.0.20
github.com/rivo/uniseg v0.4.7
golang.org/x/sys v0.38.0
)

require (
Expand All @@ -28,6 +29,5 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.3.8 // indirect
)
9 changes: 5 additions & 4 deletions internal/agents/vscode/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func (a *Adapter) InstallCommand(_ system.PlatformProfile) ([][]string, error) {
// VS Code Copilot reads .instructions.md files from the VS Code User prompts folder.
// Skills are loaded from ~/.copilot/skills/ (global), .github/skills/ (workspace),
// ~/.claude/skills/, and .claude/skills/. We target ~/.copilot/skills/ for global reach.
// Native agent files are installed globally under ~/.copilot/agents/.

func (a *Adapter) GlobalConfigDir(homeDir string) string {
return filepath.Join(homeDir, ".copilot")
Expand Down Expand Up @@ -133,15 +134,15 @@ func (a *Adapter) CommandsDir(_ string) string {
}

func (a *Adapter) SupportsSubAgents() bool {
return false
return true
}

func (a *Adapter) SubAgentsDir(_ string) string {
return ""
func (a *Adapter) SubAgentsDir(homeDir string) string {
return filepath.Join(homeDir, ".copilot", "agents")
}

func (a *Adapter) EmbeddedSubAgentsDir() string {
return ""
return "vscode/agents"
}

func (a *Adapter) SupportsSkills() bool {
Expand Down
23 changes: 23 additions & 0 deletions internal/agents/vscode/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vscode
import (
"path/filepath"
"runtime"
"strings"
"testing"

"github.com/gentleman-programming/gentle-ai/internal/model"
Expand Down Expand Up @@ -91,3 +92,25 @@ func TestMCPConfigPathUsesVSCodeUserProfile(t *testing.T) {
}
}
}

func TestSubAgentSupportUsesCopilotUserAgentsDir(t *testing.T) {
a := NewAdapter()
home := "/tmp/home"

if !a.SupportsSubAgents() {
t.Fatal("VS Code adapter must advertise native sub-agent support")
}

got := a.SubAgentsDir(home)
want := filepath.Join(home, ".copilot", "agents")
if got != want {
t.Fatalf("SubAgentsDir() = %q, want %q", got, want)
}
if got == filepath.Join(home, ".github", "agents") || strings.Contains(got, string(filepath.Separator)+".github"+string(filepath.Separator)) {
t.Fatalf("SubAgentsDir() must not target workspace .github/agents, got %q", got)
}

if got := a.EmbeddedSubAgentsDir(); got != "vscode/agents" {
t.Fatalf("EmbeddedSubAgentsDir() = %q, want %q", got, "vscode/agents")
}
}
17 changes: 16 additions & 1 deletion internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ func tuiExecute(
CodexCarrilModelAssignments: selection.CodexCarrilModelAssignments,
CodexPhaseModelAssignments: selection.CodexPhaseModelAssignments,
ModelAssignments: modelAssignmentsToState(selection.ModelAssignments),
VSCodeModelAssignments: modelAssignmentsToState(selection.VSCodeModelAssignments),
Persona: string(selection.Persona),
})
}
Expand Down Expand Up @@ -627,6 +628,9 @@ func applyOverrides(selection *model.Selection, overrides *model.SyncOverrides)
if overrides.ModelAssignments != nil {
selection.ModelAssignments = overrides.ModelAssignments
}
if overrides.VSCodeModelAssignments != nil {
selection.VSCodeModelAssignments = overrides.VSCodeModelAssignments
}
if overrides.ClaudeModelAssignments != nil {
selection.ClaudeModelAssignments = overrides.ClaudeModelAssignments
}
Expand Down Expand Up @@ -735,6 +739,9 @@ func loadPersistedAssignments(homeDir string, selection *model.Selection) {
}
selection.ModelAssignments = m
}
if len(selection.VSCodeModelAssignments) == 0 && len(s.VSCodeModelAssignments) > 0 {
selection.VSCodeModelAssignments = cli.StateModelAssignmentsToModel(s.VSCodeModelAssignments)
}
}

// persistAssignments writes the model assignments from selection back to
Expand All @@ -750,10 +757,11 @@ func persistAssignments(homeDir string, selection model.Selection) {
selection.ClaudePhaseAssignments != nil ||
selection.KiroModelAssignments != nil ||
selection.ModelAssignments != nil ||
selection.VSCodeModelAssignments != nil ||
selection.CodexModelAssignments != nil ||
selection.CodexCarrilModelAssignments != nil ||
selection.CodexPhaseModelAssignments != nil
if len(selection.ClaudeModelAssignments) == 0 && len(selection.ClaudePhaseAssignments) == 0 && len(selection.KiroModelAssignments) == 0 && len(selection.ModelAssignments) == 0 && len(selection.CodexModelAssignments) == 0 && len(selection.CodexCarrilModelAssignments) == 0 && len(selection.CodexPhaseModelAssignments) == 0 && !hasAssignmentSignal {
if len(selection.ClaudeModelAssignments) == 0 && len(selection.ClaudePhaseAssignments) == 0 && len(selection.KiroModelAssignments) == 0 && len(selection.ModelAssignments) == 0 && len(selection.VSCodeModelAssignments) == 0 && len(selection.CodexModelAssignments) == 0 && len(selection.CodexCarrilModelAssignments) == 0 && len(selection.CodexPhaseModelAssignments) == 0 && !hasAssignmentSignal {
return
}
current, err := state.Read(homeDir)
Expand Down Expand Up @@ -816,6 +824,13 @@ func persistAssignments(homeDir string, selection model.Selection) {
current.ModelAssignments = nil
}
}
if selection.VSCodeModelAssignments != nil {
if len(selection.VSCodeModelAssignments) > 0 {
current.VSCodeModelAssignments = modelAssignmentsToState(selection.VSCodeModelAssignments)
} else {
current.VSCodeModelAssignments = nil
}
}
_ = state.Write(homeDir, current)
}

Expand Down
Loading