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
@@ -2,24 +2,35 @@ import ast
2
2
  import typing as t
3
3
  from pathlib import Path
4
4
 
5
- from ..services.regex_patterns import SAFE_PATTERNS
6
5
  from .base import (
6
+ AgentContext,
7
7
  FixResult,
8
8
  Issue,
9
9
  IssueType,
10
10
  SubAgent,
11
11
  agent_registry,
12
12
  )
13
-
14
- if t.TYPE_CHECKING:
15
- from .refactoring_helpers import (
16
- ComplexityCalculator,
17
- EnhancedUsageAnalyzer,
18
- UsageDataCollector,
19
- )
13
+ from .helpers.refactoring.code_transformer import CodeTransformer
14
+ from .helpers.refactoring.complexity_analyzer import ComplexityAnalyzer
15
+ from .helpers.refactoring.dead_code_detector import DeadCodeDetector
16
+ from .semantic_helpers import (
17
+ SemanticInsight,
18
+ create_semantic_enhancer,
19
+ get_session_enhanced_recommendations,
20
+ )
20
21
 
21
22
 
22
23
  class RefactoringAgent(SubAgent):
24
+ def __init__(self, context: AgentContext) -> None:
25
+ super().__init__(context)
26
+ self.semantic_enhancer = create_semantic_enhancer(context.project_path)
27
+ self.semantic_insights: dict[str, SemanticInsight] = {}
28
+
29
+ # Initialize helper modules
30
+ self._complexity_analyzer = ComplexityAnalyzer(context)
31
+ self._code_transformer = CodeTransformer(context)
32
+ self._dead_code_detector = DeadCodeDetector(context)
33
+
23
34
  def get_supported_types(self) -> set[IssueType]:
24
35
  return {IssueType.COMPLEXITY, IssueType.DEAD_CODE}
25
36
 
@@ -114,7 +125,10 @@ class RefactoringAgent(SubAgent):
114
125
  remaining_issues=[f"Could not read file: {file_path}"],
115
126
  )
116
127
 
117
- refactored_content = self._refactor_detect_agent_needs_pattern(content)
128
+ # Delegate to code transformer helper
129
+ refactored_content = self._code_transformer.refactor_detect_agent_needs_pattern(
130
+ content
131
+ )
118
132
 
119
133
  if refactored_content != content:
120
134
  success = self.context.write_file_content(file_path, refactored_content)
@@ -126,7 +140,9 @@ class RefactoringAgent(SubAgent):
126
140
  "Applied proven complexity reduction pattern for detect_agent_needs"
127
141
  ],
128
142
  files_modified=[str(file_path)],
129
- recommendations=["Verify functionality after complexity reduction"],
143
+ recommendations=await self._enhance_recommendations_with_semantic(
144
+ ["Verify functionality after complexity reduction"]
145
+ ),
130
146
  )
131
147
  else:
132
148
  return FixResult(
@@ -176,7 +192,17 @@ class RefactoringAgent(SubAgent):
176
192
  )
177
193
 
178
194
  tree = ast.parse(content)
179
- complex_functions = self._find_complex_functions(tree, content)
195
+ # Delegate to complexity analyzer helper
196
+ complex_functions = self._complexity_analyzer.find_complex_functions(
197
+ tree, content
198
+ )
199
+
200
+ # Enhance complex function detection with semantic analysis
201
+ semantic_complex_functions = await self._find_semantic_complex_patterns(
202
+ content, file_path
203
+ )
204
+ if semantic_complex_functions:
205
+ complex_functions.extend(semantic_complex_functions)
180
206
 
181
207
  if not complex_functions:
182
208
  return FixResult(
@@ -185,19 +211,27 @@ class RefactoringAgent(SubAgent):
185
211
  recommendations=["No overly complex functions found"],
186
212
  )
187
213
 
188
- return self._apply_and_save_refactoring(file_path, content, complex_functions)
214
+ return await self._apply_and_save_refactoring(
215
+ file_path, content, complex_functions
216
+ )
189
217
 
