Skip to content

Commit 694bfd0

Browse files
committed
re-allow changing the mode of an active task via the UI.
(felt like a bug the other way)
1 parent 5d3d6bb commit 694bfd0

File tree

2 files changed

+1
-138
lines changed

2 files changed

+1
-138
lines changed

src/core/webview/ClineProvider.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,6 @@ export class ClineProvider
591591
experiments,
592592
} = await this.getState()
593593

594-
595594
const cline = new Task({
596595
provider: this,
597596
apiConfiguration,
@@ -808,17 +807,10 @@ export class ClineProvider
808807
public async handleModeSwitch(newMode: Mode) {
809808
// Telemetry for mode switch (provider level)
810809
const cline = this.getCurrentCline() // Get current cline for task ID if available
811-
telemetryService.captureModeSwitch(cline?.taskId || "global", newMode)
810+
TelemetryService.instance.captureModeSwitch(cline?.taskId || "global", newMode)
812811

813-
// Emit an event that the provider's mode is switching.
814-
// The active Cline instance itself should only update its internal currentModeSlug
815-
// if the switch_mode tool is used within its context.
816-
// A global mode switch should not change an active task's inherent mode.
817812
if (cline) {
818813
TelemetryService.instance.captureModeSwitch(cline.taskId, newMode)
819-
// Update the Cline instance's current mode *before* emitting the event
820-
// and *before* updating global state, to ensure consistency if event handlers
821-
// or global state watchers try to access the cline's mode.
822814
await cline.updateCurrentModeSlug(newMode)
823815
cline.emit("taskModeSwitched", cline.taskId, newMode)
824816
}

src/core/webview/__tests__/ClineProvider.test.ts

Lines changed: 0 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,135 +2366,6 @@ describe("Task Mode Preservation with Subtasks", () => {
23662366
})
23672367
})
23682368

2369-
describe("Task Mode Decoupling from Global UI Mode Changes", () => {
2370-
let provider: ClineProvider
2371-
let mockContext: vscode.ExtensionContext
2372-
let mockOutputChannel: vscode.OutputChannel
2373-
let mockWebviewView: vscode.WebviewView
2374-
let mockPostMessage: jest.Mock
2375-
2376-
let activeTaskMock: any
2377-
const taskSpecificMode = "task-specific-mode"
2378-
const newGlobalUIMode = "new-global-ui-mode"
2379-
2380-
beforeEach(async () => {
2381-
// Reset mocks
2382-
jest.clearAllMocks()
2383-
2384-
// Mock context
2385-
const globalState: Record<string, string | undefined> = {
2386-
mode: taskSpecificMode, // Initial global mode is the task's mode
2387-
currentApiConfigName: "task-specific-config",
2388-
"apiConfigId_task-specific-mode": "task-specific-config-id",
2389-
"apiConfiguration_task-specific-config-id": JSON.stringify({
2390-
apiProvider: "test-task-specific",
2391-
id: "task-specific-config-id",
2392-
name: "task-specific-config",
2393-
}),
2394-
"apiConfigId_new-global-ui-mode": "new-global-config-id",
2395-
"apiConfiguration_new-global-config-id": JSON.stringify({
2396-
apiProvider: "test-new-global",
2397-
id: "new-global-config-id",
2398-
name: "new-global-config",
2399-
}),
2400-
listApiConfigMeta: JSON.stringify([
2401-
{ id: "task-specific-config-id", name: "task-specific-config", apiProvider: "test-task-specific" },
2402-
{ id: "new-global-config-id", name: "new-global-config", apiProvider: "test-new-global" },
2403-
]),
2404-
}
2405-
const secrets: Record<string, string | undefined> = {}
2406-
mockContext = {
2407-
extensionPath: "/test/path",
2408-
extensionUri: {} as vscode.Uri,
2409-
globalState: {
2410-
get: jest.fn().mockImplementation((key: string) => {
2411-
const value = globalState[key]
2412-
if (key.startsWith("apiConfiguration_") || key === "listApiConfigMeta") {
2413-
return value ? JSON.parse(value) : undefined
2414-
}
2415-
return value
2416-
}),
2417-
update: jest.fn().mockImplementation((key: string, value: any) => {
2418-
if (typeof value === "object") {
2419-
globalState[key] = JSON.stringify(value)
2420-
} else {
2421-
globalState[key] = value
2422-
}
2423-
}),
2424-
keys: jest.fn().mockImplementation(() => Object.keys(globalState)),
2425-
},
2426-
secrets: {
2427-
get: jest.fn().mockImplementation((key: string) => secrets[key]),
2428-
store: jest.fn().mockImplementation((key: string, value: string | undefined) => (secrets[key] = value)),
2429-
delete: jest.fn().mockImplementation((key: string) => delete secrets[key]),
2430-
},
2431-
subscriptions: [],
2432-
extension: {
2433-
packageJSON: { version: "1.0.0" },
2434-
},
2435-
globalStorageUri: {
2436-
fsPath: "/test/storage/path",
2437-
},
2438-
} as unknown as vscode.ExtensionContext
2439-
2440-
mockOutputChannel = {
2441-
appendLine: jest.fn(),
2442-
clear: jest.fn(),
2443-
dispose: jest.fn(),
2444-
} as unknown as vscode.OutputChannel
2445-
2446-
mockPostMessage = jest.fn()
2447-
mockWebviewView = {
2448-
webview: {
2449-
postMessage: mockPostMessage,
2450-
html: "",
2451-
options: {},
2452-
onDidReceiveMessage: jest.fn(),
2453-
asWebviewUri: jest.fn(),
2454-
},
2455-
visible: true,
2456-
onDidDispose: jest.fn().mockImplementation((callback) => {
2457-
callback()
2458-
return { dispose: jest.fn() }
2459-
}),
2460-
onDidChangeVisibility: jest.fn().mockImplementation(() => ({ dispose: jest.fn() })),
2461-
} as unknown as vscode.WebviewView
2462-
2463-
provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext))
2464-
// @ts-ignore - Accessing private property for testing.
2465-
provider.customModesManager = {
2466-
updateCustomMode: jest.fn().mockResolvedValue(undefined),
2467-
getCustomModes: jest.fn().mockResolvedValue([]),
2468-
dispose: jest.fn(),
2469-
}
2470-
await provider.resolveWebviewView(mockWebviewView) // Ensure webview is resolved
2471-
;(Task as unknown as jest.Mock).mockClear()
2472-
// Set provider's initial state for the active task by calling handleModeSwitch
2473-
await provider.handleModeSwitch(taskSpecificMode)
2474-
2475-
// Initialize the active task. initClineWithTask will use the provider's current state.
2476-
await provider.initClineWithTask("Active Task")
2477-
activeTaskMock = provider.getCurrentCline()
2478-
expect(activeTaskMock.currentModeSlug).toBe(taskSpecificMode)
2479-
let providerStateAfterTaskInit = await provider.getState()
2480-
expect(providerStateAfterTaskInit.mode).toBe(taskSpecificMode)
2481-
})
2482-
2483-
test("Global UI mode switch via handleModeSwitch should NOT change active Cline's currentModeSlug", async () => {
2484-
let providerStateBeforeSwitch = await provider.getState()
2485-
expect(activeTaskMock.currentModeSlug).toBe(taskSpecificMode)
2486-
expect(providerStateBeforeSwitch.mode).toBe(taskSpecificMode)
2487-
2488-
await provider.handleModeSwitch(newGlobalUIMode) // Corrected: Only one argument
2489-
2490-
let providerStateAfterSwitch = await provider.getState()
2491-
expect(providerStateAfterSwitch.mode).toBe(newGlobalUIMode)
2492-
2493-
expect(activeTaskMock.currentModeSlug).toBe(taskSpecificMode) // Task's internal mode should remain unchanged
2494-
expect(activeTaskMock.updateCurrentModeSlug).not.toHaveBeenCalled()
2495-
})
2496-
})
2497-
24982369
describe("Project MCP Settings", () => {
24992370
let provider: ClineProvider
25002371
let mockContext: vscode.ExtensionContext

0 commit comments

Comments
 (0)