mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-11 14:40:52 -07:00
feat(cli): update approval mode cycle order (#19254)
This commit is contained in:
@@ -61,7 +61,7 @@ Other ways to start in Plan Mode:
|
|||||||
You can enter Plan Mode in three ways:
|
You can enter Plan Mode in three ways:
|
||||||
|
|
||||||
1. **Keyboard Shortcut:** Press `Shift+Tab` to cycle through approval modes
|
1. **Keyboard Shortcut:** Press `Shift+Tab` to cycle through approval modes
|
||||||
(`Default` -> `Plan` -> `Auto-Edit`).
|
(`Default` -> `Auto-Edit` -> `Plan`).
|
||||||
2. **Command:** Type `/plan` in the input box.
|
2. **Command:** Type `/plan` in the input box.
|
||||||
3. **Natural Language:** Ask the agent to "start a plan for...". The agent will
|
3. **Natural Language:** Ask the agent to "start a plan for...". The agent will
|
||||||
then call the [`enter_plan_mode`] tool to switch modes.
|
then call the [`enter_plan_mode`] tool to switch modes.
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ describe('ApprovalModeIndicator', () => {
|
|||||||
const { lastFrame } = render(
|
const { lastFrame } = render(
|
||||||
<ApprovalModeIndicator approvalMode={ApprovalMode.AUTO_EDIT} />,
|
<ApprovalModeIndicator approvalMode={ApprovalMode.AUTO_EDIT} />,
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect(output).toContain('auto-accept edits');
|
|
||||||
expect(output).toContain('shift+tab to manual');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly for AUTO_EDIT mode with plan enabled', () => {
|
it('renders correctly for AUTO_EDIT mode with plan enabled', () => {
|
||||||
@@ -26,35 +24,28 @@ describe('ApprovalModeIndicator', () => {
|
|||||||
isPlanEnabled={true}
|
isPlanEnabled={true}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect(output).toContain('auto-accept edits');
|
|
||||||
expect(output).toContain('shift+tab to manual');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly for PLAN mode', () => {
|
it('renders correctly for PLAN mode', () => {
|
||||||
const { lastFrame } = render(
|
const { lastFrame } = render(
|
||||||
<ApprovalModeIndicator approvalMode={ApprovalMode.PLAN} />,
|
<ApprovalModeIndicator approvalMode={ApprovalMode.PLAN} />,
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect(output).toContain('plan');
|
|
||||||
expect(output).toContain('shift+tab to accept edits');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly for YOLO mode', () => {
|
it('renders correctly for YOLO mode', () => {
|
||||||
const { lastFrame } = render(
|
const { lastFrame } = render(
|
||||||
<ApprovalModeIndicator approvalMode={ApprovalMode.YOLO} />,
|
<ApprovalModeIndicator approvalMode={ApprovalMode.YOLO} />,
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect(output).toContain('YOLO');
|
|
||||||
expect(output).toContain('ctrl+y');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly for DEFAULT mode', () => {
|
it('renders correctly for DEFAULT mode', () => {
|
||||||
const { lastFrame } = render(
|
const { lastFrame } = render(
|
||||||
<ApprovalModeIndicator approvalMode={ApprovalMode.DEFAULT} />,
|
<ApprovalModeIndicator approvalMode={ApprovalMode.DEFAULT} />,
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect(output).toContain('shift+tab to accept edits');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly for DEFAULT mode with plan enabled', () => {
|
it('renders correctly for DEFAULT mode with plan enabled', () => {
|
||||||
@@ -64,7 +55,6 @@ describe('ApprovalModeIndicator', () => {
|
|||||||
isPlanEnabled={true}
|
isPlanEnabled={true}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
expect(output).toContain('shift+tab to plan');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ interface ApprovalModeIndicatorProps {
|
|||||||
isPlanEnabled?: boolean;
|
isPlanEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const APPROVAL_MODE_TEXT = {
|
||||||
|
AUTO_EDIT: 'auto-accept edits',
|
||||||
|
PLAN: 'plan',
|
||||||
|
YOLO: 'YOLO',
|
||||||
|
HINT_SWITCH_TO_PLAN_MODE: 'shift+tab to plan',
|
||||||
|
HINT_SWITCH_TO_MANUAL_MODE: 'shift+tab to manual',
|
||||||
|
HINT_SWITCH_TO_AUTO_EDIT_MODE: 'shift+tab to accept edits',
|
||||||
|
HINT_SWITCH_TO_YOLO_MODE: 'ctrl+y',
|
||||||
|
};
|
||||||
|
|
||||||
export const ApprovalModeIndicator: React.FC<ApprovalModeIndicatorProps> = ({
|
export const ApprovalModeIndicator: React.FC<ApprovalModeIndicatorProps> = ({
|
||||||
approvalMode,
|
approvalMode,
|
||||||
isPlanEnabled,
|
isPlanEnabled,
|
||||||
@@ -25,26 +35,26 @@ export const ApprovalModeIndicator: React.FC<ApprovalModeIndicatorProps> = ({
|
|||||||
switch (approvalMode) {
|
switch (approvalMode) {
|
||||||
case ApprovalMode.AUTO_EDIT:
|
case ApprovalMode.AUTO_EDIT:
|
||||||
textColor = theme.status.warning;
|
textColor = theme.status.warning;
|
||||||
textContent = 'auto-accept edits';
|
textContent = APPROVAL_MODE_TEXT.AUTO_EDIT;
|
||||||
subText = 'shift+tab to manual';
|
subText = isPlanEnabled
|
||||||
|
? APPROVAL_MODE_TEXT.HINT_SWITCH_TO_PLAN_MODE
|
||||||
|
: APPROVAL_MODE_TEXT.HINT_SWITCH_TO_MANUAL_MODE;
|
||||||
break;
|
break;
|
||||||
case ApprovalMode.PLAN:
|
case ApprovalMode.PLAN:
|
||||||
textColor = theme.status.success;
|
textColor = theme.status.success;
|
||||||
textContent = 'plan';
|
textContent = APPROVAL_MODE_TEXT.PLAN;
|
||||||
subText = 'shift+tab to accept edits';
|
subText = APPROVAL_MODE_TEXT.HINT_SWITCH_TO_MANUAL_MODE;
|
||||||
break;
|
break;
|
||||||
case ApprovalMode.YOLO:
|
case ApprovalMode.YOLO:
|
||||||
textColor = theme.status.error;
|
textColor = theme.status.error;
|
||||||
textContent = 'YOLO';
|
textContent = APPROVAL_MODE_TEXT.YOLO;
|
||||||
subText = 'ctrl+y';
|
subText = APPROVAL_MODE_TEXT.HINT_SWITCH_TO_YOLO_MODE;
|
||||||
break;
|
break;
|
||||||
case ApprovalMode.DEFAULT:
|
case ApprovalMode.DEFAULT:
|
||||||
default:
|
default:
|
||||||
textColor = theme.text.accent;
|
textColor = theme.text.accent;
|
||||||
textContent = '';
|
textContent = '';
|
||||||
subText = isPlanEnabled
|
subText = APPROVAL_MODE_TEXT.HINT_SWITCH_TO_AUTO_EDIT_MODE;
|
||||||
? 'shift+tab to plan'
|
|
||||||
: 'shift+tab to accept edits';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`ApprovalModeIndicator > renders correctly for AUTO_EDIT mode 1`] = `"auto-accept edits shift+tab to manual"`;
|
||||||
|
|
||||||
|
exports[`ApprovalModeIndicator > renders correctly for AUTO_EDIT mode with plan enabled 1`] = `"auto-accept edits shift+tab to plan"`;
|
||||||
|
|
||||||
|
exports[`ApprovalModeIndicator > renders correctly for DEFAULT mode 1`] = `"shift+tab to accept edits"`;
|
||||||
|
|
||||||
|
exports[`ApprovalModeIndicator > renders correctly for DEFAULT mode with plan enabled 1`] = `"shift+tab to accept edits"`;
|
||||||
|
|
||||||
|
exports[`ApprovalModeIndicator > renders correctly for PLAN mode 1`] = `"plan shift+tab to manual"`;
|
||||||
|
|
||||||
|
exports[`ApprovalModeIndicator > renders correctly for YOLO mode 1`] = `"YOLO ctrl+y"`;
|
||||||
@@ -202,7 +202,7 @@ describe('useApprovalModeIndicator', () => {
|
|||||||
);
|
);
|
||||||
expect(result.current).toBe(ApprovalMode.YOLO);
|
expect(result.current).toBe(ApprovalMode.YOLO);
|
||||||
|
|
||||||
// Shift+Tab cycles back to DEFAULT (since PLAN is disabled by default in mock)
|
// Shift+Tab cycles back to AUTO_EDIT (from YOLO)
|
||||||
act(() => {
|
act(() => {
|
||||||
capturedUseKeypressHandler({
|
capturedUseKeypressHandler({
|
||||||
name: 'tab',
|
name: 'tab',
|
||||||
@@ -236,7 +236,7 @@ describe('useApprovalModeIndicator', () => {
|
|||||||
expect(result.current).toBe(ApprovalMode.AUTO_EDIT);
|
expect(result.current).toBe(ApprovalMode.AUTO_EDIT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should cycle through DEFAULT -> PLAN -> AUTO_EDIT -> DEFAULT when plan is enabled', () => {
|
it('should cycle through DEFAULT -> AUTO_EDIT -> PLAN -> DEFAULT when plan is enabled', () => {
|
||||||
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
|
mockConfigInstance.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
|
||||||
mockConfigInstance.isPlanEnabled.mockReturnValue(true);
|
mockConfigInstance.isPlanEnabled.mockReturnValue(true);
|
||||||
renderHook(() =>
|
renderHook(() =>
|
||||||
@@ -246,15 +246,7 @@ describe('useApprovalModeIndicator', () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// DEFAULT -> PLAN
|
// DEFAULT -> AUTO_EDIT
|
||||||
act(() => {
|
|
||||||
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
|
|
||||||
});
|
|
||||||
expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
|
|
||||||
ApprovalMode.PLAN,
|
|
||||||
);
|
|
||||||
|
|
||||||
// PLAN -> AUTO_EDIT
|
|
||||||
act(() => {
|
act(() => {
|
||||||
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
|
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
|
||||||
});
|
});
|
||||||
@@ -262,7 +254,15 @@ describe('useApprovalModeIndicator', () => {
|
|||||||
ApprovalMode.AUTO_EDIT,
|
ApprovalMode.AUTO_EDIT,
|
||||||
);
|
);
|
||||||
|
|
||||||
// AUTO_EDIT -> DEFAULT
|
// AUTO_EDIT -> PLAN
|
||||||
|
act(() => {
|
||||||
|
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
|
||||||
|
});
|
||||||
|
expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
|
||||||
|
ApprovalMode.PLAN,
|
||||||
|
);
|
||||||
|
|
||||||
|
// PLAN -> DEFAULT
|
||||||
act(() => {
|
act(() => {
|
||||||
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
|
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,14 +72,14 @@ export function useApprovalModeIndicator({
|
|||||||
const currentMode = config.getApprovalMode();
|
const currentMode = config.getApprovalMode();
|
||||||
switch (currentMode) {
|
switch (currentMode) {
|
||||||
case ApprovalMode.DEFAULT:
|
case ApprovalMode.DEFAULT:
|
||||||
nextApprovalMode = config.isPlanEnabled()
|
|
||||||
? ApprovalMode.PLAN
|
|
||||||
: ApprovalMode.AUTO_EDIT;
|
|
||||||
break;
|
|
||||||
case ApprovalMode.PLAN:
|
|
||||||
nextApprovalMode = ApprovalMode.AUTO_EDIT;
|
nextApprovalMode = ApprovalMode.AUTO_EDIT;
|
||||||
break;
|
break;
|
||||||
case ApprovalMode.AUTO_EDIT:
|
case ApprovalMode.AUTO_EDIT:
|
||||||
|
nextApprovalMode = config.isPlanEnabled()
|
||||||
|
? ApprovalMode.PLAN
|
||||||
|
: ApprovalMode.DEFAULT;
|
||||||
|
break;
|
||||||
|
case ApprovalMode.PLAN:
|
||||||
nextApprovalMode = ApprovalMode.DEFAULT;
|
nextApprovalMode = ApprovalMode.DEFAULT;
|
||||||
break;
|
break;
|
||||||
case ApprovalMode.YOLO:
|
case ApprovalMode.YOLO:
|
||||||
|
|||||||
Reference in New Issue
Block a user