From 89aa1cad41eefc4e1e7e4b3cb9d4a495707dd33e Mon Sep 17 00:00:00 2001 From: Allen Hutchison Date: Wed, 14 May 2025 15:19:45 -0700 Subject: [PATCH] Add UI memory indicator. (#348) Co-authored-by: Gregory Shikhman --- packages/cli/src/config/config.ts | 11 +++--- packages/cli/src/ui/App.tsx | 54 ++++++++++++++++++++-------- packages/server/src/config/config.ts | 16 +++++++-- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 7e564ee2..c8a2b0ef 100644 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -324,7 +324,7 @@ function concatenateInstructions( export async function loadHierarchicalGeminiMemory( currentWorkingDirectory: string, debugMode: boolean, -): Promise { +): Promise<{ memoryContent: string; fileCount: number }> { if (debugMode) logger.debug( `Loading hierarchical memory for CWD: ${currentWorkingDirectory}`, @@ -337,7 +337,7 @@ export async function loadHierarchicalGeminiMemory( ); if (filePaths.length === 0) { if (debugMode) logger.debug('No GEMINI.md files found in hierarchy.'); - return ''; + return { memoryContent: '', fileCount: 0 }; } const contentsWithPaths = await readGeminiMdFiles(filePaths, debugMode); const combinedInstructions = concatenateInstructions(contentsWithPaths); @@ -349,7 +349,7 @@ export async function loadHierarchicalGeminiMemory( logger.debug( `Combined instructions (snippet): ${combinedInstructions.substring(0, 500)}...`, ); - return combinedInstructions; + return { memoryContent: combinedInstructions, fileCount: filePaths.length }; } export async function loadCliConfig(settings: Settings): Promise { @@ -368,7 +368,7 @@ export async function loadCliConfig(settings: Settings): Promise { const argv = await parseArguments(); const debugMode = argv.debug_mode || false; - const userMemory = await loadHierarchicalGeminiMemory( + const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory( process.cwd(), debugMode, ); @@ -388,7 +388,8 @@ export async function loadCliConfig(settings: Settings): Promise { settings.toolCallCommand, settings.mcpServerCommand, userAgent, - userMemory, + memoryContent, + fileCount, ); } diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index c24c8909..5fb36480 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -4,7 +4,7 @@ * 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 { StreamingState, type HistoryItem } from './types.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 { Colors } from './colors.js'; import { Help } from './components/Help.js'; +import { loadHierarchicalGeminiMemory } from '../config/config.js'; import { LoadedSettings } from '../config/settings.js'; import { Tips } from './components/Tips.js'; import { ConsoleOutput } from './components/ConsolePatcher.js'; @@ -27,7 +28,6 @@ import { useCompletion } from './hooks/useCompletion.js'; import { SuggestionsDisplay } from './components/SuggestionsDisplay.js'; import { isAtCommand, isSlashCommand } from './utils/commandUtils.js'; import { useHistory } from './hooks/useHistoryManager.js'; -import { loadHierarchicalGeminiMemory } from '../config/config.js'; // For performMemoryRefresh import process from 'node:process'; // For performMemoryRefresh import { MessageType } from './types.js'; // For performMemoryRefresh import { getErrorMessage } from '@gemini-code/server'; // For performMemoryRefresh @@ -51,6 +51,7 @@ export const App = ({ setStaticKey((prev) => prev + 1); }, [setStaticKey]); + const [geminiMdFileCount, setGeminiMdFileCount] = useState(0); // Added for memory file count const [debugMessage, setDebugMessage] = useState(''); const [showHelp, setShowHelp] = useState(false); const [themeError, setThemeError] = useState(null); @@ -62,6 +63,13 @@ export const App = ({ handleThemeHighlight, } = useThemeCommand(settings, setThemeError); + // useEffect to initialize geminiMdFileCount from config when config is ready + useEffect(() => { + if (config) { + setGeminiMdFileCount(config.getGeminiMdFileCount()); + } + }, [config]); + const performMemoryRefresh = useCallback(async () => { addItem( { @@ -71,22 +79,25 @@ export const App = ({ Date.now(), ); try { - const newMemory = await loadHierarchicalGeminiMemory( + const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory( process.cwd(), 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 addItem( { 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(), ); if (config.getDebugMode()) { console.log( - `[DEBUG] Refreshed memory content in config: ${newMemory.substring(0, 200)}...`, + `[DEBUG] Refreshed memory content in config: ${memoryContent.substring(0, 200)}...`, ); } } catch (error) { @@ -110,20 +121,19 @@ export const App = ({ setShowHelp, setDebugMessage, openThemeDialog, - performMemoryRefresh, // Pass performMemoryRefresh + performMemoryRefresh, ); const { streamingState, submitQuery, initError, pendingHistoryItem } = useGeminiStream( addItem, - clearItems, // Pass clearItems + clearItems, refreshStatic, setShowHelp, config, setDebugMessage, - openThemeDialog, // Pass openThemeDialog + openThemeDialog, handleSlashCommand, - // performMemoryRefresh, // Removed performMemoryRefresh ); const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState); @@ -268,11 +278,25 @@ export const App = ({ /> {isInputActive && ( <> - - cwd: - - {shortenPath(config.getTargetDir(), 70)} - + + + cwd: + + {shortenPath(config.getTargetDir(), 70)} + + + {geminiMdFileCount > 0 && ( + + + Using {geminiMdFileCount} GEMINI.md files + + + )}