diff options
| author | Abhi <[email protected]> | 2025-06-10 15:59:52 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-06-10 15:59:52 -0400 |
| commit | 9c3f34890f220456235303498736938156d7fefe (patch) | |
| tree | 463b910e7e4bac945e24748fe19bbb5875d7c8eb /packages/cli/src/ui/hooks | |
| parent | 04e2fe0bff1dc59d90dd81374a652cccc39dc625 (diff) | |
feat: Add UI for /stats slash command (#883)
Diffstat (limited to 'packages/cli/src/ui/hooks')
| -rw-r--r-- | packages/cli/src/ui/hooks/slashCommandProcessor.test.ts | 37 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/slashCommandProcessor.ts | 48 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/useGeminiStream.test.tsx | 13 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/useGeminiStream.ts | 8 |
4 files changed, 44 insertions, 62 deletions
diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts index 7466e2a6..6ec356aa 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts @@ -255,18 +255,19 @@ describe('useSlashCommandProcessor', () => { describe('/stats command', () => { it('should show detailed session statistics', async () => { // Arrange + const cumulativeStats = { + totalTokenCount: 900, + promptTokenCount: 200, + candidatesTokenCount: 400, + cachedContentTokenCount: 100, + turnCount: 1, + toolUsePromptTokenCount: 50, + thoughtsTokenCount: 150, + }; mockUseSessionStats.mockReturnValue({ stats: { sessionStartTime: new Date('2025-01-01T00:00:00.000Z'), - cumulative: { - totalTokenCount: 900, - promptTokenCount: 200, - candidatesTokenCount: 400, - cachedContentTokenCount: 100, - turnCount: 1, - toolUsePromptTokenCount: 50, - thoughtsTokenCount: 150, - }, + cumulative: cumulativeStats, }, }); @@ -280,24 +281,12 @@ describe('useSlashCommandProcessor', () => { }); // Assert - const expectedContent = [ - ` ⎿ Total duration (wall): 1h 2m 3s`, - ` Total Token usage:`, - ` Turns: 1`, - ` Total: 900`, - ` ├─ Input: 200`, - ` ├─ Output: 400`, - ` ├─ Cached: 100`, - ` └─ Overhead: 200`, - ` ├─ Model thoughts: 150`, - ` └─ Tool-use prompts: 50`, - ].join('\n'); - expect(mockAddItem).toHaveBeenNthCalledWith( 2, // Called after the user message expect.objectContaining({ - type: MessageType.INFO, - text: expectedContent, + type: MessageType.STATS, + stats: cumulativeStats, + duration: '1h 2m 3s', }), expect.any(Number), ); diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index fa1e4016..9e82b6cf 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -20,7 +20,7 @@ import { Message, MessageType, HistoryItemWithoutId } from '../types.js'; import { useSessionStats } from '../contexts/SessionContext.js'; import { createShowMemoryAction } from './useShowMemoryCommand.js'; import { GIT_COMMIT_INFO } from '../../generated/git-commit.js'; -import { formatMemoryUsage } from '../utils/formatters.js'; +import { formatDuration, formatMemoryUsage } from '../utils/formatters.js'; import { getCliVersion } from '../../utils/version.js'; export interface SlashCommandActionReturn { @@ -69,6 +69,13 @@ export const useSlashCommandProcessor = ( sandboxEnv: message.sandboxEnv, modelVersion: message.modelVersion, }; + } else if (message.type === MessageType.STATS) { + historyItemContent = { + type: 'stats', + stats: message.stats, + lastTurnStats: message.lastTurnStats, + duration: message.duration, + }; } else { historyItemContent = { type: message.type as @@ -152,41 +159,14 @@ export const useSlashCommandProcessor = ( description: 'check session stats', action: (_mainCommand, _subCommand, _args) => { const now = new Date(); - const { sessionStartTime, cumulative } = session.stats; - - const duration = now.getTime() - sessionStartTime.getTime(); - const durationInSeconds = Math.floor(duration / 1000); - const hours = Math.floor(durationInSeconds / 3600); - const minutes = Math.floor((durationInSeconds % 3600) / 60); - const seconds = durationInSeconds % 60; - - const durationString = [ - hours > 0 ? `${hours}h` : '', - minutes > 0 ? `${minutes}m` : '', - `${seconds}s`, - ] - .filter(Boolean) - .join(' '); - - const overheadTotal = - cumulative.thoughtsTokenCount + cumulative.toolUsePromptTokenCount; - - const statsContent = [ - ` ⎿ Total duration (wall): ${durationString}`, - ` Total Token usage:`, - ` Turns: ${cumulative.turnCount.toLocaleString()}`, - ` Total: ${cumulative.totalTokenCount.toLocaleString()}`, - ` ├─ Input: ${cumulative.promptTokenCount.toLocaleString()}`, - ` ├─ Output: ${cumulative.candidatesTokenCount.toLocaleString()}`, - ` ├─ Cached: ${cumulative.cachedContentTokenCount.toLocaleString()}`, - ` └─ Overhead: ${overheadTotal.toLocaleString()}`, - ` ├─ Model thoughts: ${cumulative.thoughtsTokenCount.toLocaleString()}`, - ` └─ Tool-use prompts: ${cumulative.toolUsePromptTokenCount.toLocaleString()}`, - ].join('\n'); + const { sessionStartTime, cumulative, currentTurn } = session.stats; + const wallDuration = now.getTime() - sessionStartTime.getTime(); addMessage({ - type: MessageType.INFO, - content: statsContent, + type: MessageType.STATS, + stats: cumulative, + lastTurnStats: currentTurn, + duration: formatDuration(wallDuration), timestamp: new Date(), }); }, diff --git a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx index ed0f2aac..e39feb01 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx @@ -598,5 +598,18 @@ describe('useGeminiStream', () => { expect(mockStartNewTurn).toHaveBeenCalledTimes(1); expect(mockAddUsage).not.toHaveBeenCalled(); }); + + it('should not call startNewTurn for a slash command', async () => { + mockHandleSlashCommand.mockReturnValue(true); + + const { result } = renderTestHook(); + + await act(async () => { + await result.current.submitQuery('/stats'); + }); + + expect(mockStartNewTurn).not.toHaveBeenCalled(); + expect(mockSendMessageStream).not.toHaveBeenCalled(); + }); }); }); diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index bad9f78a..725d8737 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -432,10 +432,6 @@ export const useGeminiStream = ( const userMessageTimestamp = Date.now(); setShowHelp(false); - if (!options?.isContinuation) { - startNewTurn(); - } - abortControllerRef.current = new AbortController(); const abortSignal = abortControllerRef.current.signal; @@ -449,6 +445,10 @@ export const useGeminiStream = ( return; } + if (!options?.isContinuation) { + startNewTurn(); + } + if (!geminiClient) { const errorMsg = 'Gemini client is not available.'; setInitError(errorMsg); |
