Add feature flag for IDE integration (#3927)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
This commit is contained in:
parent
e9d680e8a4
commit
fadc477001
|
@ -768,3 +768,117 @@ describe('loadCliConfig extensions', () => {
|
||||||
expect(config.getExtensionContextFilePaths()).toEqual(['/path/to/ext1.md']);
|
expect(config.getExtensionContextFilePaths()).toEqual(['/path/to/ext1.md']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('loadCliConfig ideMode', () => {
|
||||||
|
const originalArgv = process.argv;
|
||||||
|
const originalEnv = { ...process.env };
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetAllMocks();
|
||||||
|
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
||||||
|
process.env.GEMINI_API_KEY = 'test-api-key';
|
||||||
|
// Explicitly delete TERM_PROGRAM and SANDBOX before each test
|
||||||
|
delete process.env.TERM_PROGRAM;
|
||||||
|
delete process.env.SANDBOX;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.argv = originalArgv;
|
||||||
|
process.env = originalEnv;
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be false by default', async () => {
|
||||||
|
process.argv = ['node', 'script.js'];
|
||||||
|
const settings: Settings = {};
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be false if --ide-mode is true but TERM_PROGRAM is not vscode', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--ide-mode'];
|
||||||
|
const settings: Settings = {};
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be false if settings.ideMode is true but TERM_PROGRAM is not vscode', async () => {
|
||||||
|
process.argv = ['node', 'script.js'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const settings: Settings = { ideMode: true };
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be true when --ide-mode is set and TERM_PROGRAM is vscode', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--ide-mode'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
const settings: Settings = {};
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be true when settings.ideMode is true and TERM_PROGRAM is vscode', async () => {
|
||||||
|
process.argv = ['node', 'script.js'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
const settings: Settings = { ideMode: true };
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prioritize --ide-mode (true) over settings (false) when TERM_PROGRAM is vscode', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--ide-mode'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
const settings: Settings = { ideMode: false };
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prioritize --no-ide-mode (false) over settings (true) even when TERM_PROGRAM is vscode', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--no-ide-mode'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
const settings: Settings = { ideMode: true };
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be false when --ide-mode is true, TERM_PROGRAM is vscode, but SANDBOX is set', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--ide-mode'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
process.env.SANDBOX = 'true';
|
||||||
|
const settings: Settings = {};
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be false when settings.ideMode is true, TERM_PROGRAM is vscode, but SANDBOX is set', async () => {
|
||||||
|
process.argv = ['node', 'script.js'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
process.env.SANDBOX = 'true';
|
||||||
|
const settings: Settings = { ideMode: true };
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add __ide_server when ideMode is true', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--ide-mode'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
process.env.TERM_PROGRAM = 'vscode';
|
||||||
|
const settings: Settings = {};
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getIdeMode()).toBe(true);
|
||||||
|
const mcpServers = config.getMcpServers();
|
||||||
|
expect(mcpServers['_ide_server']).toBeDefined();
|
||||||
|
expect(mcpServers['_ide_server'].httpUrl).toBe('http://localhost:3000/mcp');
|
||||||
|
expect(mcpServers['_ide_server'].description).toBe('IDE connection');
|
||||||
|
expect(mcpServers['_ide_server'].trust).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
DEFAULT_GEMINI_EMBEDDING_MODEL,
|
DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||||
FileDiscoveryService,
|
FileDiscoveryService,
|
||||||
TelemetryTarget,
|
TelemetryTarget,
|
||||||
|
MCPServerConfig,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
import { Settings } from './settings.js';
|
import { Settings } from './settings.js';
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ export interface CliArgs {
|
||||||
allowedMcpServerNames: string[] | undefined;
|
allowedMcpServerNames: string[] | undefined;
|
||||||
extensions: string[] | undefined;
|
extensions: string[] | undefined;
|
||||||
listExtensions: boolean | undefined;
|
listExtensions: boolean | undefined;
|
||||||
|
ideMode: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function parseArguments(): Promise<CliArgs> {
|
export async function parseArguments(): Promise<CliArgs> {
|
||||||
|
@ -175,6 +177,10 @@ export async function parseArguments(): Promise<CliArgs> {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'List all available extensions and exit.',
|
description: 'List all available extensions and exit.',
|
||||||
})
|
})
|
||||||
|
.option('ide-mode', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Run in IDE mode?',
|
||||||
|
})
|
||||||
|
|
||||||
.version(await getCliVersion()) // This will enable the --version flag based on package.json
|
.version(await getCliVersion()) // This will enable the --version flag based on package.json
|
||||||
.alias('v', 'version')
|
.alias('v', 'version')
|
||||||
|
@ -230,6 +236,11 @@ export async function loadCliConfig(
|
||||||
(v) => v === 'true' || v === '1',
|
(v) => v === 'true' || v === '1',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ideMode =
|
||||||
|
(argv.ideMode ?? settings.ideMode ?? false) &&
|
||||||
|
process.env.TERM_PROGRAM === 'vscode' &&
|
||||||
|
!process.env.SANDBOX;
|
||||||
|
|
||||||
const activeExtensions = filterActiveExtensions(
|
const activeExtensions = filterActiveExtensions(
|
||||||
extensions,
|
extensions,
|
||||||
argv.extensions || [],
|
argv.extensions || [],
|
||||||
|
@ -273,6 +284,24 @@ export async function loadCliConfig(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ideMode) {
|
||||||
|
mcpServers['_ide_server'] = new MCPServerConfig(
|
||||||
|
undefined, // command
|
||||||
|
undefined, // args
|
||||||
|
undefined, // env
|
||||||
|
undefined, // cwd
|
||||||
|
undefined, // url
|
||||||
|
'http://localhost:3000/mcp', // httpUrl
|
||||||
|
undefined, // headers
|
||||||
|
undefined, // tcp
|
||||||
|
undefined, // timeout
|
||||||
|
false, // trust
|
||||||
|
'IDE connection', // description
|
||||||
|
undefined, // includeTools
|
||||||
|
undefined, // excludeTools
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const sandboxConfig = await loadSandboxConfig(settings, argv);
|
const sandboxConfig = await loadSandboxConfig(settings, argv);
|
||||||
|
|
||||||
return new Config({
|
return new Config({
|
||||||
|
@ -333,6 +362,7 @@ export async function loadCliConfig(
|
||||||
version: e.config.version,
|
version: e.config.version,
|
||||||
})),
|
})),
|
||||||
noBrowser: !!process.env.NO_BROWSER,
|
noBrowser: !!process.env.NO_BROWSER,
|
||||||
|
ideMode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ export interface Settings {
|
||||||
maxSessionTurns?: number;
|
maxSessionTurns?: number;
|
||||||
|
|
||||||
// Add other settings here.
|
// Add other settings here.
|
||||||
|
ideMode?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsError {
|
export interface SettingsError {
|
||||||
|
|
|
@ -144,6 +144,7 @@ export interface ConfigParameters {
|
||||||
listExtensions?: boolean;
|
listExtensions?: boolean;
|
||||||
activeExtensions?: ActiveExtension[];
|
activeExtensions?: ActiveExtension[];
|
||||||
noBrowser?: boolean;
|
noBrowser?: boolean;
|
||||||
|
ideMode?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
|
@ -183,6 +184,7 @@ export class Config {
|
||||||
private readonly model: string;
|
private readonly model: string;
|
||||||
private readonly extensionContextFilePaths: string[];
|
private readonly extensionContextFilePaths: string[];
|
||||||
private readonly noBrowser: boolean;
|
private readonly noBrowser: boolean;
|
||||||
|
private readonly ideMode: boolean;
|
||||||
private modelSwitchedDuringSession: boolean = false;
|
private modelSwitchedDuringSession: boolean = false;
|
||||||
private readonly maxSessionTurns: number;
|
private readonly maxSessionTurns: number;
|
||||||
private readonly listExtensions: boolean;
|
private readonly listExtensions: boolean;
|
||||||
|
@ -234,6 +236,7 @@ export class Config {
|
||||||
this.listExtensions = params.listExtensions ?? false;
|
this.listExtensions = params.listExtensions ?? false;
|
||||||
this._activeExtensions = params.activeExtensions ?? [];
|
this._activeExtensions = params.activeExtensions ?? [];
|
||||||
this.noBrowser = params.noBrowser ?? false;
|
this.noBrowser = params.noBrowser ?? false;
|
||||||
|
this.ideMode = params.ideMode ?? false;
|
||||||
|
|
||||||
if (params.contextFileName) {
|
if (params.contextFileName) {
|
||||||
setGeminiMdFilename(params.contextFileName);
|
setGeminiMdFilename(params.contextFileName);
|
||||||
|
@ -498,6 +501,10 @@ export class Config {
|
||||||
return this.noBrowser;
|
return this.noBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIdeMode(): boolean {
|
||||||
|
return this.ideMode;
|
||||||
|
}
|
||||||
|
|
||||||
async getGitService(): Promise<GitService> {
|
async getGitService(): Promise<GitService> {
|
||||||
if (!this.gitService) {
|
if (!this.gitService) {
|
||||||
this.gitService = new GitService(this.targetDir);
|
this.gitService = new GitService(this.targetDir);
|
||||||
|
|
Loading…
Reference in New Issue