Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompt/node/gitBranch.ts
13399 views
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
6
import type * as vscode from 'vscode';
7
import { sessionResourceToId } from '../../../platform/chat/common/chatDebugFileLoggerService';
8
import { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';
9
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
10
import { ILogService } from '../../../platform/log/common/logService';
11
import { CapturingToken } from '../../../platform/requestLogger/common/capturingToken';
12
import { IRequestLogger } from '../../../platform/requestLogger/common/requestLogger';
13
import { URI } from '../../../util/vs/base/common/uri';
14
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
15
import { ChatRequestTurn } from '../../../vscodeTypes';
16
import { renderPromptElement } from '../../prompts/node/base/promptRenderer';
17
import { GitBranchPrompt } from '../../prompts/node/panel/gitBranch';
18
19
export class GitBranchNameGenerator {
20
21
constructor(
22
@ILogService private readonly logService: ILogService,
23
@IEndpointProvider private endpointProvider: IEndpointProvider,
24
@IInstantiationService private readonly instantiationService: IInstantiationService,
25
@IRequestLogger private readonly requestLogger: IRequestLogger,
26
) { }
27
28
async generateBranchName(
29
context: vscode.ChatContext,
30
token: vscode.CancellationToken,
31
): Promise<string | undefined> {
32
33
// Get the first user message directly from the context
34
// Use instanceof to properly check if the first item is a ChatRequestTurn
35
const firstRequest = context.history.find(item => item instanceof ChatRequestTurn);
36
if (!firstRequest) {
37
return '';
38
}
39
40
// Extract the parent session ID from the context's sessionResource (provided by VS Code)
41
const sessionResource = context.sessionResource;
42
const parentChatSessionId = sessionResource ? sessionResourceToId(URI.from(sessionResource)) : undefined;
43
44
const endpoint = await this.endpointProvider.getChatEndpoint('copilot-fast');
45
const normalizedCommand = firstRequest.command?.trim().replace(/^\/+/, '') ?? '';
46
const command = normalizedCommand ? `/${normalizedCommand} ` : '';
47
const userRequest = `${command}${firstRequest.prompt}`;
48
const { messages } = await renderPromptElement(this.instantiationService, endpoint, GitBranchPrompt, { userRequest });
49
50
const capturingToken = new CapturingToken(
51
'git-branch',
52
undefined,
53
undefined,
54
undefined,
55
undefined,
56
parentChatSessionId,
57
'git-branch',
58
);
59
60
const doRequest = async () => {
61
const response = await endpoint.makeChatRequest2({
62
debugName: 'git-branch',
63
messages,
64
finishedCb: undefined,
65
location: ChatLocation.Panel,
66
userInitiatedRequest: false,
67
isConversationRequest: false,
68
}, token);
69
return response;
70
};
71
72
const response = await this.requestLogger.captureInvocation(capturingToken, doRequest);
73
if (token.isCancellationRequested) {
74
return '';
75
}
76
77
if (response.type === ChatFetchResponseType.Success) {
78
let branchName = response.value.trim();
79
if (branchName.match(/^".*"$/)) {
80
branchName = branchName.slice(1, -1);
81
}
82
if (branchName.includes('can\'t assist with that')) {
83
return undefined;
84
}
85
86
branchName = normalizeBranchName(branchName);
87
if (branchName.length < 8) {
88
throw new Error('Branch name is too short. Please keep it at least 8 characters.');
89
}
90
91
return branchName;
92
} else {
93
this.logService.error(`Failed to fetch git branch name because of response type (${response.type}) and reason (${response.reason})`);
94
return '';
95
}
96
}
97
}
98
99
export function normalizeBranchName(branchName: string): string {
100
// Only support alphanumeric characters and dashes for simplicity.
101
let normalized = branchName.replace(/[^a-zA-Z0-9\-]/g, '').toLowerCase();
102
// Collapse consecutive dots (..) into a single dot
103
normalized = normalized.replace(/\.{2,}/g, '.');
104
// Strip leading '-' or '.'
105
normalized = normalized.replace(/^[-.]+/, '');
106
// Strip trailing '.' or '/'
107
normalized = normalized.replace(/[./]+$/, '');
108
// Strip trailing .lock
109
normalized = normalized.replace(/\.lock$/, '');
110
111
return normalized;
112
}
113
114