summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/hooks/atCommandProcessor.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/hooks/atCommandProcessor.ts')
-rw-r--r--packages/cli/src/ui/hooks/atCommandProcessor.ts49
1 files changed, 35 insertions, 14 deletions
diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts
index 4b583d6c..da3d71dd 100644
--- a/packages/cli/src/ui/hooks/atCommandProcessor.ts
+++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts
@@ -4,8 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import * as fs from 'fs/promises';
+import * as path from 'path';
import { PartListUnion } from '@google/genai';
-import { Config, getErrorMessage } from '@gemini-code/server';
+import { Config, getErrorMessage, isNodeError } from '@gemini-code/server';
import {
HistoryItem,
IndividualToolCallDisplay,
@@ -39,9 +41,10 @@ interface HandleAtCommandResult {
/**
* Processes user input potentially containing an '@<path>' command.
- * It finds the first '@<path>', reads the specified path, updates the UI,
- * and prepares the query for the LLM, incorporating the file content
- * and surrounding text.
+ * It finds the first '@<path>', checks if the path is a file or directory,
+ * prepares the appropriate path specification for the read_many_files tool,
+ * updates the UI, and prepares the query for the LLM, incorporating the
+ * file content and surrounding text.
*
* @returns An object containing the potentially modified query (or null)
* and a flag indicating if the main hook should proceed.
@@ -60,8 +63,6 @@ export async function handleAtCommand({
const match = trimmedQuery.match(atCommandRegex);
if (!match) {
- // This should technically not happen if isPotentiallyAtCommand was true,
- // but handle defensively.
const errorTimestamp = getNextMessageId(userMessageTimestamp);
addHistoryItem(
setHistory,
@@ -108,19 +109,41 @@ export async function handleAtCommand({
// --- Path Handling for @ command ---
let pathSpec = pathPart;
- // Basic check: If no extension or ends with '/', assume directory and add globstar.
- if (!pathPart.includes('.') || pathPart.endsWith('/')) {
- pathSpec = pathPart.endsWith('/') ? `${pathPart}**` : `${pathPart}/**`;
+ const contentLabel = pathPart;
+
+ try {
+ // Resolve the path relative to the target directory
+ const absolutePath = path.resolve(config.getTargetDir(), pathPart);
+ const stats = await fs.stat(absolutePath);
+
+ if (stats.isDirectory()) {
+ // If it's a directory, ensure it ends with a globstar for recursive read
+ pathSpec = pathPart.endsWith('/') ? `${pathPart}**` : `${pathPart}/**`;
+ setDebugMessage(`Path resolved to directory, using glob: ${pathSpec}`);
+ } else {
+ // It's a file, use the original pathPart as pathSpec
+ setDebugMessage(`Path resolved to file: ${pathSpec}`);
+ }
+ } catch (error) {
+ // If stat fails (e.g., file/dir not found), proceed with the original pathPart.
+ // The read_many_files tool will handle the error if it's invalid.
+ if (isNodeError(error) && error.code === 'ENOENT') {
+ setDebugMessage(`Path not found, proceeding with original: ${pathSpec}`);
+ } else {
+ // Log other stat errors but still proceed
+ console.error(`Error stating path ${pathPart}:`, error);
+ setDebugMessage(
+ `Error stating path, proceeding with original: ${pathSpec}`,
+ );
+ }
}
+
const toolArgs = { paths: [pathSpec] };
- const contentLabel =
- pathSpec === pathPart ? pathPart : `directory ${pathPart}`;
// --- End Path Handling ---
let toolCallDisplay: IndividualToolCallDisplay;
try {
- setDebugMessage(`Reading via @ command: ${pathSpec}`);
const result = await readManyFilesTool.execute(toolArgs);
const fileContent = result.llmContent || '';
@@ -133,7 +156,6 @@ export async function handleAtCommand({
confirmationDetails: undefined,
};
- // Construct the query for Gemini, combining parts
const processedQueryParts = [];
if (textBefore) {
processedQueryParts.push({ text: textBefore });
@@ -159,7 +181,6 @@ export async function handleAtCommand({
return { processedQuery, shouldProceed: true };
} catch (error) {
- // Construct error UI
toolCallDisplay = {
callId: `client-read-${userMessageTimestamp}`,
name: readManyFilesTool.displayName,