feat(cli): Add /model command for interactive model selection (#8940)

Co-authored-by: Miguel Solorio <miguel.solorio07@gmail.com>
This commit is contained in:
Abhi
2025-09-23 12:50:09 -04:00
committed by GitHub
parent c96f8259c1
commit 5151bedf06
19 changed files with 743 additions and 1 deletions
@@ -107,6 +107,7 @@ describe('useSlashCommandProcessor', () => {
const mockLoadHistory = vi.fn();
const mockOpenThemeDialog = vi.fn();
const mockOpenAuthDialog = vi.fn();
const mockOpenModelDialog = vi.fn();
const mockSetQuittingMessages = vi.fn();
const mockConfig = makeFakeConfig({});
@@ -147,6 +148,7 @@ describe('useSlashCommandProcessor', () => {
openEditorDialog: vi.fn(),
openPrivacyNotice: vi.fn(),
openSettingsDialog: vi.fn(),
openModelDialog: mockOpenModelDialog,
quit: mockSetQuittingMessages,
setDebugMessage: vi.fn(),
toggleCorgiMode: vi.fn(),
@@ -391,6 +393,21 @@ describe('useSlashCommandProcessor', () => {
expect(mockOpenThemeDialog).toHaveBeenCalled();
});
it('should handle "dialog: model" action', async () => {
const command = createTestCommand({
name: 'modelcmd',
action: vi.fn().mockResolvedValue({ type: 'dialog', dialog: 'model' }),
});
const result = setupProcessorHook([command]);
await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
await act(async () => {
await result.current.handleSlashCommand('/modelcmd');
});
expect(mockOpenModelDialog).toHaveBeenCalled();
});
it('should handle "load_history" action', async () => {
const mockClient = {
setHistory: vi.fn(),
@@ -49,6 +49,7 @@ interface SlashCommandProcessorActions {
openEditorDialog: () => void;
openPrivacyNotice: () => void;
openSettingsDialog: () => void;
openModelDialog: () => void;
openPermissionsDialog: () => void;
quit: (messages: HistoryItem[]) => void;
setDebugMessage: (message: string) => void;
@@ -374,6 +375,9 @@ export const useSlashCommandProcessor = (
case 'settings':
actions.openSettingsDialog();
return { type: 'handled' };
case 'model':
actions.openModelDialog();
return { type: 'handled' };
case 'permissions':
actions.openPermissionsDialog();
return { type: 'handled' };
@@ -0,0 +1,42 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { useModelCommand } from './useModelCommand.js';
describe('useModelCommand', () => {
it('should initialize with the model dialog closed', () => {
const { result } = renderHook(() => useModelCommand());
expect(result.current.isModelDialogOpen).toBe(false);
});
it('should open the model dialog when openModelDialog is called', () => {
const { result } = renderHook(() => useModelCommand());
act(() => {
result.current.openModelDialog();
});
expect(result.current.isModelDialogOpen).toBe(true);
});
it('should close the model dialog when closeModelDialog is called', () => {
const { result } = renderHook(() => useModelCommand());
// Open it first
act(() => {
result.current.openModelDialog();
});
expect(result.current.isModelDialogOpen).toBe(true);
// Then close it
act(() => {
result.current.closeModelDialog();
});
expect(result.current.isModelDialogOpen).toBe(false);
});
});
@@ -0,0 +1,31 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useCallback } from 'react';
interface UseModelCommandReturn {
isModelDialogOpen: boolean;
openModelDialog: () => void;
closeModelDialog: () => void;
}
export const useModelCommand = (): UseModelCommandReturn => {
const [isModelDialogOpen, setIsModelDialogOpen] = useState(false);
const openModelDialog = useCallback(() => {
setIsModelDialogOpen(true);
}, []);
const closeModelDialog = useCallback(() => {
setIsModelDialogOpen(false);
}, []);
return {
isModelDialogOpen,
openModelDialog,
closeModelDialog,
};
};