crackerjack 0.18.2__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 (533) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +96 -2
  3. crackerjack/__main__.py +637 -138
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +39 -0
  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/lsp/_base.py +194 -0
  27. crackerjack/adapters/lsp/_client.py +358 -0
  28. crackerjack/adapters/lsp/_manager.py +193 -0
  29. crackerjack/adapters/lsp/skylos.py +283 -0
  30. crackerjack/adapters/lsp/zuban.py +557 -0
  31. crackerjack/adapters/refactor/README.md +59 -0
  32. crackerjack/adapters/refactor/__init__.py +12 -0
  33. crackerjack/adapters/refactor/creosote.py +318 -0
  34. crackerjack/adapters/refactor/refurb.py +406 -0
  35. crackerjack/adapters/refactor/skylos.py +494 -0
  36. crackerjack/adapters/sast/README.md +132 -0
  37. crackerjack/adapters/sast/__init__.py +32 -0
  38. crackerjack/adapters/sast/_base.py +201 -0
  39. crackerjack/adapters/sast/bandit.py +423 -0
  40. crackerjack/adapters/sast/pyscn.py +405 -0
  41. crackerjack/adapters/sast/semgrep.py +241 -0
  42. crackerjack/adapters/security/README.md +111 -0
  43. crackerjack/adapters/security/__init__.py +17 -0
  44. crackerjack/adapters/security/gitleaks.py +339 -0
  45. crackerjack/adapters/type/README.md +52 -0
  46. crackerjack/adapters/type/__init__.py +12 -0
  47. crackerjack/adapters/type/pyrefly.py +402 -0
  48. crackerjack/adapters/type/ty.py +402 -0
  49. crackerjack/adapters/type/zuban.py +522 -0
  50. crackerjack/adapters/utility/README.md +51 -0
  51. crackerjack/adapters/utility/__init__.py +10 -0
  52. crackerjack/adapters/utility/checks.py +884 -0
  53. crackerjack/agents/README.md +264 -0
  54. crackerjack/agents/__init__.py +66 -0
  55. crackerjack/agents/architect_agent.py +238 -0
  56. crackerjack/agents/base.py +167 -0
  57. crackerjack/agents/claude_code_bridge.py +641 -0
  58. crackerjack/agents/coordinator.py +600 -0
  59. crackerjack/agents/documentation_agent.py +520 -0
  60. crackerjack/agents/dry_agent.py +585 -0
  61. crackerjack/agents/enhanced_coordinator.py +279 -0
  62. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  63. crackerjack/agents/error_middleware.py +53 -0
  64. crackerjack/agents/formatting_agent.py +230 -0
  65. crackerjack/agents/helpers/__init__.py +9 -0
  66. crackerjack/agents/helpers/performance/__init__.py +22 -0
  67. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  68. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  69. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  70. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  71. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  72. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  73. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  74. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  75. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  76. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  77. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  78. crackerjack/agents/import_optimization_agent.py +1181 -0
  79. crackerjack/agents/performance_agent.py +325 -0
  80. crackerjack/agents/performance_helpers.py +205 -0
  81. crackerjack/agents/proactive_agent.py +55 -0
  82. crackerjack/agents/refactoring_agent.py +511 -0
  83. crackerjack/agents/refactoring_helpers.py +247 -0
  84. crackerjack/agents/security_agent.py +793 -0
  85. crackerjack/agents/semantic_agent.py +479 -0
  86. crackerjack/agents/semantic_helpers.py +356 -0
  87. crackerjack/agents/test_creation_agent.py +570 -0
  88. crackerjack/agents/test_specialist_agent.py +526 -0
  89. crackerjack/agents/tracker.py +110 -0
  90. crackerjack/api.py +647 -0
  91. crackerjack/cli/README.md +394 -0
  92. crackerjack/cli/__init__.py +24 -0
  93. crackerjack/cli/cache_handlers.py +209 -0
  94. crackerjack/cli/cache_handlers_enhanced.py +680 -0
  95. crackerjack/cli/facade.py +162 -0
  96. crackerjack/cli/formatting.py +13 -0
  97. crackerjack/cli/handlers/__init__.py +85 -0
  98. crackerjack/cli/handlers/advanced.py +103 -0
  99. crackerjack/cli/handlers/ai_features.py +62 -0
  100. crackerjack/cli/handlers/analytics.py +479 -0
  101. crackerjack/cli/handlers/changelog.py +271 -0
  102. crackerjack/cli/handlers/config_handlers.py +16 -0
  103. crackerjack/cli/handlers/coverage.py +84 -0
  104. crackerjack/cli/handlers/documentation.py +280 -0
  105. crackerjack/cli/handlers/main_handlers.py +497 -0
  106. crackerjack/cli/handlers/monitoring.py +371 -0
  107. crackerjack/cli/handlers.py +700 -0
  108. crackerjack/cli/interactive.py +488 -0
  109. crackerjack/cli/options.py +1216 -0
  110. crackerjack/cli/semantic_handlers.py +292 -0
  111. crackerjack/cli/utils.py +19 -0
  112. crackerjack/cli/version.py +19 -0
  113. crackerjack/code_cleaner.py +1307 -0
  114. crackerjack/config/README.md +472 -0
  115. crackerjack/config/__init__.py +275 -0
  116. crackerjack/config/global_lock_config.py +207 -0
  117. crackerjack/config/hooks.py +390 -0
  118. crackerjack/config/loader.py +239 -0
  119. crackerjack/config/settings.py +141 -0
  120. crackerjack/config/tool_commands.py +331 -0
  121. crackerjack/core/README.md +393 -0
  122. crackerjack/core/__init__.py +0 -0
  123. crackerjack/core/async_workflow_orchestrator.py +738 -0
  124. crackerjack/core/autofix_coordinator.py +282 -0
  125. crackerjack/core/container.py +105 -0
  126. crackerjack/core/enhanced_container.py +583 -0
  127. crackerjack/core/file_lifecycle.py +472 -0
  128. crackerjack/core/performance.py +244 -0
  129. crackerjack/core/performance_monitor.py +357 -0
  130. crackerjack/core/phase_coordinator.py +1227 -0
  131. crackerjack/core/proactive_workflow.py +267 -0
  132. crackerjack/core/resource_manager.py +425 -0
  133. crackerjack/core/retry.py +275 -0
  134. crackerjack/core/service_watchdog.py +601 -0
  135. crackerjack/core/session_coordinator.py +239 -0
  136. crackerjack/core/timeout_manager.py +563 -0
  137. crackerjack/core/websocket_lifecycle.py +410 -0
  138. crackerjack/core/workflow/__init__.py +21 -0
  139. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  140. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  141. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  142. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  143. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  144. crackerjack/core/workflow_orchestrator.py +2243 -0
  145. crackerjack/data/README.md +11 -0
  146. crackerjack/data/__init__.py +8 -0
  147. crackerjack/data/models.py +79 -0
  148. crackerjack/data/repository.py +210 -0
  149. crackerjack/decorators/README.md +180 -0
  150. crackerjack/decorators/__init__.py +35 -0
  151. crackerjack/decorators/error_handling.py +649 -0
  152. crackerjack/decorators/error_handling_decorators.py +334 -0
  153. crackerjack/decorators/helpers.py +58 -0
  154. crackerjack/decorators/patterns.py +281 -0
  155. crackerjack/decorators/utils.py +58 -0
  156. crackerjack/docs/INDEX.md +11 -0
  157. crackerjack/docs/README.md +11 -0
  158. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  159. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  160. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  161. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  162. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  163. crackerjack/documentation/README.md +11 -0
  164. crackerjack/documentation/__init__.py +31 -0
  165. crackerjack/documentation/ai_templates.py +756 -0
  166. crackerjack/documentation/dual_output_generator.py +767 -0
  167. crackerjack/documentation/mkdocs_integration.py +518 -0
  168. crackerjack/documentation/reference_generator.py +1065 -0
  169. crackerjack/dynamic_config.py +678 -0
  170. crackerjack/errors.py +378 -0
  171. crackerjack/events/README.md +11 -0
  172. crackerjack/events/__init__.py +16 -0
  173. crackerjack/events/telemetry.py +175 -0
  174. crackerjack/events/workflow_bus.py +346 -0
  175. crackerjack/exceptions/README.md +301 -0
  176. crackerjack/exceptions/__init__.py +5 -0
  177. crackerjack/exceptions/config.py +4 -0
  178. crackerjack/exceptions/tool_execution_error.py +245 -0
  179. crackerjack/executors/README.md +591 -0
  180. crackerjack/executors/__init__.py +13 -0
  181. crackerjack/executors/async_hook_executor.py +938 -0
  182. crackerjack/executors/cached_hook_executor.py +316 -0
  183. crackerjack/executors/hook_executor.py +1295 -0
  184. crackerjack/executors/hook_lock_manager.py +708 -0
  185. crackerjack/executors/individual_hook_executor.py +739 -0
  186. crackerjack/executors/lsp_aware_hook_executor.py +349 -0
  187. crackerjack/executors/progress_hook_executor.py +282 -0
  188. crackerjack/executors/tool_proxy.py +433 -0
  189. crackerjack/hooks/README.md +485 -0
  190. crackerjack/hooks/lsp_hook.py +93 -0
  191. crackerjack/intelligence/README.md +557 -0
  192. crackerjack/intelligence/__init__.py +37 -0
  193. crackerjack/intelligence/adaptive_learning.py +693 -0
  194. crackerjack/intelligence/agent_orchestrator.py +485 -0
  195. crackerjack/intelligence/agent_registry.py +377 -0
  196. crackerjack/intelligence/agent_selector.py +439 -0
  197. crackerjack/intelligence/integration.py +250 -0
  198. crackerjack/interactive.py +719 -0
  199. crackerjack/managers/README.md +369 -0
  200. crackerjack/managers/__init__.py +11 -0
  201. crackerjack/managers/async_hook_manager.py +135 -0
  202. crackerjack/managers/hook_manager.py +585 -0
  203. crackerjack/managers/publish_manager.py +631 -0
  204. crackerjack/managers/test_command_builder.py +391 -0
  205. crackerjack/managers/test_executor.py +474 -0
  206. crackerjack/managers/test_manager.py +1357 -0
  207. crackerjack/managers/test_progress.py +187 -0
  208. crackerjack/mcp/README.md +374 -0
  209. crackerjack/mcp/__init__.py +0 -0
  210. crackerjack/mcp/cache.py +352 -0
  211. crackerjack/mcp/client_runner.py +121 -0
  212. crackerjack/mcp/context.py +802 -0
  213. crackerjack/mcp/dashboard.py +657 -0
  214. crackerjack/mcp/enhanced_progress_monitor.py +493 -0
  215. crackerjack/mcp/file_monitor.py +394 -0
  216. crackerjack/mcp/progress_components.py +607 -0
  217. crackerjack/mcp/progress_monitor.py +1016 -0
  218. crackerjack/mcp/rate_limiter.py +336 -0
  219. crackerjack/mcp/server.py +24 -0
  220. crackerjack/mcp/server_core.py +526 -0
  221. crackerjack/mcp/service_watchdog.py +505 -0
  222. crackerjack/mcp/state.py +407 -0
  223. crackerjack/mcp/task_manager.py +259 -0
  224. crackerjack/mcp/tools/README.md +27 -0
  225. crackerjack/mcp/tools/__init__.py +19 -0
  226. crackerjack/mcp/tools/core_tools.py +469 -0
  227. crackerjack/mcp/tools/error_analyzer.py +283 -0
  228. crackerjack/mcp/tools/execution_tools.py +384 -0
  229. crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
  230. crackerjack/mcp/tools/intelligence_tools.py +264 -0
  231. crackerjack/mcp/tools/monitoring_tools.py +628 -0
  232. crackerjack/mcp/tools/proactive_tools.py +367 -0
  233. crackerjack/mcp/tools/progress_tools.py +222 -0
  234. crackerjack/mcp/tools/semantic_tools.py +584 -0
  235. crackerjack/mcp/tools/utility_tools.py +358 -0
  236. crackerjack/mcp/tools/workflow_executor.py +699 -0
  237. crackerjack/mcp/websocket/README.md +31 -0
  238. crackerjack/mcp/websocket/__init__.py +14 -0
  239. crackerjack/mcp/websocket/app.py +54 -0
  240. crackerjack/mcp/websocket/endpoints.py +492 -0
  241. crackerjack/mcp/websocket/event_bridge.py +188 -0
  242. crackerjack/mcp/websocket/jobs.py +406 -0
  243. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  244. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  245. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  246. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  247. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  248. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  249. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  250. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  251. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  252. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  253. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  254. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  255. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  256. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  257. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  258. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  259. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  260. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  261. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  262. crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
  263. crackerjack/mcp/websocket/server.py +174 -0
  264. crackerjack/mcp/websocket/websocket_handler.py +276 -0
  265. crackerjack/mcp/websocket_server.py +10 -0
  266. crackerjack/models/README.md +308 -0
  267. crackerjack/models/__init__.py +40 -0
  268. crackerjack/models/config.py +730 -0
  269. crackerjack/models/config_adapter.py +265 -0
  270. crackerjack/models/protocols.py +1535 -0
  271. crackerjack/models/pydantic_models.py +320 -0
  272. crackerjack/models/qa_config.py +145 -0
  273. crackerjack/models/qa_results.py +134 -0
  274. crackerjack/models/resource_protocols.py +299 -0
  275. crackerjack/models/results.py +35 -0
  276. crackerjack/models/semantic_models.py +258 -0
  277. crackerjack/models/task.py +173 -0
  278. crackerjack/models/test_models.py +60 -0
  279. crackerjack/monitoring/README.md +11 -0
  280. crackerjack/monitoring/__init__.py +0 -0
  281. crackerjack/monitoring/ai_agent_watchdog.py +405 -0
  282. crackerjack/monitoring/metrics_collector.py +427 -0
  283. crackerjack/monitoring/regression_prevention.py +580 -0
  284. crackerjack/monitoring/websocket_server.py +406 -0
  285. crackerjack/orchestration/README.md +340 -0
  286. crackerjack/orchestration/__init__.py +43 -0
  287. crackerjack/orchestration/advanced_orchestrator.py +894 -0
  288. crackerjack/orchestration/cache/README.md +312 -0
  289. crackerjack/orchestration/cache/__init__.py +37 -0
  290. crackerjack/orchestration/cache/memory_cache.py +338 -0
  291. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  292. crackerjack/orchestration/config.py +297 -0
  293. crackerjack/orchestration/coverage_improvement.py +180 -0
  294. crackerjack/orchestration/execution_strategies.py +361 -0
  295. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  296. crackerjack/orchestration/strategies/README.md +401 -0
  297. crackerjack/orchestration/strategies/__init__.py +39 -0
  298. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  299. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  300. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  301. crackerjack/orchestration/test_progress_streamer.py +647 -0
  302. crackerjack/plugins/README.md +11 -0
  303. crackerjack/plugins/__init__.py +15 -0
  304. crackerjack/plugins/base.py +200 -0
  305. crackerjack/plugins/hooks.py +254 -0
  306. crackerjack/plugins/loader.py +335 -0
  307. crackerjack/plugins/managers.py +264 -0
  308. crackerjack/py313.py +191 -0
  309. crackerjack/security/README.md +11 -0
  310. crackerjack/security/__init__.py +0 -0
  311. crackerjack/security/audit.py +197 -0
  312. crackerjack/services/README.md +374 -0
  313. crackerjack/services/__init__.py +9 -0
  314. crackerjack/services/ai/README.md +295 -0
  315. crackerjack/services/ai/__init__.py +7 -0
  316. crackerjack/services/ai/advanced_optimizer.py +878 -0
  317. crackerjack/services/ai/contextual_ai_assistant.py +542 -0
  318. crackerjack/services/ai/embeddings.py +444 -0
  319. crackerjack/services/ai/intelligent_commit.py +328 -0
  320. crackerjack/services/ai/predictive_analytics.py +510 -0
  321. crackerjack/services/anomaly_detector.py +392 -0
  322. crackerjack/services/api_extractor.py +617 -0
  323. crackerjack/services/backup_service.py +467 -0
  324. crackerjack/services/bounded_status_operations.py +530 -0
  325. crackerjack/services/cache.py +369 -0
  326. crackerjack/services/changelog_automation.py +399 -0
  327. crackerjack/services/command_execution_service.py +305 -0
  328. crackerjack/services/config_integrity.py +132 -0
  329. crackerjack/services/config_merge.py +546 -0
  330. crackerjack/services/config_service.py +198 -0
  331. crackerjack/services/config_template.py +493 -0
  332. crackerjack/services/coverage_badge_service.py +173 -0
  333. crackerjack/services/coverage_ratchet.py +381 -0
  334. crackerjack/services/debug.py +733 -0
  335. crackerjack/services/dependency_analyzer.py +460 -0
  336. crackerjack/services/dependency_monitor.py +622 -0
  337. crackerjack/services/documentation_generator.py +493 -0
  338. crackerjack/services/documentation_service.py +704 -0
  339. crackerjack/services/enhanced_filesystem.py +497 -0
  340. crackerjack/services/enterprise_optimizer.py +865 -0
  341. crackerjack/services/error_pattern_analyzer.py +676 -0
  342. crackerjack/services/file_filter.py +221 -0
  343. crackerjack/services/file_hasher.py +149 -0
  344. crackerjack/services/file_io_service.py +361 -0
  345. crackerjack/services/file_modifier.py +615 -0
  346. crackerjack/services/filesystem.py +381 -0
  347. crackerjack/services/git.py +422 -0
  348. crackerjack/services/health_metrics.py +615 -0
  349. crackerjack/services/heatmap_generator.py +744 -0
  350. crackerjack/services/incremental_executor.py +380 -0
  351. crackerjack/services/initialization.py +823 -0
  352. crackerjack/services/input_validator.py +668 -0
  353. crackerjack/services/intelligent_commit.py +327 -0
  354. crackerjack/services/log_manager.py +289 -0
  355. crackerjack/services/logging.py +228 -0
  356. crackerjack/services/lsp_client.py +628 -0
  357. crackerjack/services/memory_optimizer.py +414 -0
  358. crackerjack/services/metrics.py +587 -0
  359. crackerjack/services/monitoring/README.md +30 -0
  360. crackerjack/services/monitoring/__init__.py +9 -0
  361. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  362. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  363. crackerjack/services/monitoring/health_metrics.py +716 -0
  364. crackerjack/services/monitoring/metrics.py +587 -0
  365. crackerjack/services/monitoring/performance_benchmarks.py +410 -0
  366. crackerjack/services/monitoring/performance_cache.py +388 -0
  367. crackerjack/services/monitoring/performance_monitor.py +569 -0
  368. crackerjack/services/parallel_executor.py +527 -0
  369. crackerjack/services/pattern_cache.py +333 -0
  370. crackerjack/services/pattern_detector.py +478 -0
  371. crackerjack/services/patterns/__init__.py +142 -0
  372. crackerjack/services/patterns/agents.py +107 -0
  373. crackerjack/services/patterns/code/__init__.py +15 -0
  374. crackerjack/services/patterns/code/detection.py +118 -0
  375. crackerjack/services/patterns/code/imports.py +107 -0
  376. crackerjack/services/patterns/code/paths.py +159 -0
  377. crackerjack/services/patterns/code/performance.py +119 -0
  378. crackerjack/services/patterns/code/replacement.py +36 -0
  379. crackerjack/services/patterns/core.py +212 -0
  380. crackerjack/services/patterns/documentation/__init__.py +14 -0
  381. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  382. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  383. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  384. crackerjack/services/patterns/formatting.py +226 -0
  385. crackerjack/services/patterns/operations.py +339 -0
  386. crackerjack/services/patterns/security/__init__.py +23 -0
  387. crackerjack/services/patterns/security/code_injection.py +122 -0
  388. crackerjack/services/patterns/security/credentials.py +190 -0
  389. crackerjack/services/patterns/security/path_traversal.py +221 -0
  390. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  391. crackerjack/services/patterns/templates.py +62 -0
  392. crackerjack/services/patterns/testing/__init__.py +18 -0
  393. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  394. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  395. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  396. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  397. crackerjack/services/patterns/tool_output/other.py +97 -0
  398. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  399. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  400. crackerjack/services/patterns/url_sanitization.py +114 -0
  401. crackerjack/services/patterns/utilities.py +42 -0
  402. crackerjack/services/patterns/utils.py +339 -0
  403. crackerjack/services/patterns/validation.py +46 -0
  404. crackerjack/services/patterns/versioning.py +62 -0
  405. crackerjack/services/predictive_analytics.py +523 -0
  406. crackerjack/services/profiler.py +280 -0
  407. crackerjack/services/quality/README.md +415 -0
  408. crackerjack/services/quality/__init__.py +11 -0
  409. crackerjack/services/quality/anomaly_detector.py +392 -0
  410. crackerjack/services/quality/pattern_cache.py +333 -0
  411. crackerjack/services/quality/pattern_detector.py +479 -0
  412. crackerjack/services/quality/qa_orchestrator.py +491 -0
  413. crackerjack/services/quality/quality_baseline.py +395 -0
  414. crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
  415. crackerjack/services/quality/quality_intelligence.py +949 -0
  416. crackerjack/services/regex_patterns.py +58 -0
  417. crackerjack/services/regex_utils.py +483 -0
  418. crackerjack/services/secure_path_utils.py +524 -0
  419. crackerjack/services/secure_status_formatter.py +450 -0
  420. crackerjack/services/secure_subprocess.py +635 -0
  421. crackerjack/services/security.py +239 -0
  422. crackerjack/services/security_logger.py +495 -0
  423. crackerjack/services/server_manager.py +411 -0
  424. crackerjack/services/smart_scheduling.py +167 -0
  425. crackerjack/services/status_authentication.py +460 -0
  426. crackerjack/services/status_security_manager.py +315 -0
  427. crackerjack/services/terminal_utils.py +0 -0
  428. crackerjack/services/thread_safe_status_collector.py +441 -0
  429. crackerjack/services/tool_filter.py +368 -0
  430. crackerjack/services/tool_version_service.py +43 -0
  431. crackerjack/services/unified_config.py +115 -0
  432. crackerjack/services/validation_rate_limiter.py +220 -0
  433. crackerjack/services/vector_store.py +689 -0
  434. crackerjack/services/version_analyzer.py +461 -0
  435. crackerjack/services/version_checker.py +223 -0
  436. crackerjack/services/websocket_resource_limiter.py +438 -0
  437. crackerjack/services/zuban_lsp_service.py +391 -0
  438. crackerjack/slash_commands/README.md +11 -0
  439. crackerjack/slash_commands/__init__.py +59 -0
  440. crackerjack/slash_commands/init.md +112 -0
  441. crackerjack/slash_commands/run.md +197 -0
  442. crackerjack/slash_commands/status.md +127 -0
  443. crackerjack/tools/README.md +11 -0
  444. crackerjack/tools/__init__.py +30 -0
  445. crackerjack/tools/_git_utils.py +105 -0
  446. crackerjack/tools/check_added_large_files.py +139 -0
  447. crackerjack/tools/check_ast.py +105 -0
  448. crackerjack/tools/check_json.py +103 -0
  449. crackerjack/tools/check_jsonschema.py +297 -0
  450. crackerjack/tools/check_toml.py +103 -0
  451. crackerjack/tools/check_yaml.py +110 -0
  452. crackerjack/tools/codespell_wrapper.py +72 -0
  453. crackerjack/tools/end_of_file_fixer.py +202 -0
  454. crackerjack/tools/format_json.py +128 -0
  455. crackerjack/tools/mdformat_wrapper.py +114 -0
  456. crackerjack/tools/trailing_whitespace.py +198 -0
  457. crackerjack/tools/validate_input_validator_patterns.py +236 -0
  458. crackerjack/tools/validate_regex_patterns.py +188 -0
  459. crackerjack/ui/README.md +11 -0
  460. crackerjack/ui/__init__.py +1 -0
  461. crackerjack/ui/dashboard_renderer.py +28 -0
  462. crackerjack/ui/templates/README.md +11 -0
  463. crackerjack/utils/console_utils.py +13 -0
  464. crackerjack/utils/dependency_guard.py +230 -0
  465. crackerjack/utils/retry_utils.py +275 -0
  466. crackerjack/workflows/README.md +590 -0
  467. crackerjack/workflows/__init__.py +46 -0
  468. crackerjack/workflows/actions.py +811 -0
  469. crackerjack/workflows/auto_fix.py +444 -0
  470. crackerjack/workflows/container_builder.py +499 -0
  471. crackerjack/workflows/definitions.py +443 -0
  472. crackerjack/workflows/engine.py +177 -0
  473. crackerjack/workflows/event_bridge.py +242 -0
  474. crackerjack-0.45.2.dist-info/METADATA +1678 -0
  475. crackerjack-0.45.2.dist-info/RECORD +478 -0
  476. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  477. crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
  478. crackerjack/.gitignore +0 -14
  479. crackerjack/.libcst.codemod.yaml +0 -18
  480. crackerjack/.pdm.toml +0 -1
  481. crackerjack/.pre-commit-config.yaml +0 -91
  482. crackerjack/.pytest_cache/.gitignore +0 -2
  483. crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
  484. crackerjack/.pytest_cache/README.md +0 -8
  485. crackerjack/.pytest_cache/v/cache/nodeids +0 -1
  486. crackerjack/.pytest_cache/v/cache/stepwise +0 -1
  487. crackerjack/.ruff_cache/.gitignore +0 -1
  488. crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  489. crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  490. crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  491. crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  492. crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  493. crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  494. crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  495. crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  496. crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  497. crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  498. crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  499. crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  500. crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  501. crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  502. crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  503. crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  504. crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  505. crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  506. crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  507. crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  508. crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  509. crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  510. crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  511. crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  512. crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  513. crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  514. crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  515. crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  516. crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  517. crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  518. crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  519. crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  520. crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  521. crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  522. crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  523. crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  524. crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  525. crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  526. crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  527. crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
  528. crackerjack/crackerjack.py +0 -855
  529. crackerjack/pyproject.toml +0 -214
  530. crackerjack-0.18.2.dist-info/METADATA +0 -420
  531. crackerjack-0.18.2.dist-info/RECORD +0 -59
  532. crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
  533. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,437 @@
