UNPKG

refakts

Version:

TypeScript refactoring tool built for AI coding agents to perform precise refactoring operations via command line instead of requiring complete code regeneration.

110 lines 5.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.changeFrequencyCheck = void 0; const child_process_1 = require("child_process"); const util_1 = require("util"); const execAsync = (0, util_1.promisify)(child_process_1.exec); exports.changeFrequencyCheck = { name: 'changeFrequency', check: async (sourceDir) => { const changeIssues = await analyzeChangeFrequency(); const filteredIssues = await filterRecentlyFixedIssues(changeIssues); return filteredIssues.map(toQualityIssue); }, getGroupDefinition: (groupKey) => { if (groupKey === 'changeFrequency') return { title: 'OPEN-CLOSED PRINCIPLE VIOLATIONS', description: 'Files changing frequently suggest design should be extensible without modification.', actionGuidance: 'Consider introducing abstractions, dependency injection, or plugin patterns to improve the design. Suggest the improvement to the user starting with the ⚠️ emoji a detailed explanation of your design suggestion.', requiresUserConsultation: true }; if (groupKey === 'cohesiveChange') return { title: 'ABSTRACTION LEAKAGE', description: 'Files changing together suggest concerns not properly encapsulated.', actionGuidance: 'Consider extracting shared abstractions or reducing coupling. Suggest the improvement to the user starting with the ⚠️ emoji a detailed explanation of your design suggestion.', requiresUserConsultation: true }; return undefined; } }; const analyzeChangeFrequency = async () => { try { const recentlyChangedFiles = await getRecentlyChangedFiles(); if (recentlyChangedFiles.length === 0) return []; const fileChanges = await analyzeFileChangeFrequency(recentlyChangedFiles); const cohesiveChanges = await analyzeCohesiveChanges(recentlyChangedFiles); return [...fileChanges, ...cohesiveChanges]; } catch (error) { return []; } }; const getRecentlyChangedFiles = async () => { const { stdout } = await execAsync('git log --oneline --name-only -2'); return stdout.split('\n') .filter(line => line.startsWith('src/')) .filter((file, index, array) => array.indexOf(file) === index); }; const analyzeFileChangeFrequency = async (recentlyChangedFiles) => { const { stdout } = await execAsync('git log --oneline --name-only -100 | grep "^src/" | sort | uniq -c | sort -nr'); return stdout.split('\n') .filter(line => line.trim()) .map(line => line.trim().split(/\s+/)) .filter(([count, file]) => parseInt(count) >= 10 && recentlyChangedFiles.includes(file)) .map(([count, file]) => `${file} changed ${count} times in last 100 commits`); }; const analyzeCohesiveChanges = async (recentlyChangedFiles) => { const { stdout } = await execAsync('git log --oneline --name-only -100'); const commits = stdout.split('\n\n').filter(Boolean); const filePairs = generateFilePairs(commits, recentlyChangedFiles); const frequentPairs = countFilePairs(filePairs); return Array.from(frequentPairs.entries()) .filter(([, count]) => count >= 10) .map(([pair, count]) => `[${pair}] change together ${count} times`); }; const generateFilePairs = (commits, recentlyChangedFiles) => { const pairs = []; commits.forEach(commit => { const files = commit.split('\n').slice(1).filter(f => f.startsWith('src/')); for (let i = 0; i < files.length; i++) { for (let j = i + 1; j < files.length; j++) { if (recentlyChangedFiles.includes(files[i]) || recentlyChangedFiles.includes(files[j])) { const pair = [files[i], files[j]].sort().join(', '); pairs.push(pair); } } } }); return pairs; }; const countFilePairs = (pairs) => { const counts = new Map(); pairs.forEach(pair => counts.set(pair, (counts.get(pair) || 0) + 1)); return counts; }; const filterRecentlyFixedIssues = async (issues) => { const recentCommits = await getRecentCommitMessages(3); const hasQualityFix = recentCommits.some(isQualityFixCommit); return hasQualityFix ? [] : issues; }; const getRecentCommitMessages = async (count) => { try { const { stdout } = await execAsync(`git log --oneline -${count} --pretty=format:"%s"`); return stdout.split('\n').filter(Boolean); } catch (error) { return []; } }; const isQualityFixCommit = (message) => { const keywords = ['refactor', 'quality', 'extract', 'simplify', 'cleanup', 'structure']; return keywords.some(keyword => message.toLowerCase().includes(keyword)); }; const toQualityIssue = (issue) => ({ type: issue.includes('change together') ? 'cohesiveChange' : 'changeFrequency', message: issue }); //# sourceMappingURL=change-frequency-check.js.map