Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/stupid-parrots-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"roo-cline": patch
---

Fix bug where the saved API provider for a mode wasn't being selected after a mode switch command
5 changes: 2 additions & 3 deletions src/core/Cline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2065,11 +2065,10 @@ export class Cline {
break
}

// Switch the mode
// Switch the mode using shared handler
const provider = this.providerRef.deref()
if (provider) {
await provider.updateGlobalState("mode", mode_slug)
await provider.postStateToWebview()
await provider.handleModeSwitch(mode_slug)
}
pushToolResult(
`Successfully switched from ${getModeBySlug(currentMode)?.name ?? currentMode} mode to ${
Expand Down
71 changes: 39 additions & 32 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,38 +781,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.postStateToWebview()
break
case "mode":
const newMode = message.text as Mode
await this.updateGlobalState("mode", newMode)

// Load the saved API config for the new mode if it exists
const savedConfigId = await this.configManager.getModeConfigId(newMode)
const listApiConfig = await this.configManager.listConfig()

// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig)

// If this mode has a saved config, use it
if (savedConfigId) {
const config = listApiConfig?.find((c) => c.id === savedConfigId)
if (config?.name) {
const apiConfig = await this.configManager.loadConfig(config.name)
await Promise.all([
this.updateGlobalState("currentApiConfigName", config.name),
this.updateApiConfiguration(apiConfig),
])
}
} else {
// If no saved config for this mode, save current config as default
const currentApiConfigName = await this.getGlobalState("currentApiConfigName")
if (currentApiConfigName) {
const config = listApiConfig?.find((c) => c.name === currentApiConfigName)
if (config?.id) {
await this.configManager.setModeConfig(newMode, config.id)
}
}
}

await this.postStateToWebview()
await this.handleModeSwitch(message.text as Mode)
break
case "updateSupportPrompt":
try {
Expand Down Expand Up @@ -1241,6 +1210,44 @@ export class ClineProvider implements vscode.WebviewViewProvider {
)
}

/**
* Handle switching to a new mode, including updating the associated API configuration
* @param newMode The mode to switch to
*/
public async handleModeSwitch(newMode: Mode) {
await this.updateGlobalState("mode", newMode)

// Load the saved API config for the new mode if it exists
const savedConfigId = await this.configManager.getModeConfigId(newMode)
const listApiConfig = await this.configManager.listConfig()

// Update listApiConfigMeta first to ensure UI has latest data
await this.updateGlobalState("listApiConfigMeta", listApiConfig)

// If this mode has a saved config, use it
if (savedConfigId) {
const config = listApiConfig?.find((c) => c.id === savedConfigId)
if (config?.name) {
const apiConfig = await this.configManager.loadConfig(config.name)
await Promise.all([
this.updateGlobalState("currentApiConfigName", config.name),
this.updateApiConfiguration(apiConfig),
])
}
} else {
// If no saved config for this mode, save current config as default
const currentApiConfigName = await this.getGlobalState("currentApiConfigName")
if (currentApiConfigName) {
const config = listApiConfig?.find((c) => c.name === currentApiConfigName)
if (config?.id) {
await this.configManager.setModeConfig(newMode, config.id)
}
}
}

await this.postStateToWebview()
}

private async updateApiConfiguration(apiConfiguration: ApiConfiguration) {
// Update mode's default config
const { mode } = await this.getState()
Expand Down
62 changes: 62 additions & 0 deletions src/core/webview/__tests__/ClineProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,68 @@ describe("ClineProvider", () => {
})
})

describe("handleModeSwitch", () => {
beforeEach(() => {
// Set up webview for each test
provider.resolveWebviewView(mockWebviewView)
})

test("loads saved API config when switching modes", async () => {
// Mock ConfigManager methods
provider.configManager = {
getModeConfigId: jest.fn().mockResolvedValue("saved-config-id"),
listConfig: jest
.fn()
.mockResolvedValue([{ name: "saved-config", id: "saved-config-id", apiProvider: "anthropic" }]),
loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }),
setModeConfig: jest.fn(),
} as any

// Switch to architect mode
await provider.handleModeSwitch("architect")

// Verify mode was updated
expect(mockContext.globalState.update).toHaveBeenCalledWith("mode", "architect")

// Verify saved config was loaded
expect(provider.configManager.getModeConfigId).toHaveBeenCalledWith("architect")
expect(provider.configManager.loadConfig).toHaveBeenCalledWith("saved-config")
expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "saved-config")

// Verify state was posted to webview
expect(mockPostMessage).toHaveBeenCalledWith(expect.objectContaining({ type: "state" }))
})

test("saves current config when switching to mode without config", async () => {
// Mock ConfigManager methods
provider.configManager = {
getModeConfigId: jest.fn().mockResolvedValue(undefined),
listConfig: jest
.fn()
.mockResolvedValue([{ name: "current-config", id: "current-id", apiProvider: "anthropic" }]),
setModeConfig: jest.fn(),
} as any

// Mock current config name
mockContext.globalState.get = jest.fn((key: string) => {
if (key === "currentApiConfigName") return "current-config"
return undefined
})

// Switch to architect mode
await provider.handleModeSwitch("architect")

// Verify mode was updated
expect(mockContext.globalState.update).toHaveBeenCalledWith("mode", "architect")

// Verify current config was saved as default for new mode
expect(provider.configManager.setModeConfig).toHaveBeenCalledWith("architect", "current-id")

// Verify state was posted to webview
expect(mockPostMessage).toHaveBeenCalledWith(expect.objectContaining({ type: "state" }))
})
})

describe("updateCustomMode", () => {
test("updates both file and state when updating custom mode", async () => {
provider.resolveWebviewView(mockWebviewView)
Expand Down
Loading