mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-15 08:31:14 -07:00
fix: ensure positional prompt arguments work with extensions flag (#10077)
Co-authored-by: Allen Hutchison <adh@google.com>
This commit is contained in:
@@ -2975,6 +2975,121 @@ describe('loadCliConfig interactive', () => {
|
||||
expect(argv.promptInteractive).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should not be interactive if positional prompt words are provided with extensions flag', async () => {
|
||||
process.stdin.isTTY = true;
|
||||
process.argv = ['node', 'script.js', '-e', 'none', 'hello'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{},
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.isInteractive()).toBe(false);
|
||||
expect(argv.query).toBe('hello');
|
||||
expect(argv.extensions).toEqual(['none']);
|
||||
});
|
||||
|
||||
it('should handle multiple positional words correctly', async () => {
|
||||
process.stdin.isTTY = true;
|
||||
process.argv = ['node', 'script.js', 'hello world how are you'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{},
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.isInteractive()).toBe(false);
|
||||
expect(argv.query).toBe('hello world how are you');
|
||||
expect(argv.prompt).toBe('hello world how are you');
|
||||
});
|
||||
|
||||
it('should handle multiple positional words with flags', async () => {
|
||||
process.stdin.isTTY = true;
|
||||
process.argv = [
|
||||
'node',
|
||||
'script.js',
|
||||
'--model',
|
||||
'gemini-1.5-pro',
|
||||
'write',
|
||||
'a',
|
||||
'function',
|
||||
'to',
|
||||
'sort',
|
||||
'array',
|
||||
];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{},
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.isInteractive()).toBe(false);
|
||||
expect(argv.query).toBe('write a function to sort array');
|
||||
expect(argv.model).toBe('gemini-1.5-pro');
|
||||
});
|
||||
|
||||
it('should handle empty positional arguments', async () => {
|
||||
process.stdin.isTTY = true;
|
||||
process.argv = ['node', 'script.js', ''];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{},
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.isInteractive()).toBe(true);
|
||||
expect(argv.query).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle extensions flag with positional arguments correctly', async () => {
|
||||
process.stdin.isTTY = true;
|
||||
process.argv = [
|
||||
'node',
|
||||
'script.js',
|
||||
'-e',
|
||||
'none',
|
||||
'hello',
|
||||
'world',
|
||||
'how',
|
||||
'are',
|
||||
'you',
|
||||
];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{},
|
||||
[],
|
||||
new ExtensionEnablementManager(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
argv.extensions,
|
||||
),
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.isInteractive()).toBe(false);
|
||||
expect(argv.query).toBe('hello world how are you');
|
||||
expect(argv.extensions).toEqual(['none']);
|
||||
});
|
||||
|
||||
it('should be interactive if no positional prompt words are provided with flags', async () => {
|
||||
process.stdin.isTTY = true;
|
||||
process.argv = ['node', 'script.js', '--model', 'gemini-1.5-pro'];
|
||||
@@ -3420,6 +3535,16 @@ describe('parseArguments with positional prompt', () => {
|
||||
expect(argv.promptInteractive).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should have correct positional argument description', async () => {
|
||||
// Test that the positional argument has the expected description
|
||||
const yargsInstance = await import('./config.js');
|
||||
// This test verifies that the positional 'query' argument is properly configured
|
||||
// with the description: "Positional prompt. Defaults to one-shot; use -i/--prompt-interactive for interactive."
|
||||
process.argv = ['node', 'script.js', 'test', 'query'];
|
||||
const argv = await yargsInstance.parseArguments({} as Settings);
|
||||
expect(argv.query).toBe('test query');
|
||||
});
|
||||
|
||||
it('should correctly parse a prompt from the --prompt flag', async () => {
|
||||
process.argv = ['node', 'script.js', '--prompt', 'test prompt'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
|
||||
@@ -255,6 +255,7 @@ export async function parseArguments(settings: Settings): Promise<CliArgs> {
|
||||
alias: 'e',
|
||||
type: 'array',
|
||||
string: true,
|
||||
nargs: 1,
|
||||
description:
|
||||
'A list of extensions to use. If not provided, all extensions are used.',
|
||||
coerce: (extensions: string[]) =>
|
||||
|
||||
@@ -13,6 +13,8 @@ describe('detectIde', () => {
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
// Clear Cursor-specific environment variables that might interfere with tests
|
||||
delete process.env['CURSOR_TRACE_ID'];
|
||||
});
|
||||
|
||||
it('should return undefined if TERM_PROGRAM is not vscode', () => {
|
||||
@@ -41,42 +43,49 @@ describe('detectIde', () => {
|
||||
it('should detect Codespaces', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('CODESPACES', 'true');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.codespaces);
|
||||
});
|
||||
|
||||
it('should detect Cloud Shell via EDITOR_IN_CLOUD_SHELL', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('EDITOR_IN_CLOUD_SHELL', 'true');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cloudshell);
|
||||
});
|
||||
|
||||
it('should detect Cloud Shell via CLOUD_SHELL', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('CLOUD_SHELL', 'true');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.cloudshell);
|
||||
});
|
||||
|
||||
it('should detect Trae', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('TERM_PRODUCT', 'Trae');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.trae);
|
||||
});
|
||||
|
||||
it('should detect Firebase Studio via MONOSPACE_ENV', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('MONOSPACE_ENV', 'true');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.firebasestudio);
|
||||
});
|
||||
|
||||
it('should detect VSCode when no other IDE is detected and command includes "code"', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('MONOSPACE_ENV', '');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.vscode);
|
||||
});
|
||||
|
||||
it('should detect VSCodeFork when no other IDE is detected and command does not include "code"', () => {
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('MONOSPACE_ENV', '');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfoNoCode)).toBe(IDE_DEFINITIONS.vscodefork);
|
||||
});
|
||||
});
|
||||
@@ -99,6 +108,7 @@ describe('detectIde with ideInfoFromFile', () => {
|
||||
it('should fall back to env detection if name is missing', () => {
|
||||
const ideInfoFromFile = { displayName: 'Custom IDE' };
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe(
|
||||
IDE_DEFINITIONS.vscode,
|
||||
);
|
||||
@@ -107,6 +117,7 @@ describe('detectIde with ideInfoFromFile', () => {
|
||||
it('should fall back to env detection if displayName is missing', () => {
|
||||
const ideInfoFromFile = { name: 'custom-ide' };
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe(
|
||||
IDE_DEFINITIONS.vscode,
|
||||
);
|
||||
|
||||
@@ -413,6 +413,11 @@ describe('ClearcutLogger', () => {
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
vi.stubEnv(key, value);
|
||||
}
|
||||
// Clear Cursor-specific environment variables that might interfere with tests
|
||||
// Only clear if not explicitly testing Cursor detection
|
||||
if (!env.CURSOR_TRACE_ID) {
|
||||
vi.stubEnv('CURSOR_TRACE_ID', '');
|
||||
}
|
||||
const event = logger?.createLogEvent(EventNames.API_ERROR, []);
|
||||
expect(event?.event_metadata[0][3]).toEqual({
|
||||
gemini_cli_key: EventMetadataKey.GEMINI_CLI_SURFACE,
|
||||
|
||||
Reference in New Issue
Block a user