summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/cli/src/ui/commands/ideCommand.test.ts9
-rw-r--r--packages/cli/src/ui/commands/ideCommand.ts36
-rw-r--r--packages/core/src/ide/detect-ide.ts9
-rw-r--r--packages/core/src/ide/ide-installer.test.ts30
-rw-r--r--packages/core/src/ide/ide-installer.ts13
5 files changed, 43 insertions, 54 deletions
diff --git a/packages/cli/src/ui/commands/ideCommand.test.ts b/packages/cli/src/ui/commands/ideCommand.test.ts
index 3c73549c..4f2b7af2 100644
--- a/packages/cli/src/ui/commands/ideCommand.test.ts
+++ b/packages/cli/src/ui/commands/ideCommand.test.ts
@@ -65,6 +65,7 @@ describe('ideCommand', () => {
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
getCurrentIde: () => DetectedIde.VSCode,
+ getDetectedIdeDisplayName: () => 'VS Code',
} as ReturnType<Config['getIdeClient']>);
const command = ideCommand(mockConfig);
expect(command).not.toBeNull();
@@ -82,6 +83,7 @@ describe('ideCommand', () => {
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
getConnectionStatus: mockGetConnectionStatus,
getCurrentIde: () => DetectedIde.VSCode,
+ getDetectedIdeDisplayName: () => 'VS Code',
} as unknown as ReturnType<Config['getIdeClient']>);
});
@@ -96,7 +98,7 @@ describe('ideCommand', () => {
expect(result).toEqual({
type: 'message',
messageType: 'info',
- content: '🟢 Connected',
+ content: '🟢 Connected to VS Code',
});
});
@@ -155,6 +157,7 @@ describe('ideCommand', () => {
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
getCurrentIde: () => DetectedIde.VSCode,
getConnectionStatus: vi.fn(),
+ getDetectedIdeDisplayName: () => 'VS Code',
} as unknown as ReturnType<Config['getIdeClient']>);
vi.mocked(core.getIdeInstaller).mockReturnValue({
install: mockInstall,
@@ -180,7 +183,7 @@ describe('ideCommand', () => {
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'info',
- text: `Installing IDE companion extension...`,
+ text: `Installing IDE companion...`,
}),
expect.any(Number),
);
@@ -210,7 +213,7 @@ describe('ideCommand', () => {
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'info',
- text: `Installing IDE companion extension...`,
+ text: `Installing IDE companion...`,
}),
expect.any(Number),
);
diff --git a/packages/cli/src/ui/commands/ideCommand.ts b/packages/cli/src/ui/commands/ideCommand.ts
index 1da7d6b0..c6d65264 100644
--- a/packages/cli/src/ui/commands/ideCommand.ts
+++ b/packages/cli/src/ui/commands/ideCommand.ts
@@ -6,6 +6,7 @@
import {
Config,
+ DetectedIde,
IDEConnectionStatus,
getIdeDisplayName,
getIdeInstaller,
@@ -19,12 +20,27 @@ import {
import { SettingScope } from '../../config/settings.js';
export const ideCommand = (config: Config | null): SlashCommand | null => {
- if (!config?.getIdeModeFeature()) {
+ if (!config || !config.getIdeModeFeature()) {
return null;
}
- const currentIDE = config.getIdeClient().getCurrentIde();
- if (!currentIDE) {
- return null;
+ const ideClient = config.getIdeClient();
+ const currentIDE = ideClient.getCurrentIde();
+ if (!currentIDE || !ideClient.getDetectedIdeDisplayName()) {
+ return {
+ name: 'ide',
+ description: 'manage IDE integration',
+ kind: CommandKind.BUILT_IN,
+ action: (): SlashCommandActionReturn =>
+ ({
+ type: 'message',
+ messageType: 'error',
+ content: `IDE integration is not supported in your current environment. To use this feature, run Gemini CLI in one of these supported IDEs: ${Object.values(
+ DetectedIde,
+ )
+ .map((ide) => getIdeDisplayName(ide))
+ .join(', ')}`,
+ }) as const,
+ };
}
const ideSlashCommand: SlashCommand = {
@@ -39,13 +55,13 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
description: 'check status of IDE integration',
kind: CommandKind.BUILT_IN,
action: (_context: CommandContext): SlashCommandActionReturn => {
- const connection = config.getIdeClient().getConnectionStatus();
- switch (connection?.status) {
+ const connection = ideClient.getConnectionStatus();
+ switch (connection.status) {
case IDEConnectionStatus.Connected:
return {
type: 'message',
messageType: 'info',
- content: `🟢 Connected`,
+ content: `🟢 Connected to ${ideClient.getDetectedIdeDisplayName()}`,
} as const;
case IDEConnectionStatus.Connecting:
return {
@@ -70,7 +86,7 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
const installCommand: SlashCommand = {
name: 'install',
- description: `install required IDE companion ${getIdeDisplayName(currentIDE)} extension `,
+ description: `install required IDE companion for ${ideClient.getDetectedIdeDisplayName()}`,
kind: CommandKind.BUILT_IN,
action: async (context) => {
const installer = getIdeInstaller(currentIDE);
@@ -78,7 +94,7 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
context.ui.addItem(
{
type: 'error',
- text: 'No installer available for your configured IDE.',
+ text: `No installer is available for ${ideClient.getDetectedIdeDisplayName()}. Please install the IDE companion manually from its marketplace.`,
},
Date.now(),
);
@@ -88,7 +104,7 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
context.ui.addItem(
{
type: 'info',
- text: `Installing IDE companion extension...`,
+ text: `Installing IDE companion...`,
},
Date.now(),
);
diff --git a/packages/core/src/ide/detect-ide.ts b/packages/core/src/ide/detect-ide.ts
index ae46789e..f3d8cc63 100644
--- a/packages/core/src/ide/detect-ide.ts
+++ b/packages/core/src/ide/detect-ide.ts
@@ -11,9 +11,12 @@ export enum DetectedIde {
export function getIdeDisplayName(ide: DetectedIde): string {
switch (ide) {
case DetectedIde.VSCode:
- return 'VSCode';
- default:
- throw new Error(`Unsupported IDE: ${ide}`);
+ return 'VS Code';
+ default: {
+ // This ensures that if a new IDE is added to the enum, we get a compile-time error.
+ const exhaustiveCheck: never = ide;
+ return exhaustiveCheck;
+ }
}
}
diff --git a/packages/core/src/ide/ide-installer.test.ts b/packages/core/src/ide/ide-installer.test.ts
index 83459d6b..698c3173 100644
--- a/packages/core/src/ide/ide-installer.test.ts
+++ b/packages/core/src/ide/ide-installer.test.ts
@@ -45,32 +45,6 @@ describe('ide-installer', () => {
vi.restoreAllMocks();
});
- describe('isInstalled', () => {
- it('should return true if command is in PATH', async () => {
- expect(await installer.isInstalled()).toBe(true);
- });
-
- it('should return true if command is in a known location', async () => {
- vi.spyOn(child_process, 'execSync').mockImplementation(() => {
- throw new Error('Command not found');
- });
- vi.spyOn(fs, 'existsSync').mockReturnValue(true);
- // Re-create the installer so it re-runs findVsCodeCommand
- installer = getIdeInstaller(DetectedIde.VSCode)!;
- expect(await installer.isInstalled()).toBe(true);
- });
-
- it('should return false if command is not found', async () => {
- vi.spyOn(child_process, 'execSync').mockImplementation(() => {
- throw new Error('Command not found');
- });
- vi.spyOn(fs, 'existsSync').mockReturnValue(false);
- // Re-create the installer so it re-runs findVsCodeCommand
- installer = getIdeInstaller(DetectedIde.VSCode)!;
- expect(await installer.isInstalled()).toBe(false);
- });
- });
-
describe('install', () => {
it('should return a failure message if VS Code is not installed', async () => {
vi.spyOn(child_process, 'execSync').mockImplementation(() => {
@@ -81,9 +55,7 @@ describe('ide-installer', () => {
installer = getIdeInstaller(DetectedIde.VSCode)!;
const result = await installer.install();
expect(result.success).toBe(false);
- expect(result.message).toContain(
- 'not found in your PATH or common installation locations',
- );
+ expect(result.message).toContain('VS Code CLI not found');
});
});
});
diff --git a/packages/core/src/ide/ide-installer.ts b/packages/core/src/ide/ide-installer.ts
index 725f4f7c..7db8e2d2 100644
--- a/packages/core/src/ide/ide-installer.ts
+++ b/packages/core/src/ide/ide-installer.ts
@@ -18,7 +18,6 @@ const VSCODE_COMPANION_EXTENSION_FOLDER = 'vscode-ide-companion';
export interface IdeInstaller {
install(): Promise<InstallResult>;
- isInstalled(): Promise<boolean>;
}
export interface InstallResult {
@@ -95,16 +94,12 @@ class VsCodeInstaller implements IdeInstaller {
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.`,
+ message: `VS Code CLI not found. Please ensure 'code' is in your system's PATH. For help, see https://code.visualstudio.com/docs/configure/command-line#_code-is-not-recognized-as-an-internal-or-external-command. You can also install the companion extension manually from the VS Code marketplace.`,
};
}
@@ -141,12 +136,12 @@ class VsCodeInstaller implements IdeInstaller {
return {
success: true,
message:
- 'VS Code companion extension installed successfully. Restart gemini-cli in a fresh terminal window.',
+ 'VS Code companion extension was installed successfully. Please restart your terminal to complete the setup.',
};
} catch (_error) {
return {
success: false,
- message: 'Failed to install VS Code companion extension.',
+ message: `Failed to install VS Code companion extension. Please try installing it manually from the VS Code marketplace.`,
};
}
}
@@ -154,7 +149,7 @@ class VsCodeInstaller implements IdeInstaller {
export function getIdeInstaller(ide: DetectedIde): IdeInstaller | null {
switch (ide) {
- case 'vscode':
+ case DetectedIde.VSCode:
return new VsCodeInstaller();
default:
return null;