@@ -7,6 +7,7 @@ import * as vscode from 'vscode';
77import Logger from '../common/logger' ;
88import { ITelemetry } from '../common/telemetry' ;
99import { getNonce , IRequestMessage , WebviewBase } from '../common/webview' ;
10+ import { CreatePullRequestDataModel } from '../view/createPullRequestDataModel' ;
1011import { CopilotRemoteAgentManager } from './copilotRemoteAgent' ;
1112import { FolderRepositoryManager , ReposManagerState } from './folderRepositoryManager' ;
1213import { IssueModel } from './issueModel' ;
@@ -30,6 +31,7 @@ export interface DashboardReady {
3031 owner : string ;
3132 name : string ;
3233 } ;
34+ currentBranch ?: string ;
3335}
3436
3537export interface GlobalDashboardLoading {
@@ -66,6 +68,12 @@ export interface IssueData {
6668 url : string ;
6769 createdAt : string ;
6870 updatedAt : string ;
71+ localTaskBranch ?: string ; // Name of the local task branch if it exists
72+ pullRequest ?: {
73+ number : number ;
74+ title : string ;
75+ url : string ;
76+ } ;
6977}
7078
7179export class DashboardWebviewProvider extends WebviewBase {
@@ -168,8 +176,9 @@ export class DashboardWebviewProvider extends WebviewBase {
168176
169177 const data = await this . getDashboardData ( ) ;
170178
171- // Get current repository info
179+ // Get current repository info and branch
172180 let repository : { owner : string ; name : string } | undefined ;
181+ let currentBranch : string | undefined ;
173182 const targetRepos = this . getTargetRepositories ( ) ;
174183 if ( targetRepos . length > 0 ) {
175184 const [ owner , name ] = targetRepos [ 0 ] . split ( '/' ) ;
@@ -178,12 +187,19 @@ export class DashboardWebviewProvider extends WebviewBase {
178187 }
179188 }
180189
190+ // Get current branch name
191+ if ( this . _repositoriesManager . folderManagers . length > 0 ) {
192+ const folderManager = this . _repositoriesManager . folderManagers [ 0 ] ;
193+ currentBranch = folderManager . repository . state . HEAD ?. name ;
194+ }
195+
181196 const readyData : DashboardReady = {
182197 state : 'ready' ,
183198 issueQuery : this . _issueQuery ,
184199 activeSessions : data . activeSessions ,
185200 milestoneIssues : data . milestoneIssues ,
186- repository
201+ repository,
202+ currentBranch
187203 } ;
188204 this . _postMessage ( {
189205 command : 'update-dashboard' ,
@@ -531,7 +547,7 @@ export class DashboardWebviewProvider extends WebviewBase {
531547 }
532548
533549 private async convertIssueToData ( issue : IssueModel ) : Promise < IssueData > {
534- return {
550+ const issueData : IssueData = {
535551 number : issue . number ,
536552 title : issue . title ,
537553 assignee : issue . assignees ?. [ 0 ] ?. login ,
@@ -541,6 +557,231 @@ export class DashboardWebviewProvider extends WebviewBase {
541557 createdAt : issue . createdAt ,
542558 updatedAt : issue . updatedAt
543559 } ;
560+
561+ // Check for local task branch
562+ try {
563+ const taskBranchName = `task/issue-${ issue . number } ` ;
564+ const localTaskBranch = await this . findLocalTaskBranch ( taskBranchName ) ;
565+ if ( localTaskBranch ) {
566+ issueData . localTaskBranch = localTaskBranch ;
567+
568+ // Check for associated pull request for this branch
569+ const pullRequest = await this . findPullRequestForBranch ( localTaskBranch ) ;
570+ if ( pullRequest ) {
571+ issueData . pullRequest = pullRequest ;
572+ }
573+ }
574+ } catch ( error ) {
575+ // If we can't check for branches, just continue without the local task info
576+ Logger . debug ( `Could not check for local task branch: ${ error } ` , DashboardWebviewProvider . ID ) ;
577+ }
578+
579+ return issueData ;
580+ }
581+
582+ private async findLocalTaskBranch ( branchName : string ) : Promise < string | undefined > {
583+ try {
584+ // Use the same logic as TaskManager to get all task branches
585+ for ( const folderManager of this . _repositoriesManager . folderManagers ) {
586+ if ( folderManager . repository . getRefs ) {
587+ const refs = await folderManager . repository . getRefs ( { pattern : 'refs/heads/' } ) ;
588+
589+ // Debug: log all branches
590+ Logger . debug ( `All local branches: ${ refs . map ( r => r . name ) . join ( ', ' ) } ` , DashboardWebviewProvider . ID ) ;
591+
592+ // Filter for task branches and look for our specific branch
593+ const taskBranches = refs . filter ( ref =>
594+ ref . name &&
595+ ref . name . startsWith ( 'task/' )
596+ ) ;
597+
598+ Logger . debug ( `Task branches: ${ taskBranches . map ( r => r . name ) . join ( ', ' ) } ` , DashboardWebviewProvider . ID ) ;
599+ Logger . debug ( `Looking for branch: ${ branchName } ` , DashboardWebviewProvider . ID ) ;
600+
601+ const matchingBranch = taskBranches . find ( ref => ref . name === branchName ) ;
602+
603+ if ( matchingBranch ) {
604+ Logger . debug ( `Found local task branch: ${ branchName } ` , DashboardWebviewProvider . ID ) ;
605+ return branchName ;
606+ }
607+ }
608+ }
609+ Logger . debug ( `Local task branch ${ branchName } not found in any repository` , DashboardWebviewProvider . ID ) ;
610+ return undefined ;
611+ } catch ( error ) {
612+ Logger . debug ( `Failed to find local task branch ${ branchName } : ${ error } ` , DashboardWebviewProvider . ID ) ;
613+ return undefined ;
614+ }
615+ }
616+
617+ private async findPullRequestForBranch ( branchName : string ) : Promise < { number : number ; title : string ; url : string } | undefined > {
618+ try {
619+ for ( const folderManager of this . _repositoriesManager . folderManagers ) {
620+ if ( folderManager . gitHubRepositories . length === 0 ) {
621+ continue ;
622+ }
623+
624+ // Try each GitHub repository in this folder manager
625+ for ( const githubRepository of folderManager . gitHubRepositories ) {
626+ try {
627+ // Use the getPullRequestForBranch method to find PRs for this branch
628+ const pullRequest = await githubRepository . getPullRequestForBranch ( branchName , githubRepository . remote . owner ) ;
629+
630+ if ( pullRequest ) {
631+ Logger . debug ( `Found PR #${ pullRequest . number } for branch ${ branchName } ` , DashboardWebviewProvider . ID ) ;
632+ return {
633+ number : pullRequest . number ,
634+ title : pullRequest . title ,
635+ url : pullRequest . html_url
636+ } ;
637+ }
638+ } catch ( error ) {
639+ Logger . debug ( `Failed to find PR for branch ${ branchName } in ${ githubRepository . remote . owner } /${ githubRepository . remote . repositoryName } : ${ error } ` , DashboardWebviewProvider . ID ) ;
640+ // Continue to next repository
641+ }
642+ }
643+ }
644+
645+ Logger . debug ( `No PR found for branch ${ branchName } ` , DashboardWebviewProvider . ID ) ;
646+ return undefined ;
647+ } catch ( error ) {
648+ Logger . debug ( `Failed to find PR for branch ${ branchName } : ${ error } ` , DashboardWebviewProvider . ID ) ;
649+ return undefined ;
650+ }
651+ }
652+
653+ private async switchToMainBranch ( ) : Promise < void > {
654+ try {
655+ // Find the first available folder manager with a repository
656+ const folderManager = this . _repositoriesManager . folderManagers . find ( fm =>
657+ fm . gitHubRepositories . length > 0
658+ ) ;
659+
660+ if ( ! folderManager ) {
661+ vscode . window . showErrorMessage ( 'No GitHub repository found in the current workspace.' ) ;
662+ return ;
663+ }
664+
665+ // Get the default branch (usually main or master)
666+ const defaultBranch = await this . getDefaultBranch ( folderManager ) || 'main' ;
667+
668+ // Switch to the default branch
669+ await folderManager . repository . checkout ( defaultBranch ) ;
670+ vscode . window . showInformationMessage ( `Switched to branch: ${ defaultBranch } ` ) ;
671+
672+ // Update dashboard to reflect the branch change
673+ setTimeout ( ( ) => {
674+ this . updateDashboard ( ) ;
675+ } , 500 ) ;
676+ } catch ( error ) {
677+ Logger . error ( `Failed to switch to main branch: ${ error } ` , DashboardWebviewProvider . ID ) ;
678+ vscode . window . showErrorMessage ( `Failed to switch to main branch: ${ error } ` ) ;
679+ }
680+ }
681+
682+ private async createPullRequest ( ) : Promise < void > {
683+ try {
684+ // Find the first available folder manager with a repository
685+ const folderManager = this . _repositoriesManager . folderManagers . find ( fm =>
686+ fm . gitHubRepositories . length > 0
687+ ) ;
688+
689+ if ( ! folderManager ) {
690+ vscode . window . showErrorMessage ( 'No GitHub repository found in the current workspace.' ) ;
691+ return ;
692+ }
693+
694+ const repository = folderManager . repository ;
695+ const currentBranch = repository . state . HEAD ?. name ;
696+
697+ if ( ! currentBranch ) {
698+ vscode . window . showErrorMessage ( 'No current branch found.' ) ;
699+ return ;
700+ }
701+
702+ // Check if there are any commits on this branch that aren't on main
703+ const hasCommits = await this . hasCommitsOnBranch ( repository , currentBranch ) ;
704+
705+ if ( ! hasCommits ) {
706+ // No commits yet, stage files, generate commit message, and open SCM view
707+ try {
708+ // Stage all changed files
709+ const workingTreeChanges = repository . state . workingTreeChanges ;
710+ if ( workingTreeChanges . length > 0 ) {
711+ await repository . add ( workingTreeChanges . map ( change => change . uri . fsPath ) ) ;
712+ Logger . debug ( `Staged ${ workingTreeChanges . length } files` , DashboardWebviewProvider . ID ) ;
713+ }
714+
715+ // Open SCM view first
716+ await vscode . commands . executeCommand ( 'workbench.view.scm' ) ;
717+
718+ // Generate commit message using Copilot
719+ try {
720+ await vscode . commands . executeCommand ( 'github.copilot.git.generateCommitMessage' ) ;
721+ } catch ( commitMsgError ) {
722+ Logger . debug ( `Failed to generate commit message: ${ commitMsgError } ` , DashboardWebviewProvider . ID ) ;
723+ // Don't fail the whole operation if commit message generation fails
724+ }
725+
726+ vscode . window . showInformationMessage ( 'Files staged and commit message generated. Make your first commit before creating a pull request.' ) ;
727+ } catch ( stagingError ) {
728+ Logger . error ( `Failed to stage files: ${ stagingError } ` , DashboardWebviewProvider . ID ) ;
729+ // Fall back to just opening SCM view
730+ await vscode . commands . executeCommand ( 'workbench.view.scm' ) ;
731+ vscode . window . showInformationMessage ( 'Make your first commit before creating a pull request.' ) ;
732+ }
733+ } else {
734+ // Has commits, proceed with create pull request flow
735+ await vscode . commands . executeCommand ( 'pr.create' ) ;
736+ }
737+ } catch ( error ) {
738+ Logger . error ( `Failed to create pull request: ${ error } ` , DashboardWebviewProvider . ID ) ;
739+ vscode . window . showErrorMessage ( `Failed to create pull request: ${ error } ` ) ;
740+ }
741+ }
742+
743+ private async hasCommitsOnBranch ( repository : any , branchName : string ) : Promise < boolean > {
744+ try {
745+ // Find the folder manager that contains this repository
746+ const folderManager = this . _repositoriesManager . folderManagers . find ( fm =>
747+ fm . repository === repository
748+ ) ;
749+
750+ if ( ! folderManager ) {
751+ Logger . debug ( `Could not find folder manager for repository` , DashboardWebviewProvider . ID ) ;
752+ return true ;
753+ }
754+
755+ // Get the default branch (usually main or master)
756+ const defaultBranch = await this . getDefaultBranch ( folderManager ) || 'main' ;
757+
758+ // Get the GitHub repository for this folder manager
759+ const githubRepo = folderManager . gitHubRepositories [ 0 ] ;
760+ if ( ! githubRepo ) {
761+ Logger . debug ( `No GitHub repository found in folder manager` , DashboardWebviewProvider . ID ) ;
762+ return true ;
763+ }
764+
765+ // Create a CreatePullRequestDataModel to check for changes
766+ const dataModel = new CreatePullRequestDataModel (
767+ folderManager ,
768+ githubRepo . remote . owner ,
769+ defaultBranch ,
770+ githubRepo . remote . owner ,
771+ branchName ,
772+ githubRepo . remote . repositoryName
773+ ) ;
774+
775+ // Check if there are any changes between the branch and the base
776+ const commits = await dataModel . gitCommits ( ) ;
777+ dataModel . dispose ( ) ;
778+
779+ return commits . length > 0 ;
780+ } catch ( error ) {
781+ // If we can't determine commit status, assume there are commits and proceed
782+ Logger . debug ( `Could not check branch commits: ${ error } ` , DashboardWebviewProvider . ID ) ;
783+ return true ;
784+ }
544785 }
545786
546787 protected override async _onDidReceiveMessage ( message : IRequestMessage < any > ) : Promise < void > {
@@ -587,6 +828,12 @@ export class DashboardWebviewProvider extends WebviewBase {
587828 case 'open-external-url' :
588829 await vscode . env . openExternal ( vscode . Uri . parse ( message . args . url ) ) ;
589830 break ;
831+ case 'switch-to-main' :
832+ await this . switchToMainBranch ( ) ;
833+ break ;
834+ case 'create-pull-request' :
835+ await this . createPullRequest ( ) ;
836+ break ;
590837 default :
591838 await super . _onDidReceiveMessage ( message ) ;
592839 break ;
0 commit comments