diff options
| author | Allen Hutchison <[email protected]> | 2025-04-29 15:39:36 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-04-29 15:39:36 -0700 |
| commit | 889200d400c4dec60de0d7b5cdd77261bbb63edb (patch) | |
| tree | e107628611d870d4a00435731d2754305888128f /packages/cli/src/ui/hooks/useGeminiStream.ts | |
| parent | c1b23c008a378c6c4b7f50fabc0ebf0280e0e5ad (diff) | |
Add @ command handling to useGeminiStream (#217)
* First integration of at commands into useGeminiStream.ts
* feat: Integrate @ command for file/directory reading
- Adds support for `@<path>` commands in the CLI UI to read file or directory contents using the `read_many_files` tool.
- Refactors `useGeminiStream` hook to handle slash, passthrough, and @ commands before sending queries to the Gemini API.
- Improves history item ID generation to prevent React duplicate key warnings.
* fix: Handle additional text after @ command path
- Modifies the `@` command processor to parse text following the file/directory path (e.g., `@README.md explain this`).
- Includes both the fetched file content and the subsequent text in the query sent to the Gemini API.
- Resolves the TODO item in `atCommandProcessor.ts`.
* feat: Allow @ command anywhere in query and fix build
- Update `atCommandProcessor` to correctly parse `@<path>` commands regardless of their position in the input string using regex. This enables queries like "Explain @README.md to me".
- Fix build error in `useGeminiStream` by importing the missing `findSafeSplitPoint` function.
* rename isPotentiallyAtCommand to isAtCommand
* respond to review comments.
Diffstat (limited to 'packages/cli/src/ui/hooks/useGeminiStream.ts')
| -rw-r--r-- | packages/cli/src/ui/hooks/useGeminiStream.ts | 76 |
1 files changed, 58 insertions, 18 deletions
diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 63a02fca..7e1f2177 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -27,9 +27,11 @@ import { IndividualToolCallDisplay, ToolCallStatus, } from '../types.js'; -import { findSafeSplitPoint } from '../utils/markdownUtilities.js'; +import { isAtCommand } from '../utils/commandUtils.js'; // Import the @ command checker import { useSlashCommandProcessor } from './slashCommandProcessor.js'; import { usePassthroughProcessor } from './passthroughCommandProcessor.js'; +import { handleAtCommand } from './atCommandProcessor.js'; // Import the @ command handler +import { findSafeSplitPoint } from '../utils/markdownUtilities.js'; // Import the split point finder const addHistoryItem = ( setHistory: React.Dispatch<React.SetStateAction<HistoryItem[]>>, @@ -61,6 +63,7 @@ export const useGeminiStream = ( // ID Generation Callback const getNextMessageId = useCallback((baseTimestamp: number): number => { + // Increment *before* adding to ensure uniqueness against the base timestamp messageIdCounterRef.current += 1; return baseTimestamp + messageIdCounterRef.current; }, []); @@ -144,32 +147,63 @@ export const useGeminiStream = ( if (typeof query === 'string' && query.trim().length === 0) return; const userMessageTimestamp = Date.now(); + messageIdCounterRef.current = 0; // Reset counter for this new submission + let queryToSendToGemini: PartListUnion | null = null; if (typeof query === 'string') { - setDebugMessage(`User query: '${query}'`); + const trimmedQuery = query.trim(); + setDebugMessage(`User query: '${trimmedQuery}'`); // 1. Check for Slash Commands - if (handleSlashCommand(query)) { - return; // Command was handled, exit early + if (handleSlashCommand(trimmedQuery)) { + return; // Handled, exit } // 2. Check for Passthrough Commands - if (handlePassthroughCommand(query)) { - return; // Command was handled, exit early + if (handlePassthroughCommand(trimmedQuery)) { + return; // Handled, exit } - // 3. Add user message if not handled by slash/passthrough - addHistoryItem( - setHistory, - { type: 'user', text: query }, - userMessageTimestamp, - ); + // 3. Check for @ Commands using the utility function + if (isAtCommand(trimmedQuery)) { + const atCommandResult = await handleAtCommand({ + query: trimmedQuery, + config, + setHistory, + setDebugMessage, + getNextMessageId, + userMessageTimestamp, + }); + + if (!atCommandResult.shouldProceed) { + return; // @ command handled it (e.g., error) or decided not to proceed + } + queryToSendToGemini = atCommandResult.processedQuery; + // User message and tool UI were added by handleAtCommand + } else { + // 4. It's a normal query for Gemini + addHistoryItem( + setHistory, + { type: 'user', text: trimmedQuery }, + userMessageTimestamp, + ); + queryToSendToGemini = trimmedQuery; + } } else { - // For function responses (PartListUnion that isn't a string), - // we don't add a user message here. The tool call/response UI handles it. + // 5. It's a function response (PartListUnion that isn't a string) + // Tool call/response UI handles history. Always proceed. + queryToSendToGemini = query; + } + + // --- Proceed to Gemini API call --- + if (queryToSendToGemini === null) { + // Should only happen if @ command failed and returned null query + setDebugMessage( + 'Query processing resulted in null, not sending to Gemini.', + ); + return; } - // 4. Proceed to Gemini API call const client = geminiClientRef.current; if (!client) { setInitError('Gemini client is not available.'); @@ -188,7 +222,6 @@ export const useGeminiStream = ( setStreamingState(StreamingState.Responding); setInitError(null); - messageIdCounterRef.current = 0; // Reset counter for new submission const chat = chatSessionRef.current; let currentToolGroupId: number | null = null; @@ -196,8 +229,12 @@ export const useGeminiStream = ( abortControllerRef.current = new AbortController(); const signal = abortControllerRef.current.signal; - // Use the original query for the Gemini call - const stream = client.sendMessageStream(chat, query, signal); + // Use the determined query for the Gemini call + const stream = client.sendMessageStream( + chat, + queryToSendToGemini, + signal, + ); // Process the stream events from the server logic let currentGeminiText = ''; // To accumulate message content @@ -488,6 +525,9 @@ export const useGeminiStream = ( updateGeminiMessage, handleSlashCommand, handlePassthroughCommand, + // handleAtCommand is implicitly included via its direct call + setDebugMessage, // Added dependency for handleAtCommand & passthrough + setStreamingState, // Added dependency for handlePassthroughCommand updateAndAddGeminiMessageContent, ], ); |
