diff options
Diffstat (limited to 'packages/cli/src/ui/hooks')
| -rw-r--r-- | packages/cli/src/ui/hooks/slashCommandProcessor.test.ts | 94 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/slashCommandProcessor.ts | 15 |
2 files changed, 109 insertions, 0 deletions
diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts index 42c2e277..30a14815 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts @@ -4,6 +4,21 @@ * SPDX-License-Identifier: Apache-2.0 */ +const { logSlashCommand, SlashCommandEvent } = vi.hoisted(() => ({ + logSlashCommand: vi.fn(), + SlashCommandEvent: vi.fn((command, subCommand) => ({ command, subCommand })), +})); + +vi.mock('@google/gemini-cli-core', async (importOriginal) => { + const original = + await importOriginal<typeof import('@google/gemini-cli-core')>(); + return { + ...original, + logSlashCommand, + SlashCommandEvent, + }; +}); + const { mockProcessExit } = vi.hoisted(() => ({ mockProcessExit: vi.fn((_code?: number): never => undefined as never), })); @@ -814,4 +829,83 @@ describe('useSlashCommandProcessor', () => { expect(abortSpy).toHaveBeenCalledTimes(1); }); }); + + describe('Slash Command Logging', () => { + const mockCommandAction = vi.fn().mockResolvedValue({ type: 'handled' }); + const loggingTestCommands: SlashCommand[] = [ + createTestCommand({ + name: 'logtest', + action: mockCommandAction, + }), + createTestCommand({ + name: 'logwithsub', + subCommands: [ + createTestCommand({ + name: 'sub', + action: mockCommandAction, + }), + ], + }), + createTestCommand({ + name: 'logalias', + altNames: ['la'], + action: mockCommandAction, + }), + ]; + + beforeEach(() => { + mockCommandAction.mockClear(); + vi.mocked(logSlashCommand).mockClear(); + vi.mocked(SlashCommandEvent).mockClear(); + }); + + it('should log a simple slash command', async () => { + const result = setupProcessorHook(loggingTestCommands); + await waitFor(() => + expect(result.current.slashCommands.length).toBeGreaterThan(0), + ); + await act(async () => { + await result.current.handleSlashCommand('/logtest'); + }); + + expect(logSlashCommand).toHaveBeenCalledTimes(1); + expect(SlashCommandEvent).toHaveBeenCalledWith('logtest', undefined); + }); + + it('should log a slash command with a subcommand', async () => { + const result = setupProcessorHook(loggingTestCommands); + await waitFor(() => + expect(result.current.slashCommands.length).toBeGreaterThan(0), + ); + await act(async () => { + await result.current.handleSlashCommand('/logwithsub sub'); + }); + + expect(logSlashCommand).toHaveBeenCalledTimes(1); + expect(SlashCommandEvent).toHaveBeenCalledWith('logwithsub', 'sub'); + }); + + it('should log the command path when an alias is used', async () => { + const result = setupProcessorHook(loggingTestCommands); + await waitFor(() => + expect(result.current.slashCommands.length).toBeGreaterThan(0), + ); + await act(async () => { + await result.current.handleSlashCommand('/la'); + }); + expect(logSlashCommand).toHaveBeenCalledTimes(1); + expect(SlashCommandEvent).toHaveBeenCalledWith('logalias', undefined); + }); + + it('should not log for unknown commands', async () => { + const result = setupProcessorHook(loggingTestCommands); + await waitFor(() => + expect(result.current.slashCommands.length).toBeGreaterThan(0), + ); + await act(async () => { + await result.current.handleSlashCommand('/unknown'); + }); + expect(logSlashCommand).not.toHaveBeenCalled(); + }); + }); }); diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index be32de11..e315ba97 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -13,6 +13,8 @@ import { Config, GitService, Logger, + logSlashCommand, + SlashCommandEvent, ToolConfirmationOutcome, } from '@google/gemini-cli-core'; import { useSessionStats } from '../contexts/SessionContext.js'; @@ -233,6 +235,7 @@ export const useSlashCommandProcessor = ( let currentCommands = commands; let commandToExecute: SlashCommand | undefined; let pathIndex = 0; + const canonicalPath: string[] = []; for (const part of commandPath) { // TODO: For better performance and architectural clarity, this two-pass @@ -253,6 +256,7 @@ export const useSlashCommandProcessor = ( if (foundCommand) { commandToExecute = foundCommand; + canonicalPath.push(foundCommand.name); pathIndex++; if (foundCommand.subCommands) { currentCommands = foundCommand.subCommands; @@ -268,6 +272,17 @@ export const useSlashCommandProcessor = ( const args = parts.slice(pathIndex).join(' '); if (commandToExecute.action) { + if (config) { + const resolvedCommandPath = canonicalPath; + const event = new SlashCommandEvent( + resolvedCommandPath[0], + resolvedCommandPath.length > 1 + ? resolvedCommandPath.slice(1).join(' ') + : undefined, + ); + logSlashCommand(config, event); + } + const fullCommandContext: CommandContext = { ...commandContext, invocation: { |
