UNPKG

coderabbitai-mcp

Version:

MCP server for interacting with CodeRabbit AI reviews on GitHub pull requests. Enables LLMs to analyze, implement, and resolve CodeRabbit suggestions programmatically.

167 lines โ€ข 6.91 kB
import { z } from 'zod'; const GetCommentDetailsSchema = z.object({ owner: z.string().min(1, "Repository owner is required"), repo: z.string().min(1, "Repository name is required"), commentId: z.number().int().positive("Comment ID must be positive") }); /** * Extract fix examples from CodeRabbit comment body */ function extractFixExamples(body) { const examples = []; // Extract committable suggestions const committableMatches = body.matchAll(/๐Ÿ“ Committable suggestion[\s\S]*?```suggestion\n([\s\S]*?)\n```/g); for (const match of committableMatches) { examples.push(match[1].trim()); } // Extract diff suggestions const diffMatches = body.matchAll(/```diff\n([\s\S]*?)\n```/g); for (const match of diffMatches) { examples.push(match[1].trim()); } // Extract code block examples const codeMatches = body.matchAll(/```(?:javascript|typescript|python|java|go|rust|cpp|c)\n([\s\S]*?)\n```/g); for (const match of codeMatches) { examples.push(match[1].trim()); } return examples; } /** * Extract file context around the comment */ function extractFileContext(diffHunk, path) { if (!diffHunk) return ''; // Clean up the diff hunk to provide readable context const lines = diffHunk.split('\n'); const contextLines = lines.filter(line => !line.startsWith('@@') && line.trim().length > 0).map(line => { // Remove diff markers but keep indentation if (line.startsWith('+') || line.startsWith('-')) { return line.substring(1); } return line.startsWith(' ') ? line.substring(1) : line; }); return `File: ${path}\n\n${contextLines.join('\n')}`; } /** * Find related comments based on file path and proximity */ function findRelatedComments(currentComment, allComments) { const related = []; for (const comment of allComments) { if (comment.id === currentComment.id) continue; // Same file if (comment.path === currentComment.path) { // Check if lines are close (within 10 lines) const currentLine = currentComment.original_line || 0; const commentLine = comment.original_line || 0; if (Math.abs(currentLine - commentLine) <= 10) { related.push(comment.id); } } } return related; } /** * Get detailed information about a specific CodeRabbit comment */ export async function getCommentDetails(input, githubClient) { // Validate input const validatedInput = GetCommentDetailsSchema.parse(input); const { owner, repo, commentId } = validatedInput; try { // Use the GitHub client's helper method to find the comment across recent PRs const result = await githubClient.findCommentInRecentPRs(owner, repo, commentId); if (!result) { throw new Error(`Comment with ID ${commentId} not found in recent pull requests`); } const { comment: targetComment, pr } = result; // Get all comments from this PR for context const allPRComments = await githubClient.getPullRequestComments(owner, repo, pr.number); // Verify this is a CodeRabbit comment if (targetComment.user.login !== 'coderabbitai[bot]') { throw new Error(`Comment ${commentId} is not from CodeRabbit AI`); } const body = targetComment.body; // Parse severity and category let severity = 'info'; let category = 'General'; if (body.includes('โš ๏ธ Potential issue')) { severity = 'warning'; category = 'Potential Issue'; } else if (body.includes('๐Ÿ› ๏ธ Refactor suggestion')) { severity = 'suggestion'; category = 'Refactor Suggestion'; } else if (body.includes('๐Ÿงน Nitpick')) { severity = 'info'; category = 'Nitpick'; } else if (body.includes('๐Ÿ”’ Security')) { severity = 'error'; category = 'Security'; } // Extract AI prompt const aiPromptMatch = body.match(/๐Ÿค– Prompt for AI Agents[\s\S]*?```\n([\s\S]*?)\n```/); const aiPrompt = aiPromptMatch ? aiPromptMatch[1].trim() : undefined; // Extract committable suggestion const committableMatch = body.match(/๐Ÿ“ Committable suggestion[\s\S]*?```suggestion\n([\s\S]*?)\n```/); const committableSuggestion = committableMatch ? committableMatch[1].trim() : undefined; // Extract description const descLines = body.split('\n').filter(line => line.trim() && !line.includes('_โš ๏ธ') && !line.includes('_๐Ÿ› ๏ธ') && !line.includes('๐Ÿค– Prompt') && !line.includes('๐Ÿ“ Committable') && !line.trim().startsWith('<') && !line.trim().startsWith('```')); const description = descLines.slice(0, 3).join(' ').trim() || 'CodeRabbit suggestion'; // Parse line range let lineRange = { start: 1, end: 1 }; if (targetComment.original_start_line && targetComment.original_line) { lineRange = { start: targetComment.original_start_line, end: targetComment.original_line }; } else if (targetComment.original_line) { lineRange = { start: targetComment.original_line, end: targetComment.original_line }; } // Check if resolved const isResolved = body.includes('โœ… Addressed') || body.includes('โœ… Fixed') || body.includes('โœ… Resolved'); // Extract additional details const fileContext = extractFileContext(targetComment.diff_hunk, targetComment.path); const relatedComments = findRelatedComments(targetComment, allPRComments); const fixExamples = extractFixExamples(body); const commentDetails = { id: targetComment.id, body: targetComment.body, path: targetComment.path, line_range: lineRange, side: targetComment.side, severity, category, description, ai_prompt: aiPrompt, committable_suggestion: committableSuggestion, html_url: targetComment.html_url, diff_hunk: targetComment.diff_hunk, created_at: targetComment.created_at, updated_at: targetComment.updated_at, is_resolved: isResolved, file_context: fileContext, related_comments: relatedComments, fix_examples: fixExamples }; return commentDetails; } catch (error) { throw new Error(`Failed to get comment details: ${error instanceof Error ? error.message : String(error)}`); } } //# sourceMappingURL=get-comment-details.js.map