PRIX / lib /src /issue-handler.js
yamxxx1's picture
Upload 117 files
a2fe16e verified
Raw
History Blame
7.93 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleIssueComment = exports.repo = exports.context = void 0;
const octokit_1 = require("./octokit");
const tokenizer_1 = require("./tokenizer");
const context_1 = require("./context");
const inputs_1 = require("./inputs");
const commenter_1 = require("./commenter");
let info = console.log;
let warning = console.warn;
exports.context = new Proxy({}, {
get(target, prop) {
return (context_1.als.getStore()?.probotContext)[prop];
}
});
exports.repo = new Proxy({}, {
get(target, prop) {
return (context_1.als.getStore()?.repo)[prop];
}
});
const ISSUE_COMMAND_TAG = '@prix';
const MAX_API_CALLS = 100;
const MAX_FILES_RETURNED = 200;
let apiCallCount = 0;
const handleIssueComment = async (heavyBot, options, prompts) => {
const inputs = new inputs_1.Inputs();
if (exports.context.name !== 'issue_comment') {
warning(`Skipped: ${exports.context.name} is not an issue_comment event`);
return;
}
if (!exports.context.payload) {
warning(`Skipped: issue_comment event is missing payload`);
return;
}
if (!exports.context.payload.repository) {
warning(`Skipped: issue_comment event is missing repository`);
return;
}
const comment = exports.context.payload.comment;
if (comment == null) {
warning(`Skipped: issue_comment event is missing comment`);
return;
}
const issue = exports.context.payload.issue;
if (issue == null) {
warning(`Skipped: issue_comment event is missing issue`);
return;
}
if (exports.context.payload.action !== 'created') {
warning(`Skipped: issue_comment event is not created`);
return;
}
const isBotComment = comment.user?.type === 'Bot';
if (isBotComment) {
info(`Skipped: issue_comment event is from the bot itself`);
return;
}
if (!comment.body.includes(ISSUE_COMMAND_TAG)) {
info(`Skipped: comment does not mention @prix`);
return;
}
if (!exports.repo?.owner || !exports.repo?.repo) {
warning(`Skipped: repo context is missing`);
return;
}
const issueNumber = issue.number;
if (!issueNumber || typeof issueNumber !== 'number') {
warning(`Skipped: issue number is invalid`);
return;
}
const issueTitle = issue.title || '';
const issueBody = issue.body || '';
const commentBody = comment.body || '';
const commentUser = comment.user?.login || 'unknown';
if (issueTitle.length > 500) {
await postIssueComment(issueNumber, `@${commentUser} Issue title is too long. Please shorten it to under 500 characters.`);
return;
}
if (issueBody.length > 10000) {
await postIssueComment(issueNumber, `@${commentUser} Issue description is too long. Please simplify it to under 10,000 characters.`);
return;
}
info(`Processing issue comment for issue #${issueNumber} from @${commentUser}`);
inputs.title = issueTitle;
inputs.description = issueBody;
inputs.comment = `${commentUser}: ${commentBody}`;
const taskDescription = extractTaskFromComment(commentBody);
inputs.taskDescription = taskDescription;
apiCallCount = 0;
const counter = { fileCount: 0 };
const codebaseStructure = await fetchCodebaseStructure(exports.repo.owner, exports.repo.repo, () => counter.fileCount >= MAX_FILES_RETURNED, counter);
inputs.codebaseStructure = codebaseStructure;
if (counter.fileCount > MAX_FILES_RETURNED) {
info(`Codebase structure truncated: ${counter.fileCount} files found, limit is ${MAX_FILES_RETURNED}`);
}
const tokens = (0, tokenizer_1.getTokenCount)(prompts.renderIssueAnalysis(inputs));
if (tokens > options.heavyTokenLimits.requestTokens) {
await postIssueComment(issueNumber, `@${commentUser} The issue content is too large to process. Please simplify or split the issue.`);
return;
}
const [analysis] = await heavyBot.chat(prompts.renderIssueAnalysis(inputs), {});
if (!analysis || analysis.trim().length === 0) {
warning(`Empty analysis received for issue #${issueNumber}`);
return;
}
const response = formatIssueResponse(commentUser, analysis);
await postIssueComment(issueNumber, response);
info(`Successfully processed issue comment for issue #${issueNumber}`);
};
exports.handleIssueComment = handleIssueComment;
const EXCLUDED_DIRS = new Set([
'node_modules',
'.git',
'dist',
'build',
'coverage',
'.next',
'__pycache__',
'venv',
'.venv',
'target',
'bin',
'obj',
'.idea',
'.vscode',
'vendor',
'packages'
]);
async function fetchCodebaseStructure(owner, repoName, shouldStop, counter) {
try {
const structure = await fetchDirectoryTree(owner, repoName, '', 3, shouldStop, 0, counter);
return structure || 'Unable to fetch codebase structure';
}
catch (error) {
warning(`Failed to fetch codebase structure: ${error}`);
return 'Codebase structure unavailable';
}
}
async function fetchDirectoryTree(owner, repo, path, depth, shouldStop, currentDepth = 0, counter) {
if (currentDepth >= depth) {
return '';
}
if (apiCallCount >= MAX_API_CALLS) {
return '';
}
if (shouldStop()) {
return '';
}
try {
apiCallCount++;
const response = await octokit_1.octokit.repos.getContent({
owner,
repo,
path,
ref: exports.context.payload.repository?.default_branch || 'main'
});
const items = Array.isArray(response.data) ? response.data : [response.data];
let tree = '';
for (const item of items) {
if (shouldStop()) {
break;
}
if (item.type === 'dir' && !EXCLUDED_DIRS.has(item.name)) {
tree += `${' '.repeat(currentDepth)}📁 ${item.name}/\n`;
const subTree = await fetchDirectoryTree(owner, repo, item.path, depth, shouldStop, currentDepth + 1, counter);
tree += subTree;
}
else if (item.type === 'file') {
const ext = item.name.split('.').pop() || '';
if (isRelevantExtension(ext)) {
tree += `${' '.repeat(currentDepth)}📄 ${item.name}\n`;
if (counter) {
counter.fileCount++;
}
}
}
}
return tree;
}
catch (error) {
return '';
}
}
function isRelevantExtension(ext) {
const relevantExts = new Set([
'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs',
'py', 'rb', 'go', 'rs', 'java', 'kt', 'scala',
'cpp', 'c', 'h', 'hpp', 'cs', 'swift',
'json', 'yaml', 'yml', 'toml', 'xml',
'md', 'txt', 'sh', 'bash', 'zsh',
'css', 'scss', 'sass', 'less',
'html', 'htm', 'vue', 'svelte',
'sql', 'graphql', 'proto',
'dockerfile', 'env', 'gitignore', 'editorconfig'
]);
return relevantExts.has(ext.toLowerCase());
}
function extractTaskFromComment(commentBody) {
const mentionPattern = /@prix\s*(.*)$/i;
const match = commentBody.match(mentionPattern);
return match ? match[1].trim() : commentBody;
}
async function postIssueComment(issueNumber, body) {
try {
await octokit_1.octokit.issues.createComment({
owner: exports.repo.owner,
repo: exports.repo.repo,
issue_number: issueNumber,
body
});
}
catch (error) {
warning(`Failed to post issue comment: ${error}`);
}
}
function formatIssueResponse(user, analysis) {
return `## @${user} Issue Analysis
${analysis}${commenter_1.PLAN_BRANDING}`;
}
//# sourceMappingURL=issue-handler.js.map