summaryrefslogtreecommitdiff
path: root/packages/core/src/ide/ide-installer.ts
diff options
context:
space:
mode:
authorchristine betts <[email protected]>2025-07-30 21:26:31 +0000
committerGitHub <[email protected]>2025-07-30 21:26:31 +0000
commit7bc876654254d9a11d66135735ad10f1066ad213 (patch)
tree3b21e328199f8631c0d865fdf041103c18bd261e /packages/core/src/ide/ide-installer.ts
parentc1fe6889569610878c45216556fb99424b5bcba4 (diff)
Introduce IDE mode installer (#4877)
Diffstat (limited to 'packages/core/src/ide/ide-installer.ts')
-rw-r--r--packages/core/src/ide/ide-installer.ts162
1 files changed, 162 insertions, 0 deletions
diff --git a/packages/core/src/ide/ide-installer.ts b/packages/core/src/ide/ide-installer.ts
new file mode 100644
index 00000000..725f4f7c
--- /dev/null
+++ b/packages/core/src/ide/ide-installer.ts
@@ -0,0 +1,162 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+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>;
+ isInstalled(): Promise<boolean>;
+}
+
+export interface InstallResult {
+ success: boolean;
+ message: string;
+}
+
+async function findVsCodeCommand(): Promise<string | null> {
+ // 1. Check PATH first.
+ try {
+ child_process.execSync(
+ process.platform === 'win32'
+ ? `where.exe ${VSCODE_COMMAND}`
+ : `command -v ${VSCODE_COMMAND}`,
+ { stdio: 'ignore' },
+ );
+ return VSCODE_COMMAND;
+ } catch {
+ // Not in PATH, continue to check common locations.
+ }
+
+ // 2. Check common installation locations.
+ const locations: string[] = [];
+ const platform = process.platform;
+ const homeDir = os.homedir();
+
+ if (platform === 'darwin') {
+ // macOS
+ locations.push(
+ '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
+ path.join(homeDir, 'Library/Application Support/Code/bin/code'),
+ );
+ } else if (platform === 'linux') {
+ // Linux
+ locations.push(
+ '/usr/share/code/bin/code',
+ '/snap/bin/code',
+ path.join(homeDir, '.local/share/code/bin/code'),
+ );
+ } else if (platform === 'win32') {
+ // Windows
+ locations.push(
+ path.join(
+ process.env.ProgramFiles || 'C:\\Program Files',
+ 'Microsoft VS Code',
+ 'bin',
+ 'code.cmd',
+ ),
+ path.join(
+ homeDir,
+ 'AppData',
+ 'Local',
+ 'Programs',
+ 'Microsoft VS Code',
+ 'bin',
+ 'code.cmd',
+ ),
+ );
+ }
+
+ for (const location of locations) {
+ if (fs.existsSync(location)) {
+ return location;
+ }
+ }
+
+ return null;
+}
+
+class VsCodeInstaller implements IdeInstaller {
+ private vsCodeCommand: Promise<string | null>;
+
+ constructor() {
+ this.vsCodeCommand = findVsCodeCommand();
+ }
+
+ async isInstalled(): Promise<boolean> {
+ return (await this.vsCodeCommand) !== null;
+ }
+
+ async install(): Promise<InstallResult> {
+ const commandPath = await this.vsCodeCommand;
+ if (!commandPath) {
+ return {
+ success: false,
+ message: `VS Code command-line tool not found in your PATH or common installation locations.`,
+ };
+ }
+
+ 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`;
+ try {
+ child_process.execSync(command, { stdio: 'pipe' });
+ return {
+ success: true,
+ message:
+ 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ };
+ } catch (_error) {
+ return {
+ success: false,
+ message: 'Failed to install VS Code companion extension.',
+ };
+ }
+ }
+}
+
+export function getIdeInstaller(ide: DetectedIde): IdeInstaller | null {
+ switch (ide) {
+ case 'vscode':
+ return new VsCodeInstaller();
+ default:
+ return null;
+ }
+}