Spaces:
Sleeping
Sleeping
| 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; | |