diff --git a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts index 71520fa1ec..aa7001a3d2 100644 --- a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts @@ -105,6 +105,7 @@ declare module 'vscode' { isComplete?: boolean; toolSpecificData?: ChatTerminalToolInvocationData; fromSubAgent?: boolean; + presentation?: 'hidden' | 'hiddenAfterComplete' | undefined; constructor(toolName: string, toolCallId: string, isError?: boolean); } diff --git a/src/@types/vscode.proposed.chatSessionsProvider.d.ts b/src/@types/vscode.proposed.chatSessionsProvider.d.ts index bd4e624430..772fc387b9 100644 --- a/src/@types/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/@types/vscode.proposed.chatSessionsProvider.d.ts @@ -95,6 +95,11 @@ declare module 'vscode' { */ description?: string | MarkdownString; + /** + * An optional badge that provides additional context about the chat session. + */ + badge?: string | MarkdownString; + /** * An optional status indicating the current state of the session. */ diff --git a/src/github/createPRViewProvider.ts b/src/github/createPRViewProvider.ts index 40763443bb..15665e7a7b 100644 --- a/src/github/createPRViewProvider.ts +++ b/src/github/createPRViewProvider.ts @@ -753,6 +753,19 @@ export class CreatePullRequestViewProvider extends BaseCreatePullRequestViewProv // Ignore and fall back to commit message Logger.debug(`Error while getting total commits: ${e}`, CreatePullRequestViewProvider.ID); } + + // Apply variable substitution to title and description + const activeIssue = this._folderRepositoryManager.activeIssue; + let currentUser: string | undefined; + try { + currentUser = activeIssue ? (await this._folderRepositoryManager.getCurrentUser(activeIssue.githubRepository)).login : undefined; + } catch (e) { + Logger.debug(`Failed to get current user for variable substitution: ${e}`, CreatePullRequestViewProvider.ID); + currentUser = undefined; + } + title = variableSubstitution(title, activeIssue, this._pullRequestDefaults, currentUser); + description = variableSubstitution(description, activeIssue, this._pullRequestDefaults, currentUser); + return { title, description }; } diff --git a/src/test/github/utils.test.ts b/src/test/github/utils.test.ts index 94683fcf85..3e5f53eaf1 100644 --- a/src/test/github/utils.test.ts +++ b/src/test/github/utils.test.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { default as assert } from 'assert'; -import { getPRFetchQuery, sanitizeIssueTitle } from '../../github/utils'; +import { getPRFetchQuery, sanitizeIssueTitle, variableSubstitution } from '../../github/utils'; +import { IssueModel } from '../../github/issueModel'; +import { PullRequestDefaults } from '../../github/folderRepositoryManager'; describe('utils', () => { @@ -41,4 +43,87 @@ describe('utils', () => { }); }); }); + + describe('variableSubstitution', () => { + const defaults: PullRequestDefaults = { + owner: 'testOwner', + repo: 'testRepo', + base: 'main' + }; + + it('replaces ${issueNumber} with issue number', () => { + const issueModel = { + number: 123, + title: 'Test Issue', + remote: { owner: 'testOwner', repositoryName: 'testRepo' } + } as unknown as IssueModel; + const result = variableSubstitution('Closes ${issueNumber}', issueModel, defaults, 'testUser'); + assert.strictEqual(result, 'Closes 123'); + }); + + it('replaces ${issueNumberLabel} with #number for same repo', () => { + const issueModel = { + number: 456, + title: 'Test Issue', + remote: { owner: 'testOwner', repositoryName: 'testRepo' } + } as unknown as IssueModel; + const result = variableSubstitution('Fixes ${issueNumberLabel}', issueModel, defaults, 'testUser'); + assert.strictEqual(result, 'Fixes #456'); + }); + + it('replaces ${issueNumberLabel} with owner/repo#number for different repo', () => { + const issueModel = { + number: 789, + title: 'Test Issue', + remote: { owner: 'otherOwner', repositoryName: 'otherRepo' } + } as unknown as IssueModel; + const result = variableSubstitution('Resolves ${issueNumberLabel}', issueModel, defaults, 'testUser'); + assert.strictEqual(result, 'Resolves otherOwner/otherRepo#789'); + }); + + it('replaces ${issueTitle} with issue title', () => { + const issueModel = { + number: 123, + title: 'Fix bug in parser', + remote: { owner: 'testOwner', repositoryName: 'testRepo' } + } as unknown as IssueModel; + const result = variableSubstitution('Working on: ${issueTitle}', issueModel, defaults, 'testUser'); + assert.strictEqual(result, 'Working on: Fix bug in parser'); + }); + + it('replaces ${user} with username', () => { + const result = variableSubstitution('Assigned to ${user}', undefined, defaults, 'johnDoe'); + assert.strictEqual(result, 'Assigned to johnDoe'); + }); + + it('replaces ${repository} with repo name', () => { + const result = variableSubstitution('From ${repository}', undefined, defaults, 'testUser'); + assert.strictEqual(result, 'From testRepo'); + }); + + it('replaces ${owner} with owner name', () => { + const result = variableSubstitution('By ${owner}', undefined, defaults, 'testUser'); + assert.strictEqual(result, 'By testOwner'); + }); + + it('replaces multiple variables in one string', () => { + const issueModel = { + number: 123, + title: 'Test Issue', + remote: { owner: 'testOwner', repositoryName: 'testRepo' } + } as unknown as IssueModel; + const result = variableSubstitution('Closes ${issueNumberLabel}: ${issueTitle}', issueModel, defaults, 'testUser'); + assert.strictEqual(result, 'Closes #123: Test Issue'); + }); + + it('leaves variable unchanged if issue is not provided', () => { + const result = variableSubstitution('Closes ${issueNumberLabel}', undefined, defaults, 'testUser'); + assert.strictEqual(result, 'Closes ${issueNumberLabel}'); + }); + + it('handles empty string', () => { + const result = variableSubstitution('', undefined, defaults, 'testUser'); + assert.strictEqual(result, ''); + }); + }); }); \ No newline at end of file