Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
af92961
feat(ui): enhance auto-approval workflow with pending tool call integ…
bulkypanda Aug 20, 2025
83c0150
fix(ui): replace forEach with Promise.all in auto-approval workflow
bulkypanda Aug 20, 2025
e8eaaf4
refactor(ui): centralize auto-approval workflow and improve sequentia…
bulkypanda Aug 21, 2025
bf50ec2
style: fix formatting in useAutoAcceptIndicator files
bulkypanda Aug 21, 2025
d79e729
test: Add comprehensive tests for handleApprovalModeChange in useGemi…
bulkypanda Aug 22, 2025
299d7c0
fix: remove duplicate ApprovalMode import in useGeminiStream.ts
bulkypanda Aug 25, 2025
056593c
fix: resolve TypeScript import issues and ToolGroupMessage spacing
bulkypanda Aug 25, 2025
ed3c432
fix: consolidate duplicate imports in useGeminiStream.ts
bulkypanda Aug 25, 2025
fa965a0
fix: resolve merge conflicts and build issues after upstream rebase
bulkypanda Aug 26, 2025
9c5d32a
Merge branch 'main' into main
jacob314 Aug 27, 2025
24482c7
refactor: centralize auto-approve logic in useGeminiStream
bulkypanda Aug 28, 2025
383f5dd
Merge upstream main and resolve conflicts
bulkypanda Sep 4, 2025
ce2cdb7
Merge upstream main and resolve App.tsx conflict
bulkypanda Sep 7, 2025
6c8bdaa
Restore missing onApprovalModeChange callback functionality
bulkypanda Sep 7, 2025
f19522e
Merge upstream main and resolve merge conflicts
bulkypanda Sep 12, 2025
ecae743
refactor: Extract edit tool names into constant
bulkypanda Sep 12, 2025
8b0202c
style: Format multi-line filter function for better readability
bulkypanda Sep 12, 2025
950f960
Merge branch 'main' into main
abhipatel12 Sep 12, 2025
3d2befc
Merge branch 'main' into main
jacob314 Sep 13, 2025
1f92b64
Merge branch 'main' into main
bulkypanda Sep 13, 2025
77b7b61
Merge branch 'main' into main
abhipatel12 Sep 13, 2025
4ca05ec
Merge branch 'main' into main
abhipatel12 Sep 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,6 @@ export const AppContainer = (props: AppContainerProps) => {

const [isConfigInitialized, setConfigInitialized] = useState(false);

// Auto-accept indicator
const showAutoAcceptIndicator = useAutoAcceptIndicator({
config,
addItem: historyManager.addItem,
});

const logger = useLogger(config.storage);
const [userMessages, setUserMessages] = useState<string[]>([]);

Expand Down Expand Up @@ -536,6 +530,7 @@ Logging in with Google... Please restart Gemini CLI to continue.
pendingHistoryItems: pendingGeminiHistoryItems,
thought,
cancelOngoingRequest,
handleApprovalModeChange,
activePtyId,
loopDetectionConfirmationRequest,
} = useGeminiStream(
Expand All @@ -560,6 +555,13 @@ Logging in with Google... Please restart Gemini CLI to continue.
shellFocused,
);

// Auto-accept indicator
const showAutoAcceptIndicator = useAutoAcceptIndicator({
config,
addItem: historyManager.addItem,
onApprovalModeChange: handleApprovalModeChange,
});

const { messageQueue, addMessage, clearQueue, getQueuedMessagesText } =
useMessageQueue({
isConfigInitialized,
Expand Down
122 changes: 121 additions & 1 deletion packages/cli/src/ui/hooks/useAutoAcceptIndicator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
import { renderHook, act } from '@testing-library/react';
import { useAutoAcceptIndicator } from './useAutoAcceptIndicator.js';

import type { Config as ActualConfigType } from '@google/gemini-cli-core';
import { Config, ApprovalMode } from '@google/gemini-cli-core';
import type { Config as ActualConfigType } from '@google/gemini-cli-core';
import type { Key } from './useKeypress.js';
import { useKeypress } from './useKeypress.js';
import { MessageType } from '../types.js';
Expand Down Expand Up @@ -470,4 +470,124 @@ describe('useAutoAcceptIndicator', () => {
expect(mockAddItem).toHaveBeenCalledTimes(2);
});
});

