UNPKG

review-copilot

Version:

ReviewCopilot - AI-powered code review assistant with customizable prompts

163 lines (159 loc) 6.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectiveReviewService = void 0; const provider_factory_1 = require("../providers/provider-factory"); const config_manager_1 = require("../config/config-manager"); const git_platform_factory_1 = require("./git-platform-factory"); class SelectiveReviewService { constructor() { } static getInstance() { if (!SelectiveReviewService.instance) { SelectiveReviewService.instance = new SelectiveReviewService(); } return SelectiveReviewService.instance; } /** * Process a selective code review request * @param context The context of the code to review * @returns The review results */ async processSelectiveReview(context) { const configManager = await config_manager_1.ConfigManager.getInstance(); const config = configManager.getConfig(); const aiProvider = provider_factory_1.ProviderFactory.createProvider(config); try { // Create a prompt that includes the context of the review const prompt = this.buildSelectiveReviewPrompt(context, config.rules.codeChanges.prompt); // Get the review from the AI provider const result = await aiProvider.review(prompt, context.fullFileContent); // Format the result as a CodeReviewResult const suggestions = result ? [ { message: typeof result === 'string' ? result : JSON.stringify(result), filename: context.filePath, line: context.startLine, severity: 'info', reviewType: 'line-specific', }, ] : []; const reviewResult = { success: true, suggestions, error: undefined, }; // Post the review as a comment await this.postReviewComment(context, reviewResult); return reviewResult; } catch (error) { return { success: false, suggestions: [], error: { message: error instanceof Error ? error.message : 'Unknown error occurred', }, }; } } /** * Build a prompt for selective review that includes context */ buildSelectiveReviewPrompt(context, basePrompt) { return `${basePrompt} Review Context: - File: ${context.filePath} - Selected Lines: ${context.startLine}-${context.endLine} - Trigger Comment: "${context.triggerComment}" The code to review is marked with <<<REVIEW_START>>> and <<<REVIEW_END>>> markers in the content. Please focus your review on the marked section, but consider the full file context for better understanding. Content to review: ${context.fullFileContent .split('\n') .map((line, index) => { const lineNum = index + 1; if (lineNum === context.startLine) { return `<<<REVIEW_START>>>${line}`; } else if (lineNum === context.endLine) { return `${line}<<<REVIEW_END>>>`; } return line; }) .join('\n')}`; } /** * Post the review results as a comment on the PR */ async postReviewComment(context, result) { const gitService = git_platform_factory_1.GitPlatformFactory.createService(); const comment = this.formatSelectiveReviewComment(context, result); try { // Check if this is a PR review comment based on event type const isReviewComment = process.env.GITHUB_EVENT_TYPE === 'pull_request_review_comment'; if (isReviewComment && context.threadId) { console.log(`Attempting to reply to review comment thread ${context.threadId}...`); // For PR review comments, use the review comment API await gitService.replyToReviewComment({ owner: context.owner, repo: context.repo, pullNumber: context.pullNumber, threadId: context.threadId, comment, }); console.log('Successfully replied to review comment thread'); } else if (context.commentId) { console.log(`Attempting to reply to comment ${context.commentId}...`); // For regular PR comments, use the issues comments API await gitService.replyToComment({ owner: context.owner, repo: context.repo, pullNumber: context.pullNumber, commentId: context.commentId, comment, }); console.log('Successfully replied to comment'); } else { console.log('No comment ID or thread ID provided, creating new comment...'); // Fallback to creating a new comment await gitService.createIssueComment({ owner: context.owner, repo: context.repo, issue_number: context.pullNumber, body: comment, }); console.log('Successfully created new comment'); } } catch (error) { console.error('Error posting review comment:', error); if (error instanceof Error) { if (error.message.includes('Not Found')) { throw new Error(`Could not find the original comment (ID: ${context.commentId || context.threadId}). The comment may have been deleted or you may not have permission to access it.`); } throw new Error(`Failed to post review comment: ${error.message}`); } throw error; } } /** * Format the review results into a markdown comment */ formatSelectiveReviewComment(context, result) { let comment = ``; if (result.suggestions?.length) { result.suggestions.forEach((suggestion) => { comment += `- ${suggestion.message}\n`; }); } if (result.error) { comment += `\n### Errors\n\n`; comment += `- ${result.error.message}\n`; } return comment; } } exports.SelectiveReviewService = SelectiveReviewService;