crackerjack 0.33.0__py3-none-any.whl → 0.33.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +618 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,459 @@
1
+ """Intelligent version bump analysis based on code changes and commit patterns."""
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from rich.console import Console
10
+
11
+ from .changelog_automation import ChangelogEntry, ChangelogGenerator
12
+ from .git import GitService
13
+
14
+
15
+ class VersionBumpType(Enum):
16
+ """Semantic versioning bump types."""
17
+
18
+ MAJOR = "major" # Breaking changes (x.y.z -> x+1.0.0)
19
+ MINOR = "minor" # New features (x.y.z -> x.y+1.0)
20
+ PATCH = "patch" # Bug fixes (x.y.z -> x.y.z+1)
21
+
22
+
23
+ @dataclass
24
+ class VersionBumpRecommendation:
25
+ """Recommendation for version bump with reasoning."""
26
+
27
+ bump_type: VersionBumpType
28
+ confidence: float # 0.0 to 1.0
29
+ reasoning: list[str]
30
+ current_version: str
31
+ recommended_version: str
32
+ breaking_changes: list[str]
33
+ new_features: list[str]
34
+ bug_fixes: list[str]
35
+ commit_analysis: dict[str, Any]
36
+
37
+
38
+ class BreakingChangeAnalyzer:
39
+ """Analyzes commits for breaking changes that require MAJOR version bump."""
40
+
41
+ def __init__(self) -> None:
42
+ # Patterns that indicate breaking changes
43
+ self.breaking_patterns = [
44
+ re.compile(
45
+ r"BREAKING\s*CHANGE[:\s]", re.IGNORECASE
46
+ ), # REGEX OK: breaking change detection
47
+ re.compile(
48
+ r"^[^:\n]*!:", re.MULTILINE
49
+ ), # REGEX OK: conventional commit breaking marker
50
+ re.compile(
51
+ r"\bremove\s+\w+\s+(api|function|method|class)", re.IGNORECASE
52
+ ), # REGEX OK: API removal detection
53
+ re.compile(
54
+ r"\bdelete\s+\w+\s+(api|endpoint|interface)", re.IGNORECASE
55
+ ), # REGEX OK: API deletion detection
56
+ re.compile(
57
+ r"\bchange\s+\w+\s+(signature|interface|api)", re.IGNORECASE
58
+ ), # REGEX OK: API signature change
59
+ ]
60
+
61
+ def analyze(self, entries: list[ChangelogEntry]) -> tuple[bool, list[str], float]:
62
+ """
63
+ Analyze changelog entries for breaking changes.
64
+
65
+ Returns:
66
+ (has_breaking_changes, breaking_change_descriptions, confidence)
67
+ """
68
+ breaking_changes: list[str] = []
69
+
70
+ for entry in entries:
71
+ # Check if entry is already marked as breaking
72
+ if entry.breaking_change:
73
+ breaking_changes.append(entry.description)
74
+ continue
75
+
76
+ # Check description against breaking change patterns
77
+ for pattern in self.breaking_patterns:
78
+ if pattern.search(entry.description):
79
+ breaking_changes.append(entry.description)
80
+ break
81
+
82
+ has_breaking = len(breaking_changes) > 0
83
+ confidence = 0.9 if has_breaking else 0.0
84
+
85
+ return has_breaking, breaking_changes, confidence
86
+
87
+
88
+ class FeatureAnalyzer:
89
+ """Analyzes commits for new features that require MINOR version bump."""
90
+
91
+ def __init__(self) -> None:
92
+ # Patterns that indicate new features
93
+ self.feature_patterns = [
94
+ re.compile(
95
+ r"^feat[(\[]", re.IGNORECASE
96
+ ), # REGEX OK: conventional commit feat
97
+ re.compile(
98
+ r"\badd\s+(new\s+)?\w+", re.IGNORECASE
99
+ ), # REGEX OK: addition detection
100
+ re.compile(
101
+ r"\bimplement\s+\w+", re.IGNORECASE
102
+ ), # REGEX OK: implementation detection
103
+ re.compile(
104
+ r"\bintroduce\s+\w+", re.IGNORECASE
105
+ ), # REGEX OK: introduction detection
106
+ re.compile(
107
+ r"\bcreate\s+(new\s+)?\w+", re.IGNORECASE
108
+ ), # REGEX OK: creation detection
109
+ ]
110
+
111
+ def analyze(self, entries: list[ChangelogEntry]) -> tuple[bool, list[str], float]:
112
+ """
113
+ Analyze changelog entries for new features.
114
+
115
+ Returns:
116
+ (has_new_features, feature_descriptions, confidence)
117
+ """
118
+ new_features: list[str] = []
119
+
120
+ for entry in entries:
121
+ # Check entry type
122
+ if entry.type in ("Added", "feat"):
123
+ new_features.append(entry.description)
124
+ continue
125
+
126
+ # Check description against feature patterns
127
+ for pattern in self.feature_patterns:
128
+ if pattern.search(entry.description):
129
+ new_features.append(entry.description)
130
+ break
131
+
132
+ has_features = len(new_features) > 0
133
+ confidence = 0.8 if has_features else 0.0
134
+
135
+ return has_features, new_features, confidence
136
+
137
+
138
+ class ConventionalCommitAnalyzer:
139
+ """Analyzes conventional commit messages for semantic versioning hints."""
140
+
141
+ def __init__(self) -> None:
142
+ # Commit type mappings to version bump types
143
+ self.commit_type_mappings = {
144
+ # MAJOR bump triggers
145
+ "breaking": VersionBumpType.MAJOR,
146
+ # MINOR bump triggers
147
+ "feat": VersionBumpType.MINOR,
148
+ "feature": VersionBumpType.MINOR,
149
+ # PATCH bump triggers
150
+ "fix": VersionBumpType.PATCH,
151
+ "bugfix": VersionBumpType.PATCH,
152
+ "patch": VersionBumpType.PATCH,
153
+ "hotfix": VersionBumpType.PATCH,
154
+ # No version bump (could be argued either way)
155
+ "docs": None,
156
+ "style": None,
157
+ "refactor": None,
158
+ "test": None,
159
+ "chore": None,
160
+ "build": None,
161
+ "ci": None,
162
+ }
163
+
164
+ def analyze(self, entries: list[ChangelogEntry]) -> dict[str, Any]:
165
+ """
166
+ Analyze conventional commit patterns in changelog entries.
167
+
168
+ Returns:
169
+ Analysis results including type counts and recommendations
170
+ """
171
+ type_counts: dict[str, int] = {}
172
+ recommended_bumps: list[VersionBumpType] = []
173
+
174
+ for entry in entries:
175
+ # Parse entry type and increment count
176
+ entry_type = entry.type.lower()
177
+ type_counts[entry_type] = type_counts.get(entry_type, 0) + 1
178
+
179
+ # Determine recommended bump type
180
+ if entry.breaking_change:
181
+ recommended_bumps.append(VersionBumpType.MAJOR)
182
+ elif entry_type in self.commit_type_mappings:
183
+ bump_type = self.commit_type_mappings[entry_type]
184
+ if bump_type:
185
+ recommended_bumps.append(bump_type)
186
+
187
+ return {
188
+ "type_counts": type_counts,
189
+ "recommended_bumps": recommended_bumps,
190
+ "total_entries": len(entries),
191
+ }
192
+
193
+
194
+ class VersionAnalyzer:
195
+ """Main service for analyzing changes and recommending version bumps."""
196
+
197
+ def __init__(self, console: Console, git_service: GitService) -> None:
198
+ self.console = console
199
+ self.git = git_service
200
+
201
+ # Initialize specialized analyzers
202
+ self.breaking_analyzer = BreakingChangeAnalyzer()
203
+ self.feature_analyzer = FeatureAnalyzer()
204
+ self.commit_analyzer = ConventionalCommitAnalyzer()
205
+
206
+ # Initialize changelog generator for getting entries
207
+ self.changelog_generator = ChangelogGenerator(console, git_service)
208
+
209
+ def _get_current_version(self) -> str | None:
210
+ """Get current version from pyproject.toml."""
211
+ pyproject_path = Path("pyproject.toml")
212
+ if not pyproject_path.exists():
213
+ return None
214
+
215
+ try:
216
+ from tomllib import loads
217
+
218
+ content = pyproject_path.read_text(encoding="utf-8")
219
+ data = loads(content)
220
+ version: str | None = data.get("project", {}).get("version")
221
+ return version
222
+ except Exception:
223
+ return None
224
+
225
+ def _calculate_next_version(self, current: str, bump_type: VersionBumpType) -> str:
226
+ """Calculate next version based on current version and bump type."""
227
+ try:
228
+ parts = current.split(".")
229
+ if len(parts) != 3:
230
+ msg = f"Invalid version format: {current}"
231
+ raise ValueError(msg)
232
+
233
+ major, minor, patch = map(int, parts)
234
+
235
+ if bump_type == VersionBumpType.MAJOR:
236
+ return f"{major + 1}.0.0"
237
+ elif bump_type == VersionBumpType.MINOR:
238
+ return f"{major}.{minor + 1}.0"
239
+ elif bump_type == VersionBumpType.PATCH:
240
+ return f"{major}.{minor}.{patch + 1}"
241
+ else:
242
+ # All enum cases are covered above
243
+ from typing import assert_never
244
+
245
+ assert_never(bump_type)
246
+
247
+ except Exception as e:
248
+ self.console.print(f"[red]❌[/red] Error calculating version: {e}")
249
+ raise
250
+
251
+ async def recommend_version_bump(
252
+ self, since_version: str | None = None
253
+ ) -> VersionBumpRecommendation:
254
+ """
255
+ Analyze changes since last version and recommend version bump.
256
+
257
+ Args:
258
+ since_version: Version/tag to analyze changes since (default: latest tag)
259
+
260
+ Returns:
261
+ VersionBumpRecommendation with analysis and recommendation
262
+ """
263
+ current_version = self._get_current_version()
264
+ if not current_version:
265
+ msg = "Could not determine current version from pyproject.toml"
266
+ raise ValueError(msg)
267
+
268
+ all_entries = self._collect_changelog_entries(since_version)
269
+
270
+ if not all_entries:
271
+ return self._create_no_changes_recommendation(current_version)
272
+
273
+ return self._analyze_entries_and_recommend(current_version, all_entries)
274
+
275
+ def _collect_changelog_entries(
276
+ self, since_version: str | None
277
+ ) -> list[ChangelogEntry]:
278
+ """Collect and flatten changelog entries for analysis."""
279
+ entries_by_type = self.changelog_generator.generate_changelog_entries(
280
+ since_version
281
+ )
282
+ all_entries: list[ChangelogEntry] = []
283
+ for entries in entries_by_type.values():
284
+ all_entries.extend(entries)
285
+ return all_entries
286
+
287
+ def _create_no_changes_recommendation(
288
+ self, current_version: str
289
+ ) -> VersionBumpRecommendation:
290
+ """Create recommendation when no changes are detected."""
291
+ return VersionBumpRecommendation(
292
+ bump_type=VersionBumpType.PATCH,
293
+ confidence=1.0,
294
+ reasoning=["No significant changes detected - patch bump recommended"],
295
+ current_version=current_version,
296
+ recommended_version=self._calculate_next_version(
297
+ current_version, VersionBumpType.PATCH
298
+ ),
299
+ breaking_changes=[],
300
+ new_features=[],
301
+ bug_fixes=[],
302
+ commit_analysis={
303
+ "type_counts": {},
304
+ "recommended_bumps": [],
305
+ "total_entries": 0,
306
+ },
307
+ )
308
+
309
+ def _analyze_entries_and_recommend(
310
+ self, current_version: str, all_entries: list[ChangelogEntry]
311
+ ) -> VersionBumpRecommendation:
312
+ """Analyze entries and create version bump recommendation."""
313
+ # Run specialized analyses
314
+ has_breaking, breaking_changes, breaking_confidence = (
315
+ self.breaking_analyzer.analyze(all_entries)
316
+ )
317
+ has_features, new_features, feature_confidence = self.feature_analyzer.analyze(
318
+ all_entries
319
+ )
320
+ commit_analysis = self.commit_analyzer.analyze(all_entries)
321
+
322
+ bug_fixes = [
323
+ entry.description
324
+ for entry in all_entries
325
+ if entry.type.lower() in ("fixed", "fix", "bugfix", "patch")
326
+ ]
327
+
328
+ bump_type, confidence, reasoning = self._determine_bump_type(
329
+ has_breaking,
330
+ breaking_changes,
331
+ breaking_confidence,
332
+ has_features,
333
+ new_features,
334
+ feature_confidence,
335
+ bug_fixes,
336
+ all_entries,
337
+ )
338
+
339
+ recommended_version = self._calculate_next_version(current_version, bump_type)
340
+
341
+ return VersionBumpRecommendation(
342
+ bump_type=bump_type,
343
+ confidence=confidence,
344
+ reasoning=reasoning,
345
+ current_version=current_version,
346
+ recommended_version=recommended_version,
347
+ breaking_changes=breaking_changes,
348
+ new_features=new_features,
349
+ bug_fixes=bug_fixes,
350
+ commit_analysis=commit_analysis,
351
+ )
352
+
353
+ def _determine_bump_type(
354
+ self,
355
+ has_breaking: bool,
356
+ breaking_changes: list[str],
357
+ breaking_confidence: float,
358
+ has_features: bool,
359
+ new_features: list[str],
360
+ feature_confidence: float,
361
+ bug_fixes: list[str],
362
+ all_entries: list[ChangelogEntry],
363
+ ) -> tuple[VersionBumpType, float, list[str]]:
364
+ """Determine the appropriate version bump type and reasoning."""
365
+ if has_breaking:
366
+ return (
367
+ VersionBumpType.MAJOR,
368
+ breaking_confidence,
369
+ [
370
+ f"Breaking changes detected ({len(breaking_changes)} found)",
371
+ "MAJOR version bump required to maintain semantic versioning",
372
+ ],
373
+ )
374
+ elif has_features:
375
+ return (
376
+ VersionBumpType.MINOR,
377
+ feature_confidence,
378
+ [
379
+ f"New features detected ({len(new_features)} found)",
380
+ "MINOR version bump recommended for backward-compatible functionality",
381
+ ],
382
+ )
383
+ elif bug_fixes:
384
+ return (
385
+ VersionBumpType.PATCH,
386
+ 0.9,
387
+ [
388
+ f"Bug fixes detected ({len(bug_fixes)} found)",
389
+ "PATCH version bump recommended for backward-compatible fixes",
390
+ ],
391
+ )
392
+ return (
393
+ VersionBumpType.PATCH,
394
+ 0.5,
395
+ [
396
+ f"Changes detected ({len(all_entries)} commits) with unclear impact",
397
+ "PATCH version bump recommended as conservative choice",
398
+ ],
399
+ )
400
+
401
+ def display_recommendation(self, recommendation: VersionBumpRecommendation) -> None:
402
+ """Display version bump recommendation in a user-friendly format."""
403
+ self._display_summary(recommendation)
404
+ self._display_reasoning(recommendation)
405
+ self._display_changes(recommendation)
406
+ self._display_commit_analysis(recommendation)
407
+
408
+ def _display_summary(self, recommendation: VersionBumpRecommendation) -> None:
409
+ """Display the main version bump summary."""
410
+ self.console.print("\n[cyan]📊 Version Bump Analysis[/cyan]")
411
+ self.console.print(
412
+ f"Current version: [bold]{recommendation.current_version}[/bold]"
413
+ )
414
+ self.console.print(
415
+ f"Recommended version: [bold green]{recommendation.recommended_version}[/bold green]"
416
+ )
417
+ self.console.print(
418
+ f"Bump type: [bold]{recommendation.bump_type.value.upper()}[/bold]"
419
+ )
420
+ self.console.print(f"Confidence: [bold]{recommendation.confidence:.0%}[/bold]")
421
+
422
+ def _display_reasoning(self, recommendation: VersionBumpRecommendation) -> None:
423
+ """Display the reasoning behind the recommendation."""
424
+ self.console.print("\n[yellow]💡 Reasoning:[/yellow]")
425
+ for reason in recommendation.reasoning:
426
+ self.console.print(f" • {reason}")
427
+
428
+ def _display_changes(self, recommendation: VersionBumpRecommendation) -> None:
429
+ """Display breaking changes, new features, and bug fixes."""
430
+ self._display_change_list(
431
+ recommendation.breaking_changes, "[red]⚠️ Breaking Changes", "red"
432
+ )
433
+ self._display_change_list(
434
+ recommendation.new_features, "[green]✨ New Features", "green"
435
+ )
436
+ self._display_change_list(
437
+ recommendation.bug_fixes, "[blue]🔧 Bug Fixes", "blue"
438
+ )
439
+
440
+ def _display_change_list(self, changes: list[str], title: str, color: str) -> None:
441
+ """Display a list[t.Any] of changes with truncation."""
442
+ if changes:
443
+ self.console.print(f"\n{title} ({len(changes)}):[/{color}]")
444
+ for change in changes[:3]:
445
+ self.console.print(f" • {change}")
446
+ if len(changes) > 3:
447
+ self.console.print(f" • ... and {len(changes) - 3} more")
448
+
449
+ def _display_commit_analysis(
450
+ self, recommendation: VersionBumpRecommendation
451
+ ) -> None:
452
+ """Display commit analysis summary."""
453
+ analysis = recommendation.commit_analysis
454
+ if analysis.get("type_counts"):
455
+ self.console.print("\n[dim]📈 Commit Analysis:[/dim]")
456
+ total = analysis["total_entries"]
457
+ for commit_type, count in sorted(analysis["type_counts"].items()):
458
+ percentage = (count / total * 100) if total > 0 else 0
459
+ self.console.print(f" {commit_type}: {count} ({percentage:.0f}%)")
@@ -133,7 +133,7 @@ class VersionChecker:
133
133
  async with session.get(url) as response:
134
134
  response.raise_for_status()
135
135
  data = await response.json()
136
- return data.get("info", {}).get("version")
136
+ return data.get("info", {}).get("version") # type: ignore[no-any-return]
137
137
 
138
138
  except Exception:
139
139
  return None