190
- def _apply_and_save_refactoring(
218
+ async def _apply_and_save_refactoring(
191
219
  self,
192
220
  file_path: Path,
193
221
  content: str,
194
222
  complex_functions: list[dict[str, t.Any]],
195
223
  ) -> FixResult:
196
- refactored_content = self._apply_complexity_reduction(
197
- content,
198
- complex_functions,
224
+ # Delegate refactoring to code transformer helper
225
+ refactored_content = self._code_transformer.refactor_complex_functions(
226
+ content, complex_functions
199
227
  )
200
228
 
229
+ if refactored_content == content:
230
+ # Try enhanced strategies if basic refactoring didn't work
231
+ refactored_content = self._code_transformer.apply_enhanced_strategies(
232
+ content
233
+ )
234
+
201
235
  if refactored_content == content:
202
236
  return self._create_no_changes_result()
203
237
 
@@ -214,7 +248,9 @@ class RefactoringAgent(SubAgent):
214
248
  confidence=0.8,
215
249
  fixes_applied=[f"Reduced complexity in {len(complex_functions)} functions"],
216
250
  files_modified=[str(file_path)],
217
- recommendations=["Verify functionality after complexity reduction"],
251
+ recommendations=await self._enhance_recommendations_with_semantic(
252
+ ["Verify functionality after complexity reduction"]
253
+ ),
218
254
  )
219
255
 
220
256
  def _create_no_changes_result(self) -> FixResult:
@@ -292,7 +328,8 @@ class RefactoringAgent(SubAgent):
292
328
  )
293
329
 
294
330
  tree = ast.parse(content)
295
- dead_code_analysis = self._analyze_dead_code(tree, content)
331
+ # Delegate to dead code detector helper
332
+ dead_code_analysis = self._dead_code_detector.analyze_dead_code(tree, content)
296
333
 
297
334
  if not dead_code_analysis["removable_items"]:
298
335
  return FixResult(
@@ -309,7 +346,14 @@ class RefactoringAgent(SubAgent):
309
346
  content: str,
310
347
  analysis: dict[str, t.Any],
311
348
  ) -> FixResult:
312
- cleaned_content = self._remove_dead_code_items(content, analysis)
349
+ # Remove dead code items
350
+ lines = content.split("\n")
351
+ lines_to_remove = self._collect_all_removable_lines(lines, analysis)
352
+
353
+ filtered_lines = [
354
+ line for i, line in enumerate(lines) if i not in lines_to_remove
355
+ ]
356
+ cleaned_content = "\n".join(filtered_lines)
313
357
 
314
358
  if cleaned_content == content:
315
359
  return self._create_no_cleanup_result()
@@ -331,6 +375,25 @@ class RefactoringAgent(SubAgent):
331
375
  recommendations=["Verify imports and functionality after cleanup"],
332
376
  )
333
377
 
378
+ def _collect_all_removable_lines(
379
+ self, lines: list[str], analysis: dict[str, t.Any]
380
+ ) -> set[int]:
381
+ """Collect all lines to remove from analysis."""
382
+ lines_to_remove: set[int] = set()
383
+
384
+ # Delegate to dead code detector helper for different removal types
385
+ lines_to_remove.update(
386
+ self._dead_code_detector.find_lines_to_remove(lines, analysis)
387
+ )
388
+ lines_to_remove.update(
389
+ self._dead_code_detector._find_unreachable_lines(lines, analysis)
390
+ )
391
+ lines_to_remove.update(
392
+ self._dead_code_detector._find_redundant_lines(lines, analysis)
393
+ )
394
+
395
+ return lines_to_remove
396
+
334
397
  def _create_no_cleanup_result(self) -> FixResult:
335
398
  return FixResult(
336
399
  success=False,
@@ -349,662 +412,100 @@ class RefactoringAgent(SubAgent):
349
412
  remaining_issues=[f"Error processing file: {error}"],
350
413
  )
351
414
 
