Launch VS Code IDE Integration (#6063)
This commit is contained in:
parent
d219f90132
commit
3a87712c1a
|
@ -1010,33 +1010,6 @@ describe('loadCliConfig model selection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig ideModeFeature', () => {
|
||||
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';
|
||||
delete process.env.SANDBOX;
|
||||
delete process.env.GEMINI_CLI_IDE_SERVER_PORT;
|
||||
});
|
||||
|
||||
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.getIdeModeFeature()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig folderTrustFeature', () => {
|
||||
const originalArgv = process.argv;
|
||||
const originalEnv = { ...process.env };
|
||||
|
|
|
@ -67,7 +67,6 @@ export interface CliArgs {
|
|||
experimentalAcp: boolean | undefined;
|
||||
extensions: string[] | undefined;
|
||||
listExtensions: boolean | undefined;
|
||||
ideModeFeature: boolean | undefined;
|
||||
proxy: string | undefined;
|
||||
includeDirectories: string[] | undefined;
|
||||
}
|
||||
|
@ -200,10 +199,6 @@ export async function parseArguments(): Promise<CliArgs> {
|
|||
type: 'boolean',
|
||||
description: 'List all available extensions and exit.',
|
||||
})
|
||||
.option('ide-mode-feature', {
|
||||
type: 'boolean',
|
||||
description: 'Run in IDE mode?',
|
||||
})
|
||||
.option('proxy', {
|
||||
type: 'string',
|
||||
description:
|
||||
|
@ -307,8 +302,6 @@ export async function loadCliConfig(
|
|||
const memoryImportFormat = settings.memoryImportFormat || 'tree';
|
||||
|
||||
const ideMode = settings.ideMode ?? false;
|
||||
const ideModeFeature =
|
||||
argv.ideModeFeature ?? settings.ideModeFeature ?? false;
|
||||
|
||||
const folderTrustFeature = settings.folderTrustFeature ?? false;
|
||||
const folderTrustSetting = settings.folderTrust ?? false;
|
||||
|
@ -474,7 +467,6 @@ export async function loadCliConfig(
|
|||
noBrowser: !!process.env.NO_BROWSER,
|
||||
summarizeToolOutput: settings.summarizeToolOutput,
|
||||
ideMode,
|
||||
ideModeFeature,
|
||||
chatCompression: settings.chatCompression,
|
||||
folderTrustFeature,
|
||||
folderTrust,
|
||||
|
|
|
@ -44,7 +44,6 @@ describe('SettingsSchema', () => {
|
|||
'telemetry',
|
||||
'bugCommand',
|
||||
'summarizeToolOutput',
|
||||
'ideModeFeature',
|
||||
'dnsResolutionOrder',
|
||||
'excludedProjectEnvVars',
|
||||
'disableUpdateNag',
|
||||
|
|
|
@ -395,15 +395,7 @@ export const SETTINGS_SCHEMA = {
|
|||
description: 'Settings for summarizing tool output.',
|
||||
showInDialog: false,
|
||||
},
|
||||
ideModeFeature: {
|
||||
type: 'boolean',
|
||||
label: 'IDE Mode Feature Flag',
|
||||
category: 'Advanced',
|
||||
requiresRestart: true,
|
||||
default: undefined as boolean | undefined,
|
||||
description: 'Internal feature flag for IDE mode.',
|
||||
showInDialog: false,
|
||||
},
|
||||
|
||||
dnsResolutionOrder: {
|
||||
type: 'string',
|
||||
label: 'DNS Resolution Order',
|
||||
|
|
|
@ -191,7 +191,7 @@ export async function main() {
|
|||
|
||||
await config.initialize();
|
||||
|
||||
if (config.getIdeMode() && config.getIdeModeFeature()) {
|
||||
if (config.getIdeMode()) {
|
||||
await config.getIdeClient().connect();
|
||||
logIdeConnection(config, new IdeConnectionEvent(IdeConnectionType.START));
|
||||
}
|
||||
|
|
|
@ -155,13 +155,13 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||
setFlashFallbackHandler: vi.fn(),
|
||||
getSessionId: vi.fn(() => 'test-session-id'),
|
||||
getUserTier: vi.fn().mockResolvedValue(undefined),
|
||||
getIdeModeFeature: vi.fn(() => false),
|
||||
getIdeMode: vi.fn(() => false),
|
||||
getIdeMode: vi.fn(() => true),
|
||||
getWorkspaceContext: vi.fn(() => ({
|
||||
getDirectories: vi.fn(() => []),
|
||||
})),
|
||||
getIdeClient: vi.fn(() => ({
|
||||
getCurrentIde: vi.fn(() => 'vscode'),
|
||||
getDetectedIdeDisplayName: vi.fn(() => 'VSCode'),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -130,7 +130,6 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
|||
registerCleanup(() => config.getIdeClient().disconnect());
|
||||
}, [config]);
|
||||
const shouldShowIdePrompt =
|
||||
config.getIdeModeFeature() &&
|
||||
currentIDE &&
|
||||
!config.getIdeMode() &&
|
||||
!settings.merged.hasSeenIdeIntegrationNudge &&
|
||||
|
|
|
@ -40,7 +40,6 @@ describe('ideCommand', () => {
|
|||
} as unknown as CommandContext;
|
||||
|
||||
mockConfig = {
|
||||
getIdeModeFeature: vi.fn(),
|
||||
getIdeMode: vi.fn(),
|
||||
getIdeClient: vi.fn(() => ({
|
||||
reconnect: vi.fn(),
|
||||
|
@ -60,14 +59,12 @@ describe('ideCommand', () => {
|
|||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should return null if ideModeFeature is not enabled', () => {
|
||||
vi.mocked(mockConfig.getIdeModeFeature).mockReturnValue(false);
|
||||
const command = ideCommand(mockConfig);
|
||||
it('should return null if config is not provided', () => {
|
||||
const command = ideCommand(null);
|
||||
expect(command).toBeNull();
|
||||
});
|
||||
|
||||
it('should return the ide command if ideModeFeature is enabled', () => {
|
||||
vi.mocked(mockConfig.getIdeModeFeature).mockReturnValue(true);
|
||||
it('should return the ide command', () => {
|
||||
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
||||
getCurrentIde: () => DetectedIde.VSCode,
|
||||
|
@ -85,7 +82,6 @@ describe('ideCommand', () => {
|
|||
describe('status subcommand', () => {
|
||||
const mockGetConnectionStatus = vi.fn();
|
||||
beforeEach(() => {
|
||||
vi.mocked(mockConfig.getIdeModeFeature).mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
||||
getConnectionStatus: mockGetConnectionStatus,
|
||||
getCurrentIde: () => DetectedIde.VSCode,
|
||||
|
@ -162,7 +158,6 @@ describe('ideCommand', () => {
|
|||
describe('install subcommand', () => {
|
||||
const mockInstall = vi.fn();
|
||||
beforeEach(() => {
|
||||
vi.mocked(mockConfig.getIdeModeFeature).mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
||||
getCurrentIde: () => DetectedIde.VSCode,
|
||||
|
|
|
@ -115,7 +115,7 @@ async function getIdeStatusMessageWithFiles(ideClient: IdeClient): Promise<{
|
|||
}
|
||||
|
||||
export const ideCommand = (config: Config | null): SlashCommand | null => {
|
||||
if (!config || !config.getIdeModeFeature()) {
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
const ideClient = config.getIdeClient();
|
||||
|
|
|
@ -45,7 +45,7 @@ export const ToolConfirmationMessage: React.FC<
|
|||
const handleConfirm = async (outcome: ToolConfirmationOutcome) => {
|
||||
if (confirmationDetails.type === 'edit') {
|
||||
const ideClient = config?.getIdeClient();
|
||||
if (config?.getIdeMode() && config?.getIdeModeFeature()) {
|
||||
if (config?.getIdeMode()) {
|
||||
const cliOutcome =
|
||||
outcome === ToolConfirmationOutcome.Cancel ? 'rejected' : 'accepted';
|
||||
await ideClient?.resolveDiffFromCli(
|
||||
|
@ -136,7 +136,7 @@ export const ToolConfirmationMessage: React.FC<
|
|||
value: ToolConfirmationOutcome.ProceedAlways,
|
||||
},
|
||||
);
|
||||
if (config?.getIdeMode() && config?.getIdeModeFeature()) {
|
||||
if (config?.getIdeMode()) {
|
||||
options.push({
|
||||
label: 'No (esc)',
|
||||
value: ToolConfirmationOutcome.Cancel,
|
||||
|
|
|
@ -191,7 +191,6 @@ export interface ConfigParameters {
|
|||
blockedMcpServers?: Array<{ name: string; extensionName: string }>;
|
||||
noBrowser?: boolean;
|
||||
summarizeToolOutput?: Record<string, SummarizeToolOutputSettings>;
|
||||
ideModeFeature?: boolean;
|
||||
folderTrustFeature?: boolean;
|
||||
folderTrust?: boolean;
|
||||
ideMode?: boolean;
|
||||
|
@ -240,7 +239,6 @@ export class Config {
|
|||
private readonly model: string;
|
||||
private readonly extensionContextFilePaths: string[];
|
||||
private readonly noBrowser: boolean;
|
||||
private readonly ideModeFeature: boolean;
|
||||
private readonly folderTrustFeature: boolean;
|
||||
private readonly folderTrust: boolean;
|
||||
private ideMode: boolean;
|
||||
|
@ -317,7 +315,6 @@ export class Config {
|
|||
this._blockedMcpServers = params.blockedMcpServers ?? [];
|
||||
this.noBrowser = params.noBrowser ?? false;
|
||||
this.summarizeToolOutput = params.summarizeToolOutput;
|
||||
this.ideModeFeature = params.ideModeFeature ?? false;
|
||||
this.folderTrustFeature = params.folderTrustFeature ?? false;
|
||||
this.folderTrust = params.folderTrust ?? false;
|
||||
this.ideMode = params.ideMode ?? false;
|
||||
|
@ -654,10 +651,6 @@ export class Config {
|
|||
return this.summarizeToolOutput;
|
||||
}
|
||||
|
||||
getIdeModeFeature(): boolean {
|
||||
return this.ideModeFeature;
|
||||
}
|
||||
|
||||
getIdeMode(): boolean {
|
||||
return this.ideMode;
|
||||
}
|
||||
|
|
|
@ -667,7 +667,7 @@ describe('Gemini Client (client.ts)', () => {
|
|||
});
|
||||
|
||||
describe('sendMessageStream', () => {
|
||||
it('should include editor context when ideModeFeature is enabled', async () => {
|
||||
it('should include editor context when ideMode is enabled', async () => {
|
||||
// Arrange
|
||||
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
||||
workspaceState: {
|
||||
|
@ -691,7 +691,7 @@ describe('Gemini Client (client.ts)', () => {
|
|||
},
|
||||
});
|
||||
|
||||
vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
|
||||
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
||||
|
||||
const mockStream = (async function* () {
|
||||
yield { type: 'content', value: 'Hello' };
|
||||
|
@ -751,7 +751,7 @@ ${JSON.stringify(
|
|||
});
|
||||
});
|
||||
|
||||
it('should not add context if ideModeFeature is enabled but no open files', async () => {
|
||||
it('should not add context if ideMode is enabled but no open files', async () => {
|
||||
// Arrange
|
||||
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
||||
workspaceState: {
|
||||
|
@ -759,7 +759,7 @@ ${JSON.stringify(
|
|||
},
|
||||
});
|
||||
|
||||
vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
|
||||
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
||||
|
||||
const mockStream = (async function* () {
|
||||
yield { type: 'content', value: 'Hello' };
|
||||
|
@ -798,7 +798,7 @@ ${JSON.stringify(
|
|||
);
|
||||
});
|
||||
|
||||
it('should add context if ideModeFeature is enabled and there is one active file', async () => {
|
||||
it('should add context if ideMode is enabled and there is one active file', async () => {
|
||||
// Arrange
|
||||
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
||||
workspaceState: {
|
||||
|
@ -814,7 +814,7 @@ ${JSON.stringify(
|
|||
},
|
||||
});
|
||||
|
||||
vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
|
||||
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
||||
|
||||
const mockStream = (async function* () {
|
||||
yield { type: 'content', value: 'Hello' };
|
||||
|
@ -873,7 +873,7 @@ ${JSON.stringify(
|
|||
});
|
||||
});
|
||||
|
||||
it('should add context if ideModeFeature is enabled and there are open files but no active file', async () => {
|
||||
it('should add context if ideMode is enabled and there are open files but no active file', async () => {
|
||||
// Arrange
|
||||
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
||||
workspaceState: {
|
||||
|
@ -890,7 +890,7 @@ ${JSON.stringify(
|
|||
},
|
||||
});
|
||||
|
||||
vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
|
||||
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
||||
|
||||
const mockStream = (async function* () {
|
||||
yield { type: 'content', value: 'Hello' };
|
||||
|
@ -1226,7 +1226,7 @@ ${JSON.stringify(
|
|||
beforeEach(() => {
|
||||
client['forceFullIdeContext'] = false; // Reset before each delta test
|
||||
vi.spyOn(client, 'tryCompressChat').mockResolvedValue(null);
|
||||
vi.spyOn(client['config'], 'getIdeModeFeature').mockReturnValue(true);
|
||||
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
||||
mockTurnRunFn.mockReturnValue(mockStream);
|
||||
|
||||
const mockChat: Partial<GeminiChat> = {
|
||||
|
|
|
@ -444,7 +444,7 @@ export class GeminiClient {
|
|||
yield { type: GeminiEventType.ChatCompressed, value: compressed };
|
||||
}
|
||||
|
||||
if (this.config.getIdeModeFeature() && this.config.getIdeMode()) {
|
||||
if (this.config.getIdeMode()) {
|
||||
const { contextParts, newIdeContext } = this.getIdeContextParts(
|
||||
this.forceFullIdeContext || this.getHistory().length === 0,
|
||||
);
|
||||
|
|
|
@ -6,15 +6,12 @@
|
|||
|
||||
import * as child_process from 'child_process';
|
||||
import * as process from 'process';
|
||||
import { glob } from 'glob';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { DetectedIde } from './detect-ide.js';
|
||||
|
||||
const VSCODE_COMMAND = process.platform === 'win32' ? 'code.cmd' : 'code';
|
||||
const VSCODE_COMPANION_EXTENSION_FOLDER = 'vscode-ide-companion';
|
||||
|
||||
export interface IdeInstaller {
|
||||
install(): Promise<InstallResult>;
|
||||
|
@ -103,34 +100,7 @@ class VsCodeInstaller implements IdeInstaller {
|
|||
};
|
||||
}
|
||||
|
||||
const bundleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
// The VSIX file is copied to the bundle directory as part of the build.
|
||||
let vsixFiles = glob.sync(path.join(bundleDir, '*.vsix'));
|
||||
if (vsixFiles.length === 0) {
|
||||
// If the VSIX file is not in the bundle, it might be a dev
|
||||
// environment running with `npm start`. Look for it in the original
|
||||
// package location, relative to the bundle dir.
|
||||
const devPath = path.join(
|
||||
bundleDir, // .../packages/core/dist/src/ide
|
||||
'..', // .../packages/core/dist/src
|
||||
'..', // .../packages/core/dist
|
||||
'..', // .../packages/core
|
||||
'..', // .../packages
|
||||
VSCODE_COMPANION_EXTENSION_FOLDER,
|
||||
'*.vsix',
|
||||
);
|
||||
vsixFiles = glob.sync(devPath);
|
||||
}
|
||||
if (vsixFiles.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
'Could not find the required VS Code companion extension. Please file a bug via /bug.',
|
||||
};
|
||||
}
|
||||
|
||||
const vsixPath = vsixFiles[0];
|
||||
const command = `"${commandPath}" --install-extension "${vsixPath}" --force`;
|
||||
const command = `"${commandPath}" --install-extension google.gemini-cli-vscode-ide-companion --force`;
|
||||
try {
|
||||
child_process.execSync(command, { stdio: 'pipe' });
|
||||
return {
|
||||
|
|
|
@ -62,7 +62,6 @@ describe('EditTool', () => {
|
|||
getWorkspaceContext: () => createMockWorkspaceContext(rootDir),
|
||||
getIdeClient: () => undefined,
|
||||
getIdeMode: () => false,
|
||||
getIdeModeFeature: () => false,
|
||||
// getGeminiConfig: () => ({ apiKey: 'test-api-key' }), // This was not a real Config method
|
||||
// Add other properties/methods of Config if EditTool uses them
|
||||
// Minimal other methods to satisfy Config type if needed by EditTool constructor or other direct uses:
|
||||
|
@ -810,7 +809,6 @@ describe('EditTool', () => {
|
|||
}),
|
||||
};
|
||||
(mockConfig as any).getIdeMode = () => true;
|
||||
(mockConfig as any).getIdeModeFeature = () => true;
|
||||
(mockConfig as any).getIdeClient = () => ideClient;
|
||||
});
|
||||
|
||||
|
|
|
@ -250,7 +250,6 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
|
|||
);
|
||||
const ideClient = this.config.getIdeClient();
|
||||
const ideConfirmation =
|
||||
this.config.getIdeModeFeature() &&
|
||||
this.config.getIdeMode() &&
|
||||
ideClient?.getConnectionStatus().status === IDEConnectionStatus.Connected
|
||||
? ideClient.openDiff(this.params.file_path, editData.newContent)
|
||||
|
|
|
@ -58,7 +58,6 @@ const mockConfigInternal = {
|
|||
getGeminiClient: vi.fn(), // Initialize as a plain mock function
|
||||
getIdeClient: vi.fn(),
|
||||
getIdeMode: vi.fn(() => false),
|
||||
getIdeModeFeature: vi.fn(() => false),
|
||||
getWorkspaceContext: () => createMockWorkspaceContext(rootDir),
|
||||
getApiKey: () => 'test-key',
|
||||
getModel: () => 'test-model',
|
||||
|
|
|
@ -195,7 +195,6 @@ export class WriteFileTool
|
|||
|
||||
const ideClient = this.config.getIdeClient();
|
||||
const ideConfirmation =
|
||||
this.config.getIdeModeFeature() &&
|
||||
this.config.getIdeMode() &&
|
||||
ideClient.getConnectionStatus().status === IDEConnectionStatus.Connected
|
||||
? ideClient.openDiff(params.file_path, correctedContent)
|
||||
|
|
|
@ -37,12 +37,4 @@ for (const file of sbFiles) {
|
|||
copyFileSync(join(root, file), join(bundleDir, basename(file)));
|
||||
}
|
||||
|
||||
// Find and copy all .vsix files from packages to the root of the bundle directory
|
||||
const vsixFiles = glob.sync('packages/vscode-ide-companion/*.vsix', {
|
||||
cwd: root,
|
||||
});
|
||||
for (const file of vsixFiles) {
|
||||
copyFileSync(join(root, file), join(bundleDir, basename(file)));
|
||||
}
|
||||
|
||||
console.log('Assets copied to bundle/');
|
||||
|
|
Loading…
Reference in New Issue