Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion docs/cli/plan-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Other ways to start in Plan Mode:
You can enter Plan Mode in three ways:

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.
3. **Natural Language:** Ask the agent to "start a plan for...".

Expand Down
22 changes: 6 additions & 16 deletions packages/cli/src/ui/components/ApprovalModeIndicator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ describe('ApprovalModeIndicator', () => {
const { lastFrame } = render(
<ApprovalModeIndicator approvalMode={ApprovalMode.AUTO_EDIT} />,
);
const output = lastFrame();
expect(output).toContain('auto-accept edits');
expect(output).toContain('shift+tab to manual');
expect(lastFrame()).toMatchSnapshot();
});

it('renders correctly for AUTO_EDIT mode with plan enabled', () => {
Expand All @@ -26,35 +24,28 @@ describe('ApprovalModeIndicator', () => {
isPlanEnabled={true}
/>,
);
const output = lastFrame();
expect(output).toContain('auto-accept edits');
expect(output).toContain('shift+tab to manual');
expect(lastFrame()).toMatchSnapshot();
});

it('renders correctly for PLAN mode', () => {
const { lastFrame } = render(
<ApprovalModeIndicator approvalMode={ApprovalMode.PLAN} />,
);
const output = lastFrame();
expect(output).toContain('plan');
expect(output).toContain('shift+tab to accept edits');
expect(lastFrame()).toMatchSnapshot();
});

it('renders correctly for YOLO mode', () => {
const { lastFrame } = render(
<ApprovalModeIndicator approvalMode={ApprovalMode.YOLO} />,
);
const output = lastFrame();
expect(output).toContain('YOLO');
expect(output).toContain('ctrl+y');
expect(lastFrame()).toMatchSnapshot();
});

it('renders correctly for DEFAULT mode', () => {
const { lastFrame } = render(
<ApprovalModeIndicator approvalMode={ApprovalMode.DEFAULT} />,
);
const output = lastFrame();
expect(output).toContain('shift+tab to accept edits');
expect(lastFrame()).toMatchSnapshot();
});

it('renders correctly for DEFAULT mode with plan enabled', () => {
Expand All @@ -64,7 +55,6 @@ describe('ApprovalModeIndicator', () => {
isPlanEnabled={true}
/>,
);
const output = lastFrame();
expect(output).toContain('shift+tab to plan');
expect(lastFrame()).toMatchSnapshot();
});
});
28 changes: 19 additions & 9 deletions packages/cli/src/ui/components/ApprovalModeIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ interface ApprovalModeIndicatorProps {
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> = ({
approvalMode,
isPlanEnabled,
Expand All @@ -25,26 +35,26 @@ export const ApprovalModeIndicator: React.FC<ApprovalModeIndicatorProps> = ({
switch (approvalMode) {
case ApprovalMode.AUTO_EDIT:
textColor = theme.status.warning;
textContent = 'auto-accept edits';
subText = 'shift+tab to manual';
textContent = APPROVAL_MODE_TEXT.AUTO_EDIT;
subText = isPlanEnabled
? APPROVAL_MODE_TEXT.HINT_SWITCH_TO_PLAN_MODE
: APPROVAL_MODE_TEXT.HINT_SWITCH_TO_MANUAL_MODE;
break;
case ApprovalMode.PLAN:
textColor = theme.status.success;
textContent = 'plan';
subText = 'shift+tab to accept edits';
textContent = APPROVAL_MODE_TEXT.PLAN;
subText = APPROVAL_MODE_TEXT.HINT_SWITCH_TO_MANUAL_MODE;
break;
case ApprovalMode.YOLO:
textColor = theme.status.error;
textContent = 'YOLO';
subText = 'ctrl+y';
textContent = APPROVAL_MODE_TEXT.YOLO;
subText = APPROVAL_MODE_TEXT.HINT_SWITCH_TO_YOLO_MODE;
break;
case ApprovalMode.DEFAULT:
default:
textColor = theme.text.accent;
textContent = '';
subText = isPlanEnabled
? 'shift+tab to plan'
: 'shift+tab to accept edits';
subText = APPROVAL_MODE_TEXT.HINT_SWITCH_TO_AUTO_EDIT_MODE;
break;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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"`;
14 changes: 7 additions & 7 deletions packages/cli/src/ui/hooks/useApprovalModeIndicator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ describe('useApprovalModeIndicator', () => {
);
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(() => {
capturedUseKeypressHandler({
name: 'tab',
Expand Down Expand Up @@ -236,7 +236,7 @@ describe('useApprovalModeIndicator', () => {
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.isPlanEnabled.mockReturnValue(true);
renderHook(() =>
Expand All @@ -246,23 +246,23 @@ describe('useApprovalModeIndicator', () => {
}),
);

// DEFAULT -> PLAN
// DEFAULT -> AUTO_EDIT
act(() => {
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
});
expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.PLAN,
ApprovalMode.AUTO_EDIT,
);

// PLAN -> AUTO_EDIT
// AUTO_EDIT -> PLAN
act(() => {
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
});
expect(mockConfigInstance.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.AUTO_EDIT,
ApprovalMode.PLAN,
);

// AUTO_EDIT -> DEFAULT
// PLAN -> DEFAULT
act(() => {
capturedUseKeypressHandler({ name: 'tab', shift: true } as Key);
});
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/ui/hooks/useApprovalModeIndicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ export function useApprovalModeIndicator({
const currentMode = config.getApprovalMode();
switch (currentMode) {
case ApprovalMode.DEFAULT:
nextApprovalMode = ApprovalMode.AUTO_EDIT;
break;
case ApprovalMode.AUTO_EDIT:
nextApprovalMode = config.isPlanEnabled()
? ApprovalMode.PLAN
: ApprovalMode.AUTO_EDIT;
: ApprovalMode.DEFAULT;
break;
case ApprovalMode.PLAN:
nextApprovalMode = ApprovalMode.AUTO_EDIT;
break;
case ApprovalMode.AUTO_EDIT:
nextApprovalMode = ApprovalMode.DEFAULT;
break;
case ApprovalMode.YOLO:
Expand Down
Loading