352
- def _find_complex_functions(
353
- self,
354
- tree: ast.AST,
355
- content: str,
415
+ async def _find_semantic_complex_patterns(
416
+ self, content: str, file_path: Path
356
417
  ) -> list[dict[str, t.Any]]:
357
- complex_functions: list[dict[str, t.Any]] = []
358
-
359
- class ComplexityAnalyzer(ast.NodeVisitor):
360
- def __init__(
361
- self,
362
- calc_complexity: t.Callable[
363
- [ast.FunctionDef | ast.AsyncFunctionDef],
364
- int,
365
- ],
366
- ) -> None:
367
- self.calc_complexity = calc_complexity
368
-
369
- def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
370
- complexity = self.calc_complexity(node)
371
- if complexity > 15:
372
- complex_functions.append(
373
- {
374
- "name": node.name,
375
- "line_start": node.lineno,
376
- "line_end": node.end_lineno or node.lineno,
377
- "complexity": complexity,
378
- "node": node,
379
- },
380
- )
381
- self.generic_visit(node)
382
-
383
- def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
384
- complexity = self.calc_complexity(node)
385
- if complexity > 15:
386
- complex_functions.append(
387
- {
388
- "name": node.name,
389
- "line_start": node.lineno,
390
- "line_end": node.end_lineno or node.lineno,
391
- "complexity": complexity,
392
- "node": node,
393
- },
394
- )
395
- self.generic_visit(node)
396
-
397
- analyzer = ComplexityAnalyzer(self._calculate_cognitive_complexity)
398
- analyzer.visit(tree)
399
-
400
- return complex_functions
401
-
402
- def _calculate_cognitive_complexity(
403
- self,
404
- node: ast.FunctionDef | ast.AsyncFunctionDef,
405
- ) -> int:
406
- calculator = self._create_complexity_calculator()
407
- calculator.visit(node)
408
- return calculator.complexity
418
+ """Find semantically complex patterns using vector similarity."""
419
+ semantic_functions = []
409
420
 
410
- def _create_complexity_calculator(self) -> "ComplexityCalculator":
411
- from . import refactoring_helpers
412
-
413
- return refactoring_helpers.ComplexityCalculator()
414
-
415
- def _apply_complexity_reduction(
416
- self,
417
- content: str,
418
- complex_functions: list[dict[str, t.Any]],
419
- ) -> str:
420
- refactored_content = self._refactor_complex_functions(
421
- content, complex_functions
422
- )
423
- if refactored_content != content:
424
- return refactored_content
425
-
426
- return self._apply_enhanced_strategies(content)
427
-
428
- def _refactor_complex_functions(
429
- self, content: str, complex_functions: list[dict[str, t.Any]]
430
- ) -> str:
431
- lines = content.split("\n")
432
-
433
- for func_info in complex_functions:
434
- func_name = func_info.get("name", "unknown")
435
-
436
- if func_name == "detect_agent_needs":
437
- refactored = self._refactor_detect_agent_needs_pattern(content)
438
- if refactored != content:
439
- return refactored
440
-
441
- func_content = self._extract_function_content(lines, func_info)
442
- if func_content:
443
- extracted_helpers = self._extract_logical_sections(
444
- func_content, func_info
445
- )
446
- if extracted_helpers:
447
- modified_content = self._apply_function_extraction(
448
- content, func_info, extracted_helpers
449
- )
450
- if modified_content != content:
451
- return modified_content
452
-
453
- return content
454
-
455
- def _apply_enhanced_strategies(self, content: str) -> str:
456
- enhanced_content = self._apply_enhanced_complexity_patterns(content)
457
- return enhanced_content
458
-
459
- def _apply_enhanced_complexity_patterns(self, content: str) -> str:
460
- operations = [
461
- self._extract_nested_conditions,
462
- self._simplify_boolean_expressions,
463
- self._extract_validation_patterns,
464
- self._simplify_data_structures,
465
- ]
466
-
467
- modified_content = content
468
- for operation in operations:
469
- modified_content = operation(modified_content)
470
-
471
- return modified_content
472
-
473
- def _extract_nested_conditions(self, content: str) -> str:
474
- lines = content.split("\n")
475
- modified_lines = []
476
-
477
- for i, line in enumerate(lines):
478
- stripped = line.strip()
479
-
480
- if (
481
- stripped.startswith("if ")
482
- and (" and " in stripped or " or " in stripped)
483
- and len(stripped) > 80
484
- ):
485
- indent = " " * (len(line) - len(line.lstrip()))
486
- helper_name = f"_is_complex_condition_{i}"
487
- modified_lines.append(f"{indent}if self.{helper_name}(): ")
488
- continue
489
-
490
- modified_lines.append(line)
491
-
492
- return "\n".join(modified_lines)
493
-
494
- def _simplify_boolean_expressions(self, content: str) -> str:
495
- lines = content.split("\n")
496
- modified_lines = []
497
-
498
- for line in lines:
499
- if " and " in line and " or " in line and len(line.strip()) > 100:
500
- if line.strip().startswith("if "):
501
- indent = " " * (len(line) - len(line.lstrip()))
502
- method_name = "_validate_complex_condition"
503
- modified_lines.append(f"{indent}if self.{method_name}(): ")
504
- continue
505
-
506
- modified_lines.append(line)
507
-
508
- return "\n".join(modified_lines)
509
-
510
- def _extract_validation_patterns(self, content: str) -> str:
511
- if "validation_extract" in SAFE_PATTERNS:
512
- content = SAFE_PATTERNS["validation_extract"].apply(content)
513
- else:
514
- pattern_obj = SAFE_PATTERNS["match_validation_patterns"]
515
- if pattern_obj.test(content):
516
- matches = len(
517
- [line for line in content.split("\n") if pattern_obj.test(line)]
421
+ try:
422
+ # Delegate to complexity analyzer helper
423
+ code_elements = (
424
+ self._complexity_analyzer.extract_code_functions_for_semantic_analysis(
425
+ content
518
426
  )
519
- if matches > 2:
520
- pass
521
-
522
- return content
427
+ )
523
428
 
524
- def _simplify_data_structures(self, content: str) -> str:
525
- lines = content.split("\n")
526
- modified_lines = []
527
-
528
- for line in lines:
529
- stripped = line.strip()
530
-
531
- if (
532
- "[" in stripped
533
- and "for" in stripped
534
- and "if" in stripped
535
- and len(stripped) > 80
536
- ):
537
- pass
538
-
539
- elif stripped.count(": ") > 5 and stripped.count(", ") > 5:
540
- pass
541
-
542
- modified_lines.append(line)
543
-
544
- return "\n".join(modified_lines)
545
-
546
- def _refactor_detect_agent_needs_pattern(self, content: str) -> str:
547
- detect_func_start = "async def detect_agent_needs("
548
- if detect_func_start not in content:
549
- return content
550
-
551
- original_pattern = """ recommendations = {
552
- "urgent_agents": [],
553
- "suggested_agents": [],
554
- "workflow_recommendations": [],
555
- "detection_reasoning": "",
556
- }
557
-
558
- if error_context: """
559
-
560
- replacement_pattern = """ recommendations = {
561
- "urgent_agents": [],
562
- "suggested_agents": [],
563
- "workflow_recommendations": [],
564
- "detection_reasoning": "",
565
- }
566
-
567
- _add_urgent_agents_for_errors(recommendations, error_context)
568
- _add_python_project_suggestions(recommendations, file_patterns)
569
- _set_workflow_recommendations(recommendations)
570
- _generate_detection_reasoning(recommendations)
571
-
572
- return json.dumps(recommendations, indent=2)"""
573
-
574
- if original_pattern in content:
575
- modified_content = content.replace(original_pattern, replacement_pattern)
576
- if modified_content != content:
577
- return modified_content
578
-
579
- return content
580
-
581
- def _extract_logical_sections(
582
- self, func_content: str, func_info: dict[str, t.Any]
583
- ) -> list[dict[str, str]]:
584
- sections: list[dict[str, str]] = []
585
- lines = func_content.split("\n")
586
- current_section: list[str] = []
587
- section_type: str | None = None
588
-
589
- for line in lines:
590
- stripped = line.strip()
591
-
592
- if self._should_start_new_section(stripped, section_type):
593
- if current_section:
594
- sections.append(
595
- self._create_section(
596
- current_section, section_type, len(sections)
429
+ for element in code_elements:
430
+ if (
431
+ element["type"] == "function"
432
+ and element["estimated_complexity"] > 10
433
+ ):
434
+ # Search for similar complex patterns
435
+ insight = (
436
+ await self.semantic_enhancer.find_refactoring_opportunities(
437
+ element["signature"]
438
+ + "\n"
439
+ + element["body"][:150], # Include body sample
440
+ current_file=file_path,
597
441
  )
598
442
  )
599
443
 
600
- current_section, section_type = self._initialize_new_section(
601
- line, stripped
602
- )
603
- else:
604
- current_section.append(line)
605
-
606
- if current_section:
607
- sections.append(
608
- self._create_section(current_section, section_type, len(sections))
609
- )
610
-
611
- return [s for s in sections if len(s["content"].split("\n")) >= 5]
612
-
613
- def _should_start_new_section(
614
- self, stripped: str, current_section_type: str | None
615
- ) -> bool:
616
- if stripped.startswith("if ") and len(stripped) > 50:
617
- return True
618
- return (
619
- stripped.startswith(("for ", "while ")) and current_section_type != "loop"
620
- )
621
-
622
- def _initialize_new_section(
623
- self, line: str, stripped: str
624
- ) -> tuple[list[str], str]:
625
- if stripped.startswith("if ") and len(stripped) > 50:
626
- return [line], "conditional"
627
- elif stripped.startswith(("for ", "while ")):
628
- return [line], "loop"
629
- return [line], "general"
630
-
631
- def _create_section(
632
- self, current_section: list[str], section_type: str | None, section_count: int
633
- ) -> dict[str, str]:
634
- effective_type = section_type or "general"
635
- name_prefix = "handle" if effective_type == "conditional" else "process"
636
-
637
- return {
638
- "type": effective_type,
639
- "content": "\n".join(current_section),
640
- "name": f"_{name_prefix}_{effective_type}_{section_count + 1}",
641
- }
642
-
643
- def _analyze_dead_code(self, tree: ast.AST, content: str) -> dict[str, t.Any]:
644
- analysis: dict[str, list[t.Any]] = {
645
- "unused_imports": [],
646
- "unused_variables": [],
647
- "unused_functions": [],
648
- "unused_classes": [],
649
- "unreachable_code": [],
650
- "removable_items": [],
651
- }
652
-
653
- analyzer_result = self._collect_usage_data(tree)
654
- self._process_unused_imports(analysis, analyzer_result)
655
- self._process_unused_functions(analysis, analyzer_result)
656
- self._process_unused_classes(analysis, analyzer_result)
657
- self._detect_unreachable_code(analysis, tree, content)
658
- self._detect_redundant_code(analysis, tree, content)
659
-
660
- return analysis
661
-
662
- def _collect_usage_data(self, tree: ast.AST) -> dict[str, t.Any]:
663
- collector = self._create_usage_data_collector()
664
- analyzer = self._create_enhanced_usage_analyzer(collector)
665
- analyzer.visit(tree)
666
- return collector.get_results(analyzer)
667
-
668
- def _create_usage_data_collector(self) -> "UsageDataCollector":
669
- from . import refactoring_helpers
670
-
671
- return refactoring_helpers.UsageDataCollector()
672
-
673
- def _create_enhanced_usage_analyzer(
674
- self, collector: "UsageDataCollector"
675
- ) -> "EnhancedUsageAnalyzer":
676
- from . import refactoring_helpers
677
-
678
- return refactoring_helpers.EnhancedUsageAnalyzer(collector)
679
-
680
- def _process_unused_imports(
681
- self,
682
- analysis: dict[str, t.Any],
683
- analyzer_result: dict[str, t.Any],
684
- ) -> None:
685
- import_lines: list[tuple[int, str, str]] = analyzer_result["import_lines"]
686
- for line_no, name, import_type in import_lines:
687
- if name not in analyzer_result["used_names"]:
688
- analysis["unused_imports"].append(
689
- {
690
- "name": name,
691
- "line": line_no,
692
- "type": import_type,
693
- },
694
- )
695
- analysis["removable_items"].append(f"unused import: {name}")
696
-
697
- def _process_unused_functions(
698
- self,
699
- analysis: dict[str, t.Any],
700
- analyzer_result: dict[str, t.Any],
701
- ) -> None:
702
- all_unused_functions: list[dict[str, t.Any]] = analyzer_result[
703
- "unused_functions"
704
- ]
705
- unused_functions = [
706
- func
707
- for func in all_unused_functions
708
- if func["name"] not in analyzer_result["used_names"]
709
- ]
710
- analysis["unused_functions"] = unused_functions
711
- for func in unused_functions:
712
- analysis["removable_items"].append(f"unused function: {func['name']}")
713
-
714
- def _process_unused_classes(
715
- self, analysis: dict[str, t.Any], analyzer_result: dict[str, t.Any]
716
- ) -> None:
717
- if "unused_classes" not in analyzer_result:
718
- return
719
-
720
- unused_classes = [
721
- cls
722
- for cls in analyzer_result["unused_classes"]
723
- if cls["name"] not in analyzer_result["used_names"]
724
- ]
725
-
726
- analysis["unused_classes"] = unused_classes
727
- for cls in unused_classes:
728
- analysis["removable_items"].append(f"unused class: {cls['name']}")
729
-
730
- def _detect_unreachable_code(
731
- self, analysis: dict[str, t.Any], tree: ast.AST, content: str
732
- ) -> None:
733
- class UnreachableCodeDetector(ast.NodeVisitor):
734
- def __init__(self) -> None:
735
- self.unreachable_blocks: list[dict[str, t.Any]] = []
736
-
737
- def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
738
- self._check_unreachable_in_function(node)
739
- self.generic_visit(node)
740
-
741
- def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
742
- self._check_unreachable_in_function(node)
743
- self.generic_visit(node)
744
-
745
- def _check_unreachable_in_function(
746
- self, node: ast.FunctionDef | ast.AsyncFunctionDef
747
- ) -> None:
748
- for i, stmt in enumerate(node.body):
749
- if isinstance(stmt, ast.Return | ast.Raise):
750
- if i + 1 < len(node.body):
751
- next_stmt = node.body[i + 1]
752
- self.unreachable_blocks.append(
753
- {
754
- "type": "unreachable_after_return",
755
- "line": next_stmt.lineno,
756
- "function": node.name,
757
- }
758
- )
759
-
760
- detector = UnreachableCodeDetector()
761
- detector.visit(tree)
762
-
763
- analysis["unreachable_code"] = detector.unreachable_blocks
764
- for block in detector.unreachable_blocks:
765
- analysis["removable_items"].append(
766
- f"unreachable code after line {block['line']} in {block['function']}"
767
- )
768
-
769
- def _detect_redundant_code(
770
- self, analysis: dict[str, t.Any], tree: ast.AST, content: str
771
- ) -> None:
772
- lines = content.split("\n")
444
+ if insight.total_matches > 2:
445
+ semantic_functions.append(
446
+ {
447
+ "name": element["name"],
448
+ "line_start": element["start_line"],
449
+ "line_end": element["end_line"],
450
+ "complexity": element["estimated_complexity"],
451
+ "semantic_matches": insight.total_matches,
452
+ "refactor_opportunities": insight.related_patterns[
453
+ :3
454
+ ], # Top 3 matches
455
+ "node": element.get("node"),
456
+ }
457
+ )
773
458
 
774
- line_hashes = {}
775
- for i, line in enumerate(lines):
776
- if line.strip() and not line.strip().startswith("#"):
777
- line_hash = hash(line.strip())
778
- if line_hash in line_hashes:
779
- analysis["removable_items"].append(
780
- f"potential duplicate code at line {i + 1}"
781
- )
782
- line_hashes[line_hash] = i
459
+ # Store insight for recommendation enhancement
460
+ self.semantic_insights[element["name"]] = insight
783
461
 
784
- class RedundantPatternDetector(ast.NodeVisitor):
785
- def __init__(self) -> None:
786
- self.redundant_items: list[dict[str, t.Any]] = []
462
+ except Exception as e:
463
+ self.log(f"Warning: Semantic complexity detection failed: {e}")
787
464
 
788
- def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
789
- if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
790
- self.redundant_items.append(
791
- {"type": "empty_except", "line": node.lineno}
792
- )
793
- self.generic_visit(node)
465
+ return semantic_functions
794
466
 
795
- def visit_If(self, node: ast.If) -> None:
796
- if isinstance(node.test, ast.Constant):
797
- if node.test.value is True:
798
- self.redundant_items.append(
799
- {"type": "if_true", "line": node.lineno}
800
- )
801
- elif node.test.value is False:
802
- self.redundant_items.append(
803
- {"type": "if_false", "line": node.lineno}
804
- )
805
- self.generic_visit(node)
806
-
807
- detector = RedundantPatternDetector()
808
- detector.visit(tree)
467
+ async def _enhance_recommendations_with_semantic(
468
+ self, base_recommendations: list[str]
469
+ ) -> list[str]:
470
+ """Enhance recommendations with semantic insights."""
471
+ enhanced = base_recommendations.copy()
809
472
 
810
- for item in detector.redundant_items:
811
- analysis["removable_items"].append(
812
- f"redundant {item['type']} at line {item['line']}"
473
+ # Add semantic insights if available
474
+ if self.semantic_insights:
475
+ total_semantic_matches = sum(
476
+ insight.total_matches for insight in self.semantic_insights.values()
813
477
  )
814
-
815
- def _should_remove_import_line(
816
- self, line: str, unused_import: dict[str, str]
817
- ) -> bool:
818
- if unused_import["type"] == "import":
819
- return f"import {unused_import['name']}" in line
820
- elif unused_import["type"] == "from_import":
821
- return (
822
- "from " in line
823
- and unused_import["name"] in line
824
- and line.strip().endswith(unused_import["name"])
478
+ high_conf_matches = sum(
479
+ insight.high_confidence_matches
480
+ for insight in self.semantic_insights.values()
825
481
  )
826
- return False
827
-
828
- def _find_lines_to_remove(
829
- self, lines: list[str], analysis: dict[str, t.Any]
830
- ) -> set[int]:
831
- lines_to_remove: set[int] = set()
832
-
833
- for unused_import in analysis["unused_imports"]:
834
- line_idx = unused_import["line"] - 1
835
- if 0 <= line_idx < len(lines):
836
- line = lines[line_idx]
837
- if self._should_remove_import_line(line, unused_import):
838
- lines_to_remove.add(line_idx)
839
-
840
- return lines_to_remove
841
-
842
- def _remove_dead_code_items(self, content: str, analysis: dict[str, t.Any]) -> str:
843
- lines = content.split("\n")
844
- lines_to_remove = self._collect_all_removable_lines(lines, analysis)
845
-
846
- filtered_lines = [
847
- line for i, line in enumerate(lines) if i not in lines_to_remove
848
- ]
849
-
850
- return "\n".join(filtered_lines)
851
-
852
- def _collect_all_removable_lines(
853
- self, lines: list[str], analysis: dict[str, t.Any]
854
- ) -> set[int]:
855
- removal_functions = [
856
- lambda: self._find_lines_to_remove(lines, analysis),
857
- lambda: self._find_unreachable_lines(lines, analysis),
858
- lambda: self._find_redundant_lines(lines, analysis),
859
- ]
860
-
861
- lines_to_remove: set[int] = set()
862
- for removal_func in removal_functions:
863
- lines_to_remove.update(removal_func())
864
482
 
865
- return lines_to_remove
866
-
867
- def _find_unreachable_lines(
868
- self, lines: list[str], analysis: dict[str, t.Any]
869
- ) -> set[int]:
870
- lines_to_remove: set[int] = set()
871
-
872
- for item in analysis.get("unreachable_code", []):
873
- if "line" in item:
874
- line_idx = item["line"] - 1
875
- if 0 <= line_idx < len(lines):
876
- lines_to_remove.add(line_idx)
877
-
878
- return lines_to_remove
879
-
880
- def _find_redundant_lines(
881
- self, lines: list[str], analysis: dict[str, t.Any]
882
- ) -> set[int]:
883
- lines_to_remove: set[int] = set()
884
-
885
- for i in range(len(lines)):
886
- if self._is_empty_except_block(lines, i):
887
- empty_pass_idx = self._find_empty_pass_line(lines, i)
888
- if empty_pass_idx is not None:
889
- lines_to_remove.add(empty_pass_idx)
890
-
891
- return lines_to_remove
892
-
893
- def _is_empty_except_block(self, lines: list[str], line_idx: int) -> bool:
894
- stripped = lines[line_idx].strip()
895
- return stripped == "except: " or stripped.startswith("except ")
896
-
897
- def _find_empty_pass_line(self, lines: list[str], except_idx: int) -> int | None:
898
- for j in range(except_idx + 1, min(except_idx + 5, len(lines))):
899
- next_line = lines[j].strip()
900
- if not next_line:
901
- continue
902
- if next_line == "pass":
903
- return j
904
- break
905
- return None
906
-
907
- def _extract_function_content(
908
- self, lines: list[str], func_info: dict[str, t.Any]
909
- ) -> str:
910
- start_line = func_info["line_start"] - 1
911
- end_line = func_info.get("line_end", len(lines)) - 1
912
-
913
- if start_line < 0 or end_line >= len(lines):
914
- return ""
915
-
916
- return "\n".join(lines[start_line : end_line + 1])
917
-
918
- def _apply_function_extraction(
919
- self,
920
- content: str,
921
- func_info: dict[str, t.Any],
922
- extracted_helpers: list[dict[str, str]],
923
- ) -> str:
924
- lines = content.split("\n")
925
-
926
- if not self._is_extraction_valid(lines, func_info, extracted_helpers):
927
- return "\n".join(lines)
928
-
929
- return self._perform_extraction(lines, func_info, extracted_helpers)
483
+ if high_conf_matches > 0:
484
+ enhanced.append(
485
+ f"Semantic analysis found {high_conf_matches} similar complex patterns - "
486
+ f"consider extracting common refactoring utilities"
487
+ )
930
488
 
931
- def _is_extraction_valid(
932
- self,
933
- lines: list[str],
934
- func_info: dict[str, t.Any],
935
- extracted_helpers: list[dict[str, str]],
936
- ) -> bool:
937
- start_line = func_info["line_start"] - 1
938
- end_line = func_info.get("line_end", len(lines)) - 1
489
+ if total_semantic_matches >= 3:
490
+ enhanced.append(
491
+ f"Found {total_semantic_matches} related complexity patterns across codebase - "
492
+ f"review for consistent refactoring approach"
493
+ )
939
494
 
940
- return bool(extracted_helpers) and start_line >= 0 and end_line < len(lines)
495
+ # Store insights for session continuity
496
+ for func_name, insight in self.semantic_insights.items():
497
+ summary = self.semantic_enhancer.get_semantic_context_summary(insight)
498
+ self.log(f"Semantic context for {func_name}: {summary}")
499
+ await self.semantic_enhancer.store_insight_to_session(
500
+ insight, "RefactoringAgent"
501
+ )
941
502
 
942
- def _perform_extraction(
943
- self,
944
- lines: list[str],
945
- func_info: dict[str, t.Any],
946
- extracted_helpers: list[dict[str, str]],
947
- ) -> str:
948
- new_lines = self._replace_function_with_calls(
949
- lines, func_info, extracted_helpers
503
+ # Enhance with session-stored insights
504
+ enhanced = await get_session_enhanced_recommendations(
505
+ enhanced, "RefactoringAgent", self.context.project_path
950
506
  )
951
- return self._add_helper_definitions(new_lines, func_info, extracted_helpers)
952
-
953
- def _replace_function_with_calls(
954
- self,
955
- lines: list[str],
956
- func_info: dict[str, t.Any],
957
- extracted_helpers: list[dict[str, str]],
958
- ) -> list[str]:
959
- start_line = func_info["line_start"] - 1
960
- end_line = func_info.get("line_end", len(lines)) - 1
961
- func_indent = len(lines[start_line]) - len(lines[start_line].lstrip())
962
- indent = " " * (func_indent + 4)
963
-
964
- new_func_lines = [lines[start_line]]
965
- for helper in extracted_helpers:
966
- new_func_lines.append(f"{indent}self.{helper['name']}()")
967
-
968
- return lines[:start_line] + new_func_lines + lines[end_line + 1 :]
969
-
970
- def _add_helper_definitions(
971
- self,
972
- new_lines: list[str],
973
- func_info: dict[str, t.Any],
974
- extracted_helpers: list[dict[str, str]],
975
- ) -> str:
976
- start_line = func_info["line_start"] - 1
977
- class_end = self._find_class_end(new_lines, start_line)
978
-
979
- for helper in extracted_helpers:
980
- helper_lines = helper["content"].split("\n")
981
- new_lines = (
982
- new_lines[:class_end] + [""] + helper_lines + new_lines[class_end:]
983
- )
984
- class_end += len(helper_lines) + 1
985
-
986
- return "\n".join(new_lines)
987
-
988
- def _find_class_end(self, lines: list[str], func_start: int) -> int:
989
- class_indent = self._find_class_indent(lines, func_start)
990
- if class_indent is None:
991
- return len(lines)
992
- return self._find_class_end_line(lines, func_start, class_indent)
993
-
994
- def _find_class_indent(self, lines: list[str], func_start: int) -> int | None:
995
- for i in range(func_start, -1, -1):
996
- if lines[i].strip().startswith("class "):
997
- return len(lines[i]) - len(lines[i].lstrip())
998
- return None
999
507
 
1000
- def _find_class_end_line(
1001
- self, lines: list[str], func_start: int, class_indent: int
1002
- ) -> int:
1003
- for i in range(func_start + 1, len(lines)):
1004
- line = lines[i]
1005
- if line.strip() and len(line) - len(line.lstrip()) <= class_indent:
1006
- return i
1007
- return len(lines)
508
+ return enhanced
1008
509
 
1009
510
 
1010
511
  agent_registry.register(RefactoringAgent)