feat: Introduce session context and add session duration stat for /stats command (#854)

This commit is contained in:
Abhi
2025-06-08 18:01:02 -04:00
committed by GitHub
parent 9104ac02f7
commit 7868ef8229
7 changed files with 146 additions and 4 deletions

View File

@@ -61,10 +61,15 @@ import {
MCPServerStatus,
getMCPServerStatus,
} from '@gemini-cli/core';
import { useSession } from '../contexts/SessionContext.js';
import * as ShowMemoryCommandModule from './useShowMemoryCommand.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
vi.mock('../contexts/SessionContext.js', () => ({
useSession: vi.fn(),
}));
vi.mock('./useShowMemoryCommand.js', () => ({
SHOW_MEMORY_COMMAND_NAME: '/memory show',
createShowMemoryAction: vi.fn(() => vi.fn()),
@@ -84,6 +89,7 @@ describe('useSlashCommandProcessor', () => {
let mockPerformMemoryRefresh: ReturnType<typeof vi.fn>;
let mockConfig: Config;
let mockCorgiMode: ReturnType<typeof vi.fn>;
const mockUseSession = useSession as Mock;
beforeEach(() => {
mockAddItem = vi.fn();
@@ -99,6 +105,9 @@ describe('useSlashCommandProcessor', () => {
getModel: vi.fn(() => 'test-model'),
} as unknown as Config;
mockCorgiMode = vi.fn();
mockUseSession.mockReturnValue({
startTime: new Date('2025-01-01T00:00:00.000Z'),
});
(open as Mock).mockClear();
mockProcessExit.mockClear();
@@ -230,6 +239,34 @@ describe('useSlashCommandProcessor', () => {
});
});
describe('/stats command', () => {
it('should show the session duration', async () => {
const { handleSlashCommand } = getProcessor();
let commandResult: SlashCommandActionReturn | boolean = false;
// Mock current time
const mockDate = new Date('2025-01-01T00:01:05.000Z');
vi.setSystemTime(mockDate);
await act(async () => {
commandResult = handleSlashCommand('/stats');
});
expect(mockAddItem).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
type: MessageType.INFO,
text: 'Session duration: 1m 5s',
}),
expect.any(Number),
);
expect(commandResult).toBe(true);
// Restore system time
vi.useRealTimers();
});
});
describe('Other commands', () => {
it('/help should open help and return true', async () => {
const { handleSlashCommand } = getProcessor();

View File

@@ -11,6 +11,7 @@ import process from 'node:process';
import { UseHistoryManagerReturn } from './useHistoryManager.js';
import { Config, MCPServerStatus, getMCPServerStatus } from '@gemini-cli/core';
import { Message, MessageType, HistoryItemWithoutId } from '../types.js';
import { useSession } from '../contexts/SessionContext.js';
import { createShowMemoryAction } from './useShowMemoryCommand.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
import { formatMemoryUsage } from '../utils/formatters.js';
@@ -49,6 +50,8 @@ export const useSlashCommandProcessor = (
toggleCorgiMode: () => void,
showToolDescriptions: boolean = false,
) => {
const session = useSession();
const addMessage = useCallback(
(message: Message) => {
// Convert Message to HistoryItemWithoutId
@@ -138,6 +141,33 @@ export const useSlashCommandProcessor = (
openThemeDialog();
},
},
{
name: 'stats',
altName: 'usage',
description: 'check session stats',
action: (_mainCommand, _subCommand, _args) => {
const now = new Date();
const duration = now.getTime() - session.startTime.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(' ');
addMessage({
type: MessageType.INFO,
content: `Session duration: ${durationString}`,
timestamp: new Date(),
});
},
},
{
name: 'mcp',
description: 'list configured MCP servers and tools',
@@ -447,6 +477,7 @@ Add any other context about the problem here.
toggleCorgiMode,
config,
showToolDescriptions,
session.startTime,
],
);