mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
fix: pre-load @file references from external editor prompts (#20963)
Signed-off-by: Kartik Angiras <angiraskartik@gmail.com>
This commit is contained in:
@@ -163,7 +163,6 @@ describe('commandUtils', () => {
|
|||||||
it('should return true when query starts with @', () => {
|
it('should return true when query starts with @', () => {
|
||||||
expect(isAtCommand('@file')).toBe(true);
|
expect(isAtCommand('@file')).toBe(true);
|
||||||
expect(isAtCommand('@path/to/file')).toBe(true);
|
expect(isAtCommand('@path/to/file')).toBe(true);
|
||||||
expect(isAtCommand('@')).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when query contains @ preceded by whitespace', () => {
|
it('should return true when query contains @ preceded by whitespace', () => {
|
||||||
@@ -172,17 +171,36 @@ describe('commandUtils', () => {
|
|||||||
expect(isAtCommand(' @file')).toBe(true);
|
expect(isAtCommand(' @file')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when query does not start with @ and has no spaced @', () => {
|
it('should return true when @ is preceded by non-whitespace (external editor scenario)', () => {
|
||||||
|
// When a user composes a prompt in an external editor, @-references may
|
||||||
|
// appear after punctuation characters such as ':' or '(' without a space.
|
||||||
|
// The processor must still recognise these as @-commands so that the
|
||||||
|
// referenced files are pre-loaded before the query is sent to the model.
|
||||||
|
expect(isAtCommand('check:@file.py')).toBe(true);
|
||||||
|
expect(isAtCommand('analyze(@file.py)')).toBe(true);
|
||||||
|
expect(isAtCommand('hello@file')).toBe(true);
|
||||||
|
expect(isAtCommand('text@path/to/file')).toBe(true);
|
||||||
|
expect(isAtCommand('user@host')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when query does not contain any @<path> pattern', () => {
|
||||||
expect(isAtCommand('file')).toBe(false);
|
expect(isAtCommand('file')).toBe(false);
|
||||||
expect(isAtCommand('hello')).toBe(false);
|
expect(isAtCommand('hello')).toBe(false);
|
||||||
expect(isAtCommand('')).toBe(false);
|
expect(isAtCommand('')).toBe(false);
|
||||||
expect(isAtCommand('email@domain.com')).toBe(false);
|
// A bare '@' with no following path characters is not an @-command.
|
||||||
expect(isAtCommand('user@host')).toBe(false);
|
expect(isAtCommand('@')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when @ is not preceded by whitespace', () => {
|
it('should return false when @ is escaped with a backslash', () => {
|
||||||
expect(isAtCommand('hello@file')).toBe(false);
|
expect(isAtCommand('\\@file')).toBe(false);
|
||||||
expect(isAtCommand('text@path')).toBe(false);
|
});
|
||||||
|
|
||||||
|
it('should return true for multi-line external editor prompts with @-references', () => {
|
||||||
|
expect(isAtCommand('Please review:\n@src/main.py\nand fix bugs.')).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
// @file after a colon on the same line.
|
||||||
|
expect(isAtCommand('Files:@src/a.py,@src/b.py')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,18 +10,29 @@ import type { SlashCommand } from '../commands/types.js';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import type { Writable } from 'node:stream';
|
import type { Writable } from 'node:stream';
|
||||||
import type { Settings } from '../../config/settingsSchema.js';
|
import type { Settings } from '../../config/settingsSchema.js';
|
||||||
|
import { AT_COMMAND_PATH_REGEX_SOURCE } from '../hooks/atCommandProcessor.js';
|
||||||
|
|
||||||
|
// Pre-compiled regex for detecting @<path> patterns consistent with parseAllAtCommands.
|
||||||
|
// Uses the same AT_COMMAND_PATH_REGEX_SOURCE so that isAtCommand is true whenever
|
||||||
|
// parseAllAtCommands would find at least one atPath part.
|
||||||
|
const AT_COMMAND_DETECT_REGEX = new RegExp(
|
||||||
|
`(?<!\\\\)@${AT_COMMAND_PATH_REGEX_SOURCE}`,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a query string potentially represents an '@' command.
|
* Checks if a query string potentially represents an '@' command.
|
||||||
* It triggers if the query starts with '@' or contains '@' preceded by whitespace
|
* Returns true if the query contains any '@<path>' pattern that would be
|
||||||
* and followed by a non-whitespace character.
|
* recognised by the @ command processor, regardless of what character
|
||||||
|
* precedes the '@' sign. This ensures that prompts written in an external
|
||||||
|
* editor (where '@' may follow punctuation like ':' or '(') are correctly
|
||||||
|
* identified and their referenced files pre-loaded before the query is sent
|
||||||
|
* to the model.
|
||||||
*
|
*
|
||||||
* @param query The input query string.
|
* @param query The input query string.
|
||||||
* @returns True if the query looks like an '@' command, false otherwise.
|
* @returns True if the query looks like an '@' command, false otherwise.
|
||||||
*/
|
*/
|
||||||
export const isAtCommand = (query: string): boolean =>
|
export const isAtCommand = (query: string): boolean =>
|
||||||
// Check if starts with @ OR has a space, then @
|
AT_COMMAND_DETECT_REGEX.test(query);
|
||||||
query.startsWith('@') || /\s@/.test(query);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a query string potentially represents an '/' command.
|
* Checks if a query string potentially represents an '/' command.
|
||||||
|
|||||||
Reference in New Issue
Block a user