Skip to content

Commit 73784bd

Browse files
committed
feat: allow exit and quit commands without leading slash
1 parent a6f55f2 commit 73784bd

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

docs/cli/commands.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ Slash commands provide meta-level control over the CLI itself.
215215
purposes.
216216

217217
- **`/quit`** (or **`/exit`**)
218-
- **Description:** Exit Gemini CLI.
218+
- **Description:** Exit Gemini CLI. You can also type `quit` or `exit` without
219+
the leading slash.
219220

220221
- **`/vim`**
221222
- **Description:** Toggle vim mode on or off. When vim mode is enabled, the

packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,25 @@ describe('useSlashCommandProcessor', () => {
573573

574574
expect(mockSetQuittingMessages).toHaveBeenCalledWith(['bye']);
575575
});
576+
577+
it('should handle "exit" command without a slash', async () => {
578+
const quitAction = vi
579+
.fn()
580+
.mockResolvedValue({ type: 'quit', messages: ['bye'] });
581+
const command = createTestCommand({
582+
name: 'exit',
583+
action: quitAction,
584+
});
585+
const result = await setupProcessorHook([command]);
586+
587+
await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
588+
589+
await act(async () => {
590+
await result.current.handleSlashCommand('exit');
591+
});
592+
593+
expect(quitAction).toHaveBeenCalled();
594+
});
576595
it('should handle "submit_prompt" action returned from a file-based command', async () => {
577596
const fileCommand = createTestCommand(
578597
{

packages/cli/src/ui/hooks/slashCommandProcessor.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,12 @@ export const useSlashCommandProcessor = (
321321
}
322322

323323
const trimmed = rawQuery.trim();
324-
if (!trimmed.startsWith('/') && !trimmed.startsWith('?')) {
324+
const isExitOrQuit = ['exit', 'quit'].includes(trimmed.toLowerCase());
325+
if (
326+
!trimmed.startsWith('/') &&
327+
!trimmed.startsWith('?') &&
328+
!isExitOrQuit
329+
) {
325330
return false;
326331
}
327332

@@ -336,11 +341,12 @@ export const useSlashCommandProcessor = (
336341
}
337342

338343
let hasError = false;
344+
const commandToParse = isExitOrQuit ? `/${trimmed}` : trimmed;
339345
const {
340346
commandToExecute,
341347
args,
342348
canonicalPath: resolvedCommandPath,
343-
} = parseSlashCommand(trimmed, commands);
349+
} = parseSlashCommand(commandToParse, commands);
344350

345351
const subcommand =
346352
resolvedCommandPath.length > 1

0 commit comments

Comments
 (0)