Skip to content

Commit b2ebd4b

Browse files
committed
Add "resolve comment" tool
1 parent 66058c0 commit b2ebd4b

File tree

6 files changed

+127
-5
lines changed

6 files changed

+127
-5
lines changed

package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4039,6 +4039,33 @@
40394039
"canBeReferencedInPrompt": true,
40404040
"userDescription": "%languageModelTools.github-pull-request_openPullRequest.description%",
40414041
"when": "config.githubPullRequests.experimental.chat"
4042+
},
4043+
{
4044+
"name": "github-pull-request_resolveReviewThread",
4045+
"tags": [
4046+
"github",
4047+
"pull request",
4048+
"review"
4049+
],
4050+
"toolReferenceName": "resolveReviewThread",
4051+
"displayName": "%languageModelTools.github-pull-request_resolveReviewThread.displayName%",
4052+
"modelDescription": "Resolve a review thread on the active GitHub pull request. Use the threadId from the reviewThreads array returned by the activePullRequest tool. Only resolves threads where canResolve is true and isResolved is false.",
4053+
"icon": "$(pass)",
4054+
"canBeReferencedInPrompt": true,
4055+
"userDescription": "%languageModelTools.github-pull-request_resolveReviewThread.description%",
4056+
"when": "config.githubPullRequests.experimental.chat",
4057+
"inputSchema": {
4058+
"type": "object",
4059+
"properties": {
4060+
"threadId": {
4061+
"type": "string",
4062+
"description": "The GraphQL node ID of the review thread to resolve. Obtain this from the id field in the reviewThreads array of the activePullRequest tool output."
4063+
}
4064+
},
4065+
"required": [
4066+
"threadId"
4067+
]
4068+
}
40424069
}
40434070
]
40444071
},

package.nls.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,5 +445,7 @@
445445
"languageModelTools.github-pull-request_pullRequestStatusChecks.displayName": "Pull Request Status Checks",
446446
"languageModelTools.github-pull-request_pullRequestStatusChecks.description": "Get the status checks and CI results for a GitHub pull request.",
447447
"languageModelTools.github-pull-request_openPullRequest.displayName": "Open Pull Request",
448-
"languageModelTools.github-pull-request_openPullRequest.description": "Get information about the open GitHub pull request. This information includes: comments, files changed, pull request title + description, and pull request state."
448+
"languageModelTools.github-pull-request_openPullRequest.description": "Get information about the open GitHub pull request. This information includes: comments, files changed, pull request title + description, and pull request state.",
449+
"languageModelTools.github-pull-request_resolveReviewThread.displayName": "Resolve Review Thread",
450+
"languageModelTools.github-pull-request_resolveReviewThread.description": "Resolve a review thread on the active GitHub pull request."
449451
}

src/lm/skills/address-pr-comments/SKILL.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ Call the `github-pull-request_activePullRequest` tool.
3030

3131
From the tool result, collect all feedback that needs action:
3232

33-
- **`comments`** array: inline review thread comments where `commentState` is `"unresolved"`
33+
- **`reviewThreads`** array: inline review thread objects with an `id`, `isResolved` flag, `canResolve` flag, `file` path, and nested `comments`. Focus on threads where `isResolved` is `false`.
3434
- **`timelineComments`** array: general PR comments and reviews where `commentType` is `"CHANGES_REQUESTED"` or `"COMMENTED"`
3535

36-
Group related comments by file (`file` field) to handle them efficiently.
36+
Group related threads by file (`file` field) to handle them efficiently.
3737

3838
### 3. Plan Changes
3939

@@ -54,10 +54,17 @@ Work through the grouped comments file by file:
5454
### 5. Verify
5555

5656
After all changes are made:
57-
- Review that each originally unresolved comment has a corresponding code change or a note about why no code change was needed.
57+
- Review that each originally unresolved thread has a corresponding code change or a note about why no code change was needed.
5858
- Ensure no unrelated code was modified
5959

60-
### 6. Summarize
60+
### 6. Resolve Threads
61+
62+
For each thread that was addressed (either by a code change or by a deliberate decision not to change):
63+
- Call `github-pull-request_resolveReviewThread` with the `id` from the `reviewThreads` array.
64+
- Only resolve threads where `canResolve` is `true`.
65+
- Skip threads that are already resolved (`isResolved: true`) or where `canResolve` is `false`.
66+
67+
### 7. Summarize
6168

6269
Provide a concise summary of:
6370
- Which comments were addressed and what changes were made

