UNPKG

autoagent-cli

Version:

Run autonomous AI agents using Claude or Gemini for task execution

320 lines (319 loc) 13.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateChange = validateChange; exports.scoreChange = scoreChange; exports.categorizeChanges = categorizeChanges; exports.detectDependencies = detectDependencies; exports.resolveConflicts = resolveConflicts; exports.prioritizeChanges = prioritizeChanges; exports.analyzeImprovements = analyzeImprovements; exports.getAnalysisQualityScore = getAnalysisQualityScore; const fs_1 = require("fs"); const path_1 = require("path"); const index_js_1 = require("../types/index.js"); function validateChange(change, issuesDir = './issues', plansDir = './plans') { const errors = []; const warnings = []; if (change.type === undefined) { errors.push('Change type is required'); } if (!change.target) { errors.push('Change target is required'); } if (!change.description) { errors.push('Change description is required'); } if (!change.content) { errors.push('Change content is required'); } if (!change.rationale) { warnings.push('Change rationale is missing - this helps understand why the change is needed'); } if (change.type === index_js_1.ChangeType.MODIFY_ISSUE || change.type === index_js_1.ChangeType.MODIFY_PLAN) { const isIssue = change.type === index_js_1.ChangeType.MODIFY_ISSUE; const dir = isIssue ? issuesDir : plansDir; const expectedPattern = isIssue ? /^\d+-.*\.md$/ : /^\d+-.*\.md$/; if (!expectedPattern.test(change.target)) { errors.push(`Invalid ${isIssue ? 'issue' : 'plan'} filename format: ${change.target}`); } else { const targetPath = (0, path_1.join)(dir, change.target); if (!(0, fs_1.existsSync)(targetPath)) { errors.push(`Target file does not exist: ${targetPath}`); } } } if (change.type === index_js_1.ChangeType.ADD_ISSUE || change.type === index_js_1.ChangeType.ADD_PLAN) { const isIssue = change.type === index_js_1.ChangeType.ADD_ISSUE; const dir = isIssue ? issuesDir : plansDir; const targetPath = (0, path_1.join)(dir, change.target); if ((0, fs_1.existsSync)(targetPath)) { errors.push(`File already exists: ${targetPath}`); } } if (change.content.trim().length < 10) { errors.push('Change content is too short (minimum 10 characters)'); } if (change.content.length > 50000) { warnings.push('Change content is very large (>50KB) - consider splitting into smaller changes'); } return { isValid: errors.length === 0, errors, warnings }; } function scoreChange(change) { let impact = 0.5; if (change.type === index_js_1.ChangeType.ADD_ISSUE || change.type === index_js_1.ChangeType.ADD_PLAN) { impact = 0.8; } else if (change.type === index_js_1.ChangeType.ADD_DEPENDENCY) { impact = 0.6; } const criticalKeywords = ['critical', 'essential', 'required', 'must', 'breaking']; const hasCriticalKeyword = criticalKeywords.some(keyword => change.rationale.toLowerCase().includes(keyword)); if (hasCriticalKeyword) { impact = Math.min(1.0, impact + 0.2); } let complexity = 0.3; const contentLines = change.content.split('\n').length; if (contentLines > 100) { complexity = 0.7; } else if (contentLines > 50) { complexity = 0.5; } if (change.type === index_js_1.ChangeType.MODIFY_ISSUE || change.type === index_js_1.ChangeType.MODIFY_PLAN) { complexity = Math.min(1.0, complexity + 0.2); } let confidence = 0.7; if (change.rationale.length > 100) { confidence = 0.8; } if (change.rationale.length > 200) { confidence = 0.9; } if (contentLines > 200) { confidence = Math.max(0.3, confidence - 0.3); } const composite = (impact * 0.5) + ((1 - complexity) * 0.3) + (confidence * 0.2); return { impact, complexity, confidence, composite }; } function categorizeChanges(changes) { const categories = new Map(); for (const change of changes) { const category = `${change.type}:${change.target.split('-')[0]}`; if (!categories.has(category)) { categories.set(category, []); } const categoryArray = categories.get(category); if (categoryArray !== undefined) { categoryArray.push(change); } } return categories; } function detectDependencies(changes) { const dependencies = []; for (let i = 0; i < changes.length; i++) { for (let j = i + 1; j < changes.length; j++) { const change1 = changes[i]; const change2 = changes[j]; if (change1 && change2 && change1.type === index_js_1.ChangeType.ADD_DEPENDENCY) { const mentionsTarget = change1.content.includes(change2.target) || change1.description.includes(change2.target); const issueNumMatch = change2.target.match(/^(\d+)-/); const mentionsIssueNum = issueNumMatch !== null && (change1.content.includes(`Issue ${issueNumMatch[1]}`) || change1.description.includes(`Issue ${issueNumMatch[1]}`)); if (mentionsTarget || mentionsIssueNum) { dependencies.push({ from: change1, to: change2, type: 'requires', reason: 'Dependency change references target' }); } } if (change1 && change2 && change2.type === index_js_1.ChangeType.ADD_DEPENDENCY) { const mentionsTarget = change2.content.includes(change1.target) || change2.description.includes(change1.target); const issueNumMatch = change1.target.match(/^(\d+)-/); const mentionsIssueNum = issueNumMatch !== null && (change2.content.includes(`Issue ${issueNumMatch[1]}`) || change2.description.includes(`Issue ${issueNumMatch[1]}`)); if (mentionsTarget || mentionsIssueNum) { dependencies.push({ from: change2, to: change1, type: 'requires', reason: 'Dependency change references target' }); } } if (change1 && change2) { if (change1.type === index_js_1.ChangeType.ADD_PLAN && change2.type === index_js_1.ChangeType.ADD_ISSUE) { const issueNum = change2.target.match(/^(\d+)-/)?.[1]; if (issueNum !== undefined && change1.target.includes(`issue-${issueNum}`)) { dependencies.push({ from: change1, to: change2, type: 'requires', reason: 'Plan requires corresponding issue' }); } } else if (change2.type === index_js_1.ChangeType.ADD_PLAN && change1.type === index_js_1.ChangeType.ADD_ISSUE) { const issueNum = change1.target.match(/^(\d+)-/)?.[1]; if (issueNum !== undefined && change2.target.includes(`issue-${issueNum}`)) { dependencies.push({ from: change2, to: change1, type: 'requires', reason: 'Plan requires corresponding issue' }); } } } if (change1 && change2 && change1.target === change2.target && (change1.type === index_js_1.ChangeType.MODIFY_ISSUE || change1.type === index_js_1.ChangeType.MODIFY_PLAN) && (change2.type === index_js_1.ChangeType.MODIFY_ISSUE || change2.type === index_js_1.ChangeType.MODIFY_PLAN)) { dependencies.push({ from: change1, to: change2, type: 'conflicts', reason: 'Multiple modifications to the same file' }); } } } return dependencies; } function resolveConflicts(changes, dependencies) { const conflicts = dependencies.filter(dep => dep.type === 'conflicts'); const conflictMap = new Map(); for (const conflict of conflicts) { const key = conflict.from.target; if (!conflictMap.has(key)) { conflictMap.set(key, []); } const conflictList = conflictMap.get(key); if (conflictList) { conflictList.push(conflict.from, conflict.to); } } const resolved = []; const unresolvedConflicts = []; const processedChanges = new Set(); for (const change of changes) { if (processedChanges.has(change)) { continue; } const conflictGroup = conflictMap.get(change.target); if (!conflictGroup || conflictGroup.length === 0) { resolved.push(change); processedChanges.add(change); } else { const uniqueConflicts = Array.from(new Set(conflictGroup)); const scored = uniqueConflicts.map(c => ({ change: c, score: scoreChange(c) })); scored.sort((a, b) => b.score.composite - a.score.composite); if (scored.length > 0 && scored[0] !== undefined) { resolved.push(scored[0].change); processedChanges.add(scored[0].change); for (let i = 1; i < scored.length; i++) { const currentItem = scored[i]; if (currentItem !== undefined && !processedChanges.has(currentItem.change)) { unresolvedConflicts.push({ change1: scored[0].change, change2: currentItem.change, reason: 'Conflicting modifications to the same file - selected higher scoring change' }); processedChanges.add(currentItem.change); } } } } } return { resolved, conflicts: unresolvedConflicts }; } function prioritizeChanges(changes, dependencies) { const scoredChanges = changes.map(change => ({ ...change, score: scoreChange(change), dependencies: [], conflicts: [], priority: 0 })); for (const dep of dependencies) { const fromChange = scoredChanges.find(c => c.type === dep.from.type && c.target === dep.from.target && c.description === dep.from.description); if (fromChange) { if (dep.type === 'requires') { fromChange.dependencies.push(dep.to.target); } else if (dep.type === 'conflicts') { fromChange.conflicts.push(dep.to.target); } } } const visited = new Set(); const result = []; let currentPriority = 1; function visit(change) { if (visited.has(change)) { return; } visited.add(change); for (const depTarget of change.dependencies) { const dep = scoredChanges.find(c => c.target === depTarget); if (dep && !visited.has(dep)) { visit(dep); } } change.priority = currentPriority++; result.push(change); } scoredChanges.sort((a, b) => b.score.composite - a.score.composite); for (const change of scoredChanges) { visit(change); } return result; } function analyzeImprovements(analysis, issuesDir = './issues', plansDir = './plans') { const validChanges = []; const invalidChanges = []; for (const change of analysis.changes) { const validation = validateChange(change, issuesDir, plansDir); if (validation.isValid) { validChanges.push(change); } else { invalidChanges.push({ change, errors: validation.errors }); } } const dependencies = detectDependencies(validChanges); const { resolved, conflicts } = resolveConflicts(validChanges, dependencies); const executionOrder = prioritizeChanges(resolved, dependencies); return { validChanges: executionOrder, invalidChanges, dependencies: dependencies.filter(d => d.type !== 'conflicts'), conflicts, executionOrder }; } function getAnalysisQualityScore(analysis) { if (analysis.changes.length === 0) { return 0.0; } const scores = analysis.changes.map(scoreChange); const avgComposite = scores.reduce((sum, s) => sum + s.composite, 0) / scores.length; const combinedScore = (analysis.score + avgComposite) / 2; return Math.max(0.0, Math.min(1.0, combinedScore)); }