"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 = ''; 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 || '', // eslint-disable-next-line camelcase 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, // eslint-disable-next-line camelcase 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, // eslint-disable-next-line camelcase 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, // eslint-disable-next-line camelcase 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(); //# sourceMappingURL=feedback-capture.js.map