Skip to content

Commit 3189ac5

Browse files
sergeibbbeamodioaxosoft-ramint
authored
Start Pull Request with AI (#4177)
* Fixes GitHub createPR URL so the title does not disappear on redirects (vscode-gitlens-private#43, #4177) * Generates PR title and message using AI: - prompt is from createCommitMessage - only diff is used (vscode-gitlens-private#43, #4177) * Includes log to generating Pull Request details (vscode-gitlens-private#43, #4177) * Uses a new AI prompt for generating Pull Request details (vscode-gitlens-private#43, #4177) * Shows one-branch diff for a PR creation by using '...' notation in diff (vscode-gitlens-private#43, #4177) * Generates title and body for GitLab merge request using AI (vscode-gitlens-private#43, #4177) * Fixes a problem with stringifying undefined branches to `origin/undefined (vscode-gitlens-private#43, #4177) * Makes AI usage in create PR optional, adds button that creates PR using AI (vscode-gitlens-private#43, #4177) * Styles the "Create PR with AI" button (vscode-gitlens-private#43, #4177) * Mentions AI assisted PR creation in the changelog (vscode-gitlens-private#43, #4177) * Hides AI PR creation button for repos that we cannot preset title and body (vscode-gitlens-private#43, #4177) * Requires AI feature access to generate a Pull Request with AI (vscode-gitlens-private#43, #4177) * Adds "(Preview)" suffix to labels of Create Pull Request with AI feature (vscode-gitlens-private#43, #4177) * Adds a new prop for specific custom instructions for create PR with AI (vscode-gitlens-private#43, #4177) * Fixes prompt/action name and settings * Updates button label --------- Co-authored-by: Eric Amodio <[email protected]> Co-authored-by: Ramin Tadayon <[email protected]>
1 parent 7c2277d commit 3189ac5

File tree

25 files changed

+354
-45
lines changed

25 files changed

+354
-45
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
99
### Added
1010

1111
- Adds AI model status and model switcher to the _Home_ view ([#4064](https://github.com/gitkraken/vscode-gitlens/issues/4064))
12+
- Adds an optional "Create with AI" button to generate pull requests using AI assistance for GitHub and GitLab.
1213
- Adds Anthropic Claude 3.7 Sonnet model for GitLens' AI features ([#4101](https://github.com/gitkraken/vscode-gitlens/issues/4101))
1314
- Adds Google Gemini 2.5 Pro (Experimental) and Gemini 2.0 Flash-Lite model for GitLens' AI features ([#4104](https://github.com/gitkraken/vscode-gitlens/issues/4104))
1415
- Adds integration with Bitbucket Cloud and Data Center ([#3916](https://github.com/gitkraken/vscode-gitlens/issues/3916))

docs/telemetry-events.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,29 @@ or
208208

209209
or
210210

211+
```typescript
212+
{
213+
'duration': number,
214+
'failed.error': string,
215+
'failed.reason': 'user-declined' | 'user-cancelled' | 'error',
216+
'input.length': number,
217+
'model.id': string,
218+
'model.provider.id': 'anthropic' | 'deepseek' | 'gemini' | 'github' | 'gitkraken' | 'huggingface' | 'openai' | 'vscode' | 'xai',
219+
'model.provider.name': string,
220+
'output.length': number,
221+
'retry.count': number,
222+
'type': 'createPullRequest',
223+
'usage.completionTokens': number,
224+
'usage.limits.limit': number,
225+
'usage.limits.resetsOn': string,
226+
'usage.limits.used': number,
227+
'usage.promptTokens': number,
228+
'usage.totalTokens': number
229+
}
230+
```
231+
232+
or
233+
211234
```typescript
212235
{
213236
'duration': number,

package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4152,7 +4152,7 @@
41524152
"preview"
41534153
]
41544154
},
4155-
"gitlens.ai.generateCloudPatchMessage.customInstructions": {
4155+
"gitlens.ai.generateCreateCloudPatch.customInstructions": {
41564156
"type": "string",
41574157
"default": null,
41584158
"markdownDescription": "Specifies custom instructions to provide to the AI provider when generating a cloud patch title and description",
@@ -4162,7 +4162,7 @@
41624162
"preview"
41634163
]
41644164
},
4165-
"gitlens.ai.generateCodeSuggestMessage.customInstructions": {
4165+
"gitlens.ai.generateCreateCodeSuggest.customInstructions": {
41664166
"type": "string",
41674167
"default": null,
41684168
"markdownDescription": "Specifies custom instructions to provide to the AI provider when generating a code suggest title and description",
@@ -4171,6 +4171,16 @@
41714171
"tags": [
41724172
"preview"
41734173
]
4174+
},
4175+
"gitlens.ai.generateCreatePullRequest.customInstructions": {
4176+
"type": "string",
4177+
"default": null,
4178+
"markdownDescription": "Specifies custom instructions to provide to the AI provider when generating a pull request title and description",
4179+
"scope": "window",
4180+
"order": 500,
4181+
"tags": [
4182+
"preview"
4183+
]
41744184
}
41754185
}
41764186
},

src/api/gitlens.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Disposable } from 'vscode';
2+
import type { Sources } from '../constants.telemetry';
23

34
export type { Disposable } from 'vscode';
45

@@ -24,6 +25,8 @@ export interface CreatePullRequestActionContext {
2425
readonly url?: string;
2526
}
2627
| undefined;
28+
readonly source?: Sources;
29+
readonly useAI?: boolean;
2730
}
2831

2932
export interface OpenPullRequestActionContext {

src/commands/createPullRequestOnRemote.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { window } from 'vscode';
2+
import type { Sources } from '../constants.telemetry';
23
import type { Container } from '../container';
34
import type { GitRemote } from '../git/models/remote';
45
import type { RemoteResource } from '../git/models/remoteResource';
@@ -17,6 +18,8 @@ export interface CreatePullRequestOnRemoteCommandArgs {
1718
repoPath: string;
1819

1920
clipboard?: boolean;
21+
source?: Sources;
22+
useAI?: boolean;
2023
}
2124

2225
@command()
@@ -72,8 +75,33 @@ export class CreatePullRequestOnRemoteCommand extends GlCommandBase {
7275
},
7376
compare: {
7477
branch: args.compare,
75-
remote: { path: compareRemote.path, url: compareRemote.url },
78+
remote: { path: compareRemote.path, url: compareRemote.url, name: compareRemote.name },
7679
},
80+
describePullRequest: !args.useAI
81+
? undefined
82+
: async (completedResource: RemoteResource & { type: RemoteResourceType.CreatePullRequest }) => {
83+
const base = completedResource.base;
84+
const compare = completedResource.compare;
85+
if (!base?.remote || !compare?.remote || !base?.branch || !compare?.branch) {
86+
return undefined;
87+
}
88+
const baseRef = `${base.remote.name}/${base.branch}`;
89+
const compareRef = `${compare.remote.name}/${compare.branch}`;
90+
try {
91+
const result = await this.container.ai.generatePullRequestMessage(
92+
repo,
93+
baseRef,
94+
compareRef,
95+
{
96+
source: args.source ?? 'scm-input',
97+
},
98+
);
99+
return result?.parsed;
100+
} catch (e) {
101+
void window.showErrorMessage(`Unable to generate pull request details: ${e}`);
102+
return undefined;
103+
}
104+
},
77105
};
78106

79107
void (await executeCommand<OpenOnRemoteCommandArgs>('gitlens.openOnRemote', {

src/config.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,24 @@ interface AIConfig {
212212
readonly generateChangelog: {
213213
readonly customInstructions: string;
214214
};
215+
readonly generatePullRequestMessage: {
216+
readonly customInstructions: string;
217+
readonly enabled: boolean;
218+
};
215219
readonly generateCommitMessage: {
216220
readonly customInstructions: string;
217221
readonly enabled: boolean;
218222
};
219223
readonly generateStashMessage: {
220224
readonly customInstructions: string;
221225
};
222-
readonly generateCloudPatchMessage: {
226+
readonly generateCreateCloudPatch: {
227+
readonly customInstructions: string;
228+
};
229+
readonly generateCreateCodeSuggest: {
223230
readonly customInstructions: string;
224231
};
225-
readonly generateCodeSuggestMessage: {
232+
readonly generateCreatePullRequest: {
226233
readonly customInstructions: string;
227234
};
228235
readonly gitkraken: {

src/constants.telemetry.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,15 @@ export interface AIGenerateChangelogEventData extends AIEventDataBase {
344344
type: 'changelog';
345345
}
346346

347+
export interface AIGenerateCreatePullRequestEventData extends AIEventDataBase {
348+
type: 'createPullRequest';
349+
}
350+
347351
type AIGenerateEvent =
348352
| AIGenerateCommitEventData
349353
| AIGenerateDraftEventData
350354
| AIGenerateStashEventData
355+
| AIGenerateCreatePullRequestEventData
351356
| AIGenerateChangelogEventData;
352357

353358
export type AISwitchModelEvent =

src/env/node/git/sub-providers/diff.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ export class DiffGitSubProvider implements GitDiffSubProvider {
8989
repoPath: string,
9090
to: string,
9191
from?: string,
92-
options?: { context?: number; includeUntracked: boolean; uris?: Uri[] },
92+
options?: { context?: number; includeUntracked: boolean; uris?: Uri[]; notation?: '..' | '...' },
9393
): Promise<GitDiff | undefined> {
9494
const scope = getLogScope();
9595
const args = [`-U${options?.context ?? 3}`];
9696

97-
from = prepareToFromDiffArgs(to, from, args);
97+
from = prepareToFromDiffArgs(to, from, args, options?.notation);
9898

9999
let paths: Set<string> | undefined;
100100
let untrackedPaths: string[] | undefined;
@@ -630,7 +630,7 @@ export class DiffGitSubProvider implements GitDiffSubProvider {
630630
}
631631
}
632632
}
633-
function prepareToFromDiffArgs(to: string, from: string | undefined, args: string[]) {
633+
function prepareToFromDiffArgs(to: string, from: string | undefined, args: string[], notation?: '..' | '...'): string {
634634
if (to === uncommitted) {
635635
if (from != null) {
636636
args.push(from);
@@ -656,6 +656,8 @@ function prepareToFromDiffArgs(to: string, from: string | undefined, args: strin
656656
}
657657
} else if (to === '') {
658658
args.push(from);
659+
} else if (notation != null) {
660+
args.push(`${from}${notation}${to}`);
659661
} else {
660662
args.push(from, to);
661663
}

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ function registerBuiltInActionRunners(container: Container): void {
309309
: ctx.branch.name,
310310
remote: ctx.remote?.name ?? '',
311311
repoPath: ctx.repoPath,
312+
source: ctx.source,
313+
useAI: ctx.useAI,
312314
}));
313315
},
314316
}),

src/features.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,17 @@ export type ProFeatures =
4040
| 'startWork'
4141
| 'associateIssueWithBranch'
4242
| ProAIFeatures;
43-
export type ProAIFeatures = 'generateStashMessage' | 'explainCommit' | 'cloudPatchGenerateTitleAndDescription';
43+
export type ProAIFeatures = 'explainCommit' | 'generateCreateDraft' | 'generateStashMessage';
4444

4545
export type AdvancedFeatures = AdvancedAIFeatures;
46-
export type AdvancedAIFeatures = 'generateChangelog';
46+
export type AdvancedAIFeatures = 'generateChangelog' | 'generateCreatePullRequest';
4747

4848
export type AIFeatures = ProAIFeatures | AdvancedAIFeatures;
4949

5050
export function isAdvancedFeature(feature: PlusFeatures): feature is AdvancedFeatures {
5151
switch (feature) {
5252
case 'generateChangelog':
53+
case 'generateCreatePullRequest':
5354
return true;
5455
default:
5556
return false;

0 commit comments

Comments
 (0)