11import * as vscode from 'vscode' ;
22import * as path from 'path' ;
3- import axios from 'axios' ;
4- import { TokenManager } from './token-manager' ;
53import { ExpertiseAnalyzer , ExpertiseAnalysis } from './expertise-analyzer' ;
64import { Expert } from '../types/expert' ;
5+ import { GitService } from './git-service' ;
76
87export interface GitHubRepository {
98 owner : string ;
@@ -30,11 +29,9 @@ export interface MCPServerStatus {
3029
3130export class CopilotMCPService {
3231 private outputChannel : vscode . OutputChannel ;
33- private tokenManager : TokenManager ;
3432
35- constructor ( outputChannel : vscode . OutputChannel , tokenManager : TokenManager ) {
33+ constructor ( outputChannel : vscode . OutputChannel ) {
3634 this . outputChannel = outputChannel ;
37- this . tokenManager = tokenManager ;
3835 }
3936
4037 //Check the status of GitHub MCP Server containers
@@ -125,26 +122,25 @@ export class CopilotMCPService {
125122 }
126123 }
127124
128-
129- //Detect GitHub repository information from the current workspace
130-
125+
126+ //Detect GitHub repository information from the current workspace (using secure GitService)
127+
131128 async detectRepository ( ) : Promise < GitHubRepository | null > {
132129 try {
133130 const workspaceFolder = vscode . workspace . workspaceFolders ?. [ 0 ] ;
134131 if ( ! workspaceFolder ) {
135132 throw new Error ( 'No workspace folder open' ) ;
136133 }
137134
138- // Try to parse git remote to get owner/repo
139- const { exec } = require ( 'child_process' ) ;
140- const util = require ( 'util' ) ;
141- const execAsync = util . promisify ( exec ) ;
135+ // Use secure GitService to get remote URL
136+ const gitService = new GitService ( workspaceFolder . uri . fsPath , this . outputChannel ) ;
137+ const remoteUrl = await gitService . getRemoteUrl ( ) ;
142138
143- const { stdout } = await execAsync ( 'git config --get remote.origin.url' , {
144- cwd : workspaceFolder . uri . fsPath
145- } ) ;
139+ if ( ! remoteUrl ) {
140+ this . outputChannel . appendLine ( 'No git remote URL found' ) ;
141+ return null ;
142+ }
146143
147- const remoteUrl = stdout . trim ( ) ;
148144 this . outputChannel . appendLine ( `Git remote URL: ${ remoteUrl } ` ) ;
149145
150146 // Parse GitHub URL (supports both HTTPS and SSH)
@@ -341,29 +337,6 @@ Just call the tool and format the response. Don't explain anything else.`;
341337 }
342338 }
343339
344- /**
345- * Create an enhanced prompt specifically designed for MCP usage
346- */
347- private createEnhancedMCPPrompt ( basePrompt : string ) : string {
348- // Extract repository info from the base prompt
349- const repoMatch = basePrompt . match ( / ( \w + \/ \w + ) / ) ;
350- const repoPath = repoMatch ? repoMatch [ 1 ] : 'owner/repo' ;
351- const [ owner , repo ] = repoPath . split ( '/' ) ;
352-
353- return `Call mcp_github_list_commits with owner: "${ owner } ", repo: "${ repo } ", perPage: 5
354-
355- Then respond with JSON format:
356- {
357- "commits": [{"sha": "...", "author": {"name": "...", "email": "...", "date": "..."}, "message": "..."}],
358- "contributors": [{"name": "...", "email": "...", "totalCommits": 0}],
359- "issues": [],
360- "pullRequests": [],
361- "collaborationInsights": ["Analysis complete"]
362- }
363-
364- Execute now.` ;
365- }
366-
367340 /**
368341 * Parse repository response from Copilot Chat
369342 */
@@ -437,29 +410,19 @@ Execute now.`;
437410 }
438411
439412 /**
440- * Get commits from local git repository
413+ * Get commits from local git repository (using secure GitService)
441414 */
442415 private async getLocalCommits ( repoPath : string ) : Promise < any [ ] > {
443- const { exec } = require ( 'child_process' ) ;
444- const util = require ( 'util' ) ;
445- const execAsync = util . promisify ( exec ) ;
446-
447416 try {
448- const { stdout } = await execAsync ( 'git log --pretty=format:"%H|%an|%ae|%ad|%s" --date=iso -n 50' , {
449- cwd : repoPath
450- } ) ;
451-
452- return stdout . split ( '\n' )
453- . filter ( ( line : string ) => line . trim ( ) )
454- . map ( ( line : string ) => {
455- const [ sha , author , email , date , message ] = line . split ( '|' ) ;
456- return {
457- sha,
458- author : { name : author , email, date } ,
459- message,
460- files : [ ] // Would need additional git commands to get files per commit
461- } ;
462- } ) ;
417+ const gitService = new GitService ( repoPath , this . outputChannel ) ;
418+ const commits = await gitService . getCommits ( 50 ) ;
419+
420+ return commits . map ( commit => ( {
421+ sha : commit . sha ,
422+ author : commit . author ,
423+ message : commit . message ,
424+ files : [ ] // Would need additional git commands to get files per commit
425+ } ) ) ;
463426 } catch ( error ) {
464427 this . outputChannel . appendLine ( `Error getting local commits: ${ error } ` ) ;
465428 return [ ] ;
@@ -475,7 +438,7 @@ Execute now.`;
475438 commits . forEach ( commit => {
476439 const email = commit . author . email ;
477440 const name = commit . author . name ;
478- const commitDate = new Date ( commit . author . date ) ;
441+ const commitDate = new Date ( commit . date ) ;
479442
480443 if ( contributorMap . has ( email ) ) {
481444 const contributor = contributorMap . get ( email ) ! ;
@@ -502,7 +465,7 @@ Execute now.`;
502465 /**
503466 * Get workspace files for analysis
504467 */
505- private async getWorkspaceFiles ( repoPath : string ) : Promise < string [ ] > {
468+ private async getWorkspaceFiles ( _repoPath : string ) : Promise < string [ ] > {
506469 try {
507470 const files = await vscode . workspace . findFiles ( '**/*' , '**/node_modules/**' ) ;
508471 return files . map ( file => vscode . workspace . asRelativePath ( file ) ) ;
@@ -727,17 +690,6 @@ Execute now.`;
727690 // vscode.window.showInformationMessage('suggestExpertForIssueDetails called. See logs.');
728691 }
729692
730-
731- private extractIssueDataFromResponse ( responseData : any ) : any {
732- return {
733- title : responseData . title ,
734- body : responseData . body ,
735- labels : responseData . labels ?. map ( ( label : any ) => label . name ) || [ ] ,
736- number : responseData . number ,
737- url : responseData . html_url
738- } ;
739- }
740-
741693 private findBestExpertForIssue ( issueData : any , experts : Expert [ ] , extractKeywords : ( text : string ) => string [ ] ) : Expert | null {
742694 if ( ! experts || experts . length === 0 ) return null ;
743695 const issueKeywords = extractKeywords ( `${ issueData . title } ${ issueData . body } ` ) ;
@@ -790,32 +742,28 @@ Execute now.`;
790742 vscode . window . showInformationMessage ( message , { modal : true } , 'Notify Expert' )
791743 . then ( async ( selection ) => {
792744 if ( selection === 'Notify Expert' ) {
793- await this . notifyExpert ( expert , issueData , issueNumber ) ;
745+ await this . _notifyExpert ( expert , issueData , issueNumber ) ;
794746 }
795747 } ) ;
796748 }
797749
798750 /**
799751 * Notify the expert about the issue (e.g., via GitHub comment, email, etc.)
752+ * @private - Placeholder for future email integration
800753 */
801- private async notifyExpert ( expert : Expert , issueData : any , issueNumber ?: number ) : Promise < void > {
754+ private async _notifyExpert ( expert : Expert , _issueData : any , issueNumber ?: number ) : Promise < void > {
802755 if ( ! expert . email ) {
803756 vscode . window . showErrorMessage ( 'No email available for the expert. Cannot send notification.' ) ;
804757 return ;
805758 }
806759
807- // Simple email notification (placeholder, integrate with actual email service)
808- const subject = `Expertise Requested for Issue #${ issueNumber } : ${ issueData . title } ` ;
809- const body = `Hello ${ expert . name } ,\n\n` +
810- `You have been suggested as an expert for GitHub Issue #${ issueNumber } - ${ issueData . title } .\n` +
811- `Issue URL: ${ issueData . url } \n\n` +
812- `Please check the issue for details and consider contributing your expertise.\n\n` +
813- `Thank you!` ;
760+ // TODO: Integrate with actual email service
761+ // Email subject: `Expertise Requested for Issue #${issueNumber}: ${issueData.title}`
762+ // Email body: Expert details and issue URL
814763
815- this . outputChannel . appendLine ( `Sending email to ${ expert . email } ...` ) ;
816- // Integrate with email service here
764+ this . outputChannel . appendLine ( `Would send email to ${ expert . email } for issue #${ issueNumber } ` ) ;
817765
818- vscode . window . showInformationMessage ( `Notification sent to ${ expert . name } <${ expert . email } >` , { modal : true } ) ;
766+ vscode . window . showInformationMessage ( `Notification would be sent to ${ expert . name } <${ expert . email } >` , { modal : true } ) ;
819767 }
820768
821769 // Add getExpertRecentActivity and showExpertActivity methods back to CopilotMCPService
@@ -827,28 +775,21 @@ Execute now.`;
827775 if ( ! workspaceFolder ) {
828776 return { success : false , error : 'No workspace folder found' } ;
829777 }
830- const { exec } = require ( 'child_process' ) ;
831- const { promisify } = require ( 'util' ) ;
832- const execAsync = promisify ( exec ) ;
778+
833779 try {
834- const commitCommand = `git log --author="${ expertEmail } " --pretty=format:"%H|%s|%ad" --date=short -n 10` ;
835- const { stdout : commitOutput } = await execAsync ( commitCommand , { cwd : workspaceFolder . uri . fsPath } ) ;
836- const recentCommits = commitOutput . split ( '\n' )
837- . filter ( ( line : string ) => line . trim ( ) )
838- . map ( ( line : string ) => {
839- const [ sha , message , date ] = line . split ( '|' ) ;
840- return {
841- repo : "Current Repository" ,
842- message : message || "No message" ,
843- date : date || new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ,
844- url : `#${ sha ?. substring ( 0 , 7 ) || 'unknown' } `
845- } ;
846- } ) ;
847- const fileCommand = `git log --author=\"${ expertEmail } \" --name-only --pretty=format: -n 20 | sort | uniq | head -10` ;
848- const { stdout : fileOutput } = await execAsync ( fileCommand , { cwd : workspaceFolder . uri . fsPath } ) ;
849- const recentFiles = fileOutput . split ( '\n' )
850- . filter ( ( file : string ) => file . trim ( ) )
851- . slice ( 0 , 5 ) ;
780+ // Use secure GitService instead of direct git commands
781+ const gitService = new GitService ( workspaceFolder . uri . fsPath , this . outputChannel ) ;
782+
783+ const commits = await gitService . getCommitsByAuthor ( expertEmail , 10 ) ;
784+ const recentCommits = commits . map ( commit => ( {
785+ repo : "Current Repository" ,
786+ message : commit . message || "No message" ,
787+ date : commit . date . split ( ' ' ) [ 0 ] || new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ,
788+ url : `#${ commit . sha . substring ( 0 , 7 ) } `
789+ } ) ) ;
790+
791+ const recentFiles = await gitService . getFilesByAuthor ( expertEmail , 10 ) ;
792+
852793 const activity = {
853794 expertName,
854795 expertEmail,
@@ -863,7 +804,7 @@ Execute now.`;
863804 recentFiles . length > 0 ? `Recently worked on: ${ recentFiles . slice ( 0 , 3 ) . join ( ', ' ) } ` : "No recent file activity found" ,
864805 `Last commit: ${ recentCommits [ 0 ] ?. date || 'Unknown' } `
865806 ] ,
866- currentFocus : recentCommits . length > 0 ?
807+ currentFocus : recentCommits . length > 0 ?
867808 `Recent work: ${ recentCommits [ 0 ] ?. message ?. substring ( 0 , 100 ) || 'No recent activity' } ` :
868809 "No recent activity in this repository"
869810 } ;
0 commit comments