Coverage for session_buddy / tools / agent_analyzer.py: 100.00%
60 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-04 00:43 -0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-04 00:43 -0800
1"""AI agent recommendation system for crackerjack failures."""
3import re
4import typing as t
5from dataclasses import dataclass
6from enum import StrEnum
9class AgentType(StrEnum):
10 """Available crackerjack AI agents."""
12 REFACTORING = "RefactoringAgent"
13 PERFORMANCE = "PerformanceAgent"
14 SECURITY = "SecurityAgent"
15 DOCUMENTATION = "DocumentationAgent"
16 TEST_CREATION = "TestCreationAgent"
17 DRY = "DRYAgent"
18 FORMATTING = "FormattingAgent"
19 IMPORT_OPTIMIZATION = "ImportOptimizationAgent"
20 TEST_SPECIALIST = "TestSpecialistAgent"
23@dataclass
24class AgentRecommendation:
25 """Recommendation for using a specific AI agent."""
27 agent: AgentType
28 confidence: float # 0.0-1.0
29 reason: str
30 quick_fix_command: str
31 pattern_matched: str
34class AgentAnalyzer:
35 """Analyze crackerjack failures and recommend AI agents."""
37 # Error patterns mapped to agents with confidence scores
38 PATTERNS: t.Final[list[dict[str, t.Any]]] = [
39 # Complexity issues → RefactoringAgent
40 {
41 "pattern": r"Complexity of (\d+) is too high",
42 "agent": AgentType.REFACTORING,
43 "confidence": 0.9,
44 "reason": "Complexity violation detected (limit: 15)",
45 "command": "python -m crackerjack --ai-fix",
46 },
47 {
48 "pattern": r"Function .* is too complex \((\d+)\)",
49 "agent": AgentType.REFACTORING,
50 "confidence": 0.85,
51 "reason": "Complex function needs refactoring",
52 "command": "python -m crackerjack --ai-fix",
53 },
54 # Security issues → SecurityAgent
55 {
56 "pattern": r"B\d{3}:",
57 "agent": AgentType.SECURITY,
58 "confidence": 0.8,
59 "reason": "Bandit security issue found",
60 "command": "python -m crackerjack --ai-fix",
61 },
62 {
63 "pattern": r"hardcoded.*path|shell=True|unsafe",
64 "agent": AgentType.SECURITY,
65 "confidence": 0.85,
66 "reason": "Security vulnerability detected",
67 "command": "python -m crackerjack --ai-fix",
68 },
69 # Test failures → TestCreationAgent
70 {
71 "pattern": r"(\d+) failed",
72 "agent": AgentType.TEST_CREATION,
73 "confidence": 0.8,
74 "reason": "Test failures need investigation",
75 "command": "python -m crackerjack --ai-fix --run-tests",
76 },
77 {
78 "pattern": r"FAILED tests/.*::",
79 "agent": AgentType.TEST_CREATION,
80 "confidence": 0.85,
81 "reason": "Specific test failure identified",
82 "command": "python -m crackerjack --ai-fix --run-tests",
83 },
84 # Coverage issues → TestSpecialistAgent
85 {
86 "pattern": r"coverage:?\s*(\d+(?:\.\d+)?)%",
87 "agent": AgentType.TEST_SPECIALIST,
88 "confidence": 0.7,
89 "reason": "Coverage below baseline (42%)",
90 "command": "python -m crackerjack --ai-fix --run-tests",
91 },
92 # Type errors → ImportOptimizationAgent
93 {
94 "pattern": r"error:|type.*error|Found (\d+) error",
95 "agent": AgentType.IMPORT_OPTIMIZATION,
96 "confidence": 0.75,
97 "reason": "Type or import errors detected",
98 "command": "python -m crackerjack --ai-fix",
99 },
100 # Formatting issues → FormattingAgent
101 {
102 "pattern": r"would reformat|line too long|trailing whitespace",
103 "agent": AgentType.FORMATTING,
104 "confidence": 0.9,
105 "reason": "Code formatting violations found",
106 "command": "python -m crackerjack --ai-fix",
107 },
108 # Code duplication → DRYAgent
109 {
110 "pattern": r"duplicate|repeated code|similar.*block",
111 "agent": AgentType.DRY,
112 "confidence": 0.8,
113 "reason": "Code duplication detected",
114 "command": "python -m crackerjack --ai-fix",
115 },
116 # Performance issues → PerformanceAgent
117 {
118 "pattern": r"slow|timeout|O\(n[²³]\)|inefficient",
119 "agent": AgentType.PERFORMANCE,
120 "confidence": 0.75,
121 "reason": "Performance issue identified",
122 "command": "python -m crackerjack --ai-fix",
123 },
124 # Documentation issues → DocumentationAgent
125 {
126 "pattern": r"missing.*docstring|undocumented|changelog",
127 "agent": AgentType.DOCUMENTATION,
128 "confidence": 0.7,
129 "reason": "Documentation needs improvement",
130 "command": "python -m crackerjack --ai-fix",
131 },
132 ]
134 @classmethod
135 def _should_skip_coverage_recommendation(
136 cls,
137 pattern_config: dict[str, t.Any],
138 combined: str,
139 ) -> bool:
140 """Check if coverage recommendation should be skipped."""
141 if pattern_config["agent"] != AgentType.TEST_SPECIALIST:
142 return False
144 coverage_match = re.search( # REGEX OK: coverage extraction
145 r"coverage:?\s*(\d+(?:\.\d+)?)%",
146 combined,
147 )
148 return bool(coverage_match and float(coverage_match.group(1)) >= 42)
150 @classmethod
151 def _deduplicate_recommendations(
152 cls,
153 recommendations: list[AgentRecommendation],
154 ) -> list[AgentRecommendation]:
155 """Remove duplicate recommendations, keeping highest confidence."""
156 unique: dict[AgentType, AgentRecommendation] = {}
157 for rec in recommendations:
158 if rec.agent not in unique or rec.confidence > unique[rec.agent].confidence:
159 unique[rec.agent] = rec
161 return sorted(unique.values(), key=lambda x: x.confidence, reverse=True)[:3]
163 @classmethod
164 def analyze(
165 cls,
166 stdout: str,
167 stderr: str,
168 exit_code: int,
169 ) -> list[AgentRecommendation]:
170 """Analyze crackerjack output and recommend agents.
172 Args:
173 stdout: Standard output from crackerjack
174 stderr: Standard error from crackerjack
175 exit_code: Process exit code
177 Returns:
178 List of agent recommendations sorted by confidence (highest first)
180 """
181 if exit_code == 0:
182 return [] # No failures, no recommendations needed
184 recommendations: list[AgentRecommendation] = []
185 combined = stdout + stderr
187 for pattern_config in cls.PATTERNS:
188 pattern = pattern_config["pattern"]
189 matches = (
190 re.findall( # REGEX OK: error pattern matching from PATTERNS config
191 pattern,
192 combined,
193 re.IGNORECASE,
194 )
195 )
197 if matches and not cls._should_skip_coverage_recommendation(
198 pattern_config,
199 combined,
200 ):
201 recommendation = AgentRecommendation(
202 agent=pattern_config["agent"],
203 confidence=pattern_config["confidence"],
204 reason=pattern_config["reason"],
205 quick_fix_command=pattern_config["command"],
206 pattern_matched=pattern,
207 )
208 recommendations.append(recommendation)
210 return cls._deduplicate_recommendations(recommendations)
212 @classmethod
213 def format_recommendations(cls, recommendations: list[AgentRecommendation]) -> str:
214 """Format recommendations for display.
216 Args:
217 recommendations: List of agent recommendations
219 Returns:
220 Formatted string for user display
222 """
223 if not recommendations:
224 return ""
226 output = "\n🤖 **AI Agent Recommendations**:\n"
228 for i, rec in enumerate(recommendations, 1):
229 confidence_emoji = "🔥" if rec.confidence >= 0.85 else "✨"
230 output += (
231 f"\n{i}. {confidence_emoji} **{rec.agent.value}** "
232 f"(confidence: {rec.confidence:.0%})\n"
233 )
234 output += f" - **Reason**: {rec.reason}\n"
235 output += f" - **Quick Fix**: `{rec.quick_fix_command}`\n"
237 return output