"use strict"; /** * Patch parsing utilities * Extracted from review.ts to improve modularity */ Object.defineProperty(exports, "__esModule", { value: true }); exports.extractPatternType = exports.parseReview = exports.shouldIgnoreContent = exports.shouldIncludeFile = exports.checkIfDocumentationOnly = exports.parsePatch = exports.patchStartEndLine = exports.splitPatch = exports.validateRemedyAST = void 0; const ts_morph_1 = require("ts-morph"); /** * Validate remedy code AST * Ensures the remedy doesn't create invalid TypeScript/JS */ const validateRemedyAST = (filename, remedy, project) => { if (!filename.endsWith('.ts') && !filename.endsWith('.js')) return true; try { const sf = project.createSourceFile(`test_${Date.now()}.ts`, remedy, { overwrite: true }); // Check for basic validity - no unexpected class declarations in remedy return !sf.getDescendantsOfKind(ts_morph_1.SyntaxKind.ClassDeclaration).length; } catch (e) { return false; } }; exports.validateRemedyAST = validateRemedyAST; /** * Split a patch into individual hunks */ const splitPatch = (patch) => { if (patch == null) return []; const pattern = /(^@@ -(\d+),(\d+) \+(\d+),(\d+) @@).*$/gm; const result = []; let match = pattern.exec(patch); if (match != null) { const first = match.index + match[0].length; let last = first; while ((match = pattern.exec(patch)) !== null) { result.push(patch.substring(last, match.index).trim()); last = match.index + match[0].length; } result.push(patch.substring(last).trim()); } return result; }; exports.splitPatch = splitPatch; /** * Extract start/end line numbers from patch header * Exported for use by review.ts */ const patchStartEndLine = (patch) => { const pattern = /(^@@ -(\d+),(\d+) \+(\d+),(\d+) @@).*$/gm; const match = pattern.exec(patch); if (match != null) { const oldStart = parseInt(match[2]); const oldLength = parseInt(match[3]); const newStart = parseInt(match[4]); const newLength = parseInt(match[5]); return { oldStart, oldLength, newStart, newLength, oldHunk: { startLine: oldStart, endLine: oldStart + oldLength - 1 }, newHunk: { startLine: newStart, endLine: newStart + newLength - 1 } }; } return null; }; exports.patchStartEndLine = patchStartEndLine; /** * Parse a patch into old and new hunks */ const parsePatch = (patch) => { const hunkInfo = (0, exports.patchStartEndLine)(patch); if (hunkInfo == null) return null; const oldHunkLines = []; const newHunkLines = []; const lines = patch.split('\n'); for (const line of lines) { if (line.startsWith('-')) { oldHunkLines.push(line.substring(1)); } else if (line.startsWith('+')) { newHunkLines.push(line.substring(1)); } else if (!line.startsWith('@@')) { oldHunkLines.push(line); newHunkLines.push(line); } } return { oldHunk: oldHunkLines.join('\n'), newHunk: newHunkLines.join('\n'), oldStart: hunkInfo.oldStart, newStart: hunkInfo.newStart }; }; exports.parsePatch = parsePatch; /** * Check if a patch contains only documentation changes (comments) */ const checkIfDocumentationOnly = (diff) => { const content = diff .split('\n') .filter(l => l.startsWith('+') || l.startsWith('-')) .map(l => l.substring(1).trim()) .join(''); return content.length > 0 && !/[a-zA-Z0-9]/.test(content.replace(/\/\/|\/\*|\*|#|"""|'''/g, '')); }; exports.checkIfDocumentationOnly = checkIfDocumentationOnly; /** * Check if a file should be included based on path filters */ const shouldIncludeFile = (filename, pathFilters) => { if (pathFilters == null) return true; return pathFilters.check(filename); }; exports.shouldIncludeFile = shouldIncludeFile; /** * Check if content contains the ignore keyword */ const shouldIgnoreContent = (content, ignoreKeyword) => { return content.includes(ignoreKeyword); }; exports.shouldIgnoreContent = shouldIgnoreContent; const parseReview = (response, patches, filename, debug = false) => { const reviews = []; // Match patterns like: // [LINE_START-LINE_END] | [SEVERITY] | [TYPE] message // or // [LINE] | [SEVERITY] message const pattern = /\[(\d+)(-(\d+))?\]\s*\|\s*(critical|major|minor|info)?\s*\|\s*(\w+)?\s*[:|-]?\s*(.+?)(?=\[(\d+)|$)/gis; let match; while ((match = pattern.exec(response)) !== null) { const startLine = parseInt(match[1]); const endLine = match[2] ? parseInt(match[2]) : startLine; const severity = (match[4] || 'minor'); const type = match[5] || 'general'; const message = match[6].trim(); // Validate line numbers against patch const validLine = patches.some((p) => startLine >= p.newStart && startLine <= p.newStart + p.newLength); if (validLine) { reviews.push({ startLine, endLine, comment: `[${severity.toUpperCase()}] | ${type.toUpperCase()}\n\n${message}`, severity, confidence: 80 }); } } // Also look for remedy suggestions in code blocks const remedyPattern = /```(?:\w+)?\s*\n([\s\S]*?)```/g; let remedyMatch; while ((remedyMatch = remedyPattern.exec(response)) !== null) { const remedy = remedyMatch[1].trim(); // Associate remedy with last review if any if (reviews.length > 0) { reviews[reviews.length - 1].remedy = remedy; } } return reviews; }; exports.parseReview = parseReview; /** * Extract pattern type from a review message */ const extractPatternType = (message) => { const patternTypes = [ 'Security', 'Bug', 'Performance', 'Architecture', 'Style', 'Documentation', 'Testing', 'Refactoring' ]; for (const type of patternTypes) { if (message.toLowerCase().includes(type.toLowerCase())) { return type.toLowerCase(); } } return 'general'; }; exports.extractPatternType = extractPatternType; //# sourceMappingURL=patch-utils.js.map