Do a more complete mode switch from switch_mode command

This commit is contained in:
Matt Rubens
2025-01-24 23:02:30 -05:00
parent f5021993ea
commit 8c929ba16a
4 changed files with 108 additions and 35 deletions

View File

@@ -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 ${

View File

@@ -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 {
@@ -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()

View File

@@ -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)