crackerjack 0.37.9__py3-none-any.whl → 0.45.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.
Files changed (425) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +30 -1
  3. crackerjack/__main__.py +342 -1263
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +27 -5
  6. crackerjack/adapters/_output_paths.py +167 -0
  7. crackerjack/adapters/_qa_adapter_base.py +309 -0
  8. crackerjack/adapters/_tool_adapter_base.py +706 -0
  9. crackerjack/adapters/ai/README.md +65 -0
  10. crackerjack/adapters/ai/__init__.py +5 -0
  11. crackerjack/adapters/ai/claude.py +853 -0
  12. crackerjack/adapters/complexity/README.md +53 -0
  13. crackerjack/adapters/complexity/__init__.py +10 -0
  14. crackerjack/adapters/complexity/complexipy.py +641 -0
  15. crackerjack/adapters/dependency/__init__.py +22 -0
  16. crackerjack/adapters/dependency/pip_audit.py +418 -0
  17. crackerjack/adapters/format/README.md +72 -0
  18. crackerjack/adapters/format/__init__.py +11 -0
  19. crackerjack/adapters/format/mdformat.py +313 -0
  20. crackerjack/adapters/format/ruff.py +516 -0
  21. crackerjack/adapters/lint/README.md +47 -0
  22. crackerjack/adapters/lint/__init__.py +11 -0
  23. crackerjack/adapters/lint/codespell.py +273 -0
  24. crackerjack/adapters/lsp/README.md +49 -0
  25. crackerjack/adapters/lsp/__init__.py +27 -0
  26. crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
  27. crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
  28. crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
  29. crackerjack/adapters/refactor/README.md +59 -0
  30. crackerjack/adapters/refactor/__init__.py +12 -0
  31. crackerjack/adapters/refactor/creosote.py +318 -0
  32. crackerjack/adapters/refactor/refurb.py +406 -0
  33. crackerjack/adapters/refactor/skylos.py +494 -0
  34. crackerjack/adapters/sast/README.md +132 -0
  35. crackerjack/adapters/sast/__init__.py +32 -0
  36. crackerjack/adapters/sast/_base.py +201 -0
  37. crackerjack/adapters/sast/bandit.py +423 -0
  38. crackerjack/adapters/sast/pyscn.py +405 -0
  39. crackerjack/adapters/sast/semgrep.py +241 -0
  40. crackerjack/adapters/security/README.md +111 -0
  41. crackerjack/adapters/security/__init__.py +17 -0
  42. crackerjack/adapters/security/gitleaks.py +339 -0
  43. crackerjack/adapters/type/README.md +52 -0
  44. crackerjack/adapters/type/__init__.py +12 -0
  45. crackerjack/adapters/type/pyrefly.py +402 -0
  46. crackerjack/adapters/type/ty.py +402 -0
  47. crackerjack/adapters/type/zuban.py +522 -0
  48. crackerjack/adapters/utility/README.md +51 -0
  49. crackerjack/adapters/utility/__init__.py +10 -0
  50. crackerjack/adapters/utility/checks.py +884 -0
  51. crackerjack/agents/README.md +264 -0
  52. crackerjack/agents/__init__.py +40 -12
  53. crackerjack/agents/base.py +1 -0
  54. crackerjack/agents/claude_code_bridge.py +641 -0
  55. crackerjack/agents/coordinator.py +49 -53
  56. crackerjack/agents/dry_agent.py +187 -3
  57. crackerjack/agents/enhanced_coordinator.py +279 -0
  58. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  59. crackerjack/agents/error_middleware.py +53 -0
  60. crackerjack/agents/formatting_agent.py +6 -8
  61. crackerjack/agents/helpers/__init__.py +9 -0
  62. crackerjack/agents/helpers/performance/__init__.py +22 -0
  63. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  64. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  65. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  66. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  67. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  68. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  69. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  70. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  71. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  72. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  73. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  74. crackerjack/agents/performance_agent.py +121 -1152
  75. crackerjack/agents/refactoring_agent.py +156 -655
  76. crackerjack/agents/semantic_agent.py +479 -0
  77. crackerjack/agents/semantic_helpers.py +356 -0
  78. crackerjack/agents/test_creation_agent.py +19 -1605
  79. crackerjack/api.py +5 -7
  80. crackerjack/cli/README.md +394 -0
  81. crackerjack/cli/__init__.py +1 -1
  82. crackerjack/cli/cache_handlers.py +23 -18
  83. crackerjack/cli/cache_handlers_enhanced.py +1 -4
  84. crackerjack/cli/facade.py +70 -8
  85. crackerjack/cli/formatting.py +13 -0
  86. crackerjack/cli/handlers/__init__.py +85 -0
  87. crackerjack/cli/handlers/advanced.py +103 -0
  88. crackerjack/cli/handlers/ai_features.py +62 -0
  89. crackerjack/cli/handlers/analytics.py +479 -0
  90. crackerjack/cli/handlers/changelog.py +271 -0
  91. crackerjack/cli/handlers/config_handlers.py +16 -0
  92. crackerjack/cli/handlers/coverage.py +84 -0
  93. crackerjack/cli/handlers/documentation.py +280 -0
  94. crackerjack/cli/handlers/main_handlers.py +497 -0
  95. crackerjack/cli/handlers/monitoring.py +371 -0
  96. crackerjack/cli/handlers.py +249 -49
  97. crackerjack/cli/interactive.py +8 -5
  98. crackerjack/cli/options.py +203 -110
  99. crackerjack/cli/semantic_handlers.py +292 -0
  100. crackerjack/cli/version.py +19 -0
  101. crackerjack/code_cleaner.py +60 -24
  102. crackerjack/config/README.md +472 -0
  103. crackerjack/config/__init__.py +256 -0
  104. crackerjack/config/global_lock_config.py +191 -54
  105. crackerjack/config/hooks.py +188 -16
  106. crackerjack/config/loader.py +239 -0
  107. crackerjack/config/settings.py +141 -0
  108. crackerjack/config/tool_commands.py +331 -0
  109. crackerjack/core/README.md +393 -0
  110. crackerjack/core/async_workflow_orchestrator.py +79 -53
  111. crackerjack/core/autofix_coordinator.py +22 -9
  112. crackerjack/core/container.py +10 -9
  113. crackerjack/core/enhanced_container.py +9 -9
  114. crackerjack/core/performance.py +1 -1
  115. crackerjack/core/performance_monitor.py +5 -3
  116. crackerjack/core/phase_coordinator.py +1018 -634
  117. crackerjack/core/proactive_workflow.py +3 -3
  118. crackerjack/core/retry.py +275 -0
  119. crackerjack/core/service_watchdog.py +167 -23
  120. crackerjack/core/session_coordinator.py +187 -382
  121. crackerjack/core/timeout_manager.py +161 -44
  122. crackerjack/core/workflow/__init__.py +21 -0
  123. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  124. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  125. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  126. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  127. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  128. crackerjack/core/workflow_orchestrator.py +1247 -953
  129. crackerjack/data/README.md +11 -0
  130. crackerjack/data/__init__.py +8 -0
  131. crackerjack/data/models.py +79 -0
  132. crackerjack/data/repository.py +210 -0
  133. crackerjack/decorators/README.md +180 -0
  134. crackerjack/decorators/__init__.py +35 -0
  135. crackerjack/decorators/error_handling.py +649 -0
  136. crackerjack/decorators/error_handling_decorators.py +334 -0
  137. crackerjack/decorators/helpers.py +58 -0
  138. crackerjack/decorators/patterns.py +281 -0
  139. crackerjack/decorators/utils.py +58 -0
  140. crackerjack/docs/README.md +11 -0
  141. crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
  142. crackerjack/documentation/README.md +11 -0
  143. crackerjack/documentation/ai_templates.py +1 -1
  144. crackerjack/documentation/dual_output_generator.py +11 -9
  145. crackerjack/documentation/reference_generator.py +104 -59
  146. crackerjack/dynamic_config.py +52 -61
  147. crackerjack/errors.py +1 -1
  148. crackerjack/events/README.md +11 -0
  149. crackerjack/events/__init__.py +16 -0
  150. crackerjack/events/telemetry.py +175 -0
  151. crackerjack/events/workflow_bus.py +346 -0
  152. crackerjack/exceptions/README.md +301 -0
  153. crackerjack/exceptions/__init__.py +5 -0
  154. crackerjack/exceptions/config.py +4 -0
  155. crackerjack/exceptions/tool_execution_error.py +245 -0
  156. crackerjack/executors/README.md +591 -0
  157. crackerjack/executors/__init__.py +2 -0
  158. crackerjack/executors/async_hook_executor.py +539 -77
  159. crackerjack/executors/cached_hook_executor.py +3 -3
  160. crackerjack/executors/hook_executor.py +967 -102
  161. crackerjack/executors/hook_lock_manager.py +31 -22
  162. crackerjack/executors/individual_hook_executor.py +66 -32
  163. crackerjack/executors/lsp_aware_hook_executor.py +136 -57
  164. crackerjack/executors/progress_hook_executor.py +282 -0
  165. crackerjack/executors/tool_proxy.py +23 -7
  166. crackerjack/hooks/README.md +485 -0
  167. crackerjack/hooks/lsp_hook.py +8 -9
  168. crackerjack/intelligence/README.md +557 -0
  169. crackerjack/interactive.py +37 -10
  170. crackerjack/managers/README.md +369 -0
  171. crackerjack/managers/async_hook_manager.py +41 -57
  172. crackerjack/managers/hook_manager.py +449 -79
  173. crackerjack/managers/publish_manager.py +81 -36
  174. crackerjack/managers/test_command_builder.py +290 -12
  175. crackerjack/managers/test_executor.py +93 -8
  176. crackerjack/managers/test_manager.py +1082 -75
  177. crackerjack/managers/test_progress.py +118 -26
  178. crackerjack/mcp/README.md +374 -0
  179. crackerjack/mcp/cache.py +25 -2
  180. crackerjack/mcp/client_runner.py +35 -18
  181. crackerjack/mcp/context.py +9 -9
  182. crackerjack/mcp/dashboard.py +24 -8
  183. crackerjack/mcp/enhanced_progress_monitor.py +34 -23
  184. crackerjack/mcp/file_monitor.py +27 -6
  185. crackerjack/mcp/progress_components.py +45 -34
  186. crackerjack/mcp/progress_monitor.py +6 -9
  187. crackerjack/mcp/rate_limiter.py +11 -7
  188. crackerjack/mcp/server.py +2 -0
  189. crackerjack/mcp/server_core.py +187 -55
  190. crackerjack/mcp/service_watchdog.py +12 -9
  191. crackerjack/mcp/task_manager.py +2 -2
  192. crackerjack/mcp/tools/README.md +27 -0
  193. crackerjack/mcp/tools/__init__.py +2 -0
  194. crackerjack/mcp/tools/core_tools.py +75 -52
  195. crackerjack/mcp/tools/execution_tools.py +87 -31
  196. crackerjack/mcp/tools/intelligence_tools.py +2 -2
  197. crackerjack/mcp/tools/proactive_tools.py +1 -1
  198. crackerjack/mcp/tools/semantic_tools.py +584 -0
  199. crackerjack/mcp/tools/utility_tools.py +180 -132
  200. crackerjack/mcp/tools/workflow_executor.py +87 -46
  201. crackerjack/mcp/websocket/README.md +31 -0
  202. crackerjack/mcp/websocket/app.py +11 -1
  203. crackerjack/mcp/websocket/event_bridge.py +188 -0
  204. crackerjack/mcp/websocket/jobs.py +27 -4
  205. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  206. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  207. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  208. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  209. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  210. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  211. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  212. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  213. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  214. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  215. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  216. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  217. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  218. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  219. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  220. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  221. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  222. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  223. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  224. crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
  225. crackerjack/mcp/websocket/server.py +1 -3
  226. crackerjack/mcp/websocket/websocket_handler.py +107 -6
  227. crackerjack/models/README.md +308 -0
  228. crackerjack/models/__init__.py +10 -1
  229. crackerjack/models/config.py +639 -22
  230. crackerjack/models/config_adapter.py +6 -6
  231. crackerjack/models/protocols.py +1167 -23
  232. crackerjack/models/pydantic_models.py +320 -0
  233. crackerjack/models/qa_config.py +145 -0
  234. crackerjack/models/qa_results.py +134 -0
  235. crackerjack/models/results.py +35 -0
  236. crackerjack/models/semantic_models.py +258 -0
  237. crackerjack/models/task.py +19 -3
  238. crackerjack/models/test_models.py +60 -0
  239. crackerjack/monitoring/README.md +11 -0
  240. crackerjack/monitoring/ai_agent_watchdog.py +5 -4
  241. crackerjack/monitoring/metrics_collector.py +4 -3
  242. crackerjack/monitoring/regression_prevention.py +4 -3
  243. crackerjack/monitoring/websocket_server.py +4 -241
  244. crackerjack/orchestration/README.md +340 -0
  245. crackerjack/orchestration/__init__.py +43 -0
  246. crackerjack/orchestration/advanced_orchestrator.py +20 -67
  247. crackerjack/orchestration/cache/README.md +312 -0
  248. crackerjack/orchestration/cache/__init__.py +37 -0
  249. crackerjack/orchestration/cache/memory_cache.py +338 -0
  250. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  251. crackerjack/orchestration/config.py +297 -0
  252. crackerjack/orchestration/coverage_improvement.py +13 -6
  253. crackerjack/orchestration/execution_strategies.py +6 -6
  254. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  255. crackerjack/orchestration/strategies/README.md +401 -0
  256. crackerjack/orchestration/strategies/__init__.py +39 -0
  257. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  258. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  259. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  260. crackerjack/orchestration/test_progress_streamer.py +1 -1
  261. crackerjack/plugins/README.md +11 -0
  262. crackerjack/plugins/hooks.py +3 -2
  263. crackerjack/plugins/loader.py +3 -3
  264. crackerjack/plugins/managers.py +1 -1
  265. crackerjack/py313.py +191 -0
  266. crackerjack/security/README.md +11 -0
  267. crackerjack/services/README.md +374 -0
  268. crackerjack/services/__init__.py +8 -21
  269. crackerjack/services/ai/README.md +295 -0
  270. crackerjack/services/ai/__init__.py +7 -0
  271. crackerjack/services/ai/advanced_optimizer.py +878 -0
  272. crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
  273. crackerjack/services/ai/embeddings.py +444 -0
  274. crackerjack/services/ai/intelligent_commit.py +328 -0
  275. crackerjack/services/ai/predictive_analytics.py +510 -0
  276. crackerjack/services/api_extractor.py +5 -3
  277. crackerjack/services/bounded_status_operations.py +45 -5
  278. crackerjack/services/cache.py +249 -318
  279. crackerjack/services/changelog_automation.py +7 -3
  280. crackerjack/services/command_execution_service.py +305 -0
  281. crackerjack/services/config_integrity.py +83 -39
  282. crackerjack/services/config_merge.py +9 -6
  283. crackerjack/services/config_service.py +198 -0
  284. crackerjack/services/config_template.py +13 -26
  285. crackerjack/services/coverage_badge_service.py +6 -4
  286. crackerjack/services/coverage_ratchet.py +53 -27
  287. crackerjack/services/debug.py +18 -7
  288. crackerjack/services/dependency_analyzer.py +4 -4
  289. crackerjack/services/dependency_monitor.py +13 -13
  290. crackerjack/services/documentation_generator.py +4 -2
  291. crackerjack/services/documentation_service.py +62 -33
  292. crackerjack/services/enhanced_filesystem.py +81 -27
  293. crackerjack/services/enterprise_optimizer.py +1 -1
  294. crackerjack/services/error_pattern_analyzer.py +10 -10
  295. crackerjack/services/file_filter.py +221 -0
  296. crackerjack/services/file_hasher.py +5 -7
  297. crackerjack/services/file_io_service.py +361 -0
  298. crackerjack/services/file_modifier.py +615 -0
  299. crackerjack/services/filesystem.py +80 -109
  300. crackerjack/services/git.py +99 -5
  301. crackerjack/services/health_metrics.py +4 -6
  302. crackerjack/services/heatmap_generator.py +12 -3
  303. crackerjack/services/incremental_executor.py +380 -0
  304. crackerjack/services/initialization.py +101 -49
  305. crackerjack/services/log_manager.py +2 -2
  306. crackerjack/services/logging.py +120 -68
  307. crackerjack/services/lsp_client.py +12 -12
  308. crackerjack/services/memory_optimizer.py +27 -22
  309. crackerjack/services/monitoring/README.md +30 -0
  310. crackerjack/services/monitoring/__init__.py +9 -0
  311. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  312. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  313. crackerjack/services/monitoring/health_metrics.py +716 -0
  314. crackerjack/services/monitoring/metrics.py +587 -0
  315. crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
  316. crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
  317. crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
  318. crackerjack/services/parallel_executor.py +166 -55
  319. crackerjack/services/patterns/__init__.py +142 -0
  320. crackerjack/services/patterns/agents.py +107 -0
  321. crackerjack/services/patterns/code/__init__.py +15 -0
  322. crackerjack/services/patterns/code/detection.py +118 -0
  323. crackerjack/services/patterns/code/imports.py +107 -0
  324. crackerjack/services/patterns/code/paths.py +159 -0
  325. crackerjack/services/patterns/code/performance.py +119 -0
  326. crackerjack/services/patterns/code/replacement.py +36 -0
  327. crackerjack/services/patterns/core.py +212 -0
  328. crackerjack/services/patterns/documentation/__init__.py +14 -0
  329. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  330. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  331. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  332. crackerjack/services/patterns/formatting.py +226 -0
  333. crackerjack/services/patterns/operations.py +339 -0
  334. crackerjack/services/patterns/security/__init__.py +23 -0
  335. crackerjack/services/patterns/security/code_injection.py +122 -0
  336. crackerjack/services/patterns/security/credentials.py +190 -0
  337. crackerjack/services/patterns/security/path_traversal.py +221 -0
  338. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  339. crackerjack/services/patterns/templates.py +62 -0
  340. crackerjack/services/patterns/testing/__init__.py +18 -0
  341. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  342. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  343. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  344. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  345. crackerjack/services/patterns/tool_output/other.py +97 -0
  346. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  347. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  348. crackerjack/services/patterns/url_sanitization.py +114 -0
  349. crackerjack/services/patterns/utilities.py +42 -0
  350. crackerjack/services/patterns/utils.py +339 -0
  351. crackerjack/services/patterns/validation.py +46 -0
  352. crackerjack/services/patterns/versioning.py +62 -0
  353. crackerjack/services/predictive_analytics.py +21 -8
  354. crackerjack/services/profiler.py +280 -0
  355. crackerjack/services/quality/README.md +415 -0
  356. crackerjack/services/quality/__init__.py +11 -0
  357. crackerjack/services/quality/anomaly_detector.py +392 -0
  358. crackerjack/services/quality/pattern_cache.py +333 -0
  359. crackerjack/services/quality/pattern_detector.py +479 -0
  360. crackerjack/services/quality/qa_orchestrator.py +491 -0
  361. crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
  362. crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
  363. crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
  364. crackerjack/services/regex_patterns.py +58 -2987
  365. crackerjack/services/regex_utils.py +55 -29
  366. crackerjack/services/secure_status_formatter.py +42 -15
  367. crackerjack/services/secure_subprocess.py +35 -2
  368. crackerjack/services/security.py +16 -8
  369. crackerjack/services/server_manager.py +40 -51
  370. crackerjack/services/smart_scheduling.py +46 -6
  371. crackerjack/services/status_authentication.py +3 -3
  372. crackerjack/services/thread_safe_status_collector.py +1 -0
  373. crackerjack/services/tool_filter.py +368 -0
  374. crackerjack/services/tool_version_service.py +9 -5
  375. crackerjack/services/unified_config.py +43 -351
  376. crackerjack/services/vector_store.py +689 -0
  377. crackerjack/services/version_analyzer.py +6 -4
  378. crackerjack/services/version_checker.py +14 -8
  379. crackerjack/services/zuban_lsp_service.py +5 -4
  380. crackerjack/slash_commands/README.md +11 -0
  381. crackerjack/slash_commands/init.md +2 -12
  382. crackerjack/slash_commands/run.md +84 -50
  383. crackerjack/tools/README.md +11 -0
  384. crackerjack/tools/__init__.py +30 -0
  385. crackerjack/tools/_git_utils.py +105 -0
  386. crackerjack/tools/check_added_large_files.py +139 -0
  387. crackerjack/tools/check_ast.py +105 -0
  388. crackerjack/tools/check_json.py +103 -0
  389. crackerjack/tools/check_jsonschema.py +297 -0
  390. crackerjack/tools/check_toml.py +103 -0
  391. crackerjack/tools/check_yaml.py +110 -0
  392. crackerjack/tools/codespell_wrapper.py +72 -0
  393. crackerjack/tools/end_of_file_fixer.py +202 -0
  394. crackerjack/tools/format_json.py +128 -0
  395. crackerjack/tools/mdformat_wrapper.py +114 -0
  396. crackerjack/tools/trailing_whitespace.py +198 -0
  397. crackerjack/tools/validate_regex_patterns.py +7 -3
  398. crackerjack/ui/README.md +11 -0
  399. crackerjack/ui/dashboard_renderer.py +28 -0
  400. crackerjack/ui/templates/README.md +11 -0
  401. crackerjack/utils/console_utils.py +13 -0
  402. crackerjack/utils/dependency_guard.py +230 -0
  403. crackerjack/utils/retry_utils.py +275 -0
  404. crackerjack/workflows/README.md +590 -0
  405. crackerjack/workflows/__init__.py +46 -0
  406. crackerjack/workflows/actions.py +811 -0
  407. crackerjack/workflows/auto_fix.py +444 -0
  408. crackerjack/workflows/container_builder.py +499 -0
  409. crackerjack/workflows/definitions.py +443 -0
  410. crackerjack/workflows/engine.py +177 -0
  411. crackerjack/workflows/event_bridge.py +242 -0
  412. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
  413. crackerjack-0.45.2.dist-info/RECORD +478 -0
  414. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  415. crackerjack/managers/test_manager_backup.py +0 -1075
  416. crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
  417. crackerjack/mixins/__init__.py +0 -3
  418. crackerjack/mixins/error_handling.py +0 -145
  419. crackerjack/services/config.py +0 -358
  420. crackerjack/ui/server_panels.py +0 -125
  421. crackerjack-0.37.9.dist-info/RECORD +0 -231
  422. /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
  423. /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
  424. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
  425. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,909 @@
