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

1"""AI agent recommendation system for crackerjack failures.""" 

2 

3import re 

4import typing as t 

5from dataclasses import dataclass 

6from enum import StrEnum 

7 

8 

9class AgentType(StrEnum): 

10 """Available crackerjack AI agents.""" 

11 

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" 

21 

22 

23@dataclass 

24class AgentRecommendation: 

25 """Recommendation for using a specific AI agent.""" 

26 

27 agent: AgentType 

28 confidence: float # 0.0-1.0 

29 reason: str 

30 quick_fix_command: str 

31 pattern_matched: str 

32 

33 

34class AgentAnalyzer: 

35 """Analyze crackerjack failures and recommend AI agents.""" 

36 

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 ] 

133 

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 

143 

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) 

149 

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 

160 

161 return sorted(unique.values(), key=lambda x: x.confidence, reverse=True)[:3] 

162 

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. 

171 

172 Args: 

173 stdout: Standard output from crackerjack 

174 stderr: Standard error from crackerjack 

175 exit_code: Process exit code 

176 

177 Returns: 

178 List of agent recommendations sorted by confidence (highest first) 

179 

180 """ 

181 if exit_code == 0: 

182 return [] # No failures, no recommendations needed 

183 

184 recommendations: list[AgentRecommendation] = [] 

185 combined = stdout + stderr 

186 

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 ) 

196 

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) 

209 

210 return cls._deduplicate_recommendations(recommendations) 

211 

212 @classmethod 

213 def format_recommendations(cls, recommendations: list[AgentRecommendation]) -> str: 

214 """Format recommendations for display. 

215 

216 Args: 

217 recommendations: List of agent recommendations 

218 

219 Returns: 

220 Formatted string for user display 

221 

222 """ 

223 if not recommendations: 

224 return "" 

225 

226 output = "\n🤖 **AI Agent Recommendations**:\n" 

227 

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" 

236 

237 return output