import logger from '../utils/logger.js'; class PRWorkflow { constructor(githubService, aiProvider, memory, developer, conversationSimulator, reviewEngine) { this.github = githubService; this.ai = aiProvider; this.memory = memory; this.developer = developer; this.conversation = conversationSimulator; this.reviewEngine = reviewEngine; } async processPRs() { try { const prs = await this.github.listPullRequests('open'); for (const pr of prs) { await this._processSinglePR(pr); } } catch (error) { logger.error(`Failed to process PRs: ${error.message}`); } } async _processSinglePR(pr) { const statusData = this.memory.recallWithMetadata(`pr-status-${pr.number}`); const status = statusData?.value || null; try { if (!status) { await this._handleNewPR(pr); } else if (status === 'reviewed-approved') { await this._handleApprovedPR(pr); } else if (status === 'reviewed-changes') { await this._handleChangesRequestedPR(pr); } else if (status === 'updated') { await this._handleUpdatedPR(pr); } } catch (error) { logger.error(`Failed to process PR #${pr.number}: ${error.message}`); } } async _handleNewPR(pr) { logger.info(`Handling new PR #${pr.number}: ${pr.title}`); await this.developer.adaptRoleFromContext(pr.title, []); const files = await this.github.getPullRequestFiles(pr.number); const reviews = await this.reviewEngine.reviewPullRequest(pr, files); if (reviews.length === 0) { const comment = await this.conversation.generatePRComment(pr, [], files); await this.github.addPullRequestComment(pr.number, comment); this.memory.remember(`pr-status-${pr.number}`, 'reviewed-comment', { tags: ['review', 'pr'], }); return; } const hasBugs = reviews.some(r => r.type === 'bug' || r.type === 'security'); const hasSuggestions = reviews.some(r => r.type === 'suggestion'); if (hasBugs) { const summary = reviews.filter(r => r.type === 'bug' || r.type === 'security') .map(r => `**${r.file}**: ${r.review}`).join('\n\n'); await this.github.addPullRequestReview( pr.number, 'CHANGES_REQUESTED', `Found some issues that need fixing:\n\n${summary}`, pr.head?.sha ); this.memory.remember(`pr-status-${pr.number}`, 'reviewed-changes', { tags: ['review', 'pr'], reviews, }); } else if (hasSuggestions) { const comment = await this.conversation.generatePRComment(pr, [], files); await this.github.addPullRequestComment(pr.number, comment); this.memory.remember(`pr-status-${pr.number}`, 'reviewed-comment', { tags: ['review', 'pr'], }); } else { const comment = await this.conversation.generatePRComment(pr, [], files); await this.github.addPullRequestReview(pr.number, 'APPROVED', comment, pr.head?.sha); this.memory.remember(`pr-status-${pr.number}`, 'reviewed-approved', { tags: ['review', 'pr'], }); } } async _handleApprovedPR(pr) { if (Math.random() < 0.3) { await this._mergePR(pr); } } async _handleChangesRequestedPR(pr) { if (Math.random() < 0.5) { await this._updatePRWithFixes(pr); } } async _handleUpdatedPR(pr) { const files = await this.github.getPullRequestFiles(pr.number); const comment = await this.conversation.generatePRComment(pr, [], files); const isApproved = Math.random() < 0.7; if (isApproved) { await this.github.addPullRequestReview(pr.number, 'APPROVED', comment, pr.head?.sha); this.memory.remember(`pr-status-${pr.number}`, 'reviewed-approved', { tags: ['review', 'pr'], }); } else { await this.github.addPullRequestComment(pr.number, comment); } } async _mergePR(pr) { logger.info(`Merging PR #${pr.number}`); try { await this.github.mergePullRequest(pr.number); if (pr.head?.ref) { await this.github.deleteBranch(pr.head.ref); } this.memory.remember(`pr-status-${pr.number}`, 'merged', { tags: ['merged', 'pr'], }); logger.info(`Merged PR #${pr.number}`); } catch (error) { logger.error(`Failed to merge PR #${pr.number}: ${error.message}`); } } async _updatePRWithFixes(pr) { logger.info(`Updating PR #${pr.number} with fixes`); const files = await this.github.getPullRequestFiles(pr.number); if (files.length === 0) return; const file = files[Math.floor(Math.random() * files.length)]; const codeContent = await this._generateFixCode(file); await this.github.createOrUpdateFile( file.filename, codeContent, 'fix: address review comments', pr.head?.ref || 'main' ); const followUp = await this.conversation.generateFollowUpComment(pr, 'review feedback', ['Fixed the issue']); await this.github.addPullRequestComment(pr.number, followUp); this.memory.remember(`pr-status-${pr.number}`, 'updated', { tags: ['updated', 'pr'], }); } async _generateFixCode(file) { const prompt = `اكتب كود محسّن للملف ${file.filename}. يجب أن يكون كود أفضل من النسخة السابقة. اكتب الكود فقط.`; const code = await this.ai.generate(prompt, { maxTokens: 1000, temperature: 0.6, }); return code.trim(); } } export default PRWorkflow;