Skip to content

Commit 0cd9002

Browse files
authored
Implement coding agent followup (#7166)
* implement coding agent follow-up (microsoft/vscode#253100, #7118) * improve code * include the summary too
1 parent d6128ba commit 0cd9002

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"command": "githubpr.remoteAgent",
7272
"displayName": "GitHub Coding Agent",
7373
"description": "The GitHub Coding Agent.",
74+
"followUpRegex": "open-pull-request-webview.*((%7B.*?%7D)|(\\{.*?\\}))",
7475
"when": "config.githubPullRequests.codingAgent.enabled && config.githubPullRequests.codingAgent.uiIntegration && copilotCodingAgentAssignable"
7576
}
7677
],

src/github/copilotRemoteAgent.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface ICopilotRemoteAgentCommandArgs {
3535
userPrompt: string;
3636
summary?: string;
3737
source?: string;
38+
followup?: string;
3839
}
3940

4041
const LEARN_MORE = vscode.l10n.t('Learn about Coding Agent');
@@ -44,6 +45,9 @@ const CONTINUE = vscode.l10n.t('Continue');
4445
const PUSH_CHANGES = vscode.l10n.t('Include changes');
4546
const CONTINUE_WITHOUT_PUSHING = vscode.l10n.t('Ignore changes');
4647

48+
const FOLLOW_UP_REGEX = /open-pull-request-webview.*((%7B.*?%7D)|(\{.*?\}))/;
49+
const COPILOT = '@copilot';
50+
4751
export class CopilotRemoteAgentManager extends Disposable {
4852
public static ID = 'CopilotRemoteAgentManager';
4953

@@ -201,12 +205,39 @@ export class CopilotRemoteAgentManager extends Disposable {
201205
return { owner, repo, baseRef, remote, repository, ghRepository, fm };
202206
}
203207

208+
private parseFollowup(followup: string | undefined, repoInfo: { owner: string; repo: string }): number | undefined {
209+
if (!followup) {
210+
return;
211+
}
212+
const match = followup.match(FOLLOW_UP_REGEX);
213+
if (!match || match.length < 2) {
214+
Logger.error(`Ignoring. Invalid followup format: ${followup}`, CopilotRemoteAgentManager.ID);
215+
return;
216+
}
217+
218+
try {
219+
const followUpData = JSON.parse(decodeURIComponent(match[1]));
220+
if (!followUpData || !followUpData.owner || !followUpData.repo || !followUpData.pullRequestNumber) {
221+
Logger.error(`Ignoring. Invalid followup data: ${followUpData}`, CopilotRemoteAgentManager.ID);
222+
return;
223+
}
224+
225+
if (repoInfo.owner !== followUpData.owner || repoInfo.repo !== followUpData.repo) {
226+
Logger.error(`Ignoring. Follow up data does not match current repository: ${JSON.stringify(followUpData)}`, CopilotRemoteAgentManager.ID);
227+
return;
228+
}
229+
return followUpData.pullRequestNumber;
230+
} catch (error) {
231+
Logger.error(`Ignoring. Error while parsing follow up data: ${followup}`, CopilotRemoteAgentManager.ID);
232+
}
233+
}
234+
204235
async commandImpl(args?: ICopilotRemoteAgentCommandArgs): Promise<string | undefined> {
205236
if (!args) {
206237
return;
207238
}
208239

209-
const { userPrompt, summary, source } = args;
240+
const { userPrompt, summary, source, followup } = args;
210241
if (!userPrompt || userPrompt.trim().length === 0) {
211242
return;
212243
}
@@ -216,6 +247,36 @@ export class CopilotRemoteAgentManager extends Disposable {
216247
return;
217248
}
218249
const { repository, owner, repo } = repoInfo;
250+
251+
// If this is a followup, parse out the necessary data
252+
// Group 2 is this, url-encoded:
253+
// {"owner":"monalisa","repo":"app","pullRequestNumber":18}
254+
let followUpPR: number | undefined = this.parseFollowup(followup, repoInfo);
255+
if (followUpPR) {
256+
try {
257+
const ghRepo = repoInfo.ghRepository;
258+
// Fetch the PullRequestModel by number
259+
const pr = await ghRepo.getPullRequest(followUpPR);
260+
if (!pr) {
261+
Logger.error(`Could not find pull request #${followUpPR}`, CopilotRemoteAgentManager.ID);
262+
return;
263+
}
264+
// Add a comment tagging @copilot with the user's prompt
265+
const commentBody = `${COPILOT} ${userPrompt} \n\n --- \n\n ${summary ?? ''}`;
266+
const commentResult = await pr.createIssueComment(commentBody);
267+
if (!commentResult) {
268+
Logger.error(`Failed to add comment to PR #${followUpPR}`, CopilotRemoteAgentManager.ID);
269+
return;
270+
}
271+
Logger.appendLine(`Added comment ${commentResult.htmlUrl}`, CopilotRemoteAgentManager.ID);
272+
// allow-any-unicode-next-line
273+
return vscode.l10n.t('🚀 Follow-up comment added to [#{0}]({1})', followUpPR, commentResult.htmlUrl);
274+
} catch (err) {
275+
Logger.error(`Failed to add follow-up comment to PR #${followUpPR}: ${err}`, CopilotRemoteAgentManager.ID);
276+
return;
277+
}
278+
}
279+
219280
const repoName = `${owner}/${repo}`;
220281
const hasChanges = repository.state.workingTreeChanges.length > 0 || repository.state.indexChanges.length > 0;
221282
const learnMoreCb = async () => {

0 commit comments

Comments
 (0)