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(
currentWorkingDirectory: string,
debugMode: boolean,
): Promise<string> {
): 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<Config> {
@ -368,7 +368,7 @@ export async function loadCliConfig(settings: Settings): Promise<Config> {
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<Config> {
settings.toolCallCommand,
settings.mcpServerCommand,
userAgent,
userMemory,
memoryContent,
fileCount,
);
}

View File

@ -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<number>(0); // Added for memory file count
const [debugMessage, setDebugMessage] = useState<string>('');
const [showHelp, setShowHelp] = useState<boolean>(false);
const [themeError, setThemeError] = useState<string | null>(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 && (
<>
<Box marginTop={1}>
<Text color={Colors.SubtleComment}>cwd: </Text>
<Text color={Colors.LightBlue}>
{shortenPath(config.getTargetDir(), 70)}
</Text>
<Box
marginTop={1}
display="flex"
justifyContent="space-between"
width="100%"
>
<Box>
<Text color={Colors.SubtleComment}>cwd: </Text>
<Text color={Colors.LightBlue}>
{shortenPath(config.getTargetDir(), 70)}
</Text>
</Box>
{geminiMdFileCount > 0 && (
<Box>
<Text color={Colors.SubtleComment}>
Using {geminiMdFileCount} GEMINI.md files
</Text>
</Box>
)}
</Box>
<InputPrompt

View File

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