| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.feedbackCapture = exports.FeedbackCapture = void 0; |
| const octokit_1 = require("./octokit"); |
| const context_1 = require("./context"); |
| const feedback_tracker_1 = require("./feedback-tracker"); |
| const info = console.log; |
| const warning = console.warn; |
| const FEEDBACK_TAG = '<!-- prix_feedback_tracking -->'; |
| class FeedbackCapture { |
| commentMetadata = new Map(); |
| pendingFeedback = new Map(); |
| getCommentKey(pullNumber, path, startLine) { |
| return `${pullNumber}:${path}:${startLine}`; |
| } |
| trackComment(pullNumber, path, startLine, patternType, severity, confidence, commentId) { |
| const key = this.getCommentKey(pullNumber, path, startLine); |
| this.commentMetadata.set(key, { |
| commentId, |
| pullNumber, |
| path, |
| patternType, |
| severity, |
| confidence |
| }); |
| } |
| async checkSuggestionApplied(pullNumber, path, startLine) { |
| const key = this.getCommentKey(pullNumber, path, startLine); |
| const metadata = this.commentMetadata.get(key); |
| if (!metadata || !metadata.commentId) |
| return false; |
| try { |
| const comments = await octokit_1.octokit.pulls.listReviewComments({ |
| owner: context_1.als.getStore()?.repo.owner || '', |
| repo: context_1.als.getStore()?.repo.repo || '', |
| |
| pull_number: pullNumber |
| }); |
| const originalComment = comments.data.find((c) => c.id === metadata.commentId); |
| if (!originalComment) |
| return false; |
| const diff = await this.getFileDiffAtComment(pullNumber, path, startLine, originalComment.original_line || startLine); |
| const hasAppliedSuggestion = this.detectSuggestionApplication(originalComment.body || '', diff); |
| if (hasAppliedSuggestion) { |
| this.recordFeedback(key, 'accepted'); |
| } |
| return hasAppliedSuggestion; |
| } |
| catch (e) { |
| warning(`Failed to check suggestion application: ${e}`); |
| return false; |
| } |
| } |
| async getFileDiffAtComment(pullNumber, path, startLine, originalLine) { |
| try { |
| const diff = await octokit_1.octokit.repos.compareCommits({ |
| owner: context_1.als.getStore()?.repo.owner || '', |
| repo: context_1.als.getStore()?.repo.repo || '', |
| base: `${pullNumber}-base`, |
| head: `${pullNumber}-head` |
| }); |
| const file = diff.data.files?.find((f) => f.filename === path); |
| if (!file?.patch) |
| return ''; |
| const hunk = file.patch |
| .split('\n') |
| .slice(Math.max(0, originalLine - startLine - 1), originalLine - startLine + 5) |
| .join('\n'); |
| return hunk; |
| } |
| catch (e) { |
| return ''; |
| } |
| } |
| detectSuggestionApplication(originalBody, currentDiff) { |
| if (!originalBody.includes('```suggestion')) |
| return false; |
| if (!currentDiff) |
| return false; |
| const suggestionMatch = originalBody.match(/```suggestion\n([\s\S]*?)```/); |
| if (!suggestionMatch) |
| return false; |
| const suggestedCode = suggestionMatch[1].trim(); |
| const hasApplied = currentDiff.includes(suggestedCode.substring(0, Math.min(50, suggestedCode.length))); |
| return hasApplied; |
| } |
| recordFeedback(key, type) { |
| const metadata = this.commentMetadata.get(key); |
| if (!metadata) |
| return; |
| this.pendingFeedback.set(key, type); |
| const ctx = context_1.als.getStore(); |
| const teamId = ctx?.repo?.owner || 'default'; |
| const repoSlug = ctx?.repo?.repo || 'default'; |
| feedback_tracker_1.feedbackTracker.recordFeedback({ |
| patternType: metadata.patternType, |
| severity: metadata.severity, |
| filenamePattern: metadata.path, |
| repoSlug, |
| teamId, |
| confidence: metadata.confidence, |
| feedbackType: type |
| }); |
| info(`Recorded ${type} feedback for ${key}`); |
| } |
| async checkReactionFeedback(pullNumber) { |
| const ctx = context_1.als.getStore(); |
| const teamId = ctx?.repo?.owner || 'default'; |
| const repoSlug = ctx?.repo?.repo || 'default'; |
| try { |
| const comments = await octokit_1.octokit.pulls.listReviewComments({ |
| owner: teamId, |
| repo: repoSlug, |
| |
| pull_number: pullNumber |
| }); |
| for (const comment of comments.data) { |
| if (!comment.body?.includes(FEEDBACK_TAG)) |
| continue; |
| const reactions = await octokit_1.octokit.reactions.listForIssueComment({ |
| owner: teamId, |
| repo: repoSlug, |
| |
| comment_id: comment.id |
| }); |
| for (const reaction of reactions.data) { |
| const key = this.getCommentKey(pullNumber, comment.path || '', comment.line || comment.original_line || 0); |
| if (reaction.content === '+1' || reaction.content === 'heart') { |
| this.recordFeedback(key, 'accepted'); |
| } |
| else if (reaction.content === '-1' || reaction.content === 'eyes') { |
| this.recordFeedback(key, 'rejected'); |
| } |
| } |
| } |
| } |
| catch (e) { |
| warning(`Failed to check reaction feedback: ${e}`); |
| } |
| } |
| async trackReviewSubmission(pullNumber, reviewId, commitId) { |
| const ctx = context_1.als.getStore(); |
| const teamId = ctx?.repo?.owner || 'default'; |
| const repoSlug = ctx?.repo?.repo || 'default'; |
| info(`Tracking review submission: PR #${pullNumber}, review ID: ${reviewId}`); |
| try { |
| const comments = await octokit_1.octokit.pulls.listReviewComments({ |
| owner: teamId, |
| repo: repoSlug, |
| |
| pull_number: pullNumber |
| }); |
| for (const comment of comments.data) { |
| if (!comment.body?.includes(FEEDBACK_TAG)) |
| continue; |
| if (comment.user?.login !== 'github-actions[bot]') |
| continue; |
| const metadataMatch = comment.body.match(/data-pattern="([^"]+)"/); |
| const severityMatch = comment.body.match(/data-severity="([^"]+)"/); |
| const confidenceMatch = comment.body.match(/data-confidence="([^"]+)"/); |
| if (metadataMatch && severityMatch && confidenceMatch) { |
| const key = this.getCommentKey(pullNumber, comment.path || '', comment.line || comment.original_line || 0); |
| this.commentMetadata.set(key, { |
| commentId: comment.id, |
| pullNumber, |
| path: comment.path || '', |
| patternType: metadataMatch[1], |
| severity: severityMatch[1], |
| confidence: parseFloat(confidenceMatch[1]) |
| }); |
| } |
| } |
| } |
| catch (e) { |
| warning(`Failed to track review submission: ${e}`); |
| } |
| } |
| getFeedbackStats(teamId, repoSlug) { |
| return feedback_tracker_1.feedbackTracker.getTeamInsights(teamId, repoSlug); |
| } |
| shouldSuppressFinding(patternType, filename, teamId, repoSlug) { |
| return feedback_tracker_1.feedbackTracker.shouldSuppressPattern(patternType, teamId, repoSlug); |
| } |
| } |
| exports.FeedbackCapture = FeedbackCapture; |
| exports.feedbackCapture = new FeedbackCapture(); |
| |