it('should call onApprovalModeChange when switching to YOLO mode', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);

const mockOnApprovalModeChange = vi.fn();

renderHook(() =>
useAutoAcceptIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
onApprovalModeChange: mockOnApprovalModeChange,
}),
);

act(() => {
capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key);
});

expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.YOLO,
);
expect(mockOnApprovalModeChange).toHaveBeenCalledWith(ApprovalMode.YOLO);
});

it('should call onApprovalModeChange when switching to AUTO_EDIT mode', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);

const mockOnApprovalModeChange = vi.fn();

renderHook(() =>
useAutoAcceptIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
onApprovalModeChange: mockOnApprovalModeChange,
}),
);

act(() => {
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
});

expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.AUTO_EDIT,
);
expect(mockOnApprovalModeChange).toHaveBeenCalledWith(
ApprovalMode.AUTO_EDIT,
);
});

it('should call onApprovalModeChange when switching to DEFAULT mode', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.YOLO);

const mockOnApprovalModeChange = vi.fn();

renderHook(() =>
useAutoAcceptIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
onApprovalModeChange: mockOnApprovalModeChange,
}),
);

act(() => {
capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key); // This should toggle from YOLO to DEFAULT
});

expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.DEFAULT,
);
expect(mockOnApprovalModeChange).toHaveBeenCalledWith(ApprovalMode.DEFAULT);
});

it('should not call onApprovalModeChange when callback is not provided', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);

renderHook(() =>
useAutoAcceptIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
}),
);

act(() => {
capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key);
});

expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.YOLO,
);
// Should not throw an error when callback is not provided
});

it('should handle multiple mode changes correctly', () => {
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);

const mockOnApprovalModeChange = vi.fn();

renderHook(() =>
useAutoAcceptIndicator({
config: mockConfigInstance as unknown as ActualConfigType,
onApprovalModeChange: mockOnApprovalModeChange,
}),
);

// Switch to YOLO
act(() => {
capturedUseKeypressHandler({ name: 'y', ctrl: true } as Key);
});

// Switch to AUTO_EDIT
act(() => {
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
});

expect(mockOnApprovalModeChange).toHaveBeenCalledTimes(2);
expect(mockOnApprovalModeChange).toHaveBeenNthCalledWith(
1,
ApprovalMode.YOLO,
);
expect(mockOnApprovalModeChange).toHaveBeenNthCalledWith(
2,
ApprovalMode.AUTO_EDIT,
);
});
});
23 changes: 15 additions & 8 deletions packages/cli/src/ui/hooks/useAutoAcceptIndicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { MessageType } from '../types.js';

export interface UseAutoAcceptIndicatorArgs {
config: Config;
addItem: (item: HistoryItemWithoutId, timestamp: number) => void;
addItem?: (item: HistoryItemWithoutId, timestamp: number) => void;
onApprovalModeChange?: (mode: ApprovalMode) => void;
}

export function useAutoAcceptIndicator({
config,
addItem,
onApprovalModeChange,
}: UseAutoAcceptIndicatorArgs): ApprovalMode {
const currentConfigValue = config.getApprovalMode();
const [showAutoAcceptIndicator, setShowAutoAcceptIndicator] =
Expand Down Expand Up @@ -48,14 +50,19 @@ export function useAutoAcceptIndicator({
config.setApprovalMode(nextApprovalMode);
// Update local state immediately for responsiveness
setShowAutoAcceptIndicator(nextApprovalMode);

// Notify the central handler about the approval mode change
onApprovalModeChange?.(nextApprovalMode);
} catch (e) {
addItem(
{
type: MessageType.INFO,
text: (e as Error).message,
},
Date.now(),
);
if (addItem) {
addItem(
{
type: MessageType.INFO,
text: (e as Error).message,
},
Date.now(),
);
}
}
}
},
Expand Down
Loading
Loading