diff --git a/packages/cli/src/test-utils/render.tsx b/packages/cli/src/test-utils/render.tsx index 6ca30dd8b9..4ac205b74c 100644 --- a/packages/cli/src/test-utils/render.tsx +++ b/packages/cli/src/test-utils/render.tsx @@ -16,7 +16,7 @@ import { vi } from 'vitest'; import stripAnsi from 'strip-ansi'; import type React from 'react'; import { act, useState } from 'react'; -import type { LoadedSettings } from '../config/settings.js'; +import { LoadedSettings } from '../config/settings.js'; import { KeypressProvider } from '../ui/contexts/KeypressContext.js'; import { SettingsContext } from '../ui/contexts/SettingsContext.js'; import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js'; @@ -611,16 +611,18 @@ export const renderWithProviders = async ( uiState: providedUiState, width, mouseEventsEnabled = false, + useAlternateBuffer: explicitUseAlternateBuffer, config, uiActions, persistentState, appState = mockAppState, }: { shellFocus?: boolean; - settings?: LoadedSettings; + settings?: LoadedSettings | Partial; uiState?: Partial; width?: number; mouseEventsEnabled?: boolean; + useAlternateBuffer?: boolean; config?: Config; uiActions?: Partial; persistentState?: { @@ -659,15 +661,34 @@ export const renderWithProviders = async ( const terminalWidth = width ?? baseState.terminalWidth; + const finalSettings = + settings instanceof LoadedSettings + ? settings + : createMockSettings(settings || {}); + if (!config) { config = await loadCliConfig( - settings.merged, + finalSettings.merged, 'random-session-id', - {} as unknown as CliArgs, + {} as CliArgs, { cwd: '/' }, ); } + const useAlternateBuffer = + explicitUseAlternateBuffer ?? + finalSettings.merged.ui?.useAlternateBuffer ?? + false; + + const finalConfig = new Proxy(config, { + get(target, prop) { + if (prop === 'getUseAlternateBuffer') { + return () => useAlternateBuffer; + } + return Reflect.get(target, prop); + }, + }); + const mainAreaWidth = providedUiState?.mainAreaWidth ?? terminalWidth; const finalUiState = { @@ -696,8 +717,8 @@ export const renderWithProviders = async ( const wrapWithProviders = (comp: React.ReactElement) => ( - - + + @@ -708,7 +729,7 @@ export const renderWithProviders = async ( ( wrapper?: React.ComponentType<{ children: React.ReactNode }>; // Options for renderWithProviders shellFocus?: boolean; - settings?: LoadedSettings; + settings?: LoadedSettings | Partial; uiState?: Partial; width?: number; mouseEventsEnabled?: boolean; diff --git a/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx b/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx index 642eec0cde..56485bfb7c 100644 --- a/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx +++ b/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx @@ -71,6 +71,9 @@ describe('ToolActionsContext', () => { beforeEach(() => { vi.clearAllMocks(); + // Default to a pending promise to avoid unwanted async state updates in tests + // that don't specifically test the IdeClient initialization. + vi.mocked(IdeClient.getInstance).mockReturnValue(new Promise(() => {})); }); const wrapper = ({ children }: { children: React.ReactNode }) => ( diff --git a/packages/cli/src/ui/contexts/ToolActionsContext.tsx b/packages/cli/src/ui/contexts/ToolActionsContext.tsx index 10e063e098..cf64ffca2b 100644 --- a/packages/cli/src/ui/contexts/ToolActionsContext.tsx +++ b/packages/cli/src/ui/contexts/ToolActionsContext.tsx @@ -52,7 +52,7 @@ interface ToolActionsContextValue { const ToolActionsContext = createContext(null); -export const useToolActions = () => { +export const useToolActions = (): ToolActionsContextValue => { const context = useContext(ToolActionsContext); if (!context) { throw new Error('useToolActions must be used within a ToolActionsProvider'); @@ -77,24 +77,23 @@ export const ToolActionsProvider: React.FC = ( useEffect(() => { let isMounted = true; + let activeClient: IdeClient | null = null; + + const handleStatusChange = () => { + if (isMounted && activeClient) { + setIsDiffingEnabled(activeClient.isDiffingEnabled()); + } + }; + if (config.getIdeMode()) { IdeClient.getInstance() .then((client) => { if (!isMounted) return; + activeClient = client; setIdeClient(client); setIsDiffingEnabled(client.isDiffingEnabled()); - const handleStatusChange = () => { - if (isMounted) { - setIsDiffingEnabled(client.isDiffingEnabled()); - } - }; - client.addStatusChangeListener(handleStatusChange); - // Return a cleanup function for the listener - return () => { - client.removeStatusChangeListener(handleStatusChange); - }; }) .catch((error) => { debugLogger.error('Failed to get IdeClient instance:', error); @@ -102,6 +101,9 @@ export const ToolActionsProvider: React.FC = ( } return () => { isMounted = false; + if (activeClient) { + activeClient.removeStatusChangeListener(handleStatusChange); + } }; }, [config]); @@ -164,7 +166,13 @@ export const ToolActionsProvider: React.FC = ( ); return ( - + {children} ); diff --git a/packages/core/src/tools/tools.ts b/packages/core/src/tools/tools.ts index 23e88b608b..0ec4da977b 100644 --- a/packages/core/src/tools/tools.ts +++ b/packages/core/src/tools/tools.ts @@ -920,7 +920,6 @@ export const isListResult = ( res: unknown, ): res is ListDirectoryResult | ReadManyFilesResult => isStructuredToolResult(res) && 'files' in res && Array.isArray(res.files); - export type ToolResultDisplay = | string | FileDiff