mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-09 21:00:56 -07:00
Add low/full CLI error verbosity mode for cleaner UI (#20399)
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
GLOB_DISPLAY_NAME,
|
||||
} from '@google/gemini-cli-core';
|
||||
import os from 'node:os';
|
||||
import { createMockSettings } from '../../../test-utils/settings.js';
|
||||
|
||||
describe('<ToolGroupMessage />', () => {
|
||||
afterEach(() => {
|
||||
@@ -64,6 +65,11 @@ describe('<ToolGroupMessage />', () => {
|
||||
ideMode: false,
|
||||
enableInteractiveShell: true,
|
||||
});
|
||||
const fullVerbositySettings = createMockSettings({
|
||||
merged: {
|
||||
ui: { errorVerbosity: 'full' },
|
||||
},
|
||||
});
|
||||
|
||||
describe('Golden Snapshots', () => {
|
||||
it('renders single successful tool call', async () => {
|
||||
@@ -73,6 +79,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -104,7 +111,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
|
||||
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{ config: baseMockConfig },
|
||||
{ config: baseMockConfig, settings: fullVerbositySettings },
|
||||
);
|
||||
|
||||
// Should render nothing because all tools in the group are confirming
|
||||
@@ -140,6 +147,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -160,6 +168,76 @@ describe('<ToolGroupMessage />', () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('hides errored tool calls in low error verbosity mode', async () => {
|
||||
const toolCalls = [
|
||||
createToolCall({
|
||||
callId: 'tool-1',
|
||||
name: 'successful-tool',
|
||||
status: CoreToolCallStatus.Success,
|
||||
}),
|
||||
createToolCall({
|
||||
callId: 'tool-2',
|
||||
name: 'error-tool',
|
||||
status: CoreToolCallStatus.Error,
|
||||
resultDisplay: 'Tool failed',
|
||||
}),
|
||||
];
|
||||
const item = createItem(toolCalls);
|
||||
|
||||
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
type: 'tool_group',
|
||||
tools: toolCalls,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
await waitUntilReady();
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('successful-tool');
|
||||
expect(output).not.toContain('error-tool');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('keeps client-initiated errored tool calls visible in low error verbosity mode', async () => {
|
||||
const toolCalls = [
|
||||
createToolCall({
|
||||
callId: 'tool-1',
|
||||
name: 'client-error-tool',
|
||||
status: CoreToolCallStatus.Error,
|
||||
isClientInitiated: true,
|
||||
resultDisplay: 'Client tool failed',
|
||||
}),
|
||||
];
|
||||
const item = createItem(toolCalls);
|
||||
|
||||
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
type: 'tool_group',
|
||||
tools: toolCalls,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('client-error-tool');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('renders mixed tool calls including shell command', async () => {
|
||||
const toolCalls = [
|
||||
createToolCall({
|
||||
@@ -187,6 +265,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -233,6 +312,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -266,6 +346,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -288,6 +369,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -326,6 +408,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
</Scrollable>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -356,6 +439,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -406,6 +490,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
</Scrollable>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -439,6 +524,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -468,6 +554,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -510,6 +597,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
uiState: {
|
||||
pendingHistoryItems: [
|
||||
{
|
||||
@@ -569,7 +657,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
|
||||
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{ config: baseMockConfig },
|
||||
{ config: baseMockConfig, settings: fullVerbositySettings },
|
||||
);
|
||||
await waitUntilReady();
|
||||
|
||||
@@ -599,7 +687,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
|
||||
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{ config: baseMockConfig },
|
||||
{ config: baseMockConfig, settings: fullVerbositySettings },
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
@@ -627,7 +715,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
toolCalls={toolCalls}
|
||||
borderBottom={false}
|
||||
/>,
|
||||
{ config: baseMockConfig },
|
||||
{ config: baseMockConfig, settings: fullVerbositySettings },
|
||||
);
|
||||
// AskUser tools in progress are rendered by AskUserDialog, so we expect nothing.
|
||||
await waitUntilReady();
|
||||
@@ -665,7 +753,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
|
||||
const { lastFrame, unmount, waitUntilReady } = renderWithProviders(
|
||||
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
|
||||
{ config: baseMockConfig },
|
||||
{ config: baseMockConfig, settings: fullVerbositySettings },
|
||||
);
|
||||
|
||||
await waitUntilReady();
|
||||
@@ -697,6 +785,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
useAlternateBuffer: true,
|
||||
uiState: {
|
||||
constrainHeight: true,
|
||||
@@ -728,6 +817,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
useAlternateBuffer: true,
|
||||
uiState: {
|
||||
constrainHeight: true,
|
||||
@@ -760,6 +850,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
useAlternateBuffer: true,
|
||||
uiState: {
|
||||
constrainHeight: true,
|
||||
@@ -791,6 +882,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
useAlternateBuffer: true,
|
||||
uiState: {
|
||||
constrainHeight: true,
|
||||
@@ -818,6 +910,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
useAlternateBuffer: true,
|
||||
uiState: {
|
||||
constrainHeight: false,
|
||||
@@ -850,6 +943,7 @@ describe('<ToolGroupMessage />', () => {
|
||||
/>,
|
||||
{
|
||||
config: baseMockConfig,
|
||||
settings: fullVerbositySettings,
|
||||
useAlternateBuffer: true,
|
||||
uiState: {
|
||||
constrainHeight: true,
|
||||
|
||||
@@ -18,7 +18,10 @@ import { ShellToolMessage } from './ShellToolMessage.js';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import { useConfig } from '../../contexts/ConfigContext.js';
|
||||
import { isShellTool, isThisShellFocused } from './ToolShared.js';
|
||||
import { shouldHideToolCall } from '@google/gemini-cli-core';
|
||||
import {
|
||||
shouldHideToolCall,
|
||||
CoreToolCallStatus,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { ShowMoreLines } from '../ShowMoreLines.js';
|
||||
import { useUIState } from '../../contexts/UIStateContext.js';
|
||||
import { useAlternateBuffer } from '../../hooks/useAlternateBuffer.js';
|
||||
@@ -27,6 +30,7 @@ import {
|
||||
calculateToolContentMaxLines,
|
||||
} from '../../utils/toolLayoutUtils.js';
|
||||
import { getToolGroupBorderAppearance } from '../../utils/borderStyles.js';
|
||||
import { useSettings } from '../../contexts/SettingsContext.js';
|
||||
|
||||
interface ToolGroupMessageProps {
|
||||
item: HistoryItem | HistoryItemWithoutId;
|
||||
@@ -51,19 +55,29 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
|
||||
borderBottom: borderBottomOverride,
|
||||
isExpandable,
|
||||
}) => {
|
||||
const settings = useSettings();
|
||||
const isLowErrorVerbosity = settings.merged.ui?.errorVerbosity !== 'full';
|
||||
|
||||
// Filter out tool calls that should be hidden (e.g. in-progress Ask User, or Plan Mode operations).
|
||||
const toolCalls = useMemo(
|
||||
() =>
|
||||
allToolCalls.filter(
|
||||
(t) =>
|
||||
!shouldHideToolCall({
|
||||
displayName: t.name,
|
||||
status: t.status,
|
||||
approvalMode: t.approvalMode,
|
||||
hasResultDisplay: !!t.resultDisplay,
|
||||
}),
|
||||
),
|
||||
[allToolCalls],
|
||||
allToolCalls.filter((t) => {
|
||||
if (
|
||||
isLowErrorVerbosity &&
|
||||
t.status === CoreToolCallStatus.Error &&
|
||||
!t.isClientInitiated
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !shouldHideToolCall({
|
||||
displayName: t.name,
|
||||
status: t.status,
|
||||
approvalMode: t.approvalMode,
|
||||
hasResultDisplay: !!t.resultDisplay,
|
||||
});
|
||||
}),
|
||||
[allToolCalls, isLowErrorVerbosity],
|
||||
);
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
@@ -50,10 +50,14 @@ describe('ToolResultDisplay Overflow', () => {
|
||||
|
||||
await waitUntilReady();
|
||||
|
||||
// ResizeObserver might take a tick, though ToolGroupMessage calculates overflow synchronously
|
||||
// In ASB mode the overflow hint can render before the scroll position
|
||||
// settles. Wait for both the hint and the tail of the content so this
|
||||
// snapshot is deterministic across slower CI runners.
|
||||
await waitFor(() => {
|
||||
const frame = lastFrame();
|
||||
expect(frame.toLowerCase()).toContain('press ctrl+o to show more lines');
|
||||
expect(frame).toBeDefined();
|
||||
expect(frame?.toLowerCase()).toContain('press ctrl+o to show more lines');
|
||||
expect(frame).toContain('line 50');
|
||||
});
|
||||
|
||||
const frame = lastFrame();
|
||||
|
||||
Reference in New Issue
Block a user