1
+ """Pattern detection for performance anti-patterns in Python code."""
2
+
3
+ import ast
4
+ import operator
5
+ import typing as t
6
+ from contextlib import suppress
7
+
8
+ from ....services.regex_patterns import SAFE_PATTERNS
9
+ from ...base import AgentContext
10
+
11
+
12
+ class PerformancePatternDetector:
13
+ """Detects performance anti-patterns in Python code using AST and pattern matching."""
14
+
15
+ def __init__(self, context: AgentContext) -> None:
16
+ """Initialize detector with agent context.
17
+
18
+ Args:
19
+ context: AgentContext for file operations and logging
20
+ """
21
+ self.context = context
22
+
23
+ def detect_performance_issues(
24
+ self,
25
+ content: str,
26
+ file_path: t.Any,
27
+ ) -> list[dict[str, t.Any]]:
28
+ """Detect all performance issues in content using pattern matching.
29
+
30
+ Args:
31
+ content: File content to analyze
32
+ file_path: Path to the file
33
+
34
+ Returns:
35
+ List of detected performance issues
36
+ """
37
+ issues: list[dict[str, t.Any]] = []
38
+
39
+ with suppress(SyntaxError):
40
+ tree = ast.parse(content)
41
+
42
+ nested_issues = self._detect_nested_loops_enhanced(tree)
43
+ issues.extend(nested_issues)
44
+
45
+ list_issues = self._detect_inefficient_list_ops_enhanced(content, tree)
46
+ issues.extend(list_issues)
47
+
48
+ repeated_issues = self._detect_repeated_operations_enhanced(content, tree)
49
+ issues.extend(repeated_issues)
50
+
51
+ string_issues = self._detect_string_inefficiencies_enhanced(content)
52
+ issues.extend(string_issues)
53
+
54
+ comprehension_issues = self._detect_list_comprehension_opportunities(tree)
55
+ issues.extend(comprehension_issues)
56
+
57
+ builtin_issues = self._detect_inefficient_builtin_usage(tree, content)
58
+ issues.extend(builtin_issues)
59
+
60
+ return issues
61
+
62
+ def _detect_nested_loops_enhanced(self, tree: ast.AST) -> list[dict[str, t.Any]]:
63
+ """Detect nested loops with complexity analysis.
64
+
65
+ Args:
66
+ tree: AST tree to analyze
67
+
68
+ Returns:
69
+ List of nested loop issues
70
+ """
71
+ analyzer = self._create_nested_loop_analyzer()
72
+ analyzer.visit(tree)
73
+ return self._build_nested_loop_issues(analyzer)
74
+
75
+ @staticmethod
76
+ def _create_nested_loop_analyzer() -> "NestedLoopAnalyzer":
77
+ """Create AST analyzer for nested loops.
78
+
79
+ Returns:
80
+ NestedLoopAnalyzer instance
81
+ """
82
+ return NestedLoopAnalyzer()
83
+
84
+ def _build_nested_loop_issues(
85
+ self, analyzer: "NestedLoopAnalyzer"
86
+ ) -> list[dict[str, t.Any]]:
87
+ """Build issue data from nested loop analysis.
88
+
89
+ Args:
90
+ analyzer: NestedLoopAnalyzer instance
91
+
92
+ Returns:
93
+ List of issues
94
+ """
95
+ if not analyzer.nested_loops:
96
+ return []
97
+
98
+ return [
99
+ {
100
+ "type": "nested_loops_enhanced",
101
+ "instances": analyzer.nested_loops,
102
+ "hotspots": analyzer.complexity_hotspots,
103
+ "total_count": len(analyzer.nested_loops),
104
+ "high_priority_count": self._count_high_priority_loops(
105
+ analyzer.nested_loops
106
+ ),
107
+ "suggestion": self._generate_nested_loop_suggestions(
108
+ analyzer.nested_loops
109
+ ),
110
+ }
111
+ ]
112
+
113
+ @staticmethod
114
+ def _count_high_priority_loops(nested_loops: list[dict[str, t.Any]]) -> int:
115
+ """Count high priority nested loops.
116
+
117
+ Args:
118
+ nested_loops: List of nested loop instances
119
+
120
+ Returns:
121
+ Count of high priority loops
122
+ """
123
+ return len([n for n in nested_loops if n["priority"] in ("high", "critical")])
124
+
125
+ @staticmethod
126
+ def _generate_nested_loop_suggestions(nested_loops: list[dict[str, t.Any]]) -> str:
127
+ """Generate optimization suggestions for nested loops.
128
+
129
+ Args:
130
+ nested_loops: List of nested loop instances
131
+
132
+ Returns:
133
+ Suggestion string
134
+ """
135
+ suggestions = []
136
+
137
+ critical_count = len(
138
+ [n for n in nested_loops if n.get("priority") == "critical"]
139
+ )
140
+ high_count = len([n for n in nested_loops if n.get("priority") == "high"])
141
+
142
+ if critical_count > 0:
143
+ suggestions.append(
144
+ f"CRITICAL: {critical_count} O(n⁴+) loops need immediate algorithmic redesign"
145
+ )
146
+ if high_count > 0:
147
+ suggestions.append(
148
+ f"HIGH: {high_count} O(n³) loops should use memoization/caching"
149
+ )
150
+
151
+ suggestions.extend(
152
+ [
153
+ "Consider: 1) Hash tables for lookups 2) List comprehensions 3) NumPy for numerical operations",
154
+ "Profile: Use timeit or cProfile to measure actual performance impact",
155
+ ]
156
+ )
157
+
158
+ return "; ".join(suggestions)
159
+
160
+ def _detect_inefficient_list_ops_enhanced(
161
+ self,
162
+ content: str,
163
+ tree: ast.AST,
164
+ ) -> list[dict[str, t.Any]]:
165
+ """Detect inefficient list operations in loops.
166
+
167
+ Args:
168
+ content: File content
169
+ tree: AST tree
170
+
171
+ Returns:
172
+ List of inefficient list operation issues
173
+ """
174
+ analyzer = self._create_enhanced_list_op_analyzer()
175
+ analyzer.visit(tree)
176
+
177
+ if not analyzer.list_ops:
178
+ return []
179
+
180
+ return self._build_list_ops_issues(analyzer)
181
+
182
+ @staticmethod
183
+ def _create_enhanced_list_op_analyzer() -> "ListOpAnalyzer":
184
+ """Create AST analyzer for list operations.
185
+
186
+ Returns:
187
+ ListOpAnalyzer instance
188
+ """
189
+ return ListOpAnalyzer()
190
+
191
+ def _build_list_ops_issues(
192
+ self, analyzer: "ListOpAnalyzer"
193
+ ) -> list[dict[str, t.Any]]:
194
+ """Build issue data from list operation analysis.
195
+
196
+ Args:
197
+ analyzer: ListOpAnalyzer instance
198
+
199
+ Returns:
200
+ List of issues
201
+ """
202
+ total_impact = sum(int(op["impact_factor"]) for op in analyzer.list_ops)
203
+ high_impact_ops = [
204
+ op for op in analyzer.list_ops if int(op["impact_factor"]) >= 10
205
+ ]
206
+
207
+ return [
208
+ {
209
+ "type": "inefficient_list_operations_enhanced",
210
+ "instances": analyzer.list_ops,
211
+ "total_impact": total_impact,
212
+ "high_impact_count": len(high_impact_ops),
213
+ "suggestion": self._generate_list_op_suggestions(analyzer.list_ops),
214
+ }
215
+ ]
216
+
217
+ @staticmethod
218
+ def _generate_list_op_suggestions(list_ops: list[dict[str, t.Any]]) -> str:
219
+ """Generate optimization suggestions for list operations.
220
+
221
+ Args:
222
+ list_ops: List of inefficient operations
223
+
224
+ Returns:
225
+ Suggestion string
226
+ """
227
+ suggestions = []
228
+
229
+ high_impact_count = len(
230
+ [op for op in list_ops if int(op["impact_factor"]) >= 10]
231
+ )
232
+ if high_impact_count > 0:
233
+ suggestions.append(
234
+ f"HIGH IMPACT: {high_impact_count} list[t.Any] operations in hot loops"
235
+ )
236
+
237
+ append_count = len([op for op in list_ops if op["optimization"] == "append"])
238
+ extend_count = len([op for op in list_ops if op["optimization"] == "extend"])
239
+
240
+ if append_count > 0:
241
+ suggestions.append(f"Replace {append_count} += [item] with .append(item)")
242
+ if extend_count > 0:
243
+ suggestions.append(
244
+ f"Replace {extend_count} += multiple_items with .extend()"
245
+ )
246
+
247
+ suggestions.append(
248
+ "Expected performance gains: 2-50x depending on loop context"
249
+ )
250
+
251
+ return "; ".join(suggestions)
252
+
253
+ def _detect_repeated_operations_enhanced(
254
+ self,
255
+ content: str,
256
+ tree: ast.AST,
257
+ ) -> list[dict[str, t.Any]]:
258
+ """Detect repeated expensive operations in loops.
259
+
260
+ Args:
261
+ content: File content
262
+ tree: AST tree
263
+
264
+ Returns:
265
+ List of repeated operation issues
266
+ """
267
+ lines = content.split("\n")
268
+ repeated_calls = self._find_expensive_operations_in_loops(lines)
269
+
270
+ return self._create_repeated_operations_issues(repeated_calls)
271
+
272
+ def _find_expensive_operations_in_loops(
273
+ self,
274
+ lines: list[str],
275
+ ) -> list[dict[str, t.Any]]:
276
+ """Find expensive operations inside loops.
277
+
278
+ Args:
279
+ lines: File lines
280
+
281
+ Returns:
282
+ List of repeated operation records
283
+ """
284
+ repeated_calls: list[dict[str, t.Any]] = []
285
+ expensive_patterns = self._get_expensive_operation_patterns()
286
+
287
+ for i, line in enumerate(lines):
288
+ stripped = line.strip()
289
+ if self._contains_expensive_operation(stripped, expensive_patterns):
290
+ if self._is_in_loop_context(lines, i):
291
+ repeated_calls.append(self._create_operation_record(i, stripped))
292
+
293
+ return repeated_calls
294
+
295
+ @staticmethod
296
+ def _get_expensive_operation_patterns() -> tuple[str, ...]:
297
+ """Get patterns for expensive operations.
298
+
299
+ Returns:
300
+ Tuple of pattern strings
301
+ """
302
+ return (
303
+ ".exists()",
304
+ ".read_text()",
305
+ ".glob(",
306
+ ".rglob(",
307
+ "Path(",
308
+ "len(",
309
+ ".get(",
310
+ )
311
+
312
+ @staticmethod
313
+ def _contains_expensive_operation(
314
+ line: str,
315
+ patterns: tuple[str, ...],
316
+ ) -> bool:
317
+ """Check if line contains expensive operations.
318
+
319
+ Args:
320
+ line: Code line
321
+ patterns: Patterns to match
322
+
323
+ Returns:
324
+ True if expensive operation found
325
+ """
326
+ return any(pattern in line for pattern in patterns)
327
+
328
+ @staticmethod
329
+ def _is_in_loop_context(lines: list[str], line_index: int) -> bool:
330
+ """Check if line is inside a loop.
331
+
332
+ Args:
333
+ lines: File lines
334
+ line_index: Index of line to check
335
+
336
+ Returns:
337
+ True if inside loop context
338
+ """
339
+ context_start = max(0, line_index - 5)
340
+ context_lines = lines[context_start : line_index + 1]
341
+
342
+ loop_keywords = ("for ", "while ")
343
+ return any(
344
+ any(keyword in ctx_line for keyword in loop_keywords)
345
+ for ctx_line in context_lines
346
+ )
347
+
348
+ @staticmethod
349
+ def _create_operation_record(
350
+ line_index: int,
351
+ content: str,
352
+ ) -> dict[str, t.Any]:
353
+ """Create record for operation.
354
+
355
+ Args:
356
+ line_index: Line number
357
+ content: Line content
358
+
359
+ Returns:
360
+ Operation record
361
+ """
362
+ return {
363
+ "line_number": line_index + 1,
364
+ "content": content,
365
+ "type": "expensive_operation_in_loop",
366
+ }
367
+
368
+ @staticmethod
369
+ def _create_repeated_operations_issues(
370
+ repeated_calls: list[dict[str, t.Any]],
371
+ ) -> list[dict[str, t.Any]]:
372
+ """Create issues from repeated operations.
373
+
374
+ Args:
375
+ repeated_calls: List of repeated operation records
376
+
377
+ Returns:
378
+ List of issues
379
+ """
380
+ if len(repeated_calls) >= 2:
381
+ return [
382
+ {
383
+ "type": "repeated_expensive_operations",
384
+ "instances": repeated_calls,
385
+ "suggestion": "Cache expensive operations outside loops",
386
+ },
387
+ ]
388
+ return []
389
+
390
+ def _detect_string_inefficiencies_enhanced(
391
+ self, content: str
392
+ ) -> list[dict[str, t.Any]]:
393
+ """Detect string building inefficiencies.
394
+
395
+ Args:
396
+ content: File content
397
+
398
+ Returns:
399
+ List of string inefficiency issues
400
+ """
401
+ issues: list[dict[str, t.Any]] = []
402
+ lines = content.split("\n")
403
+
404
+ string_concat_patterns = []
405
+ inefficient_joins = []
406
+ repeated_format_calls = []
407
+
408
+ for i, line in enumerate(lines):
409
+ stripped = line.strip()
410
+
411
+ if "+=" in stripped and any(quote in stripped for quote in ('"', "'")):
412
+ if self._is_in_loop_context_enhanced(lines, i):
413
+ context_info = self._analyze_string_context(lines, i)
414
+ string_concat_patterns.append(
415
+ {
416
+ "line_number": i + 1,
417
+ "content": stripped,
418
+ "context": context_info,
419
+ "estimated_impact": int(
420
+ context_info.get("impact_factor", "1")
421
+ ),
422
+ }
423
+ )
424
+
425
+ if ".join([])" in stripped:
426
+ inefficient_joins.append(
427
+ {
428
+ "line_number": i + 1,
429
+ "content": stripped,
430
+ "optimization": "Use empty string literal instead",
431
+ "performance_gain": "2x",
432
+ }
433
+ )
434
+
435
+ if any(pattern in stripped for pattern in ('f"', ".format(", "% ")):
436
+ if self._is_in_loop_context_enhanced(lines, i):
437
+ repeated_format_calls.append(
438
+ {
439
+ "line_number": i + 1,
440
+ "content": stripped,
441
+ "optimization": "Move formatting outside loop if static",
442
+ }
443
+ )
444
+
445
+ total_issues = (
446
+ len(string_concat_patterns)
447
+ + len(inefficient_joins)
448
+ + len(repeated_format_calls)
449
+ )
450
+
451
+ if total_issues > 0:
452
+ issues.append(
453
+ {
454
+ "type": "string_inefficiencies_enhanced",
455
+ "string_concat_patterns": string_concat_patterns,
456
+ "inefficient_joins": inefficient_joins,
457
+ "repeated_formatting": repeated_format_calls,
458
+ "total_count": total_issues,
459
+ "suggestion": self._generate_string_suggestions(
460
+ string_concat_patterns, inefficient_joins, repeated_format_calls
461
+ ),
462
+ }
463
+ )
464
+
465
+ return issues
466
+
467
+ def _analyze_string_context(
468
+ self, lines: list[str], line_idx: int
469
+ ) -> dict[str, t.Any]:
470
+ """Analyze string building context.
471
+
472
+ Args:
473
+ lines: File lines
474
+ line_idx: Line index
475
+
476
+ Returns:
477
+ Context information
478
+ """
479
+ context = self._create_default_string_context()
480
+ loop_context = self._find_loop_context_in_lines(lines, line_idx)
481
+
482
+ if loop_context:
483
+ context.update(loop_context)
484
+
485
+ return context
486
+
487
+ @staticmethod
488
+ def _create_default_string_context() -> dict[str, t.Any]:
489
+ """Create default string context.
490
+
491
+ Returns:
492
+ Default context dict
493
+ """
494
+ return {
495
+ "loop_type": "unknown",
496
+ "loop_depth": 1,
497
+ "impact_factor": "1",
498
+ }
499
+
500
+ def _find_loop_context_in_lines(
501
+ self, lines: list[str], line_idx: int
502
+ ) -> dict[str, t.Any] | None:
503
+ """Find loop context for a line.
504
+
505
+ Args:
506
+ lines: File lines
507
+ line_idx: Line index
508
+
509
+ Returns:
510
+ Loop context or None
511
+ """
512
+ for i in range(max(0, line_idx - 10), line_idx):
513
+ line = lines[i].strip()
514
+ loop_context = self._analyze_single_line_for_loop_context(line)
515
+ if loop_context:
516
+ return loop_context
517
+ return None
518
+
519
+ def _analyze_single_line_for_loop_context(
520
+ self, line: str
521
+ ) -> dict[str, t.Any] | None:
522
+ """Analyze a single line for loop context.
523
+
524
+ Args:
525
+ line: Code line
526
+
527
+ Returns:
528
+ Loop context or None
529
+ """
530
+ if "for " in line and " in " in line:
531
+ return self._analyze_for_loop_context(line)
532
+ elif "while " in line:
533
+ return self._analyze_while_loop_context()
534
+ return None
535
+
536
+ def _analyze_for_loop_context(self, line: str) -> dict[str, t.Any]:
537
+ """Analyze for loop for impact factor.
538
+
539
+ Args:
540
+ line: For loop line
541
+
542
+ Returns:
543
+ Context dict
544
+ """
545
+ context = {"loop_type": "for"}
546
+
547
+ if "range(" in line:
548
+ impact_factor = self._estimate_range_impact_factor(line)
549
+ context["impact_factor"] = str(impact_factor)
550
+ else:
551
+ context["impact_factor"] = "2"
552
+
553
+ return context
554
+
555
+ @staticmethod
556
+ def _analyze_while_loop_context() -> dict[str, t.Any]:
557
+ """Analyze while loop context.
558
+
559
+ Returns:
560
+ Context dict
561
+ """
562
+ return {
563
+ "loop_type": "while",
564
+ "impact_factor": "3",
565
+ }
566
+
567
+ def _estimate_range_impact_factor(self, line: str) -> int:
568
+ """Estimate impact factor for range size.
569
+
570
+ Args:
571
+ line: Code line
572
+
573
+ Returns:
574
+ Impact factor
575
+ """
576
+ try:
577
+ pattern_obj = SAFE_PATTERNS["extract_range_size"]
578
+ if not pattern_obj.test(line):
579
+ return 2
580
+
581
+ range_str = pattern_obj.apply(line)
582
+ range_size = self._extract_range_size_from_string(range_str)
583
+
584
+ return self._calculate_impact_from_range_size(range_size)
585
+ except (ValueError, AttributeError):
586
+ return 2
587
+
588
+ @staticmethod
589
+ def _extract_range_size_from_string(range_str: str) -> int:
590
+ """Extract range size from string.
591
+
592
+ Args:
593
+ range_str: Range string
594
+
595
+ Returns:
596
+ Range size
597
+ """
598
+ import re
599
+
600
+ number_match = re.search(r"\d+", range_str)
601
+ if number_match:
602
+ return int(number_match.group())
603
+ return 0
604
+
605
+ @staticmethod
606
+ def _calculate_impact_from_range_size(range_size: int) -> int:
607
+ """Calculate impact factor from range size.
608
+
609
+ Args:
610
+ range_size: Size of range
611
+
612
+ Returns:
613
+ Impact factor
614
+ """
615
+ if range_size > 1000:
616
+ return 10
617
+ elif range_size > 100:
618
+ return 5
619
+ return 2
620
+
621
+ @staticmethod
622
+ def _is_in_loop_context_enhanced(lines: list[str], line_index: int) -> bool:
623
+ """Check if line is in enhanced loop context.
624
+
625
+ Args:
626
+ lines: File lines
627
+ line_index: Line index
628
+
629
+ Returns:
630
+ True if in loop
631
+ """
632
+ context_start = max(0, line_index - 8)
633
+ context_lines = lines[context_start : line_index + 1]
634
+
635
+ for ctx_line in context_lines:
636
+ pattern_obj = SAFE_PATTERNS["match_loop_patterns"]
637
+ if pattern_obj.test(ctx_line):
638
+ return True
639
+
640
+ return False
641
+
642
+ @staticmethod
643
+ def _generate_string_suggestions(
644
+ concat_patterns: list[dict[str, t.Any]],
645
+ inefficient_joins: list[dict[str, t.Any]],
646
+ repeated_formatting: list[dict[str, t.Any]],
647
+ ) -> str:
648
+ """Generate string optimization suggestions.
649
+
650
+ Args:
651
+ concat_patterns: String concatenation patterns
652
+ inefficient_joins: Inefficient join patterns
653
+ repeated_formatting: Repeated formatting patterns
654
+
655
+ Returns:
656
+ Suggestion string
657
+ """
658
+ suggestions = []
659
+
660
+ if concat_patterns:
661
+ high_impact = len(
662
+ [p for p in concat_patterns if p.get("estimated_impact", 1) >= 5]
663
+ )
664
+ suggestions.append(
665
+ f"String concatenation: {len(concat_patterns)} instances "
666
+ f"({high_impact} high-impact) - use list[t.Any].append + join"
667
+ )
668
+
669
+ if inefficient_joins:
670
+ suggestions.append(
671
+ f"Empty joins: {len(inefficient_joins)} - use empty string literal"
672
+ )
673
+
674
+ if repeated_formatting:
675
+ suggestions.append(
676
+ f"Repeated formatting: {len(repeated_formatting)} - cache format strings"
677
+ )
678
+
679
+ suggestions.append("Expected gains: 3-50x for string building in loops")
680
+ return "; ".join(suggestions)
681
+
682
+ def _detect_list_comprehension_opportunities(
683
+ self, tree: ast.AST
684
+ ) -> list[dict[str, t.Any]]:
685
+ """Detect opportunities to use list comprehensions.
686
+
687
+ Args:
688
+ tree: AST tree
689
+
690
+ Returns:
691
+ List of comprehension opportunities
692
+ """
693
+ issues: list[dict[str, t.Any]] = []
694
+
695
+ class ComprehensionAnalyzer(ast.NodeVisitor):
696
+ def __init__(self) -> None:
697
+ self.opportunities: list[dict[str, t.Any]] = []
698
+
699
+ def visit_For(self, node: ast.For) -> None:
700
+ if (
701
+ len(node.body) == 1
702
+ and isinstance(node.body[0], ast.Expr)
703
+ and isinstance(
704
+ node.body[0].value,
705
+ ast.Call,
706
+ )
707
+ and isinstance(
708
+ node.body[0].value.func,
709
+ ast.Attribute,
710
+ )
711
+ and node.body[0].value.func.attr == "append"
712
+ ):
713
+ self.opportunities.append(
714
+ {
715
+ "line_number": node.lineno,
716
+ "type": "append_loop_to_comprehension",
717
+ "optimization": "list_comprehension",
718
+ "performance_gain": "20-30% faster",
719
+ "readability": "improved",
720
+ }
721
+ )
722
+
723
+ self.generic_visit(node)
724
+
725
+ analyzer = ComprehensionAnalyzer()
726
+ analyzer.visit(tree)
727
+
728
+ if analyzer.opportunities:
729
+ issues.append(
730
+ {
731
+ "type": "list_comprehension_opportunities",
732
+ "instances": analyzer.opportunities,
733
+ "total_count": len(analyzer.opportunities),
734
+ "suggestion": f"Convert {len(analyzer.opportunities)} append loops"
735
+ f" to list[t.Any] comprehensions for better performance "
736
+ f"and readability",
737
+ }
738
+ )
739
+
740
+ return issues
741
+
742
+ def _detect_inefficient_builtin_usage(
743
+ self, tree: ast.AST, content: str
744
+ ) -> list[dict[str, t.Any]]:
745
+ """Detect inefficient builtin function usage in loops.
746
+
747
+ Args:
748
+ tree: AST tree
749
+ content: File content
750
+
751
+ Returns:
752
+ List of inefficient builtin issues
753
+ """
754
+ issues: list[dict[str, t.Any]] = []
755
+
756
+ class BuiltinAnalyzer(ast.NodeVisitor):
757
+ def __init__(self) -> None:
758
+ self.inefficient_calls: list[dict[str, t.Any]] = []
759
+ self.in_loop = False
760
+
761
+ def visit_For(self, node: ast.For) -> None:
762
+ old_in_loop = self.in_loop
763
+ self.in_loop = True
764
+ self.generic_visit(node)
765
+ self.in_loop = old_in_loop
766
+
767
+ def visit_While(self, node: ast.While) -> None:
768
+ old_in_loop = self.in_loop
769
+ self.in_loop = True
770
+ self.generic_visit(node)
771
+ self.in_loop = old_in_loop
772
+
773
+ def visit_Call(self, node: ast.Call) -> None:
774
+ if self.in_loop and isinstance(node.func, ast.Name):
775
+ func_name = node.func.id
776
+
777
+ if func_name in ("len", "sum", "max", "min", "sorted"):
778
+ if node.args and isinstance(node.args[0], ast.Name):
779
+ self.inefficient_calls.append(
780
+ {
781
+ "line_number": node.lineno,
782
+ "function": func_name,
783
+ "type": "repeated_builtin_in_loop",
784
+ "optimization": f"Cache {func_name}() "
785
+ f"result outside loop",
786
+ "performance_gain": "2-10x depending on data size",
787
+ }
788
+ )
789
+
790
+ self.generic_visit(node)
791
+
792
+ analyzer = BuiltinAnalyzer()
793
+ analyzer.visit(tree)
794
+
795
+ if analyzer.inefficient_calls:
796
+ issues.append(
797
+ {
798
+ "type": "inefficient_builtin_usage",
799
+ "instances": analyzer.inefficient_calls,
800
+ "total_count": len(analyzer.inefficient_calls),
801
+ "suggestion": f"Cache {len(analyzer.inefficient_calls)} "
802
+ f"repeated builtin calls outside loops",
803
+ }
804
+ )
805
+
806
+ return issues
807
+
808
+
809
+ class NestedLoopAnalyzer(ast.NodeVisitor):
810
+ """AST visitor for detecting nested loops."""
811
+
812
+ def __init__(self) -> None:
813
+ """Initialize analyzer."""
814
+ self.nested_loops: list[dict[str, t.Any]] = []
815
+ self.complexity_hotspots: list[dict[str, t.Any]] = []
816
+ self._loop_stack: list[tuple[int, str]] = []
817
+
818
+ def visit_For(self, node: ast.For) -> None:
819
+ """Visit for loop node."""
820
+ self._handle_loop_node(node, "for")
821
+ self.generic_visit(node)
822
+
823
+ def visit_While(self, node: ast.While) -> None:
824
+ """Visit while loop node."""
825
+ self._handle_loop_node(node, "while")
826
+ self.generic_visit(node)
827
+
828
+ def _handle_loop_node(self, node: ast.For | ast.While, loop_type: str) -> None:
829
+ """Handle loop node detection."""
830
+ self._loop_stack.append((node.lineno, loop_type))
831
+
832
+ if len(self._loop_stack) > 1:
833
+ depth = len(self._loop_stack)
834
+ complexity = self._calculate_loop_complexity(depth)
835
+ priority = self._determine_priority(depth)
836
+
837
+ self.nested_loops.append(
838
+ {
839
+ "line_number": node.lineno,
840
+ "depth": depth,
841
+ "complexity": complexity,
842
+ "priority": priority,
843
+ "type": loop_type,
844
+ }
845
+ )
846
+
847
+ self._loop_stack.pop()
848
+
849
+ @staticmethod
850
+ def _calculate_loop_complexity(depth: int) -> str:
851
+ """Calculate complexity string from depth."""
852
+ if depth == 2:
853
+ return "O(n²)"
854
+ elif depth == 3:
855
+ return "O(n³)"
856
+ elif depth >= 4:
857
+ return "O(n⁴+)"
858
+ return "O(n)"
859
+
860
+ @staticmethod
861
+ def _determine_priority(depth: int) -> str:
862
+ """Determine priority from depth."""
863
+ if depth >= 4:
864
+ return "critical"
865
+ elif depth == 3:
866
+ return "high"
867
+ return "medium"
868
+
869
+
870
+ class ListOpAnalyzer(ast.NodeVisitor):
871
+ """AST visitor for detecting inefficient list operations."""
872
+
873
+ def __init__(self) -> None:
874
+ """Initialize analyzer."""
875
+ self.list_ops: list[dict[str, t.Any]] = []
876
+ self._in_loop = False
877
+
878
+ def visit_For(self, node: ast.For) -> None:
879
+ """Visit for loop."""
880
+ old_in_loop = self._in_loop
881
+ self._in_loop = True
882
+ self.generic_visit(node)
883
+ self._in_loop = old_in_loop
884
+
885
+ def visit_While(self, node: ast.While) -> None:
886
+ """Visit while loop."""
887
+ old_in_loop = self._in_loop
888
+ self._in_loop = True
889
+ self.generic_visit(node)
890
+ self._in_loop = old_in_loop
891
+
892
+ def visit_AugAssign(self, node: ast.AugAssign) -> None:
893
+ """Visit augmented assignment."""
894
+ if self._in_loop and isinstance(node.op, operator.Add):
895
+ if isinstance(node.value, ast.List):
896
+ impact = len(node.value.elts) if node.value.elts else 1
897
+ self.list_ops.append(
898
+ {
899
+ "line_number": node.lineno,
900
+ "type": "inefficient_list_concat",
901
+ "optimization": "append"
902
+ if len(node.value.elts) == 1
903
+ else "extend",
904
+ "impact_factor": impact,
905
+ "performance_gain": f"{max(2, min(50, impact * 5))}x",
906
+ }
907
+ )
908
+
909
+ self.generic_visit(node)