Skip to content

Commit fa33618

Browse files
Improvements from feedback
1 parent 746e78b commit fa33618

File tree

2 files changed

+111
-79
lines changed

2 files changed

+111
-79
lines changed

src/constants.storage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export type GlobalStorage = {
8383
// Value based on `currentOnboardingVersion` in composer's protocol
8484
'composer:onboarding:dismissed': string;
8585
'composer:onboarding:stepReached': number;
86-
'gk:cli:install': { status: 'attempted' | 'unsupported' | 'completed'; attempts: number };
86+
'gk:cli:install': { status: 'attempted' | 'unsupported' | 'completed'; attempts: number; version?: string };
8787
'gk:cli:corePath': string;
8888
'gk:cli:path': string;
8989
'home:sections:collapsed': string[];

src/env/node/gk/cli/integration.ts

Lines changed: 110 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ export class GkCliIntegrationProvider implements Disposable {
9898
if (this.container.telemetry.enabled) {
9999
this.container.telemetry.sendEvent('mcp/setup/started', { source: commandSource });
100100
}
101+
102+
if (isWeb) {
103+
void window.showErrorMessage('GitKraken MCP installation is not supported on this platform.');
104+
if (this.container.telemetry.enabled) {
105+
this.container.telemetry.sendEvent('mcp/setup/failed', {
106+
reason: 'web environment unsupported',
107+
source: commandSource,
108+
});
109+
}
110+
return;
111+
}
112+
101113
const appName = toMcpInstallProvider(await getHostAppName());
102114
if (appName == null) {
103115
void window.showInformationMessage(`Failed to install MCP integration: Could not determine app name`);
@@ -125,80 +137,74 @@ export class GkCliIntegrationProvider implements Disposable {
125137
return;
126138
}
127139

128-
let cliInstall = this.container.storage.get('gk:cli:install');
129-
let cliPath = this.container.storage.get('gk:cli:path');
130-
let cliProxyFileExists = true;
131-
if (cliPath != null) {
132-
try {
133-
await workspace.fs.stat(
134-
Uri.joinPath(Uri.file(cliPath), getPlatform() === 'windows' ? 'gk.exe' : 'gk'),
135-
);
136-
} catch {
137-
cliProxyFileExists = false;
138-
}
139-
}
140-
if (cliInstall?.status !== 'completed' || cliPath == null || !cliProxyFileExists) {
141-
try {
142-
await window.withProgress(
143-
{
144-
location: ProgressLocation.Notification,
145-
title: 'Setting up MCP integration...',
146-
cancellable: false,
147-
},
148-
async () => {
149-
cliVersion = await this.installCLI();
150-
},
151-
);
152-
} catch (ex) {
153-
let failureReason = 'unknown error';
154-
if (ex instanceof CLIInstallError) {
155-
switch (ex.reason) {
156-
case CLIInstallErrorReason.WebEnvironmentUnsupported:
157-
void window.showErrorMessage(
158-
'MCP installation is not supported in the web environment.',
159-
);
160-
failureReason = 'web environment unsupported';
161-
break;
162-
case CLIInstallErrorReason.UnsupportedPlatform:
163-
void window.showErrorMessage('MCP installation is not supported on this platform.');
164-
failureReason = 'unsupported platform';
165-
break;
166-
case CLIInstallErrorReason.ProxyUrlFetch:
167-
case CLIInstallErrorReason.ProxyUrlFormat:
168-
case CLIInstallErrorReason.ProxyFetch:
169-
case CLIInstallErrorReason.ProxyDownload:
170-
case CLIInstallErrorReason.ProxyExtract:
171-
case CLIInstallErrorReason.CoreDirectory:
172-
case CLIInstallErrorReason.CoreInstall:
173-
void window.showErrorMessage('Failed to install MCP server locally.');
174-
failureReason = 'local installation failed';
175-
break;
176-
default:
177-
void window.showErrorMessage(
178-
`Failed to install MCP integration: ${ex instanceof Error ? ex.message : 'Unknown error during installation'}`,
179-
);
180-
break;
181-
}
140+
let cliVersion: string | undefined;
141+
let cliPath: string | undefined;
142+
try {
143+
await window.withProgress(
144+
{
145+
location: ProgressLocation.Notification,
146+
title: 'Setting up the GitKraken MCP...',
147+
cancellable: false,
148+
},
149+
async () => {
150+
const { cliVersion: installedVersion, cliPath: installedPath } = await this.installCLI(
151+
false,
152+
source,
153+
);
154+
cliVersion = installedVersion;
155+
cliPath = installedPath;
156+
},
157+
);
158+
} catch (ex) {
159+
let failureReason = 'unknown error';
160+
if (ex instanceof CLIInstallError) {
161+
switch (ex.reason) {
162+
case CLIInstallErrorReason.WebEnvironmentUnsupported:
163+
void window.showErrorMessage(
164+
'GitKraken MCP installation is not supported on this platform.',
165+
);
166+
failureReason = 'web environment unsupported';
167+
break;
168+
case CLIInstallErrorReason.UnsupportedPlatform:
169+
void window.showErrorMessage(
170+
'GitKraken MCP installation is not supported on this platform.',
171+
);
172+
failureReason = 'unsupported platform';
173+
break;
174+
case CLIInstallErrorReason.ProxyUrlFetch:
175+
case CLIInstallErrorReason.ProxyUrlFormat:
176+
case CLIInstallErrorReason.ProxyFetch:
177+
case CLIInstallErrorReason.ProxyDownload:
178+
case CLIInstallErrorReason.ProxyExtract:
179+
case CLIInstallErrorReason.CoreDirectory:
180+
case CLIInstallErrorReason.CoreInstall:
181+
void window.showErrorMessage('Failed to install the GitKraken MCP server locally.');
182+
failureReason = 'local installation failed';
183+
break;
184+
default:
185+
void window.showErrorMessage(
186+
`Failed to install the GitKraken MCP integration: ${ex instanceof Error ? ex.message : 'Unknown error.'}`,
187+
);
188+
break;
182189
}
190+
}
183191

184-
if (this.container.telemetry.enabled) {
185-
this.container.telemetry.sendEvent('mcp/setup/failed', {
186-
reason: failureReason,
187-
'error.message': ex instanceof Error ? ex.message : 'Unknown error during installation',
188-
source: commandSource,
189-
});
190-
}
192+
if (this.container.telemetry.enabled) {
193+
this.container.telemetry.sendEvent('mcp/setup/failed', {
194+
reason: failureReason,
195+
'error.message': ex instanceof Error ? ex.message : 'Unknown error',
196+
source: commandSource,
197+
});
191198
}
199+
return;
192200
}
193201

194-
cliInstall = this.container.storage.get('gk:cli:install');
195-
cliPath = this.container.storage.get('gk:cli:path');
196-
if (cliInstall?.status !== 'completed' || cliPath == null) {
197-
void window.showErrorMessage('Failed to install MCP integration: Unknown error during installation.');
202+
if (cliPath == null) {
203+
void window.showErrorMessage('Failed to install the GitKraken MCP: Unknown error.');
198204
if (this.container.telemetry.enabled) {
199205
this.container.telemetry.sendEvent('mcp/setup/failed', {
200206
reason: 'unknown error',
201-
'error.message': 'Unknown error during installation',
207+
'error.message': 'Unknown error',
202208
source: commandSource,
203209
'cli.version': cliVersion,
204210
});
@@ -208,7 +214,7 @@ export class GkCliIntegrationProvider implements Disposable {
208214

209215
if (appName !== 'cursor' && appName !== 'vscode' && appName !== 'vscode-insiders') {
210216
const confirmation = await window.showInformationMessage(
211-
`MCP configured successfully. Click 'Finish' to add it to your MCP server list and complete the installation.`,
217+
`GitKraken MCP installed successfully. Click 'Finish' to add it to your MCP server list and complete the setup.`,
212218
{ modal: true },
213219
{ title: 'Finish' },
214220
{ title: 'Cancel', isCloseAffordance: true },
@@ -234,6 +240,13 @@ export class GkCliIntegrationProvider implements Disposable {
234240

235241
output = output.trim();
236242
if (output === 'GitKraken MCP Server Successfully Installed!') {
243+
if (this.container.telemetry.enabled) {
244+
this.container.telemetry.sendEvent('mcp/setup/completed', {
245+
requiresUserCompletion: false,
246+
source: commandSource,
247+
'cli.version': cliVersion,
248+
});
249+
}
237250
return;
238251
} else if (output.includes('not a supported MCP client')) {
239252
if (this.container.telemetry.enabled) {
@@ -260,15 +273,14 @@ export class GkCliIntegrationProvider implements Disposable {
260273
});
261274
}
262275
Logger.error(`Unexpected output from mcp install command: ${output}`, scope);
263-
void window.showErrorMessage(`Failed to install MCP integration: error getting install URL`);
276+
void window.showErrorMessage(`Failed to install the GitKrakenMCP integration: unknown error`);
264277
return;
265278
}
266279

267280
await openUrl(output);
268281
if (this.container.telemetry.enabled) {
269282
this.container.telemetry.sendEvent('mcp/setup/completed', {
270-
requiresUserCompletion:
271-
appName === 'cursor' || appName === 'vscode' || appName === 'vscode-insiders',
283+
requiresUserCompletion: true,
272284
source: commandSource,
273285
'cli.version': cliVersion,
274286
});
@@ -278,24 +290,41 @@ export class GkCliIntegrationProvider implements Disposable {
278290
if (this.container.telemetry.enabled) {
279291
this.container.telemetry.sendEvent('mcp/setup/failed', {
280292
reason: 'unknown error',
281-
'error.message': ex instanceof Error ? ex.message : 'Unknown error during installation',
293+
'error.message': ex instanceof Error ? ex.message : 'Unknown error',
282294
source: commandSource,
283295
'cli.version': cliVersion,
284296
});
285297
}
286298

287299
void window.showErrorMessage(
288-
`Failed to install MCP integration: ${ex instanceof Error ? ex.message : String(ex)}`,
300+
`Failed to install the GitKraken MCP integration: ${ex instanceof Error ? ex.message : 'Unknown error'}`,
289301
);
290302
}
291303
}
292304

293305
@gate()
294-
private async installCLI(autoInstall?: boolean, source?: Sources): Promise<string | undefined> {
306+
private async installCLI(
307+
autoInstall?: boolean,
308+
source?: Sources,
309+
): Promise<{ cliVersion?: string; cliPath?: string }> {
295310
let attempts = 0;
296311
let cliVersion: string | undefined;
312+
let cliPath: string | undefined;
313+
const cliInstall = this.container.storage.get('gk:cli:install');
314+
if (autoInstall) {
315+
if (cliInstall?.status === 'completed') {
316+
cliVersion = cliInstall.version;
317+
cliPath = this.container.storage.get('gk:cli:path');
318+
return { cliVersion: cliVersion, cliPath: cliPath };
319+
} else if (
320+
cliInstall?.status === 'unsupported' ||
321+
(cliInstall?.status === 'attempted' && cliInstall.attempts >= 5)
322+
) {
323+
return { cliVersion: undefined, cliPath: undefined };
324+
}
325+
}
326+
297327
try {
298-
const cliInstall = this.container.storage.get('gk:cli:install');
299328
attempts = cliInstall?.attempts ?? 0;
300329
attempts += 1;
301330
if (this.container.telemetry.enabled) {
@@ -456,7 +485,7 @@ export class GkCliIntegrationProvider implements Disposable {
456485
// Use the run function to extract the installer file from the installer zip
457486
if (platform === 'windows') {
458487
// On Windows, use PowerShell to extract the zip file.
459-
// Force overwrite if the file already exists and the force param is true
488+
// Force overwrite if the file already exists with -Force
460489
await run(
461490
'powershell.exe',
462491
[
@@ -466,7 +495,7 @@ export class GkCliIntegrationProvider implements Disposable {
466495
'utf8',
467496
);
468497
} else {
469-
// On Unix-like systems, use the unzip command to extract the zip file
498+
// On Unix-like systems, use the unzip command to extract the zip file, forcing overwrite with -o
470499
await run('unzip', ['-o', cliProxyZipFilePath.fsPath, '-d', globalStoragePath.fsPath], 'utf8');
471500
}
472501

@@ -475,8 +504,11 @@ export class GkCliIntegrationProvider implements Disposable {
475504
globalStoragePath,
476505
platform === 'windows' ? 'gk.exe' : 'gk',
477506
);
507+
508+
// This will throw if the file doesn't exist
478509
await workspace.fs.stat(cliExtractedProxyFilePath);
479510
void this.container.storage.store('gk:cli:path', globalStoragePath.fsPath).catch();
511+
cliPath = globalStoragePath.fsPath;
480512
} catch (ex) {
481513
throw new CLIInstallError(
482514
CLIInstallErrorReason.ProxyExtract,
@@ -501,7 +533,7 @@ export class GkCliIntegrationProvider implements Disposable {
501533

502534
Logger.log('CLI install completed.');
503535
void this.container.storage
504-
.store('gk:cli:install', { status: 'completed', attempts: attempts })
536+
.store('gk:cli:install', { status: 'completed', attempts: attempts, version: cliVersion })
505537
.catch();
506538
if (this.container.telemetry.enabled) {
507539
this.container.telemetry.sendEvent('cli/install/succeeded', {
@@ -553,7 +585,7 @@ export class GkCliIntegrationProvider implements Disposable {
553585
}
554586
}
555587

556-
return cliVersion;
588+
return { cliVersion: cliVersion, cliPath: cliPath };
557589
}
558590

559591
private async runCLICommand(

0 commit comments

Comments
 (0)