Add UI memory indicator. (#348)

Co-authored-by: Gregory Shikhman <shikhman@google.com>
This commit is contained in:
Allen Hutchison 2025-05-14 15:19:45 -07:00 committed by GitHub
parent 521708e294
commit 89aa1cad41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 58 additions and 23 deletions

View File

@ -324,7 +324,7 @@ function concatenateInstructions(
export async function loadHierarchicalGeminiMemory( export async function loadHierarchicalGeminiMemory(
currentWorkingDirectory: string, currentWorkingDirectory: string,
debugMode: boolean, debugMode: boolean,
): Promise<string> { ): Promise<{ memoryContent: string; fileCount: number }> {
if (debugMode) if (debugMode)
logger.debug( logger.debug(
`Loading hierarchical memory for CWD: ${currentWorkingDirectory}`, `Loading hierarchical memory for CWD: ${currentWorkingDirectory}`,
@ -337,7 +337,7 @@ export async function loadHierarchicalGeminiMemory(
); );
if (filePaths.length === 0) { if (filePaths.length === 0) {
if (debugMode) logger.debug('No GEMINI.md files found in hierarchy.'); if (debugMode) logger.debug('No GEMINI.md files found in hierarchy.');
return ''; return { memoryContent: '', fileCount: 0 };
} }
const contentsWithPaths = await readGeminiMdFiles(filePaths, debugMode); const contentsWithPaths = await readGeminiMdFiles(filePaths, debugMode);
const combinedInstructions = concatenateInstructions(contentsWithPaths); const combinedInstructions = concatenateInstructions(contentsWithPaths);
@ -349,7 +349,7 @@ export async function loadHierarchicalGeminiMemory(
logger.debug( logger.debug(
`Combined instructions (snippet): ${combinedInstructions.substring(0, 500)}...`, `Combined instructions (snippet): ${combinedInstructions.substring(0, 500)}...`,
); );
return combinedInstructions; return { memoryContent: combinedInstructions, fileCount: filePaths.length };
} }
export async function loadCliConfig(settings: Settings): Promise<Config> { export async function loadCliConfig(settings: Settings): Promise<Config> {
@ -368,7 +368,7 @@ export async function loadCliConfig(settings: Settings): Promise<Config> {
const argv = await parseArguments(); const argv = await parseArguments();
const debugMode = argv.debug_mode || false; const debugMode = argv.debug_mode || false;
const userMemory = await loadHierarchicalGeminiMemory( const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
process.cwd(), process.cwd(),
debugMode, debugMode,
); );
@ -388,7 +388,8 @@ export async function loadCliConfig(settings: Settings): Promise<Config> {
settings.toolCallCommand, settings.toolCallCommand,
settings.mcpServerCommand, settings.mcpServerCommand,
userAgent, userAgent,
userMemory, memoryContent,
fileCount,
); );
} }

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Static, Text, useStdout } from 'ink'; import { Box, Static, Text, useStdout } from 'ink';
import { StreamingState, type HistoryItem } from './types.js'; import { StreamingState, type HistoryItem } from './types.js';
import { useGeminiStream } from './hooks/useGeminiStream.js'; import { useGeminiStream } from './hooks/useGeminiStream.js';
@ -19,6 +19,7 @@ import { ThemeDialog } from './components/ThemeDialog.js';
import { shortenPath, type Config } from '@gemini-code/server'; import { shortenPath, type Config } from '@gemini-code/server';
import { Colors } from './colors.js'; import { Colors } from './colors.js';
import { Help } from './components/Help.js'; import { Help } from './components/Help.js';
import { loadHierarchicalGeminiMemory } from '../config/config.js';
import { LoadedSettings } from '../config/settings.js'; import { LoadedSettings } from '../config/settings.js';
import { Tips } from './components/Tips.js'; import { Tips } from './components/Tips.js';
import { ConsoleOutput } from './components/ConsolePatcher.js'; import { ConsoleOutput } from './components/ConsolePatcher.js';
@ -27,7 +28,6 @@ import { useCompletion } from './hooks/useCompletion.js';
import { SuggestionsDisplay } from './components/SuggestionsDisplay.js'; import { SuggestionsDisplay } from './components/SuggestionsDisplay.js';
import { isAtCommand, isSlashCommand } from './utils/commandUtils.js'; import { isAtCommand, isSlashCommand } from './utils/commandUtils.js';
import { useHistory } from './hooks/useHistoryManager.js'; import { useHistory } from './hooks/useHistoryManager.js';
import { loadHierarchicalGeminiMemory } from '../config/config.js'; // For performMemoryRefresh
import process from 'node:process'; // For performMemoryRefresh import process from 'node:process'; // For performMemoryRefresh
import { MessageType } from './types.js'; // For performMemoryRefresh import { MessageType } from './types.js'; // For performMemoryRefresh
import { getErrorMessage } from '@gemini-code/server'; // For performMemoryRefresh import { getErrorMessage } from '@gemini-code/server'; // For performMemoryRefresh
@ -51,6 +51,7 @@ export const App = ({
setStaticKey((prev) => prev + 1); setStaticKey((prev) => prev + 1);
}, [setStaticKey]); }, [setStaticKey]);
const [geminiMdFileCount, setGeminiMdFileCount] = useState<number>(0); // Added for memory file count
const [debugMessage, setDebugMessage] = useState<string>(''); const [debugMessage, setDebugMessage] = useState<string>('');
const [showHelp, setShowHelp] = useState<boolean>(false); const [showHelp, setShowHelp] = useState<boolean>(false);
const [themeError, setThemeError] = useState<string | null>(null); const [themeError, setThemeError] = useState<string | null>(null);
@ -62,6 +63,13 @@ export const App = ({
handleThemeHighlight, handleThemeHighlight,
} = useThemeCommand(settings, setThemeError); } = useThemeCommand(settings, setThemeError);
// useEffect to initialize geminiMdFileCount from config when config is ready
useEffect(() => {
if (config) {
setGeminiMdFileCount(config.getGeminiMdFileCount());
}
}, [config]);
const performMemoryRefresh = useCallback(async () => { const performMemoryRefresh = useCallback(async () => {
addItem( addItem(
{ {
@ -71,22 +79,25 @@ export const App = ({
Date.now(), Date.now(),
); );
try { try {
const newMemory = await loadHierarchicalGeminiMemory( const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
process.cwd(), process.cwd(),
config.getDebugMode(), config.getDebugMode(),
); );
config.setUserMemory(newMemory); config.setUserMemory(memoryContent);
config.setGeminiMdFileCount(fileCount);
setGeminiMdFileCount(fileCount);
// chatSessionRef.current = null; // This was in useGeminiStream, might need similar logic or pass chat ref // chatSessionRef.current = null; // This was in useGeminiStream, might need similar logic or pass chat ref
addItem( addItem(
{ {
type: MessageType.INFO, type: MessageType.INFO,
text: `Memory refreshed successfully. ${newMemory.length > 0 ? `Loaded ${newMemory.length} characters.` : 'No memory content found.'}`, text: `Memory refreshed successfully. ${memoryContent.length > 0 ? `Loaded ${memoryContent.length} characters from ${fileCount} file(s).` : 'No memory content found.'}`,
}, },
Date.now(), Date.now(),
); );
if (config.getDebugMode()) { if (config.getDebugMode()) {
console.log( console.log(
`[DEBUG] Refreshed memory content in config: ${newMemory.substring(0, 200)}...`, `[DEBUG] Refreshed memory content in config: ${memoryContent.substring(0, 200)}...`,
); );
} }
} catch (error) { } catch (error) {
@ -110,20 +121,19 @@ export const App = ({
setShowHelp, setShowHelp,
setDebugMessage, setDebugMessage,
openThemeDialog, openThemeDialog,
performMemoryRefresh, // Pass performMemoryRefresh performMemoryRefresh,
); );
const { streamingState, submitQuery, initError, pendingHistoryItem } = const { streamingState, submitQuery, initError, pendingHistoryItem } =
useGeminiStream( useGeminiStream(
addItem, addItem,
clearItems, // Pass clearItems clearItems,
refreshStatic, refreshStatic,
setShowHelp, setShowHelp,
config, config,
setDebugMessage, setDebugMessage,
openThemeDialog, // Pass openThemeDialog openThemeDialog,
handleSlashCommand, handleSlashCommand,
// performMemoryRefresh, // Removed performMemoryRefresh
); );
const { elapsedTime, currentLoadingPhrase } = const { elapsedTime, currentLoadingPhrase } =
useLoadingIndicator(streamingState); useLoadingIndicator(streamingState);
@ -268,12 +278,26 @@ export const App = ({
/> />
{isInputActive && ( {isInputActive && (
<> <>
<Box marginTop={1}> <Box
marginTop={1}
display="flex"
justifyContent="space-between"
width="100%"
>
<Box>
<Text color={Colors.SubtleComment}>cwd: </Text> <Text color={Colors.SubtleComment}>cwd: </Text>
<Text color={Colors.LightBlue}> <Text color={Colors.LightBlue}>
{shortenPath(config.getTargetDir(), 70)} {shortenPath(config.getTargetDir(), 70)}
</Text> </Text>
</Box> </Box>
{geminiMdFileCount > 0 && (
<Box>
<Text color={Colors.SubtleComment}>
Using {geminiMdFileCount} GEMINI.md files
</Text>
</Box>
)}
</Box>
<InputPrompt <InputPrompt
query={query} query={query}

View File

@ -37,6 +37,7 @@ export class Config {
private readonly mcpServerCommand: string | undefined, private readonly mcpServerCommand: string | undefined,
private readonly userAgent: string, private readonly userAgent: string,
private userMemory: string = '', // Made mutable for refresh private userMemory: string = '', // Made mutable for refresh
private geminiMdFileCount: number = 0,
) { ) {
// toolRegistry still needs initialization based on the instance // toolRegistry still needs initialization based on the instance
this.toolRegistry = createToolRegistry(this); this.toolRegistry = createToolRegistry(this);
@ -89,7 +90,6 @@ export class Config {
return this.userAgent; return this.userAgent;
} }
// Added getter for userMemory
getUserMemory(): string { getUserMemory(): string {
return this.userMemory; return this.userMemory;
} }
@ -97,6 +97,14 @@ export class Config {
setUserMemory(newUserMemory: string): void { setUserMemory(newUserMemory: string): void {
this.userMemory = newUserMemory; this.userMemory = newUserMemory;
} }
getGeminiMdFileCount(): number {
return this.geminiMdFileCount;
}
setGeminiMdFileCount(count: number): void {
this.geminiMdFileCount = count;
}
} }
function findEnvFile(startDir: string): string | null { function findEnvFile(startDir: string): string | null {
@ -139,7 +147,8 @@ export function createServerConfig(
toolCallCommand?: string, toolCallCommand?: string,
mcpServerCommand?: string, mcpServerCommand?: string,
userAgent?: string, userAgent?: string,
userMemory?: string, // Added userMemory parameter userMemory?: string,
geminiMdFileCount?: number,
): Config { ): Config {
return new Config( return new Config(
apiKey, apiKey,
@ -153,7 +162,8 @@ export function createServerConfig(
toolCallCommand, toolCallCommand,
mcpServerCommand, mcpServerCommand,
userAgent ?? 'GeminiCLI/unknown', // Default user agent userAgent ?? 'GeminiCLI/unknown', // Default user agent
userMemory ?? '', // Pass userMemory, default to empty string userMemory ?? '',
geminiMdFileCount ?? 0,
); );
} }