diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index dc78c76a50..d75592f4e1 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -80,6 +80,8 @@ import { useSessionStats } from '../contexts/SessionContext.js'; import { useKeypress } from './useKeypress.js'; import type { LoadedSettings } from '../../config/settings.js'; +import { isShellTool } from '../components/messages/ToolShared.js'; + type ToolResponseWithParts = ToolCallResponseInfo & { llmContent?: PartListUnion; }; @@ -356,6 +358,8 @@ export const useGeminiStream = ( addItem, ]); + const isVerboseMode = settings.merged.output?.verbosity === 'verbose'; + const pendingToolGroupItems = useMemo((): HistoryItemWithoutId[] => { const remainingTools = toolCalls.filter( (tc) => !pushedToolCallIds.has(tc.request.callId), @@ -375,6 +379,14 @@ export const useGeminiStream = ( // Always show a bottom border slice if we have ANY tools in the batch // and we haven't finished pushing the whole batch to history yet. // Once all tools are terminal and pushed, the last history item handles the closing border. + // NOTE: In dense mode, we skip this if there are no shell tools (which require boxes). + const requiresBoxLayout = + isVerboseMode || toolCalls.some((tc) => isShellTool(tc.request.name)); + + if (!requiresBoxLayout) { + return items; + } + const allTerminal = toolCalls.length > 0 && toolCalls.every( @@ -419,7 +431,7 @@ export const useGeminiStream = ( } return items; - }, [toolCalls, pushedToolCallIds]); + }, [toolCalls, pushedToolCallIds, isVerboseMode]); const activeToolPtyId = useMemo(() => { const executingShellTool = toolCalls.find( diff --git a/packages/cli/src/ui/hooks/useHistoryManager.test.ts b/packages/cli/src/ui/hooks/useHistoryManager.test.ts index d91bf67a7c..625802bce5 100644 --- a/packages/cli/src/ui/hooks/useHistoryManager.test.ts +++ b/packages/cli/src/ui/hooks/useHistoryManager.test.ts @@ -323,4 +323,29 @@ describe('useHistoryManager', () => { expect(thirdItem.type).toBe('tool_group'); expect(thirdItem.tools).toHaveLength(1); }); + + it('should update borderBottom when merging tool groups', () => { + const { result } = renderHook(() => useHistory()); + const timestamp = Date.now(); + const toolGroup1: HistoryItemWithoutId = { + type: 'tool_group', + tools: [{ callId: '1', name: 'tool-a' } as IndividualToolCallDisplay], + borderBottom: false, + }; + const toolGroup2: HistoryItemWithoutId = { + type: 'tool_group', + tools: [{ callId: '2', name: 'tool-b' } as IndividualToolCallDisplay], + borderBottom: true, + }; + + act(() => { + result.current.addItem(toolGroup1, timestamp); + result.current.addItem(toolGroup2, timestamp + 1); + }); + + expect(result.current.history).toHaveLength(1); + const mergedItem = result.current.history[0] as HistoryItemToolGroup; + expect(mergedItem.tools).toHaveLength(2); + expect(mergedItem.borderBottom).toBe(true); + }); }); diff --git a/packages/cli/src/ui/hooks/useHistoryManager.ts b/packages/cli/src/ui/hooks/useHistoryManager.ts index 23346cf389..29e797c84c 100644 --- a/packages/cli/src/ui/hooks/useHistoryManager.ts +++ b/packages/cli/src/ui/hooks/useHistoryManager.ts @@ -78,6 +78,7 @@ export function useHistory({ const updatedLastItem: HistoryItem = { ...lastItem, tools: [...lastItem.tools, ...newItem.tools], + borderBottom: newItem.borderBottom, }; return [...prevHistory.slice(0, -1), updatedLastItem]; }