1
+ """Dead code detection and removal logic."""
2
+
3
+ import ast
4
+ import typing as t
5
+
6
+ from ...base import AgentContext
7
+
8
+
9
+ class DeadCodeDetector:
10
+ """Detects dead code including unused imports and unreachable code."""
11
+
12
+ def __init__(self, context: AgentContext) -> None:
13
+ """Initialize detector with agent context.
14
+
15
+ Args:
16
+ context: AgentContext for logging
17
+ """
18
+ self.context = context
19
+
20
+ def analyze_dead_code(self, tree: ast.AST, content: str) -> dict[str, t.Any]:
21
+ """Analyze code for dead code patterns.
22
+
23
+ Args:
24
+ tree: AST tree
25
+ content: File content
26
+
27
+ Returns:
28
+ Analysis results
29
+ """
30
+ analysis: dict[str, list[t.Any]] = {
31
+ "unused_imports": [],
32
+ "unused_variables": [],
33
+ "unused_functions": [],
34
+ "unused_classes": [],
35
+ "unreachable_code": [],
36
+ "removable_items": [],
37
+ }
38
+
39
+ analyzer_result = self._collect_usage_data(tree)
40
+ self._process_unused_imports(analysis, analyzer_result)
41
+ self._process_unused_functions(analysis, analyzer_result)
42
+ self._process_unused_classes(analysis, analyzer_result)
43
+ self._detect_unreachable_code(analysis, tree, content)
44
+ self._detect_redundant_code(analysis, tree, content)
45
+
46
+ return analysis
47
+
48
+ def _collect_usage_data(self, tree: ast.AST) -> dict[str, t.Any]:
49
+ """Collect usage data from AST.
50
+
51
+ Args:
52
+ tree: AST tree
53
+
54
+ Returns:
55
+ Usage data
56
+ """
57
+ collector = UsageDataCollector()
58
+ analyzer = EnhancedUsageAnalyzer(collector)
59
+ analyzer.visit(tree)
60
+ return collector.get_results(analyzer)
61
+
62
+ @staticmethod
63
+ def _process_unused_imports(
64
+ analysis: dict[str, t.Any],
65
+ analyzer_result: dict[str, t.Any],
66
+ ) -> None:
67
+ """Process unused imports.
68
+
69
+ Args:
70
+ analysis: Analysis dict
71
+ analyzer_result: Analyzer result
72
+ """
73
+ import_lines: list[tuple[int, str, str]] = analyzer_result["import_lines"]
74
+ for line_no, name, import_type in import_lines:
75
+ if name not in analyzer_result["used_names"]:
76
+ analysis["unused_imports"].append(
77
+ {
78
+ "name": name,
79
+ "line": line_no,
80
+ "type": import_type,
81
+ },
82
+ )
83
+ analysis["removable_items"].append(f"unused import: {name}")
84
+
85
+ @staticmethod
86
+ def _process_unused_functions(
87
+ analysis: dict[str, t.Any],
88
+ analyzer_result: dict[str, t.Any],
89
+ ) -> None:
90
+ """Process unused functions.
91
+
92
+ Args:
93
+ analysis: Analysis dict
94
+ analyzer_result: Analyzer result
95
+ """
96
+ all_unused_functions: list[dict[str, t.Any]] = analyzer_result[
97
+ "unused_functions"
98
+ ]
99
+ unused_functions = [
100
+ func
101
+ for func in all_unused_functions
102
+ if func["name"] not in analyzer_result["used_names"]
103
+ ]
104
+ analysis["unused_functions"] = unused_functions
105
+ for func in unused_functions:
106
+ analysis["removable_items"].append(f"unused function: {func['name']}")
107
+
108
+ @staticmethod
109
+ def _process_unused_classes(
110
+ analysis: dict[str, t.Any], analyzer_result: dict[str, t.Any]
111
+ ) -> None:
112
+ """Process unused classes.
113
+
114
+ Args:
115
+ analysis: Analysis dict
116
+ analyzer_result: Analyzer result
117
+ """
118
+ if "unused_classes" not in analyzer_result:
119
+ return
120
+
121
+ unused_classes = [
122
+ cls
123
+ for cls in analyzer_result["unused_classes"]
124
+ if cls["name"] not in analyzer_result["used_names"]
125
+ ]
126
+
127
+ analysis["unused_classes"] = unused_classes
128
+ for cls in unused_classes:
129
+ analysis["removable_items"].append(f"unused class: {cls['name']}")
130
+
131
+ @staticmethod
132
+ def _detect_unreachable_code(
133
+ analysis: dict[str, t.Any], tree: ast.AST, content: str
134
+ ) -> None:
135
+ """Detect unreachable code.
136
+
137
+ Args:
138
+ analysis: Analysis dict
139
+ tree: AST tree
140
+ content: File content
141
+ """
142
+
143
+ class UnreachableCodeDetector(ast.NodeVisitor):
144
+ def __init__(self) -> None:
145
+ self.unreachable_blocks: list[dict[str, t.Any]] = []
146
+
147
+ def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
148
+ self._check_unreachable_in_function(node)
149
+ self.generic_visit(node)
150
+
151
+ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
152
+ self._check_unreachable_in_function(node)
153
+ self.generic_visit(node)
154
+
155
+ def _check_unreachable_in_function(
156
+ self, node: ast.FunctionDef | ast.AsyncFunctionDef
157
+ ) -> None:
158
+ for i, stmt in enumerate(node.body):
159
+ if isinstance(stmt, ast.Return | ast.Raise):
160
+ if i + 1 < len(node.body):
161
+ next_stmt = node.body[i + 1]
162
+ self.unreachable_blocks.append(
163
+ {
164
+ "type": "unreachable_after_return",
165
+ "line": next_stmt.lineno,
166
+ "function": node.name,
167
+ }
168
+ )
169
+
170
+ detector = UnreachableCodeDetector()
171
+ detector.visit(tree)
172
+
173
+ analysis["unreachable_code"] = detector.unreachable_blocks
174
+ for block in detector.unreachable_blocks:
175
+ analysis["removable_items"].append(
176
+ f"unreachable code after line {block['line']} in {block['function']}"
177
+ )
178
+
179
+ @staticmethod
180
+ def _detect_redundant_code(
181
+ analysis: dict[str, t.Any], tree: ast.AST, content: str
182
+ ) -> None:
183
+ """Detect redundant code patterns.
184
+
185
+ Args:
186
+ analysis: Analysis dict
187
+ tree: AST tree
188
+ content: File content
189
+ """
190
+ lines = content.split("\n")
191
+
192
+ line_hashes = {}
193
+ for i, line in enumerate(lines):
194
+ if line.strip() and not line.strip().startswith("#"):
195
+ line_hash = hash(line.strip())
196
+ if line_hash in line_hashes:
197
+ analysis["removable_items"].append(
198
+ f"potential duplicate code at line {i + 1}"
199
+ )
200
+ line_hashes[line_hash] = i
201
+
202
+ class RedundantPatternDetector(ast.NodeVisitor):
203
+ def __init__(self) -> None:
204
+ self.redundant_items: list[dict[str, t.Any]] = []
205
+
206
+ def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
207
+ if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
208
+ self.redundant_items.append(
209
+ {"type": "empty_except", "line": node.lineno}
210
+ )
211
+ self.generic_visit(node)
212
+
213
+ def visit_If(self, node: ast.If) -> None:
214
+ if isinstance(node.test, ast.Constant):
215
+ if node.test.value is True:
216
+ self.redundant_items.append(
217
+ {"type": "if_true", "line": node.lineno}
218
+ )
219
+ elif node.test.value is False:
220
+ self.redundant_items.append(
221
+ {"type": "if_false", "line": node.lineno}
222
+ )
223
+ self.generic_visit(node)
224
+
225
+ detector = RedundantPatternDetector()
226
+ detector.visit(tree)
227
+
228
+ for item in detector.redundant_items:
229
+ analysis["removable_items"].append(
230
+ f"redundant {item['type']} at line {item['line']}"
231
+ )
232
+
233
+ def find_lines_to_remove(
234
+ self, lines: list[str], analysis: dict[str, t.Any]
235
+ ) -> set[int]:
236
+ """Find lines to remove.
237
+
238
+ Args:
239
+ lines: File lines
240
+ analysis: Analysis results
241
+
242
+ Returns:
243
+ Set of line indices to remove
244
+ """
245
+ lines_to_remove: set[int] = set()
246
+
247
+ for unused_import in analysis["unused_imports"]:
248
+ line_idx = unused_import["line"] - 1
249
+ if 0 <= line_idx < len(lines):
250
+ line = lines[line_idx]
251
+ if self._should_remove_import_line(line, unused_import):
252
+ lines_to_remove.add(line_idx)
253
+
254
+ return lines_to_remove
255
+
256
+ @staticmethod
257
+ def _should_remove_import_line(line: str, unused_import: dict[str, str]) -> bool:
258
+ """Check if import line should be removed.
259
+
260
+ Args:
261
+ line: Code line
262
+ unused_import: Unused import info
263
+
264
+ Returns:
265
+ True if should remove
266
+ """
267
+ if unused_import["type"] == "import":
268
+ return f"import {unused_import['name']}" in line
269
+ elif unused_import["type"] == "from_import":
270
+ return (
271
+ "from " in line
272
+ and unused_import["name"] in line
273
+ and line.strip().endswith(unused_import["name"])
274
+ )
275
+ return False
276
+
277
+ @staticmethod
278
+ def _find_unreachable_lines(
279
+ lines: list[str], analysis: dict[str, t.Any]
280
+ ) -> set[int]:
281
+ """Find unreachable code lines.
282
+
283
+ Args:
284
+ lines: File lines
285
+ analysis: Analysis results
286
+
287
+ Returns:
288
+ Set of line indices
289
+ """
290
+ lines_to_remove: set[int] = set()
291
+
292
+ for item in analysis.get("unreachable_code", []):
293
+ if "line" in item:
294
+ line_idx = item["line"] - 1
295
+ if 0 <= line_idx < len(lines):
296
+ lines_to_remove.add(line_idx)
297
+
298
+ return lines_to_remove
299
+
300
+ @staticmethod
301
+ def _find_redundant_lines(lines: list[str], analysis: dict[str, t.Any]) -> set[int]:
302
+ """Find redundant code lines.
303
+
304
+ Args:
305
+ lines: File lines
306
+ analysis: Analysis results
307
+
308
+ Returns:
309
+ Set of line indices
310
+ """
311
+ lines_to_remove: set[int] = set()
312
+
313
+ for i in range(len(lines)):
314
+ if DeadCodeDetector._is_empty_except_block(lines, i):
315
+ empty_pass_idx = DeadCodeDetector._find_empty_pass_line(lines, i)
316
+ if empty_pass_idx is not None:
317
+ lines_to_remove.add(empty_pass_idx)
318
+
319
+ return lines_to_remove
320
+
321
+ @staticmethod
322
+ def _is_empty_except_block(lines: list[str], line_idx: int) -> bool:
323
+ """Check if except block is empty.
324
+
325
+ Args:
326
+ lines: File lines
327
+ line_idx: Line index
328
+
329
+ Returns:
330
+ True if empty except block
331
+ """
332
+ stripped = lines[line_idx].strip()
333
+ return stripped == "except: " or stripped.startswith("except ")
334
+
335
+ @staticmethod
336
+ def _find_empty_pass_line(lines: list[str], except_idx: int) -> int | None:
337
+ """Find empty pass line in except block.
338
+
339
+ Args:
340
+ lines: File lines
341
+ except_idx: Except line index
342
+
343
+ Returns:
344
+ Pass line index or None
345
+ """
346
+ for j in range(except_idx + 1, min(except_idx + 5, len(lines))):
347
+ next_line = lines[j].strip()
348
+ if not next_line:
349
+ continue
350
+ if next_line == "pass":
351
+ return j
352
+ break
353
+ return None
354
+
355
+
356
+ class UsageDataCollector:
357
+ """Collects usage data from AST traversal."""
358
+
359
+ def __init__(self) -> None:
360
+ """Initialize collector."""
361
+ self.imports: dict[str, str] = {}
362
+ self.import_lines: list[tuple[int, str, str]] = []
363
+ self.functions: list[dict[str, t.Any]] = []
364
+ self.classes: list[dict[str, t.Any]] = []
365
+ self.usages: set[str] = set()
366
+
367
+ def get_results(self, analyzer: "EnhancedUsageAnalyzer") -> dict[str, t.Any]:
368
+ """Get collection results.
369
+
370
+ Args:
371
+ analyzer: Enhanced usage analyzer
372
+
373
+ Returns:
374
+ Results dict
375
+ """
376
+ return {
377
+ "import_lines": self.import_lines,
378
+ "used_names": analyzer.used_names,
379
+ "unused_functions": self.functions,
380
+ "unused_classes": self.classes,
381
+ }
382
+
383
+
384
+ class EnhancedUsageAnalyzer(ast.NodeVisitor):
385
+ """Enhanced analyzer for tracking usage."""
386
+
387
+ def __init__(self, collector: UsageDataCollector) -> None:
388
+ """Initialize analyzer.
389
+
390
+ Args:
391
+ collector: Usage data collector
392
+ """
393
+ self.collector = collector
394
+ self.used_names: set[str] = set()
395
+ self.line_counter = 0
396
+
397
+ def visit_Import(self, node: ast.Import) -> None:
398
+ """Visit import statement."""
399
+ for alias in node.names:
400
+ name = alias.asname or alias.name
401
+ self.collector.import_lines.append((node.lineno, name, "import"))
402
+ self.generic_visit(node)
403
+
404
+ def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
405
+ """Visit from import statement."""
406
+ for alias in node.names:
407
+ name = alias.asname or alias.name
408
+ self.collector.import_lines.append((node.lineno, name, "from_import"))
409
+ self.generic_visit(node)
410
+
411
+ def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
412
+ """Visit function definition."""
413
+ self.collector.functions.append({"name": node.name, "line": node.lineno})
414
+ self.used_names.add(node.name)
415
+ self.generic_visit(node)
416
+
417
+ def visit_ClassDef(self, node: ast.ClassDef) -> None:
418
+ """Visit class definition."""
419
+ self.collector.classes.append({"name": node.name, "line": node.lineno})
420
+ self.used_names.add(node.name)
421
+ self.generic_visit(node)
422
+
423
+ def visit_Name(self, node: ast.Name) -> None:
424
+ """Visit name reference."""
425
+ self.used_names.add(node.id)
426
+ self.generic_visit(node)
427
+
428
+ def visit_Attribute(self, node: ast.Attribute) -> None:
429
+ """Visit attribute access."""
430
+ self.used_names.add(node.attr)
431
+ self.generic_visit(node)
432
+
433
+ def visit_Call(self, node: ast.Call) -> None:
434
+ """Visit function call."""
435
+ if isinstance(node.func, ast.Name):
436
+ self.used_names.add(node.func.id)
437
+ self.generic_visit(node)
@@ -0,0 +1,19 @@
1
+ """Test creation helper modules.
2
+
3
+ This package provides modular helpers for test creation:
4
+ - TestASTAnalyzer: AST parsing and code structure extraction
5
+ - TestTemplateGenerator: Test template generation
6
+ - TestCoverageAnalyzer: Coverage analysis and gap detection
7
+
8
+ All helpers use AgentContext pattern (legacy, intentional).
9
+ """
10
+
11
+ from .test_ast_analyzer import TestASTAnalyzer
12
+ from .test_coverage_analyzer import TestCoverageAnalyzer
13
+ from .test_template_generator import TestTemplateGenerator
14
+
15
+ __all__ = [
16
+ "TestASTAnalyzer",
17
+ "TestCoverageAnalyzer",
18
+ "TestTemplateGenerator",
19
+ ]
@@ -0,0 +1,216 @@
1
+ """AST parsing helper for test creation.
2
+
3
+ This module provides AST analysis capabilities for extracting code structure
4
+ from Python source files. Uses AgentContext pattern (legacy, intentional).
5
+ """
6
+
7
+ import ast
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from crackerjack.agents.base import AgentContext
12
+
13
+
14
+ class TestASTAnalyzer:
15
+ """AST parsing helper for test creation.
16
+
17
+ Uses AgentContext pattern (legacy, intentional).
18
+ """
19
+
20
+ def __init__(self, context: AgentContext) -> None:
21
+ self.context = context
22
+
23
+ async def extract_functions_from_file(
24
+ self,
25
+ file_path: Path,
26
+ ) -> list[dict[str, Any]]:
27
+ """Extract all functions from a Python file."""
28
+ functions: list[dict[str, Any]] = []
29
+
30
+ try:
31
+ content = self.context.get_file_content(file_path)
32
+ if not content:
33
+ return functions
34
+
35
+ tree = ast.parse(content)
36
+ functions = self._parse_function_nodes(tree)
37
+
38
+ except Exception as e:
39
+ self._log(f"Error parsing file {file_path}: {e}", "WARN")
40
+
41
+ return functions
42
+
43
+ def _parse_function_nodes(self, tree: ast.AST) -> list[dict[str, Any]]:
44
+ """Parse function nodes from AST."""
45
+ functions: list[dict[str, Any]] = []
46
+
47
+ for node in ast.walk(tree):
48
+ if isinstance(
49
+ node, ast.FunctionDef | ast.AsyncFunctionDef
50
+ ) and self._is_valid_function_node(node):
51
+ function_info = self._create_function_info(node)
52
+
53
+ function_info["is_async"] = isinstance(node, ast.AsyncFunctionDef)
54
+ functions.append(function_info)
55
+
56
+ return functions
57
+
58
+ def _is_valid_function_node(
59
+ self, node: ast.FunctionDef | ast.AsyncFunctionDef
60
+ ) -> bool:
61
+ """Check if function node should be included."""
62
+ return not node.name.startswith(("_", "test_"))
63
+
64
+ def _create_function_info(
65
+ self, node: ast.FunctionDef | ast.AsyncFunctionDef
66
+ ) -> dict[str, Any]:
67
+ """Create function info dict from AST node."""
68
+ return {
69
+ "name": node.name,
70
+ "line": node.lineno,
71
+ "signature": self._get_function_signature(node),
72
+ "args": [arg.arg for arg in node.args.args],
73
+ "returns": self._get_return_annotation(node),
74
+ "is_async": isinstance(node, ast.AsyncFunctionDef),
75
+ "docstring": ast.get_docstring(node) or "",
76
+ }
77
+
78
+ async def extract_classes_from_file(self, file_path: Path) -> list[dict[str, Any]]:
79
+ """Extract all classes from a Python file."""
80
+ classes: list[dict[str, Any]] = []
81
+
82
+ try:
83
+ content = self.context.get_file_content(file_path)
84
+ if not content:
85
+ return classes
86
+
87
+ tree = ast.parse(content)
88
+ classes = self._process_ast_nodes_for_classes(tree)
89
+
90
+ except Exception as e:
91
+ self._log(f"Error parsing classes from {file_path}: {e}", "WARN")
92
+
93
+ return classes
94
+
95
+ def _process_ast_nodes_for_classes(self, tree: ast.AST) -> list[dict[str, Any]]:
96
+ """Process AST nodes to extract class information."""
97
+ classes: list[dict[str, Any]] = []
98
+
99
+ for node in ast.walk(tree):
100
+ if isinstance(node, ast.ClassDef) and self._should_include_class(node):
101
+ class_info = self._create_class_info(node)
102
+ classes.append(class_info)
103
+
104
+ return classes
105
+
106
+ def _should_include_class(self, node: ast.ClassDef) -> bool:
107
+ """Check if class should be included in analysis."""
108
+ return not node.name.startswith("_")
109
+
110
+ def _create_class_info(self, node: ast.ClassDef) -> dict[str, Any]:
111
+ """Create class info dict from AST node."""
112
+ methods = self._extract_public_methods_from_class(node)
113
+ return {"name": node.name, "line": node.lineno, "methods": methods}
114
+
115
+ def _extract_public_methods_from_class(self, node: ast.ClassDef) -> list[str]:
116
+ """Extract public method names from class node."""
117
+ return [
118
+ item.name
119
+ for item in node.body
120
+ if isinstance(item, ast.FunctionDef) and not item.name.startswith("_")
121
+ ]
122
+
123
+ def _get_function_signature(
124
+ self, node: ast.FunctionDef | ast.AsyncFunctionDef
125
+ ) -> str:
126
+ """Get function signature string."""
127
+ args = [arg.arg for arg in node.args.args]
128
+ prefix = "async " if isinstance(node, ast.AsyncFunctionDef) else ""
129
+ return f"{prefix}{node.name}({', '.join(args)})"
130
+
131
+ def _get_return_annotation(
132
+ self, node: ast.FunctionDef | ast.AsyncFunctionDef
133
+ ) -> str:
134
+ """Get return type annotation string."""
135
+ if node.returns:
136
+ return ast.unparse(node.returns) if (hasattr(ast, "unparse")) else "Any"
137
+ return "Any"
138
+
139
+ async def function_has_test(
140
+ self,
141
+ func_info: dict[str, Any],
142
+ file_path: Path,
143
+ ) -> bool:
144
+ """Check if a function has corresponding test."""
145
+ test_file_path = await self.generate_test_file_path(file_path)
146
+
147
+ if not test_file_path.exists():
148
+ return False
149
+
150
+ test_content = self.context.get_file_content(test_file_path)
151
+ if not test_content:
152
+ return False
153
+
154
+ test_patterns = [
155
+ f"test_{func_info['name']}",
156
+ f"test_{func_info['name']}_",
157
+ f"def test_{func_info['name']}",
158
+ ]
159
+
160
+ return any(pattern in test_content for pattern in test_patterns)
161
+
162
+ async def generate_test_file_path(self, source_file: Path) -> Path:
163
+ """Generate path for test file corresponding to source file."""
164
+ tests_dir = self.context.project_path / "tests"
165
+ tests_dir.mkdir(exist_ok=True)
166
+
167
+ relative_path = source_file.relative_to(
168
+ self.context.project_path / "crackerjack",
169
+ )
170
+ test_name = f"test_{relative_path.stem}.py"
171
+
172
+ return tests_dir / test_name
173
+
174
+ def get_module_import_path(self, file_path: Path) -> str:
175
+ """Get module import path from file path."""
176
+ try:
177
+ relative_path = file_path.relative_to(self.context.project_path)
178
+ parts = (*relative_path.parts[:-1], relative_path.stem)
179
+ return ".".join(parts)
180
+ except ValueError:
181
+ return file_path.stem
182
+
183
+ def should_skip_module_for_coverage(self, py_file: Path) -> bool:
184
+ """Check if module should be skipped for coverage analysis."""
185
+ return py_file.name.startswith("test_") or py_file.name == "__init__.py"
186
+
187
+ def should_skip_file_for_testing(self, py_file: Path) -> bool:
188
+ """Check if file should be skipped for testing."""
189
+ return py_file.name.startswith("test_")
190
+
191
+ def has_corresponding_test(self, file_path: str) -> bool:
192
+ """Check if source file has corresponding test file."""
193
+ path = Path(file_path)
194
+
195
+ test_patterns = [
196
+ f"test_{path.stem}.py",
197
+ f"{path.stem}_test.py",
198
+ f"test_{path.stem}_*.py",
199
+ ]
200
+
201
+ tests_dir = self.context.project_path / "tests"
202
+ if tests_dir.exists():
203
+ for pattern in test_patterns:
204
+ if list(tests_dir.glob(pattern)):
205
+ return True
206
+
207
+ return False
208
+
209
+ def get_relative_module_path(self, py_file: Path) -> str:
210
+ """Get relative module path from project root."""
211
+ return str(py_file.relative_to(self.context.project_path))
212
+
213
+ def _log(self, message: str, level: str = "INFO") -> None:
214
+ """Log message through context."""
215
+ # This is a helper - logging would typically go through the agent
216
+ pass