src/lm/tools/activePullRequestTool.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ export abstract class PullRequestTool implements vscode.LanguageModelTool<FetchI
7474
body: pullRequest.body,
7575
author: pullRequest.author,
7676
assignees: pullRequest.assignees,
77+
reviewThreads: pullRequest.reviewThreadsCache.map(thread => {
78+
return {
79+
id: thread.id,
80+
isResolved: thread.isResolved,
81+
canResolve: thread.viewerCanResolve,
82+
file: thread.path,
83+
comments: thread.comments.map(c => ({
84+
author: c.user?.login,
85+
body: c.body,
86+
})),
87+
};
88+
}),
7789
comments: pullRequest.comments.map(comment => {
7890
return {
7991
author: comment.user?.login,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
'use strict';
6+
7+
import * as vscode from 'vscode';
8+
import { PullRequestModel } from '../../github/pullRequestModel';
9+
import { RepositoriesManager } from '../../github/repositoriesManager';
10+
11+
interface ResolveReviewThreadToolParameters {
12+
threadId: string;
13+
}
14+
15+
export class ResolveReviewThreadTool implements vscode.LanguageModelTool<ResolveReviewThreadToolParameters> {
16+
public static readonly toolId = 'github-pull-request_resolveReviewThread';
17+
18+
constructor(private readonly folderManagers: RepositoriesManager) { }
19+
20+
private _findActivePullRequest(): PullRequestModel | undefined {
21+
const folderManager = this.folderManagers.folderManagers.find((manager) => manager.activePullRequest);
22+
return folderManager?.activePullRequest;
23+
}
24+
25+
async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<ResolveReviewThreadToolParameters>): Promise<vscode.PreparedToolInvocation> {
26+
const pullRequest = this._findActivePullRequest();
27+
const threadId = options.input?.threadId;
28+
29+
if (!pullRequest) {
30+
return {
31+
invocationMessage: vscode.l10n.t('Resolving review thread'),
32+
};
33+
}
34+
35+
const thread = pullRequest.reviewThreadsCache.find(t => t.id === threadId);
36+
const file = thread?.path ? ` in \`${thread.path}\`` : '';
37+
const firstComment = thread?.comments[0]?.body;
38+
const snippet = firstComment ? `: "${firstComment.length > 60 ? firstComment.slice(0, 57) + '...' : firstComment}"` : '';
39+
40+
return {
41+
invocationMessage: vscode.l10n.t('Resolving review thread{0}{1}', file, snippet),
42+
};
43+
}
44+
45+
async invoke(options: vscode.LanguageModelToolInvocationOptions<ResolveReviewThreadToolParameters>, _token: vscode.CancellationToken): Promise<vscode.LanguageModelToolResult> {
46+
const pullRequest = this._findActivePullRequest();
47+
if (!pullRequest) {
48+
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart('There is no active pull request.')]);
49+
}
50+
51+
const { threadId } = options.input;
52+
if (!threadId) {
53+
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart('No threadId provided.')]);
54+
}
55+
56+
const thread = pullRequest.reviewThreadsCache.find(t => t.id === threadId);
57+
if (!thread) {
58+
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`Review thread with id "${threadId}" not found on the active pull request.`)]);
59+
}
60+
61+
if (thread.isResolved) {
62+
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`Review thread "${threadId}" is already resolved.`)]);
63+
}
64+
65+
if (!thread.viewerCanResolve) {
66+
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`You do not have permission to resolve review thread "${threadId}".`)]);
67+
}
68+
69+
await pullRequest.resolveReviewThread(threadId);
70+
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`Review thread "${threadId}" resolved successfully.`)]);
71+
}
72+
}

src/lm/tools/tools.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { FetchLabelsTool } from './fetchLabelsTool';
1111
import { FetchNotificationTool } from './fetchNotificationTool';
1212
import { OpenPullRequestTool } from './openPullRequestTool';
1313
import { PullRequestStatusChecksTool } from './pullRequestStatusChecksTool';
14+
import { ResolveReviewThreadTool } from './resolveReviewThreadTool';
1415
import { SearchTool } from './searchTools';
1516
import { CredentialStore } from '../../github/credentials';
1617
import { RepositoriesManager } from '../../github/repositoriesManager';
@@ -21,6 +22,7 @@ export function registerTools(context: vscode.ExtensionContext, credentialStore:
2122
context.subscriptions.push(vscode.lm.registerTool(ActivePullRequestTool.toolId, new ActivePullRequestTool(repositoriesManager)));
2223
context.subscriptions.push(vscode.lm.registerTool(OpenPullRequestTool.toolId, new OpenPullRequestTool(repositoriesManager)));
2324
context.subscriptions.push(vscode.lm.registerTool(PullRequestStatusChecksTool.toolId, new PullRequestStatusChecksTool(credentialStore, repositoriesManager)));
25+
context.subscriptions.push(vscode.lm.registerTool(ResolveReviewThreadTool.toolId, new ResolveReviewThreadTool(repositoriesManager)));
2426
}
2527

2628
function registerFetchingTools(context: vscode.ExtensionContext, credentialStore: CredentialStore, repositoriesManager: RepositoriesManager) {

0 commit comments

Comments
 (0)