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

2

3 import re

4 import typing as t

5 from dataclasses import dataclass

6 from enum import StrEnum

7

8

9 class 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

24 class 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

34 class 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