Spaces:
Paused
Paused
| ; | |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | |
| if (k2 === undefined) k2 = k; | |
| var desc = Object.getOwnPropertyDescriptor(m, k); | |
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | |
| desc = { enumerable: true, get: function() { return m[k]; } }; | |
| } | |
| Object.defineProperty(o, k2, desc); | |
| }) : (function(o, m, k, k2) { | |
| if (k2 === undefined) k2 = k; | |
| o[k2] = m[k]; | |
| })); | |
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | |
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | |
| }) : function(o, v) { | |
| o["default"] = v; | |
| }); | |
| var __importStar = (this && this.__importStar) || (function () { | |
| var ownKeys = function(o) { | |
| ownKeys = Object.getOwnPropertyNames || function (o) { | |
| var ar = []; | |
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | |
| return ar; | |
| }; | |
| return ownKeys(o); | |
| }; | |
| return function (mod) { | |
| if (mod && mod.__esModule) return mod; | |
| var result = {}; | |
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | |
| __setModuleDefault(result, mod); | |
| return result; | |
| }; | |
| })(); | |
| var __importDefault = (this && this.__importDefault) || function (mod) { | |
| return (mod && mod.__esModule) ? mod : { "default": mod }; | |
| }; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| const options_1 = require("./src/options"); | |
| const prompts_1 = require("./src/prompts"); | |
| const review_1 = require("./src/review"); | |
| const review_comment_1 = require("./src/review-comment"); | |
| const context_1 = require("./src/context"); | |
| const octokit_1 = require("./src/octokit"); | |
| const checkout_1 = require("./src/checkout"); | |
| const promises_1 = require("fs/promises"); | |
| const path_1 = require("path"); | |
| const os_1 = require("os"); | |
| const feedback_capture_1 = require("./src/feedback-capture"); | |
| const feedback_tracker_1 = require("./src/feedback-tracker"); | |
| const lru_cache_1 = require("lru-cache"); | |
| const pino_1 = __importDefault(require("pino")); | |
| const zod_1 = require("zod"); | |
| const prom_client_1 = __importDefault(require("prom-client")); | |
| const bot_1 = require("./src/bot"); | |
| const options_2 = require("./src/options"); | |
| const issue_handler_1 = require("./src/issue-handler"); | |
| // Environment variable validation schema | |
| const envSchema = zod_1.z.object({ | |
| GROQ_API_KEY: zod_1.z.string().min(1, 'GROQ_API_KEY is required'), | |
| AI_API_KEY: zod_1.z.string().optional(), | |
| DEBUG: zod_1.z.enum(['true', 'false']).default('false'), | |
| DISABLE_REVIEW: zod_1.z.enum(['true', 'false']).default('false'), | |
| MAX_FILES: zod_1.z.coerce.number().min(0).max(1000).default(0), | |
| REVIEW_SIMPLE_CHANGES: zod_1.z.enum(['true', 'false']).default('false'), | |
| REVIEW_COMMENT_LGTM: zod_1.z.enum(['true', 'false']).default('false'), | |
| PATH_FILTERS: zod_1.z.string().optional(), | |
| SYSTEM_MESSAGE: zod_1.z.string().default(''), | |
| LIGHT_MODEL: zod_1.z.string().default('meta-llama/llama-4-scout-17b-16e-instruct'), | |
| HEAVY_MODEL: zod_1.z.string().default('llama-3.3-70b-versatile'), | |
| MODEL_TEMPERATURE: zod_1.z.coerce.number().min(0).max(2).default(0), | |
| RETRIES: zod_1.z.coerce.number().min(0).max(10).default(3), | |
| TIMEOUT_MS: zod_1.z.coerce.number().min(1000).max(300000).default(120000), | |
| CONCURRENCY_LIMIT: zod_1.z.coerce.number().min(1).max(10).default(1), | |
| GITHUB_CONCURRENCY_LIMIT: zod_1.z.coerce.number().min(1).max(10).default(1), | |
| API_BASE_URL: zod_1.z.string().url().default('https://api.groq.com/openai/v1'), | |
| LANGUAGE: zod_1.z.string().default('en-US'), | |
| MINIMUM_SEVERITY: zod_1.z.enum(['critical', 'major', 'minor', 'info']).default('minor'), | |
| MIN_CONFIDENCE: zod_1.z.coerce.number().min(0).max(100).default(0), | |
| DENSE_MODE: zod_1.z.enum(['true', 'false']).default('false'), | |
| SHOW_IMPACT_ANALYSIS: zod_1.z.enum(['true', 'false']).default('true'), | |
| LOG_LEVEL: zod_1.z.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal']).default('info') | |
| }); | |
| // Validate env vars on startup | |
| try { | |
| envSchema.parse(process.env); | |
| } | |
| catch (error) { | |
| console.error('Invalid environment variables:', error); | |
| process.exit(1); | |
| } | |
| const logger = (0, pino_1.default)({ level: process.env.LOG_LEVEL || 'info' }); | |
| // Webhook rate limiter: max 30 requests per repo per minute | |
| const webhookRateLimiter = new lru_cache_1.LRUCache({ | |
| max: 1000, // Track 1000 repos | |
| ttl: 60000, // 1 minute window | |
| updateAgeOnGet: false | |
| }); | |
| function checkRateLimit(repo) { | |
| const current = webhookRateLimiter.get(repo) || 0; | |
| if (current >= 30) { | |
| logger.warn({ repo, count: current }, 'Webhook rate limit exceeded'); | |
| return false; | |
| } | |
| webhookRateLimiter.set(repo, current + 1); | |
| return true; | |
| } | |
| /** | |
| * PRIX (AI PR Reviewer and Auditor) | |
| * Probot Service Entry Point | |
| */ | |
| exports.default = (app, { getRouter }) => { | |
| app.log.info('PRIX module loading...'); | |
| // 0. Health Check for Hugging Face | |
| if (getRouter) { | |
| const router = getRouter(); | |
| router.get('/', (_req, res) => { | |
| res.status(200).send(` | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PRIX | AI Code Auditor</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap'); | |
| :root { | |
| --bg: #09090b; | |
| --primary: #6366f1; | |
| --secondary: #a855f7; | |
| --accent: #ec4899; | |
| --card: rgba(24, 24, 27, 0.8); | |
| --border: rgba(63, 63, 70, 0.5); | |
| --text: #fafafa; | |
| } | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| background-color: var(--bg); | |
| color: var(--text); | |
| font-family: 'Outfit', sans-serif; | |
| height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| overflow: hidden; | |
| } | |
| /* Animated background glow */ | |
| .glow { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 500px; | |
| height: 500px; | |
| background: radial-gradient(circle, var(--primary) 0%, transparent 70%); | |
| opacity: 0.15; | |
| filter: blur(80px); | |
| z-index: 0; | |
| animation: pulse 8s infinite alternate; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 0.1; transform: translate(-50%, -50%) scale(1); } | |
| 100% { opacity: 0.2; transform: translate(-50%, -50%) scale(1.2); } | |
| } | |
| .container { | |
| position: relative; | |
| z-index: 10; | |
| padding: 3rem; | |
| background: var(--card); | |
| backdrop-filter: blur(12px); | |
| border: 1px solid var(--border); | |
| border-radius: 24px; | |
| text-align: center; | |
| max-width: 500px; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); | |
| } | |
| .logo-icon { | |
| width: 80px; | |
| height: 80px; | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| margin: 0 auto 1.5rem; | |
| border-radius: 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 0 0 30px rgba(99, 102, 241, 0.4); | |
| } | |
| .logo-icon svg { | |
| width: 45px; | |
| height: 45px; | |
| fill: white; | |
| } | |
| h1 { | |
| font-size: 2.5rem; | |
| margin: 0; | |
| background: linear-gradient(to right, #fff, #94a3b8); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| letter-spacing: -0.02em; | |
| } | |
| p { | |
| color: #94a3b8; | |
| font-size: 1.125rem; | |
| margin: 1rem 0 2rem; | |
| } | |
| .status-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| background: rgba(16, 185, 129, 0.1); | |
| color: #10b981; | |
| padding: 0.5rem 1rem; | |
| border-radius: 100px; | |
| font-weight: 600; | |
| font-size: 0.875rem; | |
| border: 1px solid rgba(16, 185, 129, 0.2); | |
| } | |
| .status-dot { | |
| width: 8px; | |
| height: 8px; | |
| background: #10b981; | |
| border-radius: 50%; | |
| box-shadow: 0 0 10px #10b981; | |
| animation: blink 2s infinite; | |
| } | |
| @keyframes blink { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.4; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="glow"></div> | |
| <div class="container"> | |
| <div class="logo-icon"> | |
| <svg viewBox="0 0 24 24"><path d="M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1M12,7C13.4,7 14.8,7.3 16,7.9V11C16,14.7 14.2,18.1 11.5,20.3L12,20.6L12.5,20.3C9.8,18.1 8,14.7 8,11V7.9C9.2,7.3 10.6,7 12,7Z"/></svg> | |
| </div> | |
| <h1>PRIX AUDITOR</h1> | |
| <p>Enterprise-grade AI Code Security & Symbol Engine is humming.</p> | |
| <div class="status-badge"> | |
| <div class="status-dot"></div> | |
| SERVICE ACTIVE | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| `); | |
| }); | |
| router.get('/ping', (_req, res) => { | |
| res.status(200).send('pong'); | |
| }); | |
| // Deep health check with external service validation | |
| router.get('/health', async (_req, res) => { | |
| const checks = {}; | |
| // 1. Check GitHub App configuration | |
| try { | |
| const appId = process.env.APP_ID; | |
| checks.github = { ok: !!appId }; | |
| } | |
| catch (error) { | |
| checks.github = { ok: false, error: error.message }; | |
| } | |
| // 2. Check AI API connectivity | |
| try { | |
| const apiKey = process.env.GROQ_API_KEY || process.env.AI_API_KEY; | |
| if (!apiKey) { | |
| checks.ai = { ok: false, error: 'No API key configured' }; | |
| } | |
| else { | |
| // Simple check - verify key exists (don't make actual API call to avoid costs) | |
| checks.ai = { ok: true }; | |
| } | |
| } | |
| catch (error) { | |
| checks.ai = { ok: false, error: error.message }; | |
| } | |
| // 3. Check disk space (basic) | |
| try { | |
| const tempDir = (0, os_1.tmpdir)(); | |
| checks.disk = { ok: !!tempDir }; | |
| } | |
| catch (error) { | |
| checks.disk = { ok: false, error: error.message }; | |
| } | |
| // 4. Check memory usage | |
| const memUsage = process.memoryUsage(); | |
| checks.memory = { | |
| ok: memUsage.heapUsed < 1024 * 1024 * 1024, // Less than 1GB | |
| }; | |
| const allOk = Object.values(checks).every(c => c.ok); | |
| res.status(allOk ? 200 : 503).json({ | |
| status: allOk ? 'healthy' : 'unhealthy', | |
| checks, | |
| timestamp: new Date().toISOString(), | |
| version: process.env.npm_package_version || '1.1.0' | |
| }); | |
| }); | |
| // Prometheus metrics endpoint | |
| const register = new prom_client_1.default.Registry(); | |
| prom_client_1.default.collectDefaultMetrics({ register }); | |
| // Custom metrics | |
| const prReviewCounter = new prom_client_1.default.Counter({ | |
| name: 'prix_pr_reviews_total', | |
| help: 'Total number of PRs reviewed', | |
| labelNames: ['status'], | |
| registers: [register] | |
| }); | |
| const reviewDurationHistogram = new prom_client_1.default.Histogram({ | |
| name: 'prix_review_duration_seconds', | |
| help: 'Time spent reviewing PRs', | |
| buckets: [10, 30, 60, 120, 300, 600], | |
| registers: [register] | |
| }); | |
| router.get('/metrics', async (_req, res) => { | |
| try { | |
| res.set('Content-Type', register.contentType); | |
| res.end(await register.metrics()); | |
| } | |
| catch (ex) { | |
| res.status(500).end(ex.message); | |
| } | |
| }); | |
| } | |
| app.log.info('PRIX Service Started'); | |
| function parseSeverityLevel(value) { | |
| const validLevels = [ | |
| 'critical', | |
| 'major', | |
| 'minor', | |
| 'info' | |
| ]; | |
| if (value && validLevels.includes(value)) { | |
| return value; | |
| } | |
| return 'minor'; | |
| } | |
| // Helper to initialize options from environment variables | |
| const getOptions = () => { | |
| return new options_1.Options(process.env.DEBUG === 'true', process.env.DISABLE_REVIEW === 'true', process.env.DISABLE_RELEASE_NOTES === 'true', process.env.MAX_FILES || '0', process.env.REVIEW_SIMPLE_CHANGES === 'true', process.env.REVIEW_COMMENT_LGTM === 'true', process.env.PATH_FILTERS ? process.env.PATH_FILTERS.split(',') : null, process.env.SYSTEM_MESSAGE || '', process.env.LIGHT_MODEL || 'meta-llama/llama-4-scout-17b-16e-instruct', process.env.HEAVY_MODEL || 'llama-3.3-70b-versatile', process.env.MODEL_TEMPERATURE || '0.0', process.env.RETRIES || '3', process.env.TIMEOUT_MS || '120000', process.env.CONCURRENCY_LIMIT || '1', process.env.GITHUB_CONCURRENCY_LIMIT || '1', process.env.API_BASE_URL || 'https://api.groq.com/openai/v1', process.env.LANGUAGE || 'en-US', true, parseSeverityLevel(process.env.MINIMUM_SEVERITY), process.env.MIN_CONFIDENCE || '0', process.env.DENSE_MODE || 'false', process.env.SHOW_IMPACT_ANALYSIS || 'true', process.env.ENABLE_AUTO_PR === 'true'); | |
| }; | |
| // 1. Event Listeners for PR Audit | |
| app.on([ | |
| 'pull_request.opened', | |
| 'pull_request.synchronize', | |
| 'pull_request.reopened' | |
| ], async (context) => { | |
| const { payload } = context; | |
| // Skip audits for PRs opened by bots to prevent recursive loops | |
| const user = payload.pull_request.user; | |
| if (user?.type === 'Bot') { | |
| app.log.info(`Skipping audit for PR #${payload.pull_request.number} because it was opened by a bot (${user.login})`); | |
| return; | |
| } | |
| // Rate limiting: max 30 webhooks per repo per minute | |
| const repoKey = `${context.repo().owner}/${context.repo().repo}`; | |
| if (!checkRateLimit(repoKey)) { | |
| logger.warn({ repo: repoKey, pr: payload.pull_request.number }, 'Rate limit exceeded, skipping audit'); | |
| return; | |
| } | |
| app.log.info(`Received ${context.name}.${payload.action} for PR #${payload.pull_request.number}`); | |
| try { | |
| const options = getOptions(); | |
| const prompts = new prompts_1.Prompts(); | |
| // Create a unique temporary directory for this audit in the system temp folder | |
| const tempDir = (0, path_1.join)((0, os_1.tmpdir)(), `prix_tmp_${payload.pull_request.id}_${Date.now()}`); | |
| // Create a safe, isolated execution context | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo(), | |
| workingDir: tempDir | |
| }, async () => { | |
| try { | |
| // Clone the repo locally so Git commands work | |
| await (0, checkout_1.checkoutRepo)(tempDir); | |
| // Initialize shims | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| // Trigger the main PRIX Audit | |
| await (0, review_1.run)(context, options, prompts); | |
| } | |
| finally { | |
| // Cleanup temp directory (async non-blocking) | |
| try { | |
| await (0, promises_1.rm)(tempDir, { recursive: true, force: true }); | |
| logger.debug({ tempDir }, 'Temp directory cleaned up'); | |
| } | |
| catch (cleanupErr) { | |
| logger.error({ tempDir, error: cleanupErr }, 'Failed to cleanup temp directory'); | |
| } | |
| } | |
| }); | |
| app.log.info(`Successfully processed audit for PR #${payload.pull_request.number}`); | |
| } | |
| catch (err) { | |
| app.log.error(`Failed to process audit for PR #${payload.pull_request.number}: ${err.message}`); | |
| // Log specific reason without crashing as per requirements | |
| } | |
| }); | |
| // 1b. Event Listener for Autonomous PR (Push to default branch) | |
| app.on('push', async (context) => { | |
| const { payload } = context; | |
| // Check if push is to default branch | |
| const defaultBranch = payload.repository.default_branch; | |
| const refBranch = payload.ref.replace('refs/heads/', ''); | |
| if (refBranch !== defaultBranch) { | |
| app.log.info(`Skipping autonomous audit on non-default branch: ${refBranch}`); | |
| return; | |
| } | |
| app.log.info(`Received push event on default branch ${defaultBranch}, triggering autonomous audit...`); | |
| try { | |
| const options = getOptions(); | |
| if (!options.enableAutoPR) { | |
| app.log.info('Auto PR is disabled, skipping autonomous audit.'); | |
| return; | |
| } | |
| const prompts = new prompts_1.Prompts(); | |
| const tempDir = (0, path_1.join)((0, os_1.tmpdir)(), `prix_auto_pr_${payload.after}_${Date.now()}`); | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo(), | |
| workingDir: tempDir | |
| }, async () => { | |
| try { | |
| await (0, checkout_1.checkoutRepo)(tempDir, { ref: payload.after }); | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| // Assuming autonomousAudit takes (context, options, prompts) | |
| const { autonomousAudit } = await Promise.resolve().then(() => __importStar(require('./src/review'))); | |
| await autonomousAudit(context, options, prompts); | |
| } | |
| finally { | |
| try { | |
| await (0, promises_1.rm)(tempDir, { recursive: true, force: true }); | |
| } | |
| catch (e) { | |
| logger.error({ error: e }, 'Failed to cleanup temp directory'); | |
| } | |
| } | |
| }); | |
| app.log.info('Successfully processed autonomous audit.'); | |
| } | |
| catch (err) { | |
| app.log.error(`Failed to process autonomous audit: ${err.message}`); | |
| } | |
| }); | |
| // 2. Event Listener for "Chat with Bot" (Reply to comments) and Reaction Feedback | |
| // Handles both chat replies AND reaction capture for PR comments | |
| app.on('pull_request_review_comment.created', async (context) => { | |
| const { payload } = context; | |
| app.log.info(`Received ${context.name}.${payload.action} for comment ${payload.comment.id}`); | |
| const isBotComment = payload.comment?.user?.type === 'Bot'; | |
| try { | |
| // 2a. Chat with Bot handler (skip bot's own comments) | |
| if (!isBotComment) { | |
| const options = getOptions(); | |
| const prompts = new prompts_1.Prompts(); | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo() | |
| }, async () => { | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| await (0, review_comment_1.run)(context, options, prompts); | |
| }); | |
| app.log.info(`Successfully processed chat reply for comment ${payload.comment.id}`); | |
| } | |
| // 2b. Reaction Feedback handler (captures 👍/❤️ as acceptance, 👀/-1 as rejection) | |
| if (!isBotComment) { | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo() | |
| }, async () => { | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| const pullNumber = payload.pull_request?.number; | |
| if (pullNumber) { | |
| await feedback_capture_1.feedbackCapture.checkReactionFeedback(pullNumber); | |
| app.log.info(`Processed reaction feedback for PR #${pullNumber}`); | |
| } | |
| }); | |
| } | |
| } | |
| catch (err) { | |
| app.log.error(`Failed to process pull_request_review_comment: ${err.message}`); | |
| } | |
| }); | |
| // 4. Event Listener for PR Review Submission | |
| // Tracks when a review is submitted to monitor suggestion acceptance | |
| app.on('pull_request_review.submitted', async (context) => { | |
| const { payload } = context; | |
| app.log.info(`Received review submitted for PR #${payload.pull_request?.number}, review ID: ${payload.review?.id}`); | |
| try { | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo() | |
| }, async () => { | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| const pullNumber = payload.pull_request?.number; | |
| const reviewId = payload.review?.id; | |
| const commitId = payload.review?.commit_id; | |
| if (pullNumber && reviewId && commitId) { | |
| await feedback_capture_1.feedbackCapture.trackReviewSubmission(pullNumber, reviewId, commitId); | |
| app.log.info(`Tracked review submission: PR #${pullNumber}`); | |
| } | |
| }); | |
| } | |
| catch (err) { | |
| app.log.error(`Failed to track review submission: ${err.message}`); | |
| } | |
| }); | |
| // 5. Event Listener for Reaction on Issues (for standalone issue comments) | |
| app.on('issue_comment.created', async (context) => { | |
| const { payload } = context; | |
| // Only process user comments (not bot comments) | |
| if (payload.comment?.user?.type === 'Bot') { | |
| return; | |
| } | |
| try { | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo() | |
| }, async () => { | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| // Check for feedback reactions on issue comments | |
| const { data: reactions } = await context.octokit.rest.reactions.listForIssueComment({ | |
| owner: context.repo().owner, | |
| repo: context.repo().repo, | |
| // eslint-disable-next-line camelcase | |
| comment_id: payload.comment.id | |
| }); | |
| for (const reaction of reactions) { | |
| if (reaction.content === '+1' || reaction.content === 'heart') { | |
| feedback_tracker_1.feedbackTracker.recordFeedback({ | |
| patternType: 'general', | |
| severity: 'info', | |
| filenamePattern: 'issue', | |
| repoSlug: context.repo().repo, | |
| teamId: context.repo().owner, | |
| confidence: 50, | |
| feedbackType: 'accepted', | |
| commentId: String(payload.comment.id) | |
| }); | |
| } | |
| else if (reaction.content === '-1' || | |
| reaction.content === 'eyes') { | |
| feedback_tracker_1.feedbackTracker.recordFeedback({ | |
| patternType: 'general', | |
| severity: 'info', | |
| filenamePattern: 'issue', | |
| repoSlug: context.repo().repo, | |
| teamId: context.repo().owner, | |
| confidence: 50, | |
| feedbackType: 'rejected', | |
| commentId: String(payload.comment.id) | |
| }); | |
| } | |
| } | |
| }); | |
| } | |
| catch (err) { | |
| app.log.error(`Failed to process issue reaction feedback: ${err.message}`); | |
| } | |
| }); | |
| // 6. Event Listener for @prix mention in Issue Comments | |
| // When someone mentions @prix in an issue comment, analyze the issue and generate a fix plan | |
| app.on('issue_comment.created', async (context) => { | |
| const { payload } = context; | |
| const comment = payload.comment; | |
| if (!comment || comment.user?.type === 'Bot') { | |
| return; | |
| } | |
| if (!comment.body.includes('@prix')) { | |
| return; | |
| } | |
| app.log.info(`Received @prix mention in issue #${payload.issue?.number} from @${comment.user?.login}`); | |
| try { | |
| const options = getOptions(); | |
| const prompts = new prompts_1.Prompts(); | |
| const heavyAIOptions = new options_2.AIOptions(options.heavyModel, options.heavyTokenLimits); | |
| const heavyBot = new bot_1.Bot(options, heavyAIOptions); | |
| await context_1.als.run({ | |
| probotContext: context, | |
| octokit: context.octokit, | |
| repo: context.repo() | |
| }, async () => { | |
| (0, octokit_1.setOctokit)(context.octokit); | |
| await (0, issue_handler_1.handleIssueComment)(heavyBot, options, prompts); | |
| }); | |
| app.log.info(`Successfully processed @prix mention in issue #${payload.issue?.number}`); | |
| } | |
| catch (err) { | |
| app.log.error(`Failed to process @prix mention in issue #${payload.issue?.number}: ${err.message}`); | |
| } | |
| }); | |
| }; | |
| //# sourceMappingURL=index.js.map |