Spaces:
Paused
Paused
| ; | |
| /** | |
| * 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 |